diff --git a/platform/chromium/vapi-background.js b/platform/chromium/vapi-background.js index 461a8e797..5cc26b5cd 100644 --- a/platform/chromium/vapi-background.js +++ b/platform/chromium/vapi-background.js @@ -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 = (( ) => { diff --git a/platform/chromium/vapi-common.js b/platform/chromium/vapi-common.js index 34b1d6d64..7fc1c96ad 100644 --- a/platform/chromium/vapi-common.js +++ b/platform/chromium/vapi-common.js @@ -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(); + diff --git a/src/js/1p-filters.js b/src/js/1p-filters.js index 1cc6cc3c6..28217dd5a 100644 --- a/src/js/1p-filters.js +++ b/src/js/1p-filters.js @@ -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(( ) => { - cmEditor.clearHistory(); - return vAPI.localStorage.getItemAsync('myFiltersCursorPosition'); -}).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); +// 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 => { + if ( typeof line === 'number' ) { + cmEditor.setCursor(line, 0); } + cmEditor.on('cursorActivity', ( ) => { + 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;