diff --git a/platform/webext/background.html b/platform/webext/background.html
new file mode 100644
index 0000000..2b9603f
--- /dev/null
+++ b/platform/webext/background.html
@@ -0,0 +1,36 @@
+
+
+
+
+uBlock Origin
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/platform/webext/bootstrap.js b/platform/webext/bootstrap.js
new file mode 100644
index 0000000..0fe4fd8
--- /dev/null
+++ b/platform/webext/bootstrap.js
@@ -0,0 +1,276 @@
+/*******************************************************************************
+
+ uMatrix - a browser extension to black/white list requests.
+ Copyright (C) 2014-2017 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/uMatrix
+*/
+
+/* exported startup, shutdown, install, uninstall */
+
+'use strict';
+
+/******************************************************************************/
+
+const hostName = 'umatrix';
+
+/******************************************************************************/
+
+function startup({ webExtension }) {
+ webExtension.startup().then(api => {
+ let { browser } = api,
+ storageMigrator;
+ let onMessage = function(message, sender, callback) {
+ if ( message.what === 'webext:storageMigrateNext' ) {
+ storageMigrator = storageMigrator || getStorageMigrator();
+ storageMigrator.getNext((key, value) => {
+ if ( key === undefined ) {
+ storageMigrator.markAsDone();
+ storageMigrator = undefined;
+ browser.runtime.onMessage.removeListener(onMessage);
+ }
+ callback({ key: key, value: JSON.stringify(value) });
+ });
+ return true;
+ }
+ if ( message.what === 'webext:storageMigrateDone' ) {
+ browser.runtime.onMessage.removeListener(onMessage);
+ }
+ if ( typeof callback === 'function' ) {
+ callback();
+ }
+ };
+ browser.runtime.onMessage.addListener(onMessage);
+ });
+}
+
+function shutdown() {
+}
+
+function install() {
+}
+
+function uninstall() {
+}
+
+/******************************************************************************/
+
+var getStorageMigrator = function() {
+ var db = null;
+ var dbOpenError = '';
+
+ var close = function() {
+ if ( db !== null ) {
+ db.asyncClose();
+ }
+ db = null;
+ };
+
+ var open = function() {
+ if ( db !== null ) {
+ return db;
+ }
+
+ // Create path
+ var { Services } = Components.utils.import('resource://gre/modules/Services.jsm', null),
+ path = Services.dirsvc.get('ProfD', Components.interfaces.nsIFile);
+ path.append('extension-data');
+ path.append(hostName + '.sqlite');
+ if ( !path.exists() || !path.isFile() ) {
+ return null;
+ }
+
+ // Open database.
+ try {
+ db = Services.storage.openDatabase(path);
+ if ( db.connectionReady === false ) {
+ db.asyncClose();
+ db = null;
+ }
+ } catch (ex) {
+ if ( dbOpenError === '' ) {
+ dbOpenError = ex.name;
+ if ( ex.name === 'NS_ERROR_FILE_CORRUPTED' ) {
+ close();
+ }
+ }
+ }
+
+ if ( db === null ) {
+ return null;
+ }
+
+ // Since database could be opened successfully, reset error flag (its
+ // purpose is to avoid spamming console with error messages).
+ dbOpenError = '';
+
+ return db;
+ };
+
+ // Execute a query
+ var runStatement = function(stmt, callback) {
+ var result = {};
+
+ stmt.executeAsync({
+ handleResult: function(rows) {
+ if ( !rows || typeof callback !== 'function' ) {
+ return;
+ }
+
+ var row;
+
+ while ( (row = rows.getNextRow()) ) {
+ // we assume that there will be two columns, since we're
+ // using it only for preferences
+ result[row.getResultByIndex(0)] = row.getResultByIndex(1);
+ }
+ },
+ handleCompletion: function(reason) {
+ if ( typeof callback === 'function' && reason === 0 ) {
+ callback(result);
+ }
+ result = null;
+ },
+ handleError: function(error) {
+ // Caller expects an answer regardless of failure.
+ if ( typeof callback === 'function' ) {
+ callback({});
+ }
+ result = null;
+ // https://github.com/gorhill/uBlock/issues/1768
+ // Error cases which warrant a removal of the SQL file, so far:
+ // - SQLLite error 11 database disk image is malformed
+ // Can't find doc on MDN about the type of error.result, so I
+ // force a string comparison.
+ if ( error.result.toString() === '11' ) {
+ close();
+ }
+ }
+ });
+ };
+
+ var bindNames = function(stmt, names) {
+ if ( Array.isArray(names) === false || names.length === 0 ) {
+ return;
+ }
+ var params = stmt.newBindingParamsArray();
+ var i = names.length, bp;
+ while ( i-- ) {
+ bp = params.newBindingParams();
+ bp.bindByName('name', names[i]);
+ params.addParams(bp);
+ }
+ stmt.bindParameters(params);
+ };
+
+ var read = function(details, callback) {
+ if ( typeof callback !== 'function' ) {
+ return;
+ }
+
+ var prepareResult = function(result) {
+ var key;
+ for ( key in result ) {
+ if ( result.hasOwnProperty(key) === false ) {
+ continue;
+ }
+ result[key] = JSON.parse(result[key]);
+ }
+ if ( typeof details === 'object' && details !== null ) {
+ for ( key in details ) {
+ if ( result.hasOwnProperty(key) === false ) {
+ result[key] = details[key];
+ }
+ }
+ }
+ callback(result);
+ };
+
+ if ( open() === null ) {
+ prepareResult({});
+ return;
+ }
+
+ var names = [];
+ if ( details !== null ) {
+ if ( Array.isArray(details) ) {
+ names = details;
+ } else if ( typeof details === 'object' ) {
+ names = Object.keys(details);
+ } else {
+ names = [details.toString()];
+ }
+ }
+
+ var stmt;
+ if ( names.length === 0 ) {
+ stmt = db.createAsyncStatement('SELECT * FROM "settings"');
+ } else {
+ stmt = db.createAsyncStatement('SELECT * FROM "settings" WHERE "name" = :name');
+ bindNames(stmt, names);
+ }
+
+ runStatement(stmt, prepareResult);
+ };
+
+ let allKeys;
+
+ let readNext = function(key, callback) {
+ if ( key === undefined ) {
+ callback();
+ return;
+ }
+ read(key, bin => {
+ if ( bin instanceof Object && bin.hasOwnProperty(key) ) {
+ callback(key, bin[key]);
+ } else {
+ callback(key);
+ }
+ });
+ };
+
+ let getNext = function(callback) {
+ if ( Array.isArray(allKeys) ) {
+ readNext(allKeys.pop(), callback);
+ return;
+ }
+ if ( open() === null ) {
+ callback();
+ return;
+ }
+ let stmt = db.createAsyncStatement('SELECT "name",\'dummy\' FROM "settings"');
+ runStatement(stmt, result => {
+ allKeys = [];
+ for ( let key in result ) {
+ if ( result.hasOwnProperty(key) ) {
+ allKeys.push(key);
+ }
+ }
+ readNext(allKeys.pop(), callback);
+ });
+ };
+
+ let markAsDone = function() {
+ close();
+ };
+
+ return {
+ getNext: getNext,
+ markAsDone: markAsDone,
+ };
+};
+
+/******************************************************************************/
diff --git a/platform/webext/chrome.manifest b/platform/webext/chrome.manifest
new file mode 100644
index 0000000..9516057
--- /dev/null
+++ b/platform/webext/chrome.manifest
@@ -0,0 +1 @@
+content umatrix ./
diff --git a/platform/webext/from-legacy.js b/platform/webext/from-legacy.js
new file mode 100644
index 0000000..48aa6a5
--- /dev/null
+++ b/platform/webext/from-legacy.js
@@ -0,0 +1,68 @@
+/*******************************************************************************
+
+ uMatrix - a browser extension to black/white list requests.
+ Copyright (C) 2017 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/uMatrix
+*/
+
+// For background page
+
+'use strict';
+
+/******************************************************************************/
+
+(function() {
+ let µm = µMatrix;
+
+ let migrateAll = function(callback) {
+ let mustRestart = false;
+
+ let migrateKeyValue = function(details, callback) {
+ let bin = {};
+ bin[details.key] = JSON.parse(details.value);
+ self.browser.storage.local.set(bin, callback);
+ mustRestart = true;
+ };
+
+ let migrateNext = function() {
+ self.browser.runtime.sendMessage({ what: 'webext:storageMigrateNext' }, response => {
+ if ( response.key === undefined ) {
+ if ( mustRestart ) {
+ self.browser.runtime.reload();
+ } else {
+ callback();
+ }
+ return;
+ }
+ migrateKeyValue(response, migrateNext);
+ });
+ };
+
+ self.browser.storage.local.get('legacyStorageMigrated', bin => {
+ if ( bin && bin.legacyStorageMigrated ) {
+ self.browser.runtime.sendMessage({ what: 'webext:storageMigrateDone' });
+ return callback();
+ }
+ self.browser.storage.local.set({ legacyStorageMigrated: true });
+ migrateNext();
+ });
+ };
+
+ µm.onBeforeStartQueue.push(migrateAll);
+})();
+
+/******************************************************************************/
diff --git a/platform/webext/install.rdf b/platform/webext/install.rdf
new file mode 100644
index 0000000..5c9244b
--- /dev/null
+++ b/platform/webext/install.rdf
@@ -0,0 +1,28 @@
+
+
+
+ uMatrix-webext@raymondhill.net
+ {version}
+ {name}
+ {description}
+ https://github.com/gorhill/uMatrix
+ {author}
+ Deathamns
+ Alex Vallat
+ 2
+ true
+ true
+ true
+{localized}
+
+
+
+
+ {{ec8030f7-c20a-464f-9b0e-13a3a9e97384}}
+ 53.0a1
+ *
+
+
+
+
+
diff --git a/src/js/assets.js b/src/js/assets.js
index a0e2411..0c33e94 100644
--- a/src/js/assets.js
+++ b/src/js/assets.js
@@ -28,7 +28,6 @@
/******************************************************************************/
var reIsExternalPath = /^(?:[a-z-]+):\/\//,
- reIsUserAsset = /^user-/,
errorCantConnectTo = vAPI.i18n('errorCantConnectTo'),
noopfunc = function(){};
@@ -188,7 +187,6 @@ var migrate = function(callback) {
}
var aliases = api.listKeyAliases;
for ( var oldKey in entries ) {
- if ( oldKey.endsWith('assets/user/filters.txt') ) { continue; }
var newKey = aliases[oldKey];
if ( !newKey && /^https?:\/\//.test(oldKey) ) {
newKey = oldKey;
diff --git a/src/js/background.js b/src/js/background.js
index 0376346..54b2ea6 100644
--- a/src/js/background.js
+++ b/src/js/background.js
@@ -1,6 +1,6 @@
/*******************************************************************************
- µMatrix - a Chromium browser extension to black/white list requests.
+ uMatrix - a browser extension to black/white list requests.
Copyright (C) 2014-2017 Raymond Hill
This program is free software: you can redistribute it and/or modify
@@ -100,6 +100,8 @@ var requestStatsFactory = function() {
/******************************************************************************/
return {
+ onBeforeStartQueue: [],
+
userSettings: {
autoUpdate: false,
clearBrowserCache: true,
diff --git a/src/js/start.js b/src/js/start.js
index 3d361fc..ffe67f2 100644
--- a/src/js/start.js
+++ b/src/js/start.js
@@ -1,6 +1,6 @@
/*******************************************************************************
- µMatrix - a Chromium browser extension to black/white list requests.
+ uMatrix - a Chromium browser extension to black/white list requests.
Copyright (C) 2014-2017 Raymond Hill
This program is free software: you can redistribute it and/or modify
@@ -89,6 +89,20 @@ var rwLocalUserSettings = {
/******************************************************************************/
+var processCallbackQueue = function(queue, callback) {
+ var processOne = function() {
+ var fn = queue.pop();
+ if ( fn ) {
+ fn(processOne);
+ } else if ( typeof callback === 'function' ) {
+ callback();
+ }
+ };
+ processOne();
+};
+
+/******************************************************************************/
+
var onAllDone = function() {
µm.webRequest.start();
@@ -151,8 +165,9 @@ var onPSLReady = function() {
vAPI.tabs.getAll(onTabsReady);
};
-// Must be done ASAP
-µm.loadPublicSuffixList(onPSLReady);
+processCallbackQueue(µm.onBeforeStartQueue, function() {
+ µm.loadPublicSuffixList(onPSLReady);
+});
/******************************************************************************/
diff --git a/tools/make-firefox-meta.py b/tools/make-firefox-meta.py
index 8aa2f4e..49b3b61 100755
--- a/tools/make-firefox-meta.py
+++ b/tools/make-firefox-meta.py
@@ -116,5 +116,5 @@ install_rdf = pj(build_dir, 'install.rdf')
with open(install_rdf, 'r+t', encoding='utf-8', newline='\n') as f:
install_rdf = f.read()
f.seek(0)
-
f.write(install_rdf.format(**manifest))
+ f.truncate()
diff --git a/tools/make-webext-meta.py b/tools/make-webext-meta.py
index 15df315..41c8a85 100755
--- a/tools/make-webext-meta.py
+++ b/tools/make-webext-meta.py
@@ -2,7 +2,11 @@
import os
import json
+import re
import sys
+from io import open as uopen
+from collections import OrderedDict
+from xml.sax.saxutils import escape
if len(sys.argv) == 1 or not sys.argv[1]:
raise SystemExit('Build dir missing.')
@@ -10,7 +14,7 @@ if len(sys.argv) == 1 or not sys.argv[1]:
proj_dir = os.path.join(os.path.split(os.path.abspath(__file__))[0], '..')
build_dir = os.path.abspath(sys.argv[1])
-# Import version number from chromium platform
+# Import data from chromium platform
chromium_manifest = {}
webext_manifest = {}
@@ -18,7 +22,8 @@ chromium_manifest_file = os.path.join(proj_dir, 'platform', 'chromium', 'manifes
with open(chromium_manifest_file) as f1:
chromium_manifest = json.load(f1)
-webext_manifest_file = os.path.join(build_dir, 'manifest.json')
+# WebExtension part
+webext_manifest_file = os.path.join(build_dir, 'webextension', 'manifest.json')
with open(webext_manifest_file) as f2:
webext_manifest = json.load(f2)
@@ -27,3 +32,53 @@ webext_manifest['version'] = chromium_manifest['version']
with open(webext_manifest_file, 'w') as f2:
json.dump(webext_manifest, f2, indent=2, separators=(',', ': '), sort_keys=True)
f2.write('\n')
+
+# Legacy part
+descriptions = OrderedDict({})
+source_locale_dir = os.path.join(build_dir, 'webextension', '_locales')
+for alpha2 in sorted(os.listdir(source_locale_dir)):
+ locale_path = os.path.join(source_locale_dir, alpha2, 'messages.json')
+ with uopen(locale_path, encoding='utf-8') as f:
+ strings = json.load(f, object_pairs_hook=OrderedDict)
+ alpha2 = alpha2.replace('_', '-')
+ descriptions[alpha2] = strings['extShortDesc']['message']
+
+webext_manifest['author'] = chromium_manifest['author'];
+webext_manifest['name'] = chromium_manifest['name'] + '/webext';
+webext_manifest['homepage'] = 'https://github.com/gorhill/uMatrix'
+webext_manifest['description'] = escape(descriptions['en'])
+del descriptions['en']
+
+match = re.search('^(\d+\.\d+\.\d+)(\.\d+)$', chromium_manifest['version'])
+if match:
+ buildtype = int(match.group(2)[1:])
+ if buildtype < 100:
+ builttype = 'b' + str(buildtype)
+ else:
+ builttype = 'rc' + str(buildtype - 100)
+ webext_manifest['version'] = match.group(1) + builttype
+
+webext_manifest['localized'] = []
+t = ' '
+t3 = 3 * t
+for alpha2 in descriptions:
+ if alpha2 == 'en':
+ continue
+ webext_manifest['localized'].append(
+ '\n' + t*2 + '\n' +
+ t3 + '' + alpha2 + '\n' +
+ t3 + '' + webext_manifest['name'] + '\n' +
+ t3 + '' + escape(descriptions[alpha2]) + '\n' +
+ t3 + '' + webext_manifest['author'] + '\n' +
+ # t3 + '' + ??? + '\n' +
+ t3 + '' + webext_manifest['homepage'] + '\n' +
+ t*2 + ''
+ )
+webext_manifest['localized'] = '\n'.join(webext_manifest['localized'])
+
+install_rdf = os.path.join(build_dir, 'install.rdf')
+with uopen(install_rdf, 'r+t', encoding='utf-8', newline='\n') as f:
+ install_rdf = f.read()
+ f.seek(0)
+ f.write(install_rdf.format(**webext_manifest))
+ f.truncate()
diff --git a/tools/make-webext.sh b/tools/make-webext.sh
index 326c59e..5bbf730 100755
--- a/tools/make-webext.sh
+++ b/tools/make-webext.sh
@@ -7,17 +7,23 @@ echo "*** uMatrix.webext: Copying files"
DES=dist/build/uMatrix.webext
rm -rf $DES
-mkdir -p $DES
+mkdir -p $DES/webextension
-cp -R ./assets $DES/
-cp -R ./src/* $DES/
-cp -R $DES/_locales/nb $DES/_locales/no
-cp platform/chromium/*.html $DES/
-cp platform/webext/polyfill.js $DES/js/
-cp platform/chromium/*.js $DES/js/
-cp -R platform/chromium/img/* $DES/img/
-cp platform/webext/manifest.json $DES/
-cp LICENSE.txt $DES/
+cp -R ./assets $DES/webextension/
+cp -R ./src/* $DES/webextension/
+cp platform/chromium/*.html $DES/webextension/
+cp platform/chromium/*.js $DES/webextension/js/
+cp -R platform/chromium/img/* $DES/webextension/img/
+cp LICENSE.txt $DES/webextension/
+
+cp platform/webext/background.html $DES/webextension/
+cp platform/webext/polyfill.js $DES/webextension/js/
+cp platform/webext/from-legacy.js $DES/webextension/js/
+cp platform/webext/manifest.json $DES/webextension/
+cp platform/webext/bootstrap.js $DES/
+cp platform/webext/chrome.manifest $DES/
+cp platform/webext/install.rdf $DES/
+mv $DES/webextension/img/icon_128.png $DES/icon.png
echo "*** uMatrix.webext: Generating meta..."
python tools/make-webext-meta.py $DES/