Work toward modernizing code base: promisification

Swathes of code have been converted to use
Promises/async/await.

Related commits:
- 3224d9b5cc
- 26235d80d0
- 0051f3b5c7
- eec53c0154
- 915687fddb
- 55cc0c6997
- e27328f931
This commit is contained in:
Raymond Hill 2019-09-18 08:34:55 -04:00
parent 78f430678a
commit 022951547c
No known key found for this signature in database
GPG Key ID: 25E1490B761470C2
5 changed files with 332 additions and 286 deletions

View File

@ -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 };
})();
/******************************************************************************/

View File

@ -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;

View File

@ -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);
});

View File

@ -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;

View File

@ -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