Skip to content

Latest commit

 

History

History
151 lines (126 loc) · 7.05 KB

ep_007.md

File metadata and controls

151 lines (126 loc) · 7.05 KB

Episode Seven: The Persistence of Memory

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.

V2 Baseline

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);
});

Things to Do and Notice

  • Open chrome://extensions and be sure Developer Mode (top right) is active.
  • Drag ep_007/v2 into chrome://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.

V3 Test

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
      );
    });
  });
});

Things to Do and Notice

  • Open chrome://extensions and be sure Developer Mode (top right) is active.
  • Drag ep_007/v3 into chrome://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 for chrome.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.

Learnings

  • 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.

Current Status

Looks good from here. Seems like these restrictions will encourage smarter coding behavior and better awareness of how some global events and systems behave.