This script can be used to maintain a addon-local copy of the browser tabs' state primarily to avoid performing browser.tabs.query, browser.tabs.get, and browser.sessions.getTabValue function calls.
The script hasn't been tested on chrome.
Depends on js-syncqueue (see Queue).
Required manifest permissions: tabs, sessions.
The tabs.Tab objects that are returned from the cache methods, or passed to the cache event handlers must not be mutated.
Creating the cache: let cache = newCache(myConfig);
Initializing the cache: await cache.init(myInitFunction);
async function myOnCreatedHandler(tab) { /* */ }
async function myInitFunction(cache) { /* initialize addon state by querying the cache */ }
async function init() {
let myListeners = { onCreated: myOnCreatedHandler };
let myConfig = {
listeners: myListeners,
auto: true,
tabValueKeys: ['foo', 'bar'],
};
let cache = newCache(myConfig);
await cache.init(myInitFunction);
}
init();The listeners object may contain function callbacks for any combination of the following keys: onActivated, onAttached, onCreated, onMoved, onRemoved, and onUpdated, corresponding to browser.tabs events. The values are registered as cache event handlers (see Cache Event Handlers). Instead of passing the callbacks in this object the listeners could be added later, e.g. in myInitFunction.
auto is a boolean which can be used to avoid manually adding the required browser event listeners. If true, it will also create the required queue object (see Queue). This may be set as false if some browser events are not desired to be listened to, but hooking the cache listeners must be done manually.
queue is required only when configuration doesn't have auto: true. The value is the js-syncqueue object used for processing asynchronous browser events in a synchronous manner.
tabValueKeys is an array of keys (strings) used for the sessions api tabValue functions. Values for keys defined in the array are fetched and stored by the cache. This prefetching occurs when initializing the cache itself and in the cache onCreated handler. The fetched values are guaranteed to be available in the cache for myInitFunction and onCreated cache event handler functions.
The cache object has onActivated, onAttached, onCreated, onMoved, onRemoved, and onUpdated keys which provide a similar interface as the corresponding browser.tabs events. Each key supports the following:
addListener(myListenerFunction): the function passed as a parameter will be invoked by the cache as described in the Cache Event Handlers section.removeListener(myListenerFunction): removes the specified function from the list of functions invoked when the event is fired.hasListener(myListenerFunction): returnstrueif the parameter is currently registered as a listener of the event,falseotherwise.
The cache event handlers are the functions passed to the cache in the listeners object, and any function that was added as a listener for one of the cache events. They will be invoked after the cache state has been updated following a browser event. They may be asynchronous and will be awaited on before the cache continues processing further browser events.
onActivatedwill be invoked with argumentstabs.Tab,activeInfo.onAttachedwill be invoked with argumentstabs.Tab,attachInfo. There will not be a correspondingonDetachedevent. Instead, theattachInfocontains the values of thedetachInfoand the cache will immediately reflect the browser state after theonDetachedevent. In addition, the cache will re-submit any cached tabValues before invoking the cache event handlers.onCreatedwill be invoked with the argumenttabs.Tab. Any values (if defined) for keys intabValueKeyswill be available in the cache before the cache event handlers are invoked. If prefetching one of the values fails the cache will not invoke any events related to the tab, as the tab will have been closed.onMovedwill be invoked with the argumentstabs.Tab,moveInfo.onRemovedwill be invoked with argumentstabs.Tab,removeInfo,values.valuesis an object consisting of the key-value pairs which were present in the cache when the tab was removed. The values may not correspond to the actual values stored by the browser, for example, when the cache values were updated after a tab was removed.onUpdatedwill be invoked with argumentstabs.Tab,updateProperties.
cache.setValue(tabId, key, value)will store a tabValue in the cache. If the argument value doesn't match the currently cached value thenbrowser.sessionstabValue will be updated.cache.getValue(tabId, key)returns the cached tabValue. If the cache was configured withtabValueKeysany keys for which there is a defined value will be available in the cache, otherwise the value must be stored manually with thesetValuefunction.cache.removeValue(tabId, key)removes a value from the cache andbrowser.sessions.
cache.get(tabId)returns the correspondingtabs.Tabobject in the cache.cache.getIndexed(windowId, index)returns the correspondingtabs.Tabobject in the cache.cache.getActive(windowId)returns thetabs.Tabof the active tab in the window.
cache.forEach(callback, windowId, filter)(asynchronous) will invokecallbackfor every cached tab, passing thetabs.Tabobject as the only argument. IfwindowIdis defined, only tabs in the specified window are iterated. If defined,filterwill be invoked for every iterated tab with thetabs.Tabas the only argument. Iffilterreturns a value that coerces tofalsethecallbackwill not be invoked for that tab.callbackmay be asynchronous, and will be awaited on as a group.cache.forEachWindow(callback)(asynchronous) will invokecallbackfor every cached window, passing thewindowIdvalue as the only argument.callbackmay be asynchronous, and will be awaited on as a group.
cache.init(myInitFunction)(asynchronous) causes the cache to initialize, querying for the current tab data from the browser, updating its internal state, invokingmyInitFunctionfunction with the cache itself as the parameter, and finally beginning to process browser events.myInitFunctionis optional, may be asynchronous, and will be awaited on.cache.debug()returns an object containing the internal variables in the cache.
- The cache event handlers for
tabs.onUpdatedwill not be invoked before thetabs.onCreatedevent for a given tab. - The
indexandwindowIdoftabs.Tabpassed toonUpdatedcache event handler may differ from the values passed by the browser to the cache'stabs.onUpdatedevent callback, particularly when tabs are moved from one window to another. These values will always be coherent to the cache itself.
Internally the script maintains a js-syncqueue object to queue pending browser events while its internal state is updating and the cache event handlers are being awaited on. This also guarantees all browser events are handled, even the ones occurring during cache initialization.
src.js of js-syncqueue must be in scope when using auto:true.
Accessing the queue when using auto: true can be done via let cacheQueue = cache.debug().queue as soon as the cache has been created (not necessarily initialized).
Some event listeners must be hooked for the cache to function correctly, as below
let myQueue = newSyncQueue({ enabled: false });
let cache = newCache({ queue: myQueue });
/* mandatory listeners */
browser.tabs.onActivated.addListener(info => myQueue.do(cache.cacheOnActivated, info));
browser.tabs.onAttached.addListener((id, info) => myQueue.do(cache.cacheOnAttached, id, info));
browser.tabs.onCreated.addListener(tab => myQueue.do(cache.cacheOnCreated, tab));
browser.tabs.onMoved.addListener((id, info) => myQueue.do(cache.cacheOnMoved, id, info));
browser.tabs.onRemoved.addListener((id, info) => myQueue.do(cache.cacheOnRemoved, id, info));
/* optional listeners */
browser.tabs.onUpdated.addListener((id, info, tab) => myQueue.do(cache.cacheOnUpdated, id, info, tab));
/* initialize cache and start processing events */
await cache.init(myInitFunction);
myQueue.enable()