To remove the global variables used in a MV2 background script when migrating to a MV3 service worker, all the guides I've found have just given an example of replacing a single global variable with a few lines of setting and then getting using chrome.storage, but it's still not clear to me how it can be used in a bit more complicated scenario.
For instance:
const activatedTabs = [];
let lastActiveTabInfo;
chrome.tabs.onActivated.addListener((activeInfo) => {
if (activatedTabs.length === 0) {
activatedTabs.push(activeInfo.tabId);
lastActiveTabInfo = activeInfo;
}
}
How could the snippet above be refactored to use chrome.storage and remove the global variables?
CodePudding user response:
The number of variables in the state doesn't change the approach:
- read the state on the start of the script
- save the state on change
For small data (1MB total) use chrome.storage.session, which is in-memory i.e. it doesn't write to disk, otherwise use chrome.storage.local. Both can only store JSON-compatible types i.e. string, number, boolean, null, arrays/objects of such types. There's also IndexedDB for Blob or Uint8Array.
let activatedTabs;
let lastActiveTabInfo;
let busy = chrome.storage.session.get().then(data => {
activatedTabs = data.activatedTabs || [];
lastActiveTabInfo = data.lastActiveTabInfo;
busy = null;
});
const saveState = () => chrome.storage.session.set({
activatedTabs,
lastActiveTabInfo,
});
chrome.tabs.onActivated.addListener(async info => {
if (!activatedTabs.length) {
if (busy) await busy;
activatedTabs.push(info.tabId);
lastActiveTabInfo = info;
await saveState();
}
});
You can also maintain a single object with properties instead:
const state = {
activatedTabs: [],
lastActiveTabInfo: null,
};
const saveState = () => chrome.storage.session.set({ state });
let busy = chrome.storage.session.get('state').then(data => {
Object.assign(state, data.state);
busy = null;
});
chrome.tabs.onActivated.addListener(async info => {
if (!state.activatedTabs.length) {
if (busy) await busy;
state.activatedTabs.push(info.tabId);
state.lastActiveTabInfo = info;
await saveState();
}
});
Note that if you subscribe to frequent events like tabs.onActivated, your service worker may restart hundreds of times a day, which wastes much more resources than keeping an idle persistent background page. The Chromium team ignores this problem, but you shouldn't, and luckily there's a way to reduce the number of restarts by prolonging the SW lifetime. You still need to read/save the state as shown.