I have this code to activate an extension only if a certain website is visited. I've noticed that the extension icon will be always clickable and will be not grey if the url isn't mathcing with the condition setted and when the desired website is visited and the url match, if the user click on the extension icon, the popup will not be opened. How I can fix?
chrome.runtime.onInstalled.addListener(function() {
chrome.declarativeContent.onPageChanged.removeRules(undefined, function() {
chrome.declarativeContent.onPageChanged.addRules([
{
conditions: [
new chrome.declarativeContent.PageStateMatcher({
pageUrl: { hostEquals: 'www.example.com/video/*', schemes: ["https"] },
})
],
actions: [ new chrome.declarativeContent.ShowPageAction() ]
}
]);
});
});
chrome.pageAction.onClicked.addListener( () => {
chrome.windows.create({
url: chrome.runtime.getURL('popup.html'),
width: 500,
height: 295,
type: 'popup'
});
});
Your rule for hostEquals will never match anything because per the documentation it's compared against the host part of a URL e.g. simply www.example.com so it can't have / or *. Note that chrome.declarativeContent uses its own filtering system, it does not support any of the usual matching patterns used by content_scripts or webRequest.
Solution 1:
{ hostEquals: 'www.example.com', pathPrefix: '/video/', schemes: ['https'] }
Solution 2:
{ urlPrefix: 'https://www.example.com/video/' }
Related
I'm the author of Intab, a Chrome extension that lets you view a link inline as opposed to a new tab. There's not much fancy stuff going on behind the scenes, it's just an iframe that loads the URL the user clicked on.
It works great except for sites that set the X-Frame-Options header to DENY or SAMEORIGIN. Some really big sites like Google and Facebook both use it which makes for a slightly janky experience.
Is there any way to get around this? Since I'm using a Chrome extension, is there any browser level stuff I can access that might help? Looking for any ideas or help!
Chrome offers the webRequest API to intercept and modify HTTP requests. You can remove the X-Frame-Options header to allow inlining pages within an iframe.
chrome.webRequest.onHeadersReceived.addListener(
function(info) {
var headers = info.responseHeaders;
for (var i=headers.length-1; i>=0; --i) {
var header = headers[i].name.toLowerCase();
if (header == 'x-frame-options' || header == 'frame-options') {
headers.splice(i, 1); // Remove header
}
}
return {responseHeaders: headers};
}, {
urls: [
'*://*/*', // Pattern to match all http(s) pages
// '*://*.example.org/*', // Pattern to match one http(s) site
],
types: [ 'sub_frame' ]
}, [
'blocking',
'responseHeaders',
// Modern Chrome needs 'extraHeaders' to see and change this header,
// so the following code evaluates to 'extraHeaders' only in modern Chrome.
chrome.webRequest.OnHeadersReceivedOptions.EXTRA_HEADERS,
].filter(Boolean)
);
In the manifest, you need to specify the webRequest and webRequestBlocking permissions, plus the URLs patterns you're intending to intercept i.e. "*://*/*" or "*://www.example.org/*" for the example above.
ManifestV3 example using declarativeNetRequest
See also the warning at the end of this answer!
manifest.json for Chrome 96 and newer,
doesn't show a separate permission for "Block page content" during installation
"minimum_chrome_version": "96",
"permissions": ["declarativeNetRequestWithHostAccess"],
"host_permissions": ["*://*.example.com/"],
"background": {"service_worker": "bg.js"},
bg.js for Chrome 101 and newer using initiatorDomains and requestDomains
(don't forget to add "minimum_chrome_version": "101" in manifest.json)
const iframeHosts = [
'example.com',
];
chrome.runtime.onInstalled.addListener(() => {
const RULE = {
id: 1,
condition: {
initiatorDomains: [chrome.runtime.id],
requestDomains: iframeHosts,
resourceTypes: ['sub_frame'],
},
action: {
type: 'modifyHeaders',
responseHeaders: [
{header: 'X-Frame-Options', operation: 'remove'},
{header: 'Frame-Options', operation: 'remove'},
],
},
};
chrome.declarativeNetRequest.updateDynamicRules({
removeRuleIds: [RULE.id],
addRules: [RULE],
});
});
Old Chrome 84-100
Use the following instead, if your extension should be compatible with these old versions.
manifest.json for Chrome 84 and newer,
shows a separate permission for "Block page content" during installation
"permissions": ["declarativeNetRequest"],
"host_permissions": ["*://*.example.com/"],
"background": {"service_worker": "bg.js"},
bg.js for Chrome 84 and newer using the now deprecated domains
const iframeHosts = [
'example.com',
];
chrome.runtime.onInstalled.addListener(() => {
chrome.declarativeNetRequest.updateDynamicRules({
removeRuleIds: iframeHosts.map((h, i) => i + 1),
addRules: iframeHosts.map((h, i) => ({
id: i + 1,
condition: {
domains: [chrome.runtime.id],
urlFilter: `||${h}/`,
resourceTypes: ['sub_frame'],
},
action: {
type: 'modifyHeaders',
responseHeaders: [
{header: 'X-Frame-Options', operation: 'remove'},
{header: 'Frame-Options', operation: 'remove'},
],
},
})),
});
});
Warning: beware of site's service worker
You may have to remove the service worker of the site(s) and clear its cache before adding the iframe or before opening the extension page because many modern sites use the service worker to create the page without making a network request thus ignoring our header-stripping rule.
Add "browsingData" to "permissions" in manifest.json
Clear the SW:
function removeSW(url) {
return chrome.browsingData.remove({
origins: [new URL(url).origin],
}, {
cacheStorage: true,
serviceWorkers: true,
});
}
// If you add an iframe element in DOM:
async function addIframe(url, parent = document.body) {
await removeSW(url);
const el = document.createElement('iframe');
parent.appendChild(el);
el.src = url;
return el;
}
// If you open an extension page with an <iframe> element in its HTML:
async function openPage(url) {
await removeSW('https://example.com/');
return chrome.tabs.create({url});
}
You can try the Frame extension that lets the user drop X-Frame-Options and Content-Security-Policy HTTP response headers, allowing pages to be iframed.
The code is available on github
It's based on ManifestV3 and working perfectly with Google & Facebook.
I need the extension to be available only when the user is logged in to the parent site i.e www.flyship.com and also to the sites which are mentioned in the manifest.json.
"content_scripts": [
{
"matches": [
"*://*.flyship.com/*",
"*://app.yield.io/*",
"*://*.indeedjobs.com/*"
],
"js": ["scripts/content.js","scripts/index.js"]
}
],
In the background.js i am checking if the user is logged in to flyship.com using this code
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse){
if(request.login === "logged"){
checkUser(request, sender, sendResponse);
// checkforValidUrl();
}
return true;
}
function checkUser(request, sender, sendResponse){
chrome.cookies.get({url : "https://www.flyship.com/app", name: "csrftoken"}, function(cookie){
const CSRF = cookie.value;
sendResponse({token: CSRF});
console.log(CSRF);
});
}
I need the extension to be greyed out on all the other sites except the mentioned sites in manifest.json that too only when the user is logged in to flyship.com.
P.S : i have tried with declarativeContent but it doesn't seem to be working for me or maybe atleast am not able to deduce it according to my needs. following is the code that iam checking the urls on
function checkforValidUrl(){
chrome.runtime.onInstalled.addListener(function() {
chrome.declarativeContent.onPageChanged.removeRules(undefined, function() {
chrome.declarativeContent.onPageChanged.addRules([
{
conditions: [
new chrome.declarativeContent.PageStateMatcher({
pageUrl: { urlContains: '*://*.flyship.com/*' },
}),
new chrome.declarativeContent.PageStateMatcher({
pageUrl: { urlContains: '*://app.yield.io/*' }
}),
new chrome.declarativeContent.PageStateMatcher({
pageUrl: { urlContains: 'https://www.indeedjobs.com/*' }
})
],
actions: [ new chrome.declarativeContent.ShowPageAction() ]
}
]);
});
});
}
I have been banging my head since 1 week to solve this problem reading docs and what not.A polite help in this regard would be very helpful.
I'm the author of Intab, a Chrome extension that lets you view a link inline as opposed to a new tab. There's not much fancy stuff going on behind the scenes, it's just an iframe that loads the URL the user clicked on.
It works great except for sites that set the X-Frame-Options header to DENY or SAMEORIGIN. Some really big sites like Google and Facebook both use it which makes for a slightly janky experience.
Is there any way to get around this? Since I'm using a Chrome extension, is there any browser level stuff I can access that might help? Looking for any ideas or help!
Chrome offers the webRequest API to intercept and modify HTTP requests. You can remove the X-Frame-Options header to allow inlining pages within an iframe.
chrome.webRequest.onHeadersReceived.addListener(
function(info) {
var headers = info.responseHeaders;
for (var i=headers.length-1; i>=0; --i) {
var header = headers[i].name.toLowerCase();
if (header == 'x-frame-options' || header == 'frame-options') {
headers.splice(i, 1); // Remove header
}
}
return {responseHeaders: headers};
}, {
urls: [
'*://*/*', // Pattern to match all http(s) pages
// '*://*.example.org/*', // Pattern to match one http(s) site
],
types: [ 'sub_frame' ]
}, [
'blocking',
'responseHeaders',
// Modern Chrome needs 'extraHeaders' to see and change this header,
// so the following code evaluates to 'extraHeaders' only in modern Chrome.
chrome.webRequest.OnHeadersReceivedOptions.EXTRA_HEADERS,
].filter(Boolean)
);
In the manifest, you need to specify the webRequest and webRequestBlocking permissions, plus the URLs patterns you're intending to intercept i.e. "*://*/*" or "*://www.example.org/*" for the example above.
ManifestV3 example using declarativeNetRequest
See also the warning at the end of this answer!
manifest.json for Chrome 96 and newer,
doesn't show a separate permission for "Block page content" during installation
"minimum_chrome_version": "96",
"permissions": ["declarativeNetRequestWithHostAccess"],
"host_permissions": ["*://*.example.com/"],
"background": {"service_worker": "bg.js"},
bg.js for Chrome 101 and newer using initiatorDomains and requestDomains
(don't forget to add "minimum_chrome_version": "101" in manifest.json)
const iframeHosts = [
'example.com',
];
chrome.runtime.onInstalled.addListener(() => {
const RULE = {
id: 1,
condition: {
initiatorDomains: [chrome.runtime.id],
requestDomains: iframeHosts,
resourceTypes: ['sub_frame'],
},
action: {
type: 'modifyHeaders',
responseHeaders: [
{header: 'X-Frame-Options', operation: 'remove'},
{header: 'Frame-Options', operation: 'remove'},
],
},
};
chrome.declarativeNetRequest.updateDynamicRules({
removeRuleIds: [RULE.id],
addRules: [RULE],
});
});
Old Chrome 84-100
Use the following instead, if your extension should be compatible with these old versions.
manifest.json for Chrome 84 and newer,
shows a separate permission for "Block page content" during installation
"permissions": ["declarativeNetRequest"],
"host_permissions": ["*://*.example.com/"],
"background": {"service_worker": "bg.js"},
bg.js for Chrome 84 and newer using the now deprecated domains
const iframeHosts = [
'example.com',
];
chrome.runtime.onInstalled.addListener(() => {
chrome.declarativeNetRequest.updateDynamicRules({
removeRuleIds: iframeHosts.map((h, i) => i + 1),
addRules: iframeHosts.map((h, i) => ({
id: i + 1,
condition: {
domains: [chrome.runtime.id],
urlFilter: `||${h}/`,
resourceTypes: ['sub_frame'],
},
action: {
type: 'modifyHeaders',
responseHeaders: [
{header: 'X-Frame-Options', operation: 'remove'},
{header: 'Frame-Options', operation: 'remove'},
],
},
})),
});
});
Warning: beware of site's service worker
You may have to remove the service worker of the site(s) and clear its cache before adding the iframe or before opening the extension page because many modern sites use the service worker to create the page without making a network request thus ignoring our header-stripping rule.
Add "browsingData" to "permissions" in manifest.json
Clear the SW:
function removeSW(url) {
return chrome.browsingData.remove({
origins: [new URL(url).origin],
}, {
cacheStorage: true,
serviceWorkers: true,
});
}
// If you add an iframe element in DOM:
async function addIframe(url, parent = document.body) {
await removeSW(url);
const el = document.createElement('iframe');
parent.appendChild(el);
el.src = url;
return el;
}
// If you open an extension page with an <iframe> element in its HTML:
async function openPage(url) {
await removeSW('https://example.com/');
return chrome.tabs.create({url});
}
You can try the Frame extension that lets the user drop X-Frame-Options and Content-Security-Policy HTTP response headers, allowing pages to be iframed.
The code is available on github
It's based on ManifestV3 and working perfectly with Google & Facebook.
this is my background.js
chrome.runtime.onInstalled.addListener(function() {
chrome.declarativeContent.onPageChanged.removeRules(undefined, function() {
chrome.declarativeContent.onPageChanged.addRules([{
conditions: [
// When a page contains a <video> tag...
new chrome.declarativeContent.PageStateMatcher({
pageUrl: { hostEquals: 'www.youtube.com'}
})
],
// ... show the page action.
actions: [new chrome.declarativeContent.ShowPageAction() ]
}]);
});
});
where should I insert my ajax? and how to use jquery? like $.post in my background.js?
I am trying to make a Chrome extension that blocks URLs with a specific word in the subdomain, but not other URLs in that domain. For example, let's say I want to block all tumblr blogs with the word "cola" in the subdomain.
It should be able to block this page: http://coca-cola.tumblr.com/.
I have tried to use this url match: urls:["*://*cola*.tumblr.com/*"], but it is not working. And I cannot think of any other combinations that might work. Can somebody point me in the right direction?
This is my full background.js:
chrome.webRequest.onBeforeRequest.addListener(
function() {
return {cancel: true };
},
{
urls:["*://*cola*.tumblr.com/*"] // This is the part I'm messing up.
},
["blocking"]
);
Your code fails because *://*cola*.tumblr.com/* is not a valid match pattern. Wildcards can only be used in the path component of an URL, or at the start of a host name.
If you want to block an URL whose subdomain contains some keyword, you need to match the whole domain, and use JavaScript to check whether the subdomain contains the profane word.
chrome.webRequest.onBeforeRequest.addListener(
function(details) {
var hostname = details.url.split('/', 3)[2];
return {
cancel: hostname.indexOf('cola') >= 0
};
},
{
urls:["*://*.tumblr.com/*"]
},
["blocking"]
);
Or using the chrome.declarativeWebRequest API (omitted chrome.runtime.onInstalled event for brevity):
chrome.declarativeWebRequest.onRequest.addRules({
id: 'some rule id',
conditions: [
new chrome.declarativeWebRequest.RequestMatcher({
url: {
hostContains: 'cola',
hostSuffix: '.tumblr.com'
}
})
],
actions: [
new chrome.declarativeWebRequest.CancelRequest()
]
});