Work toward modernizing code base: promisification

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

Related commits:
- 022951547c
- 3224d9b5cc
- 26235d80d0
- 0051f3b5c7
- eec53c0154
- 915687fddb
- 55cc0c6997
- e27328f931
This commit is contained in:
Raymond Hill 2019-09-19 16:41:44 -04:00
parent 5f60c31f77
commit 58620fb051
No known key found for this signature in database
GPG Key ID: 25E1490B761470C2
2 changed files with 156 additions and 310 deletions

View File

@ -39,17 +39,6 @@ vAPI.cantWebsocket =
browser.webRequest.ResourceType instanceof Object === false ||
browser.webRequest.ResourceType.WEBSOCKET !== 'websocket';
vAPI.lastError = function() {
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 browser.runtime.lastError;
};
vAPI.supportsUserStylesheets = vAPI.webextFlavor.soup.has('user_stylesheet');
// The real actual webextFlavor value may not be set in stone, so listen
// for possible future changes.
@ -58,8 +47,6 @@ window.addEventListener('webextFlavor', function() {
vAPI.webextFlavor.soup.has('user_stylesheet');
}, { once: true });
const noopFunc = function(){};
/******************************************************************************/
vAPI.app = {
@ -111,20 +98,14 @@ vAPI.storage = webext.storage.local;
// https://github.com/gorhill/uMatrix/issues/234
// 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
// unknown reasons) and again for unknown reasons the browser acted as if
// uBlock did not declare the `privacy` permission in its manifest, putting
// uBlock in a bad, non-functional state -- because call to `chrome.privacy`
// API threw an exception.
// https://github.com/gorhill/uBlock/issues/2048
// Do not mess up with existing settings if not assigning them stricter
// values.
vAPI.browserSettings = (( ) => {
// Not all platforms support `browser.privacy`.
if ( browser.privacy instanceof Object === false ) { return; }
const bp = webext.privacy;
if ( bp instanceof Object === false ) { return; }
return {
// Whether the WebRTC-related privacy API is crashy is an open question
@ -181,75 +162,42 @@ vAPI.browserSettings = (( ) => {
// crash.
if ( this.webRTCSupported !== true ) { return; }
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 bpn.webRTCMultipleRoutesEnabled === 'object' ) {
try {
if ( setting ) {
bpn.webRTCMultipleRoutesEnabled.clear({
scope: 'regular'
}, vAPI.resetLastError);
} else {
bpn.webRTCMultipleRoutesEnabled.set({
value: false,
scope: 'regular'
}, vAPI.resetLastError);
}
} catch(ex) {
console.error(ex);
}
}
// This setting became available in Chromium 48.
if ( typeof bpn.webRTCIPHandlingPolicy === 'object' ) {
try {
if ( setting ) {
bpn.webRTCIPHandlingPolicy.clear({
scope: 'regular'
}, vAPI.resetLastError);
} else {
// https://github.com/uBlockOrigin/uAssets/issues/333#issuecomment-289426678
// Leverage virtuous side-effect of strictest setting.
// https://github.com/gorhill/uBlock/issues/3009
// Firefox currently works differently, use
// `default_public_interface_only` for now.
bpn.webRTCIPHandlingPolicy.set({
value: vAPI.webextFlavor.soup.has('chromium')
? 'disable_non_proxied_udp'
: 'default_public_interface_only',
scope: 'regular'
}, vAPI.resetLastError);
}
} catch(ex) {
console.error(ex);
}
if ( setting ) {
bpn.webRTCIPHandlingPolicy.clear({
scope: 'regular',
});
} else {
// https://github.com/uBlockOrigin/uAssets/issues/333#issuecomment-289426678
// Leverage virtuous side-effect of strictest setting.
// https://github.com/gorhill/uBlock/issues/3009
// Firefox currently works differently, use
// `default_public_interface_only` for now.
bpn.webRTCIPHandlingPolicy.set({
value: vAPI.webextFlavor.soup.has('chromium')
? 'disable_non_proxied_udp'
: 'default_public_interface_only',
scope: 'regular',
});
}
},
set: function(details) {
for ( const setting in details ) {
if ( details.hasOwnProperty(setting) === false ) {
continue;
}
if ( details.hasOwnProperty(setting) === false ) { continue; }
switch ( setting ) {
case 'prefetching':
const enabled = !!details[setting];
try {
if ( enabled ) {
browser.privacy.network.networkPredictionEnabled.clear({
scope: 'regular'
}, vAPI.resetLastError);
} else {
browser.privacy.network.networkPredictionEnabled.set({
value: false,
scope: 'regular'
}, vAPI.resetLastError);
}
} catch(ex) {
console.error(ex);
if ( enabled ) {
bp.network.networkPredictionEnabled.clear({
scope: 'regular',
});
} else {
bp.network.networkPredictionEnabled.set({
value: false,
scope: 'regular',
});
}
if ( vAPI.prefetching instanceof Function ) {
vAPI.prefetching(enabled);
@ -257,19 +205,15 @@ vAPI.browserSettings = (( ) => {
break;
case 'hyperlinkAuditing':
try {
if ( !!details[setting] ) {
browser.privacy.websites.hyperlinkAuditingEnabled.clear({
scope: 'regular'
}, vAPI.resetLastError);
} else {
browser.privacy.websites.hyperlinkAuditingEnabled.set({
value: false,
scope: 'regular'
}, vAPI.resetLastError);
}
} catch(ex) {
console.error(ex);
if ( !!details[setting] ) {
bp.websites.hyperlinkAuditingEnabled.clear({
scope: 'regular',
});
} else {
bp.websites.hyperlinkAuditingEnabled.set({
value: false,
scope: 'regular',
});
}
break;
@ -595,21 +539,27 @@ vAPI.Tabs = class {
vAPI.tabs.update(tabId, { url: targetURL });
}
remove(tabId) {
async remove(tabId) {
tabId = toTabId(tabId);
if ( tabId === 0 ) { return; }
browser.tabs.remove(tabId, vAPI.resetLastError);
try {
await webext.tabs.remove(tabId);
}
catch (reason) {
}
}
reload(tabId, bypassCache = false) {
async reload(tabId, bypassCache = false) {
tabId = toTabId(tabId);
if ( tabId === 0 ) { return; }
browser.tabs.reload(
tabId,
{ bypassCache: bypassCache === true },
vAPI.resetLastError
);
try {
await webext.tabs.reload(
tabId,
{ bypassCache: bypassCache === true }
);
}
catch (reason) {
}
}
async select(tabId) {
@ -861,7 +811,7 @@ vAPI.messaging = {
listeners: new Map(),
defaultHandler: null,
PRIVILEGED_URL: vAPI.getURL(''),
NOOPFUNC: noopFunc,
NOOPFUNC: function(){},
UNHANDLED: 'vAPI.messaging.notHandled',
listen: function(details) {
@ -1256,10 +1206,7 @@ vAPI.contextMenu = browser.contextMenus && {
_callback: null,
_entries: [],
_createEntry: function(entry) {
browser.contextMenus.create(
JSON.parse(JSON.stringify(entry)),
vAPI.resetLastError
);
webext.menus.create(JSON.parse(JSON.stringify(entry)));
},
onMustUpdate: function() {},
setEntries: function(entries, callback) {
@ -1270,12 +1217,12 @@ vAPI.contextMenu = browser.contextMenus && {
const newEntry = entries[i];
if ( oldEntryId && newEntry ) {
if ( newEntry.id !== oldEntryId ) {
browser.contextMenus.remove(oldEntryId);
webext.menus.remove(oldEntryId);
this._createEntry(newEntry);
this._entries[i] = newEntry.id;
}
} else if ( oldEntryId && !newEntry ) {
browser.contextMenus.remove(oldEntryId);
webext.menus.remove(oldEntryId);
} else if ( !oldEntryId && newEntry ) {
this._createEntry(newEntry);
this._entries[i] = newEntry.id;
@ -1287,10 +1234,10 @@ vAPI.contextMenu = browser.contextMenus && {
return;
}
if ( n !== 0 && callback !== null ) {
browser.contextMenus.onClicked.addListener(callback);
webext.menus.onClicked.addListener(callback);
this._callback = callback;
} else if ( n === 0 && this._callback !== null ) {
browser.contextMenus.onClicked.removeListener(this._callback);
webext.menus.onClicked.removeListener(this._callback);
this._callback = null;
}
}

View File

@ -24,140 +24,108 @@
// `webext` is a promisified api of `chrome`. Entries are added as
// the promisification of uBO progress.
const webext = { // jshint ignore:line
const webext = (( ) => { // jshint ignore:line
// >>>>> start of private scope
const promisifyNoFail = function(thisArg, fnName, outFn = r => r) {
const fn = thisArg[fnName];
return function() {
return new Promise(resolve => {
fn.call(thisArg, ...arguments, function() {
void chrome.runtime.lastError;
resolve(outFn(...arguments));
});
});
};
};
const promisify = function(thisArg, fnName) {
const fn = thisArg[fnName];
return function() {
return new Promise((resolve, reject) => {
fn.call(thisArg, ...arguments, function() {
const lastError = chrome.runtime.lastError;
if ( lastError instanceof Object ) {
return reject(lastError.message);
}
resolve(...arguments);
});
});
};
};
const webext = {
// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/menus
menus: {
create: function() {
return chrome.contextMenus.create(...arguments, ( ) => {
void chrome.runtime.lastError;
});
},
onClicked: chrome.contextMenus.onClicked,
remove: promisifyNoFail(chrome.contextMenus, 'remove'),
},
// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/privacy
privacy: {
},
// 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.message);
}
resolve();
});
});
},
get: function() {
return new Promise((resolve, reject) => {
chrome.storage.local.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.local.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.local.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.local.set(...arguments, ( ) => {
const lastError = chrome.runtime.lastError;
if ( lastError instanceof Object ) {
return reject(lastError.message);
}
resolve();
});
});
},
clear: promisify(chrome.storage.local, 'clear'),
get: promisify(chrome.storage.local, 'get'),
getBytesInUse: promisify(chrome.storage.local, 'getBytesInUse'),
remove: promisify(chrome.storage.local, 'remove'),
set: promisify(chrome.storage.local, 'set'),
},
},
// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/tabs
tabs: {
get: function() {
return new Promise(resolve => {
chrome.tabs.get(...arguments, tab => {
void chrome.runtime.lastError;
resolve(tab instanceof Object ? tab : null);
});
});
},
executeScript: function() {
return new Promise(resolve => {
chrome.tabs.executeScript(...arguments, result => {
void chrome.runtime.lastError;
resolve(result);
});
});
},
insertCSS: function() {
return new Promise(resolve => {
chrome.tabs.insertCSS(...arguments, ( ) => {
void chrome.runtime.lastError;
resolve();
});
});
},
query: function() {
return new Promise(resolve => {
chrome.tabs.query(...arguments, tabs => {
void chrome.runtime.lastError;
resolve(Array.isArray(tabs) ? tabs : []);
});
});
},
update: function() {
return new Promise(resolve => {
chrome.tabs.update(...arguments, tab => {
void chrome.runtime.lastError;
resolve(tab instanceof Object ? tab : null);
});
});
},
get: promisifyNoFail(chrome.tabs, 'get', tab => tab instanceof Object ? tab : null),
executeScript: promisifyNoFail(chrome.tabs, 'executeScript'),
insertCSS: promisifyNoFail(chrome.tabs, 'insertCSS'),
query: promisifyNoFail(chrome.tabs, 'query', tabs => Array.isArray(tabs) ? tabs : []),
reload: promisifyNoFail(chrome.tabs, 'reload'),
remove: promisifyNoFail(chrome.tabs, 'remove'),
update: promisifyNoFail(chrome.tabs, 'update', tab => tab instanceof Object ? tab : null),
},
// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/windows
windows: {
get: function() {
return new Promise(resolve => {
chrome.windows.get(...arguments, win => {
void chrome.runtime.lastError;
resolve(win instanceof Object ? win : null);
});
});
},
create: function() {
return new Promise(resolve => {
chrome.windows.create(...arguments, win => {
void chrome.runtime.lastError;
resolve(win instanceof Object ? win : null);
});
});
},
update: function() {
return new Promise(resolve => {
chrome.windows.update(...arguments, win => {
void chrome.runtime.lastError;
resolve(win instanceof Object ? win : null);
});
});
},
get: promisifyNoFail(chrome.windows, 'get', win => win instanceof Object ? win : null),
create: promisifyNoFail(chrome.windows, 'create', win => win instanceof Object ? win : null),
update: promisifyNoFail(chrome.windows, 'update', win => win instanceof Object ? win : null),
},
};
// browser.privacy entries
{
const settings = [
// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/privacy/network
[ 'network', 'networkPredictionEnabled' ],
[ 'network', 'webRTCIPHandlingPolicy' ],
// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/privacy/websites
[ 'websites', 'hyperlinkAuditingEnabled' ],
];
for ( const [ category, setting ] of settings ) {
let categoryEntry = webext.privacy[category];
if ( categoryEntry instanceof Object === false ) {
categoryEntry = webext.privacy[category] = {};
}
const settingEntry = categoryEntry[setting] = {};
const thisArg = chrome.privacy[category][setting];
settingEntry.clear = promisifyNoFail(thisArg, 'clear');
settingEntry.get = promisifyNoFail(thisArg, 'get');
settingEntry.set = promisifyNoFail(thisArg, 'set');
}
}
// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/storage/managed
if ( chrome.storage.managed instanceof Object ) {
webext.storage.managed = {
get: promisify(chrome.storage.managed, 'get'),
};
}
// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/storage/sync
if ( chrome.storage.sync instanceof Object ) {
webext.storage.sync = {
@ -167,89 +135,20 @@ if ( chrome.storage.sync instanceof Object ) {
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();
});
});
},
clear: promisify(chrome.storage.sync, 'clear'),
get: promisify(chrome.storage.sync, 'get'),
getBytesInUse: promisify(chrome.storage.sync, 'getBytesInUse'),
remove: promisify(chrome.storage.sync, 'remove'),
set: promisify(chrome.storage.sync, 'set'),
};
}
// https://bugs.chromium.org/p/chromium/issues/detail?id=608854
if ( chrome.tabs.removeCSS instanceof Function ) {
webext.tabs.removeCSS = function() {
return new Promise(resolve => {
chrome.tabs.removeCSS(...arguments, ( ) => {
void chrome.runtime.lastError;
resolve();
});
});
};
webext.tabs.removeCSS = promisifyNoFail(chrome.tabs, 'removeCSS');
}
// 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() {
return new Promise((resolve, reject) => {
chrome.storage.local.get(...arguments, result => {
const lastError = chrome.runtime.lastError;
if ( lastError instanceof Object ) {
return reject(lastError.message);
}
resolve(result);
});
});
},
};
}
return webext;
// <<<<< end of private scope
})();