Make chrome extension work only on certain sites (manifest v3) - google-chrome

I ran into some trouble when upgrading to manifest v3, and would greatly appreciate your help and input.
Background:
Using manifest v3
Using ShowAction() instead of ShowPageAction() which is deprecated
Used to work with manifest v2 and ShowPageAction()
Already read this post which did not apply to manifest v3 (or seemed like it didn't apply)
Also followed this google guide for upgrading from ShowPageAction to ShowAction
After following chrome's tutorial, which is reposted below:
// background.js
// Wrap in an onInstalled callback in order to avoid unnecessary work
// every time the background script is run
chrome.runtime.onInstalled.addListener(() => {
// Page actions are disabled by default and enabled on select tabs
chrome.action.disable();
// Clear all rules to ensure only our expected rules are set
chrome.declarativeContent.onPageChanged.removeRules(undefined, () => {
// Declare a rule to enable the action on example.com pages
let exampleRule = {
conditions: [
new chrome.declarativeContent.PageStateMatcher({
pageUrl: {hostSuffix: '.example.com'},
})
],
actions: [new chrome.declarativeContent.ShowAction()],
};
// Finally, apply our new array of rules
let rules = [exampleRule];
chrome.declarativeContent.onPageChanged.addRules(rules);
});
});
I noticed that the icon on my extension grey's out on sites that don't match the specified pattern, and has color on sites that do match the url pattern (expected behavior). However, when I click on the extension on sites that match the url pattern, the extension remains disabled.
Question: Has anyone been able to get this sample code to work? How would one make a chrome extension work only when user is on specific site, and has clicked on the extension?
Thanks in advance!

You just need to add "declarativeContent" into manifest.json permissions.
"permissions": [
"declarativeContent"
],

Related

How does a Chrome web extension use chrome.scripting.executeScript to inject a javascript function into a webpage?

I have a Chrome Manifest V3 web extension that needs to inject javascript into a webpage on page load of every page (not fussy as to exactly when the javascript is run).
This is so that javascript on the webpage can optionally call the function defined by the web extension.
According to the docs at https://developer.chrome.com/docs/extensions/reference/scripting/, the chrome.scripting.executeScript will do what I need:
You can use the chrome.scripting API to inject JavaScript and CSS into websites.
The chrome.scripting.executeScript function takes a tabId as a mandatory parameter, but nothing in the docs indicates where to get the tabId from. The docs make reference to an unspecified function getTabId(), but otherwise say nothing.
In addition, the docs do not say where the chrome.scripting.executeScript function is to be run. In the content script? In theory that won't work as the tab ID is not accessible. In the background service worker? What happens if the service worker is not running when the page is loaded?
Note that the following questions are not related to this question:
How would I inject code from my Chrome extension into a webpage? (closed, no reference to Manifest v3)
Use a content script to access the page context variables and functions (opposite question)
How to access the webpage DOM rather than the extension page DOM? (nothing to do with the DOM)
To get the tabId --
In Manifest V3, you pass it as a value.
chrome.action.onClicked.addListener((tab) => {
if(!tab.url.includes("chrome://")) {
chrome.scripting.executeScript({
target: { tabId: tab.id },
function: reddenPage
});
}
});
Check out Google's Chrome Extension example "Page Redder" for the full code

Chrome manifest Google URL

I have been trying to look for a better alternative, but I can't seem to find a better way to do this.
My current Chrome Extension manifest has about 300 lines of code that includes these kinds of URL's.
"http://www.google.com/webhp*", "https://www.google.com/webhp*",
"http://www.google.ad/webhp*", "https://www.google.ad/webhp*",
"http://www.google.ae/webhp*", "https://www.google.ae/webhp*",
"http://www.google.com.af/webhp*", "https://www.google.com.af/webhp*",
"http://www.google.com.ag/webhp*", "https://www.google.com.ag/webhp*",
"http://www.google.com.ai/webhp*", "https://www.google.com.ai/webhp*",
"http://www.google.am/webhp*", "https://www.google.am/webhp*",
"http://www.google.co.ao/webhp*", "https://www.google.co.ao/webhp*",
"http://www.google.com.ar/webhp*", "https://www.google.com.ar/webhp*",
"http://www.google.as/webhp*", "https://www.google.as/webhp*",
"http://www.google.at/webhp*", "https://www.google.at/webhp*",
I need to match URL's that run my script for https://www.google.com/ (exact), https://www.google.com/webph* (alternative for Google homepage) and https://www.google.com/search* (to match the search tabs that I want: images, videos, shopping, etc.)
The main problem lies with the fact that I can't use a wildcard for the domain extension (.com/.de/.org).
There has to be a better way right? My current manifest looks like a disaster.
I think you will always need to have your URL specifications somewhere, there is however an option to have it match in a broader way by moving it to JavaScript, and this gives you the option to move it away from the manifest if you desire.
In your manifest, simply declare the extension to inject the background script on all URLs:
"content_scripts": [{
"matches": ["http://*/*", "https://*/*"]
}]
And then in your background script, define where the extension should work, example:
var match = 'www.google.';
var excludes = ['maps', 'whatever'];
chrome.tabs.onUpdated.addListener(function(id, info, tab) {
if (tab.status !== "complete"){
return;
}
if(tab.url.indexOf(match) !== -1 && excludes.indexOf(tab.url) === -1){
// inject your script
chrome.tabs.executeScript(tab.id, {"file": "myScript.js"});
}
}

Why can't you create Custom Elements in content scripts?

I attempted to create a custom element in a Chrome extension content script but customElements.define is null.
customElements.define('customElement', class extends HTMLElement {
constructor() {
super();
}
...
});
So apparently Chrome doesn't want content scripts to create custom elements. But why? Is it a security risk?
I can't seem to find anything in Chrome's extension guide that says it's not allowed.
I found the solution reading this page but the information was so cumbersome I wanted to write this answer for future readers (I am using Manifest v3)
Firstly, install the polyfill :
npm install #webcomponents/webcomponentsjs -D
Then add the polyfill in your content_scripts block in your manifest file :
"content_scripts": [{
"matches": [ "..." ],
"js": [
"./node_modules/#webcomponents/webcomponentsjs/webcomponents-bundle.js",
"content.js"
]
}]
(important: you have to load it before your content script of course as the polyfill needs to load before you can use it)
Now it should works. Cheers
Note: the customElements feature is implemented in most modern browsers but for some reasons the interface is not available from a content script because the scripts are run in an isolated environment (not sharing the same window object space from the webpage the extension runs in).
As of now custom element can be used in chrome extensions UI. In Popup ui, option page ui and in the content script as well But it requires a polyfill which is this.
https://github.com/GoogleChromeLabs/ProjectVisBug - this is the one big custom element in the chrome extension.

Checking tab status in a chrome extension with a popup without the tabs permission

I'm currently trying to build my first chrome extension and I only need it to interact with pages of a few domains, so I want to avoid using the "tabs" permission since I understand it would have me request access to all information and all domains.
Instead I want to restrict myself to using the aciveTab permission and, if need be, a content script.
In short, what I want to do is display a "Subscribe button" in my extension's popup if the currently selected tab's url is of the domain(s) I'm interested in.
I can get the url of the page when it's created using a content script but I don't know how to make sure the user is still on that page when my extension is clicked.
I haven't managed to get anything done with activeTab.
Thanks in advance for any piece of advice you can give, I'll check on the answers (if any) after work.
A working example with the activeTab permisison:
In your popup.js
chrome.tabs.query({lastFocusedWindow: true, active: true}, function(tabs) {
if (tabs && tabs[0] && tabs[0].url) {
var match = tabs[0].url.match(/^[^:]+:\/\/([^\/]+)/);
if (match) {
var domain = match[1];
if (domain == 'stackoverflow.com')
alert('test');
}
}
});
Note:
You have to declare the "activeTab" permission in your manifest (of course).
JavaScript code must be in a standalone file and included in popup.html with <script src="..."></script>. Inline JavaScript is not allowed due to CSP.

Could I make a Google Chrome extension for chrome pages (downloads, extensions etc)?

I'd like to make a very simple extensions that slightly alters how the Downloads page looks. Changing the History page might be interesting too, but that's for later.
Is there a way to do that?
I tried making a Content Script extension, with "chrome://downloads" as match in manifest.json. Chrome won't allow that and responds with an error when packaging the extension.
Is there another simple way? It has to be simple, because changes would be simple, because all chrome:// pages are built with HTML, JS and CSS.
edit
After trying with background scripts a little...
I can't get chrome.tabs.executeScript to work! I added in background.html:
chrome.browserAction.onClicked.addListener(function(tab) {
alert(this.document.body.innerHTML);
alert(chrome.tabs.executeScript(null, {
code : "document.body.style.backgroundColor = 'red';"
}));
});
And I added this in manifest.json to add a (invisible) 'browser action button':
,"browser_action": {
/* "popup": "background.html",*/
"name": "Alter page"
}
The onClicked event fires both alerts (first is background.html's body, second is undefined). But the code (a string with document.body.style.backgroundColor = 'red';) doesn't execute! And ofcourse there's no debugging for extensions like this =)
Any tips anyone? I'm trying to get a hold of the tab's window.document (not background.html's window.document!). An injected script (that's what chrome.tabs.executeScript is supposed to do) should do that.
PS
I'm stealing from make_page_red/manifest and make_page_red/background.html
The 'extension' I have so far: http://hotblocks.nl/js/downloads.rar
EDIT
I found out what I want to achieve is possible with just CSS. I don't need to inject javascript. Does that make it easier? Does that make it possible? =)
According to this documentation, chrome:// URLs are an invalid scheme so they won't be matched:
A match pattern is essentially a URL that begins with a permitted scheme (http, https, file, or ftp), and that can contain '*' characters.
I would look into using override pages instead.
As requested, here's my extension that can at least load when chrome://downloads is loaded, although as I said, I don't think you can modify the page even if you know that's the page you're viewing.
manifest.json
{
"name": "Test",
"version": "0.0.1",
"background_page": "background.html",
"permissions": [
"tabs"
]
}
background.html
<script>
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab)
{
if (tab.status == "complete")
{
alert(tab.url);
// should alert 'chrome://downloads' on that page. You can
// check for this url here and then do whatever you want
}
});
</script>
Update: Since Chrome 31 there is an API for extensions that allows access to Chrome's downloads: https://developer.chrome.com/extensions/downloads
There's also an API that allows access to list and manage other installed extensions: https://developer.chrome.com/extensions/management
(Previous Answer)
Unfortunately, there's not currently an API for Chrome extensions to access information about a user's downloads. It's a widely requested feature, though, and there's some discussion among Chrome developers here: http://code.google.com/p/chromium/issues/detail?id=12133
Star the issue if it's a feature that you'd like to see, and you'll receive email updates.
As this page shows, there is no API to override the downloads page... However, there is a way to make a file you have made replace the chrome://downloads/ page whenever it is loaded using javascript in your background page...
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab){
if(changeInfo.status === "loading"){
if(tab.url === "chrome://downloads/"){
chrome.tabs.update(tab.id, {url: "REPLACEMENT.html"});
}
}
});
Essentially what this does is - As soon as the page chrome://downloads begins loading (using the tabs.onUpdated API), the page is redirected to REPLACEMENT.html (Using tabs.update API)... There is no visible delay in the tab update
as this script is run before the chrome://downloads page begins loading... You can use a similar code in your file by pressing CTRL + U on the downloads page to view and copy its source code