If my chrome extension has a persistent background script, I could use it to store data in the script that I would eventually want to store in chrome.storage.local. When / what event triggers the background script to stop running (so that I can add a callback to store the data in chrome.storage.local before the local data goes out of scope)?
Background scripts can either be registered persistent (always running while the extension is enabled and has not crashed) or non-persistent (not always but temporarily runs when runtime.getBackgroundPage or any runtime event callback registerd in the background script is invoked)
They are registered in the manifest under the "background" field, and "persistent" should be specified as false for non-persistent background scripts/pages.
}
"background": {
"scripts": ["background.js"],
"persistent": false
}
}
There is a runtime event callback for when the non-persistent background script is unloaded (because they unload on their own after a few seconds of inactivity), it does not get invoked when an extension with persistent background script is disabled (or crashed of-course).
chrome.runtime.onSuspend.addListener(function() {
console.log("Unloading.");
});
You can verify that with this simple extension:
manifest.json
{
"name": "onSuspend",
"description": "onsuspent",
"version": "1.0",
"manifest_version": 2,
"permissions": [
"storage"
],
"background": {
"scripts": ["background.js"],
"persistent": true
}
}
background.js
chrome.runtime.onSuspend.addListener(function() {
console.log("Unloading.");
chrome.storage.local.set({"Saved": "yes"}, function() {});
});
chrome.storage.local.get("Saved", function(data) {
console.log("shouldn't be an empty object": data);
});
The only way I found that you can register a callback to run before the persistent background script is disabled is by loading a background page instead and using the window's unload event, code use for testing:
{
"name": "callback before stopping persistent background script",
"description": "callback before stopping persistent background script",
"version": "1.0",
"manifest_version": 2,
"permissions": [
"storage"
],
"background": {
"page": "background.html",
"persistent": true
}
}
background.js
window.addEventListener('unload', function(event) {
localStorage.setItem("Saved", "yes");
});
console.log("Saved:", localStorage.getItem('Saved'));
background.html
<html><body></body><script src="background.js"></script></html>
To test it try disabling and re-enabling or reloading the extension twice.
Related
Good day.
I have a problem sending messages from background script to content script.
I try to send messages every time a user switches between browser tabs.
I get this error.
Unchecked runtime.lastError: Could not establish connection. Receiving end does not exist.
Below is the code for the extension itself.
Background Script:
chrome.tabs.onActivated.addListener(function changeTab(activeInfo) {
var port = chrome.tabs.connect(activeInfo.tabId);
console.log(port);
port.postMessage({tabId: activeInfo.tabId});
});
Content Script:
chrome.runtime.onConnect.addListener(function(port) {
console.log(11111111);
port.onMessage.addListener(function(msg) {
console.log(22222222);
if(msg == undefined || Object.keys(msg).length == 0) return;
if(msg.checkExtensionRBPopup)
port.postMessage({active: window.localStorage.getItem('extension_rb_popup')});
});
});
Manifest:
{
"manifest_version": 2,
"name": "Rebatemango Extension",
"description": "Rebatemango",
"version": "1.0",
"browser_action": {
"default_popup": "popup.html"
},
"content_scripts": [
{
"matches": [
"<all_urls>"
],
"js": [
"content.js",
]
}
],
"permissions": [
"background",
"tabs",
"activeTab",
"declarativeContent",
"storage",
"clipboardWrite",
"cookies",
"tabCapture",
"displaySource",
"webNavigation"
],
"background": {
"scripts": [
"js/jquery.js",
"js/libs.js",
"background.js"
],
"persistent": false
},
"icons": {
"16": "images/mango.png",
"48": "images/mango.png",
"128": "images/mango.png"
}
}
Please tell me what am I doing wrong?
How to fix or track this error?
Your content script is using port.postMessage but there is no listener on the other side of the port in the background script, which is why the error is shown.
Solution:
Use simple messaging. Don't use ports, it's not needed here and your implementation is causing a memory leak since it creates a new port each time without releasing it. The simple messaging will handle this automatically.
chrome.tabs.sendMessage(activeInfo.tabId, {foo: 1}, response => {
if (chrome.runtime.lastError) return;
console.log(response);
});
The content script listener:
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
console.log(msg);
sendResponse({bar: 2});
});
Other causes.
The error message may also appear if there is no content script running at the exact moment the message was sent. It may happen because by default content scripts run after DOMContentLoaded event in the web page. Or maybe the tab contained an unsupported URL like chrome:// or chrome-extension:// from an extension page or https://chrome.google.com/webstore/ or any URL forbidden by the runtime_blocked_hosts policy. For the first problem you can add "run_at": "document_start" to your content script declaration in manifest.json. For the other cases there's nothing you can do except for suppressing and ignoring the error message.
Also the background script sends {tabId: activeInfo.tabId} but onMessage listener in the content script doesn't read it. Instead it reads msg.checkExtensionRBPopup which doesn't make any sense in case it's not a copypaste artifact of posting the question.
My chrome extension spawns a temp .html page. I want to manipulate the DOM of the sample.html page that was created, but can't. I can manipulate the DOM for any other page without issue. The problem seems to be with the fact my temp .html page resides within chrome-extension://
Error Message:
Unchecked runtime.lastError while running tabs.executeScript: Cannot access contents of url "chrome-extension://123/sample.html?id=100". Extension manifest must request permission to access this host.
Note: for simplicity sake I provided sample code that exhibits the same Error. Once loaded I can use the key combo to inject a div and some text into any webpage (Mac-> Cmd+Shift+P or PC Ctrl+Shift+P)
I've tried adding all possible permissions and even web_accessible_resources to the manifest.json. (I don't believe this to be the issue). I've tried different ways to inject the code into the sample.html by calling out the specific tabId, activeTab or even setting the tabId to null within the background file. I've read through stackoverflow, googled and looked around but came up short.
manifest.json
{
"manifest_version": 2,
"name": "sample1",
"description": "sample1",
"version": "0.0.1",
"browser_action":
{
"default_title": "sample"
},
"commands":
{
"saveImageCommand":
{
"suggested_key":
{
"default": "Ctrl+Shift+Z",
"mac": "Command+Shift+Z"
},
"description": "Toggle Save Image"
},
"playback":
{
"suggested_key":
{
"default": "Ctrl+Shift+P",
"mac": "Command+Shift+P"
},
"description": "load player Image"
}
},
"permissions": [
"tabs",
"activeTab",
"storage",
"<all_urls>",
"*://*/*"
],
"background":
{
"persistent": false,
"scripts": ["background.js"]
}
,
"web_accessible_resources": [
"chrome-extension://*/sample.html?id=*"
]
}
background.js
chrome.commands.onCommand.addListener(function(command) {
if (command === 'saveImageCommand') {
capturecurrent();
}
if (command === 'playback') {
chrome.tabs.executeScript(null, {
code: 'var divNode = document.createElement("div");divNode.setAttribute("id", "video1Div");var instructions = document.createTextNode("testing");divNode.appendChild(instructions);document.body.appendChild(divNode)'
});
}
});
chrome.browserAction.onClicked.addListener(function() {
chrome.tabs.captureVisibleTab(function(screenshotUrl) {
var viewTabUrl = chrome.extension.getURL('sample.html')
chrome.tabs.create({ url: viewTabUrl });
});
});
sample.html
<html>
<head></head>
<body>
<div id="firstDiv">firstDiv</div>
</body>
</html>
Expected Results:
For me to interact directly with the DOM on the temp sample.html page.
Note:
I don't want to build out buttons for DOM manipulation directly within the sample.html page. That defeats the purpose of this exercise. Esp since I want to use shortcut key combos to call this DOM manipulation (Mac-> Cmd+Shift+P or PC Ctrl+Shift+P)
Actual Results:
I am able to interact with the DOM on any normal website using the shortcut key combo but not the sample.html that URL starts with chrome-extension://
I'm writing a chrome extension and I want to manage all of the data/variables with chrome storage. From what I understand, I should be able to use chrome.storage across my extension.
I want to set something in browser_action script and then access it in the window created by the background script. The HTML and JS files all have corresponding names.
This is what I have tried with no luck:
//manifest.json
{
"manifest_version": 2,
"name": "extension",
"description": "my extension",
"version": "0.1",
"permissions": [
"tabs",
"storage"
],
"background": {
"scripts": ["background.js"],
"persistent": false
},
"browser_action": {
"default_icon": {
"38": "images/icon38.png"
},
"default_popup": "settings.html"
}
}
//background.js
chrome.windows.create({'url': chrome.extension.getURL("newPage.html"),'type': "detached_panel",'focused': true}, function(){
//created newPage.html which has newPage.js
});
//setting.js
document.addEventListener('DOMContentLoaded', function(){
chrome.storage.local.set({'enabled': 'TRUE'});
});
//newPage.js
chrome.storage.local.get('enabled', function(result){
document.getElementById("myId").innerHTML += "<br>script loaded... and status: " + result.enabled;
});
When I do this, newPage.html displays "script loaded... and status: undefined".
Even with it being asyncronous, the storage value should be populated on running the script a second time, right?
I may just be using this incorrectly. If that's the case, what's the correct way to set something with chrome storage to access in my new window?
Any help would be appreciated!
I think that your newPage.html is asking for the value of 'enabled' before it's been set by the event handler in settings.html, which only executes after you've actually opened the browser popup. So this is why it's undefined. Open the popup and you should see that it's defined.
I would like my Google Extension to start execution as soon as Google Chrome window is opened. I have the following code in my background.js :
if (window.Notification) {
setInterval( function() { callAutomate(); }, 60000 );
}
function callAutomate() {
// Code to automate hello-hello.com
}
The manifest file is as follows -
{
"name" : "Hello.co Extension",
"version" : "1.1",
"description" : "Say Hello",
"background" :
{
"scripts": ["background.js"],
"persistent": false
},
"page_action" :
{
"default_icon" : "hello-19.png",
"default_title": "Hello World",
"default_popup": "popup.html"
},
"content_scripts": [
{
"matches": ["https://www.hellohello.com/*"],
"js": [
"content.js",
"webDB.js"
]
}
],
"permissions": [
"tabs",
"storage",
"unlimitedStorage",
"webNavigation",
"notifications",
"https://www.hellohello.com/"
],
"options_page": "options.html",
"icons" : {
"48" : "hello-48.png",
"128" : "hello-128.png"
},
"manifest_version": 2,
"web_accessible_resources": [
"hello-48.png"
]
}
Here is my problem the callAutomate(); function seems to called only while I'm observing the console logs for background.js. However the expected behavior of the extension is to call the callAutomate(); function every one minute from the time Google Chrome window is opened.
Any help with explanatory code would be highly appreciated.
As explained in the Chrome extension documentation, there are 2 types of background pages:
persistent background page : always "opened"
event background page : "open and closed" as needed
You are using the second one, as specified with the "persistent": false in your manifest.json file,
so the background code will not execute itself when you normally load your page.
And I am pretty sure that when you are using the developer tool ("observing the console") on your background page, the page is "opened" and does not close while the console remains open.
By removing the "persistent": false your code will be executed.
But as suggested in the documentation, you should use event pages as much as possible so have a look at the lifetime documentation to see the different ways you can communicate with your background page and thus execute your desired code.
That is because of "persistent": false in the manifest.
This describes an Event page, that is, a page Chrome can unload at will if it is idle for more than a couple of seconds, only keeping track of registered event handlers.
Notes for Event pages explicitly mention:
If your extension uses window.setTimeout() or window.setInterval(), switch to using the alarms API instead. DOM-based timers won't be honored if the event page shuts down.
And if you open a Dev Tools window for the background page, it will NOT be unloaded, leading to your code executing properly.
You can switch to using chrome.alarms API, but before you do, carefully read the Event page documentation. You need to understand all the limitations: for instance, since the page is unloaded, all local state in variables is lost. If you need to persist state, you'll need to employ storage APIs.
If that is too complicated for your purposes, remove "persistent": false to revert to a normal Background page.
So i'd like to run a script when the tab reloads in a specified URL. It almost works, but actually id doesn't :)
This is my manifest file:
{
"manifest_version": 2,
"name": "Sample Extension",
"description": "Sample Chrome Extension",
"version": "1.0",
"content_scripts":
[
{
"matches": ["http://translate.google.hu/*"],
"js": ["run.js"]
}
],
"permissions":
[
"activeTab",
"tabs"
],
"browser_action":
{
"default_title": "Sample",
"default_icon": "icon.png"
}
}
and this is run.js:
chrome.tabs.onUpdated.addListener(
function ( tabId, changeInfo, tab )
{
if ( changeInfo.status === "complete" )
{
chrome.tabs.executeScript( null, {file: "program.js"} );
}
}
);
The programs.js just alerts some text (yet). When I put an alert to the first line of the run.js, it alerts, but when I put it in the if, it doesn't. I can't find the problem. Did I type something wrong?
Assuming that http://translate.google.hu/* pages are the ones you wish to inject code into on reload, you would have to go about it in a slightly different way. Currently you are always injecting code into those pages (without the permission to do so, no less) and then trying to use the chrome.tabs api inside that content script, which you can't do. Instead, we will put the listener in a background page and inject the code only on a page refresh, like you want. First the manifest:
{
"manifest_version": 2,
"name": "Sample Extension",
"description": "Sample Chrome Extension",
"version": "1.0",
"background": {
"scripts": ["background.js"]
},
"permissions":[
"http://translate.google.hu/*", "tabs"
]
}
background.js
chrome.tabs.onUpdated.addListener(function(tabId,changeInfo,tab){
if (tab.url.indexOf("http://translate.google.hu/") > -1 &&
changeInfo.url === undefined){
chrome.tabs.executeScript(tabId, {file: "program.js"} );
}
});
This will listen for the onUpdated event, checks if it is one of the url's that we want to inject into, and then it checks if the page was reloaded. That last step is accomplished by checking if changeInfo.url exists. If it does, then that means that the url was changed and thus not a refresh. Conversely, if it doesn't exist, then the page must have only been refreshed.
2021
If you want to detect reload from background.js in manifest 3 (maybe also 2), chrome.tabs.onUpdated approach didn't work for me :/ It was invoked too many times.
That what worked for me in the end!
// --- On Reloading or Entering example.com ---
chrome.webNavigation.onCommitted.addListener((details) => {
if (["reload", "link", "typed", "generated"].includes(details.transitionType) &&
details.url === "http://example.com/") {
codeAfterReload();
// If you want to run only when the reload finished (at least the DOM was loaded)
chrome.webNavigation.onCompleted.addListener(function onComplete() {
codeAfterReloadAndFinishSomeLoading();
chrome.webNavigation.onCompleted.removeListener(onComplete);
});
}
});
For more transition types: https://developer.chrome.com/docs/extensions/reference/history/#transition_types
good luck :)
content_scripts are run at every page (re)load, so it's best to just use those to detect it.
This way you also don't risk running any code in the background before your content_script is ready to receive any message.