Something we've come to depend on over the years: background processes keep running in the background. This means we can set a variable, go away for a while, and (assuming the browser hasn't been restarted) count on that variable to be there untouched upon our return.
With the move to background service workers in Manifest V3 we will need to think carefully about how to preserve data, because service workers can be suspended and re-run at any time. And things get especially interesting in the interface between global features like browser buttons and context menus, which persist outside the background process.
Here's our manifest, which will allow our context menu to appear over all URLs. (This is not required; as soon as v3.manifest.action
lands in Canary I'll fix it up.)
{
"name": "Baseline",
"description": "Manifest V2 Baseline",
"version": "0.1.107",
"permissions": ["contextMenus", "<all_urls>"],
"background": {
"scripts": ["js/background.js"]
},
"manifest_version": 2
}
Here's our background script, which creates a context menu item and logs a count of clicks made during this browsing session:
// Initialize click counter
let clickCounter = 0;
console.log("V2 Baseline: initialized test click counter to 0");
// create a menu item
chrome.contextMenus.create({
id: "BaselineV2",
title: "V2 Baseline",
// show the menu over everything
contexts: ["all"]
});
// what to do when our menu is clicked
chrome.contextMenus.onClicked.addListener(event => {
// bump the count
clickCounter = clickCounter + 1;
// let us know it worked
console.log("V2 Baseline: updated baseline click counter to " + clickCounter);
});
- Open
chrome://extensions
and be sure Developer Mode (top right) is active. - Drag
ep_007/v2
intochrome://extensions
to install. - Inspect the background page and switch to the Console tab.
- Observe the message
V2 Baseline: initialized test click counter to 0
. - Right-click on
chrome://extensions
and choose the V2 Baseline context menu. - Observe the message
V2 Baseline: updated baseline click counter to 1
. - Do this a few more times and either restart Chrome or reload the extension.
- Inspect the background page and observe that the counter has restarted at zero.
Because our extension won't maintain anything in memory when it stops and restarts, we're going to use local storage to maintain our state. Our background script (as of this writing) must be in the extension's root directory instead of the usual /js
subdirectory. Here's our manifest:
{
"name": "Test",
"description": "Manifest V3 Test",
"version": "0.1.107",
"permissions": ["contextMenus", "storage"],
"host_permissions": ["<all_urls>"],
"background": {
"service_worker": "background.js"
},
"manifest_version": 3
}
We're going to use chrome.storage.local
to keep track of our click count. But because local storage is persistent across sessions, we're going to need to listen for chrome.runtime.onStartup
to initialize it to zero for each session.
Here's our background script
// listen for extension install
chrome.runtime.onInstalled.addListener(() => {
// only create our menu item once
chrome.contextMenus.create({
id: "TestV3",
title: "V3 Test",
// show the menu over everything
contexts: ["all"]
});
});
// listen for a browser session start
chrome.runtime.onStartup.addListener(() => {
// initialize the click counter to zero
chrome.storage.local.set({ clickCounter: 0 }, function() {
// let us know it worked
console.log("V3 Test: initialized test click counter to 0");
});
});
// what to do when our menu is clicked
chrome.contextMenus.onClicked.addListener(event => {
// get the current value
chrome.storage.local.get("clickCounter", result => {
// bump the count
result.clickCounter = result.clickCounter + 1;
// update local storage
chrome.storage.local.set({ clickCounter: result.clickCounter }, function() {
// let us know it worked
console.log(
"V3 Test: updated test click counter to " + result.clickCounter
);
});
});
});
- Open
chrome://extensions
and be sure Developer Mode (top right) is active. - Drag
ep_007/v3
intochrome://extensions
to install. - Open
chrome://serviceworker-internals
, find your worker, and click Inspect. - In the inspector, switch to the Console tab.
- Observe the message
V3 Test: initialized test click counter to 0
. - Right-click in
chrome://serviceworker-internals
and choose the V3 Test context menu. - Observe the message
V3 Test: updated baseline click counter to 1
. - From
chrome://serviceworker-internals
with your inspector window still open, suspend your worker by clicking Stop. - Observe that your inspector window goes dark with a message that DevTools was disconnected.
- Right-click in
chrome://serviceworker-internals
and choose the V3 Test context menu. - Observe that your inspector window lights back up again with an increased value for your click counter
- Reload your extension in
chrome://extensions
. - Observe that your inspector window goes dark again. It's disconnected and won't come back since its instance of
background.js
has gone. - Return to
chrome://serviceworker-internals
and click Inspect. - Right-click in
chrome://serviceworker-internals
and choose the V3 Test context menu. - Observe that your click counter has not reset, because local storage persists during extension reloads and reloading the extension does not fire
chrome.runtime.onStartup
. We could fix this by listening forchrome.runtime.onReload
but it's not worth the trouble; just wanted to note here that it's expected behavior. - Restart Chrome.
- Open
chrome://serviceworker-internals
, find your worker, and click Inspect. - In the inspector, switch to the Console tab.
- Observe that the counter has restarted at zero.
- If you're only installing context menus once, listen for
chrome.runtime.onInstalled
first. (Hat tip: Dave Bertoni.) - Otherwise remember context menus are global and do not go away when the background script suspends. If you don't do something like
chrome.contextMenus.removeAll()
on startup you will see an error. - Extension reloads do not overwrite local storage, nor do they fire the
chrome.runtime.onStartup
event. - If you're currently logging browser session starts from your V2 extension, you'll need to listen for
chrome.runtime.onStartup
instead of pinging when your background script starts running.
Looks good from here. Seems like these restrictions will encourage smarter coding behavior and better awareness of how some global events and systems behave.