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 browser = self.browser;
const manifest = chrome.runtime.getManifest(); const manifest = browser.runtime.getManifest();
vAPI.cantWebsocket = vAPI.cantWebsocket =
chrome.webRequest.ResourceType instanceof Object === false || browser.webRequest.ResourceType instanceof Object === false ||
chrome.webRequest.ResourceType.WEBSOCKET !== 'websocket'; browser.webRequest.ResourceType.WEBSOCKET !== 'websocket';
vAPI.lastError = function() { vAPI.lastError = function() {
return chrome.runtime.lastError; return browser.runtime.lastError;
}; };
// https://github.com/gorhill/uBlock/issues/875 // https://github.com/gorhill/uBlock/issues/875
// https://code.google.com/p/chromium/issues/detail?id=410868#c8 // https://code.google.com/p/chromium/issues/detail?id=410868#c8
// Must not leave `lastError` unchecked. // Must not leave `lastError` unchecked.
vAPI.resetLastError = function() { vAPI.resetLastError = function() {
void chrome.runtime.lastError; void browser.runtime.lastError;
}; };
vAPI.supportsUserStylesheets = vAPI.webextFlavor.soup.has('user_stylesheet'); 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://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 // 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 // event in which it appeared Chrome 46 decided to restart uBlock (for
@ -123,8 +123,8 @@ vAPI.storage = webext.storage.local;
// values. // values.
vAPI.browserSettings = (( ) => { vAPI.browserSettings = (( ) => {
// Not all platforms support `chrome.privacy`. // Not all platforms support `browser.privacy`.
if ( chrome.privacy instanceof Object === false ) { return; } if ( browser.privacy instanceof Object === false ) { return; }
return { return {
// Whether the WebRTC-related privacy API is crashy is an open question // Whether the WebRTC-related privacy API is crashy is an open question
@ -181,19 +181,19 @@ vAPI.browserSettings = (( ) => {
// crash. // crash.
if ( this.webRTCSupported !== true ) { return; } if ( this.webRTCSupported !== true ) { return; }
const cp = chrome.privacy; const bp = browser.privacy;
const cpn = cp.network; const bpn = bp.network;
// Older version of Chromium do not support this setting, and is // Older version of Chromium do not support this setting, and is
// marked as "deprecated" since Chromium 48. // marked as "deprecated" since Chromium 48.
if ( typeof cpn.webRTCMultipleRoutesEnabled === 'object' ) { if ( typeof bpn.webRTCMultipleRoutesEnabled === 'object' ) {
try { try {
if ( setting ) { if ( setting ) {
cpn.webRTCMultipleRoutesEnabled.clear({ bpn.webRTCMultipleRoutesEnabled.clear({
scope: 'regular' scope: 'regular'
}, vAPI.resetLastError); }, vAPI.resetLastError);
} else { } else {
cpn.webRTCMultipleRoutesEnabled.set({ bpn.webRTCMultipleRoutesEnabled.set({
value: false, value: false,
scope: 'regular' scope: 'regular'
}, vAPI.resetLastError); }, vAPI.resetLastError);
@ -204,10 +204,10 @@ vAPI.browserSettings = (( ) => {
} }
// This setting became available in Chromium 48. // This setting became available in Chromium 48.
if ( typeof cpn.webRTCIPHandlingPolicy === 'object' ) { if ( typeof bpn.webRTCIPHandlingPolicy === 'object' ) {
try { try {
if ( setting ) { if ( setting ) {
cpn.webRTCIPHandlingPolicy.clear({ bpn.webRTCIPHandlingPolicy.clear({
scope: 'regular' scope: 'regular'
}, vAPI.resetLastError); }, vAPI.resetLastError);
} else { } else {
@ -216,7 +216,7 @@ vAPI.browserSettings = (( ) => {
// https://github.com/gorhill/uBlock/issues/3009 // https://github.com/gorhill/uBlock/issues/3009
// Firefox currently works differently, use // Firefox currently works differently, use
// `default_public_interface_only` for now. // `default_public_interface_only` for now.
cpn.webRTCIPHandlingPolicy.set({ bpn.webRTCIPHandlingPolicy.set({
value: vAPI.webextFlavor.soup.has('chromium') value: vAPI.webextFlavor.soup.has('chromium')
? 'disable_non_proxied_udp' ? 'disable_non_proxied_udp'
: 'default_public_interface_only', : 'default_public_interface_only',
@ -239,11 +239,11 @@ vAPI.browserSettings = (( ) => {
const enabled = !!details[setting]; const enabled = !!details[setting];
try { try {
if ( enabled ) { if ( enabled ) {
chrome.privacy.network.networkPredictionEnabled.clear({ browser.privacy.network.networkPredictionEnabled.clear({
scope: 'regular' scope: 'regular'
}, vAPI.resetLastError); }, vAPI.resetLastError);
} else { } else {
chrome.privacy.network.networkPredictionEnabled.set({ browser.privacy.network.networkPredictionEnabled.set({
value: false, value: false,
scope: 'regular' scope: 'regular'
}, vAPI.resetLastError); }, vAPI.resetLastError);
@ -259,11 +259,11 @@ vAPI.browserSettings = (( ) => {
case 'hyperlinkAuditing': case 'hyperlinkAuditing':
try { try {
if ( !!details[setting] ) { if ( !!details[setting] ) {
chrome.privacy.websites.hyperlinkAuditingEnabled.clear({ browser.privacy.websites.hyperlinkAuditingEnabled.clear({
scope: 'regular' scope: 'regular'
}, vAPI.resetLastError); }, vAPI.resetLastError);
} else { } else {
chrome.privacy.websites.hyperlinkAuditingEnabled.set({ browser.privacy.websites.hyperlinkAuditingEnabled.set({
value: false, value: false,
scope: 'regular' scope: 'regular'
}, vAPI.resetLastError); }, vAPI.resetLastError);
@ -302,8 +302,8 @@ const toTabId = function(tabId) {
: 0; : 0;
}; };
// https://developer.chrome.com/extensions/webNavigation // https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/webNavigation
// https://developer.chrome.com/extensions/tabs // https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/tabs
vAPI.Tabs = class { vAPI.Tabs = class {
constructor() { constructor() {
@ -546,10 +546,9 @@ vAPI.Tabs = class {
return; 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." // "Note that fragment identifiers are not matched."
// It's a lie, fragment identifiers ARE matched. So we need to remove // Fragment identifiers ARE matched -- we need to remove the fragment.
// the fragment.
const pos = targetURL.indexOf('#'); const pos = targetURL.indexOf('#');
const targetURLWithoutHash = pos === -1 const targetURLWithoutHash = pos === -1
? targetURL ? targetURL
@ -706,7 +705,7 @@ if ( browser.windows instanceof Object ) {
// Ensure ImageData for toolbar icon is valid before use. // Ensure ImageData for toolbar icon is valid before use.
vAPI.setIcon = (( ) => { vAPI.setIcon = (( ) => {
const browserAction = chrome.browserAction; const browserAction = browser.browserAction;
const titleTemplate = const titleTemplate =
browser.runtime.getManifest().browser_action.default_title + browser.runtime.getManifest().browser_action.default_title +
' ({badge})'; ' ({badge})';
@ -841,7 +840,7 @@ vAPI.setIcon = (( ) => {
}; };
})(); })();
chrome.browserAction.onClicked.addListener(function(tab) { browser.browserAction.onClicked.addListener(function(tab) {
vAPI.tabs.open({ vAPI.tabs.open({
select: true, select: true,
url: 'popup.html?tabId=' + tab.id + '&responsive=1' 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 // https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/contextMenus#Browser_compatibility
// Firefox for Android does no support browser.contextMenus. // Firefox for Android does no support browser.contextMenus.
vAPI.contextMenu = chrome.contextMenus && { vAPI.contextMenu = browser.contextMenus && {
_callback: null, _callback: null,
_entries: [], _entries: [],
_createEntry: function(entry) { _createEntry: function(entry) {
chrome.contextMenus.create( browser.contextMenus.create(
JSON.parse(JSON.stringify(entry)), JSON.parse(JSON.stringify(entry)),
vAPI.resetLastError vAPI.resetLastError
); );
@ -1263,12 +1262,12 @@ vAPI.contextMenu = chrome.contextMenus && {
const newEntry = entries[i]; const newEntry = entries[i];
if ( oldEntryId && newEntry ) { if ( oldEntryId && newEntry ) {
if ( newEntry.id !== oldEntryId ) { if ( newEntry.id !== oldEntryId ) {
chrome.contextMenus.remove(oldEntryId); browser.contextMenus.remove(oldEntryId);
this._createEntry(newEntry); this._createEntry(newEntry);
this._entries[i] = newEntry.id; this._entries[i] = newEntry.id;
} }
} else if ( oldEntryId && !newEntry ) { } else if ( oldEntryId && !newEntry ) {
chrome.contextMenus.remove(oldEntryId); browser.contextMenus.remove(oldEntryId);
} else if ( !oldEntryId && newEntry ) { } else if ( !oldEntryId && newEntry ) {
this._createEntry(newEntry); this._createEntry(newEntry);
this._entries[i] = newEntry.id; this._entries[i] = newEntry.id;
@ -1280,10 +1279,10 @@ vAPI.contextMenu = chrome.contextMenus && {
return; return;
} }
if ( n !== 0 && callback !== null ) { if ( n !== 0 && callback !== null ) {
chrome.contextMenus.onClicked.addListener(callback); browser.contextMenus.onClicked.addListener(callback);
this._callback = callback; this._callback = callback;
} else if ( n === 0 && this._callback !== null ) { } else if ( n === 0 && this._callback !== null ) {
chrome.contextMenus.onClicked.removeListener(this._callback); browser.contextMenus.onClicked.removeListener(this._callback);
this._callback = null; 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() { // https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/storage/sync
// Not all platforms support `chrome.storage.sync`.
if ( chrome.storage.sync instanceof Object === false ) {
return;
}
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) // Currently, only Chromium supports the following constants -- these
let maxChunkCountPerItem = Math.floor(512 * 0.75) & ~(chunkCountPerFetch - 1); // 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 // https://github.com/gorhill/uBlock/issues/3006
// For Firefox, we will use a lower ratio to allow for more overhead for // For Firefox, we will use a lower ratio to allow for more overhead for
// the infrastructure. Unfortunately this leads to less usable space for // the infrastructure. Unfortunately this leads to less usable space for
// actual data, but all of this is provided for free by browser vendors, // actual data, but all of this is provided for free by browser vendors,
// so we need to accept and deal with these limitations. // so we need to accept and deal with these limitations.
let evalMaxChunkSize = function() { const evalMaxChunkSize = function() {
return Math.floor( return Math.floor(
(chrome.storage.sync.QUOTA_BYTES_PER_ITEM || 8192) * QUOTA_BYTES_PER_ITEM *
(vAPI.webextFlavor.soup.has('firefox') ? 0.6 : 0.75) (vAPI.webextFlavor.soup.has('firefox') ? 0.6 : 0.75)
); );
}; };
@ -1366,13 +1373,9 @@ vAPI.cloud = (function() {
maxChunkSize = evalMaxChunkSize(); maxChunkSize = evalMaxChunkSize();
}, { once: true }); }, { once: true });
// Mind chrome.storage.sync.QUOTA_BYTES (128 kB at time of writing) const maxStorageSize = QUOTA_BYTES;
// 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;
let options = { const options = {
defaultDeviceName: window.navigator.platform, defaultDeviceName: window.navigator.platform,
deviceName: vAPI.localStorage.getItem('deviceName') || '' deviceName: vAPI.localStorage.getItem('deviceName') || ''
}; };
@ -1384,34 +1387,32 @@ vAPI.cloud = (function() {
// good thing given chrome.storage.sync.MAX_WRITE_OPERATIONS_PER_MINUTE // good thing given chrome.storage.sync.MAX_WRITE_OPERATIONS_PER_MINUTE
// and chrome.storage.sync.MAX_WRITE_OPERATIONS_PER_HOUR. // and chrome.storage.sync.MAX_WRITE_OPERATIONS_PER_HOUR.
let getCoarseChunkCount = function(dataKey, callback) { const getCoarseChunkCount = async function(dataKey) {
let bin = {}; const keys = {};
for ( let i = 0; i < maxChunkCountPerItem; i += 16 ) { for ( let i = 0; i < maxChunkCountPerItem; i += 16 ) {
bin[dataKey + i.toString()] = ''; keys[dataKey + i.toString()] = '';
} }
let bin;
chrome.storage.sync.get(bin, function(bin) { try {
if ( chrome.runtime.lastError ) { bin = await webext.storage.sync.get(keys);
return callback(0, chrome.runtime.lastError.message); } catch (reason) {
} return reason;
}
let chunkCount = 0; let chunkCount = 0;
for ( let i = 0; i < maxChunkCountPerItem; i += 16 ) { for ( let i = 0; i < maxChunkCountPerItem; i += 16 ) {
if ( bin[dataKey + i.toString()] === '' ) { break; } if ( bin[dataKey + i.toString()] === '' ) { break; }
chunkCount = i + 16; chunkCount = i + 16;
} }
return chunkCount;
callback(chunkCount);
});
}; };
let deleteChunks = function(dataKey, start) { const deleteChunks = function(dataKey, start) {
let keys = []; const keys = [];
// No point in deleting more than: // No point in deleting more than:
// - The max number of chunks per item // - The max number of chunks per item
// - The max number of chunks per storage limit // - The max number of chunks per storage limit
let n = Math.min( const n = Math.min(
maxChunkCountPerItem, maxChunkCountPerItem,
Math.ceil(maxStorageSize / maxChunkSize) Math.ceil(maxStorageSize / maxChunkSize)
); );
@ -1419,14 +1420,11 @@ vAPI.cloud = (function() {
keys.push(dataKey + i.toString()); keys.push(dataKey + i.toString());
} }
if ( keys.length !== 0 ) { if ( keys.length !== 0 ) {
chrome.storage.sync.remove(keys); webext.storage.sync.remove(keys);
} }
}; };
let start = function(/* dataKeys */) { const push = async function(dataKey, data) {
};
let push = function(dataKey, data, callback) {
let bin = { let bin = {
'source': options.deviceName || options.defaultDeviceName, 'source': options.deviceName || options.defaultDeviceName,
@ -1435,7 +1433,7 @@ vAPI.cloud = (function() {
'size': 0 'size': 0
}; };
bin.size = JSON.stringify(bin).length; bin.size = JSON.stringify(bin).length;
let item = JSON.stringify(bin); const item = JSON.stringify(bin);
// Chunkify taking into account QUOTA_BYTES_PER_ITEM: // Chunkify taking into account QUOTA_BYTES_PER_ITEM:
// https://developer.chrome.com/extensions/storage#property-sync // https://developer.chrome.com/extensions/storage#property-sync
@ -1449,84 +1447,77 @@ vAPI.cloud = (function() {
} }
bin[dataKey + chunkCount.toString()] = ''; // Sentinel bin[dataKey + chunkCount.toString()] = ''; // Sentinel
chrome.storage.sync.set(bin, function() { let result;
let errorStr; let errorStr;
if ( chrome.runtime.lastError ) { try {
errorStr = chrome.runtime.lastError.message; result = await webext.storage.sync.set(bin);
// https://github.com/gorhill/uBlock/issues/3006#issuecomment-332597677 } catch (reason) {
// - Delete all that was pushed in case of failure. errorStr = reason;
// - 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);
// Remove potentially unused trailing chunks // https://github.com/gorhill/uBlock/issues/3006#issuecomment-332597677
deleteChunks(dataKey, chunkCount); // - 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) { const result = await getCoarseChunkCount(dataKey);
if ( chrome.runtime.lastError ) { if ( typeof result !== 'number' ) {
callback(null, chrome.runtime.lastError.message); return result;
return; }
} const chunkKeys = {};
for ( let i = 0; i < result; i++ ) {
chunkKeys[dataKey + i.toString()] = '';
}
// Assemble chunks into a single string. let bin;
// https://www.reddit.com/r/uMatrix/comments/8lc9ia/my_rules_tab_hangs_with_cloud_storage_support/ try {
// Explicit sentinel is not necessarily present: this can bin = await webext.storage.sync.get(chunkKeys);
// happen when the number of chunks is a multiple of } catch (reason) {
// chunkCountPerFetch. Hence why we must also test against return reason;
// 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; // Assemble chunks into a single string.
try { // https://www.reddit.com/r/uMatrix/comments/8lc9ia/my_rules_tab_hangs_with_cloud_storage_support/
entry = JSON.parse(json.join('')); // Explicit sentinel is not necessarily present: this can
} catch(ex) { // happen when the number of chunks is a multiple of
} // chunkCountPerFetch. Hence why we must also test against
callback(entry); // undefined.
}; let json = [], jsonSlice;
let i = 0;
let fetchChunks = function(coarseCount, errorStr) { for (;;) {
if ( coarseCount === 0 || typeof errorStr === 'string' ) { jsonSlice = bin[dataKey + i.toString()];
callback(null, errorStr); if ( jsonSlice === '' || jsonSlice === undefined ) { break; }
return; json.push(jsonSlice);
} i += 1;
}
let bin = {}; let entry = null;
for ( let i = 0; i < coarseCount; i++ ) { try {
bin[dataKey + i.toString()] = ''; entry = JSON.parse(json.join(''));
} } catch(ex) {
}
chrome.storage.sync.get(bin, assembleChunks); return entry;
};
getCoarseChunkCount(dataKey, fetchChunks);
}; };
let getOptions = function(callback) { const getOptions = function(callback) {
if ( typeof callback !== 'function' ) { return; } if ( typeof callback !== 'function' ) { return; }
callback(options); callback(options);
}; };
let setOptions = function(details, callback) { const setOptions = function(details, callback) {
if ( typeof details !== 'object' || details === null ) { if ( typeof details !== 'object' || details === null ) { return; }
return;
}
if ( typeof details.deviceName === 'string' ) { if ( typeof details.deviceName === 'string' ) {
vAPI.localStorage.setItem('deviceName', details.deviceName); vAPI.localStorage.setItem('deviceName', details.deviceName);
@ -1536,13 +1527,7 @@ vAPI.cloud = (function() {
getOptions(callback); getOptions(callback);
}; };
return { return { push, pull, getOptions, setOptions };
start: start,
push: push,
pull: pull,
getOptions: getOptions,
setOptions: setOptions
};
})(); })();
/******************************************************************************/ /******************************************************************************/

View File

@ -83,28 +83,113 @@ vAPI.messaging = {
msgIdGenerator: 1, msgIdGenerator: 1,
shuttingDown: false, shuttingDown: false,
Connection: function(handler, details) { Connection: class {
this.messaging = vAPI.messaging; constructor(handler, details) {
this.handler = handler; this.messaging = vAPI.messaging;
this.id = details.id; this.handler = handler;
this.to = details.to; this.id = details.id;
this.toToken = details.toToken; this.to = details.to;
this.from = details.from; this.toToken = details.toToken;
this.fromToken = details.fromToken; this.from = details.from;
this.checkTimer = undefined; this.fromToken = details.fromToken;
// On Firefox it appears ports are not automatically disconnected this.checkTimer = undefined;
// when navigating to another page. // On Firefox it appears ports are not automatically disconnected
const ctor = this.messaging.Connection; // when navigating to another page.
if ( ctor.pagehide !== undefined ) { return; } const ctor = this.messaging.Connection;
ctor.pagehide = ( ) => { if ( ctor.pagehide !== undefined ) { return; }
for ( const connection of this.messaging.connections.values() ) { ctor.pagehide = ( ) => {
connection.disconnect(); for ( const connection of this.messaging.connections.values() ) {
connection.handler( connection.disconnect();
connection.toDetails('connectionBroken') 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;
} }
}; this.messaging.connections.delete(this.id);
window.addEventListener('pagehide', ctor.pagehide); 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() { shutdown: function() {
@ -125,7 +210,7 @@ vAPI.messaging = {
disconnectListenerBound: null, disconnectListenerBound: null,
messageListener: function(details) { messageListener: function(details) {
if ( !details ) { return; } if ( details instanceof Object === false ) { return; }
// Sent to all channels // Sent to all channels
if ( details.broadcast ) { 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.shutdown.add(function() {
vAPI.messaging.shutdown(); vAPI.messaging.shutdown();
window.vAPI = undefined; window.vAPI = undefined;

View File

@ -25,14 +25,16 @@
// the promisification of uBO progress. // the promisification of uBO progress.
const webext = { // jshint ignore:line const webext = { // jshint ignore:line
// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/storage
storage: { storage: {
// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/storage/local
local: { local: {
clear: function() { clear: function() {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
chrome.storage.local.clear(( ) => { chrome.storage.local.clear(( ) => {
const lastError = chrome.runtime.lastError; const lastError = chrome.runtime.lastError;
if ( lastError instanceof Object ) { if ( lastError instanceof Object ) {
return reject(lastError); return reject(lastError.message);
} }
resolve(); resolve();
}); });
@ -43,7 +45,7 @@ const webext = { // jshint ignore:line
chrome.storage.local.get(...arguments, result => { chrome.storage.local.get(...arguments, result => {
const lastError = chrome.runtime.lastError; const lastError = chrome.runtime.lastError;
if ( lastError instanceof Object ) { if ( lastError instanceof Object ) {
return reject(lastError); return reject(lastError.message);
} }
resolve(result); resolve(result);
}); });
@ -54,7 +56,7 @@ const webext = { // jshint ignore:line
chrome.storage.local.getBytesInUse(...arguments, result => { chrome.storage.local.getBytesInUse(...arguments, result => {
const lastError = chrome.runtime.lastError; const lastError = chrome.runtime.lastError;
if ( lastError instanceof Object ) { if ( lastError instanceof Object ) {
return reject(lastError); return reject(lastError.message);
} }
resolve(result); resolve(result);
}); });
@ -65,7 +67,7 @@ const webext = { // jshint ignore:line
chrome.storage.local.remove(...arguments, ( ) => { chrome.storage.local.remove(...arguments, ( ) => {
const lastError = chrome.runtime.lastError; const lastError = chrome.runtime.lastError;
if ( lastError instanceof Object ) { if ( lastError instanceof Object ) {
return reject(lastError); return reject(lastError.message);
} }
resolve(); resolve();
}); });
@ -76,7 +78,7 @@ const webext = { // jshint ignore:line
chrome.storage.local.set(...arguments, ( ) => { chrome.storage.local.set(...arguments, ( ) => {
const lastError = chrome.runtime.lastError; const lastError = chrome.runtime.lastError;
if ( lastError instanceof Object ) { if ( lastError instanceof Object ) {
return reject(lastError); return reject(lastError.message);
} }
resolve(); resolve();
}); });
@ -84,7 +86,7 @@ const webext = { // jshint ignore:line
}, },
}, },
}, },
// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/tabs
tabs: { tabs: {
get: function() { get: function() {
return new Promise(resolve => { 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: { windows: {
get: function() { get: function() {
return new Promise(resolve => { 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 // https://bugs.chromium.org/p/chromium/issues/detail?id=608854
if ( chrome.tabs.removeCSS instanceof Function ) { if ( chrome.tabs.removeCSS instanceof Function ) {
webext.tabs.removeCSS = 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 ) { if ( chrome.storage.managed instanceof Object ) {
webext.storage.managed = { webext.storage.managed = {
get: function() { get: function() {
@ -175,7 +245,7 @@ if ( chrome.storage.managed instanceof Object ) {
chrome.storage.local.get(...arguments, result => { chrome.storage.local.get(...arguments, result => {
const lastError = chrome.runtime.lastError; const lastError = chrome.runtime.lastError;
if ( lastError instanceof Object ) { if ( lastError instanceof Object ) {
return reject(lastError); return reject(lastError.message);
} }
resolve(result); resolve(result);
}); });

View File

@ -77,7 +77,7 @@ const onMessage = function(request, sender, callback) {
return; return;
case 'reloadAllFilters': case 'reloadAllFilters':
µb.loadFilterLists(); µb.loadFilterLists().then(( ) => { callback(); });
return; return;
case 'scriptlet': case 'scriptlet':
@ -752,10 +752,14 @@ const onMessage = function(request, sender, callback) {
return; return;
case 'cloudPull': case 'cloudPull':
return vAPI.cloud.pull(request.datakey, callback); return vAPI.cloud.pull(request.datakey).then(result => {
callback(result);
});
case 'cloudPush': case 'cloudPush':
return vAPI.cloud.push(request.datakey, request.data, callback); return vAPI.cloud.push(request.datakey, request.data).then(result => {
callback(result);
});
default: default:
break; break;

View File

@ -318,16 +318,6 @@ initializeTabs();
: 0 : 0
); );
// vAPI.cloud is optional.
if ( µb.cloudStorageSupported ) {
vAPI.cloud.start([
'tpFiltersPane',
'myFiltersPane',
'myRulesPane',
'whitelistPane'
]);
}
µb.contextMenu.update(null); µb.contextMenu.update(null);
// https://github.com/uBlockOrigin/uBlock-issues/issues/717 // https://github.com/uBlockOrigin/uBlock-issues/issues/717