I wrote my small extension for Chrome which works in the context of the webpage.
Everything is fine, except that code is executed every time when I visit URL defined in the manifest.json in content-scripts matches.
What I would like is to launch it manually - 'on demand' - after clicking on the icon of the extension next to the url bar.
Is this possible?
Yes - it is possible.
I've took it from: http://developer.chrome.com/extensions/content_scripts.html
The relevant part is:
"...To insert code into a page, your extension must have cross-origin permissions for the page. It also must be able to use the chrome.tabs module. You can get both kinds of permission using the manifest file's permissions field. Once you have permissions set up, you can inject JavaScript into a page by calling executeScript()..."
/* in background.html */
chrome.browserAction.onClicked.addListener(function(tab) {
chrome.tabs.executeScript(null,
{code:"document.body.bgColor='red'"});
});
Related
How would I overwrite the response body for an image with a dynamic value in a Manifest V3 Chrome extension?
This overwrite would happen in the background, as per the Firefox example, (see below) meaning no attaching debuggers or requiring users to press a button every time the page loads to modify the response.
I'm creating a web extension that would store an image in the extension's IndexedDB storage and then override the response body with that image on requests to a certain image. A redirect to a dataurl: I have it working in a Manifest V2 extension in Firefox via the browser.webRequest.onBeforeRequest api with the following code, but browser.webRequest and MV2 are depreciated in Chrome. In MV3, browser.webRequest was replaced with browser.declarativeNetRequest, but it doesn't have the same level of access, as you can only redirect and modify headers, not the body.
Firefox-compatible example:
browser.webRequest.onBeforeRequest.addListener(
(details) => {
const request = browser.webRequest.filterResponseData(details.requestId);
request.onstart = async () => {
request.write(racetrack);
request.disconnect();
};
},
{
urls: ['https://www.example.com/image.png'],
},
['requestBody', 'blocking']
);
The Firefox solution is the only one that worked for me, albeit being exlusive to Firefox. I attempted to write a POC userscript with xhook to modify the content of a DOM image element, but it didn't seem to return the modified image as expected. Previously, I tried using a redirect to a data URI and an external image, but while the redirect worked fine, the website threw an error that it couldn't load the required resources.
I'm guessing I'm going to have to write a content script that injects a Service Worker (unexplored territory for me) into the page and create a page rule that redirects, say /extension-injected-sw.js to either a web-available script, but I'm not too sure about how to pull that off, or if I'd still be able to have the service worker communicate with the extension, or if that would even work at all. Or is there a better way to do this that I'm overlooking?
Thank you for your time!
I am not talking about executing a content script conditionally.
I know how to do that via if (...) { chrome.scripting.executeScript(...) }
I'm talking about not even having the content.js file available in the page's source code unless a condition is met.
In order for a Chrome Extension content script to run on a page, I need to declare it in the manifest like so:
"content_scripts": [
{
"matches": ["https://allowed-host/*"],
"js": [ "content.js" ]
}
]
However, with the above, as soon as the user goes to https://allowed-host/* the content.js file becomes available on the Sources panel from what I can see (even if my extension chooses not to use it just yet).
I'm trying out https://extensionpay.com/ which is supposed to allow you to create subscription-based chrome extensions.
Inside background.js you can use the ExtPay API to see if the user has paid or not and execute code accordingly.
But my Chrome Extension relies on content scripts running on the page and manipulating the DOM and those script files themselves are available for anyone to view as soon as the relevant page loads, regardless of subscriptions etc.
Is there a way to prevent that ?
I understand I could set up my own server for this but I'm wondering if there is a no-backend-involved solution, only through the Chrome Extension API, where I could load a file on the page conditionally. Again, I'm not talking about executing the file conditionally, but loading it on the page, and making its source code available for anyone to view conditionally.
Thanks for any help.
Is it possible to launch a Google Chrome extension within a website? E.g run some javascript that will launch the extensions UI?
I'm building a web-app that will allow users to take screenshots of their desktop and edit them. I've got a sample extension up and running using dektopCapture but it is an 'app' style of an extension.
It allows to select a window to stream from, then take a
snapshot within the extension UI(using a button) that is saved as an image string
My question is:
Is it possible to fire up the desktopCapture UI (the window that gets the available windows to stream from), from within my web-app, maybe a button, take the stream and place it on a canvas/HTML5 video element within my web-app?
I'm figuring that I could hook-up an event-listener within the extension and use runtime.onMessage to post a message from within my app
Notes:
If there's a more intuitive way to do this, I can go that route - e.g If I could keep as much interaction within the web-app with just the extension running in the background, that would be even better.
The extension is of type browser_action but I want it to be applicable to a single page(the app's webpage) so if it can be used in a page_action I'd prefer that instead. There's really no need to have browser_action icon if I can trigger this from within a webpage
I'm also planning to build a FF extension so any insights there are also appreciated.
So I'm answering my own question.
I've managed to get it working using externally_connectables.
The externally_connectable manifest property declares which
extensions, apps, and web pages can connect to your extension via
runtime.connect and runtime.sendMessage.
1. Declare app/webpage in manifest.json
Just declare your web-app/page within your manifest.json as an externally_connectable.
E.g I wanted to connect my app is hosted on Github Pages and I have a domain name of https://nicholaswmin.github.io, so it does a bit like this:
"externally_connectable": {
"matches": ["https://nicholaswmin.github.io/*"]
}, //rest of manifest.json
2. Set up event listener for messages in background.js
Then set up an event listener in your background.js like so:
chrome.runtime.onMessageExternal.addListener(function(request, sender, sendResponse) {
//Stuff you want to run goes here, even desktopCapture calls
});
3. Send message from your web/app page
And call it from within your web-app/website like this:
chrome.runtime.sendMessage("APP ID GOES HERE",
{data: { key : "capture"}});
Make sure that your website is correctly declared as an externally_connectable in your manifest.json and that you are passing the app-id when sending the message
Content Script can be injected programatically or permanently by declaring in Extension manifest file. Programatic injection require host permission, which is generally grant by browser or page action.
In my use case, I want to inject gmail, outlook.com and yahoo mail web site without user action. I can do by declaring all of them manifest, but by doing so require all data access to those account. Some use may want to grant only outlook.com, but not gmail. Programatic injection does not work because I need to know when to inject. Using tabs permission is also require another permission.
Is there any good way to optionally inject web site?
You cannot run code on a site without the appropriate permissions. Fortunately, you can add the host permissions to optional_permissions in the manifest file to declare them optional and still allow the extension to use them.
In response to a user gesture, you can use chrome.permission.request to request additional permissions. This API can only be used in extension pages (background page, popup page, options page, ...). As of Chrome 36.0.1957.0, the required user gesture also carries over from content scripts, so if you want to, you could add a click event listener from a content script and use chrome.runtime.sendMessage to send the request to the background page, which in turn calls chrome.permissions.request.
Optional code execution in tabs
After obtaining the host permissions (optional or mandatory), you have to somehow inject the content script (or CSS style) in the matching pages. There are a few options, in order of my preference:
Use the chrome.declarativeContent.RequestContentScript action to insert a content script in the page. Read the documentation if you want to learn how to use this API.
Use the webNavigation API (e.g. chrome.webNavigation.onCommitted) to detect when the user has navigated to the page, then use chrome.tabs.executeScript to insert the content script in the tab (or chrome.tabs.insertCSS to insert styles).
Use the tabs API (chrome.tabs.onUpdated) to detect that a page might have changed, and insert a content script in the page using chrome.tabs.executeScript.
I strongly recommend option 1, because it was specifically designed for this use case. Note: This API was added in Chrome 38, but only worked with optional permissions since Chrome 39. Despite the "WARNING: This action is still experimental and is not supported on stable builds of Chrome." in the documentation, the API is actually supported on stable. Initially the idea was to wait for a review before publishing the API on stable, but that review never came and so now this API has been working fine for almost two years.
The second and third options are similar. The difference between the two is that using the webNavigation API adds an additional permission warning ("Read your browsing history"). For this warning, you get an API that can efficiently filter the navigations, so the number of chrome.tabs.executeScript calls can be minimized.
If you don't want to put this extra permission warning in your permission dialog, then you could blindly try to inject on every tab. If your extension has the permission, then the injection will succeed. Otherwise, it fails. This doesn't sound very efficient, and it is not... ...on the bright side, this method does not require any additional permissions.
By using either of the latter two methods, your content script must be designed in such a way that it can handle multiple insertions (e.g. with a guard). Inserting in frames is also supported (allFrames:true), but only if your extension is allowed to access the tab's URL (or the frame's URL if frameId is set).
I advise against using declarativeContent APIs because they're deprecated and buggy with CSS, as described by the last comment on https://bugs.chromium.org/p/chromium/issues/detail?id=708115.
Use the new content script registration APIs instead. Here's what you need, in two parts:
Programmatic script injection
There's a new contentScripts.register() API which can programmatically register content scripts and they'll be loaded exactly like content_scripts defined in the manifest:
browser.contentScripts.register({
matches: ['https://your-dynamic-domain.example.com/*'],
js: [{file: 'content.js'}]
});
This API is only available in Firefox but there's a Chrome polyfill you can use. If you're using Manifest v3, there's the native chrome.scripting.registerContentScript which does the same thing but slightly differently.
Acquiring new permissions
By using chrome.permissions.request you can add new domains on which you can inject content scripts. An example would be:
// In a content script or options page
document.querySelector('button').addEventListener('click', () => {
chrome.permissions.request({
origins: ['https://your-dynamic-domain.example.com/*']
}, granted => {
if (granted) {
/* Use contentScripts.register */
}
});
});
And you'll have to add optional_permissions in your manifest.json to allow new origins to be requested:
{
"optional_permissions": [
"*://*/*"
]
}
In Manifest v3 this property was renamed to optional_host_permissions.
I also wrote some tools to further simplify this for you and for the end user, such as
webext-domain-permission-toggle and webext-dynamic-content-scripts. They will automatically register your scripts in the next browser launches and allow the user the remove the new permissions and scripts.
Since the existing answer is now a few years old, optional injection is now much easier and is described here. It says that to inject a new file conditionally, you can use the following code:
// The lines I have commented are in the documentation, but the uncommented
// lines are the important part
//chrome.runtime.onMessage.addListener((message, callback) => {
// if (message == “runContentScript”){
chrome.tabs.executeScript({
file: 'contentScript.js'
});
// }
//});
You will need the Active Tab Permission to do this.
maybe this question is a bit noob style but I don't understand this JavaScript stuff.
My question: how do I debug injected code of the following
chrome extension example?
The file popup.js executes send_links.js (this is the injected file, if I understand this correct). I would like to debug send_links.js. I cannot set any breakpoint because I cannot see send_links.js in the Developer Tools of Chrome. I tried the command "debugger;" in send_links.js but it does not work. "console.log("blah");" commands are ignored too.
Thank you!
The debugger keyword will work if you open the Developer Tools for the current tab before you press the action button.
Also, if you want the script to display with its name, add the following line anywhere in send_links.js:
//# sourceURL=send_links.js
Then the script will show up in the 'Content Scripts' tab of the Developer Tools of the current tab. There you can set breakpoints and such. But you need always to open the Developer Tools for the tab before pressing the button for this to work.
All Injected Files or Content Scripts are Exposed to Page Developer Tools, You can set breakpoints and all regular stuff you do on regular Java Script Pages.
(source: google.com)
All your console log(s) appear in the page they are injected.
Ex:
If i inject console.log(document.getElementsByTagName('body')[0].style); to http://www.google.co.in/, then i need to open devtools of http://www.google.co.in/ page and look in its console.
The Output appeared is similar to regular debugging result.
EDIT 1
They are exposed through chrome.tabs.executeScript() but indirectly, when you set debugger; command you can inspect code.
Demonstration
If some sample code injects
chrome.tabs.executeScript(35, {
"code": "console.log('hi');debugger;//# sourceURL=send_links.js"
});
debugger of page shows the script being injected.
I guess it's because you open the debugger tool on the tab and not on the extension. Right click on the icon of your extension and choose the Inspect popup menu item. You can find more informations on this page http://developer.chrome.com/extensions/tut_debugging.html
In this case the script is not injected until you open the popup. Once the popup window loads, it injects send_links.js, which in turn sends a message as soon as it is done getting the links. You could reverse this messaging and inject the file in the manifest:
"content_scripts": [{
"matches": ["<all_urls>"],
"js": ["send_links.js"]
}],
add an on message handler to send_links.js with support to send a response
chrome.extension.onMessage.addListener(function(message,sender,sendResponse){
[...]
sendResponse(links);
});
and then replace the onMessage handler and executeScript in the popup with a sendMessage callback
chrome.windows.getCurrent(function (currentWindow) {
chrome.tabs.query({active: true, windowId: currentWindow.id},function(tab) {
chrome.tabs.sendMessage(tab[0].id, {method: "getlinks"},function(links){
for (var index in links) {
allLinks.push(links[index]);
}
allLinks.sort();
visibleLinks = allLinks;
showLinks();
});
});
});
This arrangement will put send_links.js into every page so that you can debug it more easily. Once it is bug free you can switch back to programmatic injection because in cases like these it is more efficient.
You can find the script under Sources > Content Scripts > One of them (plfdheimenpnchlahmhicnkejgmhjhom for example).
Edited source files