I'm trying to execute a script in a new tab when that tab is opened.
In my background script I have:
var listener = function (tab) {
browser.tabs.executeScript(null, { file: "content_scripts/contentScript.js" });
}
browser.tabs.onCreated.addListener(listener);
In contentScript.js:
function foo() {
console.log("Executed");
}
foo();
From this I get the following error:
Error: Permission denied to access property "chrome"
If I simply execute code rather than call a js script, ex:
browser.tabs.executeScript(null, { code: "console.log("Executed") });
This does not cause the error. Any idea what is causing this?
It doesn't work properly because apparently this bug has not been fixed and still exists in Firefox 59.
You can work around the issue by letting the tab load for some milliseconds before running executeScript on it.
I have tested this an it works for me:
(Remember that this does not work for new blank tabs as in "about:newtab". These can not be accessed for security reasons.)
background.js
var listener = function (tab) {
setTimeout(()=>{
browser.tabs.executeScript(tab.id, {file: "/content_scripts/contentScript.js"}).then(()=>{
console.log("Executed!")
}).catch(err=>{
console.error("Not executed:")
console.error(err)
})
},1000) // Add a delay because of this bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1254003
}
browser.tabs.onCreated.addListener(listener);
content_scripts/contentScript.js
function foo() {
console.log("Executed");
alert("Executed!");
}
foo();
manifest.json
{
"description": "Test executeScript api",
"manifest_version": 2,
"name": "ExecuteScript-Test",
"version": "1.0",
"permissions": [
"activeTab",
"tabs",
"<all_urls>"
],
"background": {
"scripts": ["background.js"]
}
}
If FF developers docs it's said:
In Firefox, relative URLs passed into tabs.executeScript() or
tabs.insertCSS() are resolved relative to the current page URL. In
Chrome, these URLs are resolved relative to the add-on's base URL. To
work cross-browser, you can specify the path as an absolute URL,
starting at the add-on's root, like this:
/path/to/script.js
so this should do the trick, but in fact FF48 due to unknown reasons both insertCSS() and executeScript() don't want to execute. At the same time I've found if to execute code with these functions calls in debugger in step-by-step mode, they triggered normally. So it looks like FF bug.
Can't comment, so sorry this isn't a real answer ...
I'm not seeing the same results. Provided details in case anything jumps out at you as being different from how I ran it and how you are running it.
In background script (located in the extension root directory), on extension startup, both:
var temp = function (tab) {
browser.tabs.executeScript(null, { file: "src/js/asdf.js" });
};
browser.tabs.onCreated.addListener(temp);
and
browser.tabs.onCreated.addListener(function (tab) {
browser.tabs.executeScript(null, { file: "src/js/asdf.js" });
});
register correctly (no errors on startup).
The file src/js/asdf.js exists, is the correct relative path from background.js, and contains your foo method and call.
When I create a new blank tab (which by definition cannot have content scripts attached), I see the expected error in the console (albeit from undefined instead from background.js):
Error: Missing host permission for the tab
When I create a new blank tab with content from the beginning (i.e., context click to open a link in a new tab), I see the expected result (Executed in the console log).
When I create a new tab from within the extension, I also see the expected result (Executed in the console log.
Potentially relevant manifest info:
asdf.js is not web accessible
permissions include tabs and <all_urls>
there are no content scripts defined in manifest.json
Running Firefox 59.0.2 (64-bit) on Mac 10.13.4
Related
I have a popup.js:
function registerButtonAction(tabId, button, action) {
// clicking button will send a message to
// content script in the same tab as the popup
button.addEventListener('click', () => chrome.tabs.sendMessage(tabId, { [action]: true }));
}
function setupButtons(tabId) {
// add click actions to each 3 buttons
registerButtonAction(tabId, document.getElementById('start-btn'), 'startSearch');
registerButtonAction(tabId, document.getElementById('deals-btn'), 'startDeals');
}
function injectStartSearchScript() {
chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
// Injects JavaScript code into a page
// chrome.tabs.executeScript(tabs[0].id, { file: 'main.js' });
// dd click handlers for buttons
setupButtons(tabs[0].id);
});
}
injectStartSearchScript()
// document.getElementById('inject-btn').addEventListener('click', injectStartSearchScript);
Which I use to inject my script into the page with the "start-btn" inside my popup.html.
This is my main.js which includes my functions I would like to call on a page:
function pong() {
// do something
}
function ping() {
// do something else
}
my manifest.json:
{
"manifest_version": 2,
"name": "test app",
"description": "test desc",
"version": "1.0",
"browser_action": {
"default_icon": "icon.png",
"default_popup": "popup.html"
},
"permissions": ["tabs", "<all_urls>"],
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["main.js"]
}
]
}
So basically my setup is that I have a popup.html which includes 3 buttons and they should call one of the functions inside my main.js dpending on what button i press.
But I can not get that working. Currently I only can make at least one function call if I directly call pong() inside main.js on load. But I would need to call one of the functions after I click on a button inside my popup.html.
EDIT: I have updated the code as far as I understood. I am very sorry but I don't understand what would be needed to be changed else to fulfill your proposal. I mean how to write it to be more correct.
EDIT 2: I have removed the line chrome.tabs.executeScript(tabs[0].id, { file: 'main.js' }); as well as document.getElementById('inject-btn').addEventListener('click', injectStartSearchScript)and added injectStartSearchScript()into the popup.js file. Is that what you meant?
Updated and complete example with explanation (you are almost there!)
manifest
You manifest looks good, no changes needed there.
This configuration says to load the content script in each tab. So before popup even opens each tab will have "main.js" injected into it (exactly once).
popup.js
Popup script looks good, no changes needed there.
The tab lookup is still necessary, since to send a message to a specific tab, must know its id. The popup looks for the currently active tab in current window (same as the popup is in) and sets up the button click actions to send a message to the tab.
main.js
Will need minor changes here
Make sure to register the onMessage listener, as included in the example below.
note the conditions: message.startSearch and message.startDeals -- these must match the messages sent from the popup, i.e. when popup sends a message with content {startDeals: true}, the if condition is startDeals. It is matching by a key in the sent message and if the key does not match any condition, the message is going to be ignored.
function pong() {
// do something
alert('Start!');
}
function ping() {
// do something else
alert('Deals!');
}
// register listener to receive messages
chrome.runtime.onMessage.addListener(message => {
// what to do on each received message:
if (message.startSearch) pong();
else if (message.startDeals) ping();
});
// sanity check: content has loaded in the tab
console.log('content loaded');
One more note as it relates to debugging extensions (and perhaps a source of some of these debugging issues) when the content script has been configured to be injected in the manifest, Chrome will inject main.js into tabs, in a separate extension context. If, after this injection, the developer reloads the extension (circular arrow in chrome://extensions or other tool), this will invalidate the context of the content script in the tab. The tab has to be reloaded to reactivate the content script. This is not an issue with a real user, but it does happen during debugging, so double check this is not the cause of issues while debugging.
I got a script from Stack Overflow for getting the current Chrome tab, but every time I try to run it, this error shows up in Google Chrome:
Uncaught TypeError: Cannot read property 'query' of undefined
Context
https://www.youtube.com/
Stack Trace
**chrome.tabs.query({active: true, currentWindow: true}, tabs => {**
let url = tabs[0].url;
// use `url` here inside the callback because it's asynchronous!
});
The code in bold is the highlighted code in the error. I have tried to replace currentWindow with lastFocusedWindow, but that did nothing. The permissions are all correct, and I even added extra permissions that I do not need, as shown here:
"permissions": [
"tabs",
"<all_urls>",
"activeTab" ]
I only need tabs and <all_urls> for my script, but I have activeTab just in case. What is the problem here, and how can I fix it?
There are two ways to do it.
calling getCurrent(). More details here on which page you should execute the script -> https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/tabs/getCurrent
chrome.tabs.getCurrent(function callback)
If you want the current URL on click of browser action then.
chrome.browserAction.onClicked.addListener( activeTab => {
console.log(activeTab);
});
Sorry for my poor English, i hope you can understand the issue.
I'm new to chrome extension development,and for sure in my code there are a lot of
thing to change or optimize;
anyway i've written a simple code that, (seems) works at least from my chrome.
The code clicks a button every X minutes in specific page, then wait and parse the result in page.
I've :
a content script (loaded from manifest.json) which "inject" some button and text Input box in page, so user can sets some "filter params" before click a "start button"; the start button then sendMessage() to background.js to set Alarm Event for the click ;
an eventPage (which is set persistent true in actually ) which handle the request from tabs and set a countdown alarm for each tab; when X min are passed fire a message to the interested tab;
I also have a popup.html e popup.js not important here (i think).
I've to distribuite this extension manually, so i would distribuite a zip that user can load with "developer mode ".
*Now the issue is: why the code was working only on my Chrome ? *
I've tested with others 2-3 laptop with Chrome, the background script is loaded (i can see the background page printint console log)
but in webpage the contents.js seems no way executed .
In my chrome works well: i can see in console some initial output (i print the name of dir extension to check) and
the dynamic created element (button,input box ect.) in page.
And all is working, i can fire the start button and receive results of parsing.
During the development i've never run the extension on other machine. Yesterday i've succssfully tested on 2-3 laptop.. then i made only few change but nothing serious.
Today i can run only in my chrome.
In other pc nothing, neither the simple console.log output first line of script.
I can read in console log :
"Unchecked runtime.lastError: Could not establish connection. Receiving end does not exist."
but this also in my working istance in my laptop chrome .
The zip file is the same and the extraction is good, in fact i can actually load the extension and i see the background page debug console.log() sentences
In some case, in laptop where it dosen't work, i've received a message relative jQuery and the fact that chrome.runtime.sendMessage() is not defined; and it points to code in webpage, not mine.
I've see that in webpage code there is something like:
var extid = "mcmhdskbnejjjdjsdkksmeadjaibo";
var extVer = "1.5";
var extStatus = 0;
$(document).ready(function () {
///...
chrome.runtime.sendMessage(extid, {message: "version"},
function (reply) {
if (reply) {
if (reply.version) {
if (reply.version == extVer) {
if (reply.gminfo != 'OK') {
extStatus = 1; /// ...
Seems that chrome.runtime is undefined, and the webpage can't call the sendMessage().
EDIT: this undefined occurs only when my extension is loaded
Maybe there is some conflict when i load my extension? But in my chrome browser works...
Can some expert indicate in where direction i've to investigate?
Thanks a lot for any suggestions.
My Manifest.json :
{"manifest_version": 2,
"name": "myAlarm",
"description": "This extension alerts.",
"version": "0.1",
"permissions": [
"alarms",
"system.cpu",
"storage",
"tabs",
"webNavigation",
"https://www.mytargetsite.com/subUrl/"
],
"web_accessible_resources": [
"icon.png",
"vanillaSelectBox.css"],
"content_scripts": [
{
"matches": ["https://www.mytargetsite.com/subUrl/"],
"css": ["vanillaSelectBox.css"],
"js": ["jquery-3.3.1.min.js","vanillaSelectBox.js","taffy-min.js","content.js"],
"run_at": "document_end"
}
],
"background": {
"scripts": ["eventPage.js"],
"persistent": true
},
"browser_action": {
"default_icon": "icon.png",
"default_popup": "popup.html"
},
"icons": {
....
}
}
My contents,js (stripped):
chrome.runtime.onMessage.addListener(
function(request, sender) {
// here i parse message "time'up" from background js
});
window.addEventListener('load', function() {
var pt=chrome.runtime.getURL('filterOff.wav');
var p=pt.split("/");
console.log("[myAlarm v0.1] started" );
console.log("[myAlarm v0.1] folder : ("+p[2]+")");
// here i start an active wait for the presence in page of button with ID= btntarget_id
waitForElementToDisplay("#btntarget_id", 500); //when function find button then create and add button and input text to webpage
});
My eventPage.js :
var curr_alarms =[];
chrome.extension.onMessage.addListener(function(request, sender)
{ /// here receive start countdown message from content.js and set alarm ...
}
chrome.alarms.onAlarm.addListener(function(alarm) {
/// here i manage each alarm for each tab
});
chrome.tabs.onRemoved.addListener(function(tabid, removed) {
// ...
});
chrome.tabs.onUpdated.addListener(function
(tabId, changeInfo, tab) {
//
});
edit : in browser where it dosen't work i can read also :
Access to XMLHttpRequest at 'https://mytargetsite.com/suburl/grid.php' (redirected from 'https://mytargetsite.com/suburl/grid.php') from origin 'https://mytargetsite.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: Redirect is not allowed for a preflight request.
The fact that the declared content script runs or not, should be verified by inspecting in devtools => sources sub-tab => content scripts sub-sub-tab. If it really doesn't run, there can be just two explanations: the URL is different (for example not https) or extensions are blocked by their domain admin via runtime_blocked_hosts which you can see in chrome://policy.
Your development mode extension's id will be different on a different machine unless you pin it by adding a "key" in manifest.json
To use chrome.runtime to send messages to your extension from a webpage code (not from a content script!) your extension's manifest should declare "externally_connectable" and use a different event onMessageExternal, see also sending messages from web pages.
The CORS error may be irrelevant to your code (you can investigate the source of the error by expanding the error's call stack in devtools console).
I want to be able to communicate from content-script (any tab) to background.
According to the documentation, I should be using chrome.extension chrome.runtime.onMessage with chrome.runtime.sendMessage.
So that's what I did:
manifest.json
"background": {
"scripts": ["background.js"],
"persistent": false
},
"permissions": [
"*://*/*"
],
background.js
console.info('1');
chrome.runtime.onMessage.addListener((request, sender, sendReponse) => {
console.info('2');
sendReponse({ msg: 'foo' });
return true;
});
I'm not very sure the return true; is needed. I tried with and without.
After building and reloading the extension I accessed chrome background page through chrome://extensions
> 1 // background.js file was run
> chrome.runtime.sendMessage({ hello: 'so' }, v => console.info(v))
> undefined
> undefined // This is the callback response I get. Why?
More important, I also get an empty response callback when running sendMessage from other tabs (i.e. stackoverflow.com)
> chrome.runtime.sendMessage('extension id', { hello: 'so' }, v => console.info(v))
> undefined
> undefined
Why do I get an empty response callback?
Am I missing any special permission? Wrong parameters? Maybe wrong API functions to do so?
runtime.sendMessage doesn't send the message to the sender's context since Chrome 49.
If sending to your extension, the runtime.onMessage event will be fired in every frame of your extension (except for the sender's frame),
webpages can send messages to your background/event page in two cases:
externally_connectable key in manifest.json allowed that page
a content script of your extension is loaded for that page, in which case you can switch the context to your extension in the console toolbar to be able to send messages manually from console:
The content script can be declared in manifest.json in which case it's autoinjected by [re]loading the page. Or you can inject it explicitly with tabs.executeScript from the background/event page.
I am making an Chrome extension and it needs to get some values from a webpage.
The page has a script element with a function:
<script>D.setConst({"TIME":1444940082,"LANG":"En","ID":1463887,"DEV":false,"DEV_NAME":......
When I type in my browser console D.TIME, I get the time value.
In that function there are many variables and I need few of them for my extension.
Steps which I have done:
1. manifest file has all the permissions, declared the content script.
2. have a popup.html window with a button which executes code from my content script:
function click(e) {
chrome.tabs.executeScript(null,
{code:'var btn = document.createElement("SCRIPT"); btn.innerHTML = "console.log(D.nonce.name);"; document.body.appendChild(btn);'},function(results){ console.log(results); } );
//window.close();
}
document.addEventListener('DOMContentLoaded', function () {
var divs = document.getElementById('message');
divs.addEventListener('click', click);
});
As a result in my extension console I get a result [object], but no the value I need. I get the result in the browser console, but I can't get it for the extension.
I have tried to find a solution, but all I have found is how to inject a script in the DOM.
Please help.
Edited.
manifest file
{
"name": "tesiting",
"description": "generic",
"version": "1.0",
"permissions": [
"https://www.example.lv/*",
"https://*.example.*",
"activeTab"
],
"content_scripts": [
{
"matches": ["https://www.example.lv/*"],
"js": ["testevent.js"]
}
],
"browser_action": {
"default_icon": "icon.png",
"default_title": "Mhmmm",
"default_popup": "popup.html"
},
"manifest_version": 2
}
popup.html
<!doctype html>
<html>
<head>
</head>
<body>
<div id='message'>Geting code</div>
<script type="text/javascript" src="testevent.js"></script>
</body>
</html>
content script testevent.js
window.addEventListener("MyCustomEvent", function (e) {
var check = e.detail.passback;
console.log(check);
});
function click(b) {
chrome.tabs.executeScript(null,
{file: "eventer.js"});
}
document.addEventListener('DOMContentLoaded', function () {
var divs = document.getElementById('message');
divs.addEventListener('click', click);
});
eventer.js
var scr = document.createElement('script');
scr.textContent = '(' + function () {
var check = [console.log(D.nonce.name)]; //this is the function which I execute to get the desired values from it, right now it should get a Name.
var event = document.createEvent("CustomEvent");
event.initCustomEvent("MyCustomEvent", true, true, {"passback":check});
window.dispatchEvent(event); } + ')();';
(document.head || document.documentElement).appendChild(scr);
scr.parentNode.removeChild(scr);
Only JSON-serializable objects (values like number, string, array, simple {...} object) can be passed into the callback, not a DOM element.
When typing in the console interactively the code runs in the context of the page, so the page variables are accessible. This is not the case with an extension's content script which runs in an isolated world.
Injecting a script into the page itself via <script> element is an asynchronous operation, which means that the content script returns immediately to executeScript's callback with a non-serializable value returned by the last statement (document.body.appendChild in the code).
The first usable value in the console log you see is from that inner <script>-injected code, not from the callback.
There are several solutions:
Force immediate execution of the inner injected code by abusing an inline event listener and triggering its event: Method 3 in Building a Chrome Extension - Inject code in a page using a Content script answer.
Send the value via postMessage and custom event in the page-injected code, receive it in the injected content script with window.addEventListener which then sends it into the extension via chrome.runtime.sendMessage, receive it in the popup script with chrome.runtime.onMessage.
Chrome Extension - access document/page variable from extension
Building a Chrome Extension - Inject code in a page using a Content script
Use "externally_connectable": Sending messages from web pages in the documentation. In short: explicitly allow the url of the page in manifest.json, send with chrome.runtime.sendMessage in the page-injected code, receive with chrome.runtime.onMessageExternal in the popup/background script).
There could be other examples/solutions findable with chrome extension access page variables