diff --git a/.jshintrc b/.jshintrc
index 940e5edf9..ba4d941e0 100644
--- a/.jshintrc
+++ b/.jshintrc
@@ -11,6 +11,7 @@
"safari": false,
"self": false,
"vAPI": false,
+ "webext": false,
"µBlock": false
},
"laxbreak": true,
diff --git a/platform/chromium/vapi-background.js b/platform/chromium/vapi-background.js
index a9234ee70..c3a4d7aec 100644
--- a/platform/chromium/vapi-background.js
+++ b/platform/chromium/vapi-background.js
@@ -43,15 +43,6 @@ vAPI.lastError = function() {
return chrome.runtime.lastError;
};
-vAPI.apiIsPromisified = (( ) => {
- try {
- return browser.storage.local.get('_') instanceof Promise;
- }
- catch(ex) {
- }
- return false;
-})();
-
// https://github.com/gorhill/uBlock/issues/875
// https://code.google.com/p/chromium/issues/detail?id=410868#c8
// Must not leave `lastError` unchecked.
@@ -116,83 +107,7 @@ vAPI.app = {
/******************************************************************************/
/******************************************************************************/
-vAPI.storage = (( ) => {
- if ( vAPI.apiIsPromisified ) {
- return browser.storage.local;
- }
- return {
- clear: function(callback) {
- if ( callback !== undefined ) {
- return browser.storage.local.clear(...arguments);
- }
- return new Promise((resolve, reject) => {
- browser.storage.local.clear(( ) => {
- const lastError = browser.runtime.lastError;
- if ( lastError instanceof Object ) {
- return reject(lastError);
- }
- resolve();
- });
- });
- },
- get: function(keys, callback) {
- if ( callback !== undefined ) {
- return browser.storage.local.get(...arguments);
- }
- return new Promise((resolve, reject) => {
- browser.storage.local.get(keys, result => {
- const lastError = browser.runtime.lastError;
- if ( lastError instanceof Object ) {
- return reject(lastError);
- }
- resolve(result);
- });
- });
- },
- getBytesInUse: function(keys, callback) {
- if ( callback !== undefined ) {
- return browser.storage.local.getBytesInUse(...arguments);
- }
- return new Promise((resolve, reject) => {
- browser.storage.local.getBytesInUse(keys, result => {
- const lastError = browser.runtime.lastError;
- if ( lastError instanceof Object ) {
- return reject(lastError);
- }
- resolve(result);
- });
- });
- },
- remove: function(keys, callback) {
- if ( callback !== undefined ) {
- return browser.storage.local.remove(...arguments);
- }
- return new Promise((resolve, reject) => {
- browser.storage.local.remove(keys, ( ) => {
- const lastError = browser.runtime.lastError;
- if ( lastError instanceof Object ) {
- return reject(lastError);
- }
- resolve();
- });
- });
- },
- set: function(items, callback) {
- if ( callback !== undefined ) {
- return browser.storage.local.set(...arguments);
- }
- return new Promise((resolve, reject) => {
- browser.storage.local.set(items, ( ) => {
- const lastError = browser.runtime.lastError;
- if ( lastError instanceof Object ) {
- return reject(lastError);
- }
- resolve();
- });
- });
- },
- };
-})();
+vAPI.storage = webext.storage.local;
/******************************************************************************/
/******************************************************************************/
@@ -211,7 +126,7 @@ vAPI.storage = (( ) => {
// Do not mess up with existing settings if not assigning them stricter
// values.
-vAPI.browserSettings = (function() {
+vAPI.browserSettings = (( ) => {
// Not all platforms support `chrome.privacy`.
if ( chrome.privacy instanceof Object === false ) { return; }
@@ -384,9 +299,8 @@ vAPI.isBehindTheSceneTabId = function(tabId) {
vAPI.unsetTabId = 0;
vAPI.noTabId = -1; // definitely not any existing tab
-// To remove when tabId-as-integer has been tested enough.
-
-const toChromiumTabId = function(tabId) {
+// To ensure we always use a good tab id
+const toTabId = function(tabId) {
return typeof tabId === 'number' && isNaN(tabId) === false
? tabId
: 0;
@@ -436,17 +350,12 @@ vAPI.Tabs = class {
// https://github.com/uBlockOrigin/uBlock-issues/issues/151
// https://github.com/uBlockOrigin/uBlock-issues/issues/680#issuecomment-515215220
if ( browser.windows instanceof Object ) {
- browser.windows.onFocusChanged.addListener(windowId => {
+ browser.windows.onFocusChanged.addListener(async windowId => {
if ( windowId === browser.windows.WINDOW_ID_NONE ) { return; }
- browser.tabs.query({ active: true, windowId }, tabs => {
- if ( Array.isArray(tabs) === false ) { return; }
- if ( tabs.length === 0 ) { return; }
- const tab = tabs[0];
- this.onActivated({
- tabId: tab.id,
- windowId: tab.windowId,
- });
- });
+ const tabs = await vAPI.tabs.query({ active: true, windowId });
+ if ( tabs.length === 0 ) { return; }
+ const tab = tabs[0];
+ this.onActivated({ tabId: tab.id, windowId: tab.windowId });
});
}
@@ -455,32 +364,33 @@ vAPI.Tabs = class {
});
}
- get(tabId, callback) {
+ async get(tabId) {
if ( tabId === null ) {
- browser.tabs.query(
- { active: true, currentWindow: true },
- tabs => {
- void browser.runtime.lastError;
- callback(
- Array.isArray(tabs) && tabs.length !== 0
- ? tabs[0]
- : null
- );
- }
- );
- return;
+ return this.getCurrent();
}
-
- tabId = toChromiumTabId(tabId);
- if ( tabId === 0 ) {
- callback(null);
- return;
+ if ( tabId <= 0 ) { return null; }
+ let tab;
+ try {
+ tab = await webext.tabs.get(tabId);
}
+ catch(reason) {
+ }
+ return tab instanceof Object ? tab : null;
+ }
- browser.tabs.get(tabId, function(tab) {
- void browser.runtime.lastError;
- callback(tab);
- });
+ async getCurrent() {
+ const tabs = await this.query({ active: true, currentWindow: true });
+ return tabs.length !== 0 ? tabs[0] : null;
+ }
+
+ async query(queryInfo) {
+ let tabs;
+ try {
+ tabs = await webext.tabs.query(queryInfo);
+ }
+ catch(reason) {
+ }
+ return Array.isArray(tabs) ? tabs : [];
}
// Properties of the details object:
@@ -491,12 +401,12 @@ vAPI.Tabs = class {
// foreground: undefined
// - popup: 'popup' => open in a new window
- create(url, details) {
+ async create(url, details) {
if ( details.active === undefined ) {
details.active = true;
}
- const subWrapper = ( ) => {
+ const subWrapper = async ( ) => {
const updateDetails = {
url: url,
active: !!details.active
@@ -505,8 +415,8 @@ vAPI.Tabs = class {
// Opening a tab from incognito window won't focus the window
// in which the tab was opened
const focusWindow = tab => {
- if ( tab.active && browser.windows instanceof Object ) {
- browser.windows.update(tab.windowId, { focused: true });
+ if ( tab.active && vAPI.windows instanceof Object ) {
+ vAPI.windows.update(tab.windowId, { focused: true });
}
};
@@ -519,29 +429,27 @@ vAPI.Tabs = class {
}
// update doesn't accept index, must use move
- browser.tabs.update(
- toChromiumTabId(details.tabId),
- updateDetails,
- tab => {
- // if the tab doesn't exist
- if ( vAPI.lastError() ) {
- browser.tabs.create(updateDetails, focusWindow);
- } else if ( details.index !== undefined ) {
- browser.tabs.move(tab.id, { index: details.index });
- }
- }
+ const tab = await vAPI.tabs.update(
+ toTabId(details.tabId),
+ updateDetails
);
+ // if the tab doesn't exist
+ if ( tab === null ) {
+ browser.tabs.create(updateDetails, focusWindow);
+ } else if ( details.index !== undefined ) {
+ browser.tabs.move(tab.id, { index: details.index });
+ }
};
// Open in a standalone window
//
// https://github.com/uBlockOrigin/uBlock-issues/issues/168#issuecomment-413038191
- // Not all platforms support browser.windows API.
+ // Not all platforms support vAPI.windows.
//
// For some reasons, some platforms do not honor the left,top
// position when specified. I found that further calling
// windows.update again with the same position _may_ help.
- if ( details.popup !== undefined && browser.windows instanceof Object ) {
+ if ( details.popup !== undefined && vAPI.windows instanceof Object ) {
const createDetails = {
url: details.url,
type: details.popup,
@@ -549,19 +457,18 @@ vAPI.Tabs = class {
if ( details.box instanceof Object ) {
Object.assign(createDetails, details.box);
}
- browser.windows.create(createDetails, win => {
- if ( win instanceof Object === false ) { return; }
- if ( details.box instanceof Object === false ) { return; }
- if (
- win.left === details.box.left &&
- win.top === details.box.top
- ) {
- return;
- }
- browser.windows.update(win.id, {
- left: details.box.left,
- top: details.box.top
- });
+ const win = await vAPI.windows.create(createDetails);
+ if ( win === null ) { return; }
+ if ( details.box instanceof Object === false ) { return; }
+ if (
+ win.left === details.box.left &&
+ win.top === details.box.top
+ ) {
+ return;
+ }
+ vAPI.windows.update(win.id, {
+ left: details.box.left,
+ top: details.box.top
});
return;
}
@@ -571,15 +478,13 @@ vAPI.Tabs = class {
return;
}
- vAPI.tabs.get(null, tab => {
- if ( tab ) {
- details.index = tab.index + 1;
- } else {
- delete details.index;
- }
-
- subWrapper();
- });
+ const tab = await vAPI.tabs.getCurrent();
+ if ( tab !== null ) {
+ details.index = tab.index + 1;
+ } else {
+ details.index = undefined;
+ }
+ subWrapper();
}
// Properties of the details object:
@@ -593,7 +498,7 @@ vAPI.Tabs = class {
// it instead of opening a new one
// - popup: true => open in a new window
- open(details) {
+ async open(details) {
let targetURL = details.url;
if ( typeof targetURL !== 'string' || targetURL === '' ) {
return null;
@@ -628,29 +533,35 @@ vAPI.Tabs = class {
? targetURL
: targetURL.slice(0, pos);
- browser.tabs.query({ url: targetURLWithoutHash }, tabs => {
- void browser.runtime.lastError;
- const tab = Array.isArray(tabs) && tabs[0];
- if ( !tab ) {
- this.create(targetURL, details);
- return;
- }
- const updateDetails = { active: true };
- // https://github.com/uBlockOrigin/uBlock-issues/issues/592
- if ( tab.url.startsWith(targetURL) === false ) {
- updateDetails.url = targetURL;
- }
- browser.tabs.update(tab.id, updateDetails, tab => {
- if ( browser.windows instanceof Object === false ) { return; }
- browser.windows.update(tab.windowId, { focused: true });
- });
- });
+ const tabs = await vAPI.tabs.query({ url: targetURLWithoutHash });
+ if ( tabs.length === 0 ) {
+ this.create(targetURL, details);
+ return;
+ }
+ let tab = tabs[0];
+ const updateDetails = { active: true };
+ // https://github.com/uBlockOrigin/uBlock-issues/issues/592
+ if ( tab.url.startsWith(targetURL) === false ) {
+ updateDetails.url = targetURL;
+ }
+ tab = await vAPI.tabs.update(tab.id, updateDetails);
+ if ( vAPI.windows instanceof Object === false ) { return; }
+ vAPI.windows.update(tab.windowId, { focused: true });
+ }
+
+ async update() {
+ let tab;
+ try {
+ tab = await webext.tabs.update(...arguments);
+ }
+ catch (reason) {
+ }
+ return tab instanceof Object ? tab : null;
}
// Replace the URL of a tab. Noop if the tab does not exist.
-
replace(tabId, url) {
- tabId = toChromiumTabId(tabId);
+ tabId = toTabId(tabId);
if ( tabId === 0 ) { return; }
let targetURL = url;
@@ -660,18 +571,17 @@ vAPI.Tabs = class {
targetURL = vAPI.getURL(targetURL);
}
- browser.tabs.update(tabId, { url: targetURL }, vAPI.resetLastError);
+ vAPI.tabs.update(tabId, { url: targetURL });
}
remove(tabId) {
- tabId = toChromiumTabId(tabId);
+ tabId = toTabId(tabId);
if ( tabId === 0 ) { return; }
-
browser.tabs.remove(tabId, vAPI.resetLastError);
}
reload(tabId, bypassCache = false) {
- tabId = toChromiumTabId(tabId);
+ tabId = toTabId(tabId);
if ( tabId === 0 ) { return; }
browser.tabs.reload(
@@ -681,16 +591,13 @@ vAPI.Tabs = class {
);
}
- select(tabId) {
- tabId = toChromiumTabId(tabId);
+ async select(tabId) {
+ tabId = toTabId(tabId);
if ( tabId === 0 ) { return; }
-
- browser.tabs.update(tabId, { active: true }, function(tab) {
- void browser.runtime.lastError;
- if ( !tab ) { return; }
- if ( browser.windows instanceof Object === false ) { return; }
- browser.windows.update(tab.windowId, { focused: true });
- });
+ const tab = await vAPI.tabs.update(tabId, { active: true });
+ if ( tab === null ) { return; }
+ if ( vAPI.windows instanceof Object === false ) { return; }
+ vAPI.windows.update(tab.windowId, { focused: true });
}
injectScript(tabId, details, callback) {
@@ -703,7 +610,7 @@ vAPI.Tabs = class {
};
if ( tabId ) {
browser.tabs.executeScript(
- toChromiumTabId(tabId),
+ toTabId(tabId),
details,
onScriptExecuted
);
@@ -749,6 +656,41 @@ vAPI.Tabs = class {
/******************************************************************************/
/******************************************************************************/
+if ( browser.windows instanceof Object ) {
+ vAPI.windows = {
+ get: async function() {
+ let win;
+ try {
+ win = await webext.windows.get(...arguments);
+ }
+ catch (reason) {
+ }
+ return win instanceof Object ? win : null;
+ },
+ create: async function() {
+ let win;
+ try {
+ win = await webext.windows.create(...arguments);
+ }
+ catch (reason) {
+ }
+ return win instanceof Object ? win : null;
+ },
+ update: async function() {
+ let win;
+ try {
+ win = await webext.windows.update(...arguments);
+ }
+ catch (reason) {
+ }
+ return win instanceof Object ? win : null;
+ },
+ };
+}
+
+/******************************************************************************/
+/******************************************************************************/
+
// Must read: https://code.google.com/p/chromium/issues/detail?id=410868#c8
// https://github.com/chrisaljoudi/uBlock/issues/19
@@ -852,8 +794,17 @@ vAPI.setIcon = (( ) => {
}
})();
- const onTabReady = function(tab, details) {
- if ( vAPI.lastError() || !tab ) { return; }
+ // parts: bit 0 = icon
+ // bit 1 = badge text
+ // bit 2 = badge color
+ // bit 3 = hide badge
+
+ return async function(tabId, details) {
+ tabId = toTabId(tabId);
+ if ( tabId === 0 ) { return; }
+
+ const tab = await vAPI.tabs.get(tabId);
+ if ( tab === null ) { return; }
const { parts, state, badge, color } = details;
@@ -883,18 +834,6 @@ vAPI.setIcon = (( ) => {
)
});
}
- };
-
- // parts: bit 0 = icon
- // bit 1 = badge text
- // bit 2 = badge color
- // bit 3 = hide badge
-
- return function(tabId, details) {
- tabId = toChromiumTabId(tabId);
- if ( tabId === 0 ) { return; }
-
- browser.tabs.get(tabId, tab => onTabReady(tab, details));
if ( vAPI.contextMenu instanceof Object ) {
vAPI.contextMenu.onMustUpdate(tabId);
@@ -1381,48 +1320,27 @@ vAPI.commands = chrome.commands;
// Also, UC Browser: http://www.upsieutoc.com/image/WXuH
vAPI.adminStorage = (( ) => {
- if ( browser.storage.managed instanceof Object === false ) {
+ if ( webext.storage.managed instanceof Object === false ) {
return {
getItem: function() {
return Promise.resolve();
},
};
}
- const managedStorage = vAPI.apiIsPromisified
- ? browser.storage.managed
- : {
- get: function(keys) {
- return new Promise((resolve, reject) => {
- browser.storage.managed.get(keys, result => {
- const lastError = browser.runtime.lastError;
- if ( lastError instanceof Object ) {
- return reject(lastError);
- }
- resolve(result);
- });
- });
- },
- };
return {
getItem: async function(key) {
let bin;
try {
- bin = await managedStorage.get(key);
+ bin = await webext.storage.managed.get(key);
} catch(ex) {
}
- let data;
- if (
- chrome.runtime.lastError instanceof Object === false &&
- bin instanceof Object
- ) {
- data = bin[key];
+ if ( bin instanceof Object ) {
+ return bin[key];
}
- return data;
}
};
})();
-
/******************************************************************************/
/******************************************************************************/
diff --git a/platform/chromium/webext.js b/platform/chromium/webext.js
new file mode 100644
index 000000000..00b7da1fd
--- /dev/null
+++ b/platform/chromium/webext.js
@@ -0,0 +1,157 @@
+/*******************************************************************************
+
+ uBlock Origin - a browser extension to block requests.
+ Copyright (C) 2019-present Raymond Hill
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see {http://www.gnu.org/licenses/}.
+
+ Home: https://github.com/gorhill/uBlock
+*/
+
+'use strict';
+
+// `webext` is a promisified api of `chrome`. Entries are added as
+// the promisification of uBO progress.
+
+const webext = { // jshint ignore:line
+ storage: {
+ local: {
+ clear: function() {
+ return new Promise((resolve, reject) => {
+ chrome.storage.local.clear(( ) => {
+ const lastError = chrome.runtime.lastError;
+ if ( lastError instanceof Object ) {
+ return reject(lastError);
+ }
+ 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);
+ }
+ 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);
+ }
+ 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);
+ }
+ 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);
+ }
+ resolve();
+ });
+ });
+ },
+ },
+ },
+
+ tabs: {
+ get: function() {
+ return new Promise(resolve => {
+ chrome.tabs.get(...arguments, tab => {
+ void chrome.runtime.lastError;
+ resolve(tab instanceof Object ? tab : null);
+ });
+ });
+ },
+ 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);
+ });
+ });
+ },
+ },
+
+ windows: {
+ get: async function() {
+ return new Promise(resolve => {
+ chrome.windows.get(...arguments, win => {
+ void chrome.runtime.lastError;
+ resolve(win instanceof Object ? win : null);
+ });
+ });
+ },
+ create: async function() {
+ return new Promise(resolve => {
+ chrome.windows.create(...arguments, win => {
+ void chrome.runtime.lastError;
+ resolve(win instanceof Object ? win : null);
+ });
+ });
+ },
+ update: async function() {
+ return new Promise(resolve => {
+ chrome.windows.update(...arguments, win => {
+ void chrome.runtime.lastError;
+ resolve(win instanceof Object ? win : null);
+ });
+ });
+ },
+ },
+};
+
+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);
+ }
+ resolve(result);
+ });
+ });
+ },
+ };
+}
diff --git a/platform/firefox/webext.js b/platform/firefox/webext.js
new file mode 100644
index 000000000..4729e7c17
--- /dev/null
+++ b/platform/firefox/webext.js
@@ -0,0 +1,24 @@
+/*******************************************************************************
+
+ uBlock Origin - a browser extension to block requests.
+ Copyright (C) 2019-present Raymond Hill
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see {http://www.gnu.org/licenses/}.
+
+ Home: https://github.com/gorhill/uBlock
+*/
+
+'use strict';
+
+const webext = browser; // jshint ignore:line
diff --git a/src/background.html b/src/background.html
index 0f9b37070..8db736597 100644
--- a/src/background.html
+++ b/src/background.html
@@ -9,6 +9,7 @@
+
diff --git a/src/js/cachestorage.js b/src/js/cachestorage.js
index cb0f3690d..1657ee3ec 100644
--- a/src/js/cachestorage.js
+++ b/src/js/cachestorage.js
@@ -54,64 +54,15 @@
const STORAGE_NAME = 'uBlock0CacheStorage';
- // Default to webext storage. Wrapped into promises if the API does not
- // support returning promises.
- const promisified = (( ) => {
- try {
- return browser.storage.local.get('_') instanceof Promise;
- }
- catch(ex) {
- }
- return false;
- })();
-
+ // Default to webext storage.
+ const localStorage = webext.storage.local;
const api = {
name: 'browser.storage.local',
- get: promisified ?
- browser.storage.local.get :
- function(keys) {
- return new Promise(resolve => {
- browser.storage.local.get(keys, bin => {
- resolve(bin);
- });
- });
- },
- set: promisified ?
- browser.storage.local.set :
- function(keys) {
- return new Promise(resolve => {
- browser.storage.local.set(keys, ( ) => {
- resolve();
- });
- });
- },
- remove: promisified ?
- browser.storage.local.remove :
- function(keys) {
- return new Promise(resolve => {
- browser.storage.local.remove(keys, ( ) => {
- resolve();
- });
- });
- },
- clear: promisified ?
- browser.storage.local.clear :
- function() {
- return new Promise(resolve => {
- browser.storage.local.clear(( ) => {
- resolve();
- });
- });
- },
- getBytesInUse: promisified ?
- browser.storage.local.getBytesInUse :
- function(keys) {
- return new Promise(resolve => {
- browser.storage.local.getBytesInUse(keys, count => {
- resolve(count);
- });
- });
- },
+ get: localStorage.get,
+ set: localStorage.set,
+ remove: localStorage.remove,
+ clear: localStorage.clear,
+ getBytesInUse: localStorage.getBytesInUse,
select: function(selectedBackend) {
let actualBackend = selectedBackend;
if ( actualBackend === undefined || actualBackend === 'unset' ) {
@@ -139,7 +90,7 @@
};
// Reassign API entries to that of indexedDB-based ones
- const selectIDB = function() {
+ const selectIDB = async function() {
let db;
let dbPromise;
let dbTimer;
@@ -244,7 +195,7 @@
return dbPromise;
};
- const getFromDb = function(keys, keyvalStore, callback) {
+ const getFromDb = async function(keys, keyvalStore, callback) {
if ( typeof callback !== 'function' ) { return; }
if ( keys.length === 0 ) { return callback(keyvalStore); }
const promises = [];
@@ -261,7 +212,8 @@
})
);
};
- getDb().then(db => {
+ try {
+ const db = await getDb();
if ( !db ) { return callback(); }
const transaction = db.transaction(STORAGE_NAME, 'readonly');
transaction.oncomplete =
@@ -277,29 +229,29 @@
req.onsuccess = gotOne;
req.onerror = noopfn;
}
- }).catch(reason => {
+ }
+ catch(reason) {
console.info(`cacheStorage.getFromDb() failed: ${reason}`);
callback();
- });
+ }
};
- const visitAllFromDb = function(visitFn) {
- getDb().then(db => {
- if ( !db ) { return visitFn(); }
- const transaction = db.transaction(STORAGE_NAME, 'readonly');
- transaction.oncomplete =
- transaction.onerror =
- transaction.onabort = ( ) => visitFn();
- const table = transaction.objectStore(STORAGE_NAME);
- const req = table.openCursor();
- req.onsuccess = function(ev) {
- let cursor = ev.target && ev.target.result;
- if ( !cursor ) { return; }
- let entry = cursor.value;
- visitFn(entry);
- cursor.continue();
- };
- });
+ const visitAllFromDb = async function(visitFn) {
+ const db = await getDb();
+ if ( !db ) { return visitFn(); }
+ const transaction = db.transaction(STORAGE_NAME, 'readonly');
+ transaction.oncomplete =
+ transaction.onerror =
+ transaction.onabort = ( ) => visitFn();
+ const table = transaction.objectStore(STORAGE_NAME);
+ const req = table.openCursor();
+ req.onsuccess = function(ev) {
+ let cursor = ev.target && ev.target.result;
+ if ( !cursor ) { return; }
+ let entry = cursor.value;
+ visitFn(entry);
+ cursor.continue();
+ };
};
const getAllFromDb = function(callback) {
@@ -335,7 +287,7 @@
// https://developer.mozilla.org/en-US/docs/Web/API/IDBDatabase/transaction
// https://developer.mozilla.org/en-US/docs/Web/API/IDBObjectStore/put
- const putToDb = function(keyvalStore, callback) {
+ const putToDb = async function(keyvalStore, callback) {
if ( typeof callback !== 'function' ) {
callback = noopfn;
}
@@ -359,67 +311,67 @@
µBlock.lz4Codec.encode(key, data).then(handleEncodingResult)
);
}
- Promise.all(promises).then(results => {
+ const finish = ( ) => {
+ if ( callback === undefined ) { return; }
+ let cb = callback;
+ callback = undefined;
+ cb();
+ };
+ try {
+ const results = await Promise.all(promises);
const db = results[0];
if ( !db ) { return callback(); }
- const finish = ( ) => {
- if ( callback === undefined ) { return; }
- let cb = callback;
- callback = undefined;
- cb();
- };
- try {
- const transaction = db.transaction(
- STORAGE_NAME,
- 'readwrite'
- );
- transaction.oncomplete =
- transaction.onerror =
- transaction.onabort = finish;
- const table = transaction.objectStore(STORAGE_NAME);
- for ( const entry of entries ) {
- table.put(entry);
- }
- } catch (ex) {
- finish();
+ const transaction = db.transaction(
+ STORAGE_NAME,
+ 'readwrite'
+ );
+ transaction.oncomplete =
+ transaction.onerror =
+ transaction.onabort = finish;
+ const table = transaction.objectStore(STORAGE_NAME);
+ for ( const entry of entries ) {
+ table.put(entry);
}
- });
+ } catch (ex) {
+ finish();
+ }
};
- const deleteFromDb = function(input, callback) {
+ const deleteFromDb = async function(input, callback) {
if ( typeof callback !== 'function' ) {
callback = noopfn;
}
const keys = Array.isArray(input) ? input.slice() : [ input ];
if ( keys.length === 0 ) { return callback(); }
- getDb().then(db => {
+ const finish = ( ) => {
+ if ( callback === undefined ) { return; }
+ let cb = callback;
+ callback = undefined;
+ cb();
+ };
+ try {
+ const db = await getDb();
if ( !db ) { return callback(); }
- const finish = ( ) => {
- if ( callback === undefined ) { return; }
- let cb = callback;
- callback = undefined;
- cb();
- };
- try {
- const transaction = db.transaction(STORAGE_NAME, 'readwrite');
- transaction.oncomplete =
- transaction.onerror =
- transaction.onabort = finish;
- const table = transaction.objectStore(STORAGE_NAME);
- for ( const key of keys ) {
- table.delete(key);
- }
- } catch (ex) {
- finish();
+ const transaction = db.transaction(STORAGE_NAME, 'readwrite');
+ transaction.oncomplete =
+ transaction.onerror =
+ transaction.onabort = finish;
+ const table = transaction.objectStore(STORAGE_NAME);
+ for ( const key of keys ) {
+ table.delete(key);
}
- });
+ } catch (ex) {
+ finish();
+ }
};
- const clearDb = function(callback) {
+ const clearDb = async function(callback) {
if ( typeof callback !== 'function' ) {
callback = noopfn;
}
- getDb().then(db => {
+ try {
+ const db = await getDb();
+ if ( !db ) { return callback(); }
const transaction = db.transaction(STORAGE_NAME, 'readwrite');
transaction.oncomplete =
transaction.onerror =
@@ -427,77 +379,77 @@
callback();
};
transaction.objectStore(STORAGE_NAME).clear();
- }).catch(reason => {
+ }
+ catch(reason) {
console.info(`cacheStorage.clearDb() failed: ${reason}`);
callback();
- });
+ }
};
- return getDb().then(db => {
- if ( !db ) { return false; }
- api.name = 'indexedDB';
- api.get = function get(keys) {
- return new Promise(resolve => {
- if ( keys === null ) {
- return getAllFromDb(bin => resolve(bin));
- }
- let toRead, output = {};
- if ( typeof keys === 'string' ) {
- toRead = [ keys ];
- } else if ( Array.isArray(keys) ) {
- toRead = keys;
- } else /* if ( typeof keys === 'object' ) */ {
- toRead = Object.keys(keys);
- output = keys;
- }
- getFromDb(toRead, output, bin => resolve(bin));
- });
- };
- api.set = function set(keys) {
- return new Promise(resolve => {
- putToDb(keys, details => resolve(details));
- });
- };
- api.remove = function remove(keys) {
- return new Promise(resolve => {
- deleteFromDb(keys, ( ) => resolve());
- });
- };
- api.clear = function clear() {
- return new Promise(resolve => {
- clearDb(( ) => resolve());
- });
- };
- api.getBytesInUse = function getBytesInUse() {
- return Promise.resolve(0);
- };
- return true;
- });
+ await getDb();
+ if ( !db ) { return false; }
+
+ api.name = 'indexedDB';
+ api.get = function get(keys) {
+ return new Promise(resolve => {
+ if ( keys === null ) {
+ return getAllFromDb(bin => resolve(bin));
+ }
+ let toRead, output = {};
+ if ( typeof keys === 'string' ) {
+ toRead = [ keys ];
+ } else if ( Array.isArray(keys) ) {
+ toRead = keys;
+ } else /* if ( typeof keys === 'object' ) */ {
+ toRead = Object.keys(keys);
+ output = keys;
+ }
+ getFromDb(toRead, output, bin => resolve(bin));
+ });
+ };
+ api.set = function set(keys) {
+ return new Promise(resolve => {
+ putToDb(keys, details => resolve(details));
+ });
+ };
+ api.remove = function remove(keys) {
+ return new Promise(resolve => {
+ deleteFromDb(keys, ( ) => resolve());
+ });
+ };
+ api.clear = function clear() {
+ return new Promise(resolve => {
+ clearDb(( ) => resolve());
+ });
+ };
+ api.getBytesInUse = function getBytesInUse() {
+ return Promise.resolve(0);
+ };
+ return true;
};
// https://github.com/uBlockOrigin/uBlock-issues/issues/328
// Delete cache-related entries from webext storage.
- const clearWebext = function() {
- browser.storage.local.get('assetCacheRegistry', bin => {
- if (
- bin instanceof Object === false ||
- bin.assetCacheRegistry instanceof Object === false
- ) {
- return;
+ const clearWebext = async function() {
+ const bin = await webext.storage.local.get('assetCacheRegistry');
+ if (
+ bin instanceof Object === false ||
+ bin.assetCacheRegistry instanceof Object === false
+ ) {
+ return;
+ }
+ const toRemove = [
+ 'assetCacheRegistry',
+ 'assetSourceRegistry',
+ 'resourcesSelfie',
+ 'selfie'
+ ];
+ for ( const key in bin.assetCacheRegistry ) {
+ if ( bin.assetCacheRegistry.hasOwnProperty(key) ) {
+ toRemove.push('cache/' + key);
}
- const toRemove = [
- 'assetCacheRegistry',
- 'assetSourceRegistry',
- 'resourcesSelfie',
- 'selfie'
- ];
- for ( const key in bin.assetCacheRegistry ) {
- if ( bin.assetCacheRegistry.hasOwnProperty(key) ) {
- toRemove.push('cache/' + key);
- }
- }
- browser.storage.local.remove(toRemove);
- });
+ }
+ webext.storage.local.remove(toRemove);
};
const clearIDB = function() {
diff --git a/src/js/commands.js b/src/js/commands.js
index dff8a7772..4a5c61521 100644
--- a/src/js/commands.js
+++ b/src/js/commands.js
@@ -128,36 +128,37 @@ const relaxBlockingMode = (( ) => {
};
})();
-vAPI.commands.onCommand.addListener(command => {
+vAPI.commands.onCommand.addListener(async command => {
const µb = µBlock;
switch ( command ) {
case 'launch-element-picker':
- case 'launch-element-zapper':
- vAPI.tabs.get(null, tab => {
- if ( tab instanceof Object === false ) { return; }
- µb.mouseEventRegister.x = µb.mouseEventRegister.y = -1;
- µb.elementPickerExec(
- tab.id,
- undefined,
- command === 'launch-element-zapper'
- );
- });
- break;
- case 'launch-logger':
- vAPI.tabs.get(null, tab => {
- const hash = tab.url.startsWith(vAPI.getURL(''))
- ? ''
- : `#_+${tab.id}`;
- µb.openNewTab({
- url: `logger-ui.html${hash}`,
- select: true,
- index: -1
- });
+ case 'launch-element-zapper': {
+ const tab = await vAPI.tabs.getCurrent();
+ if ( tab instanceof Object === false ) { return; }
+ µb.mouseEventRegister.x = µb.mouseEventRegister.y = -1;
+ µb.elementPickerExec(
+ tab.id,
+ undefined,
+ command === 'launch-element-zapper'
+ );
+ break;
+ }
+ case 'launch-logger': {
+ const tab = await vAPI.tabs.getCurrent();
+ if ( tab instanceof Object === false ) { return; }
+ const hash = tab.url.startsWith(vAPI.getURL(''))
+ ? ''
+ : `#_+${tab.id}`;
+ µb.openNewTab({
+ url: `logger-ui.html${hash}`,
+ select: true,
+ index: -1
});
break;
+ }
case 'relax-blocking-mode':
- vAPI.tabs.get(null, relaxBlockingMode);
+ relaxBlockingMode(await vAPI.tabs.getCurrent());
break;
default:
break;
diff --git a/src/js/contextmenu.js b/src/js/contextmenu.js
index 4d70ecb24..4c5ff4c9e 100644
--- a/src/js/contextmenu.js
+++ b/src/js/contextmenu.js
@@ -128,17 +128,16 @@ const update = function(tabId = undefined) {
// For unknown reasons, the currently active tab will not be successfully
// looked up after closing a window.
-vAPI.contextMenu.onMustUpdate = function(tabId = undefined) {
+vAPI.contextMenu.onMustUpdate = async function(tabId = undefined) {
if ( µBlock.userSettings.contextMenuEnabled === false ) {
return update();
}
if ( tabId !== undefined ) {
return update(tabId);
}
- vAPI.tabs.get(null, tab => {
- if ( tab instanceof Object === false ) { return; }
- update(tab.id);
- });
+ const tab = await vAPI.tabs.getCurrent();
+ if ( tab instanceof Object === false ) { return; }
+ update(tab.id);
};
return { update: vAPI.contextMenu.onMustUpdate };
diff --git a/src/js/messaging.js b/src/js/messaging.js
index 3ea332a4c..0ccb7d123 100644
--- a/src/js/messaging.js
+++ b/src/js/messaging.js
@@ -346,22 +346,20 @@ const popupDataFromTabId = function(tabId, tabTitle) {
return r;
};
-const popupDataFromRequest = function(request, callback) {
+const popupDataFromRequest = async function(request) {
if ( request.tabId ) {
- callback(popupDataFromTabId(request.tabId, ''));
- return;
+ return popupDataFromTabId(request.tabId, '');
}
// Still no target tab id? Use currently selected tab.
- vAPI.tabs.get(null, function(tab) {
- var tabId = '';
- var tabTitle = '';
- if ( tab ) {
- tabId = tab.id;
- tabTitle = tab.title || '';
- }
- callback(popupDataFromTabId(tabId, tabTitle));
- });
+ const tab = await vAPI.tabs.getCurrent();
+ let tabId = '';
+ let tabTitle = '';
+ if ( tab instanceof Object ) {
+ tabId = tab.id;
+ tabTitle = tab.title || '';
+ }
+ return popupDataFromTabId(tabId, tabTitle);
};
const onMessage = function(request, sender, callback) {
@@ -370,7 +368,9 @@ const onMessage = function(request, sender, callback) {
// Async
switch ( request.what ) {
case 'getPopupData':
- popupDataFromRequest(request, callback);
+ popupDataFromRequest(request).then(popupData => {
+ callback(popupData);
+ });
return;
default:
@@ -1151,7 +1151,7 @@ vAPI.messaging.listen({
const µb = µBlock;
const extensionOriginURL = vAPI.getURL('');
-const getLoggerData = function(details, activeTabId, callback) {
+const getLoggerData = async function(details, activeTabId, callback) {
const response = {
colorBlind: µb.userSettings.colorBlindFriendly,
entries: µb.logger.readAll(details.ownerId),
@@ -1178,26 +1178,20 @@ const getLoggerData = function(details, activeTabId, callback) {
response.activeTabId = undefined;
}
}
- if ( details.popupLoggerBoxChanged && browser.windows instanceof Object ) {
- browser.tabs.query(
- { url: vAPI.getURL('/logger-ui.html?popup=1') },
- tabs => {
- if ( Array.isArray(tabs) === false ) { return; }
- if ( tabs.length === 0 ) { return; }
- browser.windows.get(tabs[0].windowId, win => {
- if ( win instanceof Object === false ) { return; }
- vAPI.localStorage.setItem(
- 'popupLoggerBox',
- JSON.stringify({
- left: win.left,
- top: win.top,
- width: win.width,
- height: win.height,
- })
- );
- });
- }
- );
+ if ( details.popupLoggerBoxChanged && vAPI.windows instanceof Object ) {
+ const tabs = await vAPI.tabs.query({
+ url: vAPI.getURL('/logger-ui.html?popup=1')
+ });
+ if ( tabs.length !== 0 ) {
+ const win = await vAPI.windows.get(tabs[0].windowId);
+ if ( win === null ) { return; }
+ vAPI.localStorage.setItem('popupLoggerBox', JSON.stringify({
+ left: win.left,
+ top: win.top,
+ width: win.width,
+ height: win.height,
+ }));
+ }
}
callback(response);
};
@@ -1242,10 +1236,9 @@ const onMessage = function(request, sender, callback) {
µb.logger.ownerId !== undefined &&
µb.logger.ownerId !== request.ownerId
) {
- callback({ unavailable: true });
- return;
+ return callback({ unavailable: true });
}
- vAPI.tabs.get(null, function(tab) {
+ vAPI.tabs.getCurrent().then(tab => {
getLoggerData(request, tab && tab.id, callback);
});
return;
diff --git a/src/js/start.js b/src/js/start.js
index 45d42b5d4..05384814a 100644
--- a/src/js/start.js
+++ b/src/js/start.js
@@ -106,7 +106,7 @@ const onAllReady = function() {
// in already opened web pages, to remove whatever nuisance could make it to
// the web pages before uBlock was ready.
-const initializeTabs = function() {
+const initializeTabs = async function() {
const handleScriptResponse = function(tabId, results) {
if (
Array.isArray(results) === false ||
@@ -128,24 +128,21 @@ const initializeTabs = function() {
}
}
};
- const bindToTabs = function(tabs) {
- for ( const tab of tabs ) {
- µb.tabContextManager.commit(tab.id, tab.url);
- µb.bindTabToPageStats(tab.id);
- // https://github.com/chrisaljoudi/uBlock/issues/129
- // Find out whether content scripts need to be injected
- // programmatically. This may be necessary for web pages which
- // were loaded before uBO launched.
- if ( /^https?:\/\//.test(tab.url) === false ) { continue; }
- vAPI.tabs.injectScript(
- tab.id,
- { file: 'js/scriptlets/should-inject-contentscript.js' },
- handleScriptResponse.bind(null, tab.id)
- );
- }
- };
-
- browser.tabs.query({ url: '' }, bindToTabs);
+ const tabs = await vAPI.tabs.query({ url: '' });
+ for ( const tab of tabs ) {
+ µb.tabContextManager.commit(tab.id, tab.url);
+ µb.bindTabToPageStats(tab.id);
+ // https://github.com/chrisaljoudi/uBlock/issues/129
+ // Find out whether content scripts need to be injected
+ // programmatically. This may be necessary for web pages which
+ // were loaded before uBO launched.
+ if ( /^https?:\/\//.test(tab.url) === false ) { continue; }
+ vAPI.tabs.injectScript(
+ tab.id,
+ { file: 'js/scriptlets/should-inject-contentscript.js' },
+ handleScriptResponse.bind(null, tab.id)
+ );
+ }
};
/******************************************************************************/
diff --git a/src/js/tab.js b/src/js/tab.js
index 1dbecb1b2..62c5ad9f8 100644
--- a/src/js/tab.js
+++ b/src/js/tab.js
@@ -542,19 +542,18 @@ housekeep itself.
}
};
- TabContext.prototype.onGC = function() {
+ TabContext.prototype.onGC = async function() {
if ( vAPI.isBehindTheSceneTabId(this.tabId) ) { return; }
// https://github.com/gorhill/uBlock/issues/1713
// For unknown reasons, Firefox's setTimeout() will sometimes
// causes the callback function to be called immediately, bypassing
// the main event loop. For now this should prevent uBO from crashing
// as a result of the bad setTimeout() behavior.
- if ( this.onGCBarrier ) {
- return;
- }
+ if ( this.onGCBarrier ) { return; }
this.onGCBarrier = true;
this.gcTimer = null;
- vAPI.tabs.get(this.tabId, tab => { this.onTab(tab); });
+ const tab = await vAPI.tabs.get(this.tabId);
+ this.onTab(tab);
this.onGCBarrier = false;
};
@@ -1065,9 +1064,10 @@ vAPI.tabs = new vAPI.Tabs();
}
};
- const updateTitle = function(entry) {
+ const updateTitle = async function(entry) {
tabIdToTimer.delete(entry.tabId);
- vAPI.tabs.get(entry.tabId, onTabReady.bind(null, entry));
+ const tab = await vAPI.tabs.get(entry.tabId);
+ onTabReady(entry, tab);
};
return function(tabId) {
@@ -1098,11 +1098,10 @@ vAPI.tabs = new vAPI.Tabs();
const pageStoreJanitor = function() {
const tabIds = Array.from(µBlock.pageStores.keys()).sort();
- const checkTab = tabId => {
- vAPI.tabs.get(tabId, tab => {
- if ( tab ) { return; }
- µBlock.unbindTabFromPageStats(tabId);
- });
+ const checkTab = async tabId => {
+ const tab = await vAPI.tabs.get(tabId);
+ if ( tab ) { return; }
+ µBlock.unbindTabFromPageStats(tabId);
};
if ( pageStoreJanitorSampleAt >= tabIds.length ) {
pageStoreJanitorSampleAt = 0;
diff --git a/tools/make-firefox.sh b/tools/make-firefox.sh
index f46ed0ce8..6bca88b7c 100755
--- a/tools/make-firefox.sh
+++ b/tools/make-firefox.sh
@@ -12,11 +12,12 @@ mkdir -p $DES
echo "*** uBlock0.firefox: copying common files"
bash ./tools/copy-common-files.sh $DES
-cp -R $DES/_locales/nb $DES/_locales/no
+cp -R $DES/_locales/nb $DES/_locales/no
-cp platform/firefox/manifest.json $DES/
-cp platform/firefox/vapi-usercss.js $DES/js/
-cp platform/firefox/vapi-webrequest.js $DES/js/
+cp platform/firefox/manifest.json $DES/
+cp platform/firefox/webext.js $DES/js/
+cp platform/firefox/vapi-usercss.js $DES/js/
+cp platform/firefox/vapi-webrequest.js $DES/js/
echo "*** uBlock0.firefox: concatenating content scripts"
cat $DES/js/vapi-usercss.js > /tmp/contentscript.js