Centralize access to browser.storage.local('localStorage')

Related commit:
- 2ac288397c

Instead of having the `localStorage` data being accessed
from different locations, all accesses are now funnelled
to the main process.

Doing so simplifies the code in auxiliary processes and
also remove the need for browser.storage.local.onChanged()
listeners.

No longer using an onChanged() listener also happens to
remove spurious warnings from the Firefox console.
This commit is contained in:
Raymond Hill 2020-04-15 15:55:29 -04:00
parent 572750584c
commit 0bcf04d3dd
No known key found for this signature in database
GPG Key ID: 25E1490B761470C2
3 changed files with 123 additions and 90 deletions

View File

@ -943,6 +943,7 @@ vAPI.messaging = {
onFrameworkMessage: function(request, port, callback) {
const sender = port && port.sender;
if ( !sender ) { return; }
const fromDetails = this.ports.get(port.name) || {};
const tabId = sender.tab && sender.tab.id || undefined;
const msg = request.msg;
switch ( msg.what ) {
@ -981,12 +982,21 @@ vAPI.messaging = {
break;
}
case 'extendClient':
if ( fromDetails.privileged !== true ) { break; }
vAPI.tabs.executeScript(tabId, {
file: '/js/vapi-client-extra.js',
}).then(( ) => {
callback();
});
break;
case 'localStorage': {
if ( fromDetails.privileged !== true ) { break; }
const args = msg.args || [];
vAPI.localStorage[msg.fn](...args).then(result => {
callback(result);
});
break;
}
case 'userCSS':
if ( tabId === undefined ) { break; }
const details = {
@ -1401,6 +1411,78 @@ vAPI.adminStorage = (( ) => {
/******************************************************************************/
/******************************************************************************/
// A localStorage-like object which should be accessible from the
// background page or auxiliary pages.
//
// https://github.com/uBlockOrigin/uBlock-issues/issues/899
// Convert into asynchronous access API.
//
// Note: vAPI.localStorage should already be defined with the client-side
// implementation at this point, but we override with the
// background-side implementation.
vAPI.localStorage = {
start: async function() {
if ( this.cache instanceof Promise ) { return this.cache; }
if ( this.cache instanceof Object ) { return this.cache; }
this.cache = webext.storage.local.get('localStorage').then(bin => {
this.cache = undefined;
try {
if (
bin instanceof Object === false ||
bin.localStorage instanceof Object === false
) {
this.cache = {};
const ls = self.localStorage;
for ( let i = 0; i < ls.length; i++ ) {
const key = ls.key(i);
this.cache[key] = ls.getItem(key);
}
webext.storage.local.set({ localStorage: this.cache });
} else {
this.cache = bin.localStorage;
}
} catch(ex) {
}
if ( this.cache instanceof Object === false ) {
this.cache = {};
}
});
return this.cache;
},
clear: function() {
this.cache = {};
return webext.storage.local.set({ localStorage: this.cache });
},
getItem: function(key) {
if ( this.cache instanceof Object === false ) {
console.info(`localStorage.getItem('${key}') not ready`);
return null;
}
const value = this.cache[key];
return value !== undefined ? value : null;
},
getItemAsync: async function(key) {
await this.start();
const value = this.cache[key];
return value !== undefined ? value : null;
},
removeItem: async function(key) {
this.setItem(key);
},
setItem: async function(key, value = undefined) {
await this.start();
if ( value === this.cache[key] ) { return; }
this.cache[key] = value;
return webext.storage.local.set({ localStorage: this.cache });
},
cache: undefined,
};
vAPI.localStorage.start();
/******************************************************************************/
/******************************************************************************/
// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/storage/sync
vAPI.cloud = (( ) => {

View File

@ -216,98 +216,41 @@ vAPI.closePopup = function() {
// A localStorage-like object which should be accessible from the
// background page or auxiliary pages.
// This storage is optional, but it is nice to have, for a more polished user
// experience.
//
// https://github.com/gorhill/uBlock/issues/2824
// Use a dummy localStorage if for some reasons it's not available.
//
// https://github.com/gorhill/uMatrix/issues/840
// Always use a wrapper to seamlessly handle exceptions
//
// https://github.com/uBlockOrigin/uBlock-issues/issues/899
// Convert into asynchronous access API.
vAPI.localStorage = {
start: function() {
if ( this.cache instanceof Promise ) { return this.cache; }
if ( this.cache instanceof Object ) { return Promise.resolve(); }
const onChanged = (changes, area) => {
if (
area !== 'local' ||
changes instanceof Object === false ||
changes.localStorage instanceof Object === false
) {
return;
}
const newValue = changes.localStorage.newValue;
this.cache = newValue instanceof Object ? newValue : {};
};
this.cache = new Promise(resolve => {
browser.storage.local.get('localStorage', bin => {
this.cache = undefined;
try {
if (
bin instanceof Object === false ||
bin.localStorage instanceof Object === false
) {
this.cache = {};
const ls = self.localStorage;
for ( let i = 0; i < ls.length; i++ ) {
const key = ls.key(i);
this.cache[key] = ls.getItem(key);
}
browser.storage.local.set({ localStorage: this.cache });
} else {
this.cache = bin.localStorage;
}
} catch(ex) {
}
if ( this.cache instanceof Object === false ) {
this.cache = {};
}
resolve();
browser.storage.onChanged.addListener(onChanged);
self.addEventListener('beforeunload', ( ) => {
this.cache = undefined;
browser.storage.onChanged.removeListener(onChanged);
});
});
});
return this.cache;
},
clear: function() {
this.cache = {};
return browser.storage.local.set({ localStorage: this.cache });
},
getItem: function(key) {
if ( this.cache instanceof Object === false ) {
console.info(`localStorage.getItem('${key}') not ready`);
return null;
}
const value = this.cache[key];
return value !== undefined ? value : null;
vAPI.messaging.send('vapi', {
what: 'localStorage',
fn: 'clear',
});
},
getItemAsync: function(key) {
return this.start().then(( ) => {
const value = this.cache[key];
return value !== undefined ? value : null;
return vAPI.messaging.send('vapi', {
what: 'localStorage',
fn: 'getItemAsync',
args: [ key ],
});
},
removeItem: function(key) {
this.setItem(key);
},
setItem: function(key, value = undefined) {
return this.start().then(( ) => {
if ( value === this.cache[key] ) { return; }
this.cache[key] = value;
return browser.storage.local.set({ localStorage: this.cache });
return vAPI.messaging.send('vapi', {
what: 'localStorage',
fn: 'removeItem',
args: [ key ],
});
},
setItem: function(key, value = undefined) {
return vAPI.messaging.send('vapi', {
what: 'localStorage',
fn: 'setItem',
args: [ key, value ]
});
},
cache: undefined,
};
vAPI.localStorage.start();

View File

@ -202,21 +202,29 @@ uDom('#userFiltersRevert').on('click', revertChanges);
// https://github.com/gorhill/uBlock/issues/3706
// Save/restore cursor position
//
// CoreMirror reference: https://codemirror.net/doc/manual.html#api_selection
renderUserFilters().then(( ) => {
// CodeMirror reference: https://codemirror.net/doc/manual.html#api_selection
{
let curline = 0;
let timer;
renderUserFilters().then(( ) => {
cmEditor.clearHistory();
return vAPI.localStorage.getItemAsync('myFiltersCursorPosition');
}).then(line => {
}).then(line => {
if ( typeof line === 'number' ) {
cmEditor.setCursor(line, 0);
}
cmEditor.on('cursorActivity', ( ) => {
const line = cmEditor.getCursor().line;
if ( vAPI.localStorage.getItem('myFiltersCursorPosition') !== line ) {
vAPI.localStorage.setItem('myFiltersCursorPosition', line);
}
if ( timer !== undefined ) { return; }
if ( cmEditor.getCursor().line === curline ) { return; }
timer = vAPI.setTimeout(( ) => {
timer = undefined;
curline = cmEditor.getCursor().line;
vAPI.localStorage.setItem('myFiltersCursorPosition', curline);
}, 701);
});
});
});
}
cmEditor.on('changes', userFiltersChanged);
CodeMirror.commands.save = applyChanges;