diff --git a/platform/chromium/vapi-background.js b/platform/chromium/vapi-background.js index 9441622c7..3f4d4cc79 100644 --- a/platform/chromium/vapi-background.js +++ b/platform/chromium/vapi-background.js @@ -32,22 +32,22 @@ /******************************************************************************/ /******************************************************************************/ -const chrome = self.chrome; -const manifest = chrome.runtime.getManifest(); +const browser = self.browser; +const manifest = browser.runtime.getManifest(); vAPI.cantWebsocket = - chrome.webRequest.ResourceType instanceof Object === false || - chrome.webRequest.ResourceType.WEBSOCKET !== 'websocket'; + browser.webRequest.ResourceType instanceof Object === false || + browser.webRequest.ResourceType.WEBSOCKET !== 'websocket'; vAPI.lastError = function() { - return chrome.runtime.lastError; + return browser.runtime.lastError; }; // https://github.com/gorhill/uBlock/issues/875 // https://code.google.com/p/chromium/issues/detail?id=410868#c8 // Must not leave `lastError` unchecked. vAPI.resetLastError = function() { - void chrome.runtime.lastError; + void browser.runtime.lastError; }; vAPI.supportsUserStylesheets = vAPI.webextFlavor.soup.has('user_stylesheet'); @@ -109,7 +109,7 @@ vAPI.storage = webext.storage.local; /******************************************************************************/ // https://github.com/gorhill/uMatrix/issues/234 -// https://developer.chrome.com/extensions/privacy#property-network +// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/privacy/network // 2015-08-12: Wrapped Chrome API in try-catch statements. I had a fluke // event in which it appeared Chrome 46 decided to restart uBlock (for @@ -123,8 +123,8 @@ vAPI.storage = webext.storage.local; // values. vAPI.browserSettings = (( ) => { - // Not all platforms support `chrome.privacy`. - if ( chrome.privacy instanceof Object === false ) { return; } + // Not all platforms support `browser.privacy`. + if ( browser.privacy instanceof Object === false ) { return; } return { // Whether the WebRTC-related privacy API is crashy is an open question @@ -181,19 +181,19 @@ vAPI.browserSettings = (( ) => { // crash. if ( this.webRTCSupported !== true ) { return; } - const cp = chrome.privacy; - const cpn = cp.network; + const bp = browser.privacy; + const bpn = bp.network; // Older version of Chromium do not support this setting, and is // marked as "deprecated" since Chromium 48. - if ( typeof cpn.webRTCMultipleRoutesEnabled === 'object' ) { + if ( typeof bpn.webRTCMultipleRoutesEnabled === 'object' ) { try { if ( setting ) { - cpn.webRTCMultipleRoutesEnabled.clear({ + bpn.webRTCMultipleRoutesEnabled.clear({ scope: 'regular' }, vAPI.resetLastError); } else { - cpn.webRTCMultipleRoutesEnabled.set({ + bpn.webRTCMultipleRoutesEnabled.set({ value: false, scope: 'regular' }, vAPI.resetLastError); @@ -204,10 +204,10 @@ vAPI.browserSettings = (( ) => { } // This setting became available in Chromium 48. - if ( typeof cpn.webRTCIPHandlingPolicy === 'object' ) { + if ( typeof bpn.webRTCIPHandlingPolicy === 'object' ) { try { if ( setting ) { - cpn.webRTCIPHandlingPolicy.clear({ + bpn.webRTCIPHandlingPolicy.clear({ scope: 'regular' }, vAPI.resetLastError); } else { @@ -216,7 +216,7 @@ vAPI.browserSettings = (( ) => { // https://github.com/gorhill/uBlock/issues/3009 // Firefox currently works differently, use // `default_public_interface_only` for now. - cpn.webRTCIPHandlingPolicy.set({ + bpn.webRTCIPHandlingPolicy.set({ value: vAPI.webextFlavor.soup.has('chromium') ? 'disable_non_proxied_udp' : 'default_public_interface_only', @@ -239,11 +239,11 @@ vAPI.browserSettings = (( ) => { const enabled = !!details[setting]; try { if ( enabled ) { - chrome.privacy.network.networkPredictionEnabled.clear({ + browser.privacy.network.networkPredictionEnabled.clear({ scope: 'regular' }, vAPI.resetLastError); } else { - chrome.privacy.network.networkPredictionEnabled.set({ + browser.privacy.network.networkPredictionEnabled.set({ value: false, scope: 'regular' }, vAPI.resetLastError); @@ -259,11 +259,11 @@ vAPI.browserSettings = (( ) => { case 'hyperlinkAuditing': try { if ( !!details[setting] ) { - chrome.privacy.websites.hyperlinkAuditingEnabled.clear({ + browser.privacy.websites.hyperlinkAuditingEnabled.clear({ scope: 'regular' }, vAPI.resetLastError); } else { - chrome.privacy.websites.hyperlinkAuditingEnabled.set({ + browser.privacy.websites.hyperlinkAuditingEnabled.set({ value: false, scope: 'regular' }, vAPI.resetLastError); @@ -302,8 +302,8 @@ const toTabId = function(tabId) { : 0; }; -// https://developer.chrome.com/extensions/webNavigation -// https://developer.chrome.com/extensions/tabs +// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/webNavigation +// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/tabs vAPI.Tabs = class { constructor() { @@ -546,10 +546,9 @@ vAPI.Tabs = class { return; } - // https://developer.chrome.com/extensions/tabs#method-query + // https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/tabs/query#Parameters // "Note that fragment identifiers are not matched." - // It's a lie, fragment identifiers ARE matched. So we need to remove - // the fragment. + // Fragment identifiers ARE matched -- we need to remove the fragment. const pos = targetURL.indexOf('#'); const targetURLWithoutHash = pos === -1 ? targetURL @@ -706,7 +705,7 @@ if ( browser.windows instanceof Object ) { // Ensure ImageData for toolbar icon is valid before use. vAPI.setIcon = (( ) => { - const browserAction = chrome.browserAction; + const browserAction = browser.browserAction; const titleTemplate = browser.runtime.getManifest().browser_action.default_title + ' ({badge})'; @@ -841,7 +840,7 @@ vAPI.setIcon = (( ) => { }; })(); -chrome.browserAction.onClicked.addListener(function(tab) { +browser.browserAction.onClicked.addListener(function(tab) { vAPI.tabs.open({ select: true, url: 'popup.html?tabId=' + tab.id + '&responsive=1' @@ -1245,11 +1244,11 @@ vAPI.Net = class { // https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/contextMenus#Browser_compatibility // Firefox for Android does no support browser.contextMenus. -vAPI.contextMenu = chrome.contextMenus && { +vAPI.contextMenu = browser.contextMenus && { _callback: null, _entries: [], _createEntry: function(entry) { - chrome.contextMenus.create( + browser.contextMenus.create( JSON.parse(JSON.stringify(entry)), vAPI.resetLastError ); @@ -1263,12 +1262,12 @@ vAPI.contextMenu = chrome.contextMenus && { const newEntry = entries[i]; if ( oldEntryId && newEntry ) { if ( newEntry.id !== oldEntryId ) { - chrome.contextMenus.remove(oldEntryId); + browser.contextMenus.remove(oldEntryId); this._createEntry(newEntry); this._entries[i] = newEntry.id; } } else if ( oldEntryId && !newEntry ) { - chrome.contextMenus.remove(oldEntryId); + browser.contextMenus.remove(oldEntryId); } else if ( !oldEntryId && newEntry ) { this._createEntry(newEntry); this._entries[i] = newEntry.id; @@ -1280,10 +1279,10 @@ vAPI.contextMenu = chrome.contextMenus && { return; } if ( n !== 0 && callback !== null ) { - chrome.contextMenus.onClicked.addListener(callback); + browser.contextMenus.onClicked.addListener(callback); this._callback = callback; } else if ( n === 0 && this._callback !== null ) { - chrome.contextMenus.onClicked.removeListener(this._callback); + browser.contextMenus.onClicked.removeListener(this._callback); this._callback = null; } } @@ -1292,7 +1291,7 @@ vAPI.contextMenu = chrome.contextMenus && { /******************************************************************************/ /******************************************************************************/ -vAPI.commands = chrome.commands; +vAPI.commands = browser.commands; /******************************************************************************/ /******************************************************************************/ @@ -1334,26 +1333,34 @@ vAPI.adminStorage = (( ) => { /******************************************************************************/ /******************************************************************************/ -vAPI.cloud = (function() { - // Not all platforms support `chrome.storage.sync`. - if ( chrome.storage.sync instanceof Object === false ) { - return; - } +// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/storage/sync - let chunkCountPerFetch = 16; // Must be a power of 2 +vAPI.cloud = (( ) => { + // Not all platforms support `webext.storage.sync`. + if ( webext.storage.sync instanceof Object === false ) { return; } - // Mind chrome.storage.sync.MAX_ITEMS (512 at time of writing) - let maxChunkCountPerItem = Math.floor(512 * 0.75) & ~(chunkCountPerFetch - 1); + // Currently, only Chromium supports the following constants -- these + // values will be assumed for platforms which do not define them. + // https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/storage/sync + // > You can store up to 100KB of data using this API + const MAX_ITEMS = + webext.storage.sync.MAX_ITEMS || 512; + const QUOTA_BYTES = + webext.storage.sync.QUOTA_BYTES || 102400; + const QUOTA_BYTES_PER_ITEM = + webext.storage.sync.QUOTA_BYTES_PER_ITEM || 8192; + + const chunkCountPerFetch = 16; // Must be a power of 2 + const maxChunkCountPerItem = Math.floor(MAX_ITEMS * 0.75) & ~(chunkCountPerFetch - 1); - // Mind chrome.storage.sync.QUOTA_BYTES_PER_ITEM (8192 at time of writing) // https://github.com/gorhill/uBlock/issues/3006 - // For Firefox, we will use a lower ratio to allow for more overhead for - // the infrastructure. Unfortunately this leads to less usable space for - // actual data, but all of this is provided for free by browser vendors, - // so we need to accept and deal with these limitations. - let evalMaxChunkSize = function() { + // For Firefox, we will use a lower ratio to allow for more overhead for + // the infrastructure. Unfortunately this leads to less usable space for + // actual data, but all of this is provided for free by browser vendors, + // so we need to accept and deal with these limitations. + const evalMaxChunkSize = function() { return Math.floor( - (chrome.storage.sync.QUOTA_BYTES_PER_ITEM || 8192) * + QUOTA_BYTES_PER_ITEM * (vAPI.webextFlavor.soup.has('firefox') ? 0.6 : 0.75) ); }; @@ -1366,13 +1373,9 @@ vAPI.cloud = (function() { maxChunkSize = evalMaxChunkSize(); }, { once: true }); - // Mind chrome.storage.sync.QUOTA_BYTES (128 kB at time of writing) - // Firefox: - // https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/storage/sync - // > You can store up to 100KB of data using this API/ - let maxStorageSize = chrome.storage.sync.QUOTA_BYTES || 102400; + const maxStorageSize = QUOTA_BYTES; - let options = { + const options = { defaultDeviceName: window.navigator.platform, deviceName: vAPI.localStorage.getItem('deviceName') || '' }; @@ -1384,34 +1387,32 @@ vAPI.cloud = (function() { // good thing given chrome.storage.sync.MAX_WRITE_OPERATIONS_PER_MINUTE // and chrome.storage.sync.MAX_WRITE_OPERATIONS_PER_HOUR. - let getCoarseChunkCount = function(dataKey, callback) { - let bin = {}; + const getCoarseChunkCount = async function(dataKey) { + const keys = {}; for ( let i = 0; i < maxChunkCountPerItem; i += 16 ) { - bin[dataKey + i.toString()] = ''; + keys[dataKey + i.toString()] = ''; } - - chrome.storage.sync.get(bin, function(bin) { - if ( chrome.runtime.lastError ) { - return callback(0, chrome.runtime.lastError.message); - } - - let chunkCount = 0; - for ( let i = 0; i < maxChunkCountPerItem; i += 16 ) { - if ( bin[dataKey + i.toString()] === '' ) { break; } - chunkCount = i + 16; - } - - callback(chunkCount); - }); + let bin; + try { + bin = await webext.storage.sync.get(keys); + } catch (reason) { + return reason; + } + let chunkCount = 0; + for ( let i = 0; i < maxChunkCountPerItem; i += 16 ) { + if ( bin[dataKey + i.toString()] === '' ) { break; } + chunkCount = i + 16; + } + return chunkCount; }; - let deleteChunks = function(dataKey, start) { - let keys = []; + const deleteChunks = function(dataKey, start) { + const keys = []; // No point in deleting more than: // - The max number of chunks per item // - The max number of chunks per storage limit - let n = Math.min( + const n = Math.min( maxChunkCountPerItem, Math.ceil(maxStorageSize / maxChunkSize) ); @@ -1419,14 +1420,11 @@ vAPI.cloud = (function() { keys.push(dataKey + i.toString()); } if ( keys.length !== 0 ) { - chrome.storage.sync.remove(keys); + webext.storage.sync.remove(keys); } }; - let start = function(/* dataKeys */) { - }; - - let push = function(dataKey, data, callback) { + const push = async function(dataKey, data) { let bin = { 'source': options.deviceName || options.defaultDeviceName, @@ -1435,7 +1433,7 @@ vAPI.cloud = (function() { 'size': 0 }; bin.size = JSON.stringify(bin).length; - let item = JSON.stringify(bin); + const item = JSON.stringify(bin); // Chunkify taking into account QUOTA_BYTES_PER_ITEM: // https://developer.chrome.com/extensions/storage#property-sync @@ -1449,84 +1447,77 @@ vAPI.cloud = (function() { } bin[dataKey + chunkCount.toString()] = ''; // Sentinel - chrome.storage.sync.set(bin, function() { - let errorStr; - if ( chrome.runtime.lastError ) { - errorStr = chrome.runtime.lastError.message; - // https://github.com/gorhill/uBlock/issues/3006#issuecomment-332597677 - // - Delete all that was pushed in case of failure. - // - It's unknown whether such issue applies only to Firefox: - // until such cases are reported for other browsers, we will - // reset the (now corrupted) content of the cloud storage - // only on Firefox. - if ( vAPI.webextFlavor.soup.has('firefox') ) { - chunkCount = 0; - } - } - callback(errorStr); + let result; + let errorStr; + try { + result = await webext.storage.sync.set(bin); + } catch (reason) { + errorStr = reason; + } - // Remove potentially unused trailing chunks - deleteChunks(dataKey, chunkCount); - }); + // https://github.com/gorhill/uBlock/issues/3006#issuecomment-332597677 + // - Delete all that was pushed in case of failure. + // - It's unknown whether such issue applies only to Firefox: + // until such cases are reported for other browsers, we will + // reset the (now corrupted) content of the cloud storage + // only on Firefox. + if ( errorStr !== undefined && vAPI.webextFlavor.soup.has('firefox') ) { + chunkCount = 0; + } + + // Remove potentially unused trailing chunks + deleteChunks(dataKey, chunkCount); + + return errorStr; }; - let pull = function(dataKey, callback) { + const pull = async function(dataKey) { - let assembleChunks = function(bin) { - if ( chrome.runtime.lastError ) { - callback(null, chrome.runtime.lastError.message); - return; - } + const result = await getCoarseChunkCount(dataKey); + if ( typeof result !== 'number' ) { + return result; + } + const chunkKeys = {}; + for ( let i = 0; i < result; i++ ) { + chunkKeys[dataKey + i.toString()] = ''; + } - // Assemble chunks into a single string. - // https://www.reddit.com/r/uMatrix/comments/8lc9ia/my_rules_tab_hangs_with_cloud_storage_support/ - // Explicit sentinel is not necessarily present: this can - // happen when the number of chunks is a multiple of - // chunkCountPerFetch. Hence why we must also test against - // undefined. - let json = [], jsonSlice; - let i = 0; - for (;;) { - jsonSlice = bin[dataKey + i.toString()]; - if ( jsonSlice === '' || jsonSlice === undefined ) { break; } - json.push(jsonSlice); - i += 1; - } + let bin; + try { + bin = await webext.storage.sync.get(chunkKeys); + } catch (reason) { + return reason; + } - let entry = null; - try { - entry = JSON.parse(json.join('')); - } catch(ex) { - } - callback(entry); - }; - - let fetchChunks = function(coarseCount, errorStr) { - if ( coarseCount === 0 || typeof errorStr === 'string' ) { - callback(null, errorStr); - return; - } - - let bin = {}; - for ( let i = 0; i < coarseCount; i++ ) { - bin[dataKey + i.toString()] = ''; - } - - chrome.storage.sync.get(bin, assembleChunks); - }; - - getCoarseChunkCount(dataKey, fetchChunks); + // Assemble chunks into a single string. + // https://www.reddit.com/r/uMatrix/comments/8lc9ia/my_rules_tab_hangs_with_cloud_storage_support/ + // Explicit sentinel is not necessarily present: this can + // happen when the number of chunks is a multiple of + // chunkCountPerFetch. Hence why we must also test against + // undefined. + let json = [], jsonSlice; + let i = 0; + for (;;) { + jsonSlice = bin[dataKey + i.toString()]; + if ( jsonSlice === '' || jsonSlice === undefined ) { break; } + json.push(jsonSlice); + i += 1; + } + let entry = null; + try { + entry = JSON.parse(json.join('')); + } catch(ex) { + } + return entry; }; - let getOptions = function(callback) { + const getOptions = function(callback) { if ( typeof callback !== 'function' ) { return; } callback(options); }; - let setOptions = function(details, callback) { - if ( typeof details !== 'object' || details === null ) { - return; - } + const setOptions = function(details, callback) { + if ( typeof details !== 'object' || details === null ) { return; } if ( typeof details.deviceName === 'string' ) { vAPI.localStorage.setItem('deviceName', details.deviceName); @@ -1536,13 +1527,7 @@ vAPI.cloud = (function() { getOptions(callback); }; - return { - start: start, - push: push, - pull: pull, - getOptions: getOptions, - setOptions: setOptions - }; + return { push, pull, getOptions, setOptions }; })(); /******************************************************************************/ diff --git a/platform/chromium/vapi-client.js b/platform/chromium/vapi-client.js index 1b22f4d99..5acb284c4 100644 --- a/platform/chromium/vapi-client.js +++ b/platform/chromium/vapi-client.js @@ -83,28 +83,113 @@ vAPI.messaging = { msgIdGenerator: 1, shuttingDown: false, - Connection: function(handler, details) { - this.messaging = vAPI.messaging; - this.handler = handler; - this.id = details.id; - this.to = details.to; - this.toToken = details.toToken; - this.from = details.from; - this.fromToken = details.fromToken; - this.checkTimer = undefined; - // On Firefox it appears ports are not automatically disconnected - // when navigating to another page. - const ctor = this.messaging.Connection; - if ( ctor.pagehide !== undefined ) { return; } - ctor.pagehide = ( ) => { - for ( const connection of this.messaging.connections.values() ) { - connection.disconnect(); - connection.handler( - connection.toDetails('connectionBroken') - ); + Connection: class { + constructor(handler, details) { + this.messaging = vAPI.messaging; + this.handler = handler; + this.id = details.id; + this.to = details.to; + this.toToken = details.toToken; + this.from = details.from; + this.fromToken = details.fromToken; + this.checkTimer = undefined; + // On Firefox it appears ports are not automatically disconnected + // when navigating to another page. + const ctor = this.messaging.Connection; + if ( ctor.pagehide !== undefined ) { return; } + ctor.pagehide = ( ) => { + for ( const connection of this.messaging.connections.values() ) { + connection.disconnect(); + connection.handler( + connection.toDetails('connectionBroken') + ); + } + }; + window.addEventListener('pagehide', ctor.pagehide); + } + toDetails(what, payload) { + return { + what: what, + id: this.id, + from: this.from, + fromToken: this.fromToken, + to: this.to, + toToken: this.toToken, + payload: payload + }; + } + disconnect() { + if ( this.checkTimer !== undefined ) { + clearTimeout(this.checkTimer); + this.checkTimer = undefined; } - }; - window.addEventListener('pagehide', ctor.pagehide); + this.messaging.connections.delete(this.id); + const port = this.messaging.getPort(); + if ( port === null ) { return; } + port.postMessage({ + channelName: 'vapi', + msg: this.toDetails('connectionBroken') + }); + } + checkAsync() { + if ( this.checkTimer !== undefined ) { + clearTimeout(this.checkTimer); + } + this.checkTimer = vAPI.setTimeout( + ( ) => { this.check(); }, + 499 + ); + } + check() { + this.checkTimer = undefined; + if ( this.messaging.connections.has(this.id) === false ) { return; } + const port = this.messaging.getPort(); + if ( port === null ) { return; } + port.postMessage({ + channelName: 'vapi', + msg: this.toDetails('connectionCheck') + }); + this.checkAsync(); + } + receive(details) { + switch ( details.what ) { + case 'connectionAccepted': + this.toToken = details.toToken; + this.handler(details); + this.checkAsync(); + break; + case 'connectionBroken': + this.messaging.connections.delete(this.id); + this.handler(details); + break; + case 'connectionMessage': + this.handler(details); + this.checkAsync(); + break; + case 'connectionCheck': + const port = this.messaging.getPort(); + if ( port === null ) { return; } + if ( this.messaging.connections.has(this.id) ) { + this.checkAsync(); + } else { + details.what = 'connectionBroken'; + port.postMessage({ channelName: 'vapi', msg: details }); + } + break; + case 'connectionRefused': + this.messaging.connections.delete(this.id); + this.handler(details); + break; + } + } + send(payload) { + const port = this.messaging.getPort(); + if ( port === null ) { return; } + port.postMessage({ + channelName: 'vapi', + msg: this.toDetails('connectionMessage', payload) + }); + } }, shutdown: function() { @@ -125,7 +210,7 @@ vAPI.messaging = { disconnectListenerBound: null, messageListener: function(details) { - if ( !details ) { return; } + if ( details instanceof Object === false ) { return; } // Sent to all channels if ( details.broadcast ) { @@ -357,94 +442,6 @@ vAPI.messaging = { /******************************************************************************/ -vAPI.messaging.Connection.prototype = { - toDetails: function(what, payload) { - return { - what: what, - id: this.id, - from: this.from, - fromToken: this.fromToken, - to: this.to, - toToken: this.toToken, - payload: payload - }; - }, - disconnect: function() { - if ( this.checkTimer !== undefined ) { - clearTimeout(this.checkTimer); - this.checkTimer = undefined; - } - this.messaging.connections.delete(this.id); - const port = this.messaging.getPort(); - if ( port === null ) { return; } - port.postMessage({ - channelName: 'vapi', - msg: this.toDetails('connectionBroken') - }); - }, - checkAsync: function() { - if ( this.checkTimer !== undefined ) { - clearTimeout(this.checkTimer); - } - this.checkTimer = vAPI.setTimeout( - ( ) => { this.check(); }, - 499 - ); - }, - check: function() { - this.checkTimer = undefined; - if ( this.messaging.connections.has(this.id) === false ) { return; } - const port = this.messaging.getPort(); - if ( port === null ) { return; } - port.postMessage({ - channelName: 'vapi', - msg: this.toDetails('connectionCheck') - }); - this.checkAsync(); - }, - receive: function(details) { - switch ( details.what ) { - case 'connectionAccepted': - this.toToken = details.toToken; - this.handler(details); - this.checkAsync(); - break; - case 'connectionBroken': - this.messaging.connections.delete(this.id); - this.handler(details); - break; - case 'connectionMessage': - this.handler(details); - this.checkAsync(); - break; - case 'connectionCheck': - const port = this.messaging.getPort(); - if ( port === null ) { return; } - if ( this.messaging.connections.has(this.id) ) { - this.checkAsync(); - } else { - details.what = 'connectionBroken'; - port.postMessage({ channelName: 'vapi', msg: details }); - } - break; - case 'connectionRefused': - this.messaging.connections.delete(this.id); - this.handler(details); - break; - } - }, - send: function(payload) { - const port = this.messaging.getPort(); - if ( port === null ) { return; } - port.postMessage({ - channelName: 'vapi', - msg: this.toDetails('connectionMessage', payload) - }); - } -}; - -/******************************************************************************/ - vAPI.shutdown.add(function() { vAPI.messaging.shutdown(); window.vAPI = undefined; diff --git a/platform/chromium/webext.js b/platform/chromium/webext.js index 0712e5784..b5130e421 100644 --- a/platform/chromium/webext.js +++ b/platform/chromium/webext.js @@ -25,14 +25,16 @@ // the promisification of uBO progress. const webext = { // jshint ignore:line + // https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/storage storage: { + // https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/storage/local local: { clear: function() { return new Promise((resolve, reject) => { chrome.storage.local.clear(( ) => { const lastError = chrome.runtime.lastError; if ( lastError instanceof Object ) { - return reject(lastError); + return reject(lastError.message); } resolve(); }); @@ -43,7 +45,7 @@ const webext = { // jshint ignore:line chrome.storage.local.get(...arguments, result => { const lastError = chrome.runtime.lastError; if ( lastError instanceof Object ) { - return reject(lastError); + return reject(lastError.message); } resolve(result); }); @@ -54,7 +56,7 @@ const webext = { // jshint ignore:line chrome.storage.local.getBytesInUse(...arguments, result => { const lastError = chrome.runtime.lastError; if ( lastError instanceof Object ) { - return reject(lastError); + return reject(lastError.message); } resolve(result); }); @@ -65,7 +67,7 @@ const webext = { // jshint ignore:line chrome.storage.local.remove(...arguments, ( ) => { const lastError = chrome.runtime.lastError; if ( lastError instanceof Object ) { - return reject(lastError); + return reject(lastError.message); } resolve(); }); @@ -76,7 +78,7 @@ const webext = { // jshint ignore:line chrome.storage.local.set(...arguments, ( ) => { const lastError = chrome.runtime.lastError; if ( lastError instanceof Object ) { - return reject(lastError); + return reject(lastError.message); } resolve(); }); @@ -84,7 +86,7 @@ const webext = { // jshint ignore:line }, }, }, - + // https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/tabs tabs: { get: function() { return new Promise(resolve => { @@ -127,7 +129,7 @@ const webext = { // jshint ignore:line }); }, }, - + // https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/windows windows: { get: function() { return new Promise(resolve => { @@ -156,6 +158,73 @@ const webext = { // jshint ignore:line }, }; +// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/storage/sync +if ( chrome.storage.sync instanceof Object ) { + webext.storage.sync = { + QUOTA_BYTES: chrome.storage.sync.QUOTA_BYTES, + QUOTA_BYTES_PER_ITEM: chrome.storage.sync.QUOTA_BYTES_PER_ITEM, + MAX_ITEMS: chrome.storage.sync.MAX_ITEMS, + MAX_WRITE_OPERATIONS_PER_HOUR: chrome.storage.sync.MAX_WRITE_OPERATIONS_PER_HOUR, + MAX_WRITE_OPERATIONS_PER_MINUTE: chrome.storage.sync.MAX_WRITE_OPERATIONS_PER_MINUTE, + + clear: function() { + return new Promise((resolve, reject) => { + chrome.storage.sync.clear(( ) => { + const lastError = chrome.runtime.lastError; + if ( lastError instanceof Object ) { + return reject(lastError.message); + } + resolve(); + }); + }); + }, + get: function() { + return new Promise((resolve, reject) => { + chrome.storage.sync.get(...arguments, result => { + const lastError = chrome.runtime.lastError; + if ( lastError instanceof Object ) { + return reject(lastError.message); + } + resolve(result); + }); + }); + }, + getBytesInUse: function() { + return new Promise((resolve, reject) => { + chrome.storage.sync.getBytesInUse(...arguments, result => { + const lastError = chrome.runtime.lastError; + if ( lastError instanceof Object ) { + return reject(lastError.message); + } + resolve(result); + }); + }); + }, + remove: function() { + return new Promise((resolve, reject) => { + chrome.storage.sync.remove(...arguments, ( ) => { + const lastError = chrome.runtime.lastError; + if ( lastError instanceof Object ) { + return reject(lastError.message); + } + resolve(); + }); + }); + }, + set: function() { + return new Promise((resolve, reject) => { + chrome.storage.sync.set(...arguments, ( ) => { + const lastError = chrome.runtime.lastError; + if ( lastError instanceof Object ) { + return reject(lastError.message); + } + resolve(); + }); + }); + }, + }; +} + // https://bugs.chromium.org/p/chromium/issues/detail?id=608854 if ( chrome.tabs.removeCSS instanceof Function ) { webext.tabs.removeCSS = function() { @@ -168,6 +237,7 @@ if ( chrome.tabs.removeCSS instanceof Function ) { }; } +// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/storage/managed if ( chrome.storage.managed instanceof Object ) { webext.storage.managed = { get: function() { @@ -175,7 +245,7 @@ if ( chrome.storage.managed instanceof Object ) { chrome.storage.local.get(...arguments, result => { const lastError = chrome.runtime.lastError; if ( lastError instanceof Object ) { - return reject(lastError); + return reject(lastError.message); } resolve(result); }); diff --git a/src/js/messaging.js b/src/js/messaging.js index 4b3f22277..4eed67bbc 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -77,7 +77,7 @@ const onMessage = function(request, sender, callback) { return; case 'reloadAllFilters': - µb.loadFilterLists(); + µb.loadFilterLists().then(( ) => { callback(); }); return; case 'scriptlet': @@ -752,10 +752,14 @@ const onMessage = function(request, sender, callback) { return; case 'cloudPull': - return vAPI.cloud.pull(request.datakey, callback); + return vAPI.cloud.pull(request.datakey).then(result => { + callback(result); + }); case 'cloudPush': - return vAPI.cloud.push(request.datakey, request.data, callback); + return vAPI.cloud.push(request.datakey, request.data).then(result => { + callback(result); + }); default: break; diff --git a/src/js/start.js b/src/js/start.js index ff62e011b..4018c5187 100644 --- a/src/js/start.js +++ b/src/js/start.js @@ -318,16 +318,6 @@ initializeTabs(); : 0 ); -// vAPI.cloud is optional. -if ( µb.cloudStorageSupported ) { - vAPI.cloud.start([ - 'tpFiltersPane', - 'myFiltersPane', - 'myRulesPane', - 'whitelistPane' - ]); -} - µb.contextMenu.update(null); // https://github.com/uBlockOrigin/uBlock-issues/issues/717