I'm trying to implement an extension that can auto fill all fields in an application website. Currently it works great in main frame, but doesn't work if the fields are in a iframe.
I'm allowing users to either use content script to auto inject JS or click a button to inject JS manually.
The problem I have is my JS is not injecting into iframe even I set allFrames to true, but content script work.
"content_scripts" : [
{
"matches" : ["https://*/my_url/*"],
"js" : ["/auto_fill.js"],
"all_frames": true
}
]
This content scripts work fine, JS is being injected. However,
my_button.addEventListener('click', () => {
chrome.tabs.executeScript(null, {
file : "/auto_fill.js",
allFrames : true
})
});
This executeScript doesn't work, nothing happen.
The iframe I'm trying to work with is kinda a dynamic iframe (where I have to click few buttons to load and navigate to the application site I want)
Do I have to find out the iframe id or tab id in order for this to work, please provide some hints.
Please let me know if I'm not making myself clear.
Thank you so much for your time.
As the comments revealed, the extension is using the activeTab permission without host URL patterns (just as the documentation suggests), but this permission only grants access to the tab's main URL. When an iframe points to a different URL origin, it won't be granted by activeTab, which is an intentional restriction enforced since Chrome 45, see https://crbug.com/826433.
Related
chrome.tabs returns undefined despite the fact I set tabs in the permissions block.
"permissions": [
"tabs",
"http://*/*",
"https://*/*"
],
"content_scripts": [
{
"matches": [
"http://*/*",
"https://*/*"
],
"js": [
"js/myScript.js"
],
"all_frames": true
}
],
But in myScript.js the following returns undefined.
chrome.tabs
As content script has its own limitations,
chrome.tabs is only available in background scripts and popup scripts.
If you wanna to use chrome.tabs then pass message from content_script to background script and play with chrome.tabs.
Content scripts have only limited access to Chrome APIs. This access does not include the API you are trying to use (e.g. chrome.tabs). If you need to use that API, you will have to do so in a background script1.
As listed in Chrome's content scripts documentation, the APIs available to a content script are [I have placed deprecated methods in strikethrough format]:
extension ( getURL , inIncognitoContext , lastError , onRequest , sendRequest )
i18n
runtime ( connect , getManifest , getURL , id , onConnect , onMessage , sendMessage )
storage
A couple of the listed APIs are deprecated and have been for some time. Those that are deprecated have moved to different locations (also listed above):
extension.onRequest ➞ runtime.onMessage
extension.sendRequest ➞ runtime.sendMessage
While not officially deprecated, extension.lastError is also available as runtime.lastError. At this point, it is usually referred to at that location:
extension.lastError ➞ runtime.lastError
Partition your extension into background scripts and content scripts
You are going to need to separate your code into what needs to be in a background script and what needs to be in content scripts, based on the capabilities available to each type of script. Content scripts have access to the DOM of the web page into which they are injected, but limited access to extension APIs. Background scripts have full access to the extension APIs, but no access to web page content. You should read the Chrome extension overview, and the pages linked from there, to get a feel for what functionality should be located in which type of script.
It is common to need to communicate between your content scripts and background scripts. To do so you can use message passing. This allows you to communicate information between the two scripts to accomplish things which are not possible using only one type of script.
For instance, in your content script, you may need information which is only available from one of the other Chrome APIs, or you need something to happen which can most appropriately (or only) be done through one of the other Chrome extension APIs. In these cases, you will need to send a message to your background script, using chrome.runtime.sendMessage(), to tell it what needs to be done, while providing enough informaiton for it to be able to do so. Your background script can then return the desired information, if any, to your content script. Alternately, you will have times when the processing will primarily be done in the background script. The background script may inject a content script, or just message an already injected script, to obtain information from a page, or make changes to the web page.
Background script means any script that is in the background context. In addition to actual background scripts, this includes popups and options pages, etc. However, the only page that you can be sure to have consistently available to receive messages from a content script are your actual background scripts defined in manifest.json. Other pages may be available at some times as a result of the user's interaction with the browser, but they are not available consistently.
This answer was moved from a duplicate question, and then modified.
https://developer.chrome.com/extensions/tabs#method-getSelected shows
getSelected
chrome.tabs.getSelected(integer windowId, function
callback)
Deprecated since Chrome 33. Please use tabs.query {active: true}.
Gets the tab that is selected in the specified window.
Maybe, you should use chrome.tabs.query in popup.js like this
chrome.tabs.query({active: true, currentWindow: true}, function(tabs){
console.log(tabs[0].url);
});
, reload your extension and check the result in the inspect element of your extension.
result image
code image
https://developer.chrome.com/extensions/tabs#type-Tab shows that
The URL the tab is displaying. This property is only present if the extension's manifest includes the "tabs" permission.(Just for remind someone forgot. I was forgot it when I just test it.)
Check this answer also https://stackoverflow.com/a/6718277/449345
This one worked for me
chrome.tabs.getSelected(null, function(tab){
console.log(tab);
});
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.
I am trying to write an extension that adds functionality to the Chrome devtools.
According to the devtools documentation, it says that the pages in devtools support very limited apis. Any API that is not supported can be access by accessing it through the background page, just as what contentscripts does.
Here is the relevant documentation snippet:
The tabId property provides the tab identifier that you can use with the chrome.tabs.* API calls. However, please note that chrome.tabs.* API is not exposed to the Developer Tools extension pages due to security considerations — you will need to pass the tab ID to the background page and invoke the chrome.tabs.* API functions from there.
Here is the source url: http://developer.chrome.com/extensions/devtools.inspectedWindow.html
However, when I try to do that, I get the following error in the console:
uncaught Error: "getBackgroundPage" can only be used in extension processes. See the content scripts documentation for more details.
Here is my code in my devtools.js script:
chrome.extension.getBackgroundPage().getLocation();
What am I doing wrong?
EDIT
I should describe my scenario first, and show how I am implementing it.
What I want to do is to display extra data in a devtools panel related to a webpage. In order to get that data, I will need to send a HTTP request in the same session as the page being debugged, because it requires authentication.
Use Case:
User browses to a particular URL. He is authenticated to the site. He then invokes devtools. The devtools panel opens up and a new panel shows up that has extra data related to the page.
Implementation:
1) DevTools script finds out the url of the page being inspected. If the url matches the site base hostname, then it opens a panel. In the callback of the panel creation, it sends a message to a background page, asking it to download a JSON payload from a debug endpoint on the same site, and then sends it to the devtools extension, wh ich then displays it.
Problems:
1) The background page gets the request, and downloads the URL. However the download is not using the same session as the user, so the download request fails.
2) From devtools window, I got the tabId of the inspected window. I send this tabId to the background page so that it can parse some stuff out of the url. However, chrome.tabs.get(tabId) does not return the tab.
To summarize, I need to
1) Get the background page to download data in the same session as the user's tab that is being debugged.
2) I need to have the background page be able to get access to the user's tab.
The APIs available to extension pages within the Developer Tools window include all devtools modules listed above and chrome.extension API. Other extension APIs are not available to the Developer Tools pages, but you may invoke them by sending a request to the background page of your extension, similarly to how it's done in the content scripts.
I guess the documentation is little ambiguous, By chrome.extension API they mean the Supported API's for content scripts.
So, you can use long lived communication for communication between inspected page and background page
Demonstration:
The following code illustrate scenario where a devtools page need some information from background page, it uses messages for communication.
manifest.json
Ensured permissions are all available in manifest file
{
"name":"Inspected Windows Demo",
"description":"This demonstrates Inspected window API",
"devtools_page":"devtools.html",
"manifest_version":2,
"version":"2",
"permissions":["experimental"],
"background":{
"scripts" : ["background.js"]
}
}
devtools.html
A trivial HTML File
<html>
<head>
<script src="devtools.js"></script>
</head>
<body>
</body>
</html>
devtools.js
Used Long lived Communication API's
var port = chrome.extension.connect({
name: "Sample Communication"
});
port.postMessage("Request Tab Data");
port.onMessage.addListener(function (msg) {
console.log("Tab Data recieved is " + msg);
});
background.js
Responded to communication request and passed trivial information using tab API()'s
chrome.extension.onConnect.addListener(function (port) {
port.onMessage.addListener(function (message) {
chrome.tabs.query({
"status": "complete",
"currentWindow": true,
"active": true
}, function (tabs) {
port.postMessage(tabs[0].id);
});
console.log("Message recived is "+message);
});
});
Sample Output received for trivial devtools.js here
Let me know if you need more information
EDIT 1)
For your question 1)
Can you make you call(s) from browser extension HTML Page\Content Script so same session is shared, i have tried both the ways in a sample and it is working form me, instead of code in background page- make the code in content script or browser action HTML Page.
Let me know if you are still facing problems.
For your question 2)
The following code always fetches current window user is browsing
manifest.json
Ensure you have tabs permission in your manifest.
{
"name":"Inspected Windows Demo",
"description":"This demonstrates Inspected window API",
"manifest_version":2,
"version":"2",
"permissions":["tabs"],
"background":{
"scripts" : ["background.js"]
}
}
background.js
chrome.tabs.query({
"status": "complete", // Window load is completed
"currentWindow": true, // It is in current window
"active": true //Window user is browsing
}, function (tabs) {
for (tab in tabs) { // It returns array so used a loop to iterate over items
console.log(tabs[tab].id); // Catch tab id
}
});
Let me know if you are still unable to get tab id of current window.
I want to "proxy" web pages through a chrome-extension:// page (using an full-frame iframe), so that they show up as an extension page, instead of a webpage. I still want to apply content scripts to them, however, and have not had any success doing this. Is there a way to allow the chrome-extension page to access the contents of the iframe because it's "privileged" and should be allowed to get around XSS limitations?
Whenever I try to executeScript( { code : blah , allFrames: true }) I get an error about XSS about the chrome-extenion page, not the http page inside it...
Ideas?
I am trying to learn to use the chrome.tabs.executeScript commend. I've created a simple extension with a browser action. My background.html file currently looks like this:
<html>
<script>
chrome.browserAction.onClicked.addListener(function(tab) {
chrome.tabs.executeScript(null,{code:"document.body.bgColor='red'"});
chrome.tabs.executeScript(null, {file: "content_script.js"});
});
</script>
</html>
The "content_script.js" file contains document.body.bgColor='red'.
When pushing the browser action's button nothing happens. Obviously I'm missing something very basic.
I've checked with console.log that indeed control reaches the chrome.tabs.executeScript calls when the browser action is pressed. Otherwise I'm not sure how to even check if my content script's code is run (it seems not; console.log I put in the content script has no effect, but maybe it shouldn't have one even if the script is run successfully).
Make sure you have domain and tab permissions in the manifest:
"permissions": [
"tabs", "http://*/*", "https://*/*"
]
Then to change body color try:
chrome.tabs.executeScript(null,{code:"document.body.style.backgroundColor='red'"});
Also keep in mind that content scripts are not injected into any chrome:// or extension gallery pages.
For those of you still having issues, you need to make sure to reload the extension's permissions in Chrome.
Go to chrome://extensions , scroll to your extension, and click on "reload". Make sure that your permissions have been updated by clicking on the permissions link right next to your extension.
You actually don't need and don't want the 'tabs' permission for executeScript.
"permissions": [
"http://*/*",
"https://*/*"
]
Should be enough.
It's not recommended to use http://*/* and https://*/*. From the Google documentation:
To inject a programmatic content script, provide the activeTab permission in the manifest. This grants secure access to the active site's host and temporary access to the tabs permission, enabling the content script to run on the current active tab without specifying cross-origin permissions.
Instead, (as suggested in the page) just use activeTab permission.
Remark: more explanation for the security issue
Without activeTab, this extension would need to request full, persistent access to every web site, just so that it could do its work if it happened to be called upon by the user. This is a lot of power to entrust to such a simple extension. And if the extension is ever compromised, the attacker gets access to everything the extension had.
In contrast, an extension with the activeTab permission only obtains access to a tab in response to an explicit user gesture. If the extension is compromised the attacker would need to wait for the user to invoke the extension before obtaining access. And that access only lasts until the tab is navigated or is closed.
(emphasis mine)
In the example code posted by the OP, activeTab is sufficient.
However, if the extension is more complex and needs to work "automatically" (i.e. without the user clicking the button); then this method will not work and additional permission is required.
Most of the answers above seems to be working fine for manifest version 2 but when it comes manifest-3 their seems to be some workaround to make the content-script load in the latest manifest 3.We need to use the following steps to execute content script in manifest 3
First adding permission "scripting" in manifest
"permissions": [
"storage",
"tabs",
"activeTab",
"scripting"
]
Once the scripting perimission is provided, we can use the scripting api like below
In background.js,
chrome.tabs.query({}, (tabList) => {
if (!tabList.length) return;
tabList.forEach((tab) => {
chrome.scripting.executeScript(
{
files: ['contentScript.js'],
target: {
tabId: tab.id,
allFrames: true
}
}
);
});
});
In the above code we are executing the contentScript for all the available tabs in tab browser.