aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesús <heckyel@hyperbola.info>2019-08-11 19:58:26 -0500
committerJesús <heckyel@hyperbola.info>2019-08-11 19:58:26 -0500
commit9ec39f09621c9975582a2b6d9a6fa0313b308086 (patch)
tree918786fdf371606f80bc9cdfbc2966909703e9bf
parent144581a54b8bb1808e23a3ea5c81e619e36a459f (diff)
downloadematrix-9ec39f09621c9975582a2b6d9a6fa0313b308086.tar.lz
ematrix-9ec39f09621c9975582a2b6d9a6fa0313b308086.tar.xz
ematrix-9ec39f09621c9975582a2b6d9a6fa0313b308086.zip
remove tabs in javascript files
-rw-r--r--js/about.js198
-rw-r--r--js/assets.js1672
-rw-r--r--js/background.js347
-rw-r--r--js/browsercache.js42
-rw-r--r--js/cloud-ui.js298
-rw-r--r--js/contentscript-start.js4
-rw-r--r--js/contentscript.js902
-rw-r--r--js/cookies.js837
-rw-r--r--js/dashboard-common.js20
-rw-r--r--js/hosts-files.js621
-rw-r--r--js/i18n.js336
-rw-r--r--js/liquid-dict.js310
-rw-r--r--js/logger-ui.js1517
-rw-r--r--js/logger.js2
-rw-r--r--js/main-blocked.js238
-rw-r--r--js/matrix.js1390
-rw-r--r--js/messaging.js1408
-rw-r--r--js/pagestats.js440
-rw-r--r--js/popup.js2600
-rw-r--r--js/raw-settings.js140
-rw-r--r--js/settings.js270
-rw-r--r--js/start.js110
-rw-r--r--js/storage.js325
-rw-r--r--js/tab.js1062
-rw-r--r--js/traffic.js719
-rw-r--r--js/udom.js1122
-rw-r--r--js/uritools.js883
-rw-r--r--js/user-rules.js513
-rw-r--r--js/usersettings.js8
-rw-r--r--js/vapi-background.js1286
-rw-r--r--js/vapi-browser.js258
-rw-r--r--js/vapi-client.js162
-rw-r--r--js/vapi-cloud.js116
-rw-r--r--js/vapi-common.js142
-rw-r--r--js/vapi-contextmenu.js188
-rw-r--r--js/vapi-cookies.js100
-rw-r--r--js/vapi-core.js120
-rw-r--r--js/vapi-messaging.js136
-rw-r--r--js/vapi-net.js46
-rw-r--r--js/vapi-storage.js298
-rw-r--r--js/vapi-tabs.js736
-rw-r--r--js/vapi-window.js184
-rw-r--r--js/xal.js56
43 files changed, 11107 insertions, 11055 deletions
diff --git a/js/about.js b/js/about.js
index 233c3f0..098bb5e 100644
--- a/js/about.js
+++ b/js/about.js
@@ -29,119 +29,119 @@
uDom.onLoad(function() {
-/******************************************************************************/
-
-var backupUserDataToFile = function() {
- var userDataReady = function(userData) {
- vAPI.download({
- 'url': 'data:text/plain,' + encodeURIComponent(JSON.stringify(userData, null, 2)),
- 'filename': uDom('[data-i18n="aboutBackupFilename"]').text()
- });
- };
+ /******************************************************************************/
- vAPI.messaging.send('about.js', { what: 'getAllUserData' }, userDataReady);
-};
+ var backupUserDataToFile = function() {
+ var userDataReady = function(userData) {
+ vAPI.download({
+ 'url': 'data:text/plain,' + encodeURIComponent(JSON.stringify(userData, null, 2)),
+ 'filename': uDom('[data-i18n="aboutBackupFilename"]').text()
+ });
+ };
-/******************************************************************************/
-
-function restoreUserDataFromFile() {
- var validateBackup = function(s) {
- var userData = null;
- try {
- userData = JSON.parse(s);
- }
- catch (e) {
- userData = null;
- }
- if ( userData === null ) {
- return null;
- }
- if (
- typeof userData !== 'object' ||
- typeof userData.version !== 'string' ||
- typeof userData.when !== 'number' ||
- typeof userData.settings !== 'object' ||
- typeof userData.rules !== 'string' ||
- typeof userData.hostsFiles !== 'object'
- ) {
- return null;
- }
- return userData;
+ vAPI.messaging.send('about.js', { what: 'getAllUserData' }, userDataReady);
};
- var fileReaderOnLoadHandler = function() {
- var userData = validateBackup(this.result);
- if ( !userData ) {
- window.alert(uDom('[data-i18n="aboutRestoreError"]').text());
+ /******************************************************************************/
+
+ function restoreUserDataFromFile() {
+ var validateBackup = function(s) {
+ var userData = null;
+ try {
+ userData = JSON.parse(s);
+ }
+ catch (e) {
+ userData = null;
+ }
+ if ( userData === null ) {
+ return null;
+ }
+ if (
+ typeof userData !== 'object' ||
+ typeof userData.version !== 'string' ||
+ typeof userData.when !== 'number' ||
+ typeof userData.settings !== 'object' ||
+ typeof userData.rules !== 'string' ||
+ typeof userData.hostsFiles !== 'object'
+ ) {
+ return null;
+ }
+ return userData;
+ };
+
+ var fileReaderOnLoadHandler = function() {
+ var userData = validateBackup(this.result);
+ if ( !userData ) {
+ window.alert(uDom('[data-i18n="aboutRestoreError"]').text());
+ return;
+ }
+ var time = new Date(userData.when);
+ var msg = uDom('[data-i18n="aboutRestoreConfirm"]').text()
+ .replace('{{time}}', time.toLocaleString());
+ var proceed = window.confirm(msg);
+ if ( proceed ) {
+ vAPI.messaging.send(
+ 'about.js',
+ { what: 'restoreAllUserData', userData: userData }
+ );
+ }
+ };
+
+ var file = this.files[0];
+ if ( file === undefined || file.name === '' ) {
return;
}
- var time = new Date(userData.when);
- var msg = uDom('[data-i18n="aboutRestoreConfirm"]').text()
- .replace('{{time}}', time.toLocaleString());
- var proceed = window.confirm(msg);
- if ( proceed ) {
- vAPI.messaging.send(
- 'about.js',
- { what: 'restoreAllUserData', userData: userData }
- );
+ if ( file.type.indexOf('text') !== 0 ) {
+ return;
}
- };
-
- var file = this.files[0];
- if ( file === undefined || file.name === '' ) {
- return;
+ var fr = new FileReader();
+ fr.onload = fileReaderOnLoadHandler;
+ fr.readAsText(file);
}
- if ( file.type.indexOf('text') !== 0 ) {
- return;
- }
- var fr = new FileReader();
- fr.onload = fileReaderOnLoadHandler;
- fr.readAsText(file);
-}
-
-/******************************************************************************/
-var startRestoreFilePicker = function() {
- var input = document.getElementById('restoreFilePicker');
- // Reset to empty string, this will ensure an change event is properly
- // triggered if the user pick a file, even if it is the same as the last
- // one picked.
- input.value = '';
- input.click();
-};
+ /******************************************************************************/
-/******************************************************************************/
+ var startRestoreFilePicker = function() {
+ var input = document.getElementById('restoreFilePicker');
+ // Reset to empty string, this will ensure an change event is properly
+ // triggered if the user pick a file, even if it is the same as the last
+ // one picked.
+ input.value = '';
+ input.click();
+ };
-var resetUserData = function() {
- var proceed = window.confirm(uDom('[data-i18n="aboutResetConfirm"]').text());
- if ( proceed ) {
- vAPI.messaging.send('about.js', { what: 'resetAllUserData' });
- }
-};
+ /******************************************************************************/
-/******************************************************************************/
-
-(function() {
- var renderStats = function(details) {
- document.getElementById('aboutVersion').textContent = details.version;
- var template = uDom('[data-i18n="aboutStorageUsed"]').text();
- var storageUsed = '?';
- if ( typeof details.storageUsed === 'number' ) {
- storageUsed = details.storageUsed.toLocaleString();
+ var resetUserData = function() {
+ var proceed = window.confirm(uDom('[data-i18n="aboutResetConfirm"]').text());
+ if ( proceed ) {
+ vAPI.messaging.send('about.js', { what: 'resetAllUserData' });
}
- document.getElementById('aboutStorageUsed').textContent =
- template.replace('{{storageUsed}}', storageUsed);
};
- vAPI.messaging.send('about.js', { what: 'getSomeStats' }, renderStats);
-})();
-
-/******************************************************************************/
-
-uDom('#backupUserDataButton').on('click', backupUserDataToFile);
-uDom('#restoreUserDataButton').on('click', startRestoreFilePicker);
-uDom('#restoreFilePicker').on('change', restoreUserDataFromFile);
-uDom('#resetUserDataButton').on('click', resetUserData);
-/******************************************************************************/
+ /******************************************************************************/
+
+ (function() {
+ var renderStats = function(details) {
+ document.getElementById('aboutVersion').textContent = details.version;
+ var template = uDom('[data-i18n="aboutStorageUsed"]').text();
+ var storageUsed = '?';
+ if ( typeof details.storageUsed === 'number' ) {
+ storageUsed = details.storageUsed.toLocaleString();
+ }
+ document.getElementById('aboutStorageUsed').textContent =
+ template.replace('{{storageUsed}}', storageUsed);
+ };
+ vAPI.messaging.send('about.js', { what: 'getSomeStats' }, renderStats);
+ })();
+
+ /******************************************************************************/
+
+ uDom('#backupUserDataButton').on('click', backupUserDataToFile);
+ uDom('#restoreUserDataButton').on('click', startRestoreFilePicker);
+ uDom('#restoreFilePicker').on('change', restoreUserDataFromFile);
+ uDom('#resetUserDataButton').on('click', resetUserData);
+
+ /******************************************************************************/
});
diff --git a/js/assets.js b/js/assets.js
index 595b681..ec06c62 100644
--- a/js/assets.js
+++ b/js/assets.js
@@ -33,21 +33,21 @@
let connectionError = vAPI.i18n('errorCantConnectTo');
let notifyObservers = function (topic, details) {
- let result;
+ let result;
- for (let i=0; i<observers.length; ++i) {
- result = observers[i](topic, details);
- }
+ for (let i=0; i<observers.length; ++i) {
+ result = observers[i](topic, details);
+ }
- return result;
+ return result;
}
function isEmptyString(s) {
- return (typeof s === 'string' && s === '');
+ return (typeof s === 'string' && s === '');
}
function noOp() {
- return;
+ return;
}
// Cache Registry
@@ -57,308 +57,308 @@
let cacheRegistryStart = Date.now();
let saveCacheRegistry = (function () {
- let timer;
-
- function save() {
- timer = undefined;
- vAPI.cacheStorage.set({
- assetCacheRegistry: cacheRegistry,
- });
- }
-
- return function (lazy) {
- if (timer !== undefined) {
- clearTimeout(timer);
- }
-
- if (lazy === true) {
- timer = vAPI.setTimeout(save, 500);
- } else {
- save();
- }
- };
+ let timer;
+
+ function save() {
+ timer = undefined;
+ vAPI.cacheStorage.set({
+ assetCacheRegistry: cacheRegistry,
+ });
+ }
+
+ return function (lazy) {
+ if (timer !== undefined) {
+ clearTimeout(timer);
+ }
+
+ if (lazy === true) {
+ timer = vAPI.setTimeout(save, 500);
+ } else {
+ save();
+ }
+ };
})();
let getCacheRegistry = function (callback) {
- if (cacheRegistryReady == true) {
- callback(cacheRegistry);
- return;
- }
-
- if (cacheRegistryCallbacks !== undefined) {
- // If it's not undefined it's always an array
- //
- // eMatrix: this block in particular is probably never
- // called: originally, it was used because uMatrix called
- // some code in a callback that could wait
- //
- // While waiting, more elements could've been pushed in
- // the array
- //
- // Since the waiting callback is not used here, any
- // further callback are likely to be handled by the
- // condition above
- // This block is left here just in case
- cacheRegistryCallbacks.push(callback);
- return;
- }
-
- cacheRegistryCallbacks = [callback];
- cacheRegistryReady = true;
-
- let onRead = function (bin) {
- if (!bin || !bin['assetCacheRegistry']) {
- cacheRegistry = {};
- } else {
- cacheRegistry = bin['assetCacheRegistry'];
- }
- };
-
- vAPI.cacheStorage.get('assetCacheRegistry', onRead);
-
- let f;
- while ((f = cacheRegistryCallbacks.shift())) {
- f(cacheRegistry);
- }
+ if (cacheRegistryReady == true) {
+ callback(cacheRegistry);
+ return;
+ }
+
+ if (cacheRegistryCallbacks !== undefined) {
+ // If it's not undefined it's always an array
+ //
+ // eMatrix: this block in particular is probably never
+ // called: originally, it was used because uMatrix called
+ // some code in a callback that could wait
+ //
+ // While waiting, more elements could've been pushed in
+ // the array
+ //
+ // Since the waiting callback is not used here, any
+ // further callback are likely to be handled by the
+ // condition above
+ // This block is left here just in case
+ cacheRegistryCallbacks.push(callback);
+ return;
+ }
+
+ cacheRegistryCallbacks = [callback];
+ cacheRegistryReady = true;
+
+ let onRead = function (bin) {
+ if (!bin || !bin['assetCacheRegistry']) {
+ cacheRegistry = {};
+ } else {
+ cacheRegistry = bin['assetCacheRegistry'];
+ }
+ };
+
+ vAPI.cacheStorage.get('assetCacheRegistry', onRead);
+
+ let f;
+ while ((f = cacheRegistryCallbacks.shift())) {
+ f(cacheRegistry);
+ }
};
let readCache = function (key, callback) {
- let report = function (content, error) {
- let details = {
- assetKey: key,
- content: content,
- };
-
- if (error) {
- details.error = error;
- }
-
- callback(details);
- };
-
- let onRead = function (bin) {
- if (!bin || !bin[key]) {
- report('', 'E_NOTFOUND');
- return;
- }
-
- let entry = cacheRegistry[key];
- if (entry === undefined) {
- let onRead2 = function (bin) {
- if (!bin || !bin['assetCacheRegistry']) {
- cacheRegistry = {};
- } else {
- cacheRegistry = bin['assetCacheRegistry'];
- }
- };
-
- vAPI.cacheStorage.get('assetCacheRegistry', onRead2);
-
- entry = cacheRegistry[key];
- if (entry === undefined) {
- report('', 'E_NOTFOUND');
- return;
- }
- }
-
- entry.readTime = Date.now();
- saveCacheRegistry(true);
-
- report(bin[key]);
- };
-
- let onReady = function () {
- vAPI.cacheStorage.get(key, onRead);
- };
-
- getCacheRegistry(onReady);
+ let report = function (content, error) {
+ let details = {
+ assetKey: key,
+ content: content,
+ };
+
+ if (error) {
+ details.error = error;
+ }
+
+ callback(details);
+ };
+
+ let onRead = function (bin) {
+ if (!bin || !bin[key]) {
+ report('', 'E_NOTFOUND');
+ return;
+ }
+
+ let entry = cacheRegistry[key];
+ if (entry === undefined) {
+ let onRead2 = function (bin) {
+ if (!bin || !bin['assetCacheRegistry']) {
+ cacheRegistry = {};
+ } else {
+ cacheRegistry = bin['assetCacheRegistry'];
+ }
+ };
+
+ vAPI.cacheStorage.get('assetCacheRegistry', onRead2);
+
+ entry = cacheRegistry[key];
+ if (entry === undefined) {
+ report('', 'E_NOTFOUND');
+ return;
+ }
+ }
+
+ entry.readTime = Date.now();
+ saveCacheRegistry(true);
+
+ report(bin[key]);
+ };
+
+ let onReady = function () {
+ vAPI.cacheStorage.get(key, onRead);
+ };
+
+ getCacheRegistry(onReady);
};
let writeCache = function (key, details, callback) {
- let content = '';
-
- if (typeof details === 'string') {
- content = details;
- } else if (details instanceof Object) {
- content = details.content || '';
- }
-
- if (content === '') {
- removeCache(key, callback);
- return;
- }
-
- let report = function (content) {
- let details = {
- assetKey: key,
- content: content,
- };
-
- if (typeof callback === 'function') {
- callback(details);
- }
-
- notifyObservers('after-asset-updated', details);
- };
-
- let onReady = function () {
- let entry = cacheRegistry[key];
- if (entry === undefined) {
- entry = cacheRegistry[key] = {};
- }
-
- entry.writeTime = entry.readTime = Date.now();
- if (details instanceof Object && typeof details.url === 'string') {
- entry.remoteURL = details.url;
- }
-
- let bin = {
- assetCacheRegistry: cacheRegistry,
- };
-
- bin[key] = content;
- vAPI.cacheStorage.set(bin);
- report(content);
- };
-
- getCacheRegistry(onReady);
+ let content = '';
+
+ if (typeof details === 'string') {
+ content = details;
+ } else if (details instanceof Object) {
+ content = details.content || '';
+ }
+
+ if (content === '') {
+ removeCache(key, callback);
+ return;
+ }
+
+ let report = function (content) {
+ let details = {
+ assetKey: key,
+ content: content,
+ };
+
+ if (typeof callback === 'function') {
+ callback(details);
+ }
+
+ notifyObservers('after-asset-updated', details);
+ };
+
+ let onReady = function () {
+ let entry = cacheRegistry[key];
+ if (entry === undefined) {
+ entry = cacheRegistry[key] = {};
+ }
+
+ entry.writeTime = entry.readTime = Date.now();
+ if (details instanceof Object && typeof details.url === 'string') {
+ entry.remoteURL = details.url;
+ }
+
+ let bin = {
+ assetCacheRegistry: cacheRegistry,
+ };
+
+ bin[key] = content;
+ vAPI.cacheStorage.set(bin);
+ report(content);
+ };
+
+ getCacheRegistry(onReady);
};
let cacheRemove = function (pattern, callback) {
- let onReady = function (cache) {
- let entries = [];
+ let onReady = function (cache) {
+ let entries = [];
- for (let key in cache) {
- if (pattern instanceof RegExp && !pattern.test(key)) {
- continue;
- }
+ for (let key in cache) {
+ if (pattern instanceof RegExp && !pattern.test(key)) {
+ continue;
+ }
- if (typeof pattern === 'string' && key !== pattern) {
- continue;
- }
+ if (typeof pattern === 'string' && key !== pattern) {
+ continue;
+ }
- entries.push(key);
+ entries.push(key);
- delete cache[key];
- }
+ delete cache[key];
+ }
- if (entries.length !== 0) {
- vAPI.cacheStorage.remove(content);
+ if (entries.length !== 0) {
+ vAPI.cacheStorage.remove(content);
- let bin = {
- assetCacheRegistry: cache,
- };
- vAPI.cacheStorage.set(bin);
- }
+ let bin = {
+ assetCacheRegistry: cache,
+ };
+ vAPI.cacheStorage.set(bin);
+ }
- if (typeof callback === 'function') {
- callback();
- }
+ if (typeof callback === 'function') {
+ callback();
+ }
- for (let i=0; i<entries.length; ++i) {
- notifyObservers('after-asset-updated', {
- assetKey: entries[i],
- });
- }
- };
+ for (let i=0; i<entries.length; ++i) {
+ notifyObservers('after-asset-updated', {
+ assetKey: entries[i],
+ });
+ }
+ };
- getCacheRegistry(onReady);
+ getCacheRegistry(onReady);
};
let markDirtyCache = function (pattern, exclude, callback) {
- let onReady = function (registry) {
- let entry;
- let mustSave = false;
-
- for (let key in registry) {
- if (pattern instanceof RegExp && pattern.test(key) === false) {
- continue;
- } else if (typeof pattern === 'string' && key !== pattern) {
- continue;
- } else if (Array.isArray(pattern)
- && pattern.indexOf(key) === -1) {
- continue;
- }
-
- if (exclude instanceof RegExp && exclude.test(key)) {
- continue;
- } else if (typeof exclude === 'string' && key === exclude) {
- continue;
- } else if (Array.isArray(exclude)
- && exclude.indexOf(key) !== -1) {
- continue;
- }
-
- entry = registry[key];
- if (!entry.writeTime) {
- continue;
- }
-
- registry[key].writeTime = 0;
- mustSave = true;
- }
-
- if (mustSave) {
- let bin = {
- assetCacheRegistry: registry,
- };
- vAPI.cacheStorage.set(bin);
- }
-
- if (typeof callback === 'function') {
- callback();
- }
- };
-
- if (typeof exclude === 'function') {
- callback = exclude;
- exclude = undefined;
- }
-
- getCacheRegistry(onReady);
+ let onReady = function (registry) {
+ let entry;
+ let mustSave = false;
+
+ for (let key in registry) {
+ if (pattern instanceof RegExp && pattern.test(key) === false) {
+ continue;
+ } else if (typeof pattern === 'string' && key !== pattern) {
+ continue;
+ } else if (Array.isArray(pattern)
+ && pattern.indexOf(key) === -1) {
+ continue;
+ }
+
+ if (exclude instanceof RegExp && exclude.test(key)) {
+ continue;
+ } else if (typeof exclude === 'string' && key === exclude) {
+ continue;
+ } else if (Array.isArray(exclude)
+ && exclude.indexOf(key) !== -1) {
+ continue;
+ }
+
+ entry = registry[key];
+ if (!entry.writeTime) {
+ continue;
+ }
+
+ registry[key].writeTime = 0;
+ mustSave = true;
+ }
+
+ if (mustSave) {
+ let bin = {
+ assetCacheRegistry: registry,
+ };
+ vAPI.cacheStorage.set(bin);
+ }
+
+ if (typeof callback === 'function') {
+ callback();
+ }
+ };
+
+ if (typeof exclude === 'function') {
+ callback = exclude;
+ exclude = undefined;
+ }
+
+ getCacheRegistry(onReady);
};
let removeCache = function (key, callback) {
- let onReady = function (cacheRegistry) {
- let removedEntries = [];
- let removedContent = [];
-
- for (let k in cacheRegistry) {
- if (key instanceof RegExp && !key.test(k)) {
- continue;
- }
- if (typeof key === 'string' && k !== key) {
- continue;
- }
-
- removedEntries.push(k);
- removedContent.push(k);
-
- delete cacheRegistry[k];
- }
-
- if (removedContent.length > 0) {
- vAPI.cacheStorage.remove(removedContent);
-
- let bin = {
- assetCacheRegistry: cacheRegistry,
- };
-
- vAPI.cacheStorage.set(bin);
- }
-
- if (typeof callback === 'function') {
- callback();
- }
-
- for (let i=0; i<removedEntries.length; ++i) {
- notifyObservers('after-asset-updated', {
- assetKey: removedEntries[i],
- });
- }
- };
-
- getCacheRegistry(onReady);
+ let onReady = function (cacheRegistry) {
+ let removedEntries = [];
+ let removedContent = [];
+
+ for (let k in cacheRegistry) {
+ if (key instanceof RegExp && !key.test(k)) {
+ continue;
+ }
+ if (typeof key === 'string' && k !== key) {
+ continue;
+ }
+
+ removedEntries.push(k);
+ removedContent.push(k);
+
+ delete cacheRegistry[k];
+ }
+
+ if (removedContent.length > 0) {
+ vAPI.cacheStorage.remove(removedContent);
+
+ let bin = {
+ assetCacheRegistry: cacheRegistry,
+ };
+
+ vAPI.cacheStorage.set(bin);
+ }
+
+ if (typeof callback === 'function') {
+ callback();
+ }
+
+ for (let i=0; i<removedEntries.length; ++i) {
+ notifyObservers('after-asset-updated', {
+ assetKey: removedEntries[i],
+ });
+ }
+ };
+
+ getCacheRegistry(onReady);
};
// Source Registry
@@ -367,246 +367,246 @@
let sourceRegistryCallbacks = undefined;
let saveSourceRegistry = (function () {
- let timer;
-
- function save() {
- timer = undefined;
- vAPI.cacheStorage.set({
- assetSourceRegistry: sourceRegistry,
- });
- }
-
- return function (lazy) {
- if (timer !== undefined) {
- clearTimeout(timer);
- }
-
- if (lazy === true) {
- timer = vAPI.setTimeout(save, 500);
- } else {
- save();
- }
- };
+ let timer;
+
+ function save() {
+ timer = undefined;
+ vAPI.cacheStorage.set({
+ assetSourceRegistry: sourceRegistry,
+ });
+ }
+
+ return function (lazy) {
+ if (timer !== undefined) {
+ clearTimeout(timer);
+ }
+
+ if (lazy === true) {
+ timer = vAPI.setTimeout(save, 500);
+ } else {
+ save();
+ }
+ };
})();
let registerSource = function (key, details) {
- let entry = sourceRegistry[key] || {};
-
- for (let p in details) {
- if (details.hasOwnProperty(p) === false) {
- continue;
- }
-
- if (details[p] !== undefined) {
- entry[p] = details[p];
- } else {
- delete entry[p];
- }
- }
-
- let contentUrl = details.contentURL;
- if (contentUrl) {
- if (typeof contentUrl === 'string') {
- contentUrl = entry.contentURL = [contentUrl];
- } else if (Array.isArray(contentUrl) === false) {
- contentUrl = entry.contentURL = [];
- }
-
- let remoteCount = 0;
-
- for (let i=0; i<contentUrl.length; ++i) {
- if (externalPathRegex.test(contentUrl[i])) {
- ++remoteCount;
- }
- }
-
- entry.hasLocalURL = (remoteCount !== contentUrl.length);
- entry.hasRemoteURL = (remoteCount !== 0);
- } else {
- entry.contentURL = [];
- }
-
- if (typeof entry.updateAfter !== 'number') {
- entry.updateAfter = 13;
- }
-
- if (entry.submitter) {
- entry.submitTime = Date.now(); // Detects stale entries
- }
-
- sourceRegistry[key] = entry;
+ let entry = sourceRegistry[key] || {};
+
+ for (let p in details) {
+ if (details.hasOwnProperty(p) === false) {
+ continue;
+ }
+
+ if (details[p] !== undefined) {
+ entry[p] = details[p];
+ } else {
+ delete entry[p];
+ }
+ }
+
+ let contentUrl = details.contentURL;
+ if (contentUrl) {
+ if (typeof contentUrl === 'string') {
+ contentUrl = entry.contentURL = [contentUrl];
+ } else if (Array.isArray(contentUrl) === false) {
+ contentUrl = entry.contentURL = [];
+ }
+
+ let remoteCount = 0;
+
+ for (let i=0; i<contentUrl.length; ++i) {
+ if (externalPathRegex.test(contentUrl[i])) {
+ ++remoteCount;
+ }
+ }
+
+ entry.hasLocalURL = (remoteCount !== contentUrl.length);
+ entry.hasRemoteURL = (remoteCount !== 0);
+ } else {
+ entry.contentURL = [];
+ }
+
+ if (typeof entry.updateAfter !== 'number') {
+ entry.updateAfter = 13;
+ }
+
+ if (entry.submitter) {
+ entry.submitTime = Date.now(); // Detects stale entries
+ }
+
+ sourceRegistry[key] = entry;
};
let unregisterSource = function (key) {
- removeCache(key);
- delete sourceRegistry[key];
- saveSourceRegistry();
+ removeCache(key);
+ delete sourceRegistry[key];
+ saveSourceRegistry();
};
let updateSourceRegistry = function (string, silent) {
- let json;
-
- try {
- json = JSON.parse(string);
- } catch (e) {
- return;
- }
-
- for (let key in sourceRegistry) {
- if (json[key] === undefined
- && sourceRegistry[key].submitter === undefined) {
- unregisterSource(key);
- }
- }
-
- for (let key in json) {
- if (sourceRegistry[key] === undefined && !silent) {
- notifyObservers('builtin-asset-source-added', {
- assetKey: key,
- entry: json[key],
- });
- }
-
- registerSource(key, json[key]);
- }
-
- saveSourceRegistry();
+ let json;
+
+ try {
+ json = JSON.parse(string);
+ } catch (e) {
+ return;
+ }
+
+ for (let key in sourceRegistry) {
+ if (json[key] === undefined
+ && sourceRegistry[key].submitter === undefined) {
+ unregisterSource(key);
+ }
+ }
+
+ for (let key in json) {
+ if (sourceRegistry[key] === undefined && !silent) {
+ notifyObservers('builtin-asset-source-added', {
+ assetKey: key,
+ entry: json[key],
+ });
+ }
+
+ registerSource(key, json[key]);
+ }
+
+ saveSourceRegistry();
};
let getSourceRegistry = function (callback) {
- if (sourceRegistryReady === true) {
- callback(sourceRegistry);
- return;
- }
-
- if (sourceRegistryCallbacks !== undefined) {
- // If it's not undefined it's always an array
- sourceRegistryCallbacks.push(callback);
- return;
- }
-
- sourceRegistryCallbacks = [callback];
-
- let onReady = function () {
- sourceRegistryReady = true;
-
- let f;
- while ((f = sourceRegistryCallbacks.shift())) {
- f(sourceRegistry);
- }
- };
-
- let createRegistry = function () {
- api.fetchText
- (ηMatrix.assetsBootstrapLocation || 'assets/assets.json',
- function (details) {
- updateSourceRegistry(details.content, true);
- onReady();
- });
- };
-
- let onRead = function (bin) {
- if (!bin || !bin.assetSourceRegistry
- || Object.keys(bin.assetSourceRegistry).length == 0) {
- createRegistry();
- return;
- }
-
- sourceRegistry = bin.assetSourceRegistry;
- onReady();
- };
-
- vAPI.cacheStorage.get('assetSourceRegistry', onRead);
+ if (sourceRegistryReady === true) {
+ callback(sourceRegistry);
+ return;
+ }
+
+ if (sourceRegistryCallbacks !== undefined) {
+ // If it's not undefined it's always an array
+ sourceRegistryCallbacks.push(callback);
+ return;
+ }
+
+ sourceRegistryCallbacks = [callback];
+
+ let onReady = function () {
+ sourceRegistryReady = true;
+
+ let f;
+ while ((f = sourceRegistryCallbacks.shift())) {
+ f(sourceRegistry);
+ }
+ };
+
+ let createRegistry = function () {
+ api.fetchText
+ (ηMatrix.assetsBootstrapLocation || 'assets/assets.json',
+ function (details) {
+ updateSourceRegistry(details.content, true);
+ onReady();
+ });
+ };
+
+ let onRead = function (bin) {
+ if (!bin || !bin.assetSourceRegistry
+ || Object.keys(bin.assetSourceRegistry).length == 0) {
+ createRegistry();
+ return;
+ }
+
+ sourceRegistry = bin.assetSourceRegistry;
+ onReady();
+ };
+
+ vAPI.cacheStorage.get('assetSourceRegistry', onRead);
};
// Remote
let getRemote = function (key, callback) {
- let assetDetails = {};
- let contentUrl;
-
- let report = function (content, error) {
- let details = {
- assetKey: key,
- content: content,
- };
- if (error) {
- details.error = assetDetails.lastError = error;
- }
- callback(details);
- };
-
- let tryLoad = function (tries) {
- let urls = [];
-
- let tr = (tries === undefined) ? 10 : tries;
-
- if (tr <= 0) {
- console.warn('ηMatrix could not load the asset '
- +assetDetails.title);
- return;
- }
-
- if (typeof assetDetails.contentURL === 'string') {
- urls = [assetDetails.contentURL];
- } else if (Array.isArray(assetDetails.contentURL)) {
- urls = assetDetails.contentURL.slice(0);
- }
-
- while ((contentUrl = urls.shift())) {
- if (externalPathRegex.test(contentUrl)) {
- break;
- }
- }
-
- if (!contentUrl) {
- report('', 'E_NOTFOUND');
- return;
- }
-
- api.fetchText(contentUrl, onRemoteContentLoad, onRemoteContentError,
- tr-1);
- };
-
- let onRemoteContentLoad = function (details, tries) {
- if (isEmptyString(details.content) === true) {
- registerSource(key, {
- error: {
- time: Date.now(),
- error: 'No content'
- }
- });
- tryLoad(tries);
- }
-
- writeCache(key, {
- content: details.content,
- url: contentUrl,
- });
-
- registerSource(key, {error: undefined});
- report(details.content);
- };
-
- let onRemoteContentError = function (details, tries) {
- let text = detail.statusText;
- if (details.statusCode === 0) {
- text = 'network error';
- }
- registerSource(key, {
- error: {
- time: Date.now(),
- error: text,
- }
- });
- tryLoad(tries);
- };
-
- let onReady = function (registry) {
- assetDetails = registry[key] || {};
- tryLoad();
- };
-
- getSourceRegistry(onReady);
+ let assetDetails = {};
+ let contentUrl;
+
+ let report = function (content, error) {
+ let details = {
+ assetKey: key,
+ content: content,
+ };
+ if (error) {
+ details.error = assetDetails.lastError = error;
+ }
+ callback(details);
+ };
+
+ let tryLoad = function (tries) {
+ let urls = [];
+
+ let tr = (tries === undefined) ? 10 : tries;
+
+ if (tr <= 0) {
+ console.warn('ηMatrix could not load the asset '
+ +assetDetails.title);
+ return;
+ }
+
+ if (typeof assetDetails.contentURL === 'string') {
+ urls = [assetDetails.contentURL];
+ } else if (Array.isArray(assetDetails.contentURL)) {
+ urls = assetDetails.contentURL.slice(0);
+ }
+
+ while ((contentUrl = urls.shift())) {
+ if (externalPathRegex.test(contentUrl)) {
+ break;
+ }
+ }
+
+ if (!contentUrl) {
+ report('', 'E_NOTFOUND');
+ return;
+ }
+
+ api.fetchText(contentUrl, onRemoteContentLoad, onRemoteContentError,
+ tr-1);
+ };
+
+ let onRemoteContentLoad = function (details, tries) {
+ if (isEmptyString(details.content) === true) {
+ registerSource(key, {
+ error: {
+ time: Date.now(),
+ error: 'No content'
+ }
+ });
+ tryLoad(tries);
+ }
+
+ writeCache(key, {
+ content: details.content,
+ url: contentUrl,
+ });
+
+ registerSource(key, {error: undefined});
+ report(details.content);
+ };
+
+ let onRemoteContentError = function (details, tries) {
+ let text = detail.statusText;
+ if (details.statusCode === 0) {
+ text = 'network error';
+ }
+ registerSource(key, {
+ error: {
+ time: Date.now(),
+ error: text,
+ }
+ });
+ tryLoad(tries);
+ };
+
+ let onReady = function (registry) {
+ assetDetails = registry[key] || {};
+ tryLoad();
+ };
+
+ getSourceRegistry(onReady);
};
// Updater
@@ -618,385 +618,385 @@
let updateFetch = new Set();
let updateStart = function () {
- updateStatus = 'running';
- updateFetch.clear();
- updated = [];
- notifyObservers('before-assets-updated');
- updateNext();
+ updateStatus = 'running';
+ updateFetch.clear();
+ updated = [];
+ notifyObservers('before-assets-updated');
+ updateNext();
};
let updateNext = function () {
- let gcOne = function (key) {
- let entry = cacheRegistry[key];
- if (entry && entry.readTime < cacheRegistryStart) {
- cacheRemove(key);
- }
- };
-
- let findOne = function () {
- let now = Date.now();
- let sourceEntry;
- let cacheEntry;
-
- for (let key in sourceRegistry) {
- sourceEntry = sourceRegistry[key];
- if (sourceEntry.hasRemoteURL !== true) {
- continue;
- }
- if (updateFetch.has(key)) {
- continue;
- }
-
- cacheEntry = cacheRegistry[key];
- if (cacheEntry
- && (cacheEntry.writeTime
- + sourceEntry.updateAfter*86400000) > now) {
- continue;
- }
- if (notifyObservers('before-asset-updated', {assetKey: key})) {
- return key;
- }
-
- gcOne(key);
- }
-
- return undefined;
- };
-
- let onUpdate = function (details) {
- if (details.content !== '') {
- updated.push(details.assetKey);
- if (details.assetKey === 'asset.json') {
- updateSourceRegistry(details.content);
- }
- } else {
- notifyObservers('asset-update-failed', {
- assetKey: details.assetKey,
- });
- }
-
- if (findOne() !== undefined) {
- vAPI.setTimeout(updateNext, updateDelay);
- } else {
- updateEnd();
- }
- };
-
- let updateOne = function () {
- let key = findOne();
- if (key === undefined) {
- updateEnd();
- return;
- }
-
- updateFetch.add(key);
- getRemote(key, onUpdate);
- };
-
- let onSourceReady = function (registry) {
- updateOne();
- };
-
- let onCacheReady = function (registry) {
- getSourceRegistry(onSourceReady);
- };
-
- getCacheRegistry(onCacheReady);
+ let gcOne = function (key) {
+ let entry = cacheRegistry[key];
+ if (entry && entry.readTime < cacheRegistryStart) {
+ cacheRemove(key);
+ }
+ };
+
+ let findOne = function () {
+ let now = Date.now();
+ let sourceEntry;
+ let cacheEntry;
+
+ for (let key in sourceRegistry) {
+ sourceEntry = sourceRegistry[key];
+ if (sourceEntry.hasRemoteURL !== true) {
+ continue;
+ }
+ if (updateFetch.has(key)) {
+ continue;
+ }
+
+ cacheEntry = cacheRegistry[key];
+ if (cacheEntry
+ && (cacheEntry.writeTime
+ + sourceEntry.updateAfter*86400000) > now) {
+ continue;
+ }
+ if (notifyObservers('before-asset-updated', {assetKey: key})) {
+ return key;
+ }
+
+ gcOne(key);
+ }
+
+ return undefined;
+ };
+
+ let onUpdate = function (details) {
+ if (details.content !== '') {
+ updated.push(details.assetKey);
+ if (details.assetKey === 'asset.json') {
+ updateSourceRegistry(details.content);
+ }
+ } else {
+ notifyObservers('asset-update-failed', {
+ assetKey: details.assetKey,
+ });
+ }
+
+ if (findOne() !== undefined) {
+ vAPI.setTimeout(updateNext, updateDelay);
+ } else {
+ updateEnd();
+ }
+ };
+
+ let updateOne = function () {
+ let key = findOne();
+ if (key === undefined) {
+ updateEnd();
+ return;
+ }
+
+ updateFetch.add(key);
+ getRemote(key, onUpdate);
+ };
+
+ let onSourceReady = function (registry) {
+ updateOne();
+ };
+
+ let onCacheReady = function (registry) {
+ getSourceRegistry(onSourceReady);
+ };
+
+ getCacheRegistry(onCacheReady);
};
let updateEnd = function () {
- let keys = updated.slice(0);
- updateFetch.clear();
- updateStatus = 'stop';
- updateDelay = updateDefaultDelay;
- notifyObservers('after-asset-updated', {
- assetKeys: keys,
- });
+ let keys = updated.slice(0);
+ updateFetch.clear();
+ updateStatus = 'stop';
+ updateDelay = updateDefaultDelay;
+ notifyObservers('after-asset-updated', {
+ assetKeys: keys,
+ });
};
// Assets API
api.addObserver = function (observer) {
- if (observers.indexOf(observer) === -1) {
- observers.push(observer);
- }
+ if (observers.indexOf(observer) === -1) {
+ observers.push(observer);
+ }
};
api.removeObserver = function (observer) {
- let pos = observers.indexOf(observer);
- if (pos !== -1) {
- observers.splice(pos, 1);
- }
+ let pos = observers.indexOf(observer);
+ if (pos !== -1) {
+ observers.splice(pos, 1);
+ }
};
api.fetchText = function (url, onLoad, onError, tries) {
- let iurl = externalPathRegex.test(url) ? url : vAPI.getURL(url);
- let tr = (tries === undefined) ? 10 : tries;
-
- if (typeof onError !== 'function') {
- onError = onLoad;
- }
-
- let onResponseReceived = function () {
- this.onload = this.onerror = this.ontimeout = null;
-
- let details = {
- url: url,
- content: '',
- // On local files this.status is 0, but the request
- // is successful
- statusCode: this.status || 200,
- statusText: this.statusText || '',
- };
-
- if (details.statusCode < 200 || details.statusCode >= 300) {
- onError.call(null, details, tr);
- return;
- }
-
- if (isEmptyString(this.responseText) === true) {
- onError.call(null, details, tr);
- return;
- }
-
- let t = this.responseText.trim();
-
- // Discard HTML as it's probably an error
- // (the request expects plain text as a response)
- if (t.startsWith('<') && t.endsWith('>')) {
- onError.call(null, details, tr);
- return;
- }
-
- details.content = t;
- onLoad.call(null, details, tr);
- };
-
- let onErrorReceived = function () {
- this.onload = this.onerror = this.ontimeout = null;
-
- ηMatrix.logger.writeOne('', 'error',
- connectionError.replace('{{url}}', iurl));
-
- onError.call(null, {
- url: url,
- content: '',
- }, tr);
- };
-
- let req = new XMLHttpRequest();
- req.open('GET', iurl, true);
- req.timeout = 30000;
- req.onload = onResponseReceived;
- req.onerror = onErrorReceived;
- req.ontimeout = onErrorReceived;
- req.responseType = 'text';
-
- try {
- // This can throw in some cases
- req.send();
- } catch (e) {
- onErrorReceived.call(req);
- }
+ let iurl = externalPathRegex.test(url) ? url : vAPI.getURL(url);
+ let tr = (tries === undefined) ? 10 : tries;
+
+ if (typeof onError !== 'function') {
+ onError = onLoad;
+ }
+
+ let onResponseReceived = function () {
+ this.onload = this.onerror = this.ontimeout = null;
+
+ let details = {
+ url: url,
+ content: '',
+ // On local files this.status is 0, but the request
+ // is successful
+ statusCode: this.status || 200,
+ statusText: this.statusText || '',
+ };
+
+ if (details.statusCode < 200 || details.statusCode >= 300) {
+ onError.call(null, details, tr);
+ return;
+ }
+
+ if (isEmptyString(this.responseText) === true) {
+ onError.call(null, details, tr);
+ return;
+ }
+
+ let t = this.responseText.trim();
+
+ // Discard HTML as it's probably an error
+ // (the request expects plain text as a response)
+ if (t.startsWith('<') && t.endsWith('>')) {
+ onError.call(null, details, tr);
+ return;
+ }
+
+ details.content = t;
+ onLoad.call(null, details, tr);
+ };
+
+ let onErrorReceived = function () {
+ this.onload = this.onerror = this.ontimeout = null;
+
+ ηMatrix.logger.writeOne('', 'error',
+ connectionError.replace('{{url}}', iurl));
+
+ onError.call(null, {
+ url: url,
+ content: '',
+ }, tr);
+ };
+
+ let req = new XMLHttpRequest();
+ req.open('GET', iurl, true);
+ req.timeout = 30000;
+ req.onload = onResponseReceived;
+ req.onerror = onErrorReceived;
+ req.ontimeout = onErrorReceived;
+ req.responseType = 'text';
+
+ try {
+ // This can throw in some cases
+ req.send();
+ } catch (e) {
+ onErrorReceived.call(req);
+ }
};
api.registerAssetSource = function (key, details) {
- getSourceRegistry(function () {
- registerSource(key, details);
- saveSourceRegistry(true);
- });
+ getSourceRegistry(function () {
+ registerSource(key, details);
+ saveSourceRegistry(true);
+ });
};
api.unregisterAssetSource = function (key) {
- getSourceRegistry(function () {
- unregisterSource(key);
- saveSourceRegistry(true);
- });
+ getSourceRegistry(function () {
+ unregisterSource(key);
+ saveSourceRegistry(true);
+ });
};
api.get = function (key, options, callback) {
- let cb;
- let opt;
-
- if (typeof options === 'function') {
- cb = options;
- opt = {};
- } else if (typeof callback !== 'function') {
- cb = noOp;
- opt = options;
- } else {
- cb = callback;
- opt = options;
- }
-
- let assetDetails = {};
- let contentUrl = undefined;
-
- let report = function (content, error) {
- let details = {
- assetKey: key,
- content: content,
- };
-
- if (error) {
- details.error = assetDetails.error = error;
- } else {
- assetDetails.error = undefined;
- }
-
- cb(details);
- };
-
- let onContentNotLoaded = function (details, tries) {
- let external;
- let urls = [];
-
- let tr = (tries === undefined) ? 10 : tries;
-
- if (tr <= 0) {
- console.warn('ηMatrix couldn\'t download the asset '
- +assetDetails.title);
- return;
- }
-
- if (typeof assetDetails.contentURL === 'string') {
- urls = [assetDetails.contentURL];
- } else if (Array.isArray(assetDetails.contentURL)) {
- urls = assetDetails.contentURL.slice(0);
- }
-
- while ((contentUrl = urls.shift())) {
- external = externalPathRegex.test(contentUrl);
- if (external === true && assetDetails.loaded !== true) {
- break;
- }
- if (external === false || assetDetails.hasLocalURL !== true) {
- break;
- }
- }
-
- if (!contentUrl) {
- report('', 'E_NOTFOUND');
- return;
- }
-
- api.fetchText(contentUrl, onContentLoaded, onContentNotLoaded,
- tr-1);
- };
-
- let onContentLoaded = function (details, tries) {
- if (isEmptyString(details.content) === true) {
- onContentNotLoaded(undefined, tries);
- return;
- }
-
- if (externalPathRegex.test(details.url)
- && opt.dontCache !== true) {
- writeCache(key, {
- content: details.content,
- url: contentUrl,
- });
- }
-
- assetDetails.loaded = true;
-
- report(details.content);
- };
-
- let onCachedContentLoad = function (details) {
- if (details.content !== '') {
- report(details.content);
- return;
- }
-
- let onReady = function (registry) {
- assetDetails = registry[key] || {};
- onContentNotLoaded();
- }
-
- getSourceRegistry(onReady);
- };
-
- readCache(key, onCachedContentLoad);
+ let cb;
+ let opt;
+
+ if (typeof options === 'function') {
+ cb = options;
+ opt = {};
+ } else if (typeof callback !== 'function') {
+ cb = noOp;
+ opt = options;
+ } else {
+ cb = callback;
+ opt = options;
+ }
+
+ let assetDetails = {};
+ let contentUrl = undefined;
+
+ let report = function (content, error) {
+ let details = {
+ assetKey: key,
+ content: content,
+ };
+
+ if (error) {
+ details.error = assetDetails.error = error;
+ } else {
+ assetDetails.error = undefined;
+ }
+
+ cb(details);
+ };
+
+ let onContentNotLoaded = function (details, tries) {
+ let external;
+ let urls = [];
+
+ let tr = (tries === undefined) ? 10 : tries;
+
+ if (tr <= 0) {
+ console.warn('ηMatrix couldn\'t download the asset '
+ +assetDetails.title);
+ return;
+ }
+
+ if (typeof assetDetails.contentURL === 'string') {
+ urls = [assetDetails.contentURL];
+ } else if (Array.isArray(assetDetails.contentURL)) {
+ urls = assetDetails.contentURL.slice(0);
+ }
+
+ while ((contentUrl = urls.shift())) {
+ external = externalPathRegex.test(contentUrl);
+ if (external === true && assetDetails.loaded !== true) {
+ break;
+ }
+ if (external === false || assetDetails.hasLocalURL !== true) {
+ break;
+ }
+ }
+
+ if (!contentUrl) {
+ report('', 'E_NOTFOUND');
+ return;
+ }
+
+ api.fetchText(contentUrl, onContentLoaded, onContentNotLoaded,
+ tr-1);
+ };
+
+ let onContentLoaded = function (details, tries) {
+ if (isEmptyString(details.content) === true) {
+ onContentNotLoaded(undefined, tries);
+ return;
+ }
+
+ if (externalPathRegex.test(details.url)
+ && opt.dontCache !== true) {
+ writeCache(key, {
+ content: details.content,
+ url: contentUrl,
+ });
+ }
+
+ assetDetails.loaded = true;
+
+ report(details.content);
+ };
+
+ let onCachedContentLoad = function (details) {
+ if (details.content !== '') {
+ report(details.content);
+ return;
+ }
+
+ let onReady = function (registry) {
+ assetDetails = registry[key] || {};
+ onContentNotLoaded();
+ }
+
+ getSourceRegistry(onReady);
+ };
+
+ readCache(key, onCachedContentLoad);
};
api.put = function (key, content, callback) {
- writeCache(key, content, callback);
+ writeCache(key, content, callback);
};
api.metadata = function (callback) {
- let onSourceReady = function (registry) {
- let source = JSON.parse(JSON.stringify(registry));
- let cache = cacheRegistry;
- let sourceEntry;
- let cacheEntry;
- let now = Date.now();
- let obsoleteAfter;
-
- for (let key in source) {
- sourceEntry = source[key];
- cacheEntry = cache[key];
-
- if (cacheEntry) {
- sourceEntry.cached = true;
- sourceEntry.writeTime = cacheEntry.writeTime;
- obsoleteAfter = cacheEntry.writeTime
- + sourceEntry.updateAfter * 86400000;
- sourceEntry.obsolete = obsoleteAfter < now;
- sourceEntry.remoteURL = cacheEntry.remoteURL;
- } else {
- sourceEntry.writeTime = 0;
- obsoleteAfter = 0;
- sourceEntry.obsolete = true;
- }
- }
-
- callback(source);
- }
-
- let onCacheReady = function () {
- getSourceRegistry(onSourceReady);
- }
-
- getCacheRegistry(onCacheReady);
+ let onSourceReady = function (registry) {
+ let source = JSON.parse(JSON.stringify(registry));
+ let cache = cacheRegistry;
+ let sourceEntry;
+ let cacheEntry;
+ let now = Date.now();
+ let obsoleteAfter;
+
+ for (let key in source) {
+ sourceEntry = source[key];
+ cacheEntry = cache[key];
+
+ if (cacheEntry) {
+ sourceEntry.cached = true;
+ sourceEntry.writeTime = cacheEntry.writeTime;
+ obsoleteAfter = cacheEntry.writeTime
+ + sourceEntry.updateAfter * 86400000;
+ sourceEntry.obsolete = obsoleteAfter < now;
+ sourceEntry.remoteURL = cacheEntry.remoteURL;
+ } else {
+ sourceEntry.writeTime = 0;
+ obsoleteAfter = 0;
+ sourceEntry.obsolete = true;
+ }
+ }
+
+ callback(source);
+ }
+
+ let onCacheReady = function () {
+ getSourceRegistry(onSourceReady);
+ }
+
+ getCacheRegistry(onCacheReady);
};
api.purge = function (pattern, exclude, callback) {
- markDirtyCache(pattern, exclude, callback);
+ markDirtyCache(pattern, exclude, callback);
};
api.remove = function (pattern, callback) {
- cacheRemove(pattern, callback);
+ cacheRemove(pattern, callback);
};
api.rmrf = function () {
- cacheRemove(/./);
+ cacheRemove(/./);
};
api.updateStart = function (details) {
- let oldDelay = updateDelay;
- let newDelay = details.delay || updateDefaultDelay;
+ let oldDelay = updateDelay;
+ let newDelay = details.delay || updateDefaultDelay;
- updateDelay = Math.min(oldDelay, newDelay);
+ updateDelay = Math.min(oldDelay, newDelay);
- if (updateStatus === 'running') {
- if (newDelay < oldDelay) {
- clearTimeout(updateTimer);
- updateTimer = vAPI.setTimeout(updateNext, updateDelay);
- }
- return;
- }
+ if (updateStatus === 'running') {
+ if (newDelay < oldDelay) {
+ clearTimeout(updateTimer);
+ updateTimer = vAPI.setTimeout(updateNext, updateDelay);
+ }
+ return;
+ }
- updateStart();
+ updateStart();
};
api.updateStop = function () {
- if (updateTimer) {
- clearTimeout(updateTimer);
- updateTimer = undefined;
- }
- if (updateStatus === 'running') {
- updateEnd();
- }
+ if (updateTimer) {
+ clearTimeout(updateTimer);
+ updateTimer = undefined;
+ }
+ if (updateStatus === 'running') {
+ updateEnd();
+ }
};
return api;
diff --git a/js/background.js b/js/background.js
index aad19e7..07065b7 100644
--- a/js/background.js
+++ b/js/background.js
@@ -27,61 +27,61 @@
var ηMatrix = (function() { // jshint ignore:line
-/******************************************************************************/
-
-var oneSecond = 1000;
-var oneMinute = 60 * oneSecond;
-var oneHour = 60 * oneMinute;
-var oneDay = 24 * oneHour;
-
-/******************************************************************************/
-/******************************************************************************/
-
-var _RequestStats = function() {
- this.reset();
-};
-
-_RequestStats.prototype.reset = function() {
- this.all =
- this.doc =
- this.frame =
- this.script =
- this.css =
- this.image =
- this.media =
- this.xhr =
- this.other =
- this.cookie = 0;
-};
-
-/******************************************************************************/
-
-var RequestStats = function() {
- this.allowed = new _RequestStats();
- this.blocked = new _RequestStats();
-};
-
-RequestStats.prototype.reset = function() {
- this.blocked.reset();
- this.allowed.reset();
-};
-
-RequestStats.prototype.record = function(type, blocked) {
- // Remember: always test against **false**
- if ( blocked !== false ) {
- this.blocked[type] += 1;
- this.blocked.all += 1;
- } else {
- this.allowed[type] += 1;
- this.allowed.all += 1;
- }
-};
-
-var requestStatsFactory = function() {
- return new RequestStats();
-};
-
-/*******************************************************************************
+ /******************************************************************************/
+
+ var oneSecond = 1000;
+ var oneMinute = 60 * oneSecond;
+ var oneHour = 60 * oneMinute;
+ var oneDay = 24 * oneHour;
+
+ /******************************************************************************/
+ /******************************************************************************/
+
+ var _RequestStats = function() {
+ this.reset();
+ };
+
+ _RequestStats.prototype.reset = function() {
+ this.all =
+ this.doc =
+ this.frame =
+ this.script =
+ this.css =
+ this.image =
+ this.media =
+ this.xhr =
+ this.other =
+ this.cookie = 0;
+ };
+
+ /******************************************************************************/
+
+ var RequestStats = function() {
+ this.allowed = new _RequestStats();
+ this.blocked = new _RequestStats();
+ };
+
+ RequestStats.prototype.reset = function() {
+ this.blocked.reset();
+ this.allowed.reset();
+ };
+
+ RequestStats.prototype.record = function(type, blocked) {
+ // Remember: always test against **false**
+ if ( blocked !== false ) {
+ this.blocked[type] += 1;
+ this.blocked.all += 1;
+ } else {
+ this.allowed[type] += 1;
+ this.allowed.all += 1;
+ }
+ };
+
+ var requestStatsFactory = function() {
+ return new RequestStats();
+ };
+
+ /*******************************************************************************
SVG-based icons below were extracted from
fontawesome-webfont.svg v4.7. Excerpt of copyright notice at
@@ -100,56 +100,56 @@ var requestStatsFactory = function() {
Font icons:
- glyph-name: "external_link"
-*/
+ */
-var rawSettingsDefault = {
- disableCSPReportInjection: false,
- placeholderBackground:
+ var rawSettingsDefault = {
+ disableCSPReportInjection: false,
+ placeholderBackground:
[
'url("data:image/png;base64,',
- 'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAK',
- 'CAAAAACoWZBhAAAABGdBTUEAALGPC/xh',
- 'BQAAAAJiS0dEAP+Hj8y/AAAAB3RJTUUH',
- '3wwIAAgyL/YaPAAAACJJREFUCFtjfMbO',
- 'AAQ/gZiFnQPEBAEmGIMIJgtIL8QEgtoA',
- 'In4D/96X1KAAAAAldEVYdGRhdGU6Y3Jl',
- 'YXRlADIwMTUtMTItMDhUMDA6MDg6NTAr',
- 'MDM6MDAasuuJAAAAJXRFWHRkYXRlOm1v',
- 'ZGlmeQAyMDE1LTEyLTA4VDAwOjA4OjUw',
- 'KzAzOjAwa+9TNQAAAABJRU5ErkJggg==',
+ 'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAK',
+ 'CAAAAACoWZBhAAAABGdBTUEAALGPC/xh',
+ 'BQAAAAJiS0dEAP+Hj8y/AAAAB3RJTUUH',
+ '3wwIAAgyL/YaPAAAACJJREFUCFtjfMbO',
+ 'AAQ/gZiFnQPEBAEmGIMIJgtIL8QEgtoA',
+ 'In4D/96X1KAAAAAldEVYdGRhdGU6Y3Jl',
+ 'YXRlADIwMTUtMTItMDhUMDA6MDg6NTAr',
+ 'MDM6MDAasuuJAAAAJXRFWHRkYXRlOm1v',
+ 'ZGlmeQAyMDE1LTEyLTA4VDAwOjA4OjUw',
+ 'KzAzOjAwa+9TNQAAAABJRU5ErkJggg==',
'") ',
'repeat scroll #fff'
].join(''),
- placeholderBorder: '1px solid rgba(0, 0, 0, 0.1)',
- imagePlaceholder: true,
- imagePlaceholderBackground: 'default',
- imagePlaceholderBorder: 'default',
- framePlaceholder: true,
- framePlaceholderDocument:
+ placeholderBorder: '1px solid rgba(0, 0, 0, 0.1)',
+ imagePlaceholder: true,
+ imagePlaceholderBackground: 'default',
+ imagePlaceholderBorder: 'default',
+ framePlaceholder: true,
+ framePlaceholderDocument:
[
'<html><head>',
'<meta charset="utf-8">',
'<style>',
'body { ',
- 'background: {{bg}};',
- 'color: gray;',
- 'font: 12px sans-serif;',
- 'margin: 0;',
- 'overflow: hidden;',
- 'padding: 2px;',
- 'white-space: nowrap;',
+ 'background: {{bg}};',
+ 'color: gray;',
+ 'font: 12px sans-serif;',
+ 'margin: 0;',
+ 'overflow: hidden;',
+ 'padding: 2px;',
+ 'white-space: nowrap;',
'}',
'a { ',
- 'color: inherit;',
- 'padding: 0 3px;',
- 'text-decoration: none;',
+ 'color: inherit;',
+ 'padding: 0 3px;',
+ 'text-decoration: none;',
'}',
'svg {',
- 'display: inline-block;',
- 'fill: gray;',
- 'height: 12px;',
- 'vertical-align: bottom;',
- 'width: 12px;',
+ 'display: inline-block;',
+ 'fill: gray;',
+ 'height: 12px;',
+ 'vertical-align: bottom;',
+ 'width: 12px;',
'}',
'</style></head><body>',
'<span><a href="{{url}}" title="{{url}}" target="_blank">',
@@ -157,91 +157,90 @@ var rawSettingsDefault = {
'</a>{{url}}</span>',
'</body></html>'
].join(''),
- framePlaceholderBackground: 'default',
-};
-
-/******************************************************************************/
-
-return {
- onBeforeStartQueue: [],
-
- userSettings: {
- alwaysDetachLogger: false,
- autoUpdate: false,
- clearBrowserCache: true,
- clearBrowserCacheAfter: 60,
- cloudStorageEnabled: false,
- collapseBlacklisted: true,
- collapseBlocked: false,
- colorBlindFriendly: false,
- deleteCookies: false,
- deleteUnusedSessionCookies: false,
- deleteUnusedSessionCookiesAfter: 60,
- deleteLocalStorage: false,
- displayTextSize: '14px',
- externalHostsFiles: '',
- iconBadgeEnabled: false,
- maxLoggedRequests: 1000,
- popupCollapseAllDomains: false,
- popupCollapseBlacklistedDomains: false,
- popupScopeLevel: 'domain',
- processHyperlinkAuditing: true,
- processReferer: false
- },
-
- rawSettingsDefault: rawSettingsDefault,
- rawSettings: Object.assign({}, rawSettingsDefault),
- rawSettingsWriteTime: 0,
-
- clearBrowserCacheCycle: 0,
- cspNoInlineScript: "script-src 'unsafe-eval' blob: *",
- cspNoInlineStyle: "style-src blob: *",
- cspNoWorker: undefined,
- updateAssetsEvery: 11 * oneDay + 1 * oneHour + 1 * oneMinute + 1 * oneSecond,
- firstUpdateAfter: 11 * oneMinute,
- nextUpdateAfter: 11 * oneHour,
- assetsBootstrapLocation: 'assets/assets.json',
- pslAssetKey: 'public_suffix_list.dat',
-
- // list of live hosts files
- liveHostsFiles: {
- },
-
- // urls stats are kept on the back burner while waiting to be reactivated
- // in a tab or another.
- pageStores: {},
- pageStoresToken: 0,
- pageStoreCemetery: {},
-
- // page url => permission scope
- tMatrix: null,
- pMatrix: null,
-
- ubiquitousBlacklist: null,
-
- // various stats
- requestStatsFactory: requestStatsFactory,
- requestStats: requestStatsFactory(),
- cookieRemovedCounter: 0,
- localStorageRemovedCounter: 0,
- cookieHeaderFoiledCounter: 0,
- refererHeaderFoiledCounter: 0,
- hyperlinkAuditingFoiledCounter: 0,
- browserCacheClearedCounter: 0,
- storageUsed: 0,
-
- // record what the browser is doing behind the scene
- behindTheSceneScope: 'behind-the-scene',
-
- noopFunc: function(){},
-
- // so that I don't have to care for last comma
- dummy: 0
-};
-
-/******************************************************************************/
+ framePlaceholderBackground: 'default',
+ };
+
+ /******************************************************************************/
+
+ return {
+ onBeforeStartQueue: [],
+
+ userSettings: {
+ alwaysDetachLogger: false,
+ autoUpdate: false,
+ clearBrowserCache: true,
+ clearBrowserCacheAfter: 60,
+ cloudStorageEnabled: false,
+ collapseBlacklisted: true,
+ collapseBlocked: false,
+ colorBlindFriendly: false,
+ deleteCookies: false,
+ deleteUnusedSessionCookies: false,
+ deleteUnusedSessionCookiesAfter: 60,
+ deleteLocalStorage: false,
+ displayTextSize: '14px',
+ externalHostsFiles: '',
+ iconBadgeEnabled: false,
+ maxLoggedRequests: 1000,
+ popupCollapseAllDomains: false,
+ popupCollapseBlacklistedDomains: false,
+ popupScopeLevel: 'domain',
+ processHyperlinkAuditing: true,
+ processReferer: false
+ },
+
+ rawSettingsDefault: rawSettingsDefault,
+ rawSettings: Object.assign({}, rawSettingsDefault),
+ rawSettingsWriteTime: 0,
+
+ clearBrowserCacheCycle: 0,
+ cspNoInlineScript: "script-src 'unsafe-eval' blob: *",
+ cspNoInlineStyle: "style-src blob: *",
+ cspNoWorker: undefined,
+ updateAssetsEvery: 11 * oneDay + 1 * oneHour + 1 * oneMinute + 1 * oneSecond,
+ firstUpdateAfter: 11 * oneMinute,
+ nextUpdateAfter: 11 * oneHour,
+ assetsBootstrapLocation: 'assets/assets.json',
+ pslAssetKey: 'public_suffix_list.dat',
+
+ // list of live hosts files
+ liveHostsFiles: {
+ },
+
+ // urls stats are kept on the back burner while waiting to be reactivated
+ // in a tab or another.
+ pageStores: {},
+ pageStoresToken: 0,
+ pageStoreCemetery: {},
+
+ // page url => permission scope
+ tMatrix: null,
+ pMatrix: null,
+
+ ubiquitousBlacklist: null,
+
+ // various stats
+ requestStatsFactory: requestStatsFactory,
+ requestStats: requestStatsFactory(),
+ cookieRemovedCounter: 0,
+ localStorageRemovedCounter: 0,
+ cookieHeaderFoiledCounter: 0,
+ refererHeaderFoiledCounter: 0,
+ hyperlinkAuditingFoiledCounter: 0,
+ browserCacheClearedCounter: 0,
+ storageUsed: 0,
+
+ // record what the browser is doing behind the scene
+ behindTheSceneScope: 'behind-the-scene',
+
+ noopFunc: function(){},
+
+ // so that I don't have to care for last comma
+ dummy: 0
+ };
+
+ /******************************************************************************/
})();
/******************************************************************************/
-
diff --git a/js/browsercache.js b/js/browsercache.js
index d8ae19f..1360f92 100644
--- a/js/browsercache.js
+++ b/js/browsercache.js
@@ -29,37 +29,37 @@
(function() {
-/******************************************************************************/
+ /******************************************************************************/
-// Browser data jobs
+ // Browser data jobs
-var clearCache = function() {
- vAPI.setTimeout(clearCache, 15 * 60 * 1000);
+ var clearCache = function() {
+ vAPI.setTimeout(clearCache, 15 * 60 * 1000);
- var ηm = ηMatrix;
- if ( !ηm.userSettings.clearBrowserCache ) {
- return;
- }
+ var ηm = ηMatrix;
+ if ( !ηm.userSettings.clearBrowserCache ) {
+ return;
+ }
- ηm.clearBrowserCacheCycle -= 15;
- if ( ηm.clearBrowserCacheCycle > 0 ) {
- return;
- }
+ ηm.clearBrowserCacheCycle -= 15;
+ if ( ηm.clearBrowserCacheCycle > 0 ) {
+ return;
+ }
- vAPI.browser.data.clearCache();
+ vAPI.browser.data.clearCache();
- ηm.clearBrowserCacheCycle = ηm.userSettings.clearBrowserCacheAfter;
- ηm.browserCacheClearedCounter++;
+ ηm.clearBrowserCacheCycle = ηm.userSettings.clearBrowserCacheAfter;
+ ηm.browserCacheClearedCounter++;
- // TODO: i18n
- ηm.logger.writeOne('', 'info', vAPI.i18n('loggerEntryBrowserCacheCleared'));
+ // TODO: i18n
+ ηm.logger.writeOne('', 'info', vAPI.i18n('loggerEntryBrowserCacheCleared'));
- //console.debug('clearBrowserCacheCallback()> vAPI.browser.data.clearCache() called');
-};
+ //console.debug('clearBrowserCacheCallback()> vAPI.browser.data.clearCache() called');
+ };
-vAPI.setTimeout(clearCache, 15 * 60 * 1000);
+ vAPI.setTimeout(clearCache, 15 * 60 * 1000);
-/******************************************************************************/
+ /******************************************************************************/
})();
diff --git a/js/cloud-ui.js b/js/cloud-ui.js
index 1afd9d1..0f81833 100644
--- a/js/cloud-ui.js
+++ b/js/cloud-ui.js
@@ -29,187 +29,187 @@
(function() {
-/******************************************************************************/
-
-self.cloud = {
- options: {},
- datakey: '',
- data: undefined,
- onPush: null,
- onPull: null
-};
-
-/******************************************************************************/
-
-var widget = uDom.nodeFromId('cloudWidget');
-if ( widget === null ) {
- return;
-}
+ /******************************************************************************/
+
+ self.cloud = {
+ options: {},
+ datakey: '',
+ data: undefined,
+ onPush: null,
+ onPull: null
+ };
-self.cloud.datakey = widget.getAttribute('data-cloud-entry') || '';
-if ( self.cloud.datakey === '' ) {
- return;
-}
+ /******************************************************************************/
-/******************************************************************************/
+ var widget = uDom.nodeFromId('cloudWidget');
+ if ( widget === null ) {
+ return;
+ }
-var onCloudDataReceived = function(entry) {
- if ( typeof entry !== 'object' || entry === null ) {
+ self.cloud.datakey = widget.getAttribute('data-cloud-entry') || '';
+ if ( self.cloud.datakey === '' ) {
return;
}
- self.cloud.data = entry.data;
+ /******************************************************************************/
- uDom.nodeFromId('cloudPull').removeAttribute('disabled');
- uDom.nodeFromId('cloudPullAndMerge').removeAttribute('disabled');
+ var onCloudDataReceived = function(entry) {
+ if ( typeof entry !== 'object' || entry === null ) {
+ return;
+ }
- var timeOptions = {
- weekday: 'short',
- year: 'numeric',
- month: 'short',
- day: 'numeric',
- hour: 'numeric',
- minute: 'numeric',
- second: 'numeric',
- timeZoneName: 'short'
+ self.cloud.data = entry.data;
+
+ uDom.nodeFromId('cloudPull').removeAttribute('disabled');
+ uDom.nodeFromId('cloudPullAndMerge').removeAttribute('disabled');
+
+ var timeOptions = {
+ weekday: 'short',
+ year: 'numeric',
+ month: 'short',
+ day: 'numeric',
+ hour: 'numeric',
+ minute: 'numeric',
+ second: 'numeric',
+ timeZoneName: 'short'
+ };
+
+ var time = new Date(entry.tstamp);
+ widget.querySelector('span').textContent =
+ entry.source + '\n' +
+ time.toLocaleString('fullwide', timeOptions);
};
- var time = new Date(entry.tstamp);
- widget.querySelector('span').textContent =
- entry.source + '\n' +
- time.toLocaleString('fullwide', timeOptions);
-};
-
-/******************************************************************************/
-
-var fetchCloudData = function() {
- vAPI.messaging.send(
- 'cloud-ui.js',
- {
- what: 'cloudPull',
- datakey: self.cloud.datakey
- },
- onCloudDataReceived
- );
-};
-
-/******************************************************************************/
-
-var pushData = function() {
- if ( typeof self.cloud.onPush !== 'function' ) {
- return;
- }
- vAPI.messaging.send(
- 'cloud-ui.js',
- {
- what: 'cloudPush',
- datakey: self.cloud.datakey,
- data: self.cloud.onPush()
- },
- fetchCloudData
- );
-};
+ /******************************************************************************/
+
+ var fetchCloudData = function() {
+ vAPI.messaging.send(
+ 'cloud-ui.js',
+ {
+ what: 'cloudPull',
+ datakey: self.cloud.datakey
+ },
+ onCloudDataReceived
+ );
+ };
-/******************************************************************************/
+ /******************************************************************************/
-var pullData = function(ev) {
- if ( typeof self.cloud.onPull === 'function' ) {
- self.cloud.onPull(self.cloud.data, ev.shiftKey);
- }
-};
+ var pushData = function() {
+ if ( typeof self.cloud.onPush !== 'function' ) {
+ return;
+ }
+ vAPI.messaging.send(
+ 'cloud-ui.js',
+ {
+ what: 'cloudPush',
+ datakey: self.cloud.datakey,
+ data: self.cloud.onPush()
+ },
+ fetchCloudData
+ );
+ };
-/******************************************************************************/
+ /******************************************************************************/
-var pullAndMergeData = function() {
- if ( typeof self.cloud.onPull === 'function' ) {
- self.cloud.onPull(self.cloud.data, true);
- }
-};
+ var pullData = function(ev) {
+ if ( typeof self.cloud.onPull === 'function' ) {
+ self.cloud.onPull(self.cloud.data, ev.shiftKey);
+ }
+ };
-/******************************************************************************/
+ /******************************************************************************/
-var openOptions = function() {
- var input = uDom.nodeFromId('cloudDeviceName');
- input.value = self.cloud.options.deviceName;
- input.setAttribute('placeholder', self.cloud.options.defaultDeviceName);
- uDom.nodeFromId('cloudOptions').classList.add('show');
-};
+ var pullAndMergeData = function() {
+ if ( typeof self.cloud.onPull === 'function' ) {
+ self.cloud.onPull(self.cloud.data, true);
+ }
+ };
-/******************************************************************************/
+ /******************************************************************************/
-var closeOptions = function(ev) {
- var root = uDom.nodeFromId('cloudOptions');
- if ( ev.target !== root ) {
- return;
- }
- root.classList.remove('show');
-};
+ var openOptions = function() {
+ var input = uDom.nodeFromId('cloudDeviceName');
+ input.value = self.cloud.options.deviceName;
+ input.setAttribute('placeholder', self.cloud.options.defaultDeviceName);
+ uDom.nodeFromId('cloudOptions').classList.add('show');
+ };
-/******************************************************************************/
+ /******************************************************************************/
-var submitOptions = function() {
- var onOptions = function(options) {
- if ( typeof options !== 'object' || options === null ) {
+ var closeOptions = function(ev) {
+ var root = uDom.nodeFromId('cloudOptions');
+ if ( ev.target !== root ) {
return;
}
- self.cloud.options = options;
+ root.classList.remove('show');
};
- vAPI.messaging.send('cloud-ui.js', {
- what: 'cloudSetOptions',
- options: {
- deviceName: uDom.nodeFromId('cloudDeviceName').value
- }
- }, onOptions);
- uDom.nodeFromId('cloudOptions').classList.remove('show');
-};
-
-/******************************************************************************/
+ /******************************************************************************/
+
+ var submitOptions = function() {
+ var onOptions = function(options) {
+ if ( typeof options !== 'object' || options === null ) {
+ return;
+ }
+ self.cloud.options = options;
+ };
+
+ vAPI.messaging.send('cloud-ui.js', {
+ what: 'cloudSetOptions',
+ options: {
+ deviceName: uDom.nodeFromId('cloudDeviceName').value
+ }
+ }, onOptions);
+ uDom.nodeFromId('cloudOptions').classList.remove('show');
+ };
-var onInitialize = function(options) {
- if ( typeof options !== 'object' || options === null ) {
- return;
- }
+ /******************************************************************************/
- if ( !options.enabled ) {
- return;
- }
- self.cloud.options = options;
-
- var xhr = new XMLHttpRequest();
- xhr.open('GET', 'cloud-ui.html', true);
- xhr.overrideMimeType('text/html;charset=utf-8');
- xhr.responseType = 'text';
- xhr.onload = function() {
- this.onload = null;
- var parser = new DOMParser(),
- parsed = parser.parseFromString(this.responseText, 'text/html'),
- fromParent = parsed.body;
- while ( fromParent.firstElementChild !== null ) {
- widget.appendChild(
- document.adoptNode(fromParent.firstElementChild)
- );
+ var onInitialize = function(options) {
+ if ( typeof options !== 'object' || options === null ) {
+ return;
}
- vAPI.i18n.render(widget);
- widget.classList.remove('hide');
-
- uDom('#cloudPush').on('click', pushData);
- uDom('#cloudPull').on('click', pullData);
- uDom('#cloudPullAndMerge').on('click', pullAndMergeData);
- uDom('#cloudCog').on('click', openOptions);
- uDom('#cloudOptions').on('click', closeOptions);
- uDom('#cloudOptionsSubmit').on('click', submitOptions);
+ if ( !options.enabled ) {
+ return;
+ }
+ self.cloud.options = options;
- fetchCloudData();
+ var xhr = new XMLHttpRequest();
+ xhr.open('GET', 'cloud-ui.html', true);
+ xhr.overrideMimeType('text/html;charset=utf-8');
+ xhr.responseType = 'text';
+ xhr.onload = function() {
+ this.onload = null;
+ var parser = new DOMParser(),
+ parsed = parser.parseFromString(this.responseText, 'text/html'),
+ fromParent = parsed.body;
+ while ( fromParent.firstElementChild !== null ) {
+ widget.appendChild(
+ document.adoptNode(fromParent.firstElementChild)
+ );
+ }
+
+ vAPI.i18n.render(widget);
+ widget.classList.remove('hide');
+
+ uDom('#cloudPush').on('click', pushData);
+ uDom('#cloudPull').on('click', pullData);
+ uDom('#cloudPullAndMerge').on('click', pullAndMergeData);
+ uDom('#cloudCog').on('click', openOptions);
+ uDom('#cloudOptions').on('click', closeOptions);
+ uDom('#cloudOptionsSubmit').on('click', submitOptions);
+
+ fetchCloudData();
+ };
+ xhr.send();
};
- xhr.send();
-};
-vAPI.messaging.send('cloud-ui.js', { what: 'cloudGetOptions' }, onInitialize);
+ vAPI.messaging.send('cloud-ui.js', { what: 'cloudGetOptions' }, onInitialize);
-/******************************************************************************/
+ /******************************************************************************/
-// https://www.youtube.com/watch?v=aQFp67VoiDA
+ // https://www.youtube.com/watch?v=aQFp67VoiDA
})();
diff --git a/js/contentscript-start.js b/js/contentscript-start.js
index 80799ef..3096023 100644
--- a/js/contentscript-start.js
+++ b/js/contentscript-start.js
@@ -39,7 +39,7 @@
var handler = function(ev) {
if (
ev.isTrusted !== true ||
- ev.originalPolicy.includes('report-uri about:blank') === false
+ ev.originalPolicy.includes('report-uri about:blank') === false
) {
return false;
}
@@ -48,7 +48,7 @@
// 'effectiveDirective' property.
if (
ev.effectiveDirective.startsWith('worker-src') === false &&
- ev.effectiveDirective.startsWith('frame-src') === false
+ ev.effectiveDirective.startsWith('frame-src') === false
) {
return false;
}
diff --git a/js/contentscript.js b/js/contentscript.js
index a4595cb..650d1be 100644
--- a/js/contentscript.js
+++ b/js/contentscript.js
@@ -32,511 +32,511 @@
(function() {
-/******************************************************************************/
+ /******************************************************************************/
-// https://github.com/chrisaljoudi/uBlock/issues/464
-// https://github.com/gorhill/uMatrix/issues/621
-if (
- document instanceof HTMLDocument === false &&
- document instanceof XMLDocument === false
-) {
- return;
-}
-
-// This can also happen (for example if script injected into a `data:` URI doc)
-if ( !window.location ) {
- return;
-}
-
-// This can happen
-if ( typeof vAPI !== 'object' ) {
- //console.debug('contentscript.js > vAPI not found');
- return;
-}
-
-// https://github.com/chrisaljoudi/uBlock/issues/456
-// Already injected?
-if ( vAPI.contentscriptEndInjected ) {
- //console.debug('contentscript.js > content script already injected');
- return;
-}
-vAPI.contentscriptEndInjected = true;
+ // https://github.com/chrisaljoudi/uBlock/issues/464
+ // https://github.com/gorhill/uMatrix/issues/621
+ if (
+ document instanceof HTMLDocument === false &&
+ document instanceof XMLDocument === false
+ ) {
+ return;
+ }
-/******************************************************************************/
-/******************************************************************************/
+ // This can also happen (for example if script injected into a `data:` URI doc)
+ if ( !window.location ) {
+ return;
+ }
-// Executed only once.
+ // This can happen
+ if ( typeof vAPI !== 'object' ) {
+ //console.debug('contentscript.js > vAPI not found');
+ return;
+ }
-(function() {
- var localStorageHandler = function(mustRemove) {
- if ( mustRemove ) {
- window.localStorage.clear();
- window.sessionStorage.clear();
- }
- };
-
- // Check with extension whether local storage must be emptied
- // rhill 2014-03-28: we need an exception handler in case 3rd-party access
- // to site data is disabled.
- // https://github.com/gorhill/httpswitchboard/issues/215
- try {
- var hasLocalStorage =
- window.localStorage && window.localStorage.length !== 0;
- var hasSessionStorage =
- window.sessionStorage && window.sessionStorage.length !== 0;
- if ( hasLocalStorage || hasSessionStorage ) {
- vAPI.messaging.send('contentscript.js', {
- what: 'contentScriptHasLocalStorage',
- originURL: window.location.origin
- }, localStorageHandler);
- }
+ // https://github.com/chrisaljoudi/uBlock/issues/456
+ // Already injected?
+ if ( vAPI.contentscriptEndInjected ) {
+ //console.debug('contentscript.js > content script already injected');
+ return;
+ }
+ vAPI.contentscriptEndInjected = true;
+
+ /******************************************************************************/
+ /******************************************************************************/
+
+ // Executed only once.
+
+ (function() {
+ var localStorageHandler = function(mustRemove) {
+ if ( mustRemove ) {
+ window.localStorage.clear();
+ window.sessionStorage.clear();
+ }
+ };
+
+ // Check with extension whether local storage must be emptied
+ // rhill 2014-03-28: we need an exception handler in case 3rd-party access
+ // to site data is disabled.
+ // https://github.com/gorhill/httpswitchboard/issues/215
+ try {
+ var hasLocalStorage =
+ window.localStorage && window.localStorage.length !== 0;
+ var hasSessionStorage =
+ window.sessionStorage && window.sessionStorage.length !== 0;
+ if ( hasLocalStorage || hasSessionStorage ) {
+ vAPI.messaging.send('contentscript.js', {
+ what: 'contentScriptHasLocalStorage',
+ originURL: window.location.origin
+ }, localStorageHandler);
+ }
- // TODO: indexedDB
- //if ( window.indexedDB && !!window.indexedDB.webkitGetDatabaseNames ) {
+ // TODO: indexedDB
+ //if ( window.indexedDB && !!window.indexedDB.webkitGetDatabaseNames ) {
// var db = window.indexedDB.webkitGetDatabaseNames().onsuccess = function(sender) {
// console.debug('webkitGetDatabaseNames(): result=%o', sender.target.result);
// };
- //}
+ //}
- // TODO: Web SQL
- // if ( window.openDatabase ) {
+ // TODO: Web SQL
+ // if ( window.openDatabase ) {
// Sad:
// "There is no way to enumerate or delete the databases available for an origin from this API."
// Ref.: http://www.w3.org/TR/webdatabase/#databases
- // }
- }
- catch (e) {
- }
-})();
-
-/******************************************************************************/
-/******************************************************************************/
-
-// https://github.com/gorhill/uMatrix/issues/45
-
-var collapser = (function() {
- var resquestIdGenerator = 1,
- processTimer,
- toProcess = [],
- toFilter = [],
- toCollapse = new Map(),
- cachedBlockedMap,
- cachedBlockedMapHash,
- cachedBlockedMapTimer,
- reURLPlaceholder = /\{\{url\}\}/g;
- var src1stProps = {
- 'embed': 'src',
- 'iframe': 'src',
- 'img': 'src',
- 'object': 'data'
- };
- var src2ndProps = {
- 'img': 'srcset'
- };
- var tagToTypeMap = {
- embed: 'media',
- iframe: 'frame',
- img: 'image',
- object: 'media'
- };
- var cachedBlockedSetClear = function() {
- cachedBlockedMap =
- cachedBlockedMapHash =
- cachedBlockedMapTimer = undefined;
- };
-
- // https://github.com/chrisaljoudi/uBlock/issues/174
- // Do not remove fragment from src URL
- var onProcessed = function(response) {
- if ( !response ) { // This happens if uBO is disabled or restarted.
- toCollapse.clear();
- return;
+ // }
}
+ catch (e) {
+ }
+ })();
+
+ /******************************************************************************/
+ /******************************************************************************/
+
+ // https://github.com/gorhill/uMatrix/issues/45
+
+ var collapser = (function() {
+ var resquestIdGenerator = 1,
+ processTimer,
+ toProcess = [],
+ toFilter = [],
+ toCollapse = new Map(),
+ cachedBlockedMap,
+ cachedBlockedMapHash,
+ cachedBlockedMapTimer,
+ reURLPlaceholder = /\{\{url\}\}/g;
+ var src1stProps = {
+ 'embed': 'src',
+ 'iframe': 'src',
+ 'img': 'src',
+ 'object': 'data'
+ };
+ var src2ndProps = {
+ 'img': 'srcset'
+ };
+ var tagToTypeMap = {
+ embed: 'media',
+ iframe: 'frame',
+ img: 'image',
+ object: 'media'
+ };
+ var cachedBlockedSetClear = function() {
+ cachedBlockedMap =
+ cachedBlockedMapHash =
+ cachedBlockedMapTimer = undefined;
+ };
- var targets = toCollapse.get(response.id);
- if ( targets === undefined ) { return; }
- toCollapse.delete(response.id);
- if ( cachedBlockedMapHash !== response.hash ) {
- cachedBlockedMap = new Map(response.blockedResources);
- cachedBlockedMapHash = response.hash;
- if ( cachedBlockedMapTimer !== undefined ) {
- clearTimeout(cachedBlockedMapTimer);
+ // https://github.com/chrisaljoudi/uBlock/issues/174
+ // Do not remove fragment from src URL
+ var onProcessed = function(response) {
+ if ( !response ) { // This happens if uBO is disabled or restarted.
+ toCollapse.clear();
+ return;
+ }
+
+ var targets = toCollapse.get(response.id);
+ if ( targets === undefined ) { return; }
+ toCollapse.delete(response.id);
+ if ( cachedBlockedMapHash !== response.hash ) {
+ cachedBlockedMap = new Map(response.blockedResources);
+ cachedBlockedMapHash = response.hash;
+ if ( cachedBlockedMapTimer !== undefined ) {
+ clearTimeout(cachedBlockedMapTimer);
+ }
+ cachedBlockedMapTimer = vAPI.setTimeout(cachedBlockedSetClear, 30000);
+ }
+ if ( cachedBlockedMap === undefined || cachedBlockedMap.size === 0 ) {
+ return;
}
- cachedBlockedMapTimer = vAPI.setTimeout(cachedBlockedSetClear, 30000);
- }
- if ( cachedBlockedMap === undefined || cachedBlockedMap.size === 0 ) {
- return;
- }
- var placeholders = response.placeholders,
- tag, prop, src, collapsed, docurl, replaced;
+ var placeholders = response.placeholders,
+ tag, prop, src, collapsed, docurl, replaced;
- for ( var target of targets ) {
- tag = target.localName;
- prop = src1stProps[tag];
- if ( prop === undefined ) { continue; }
- src = target[prop];
- if ( typeof src !== 'string' || src.length === 0 ) {
- prop = src2ndProps[tag];
+ for ( var target of targets ) {
+ tag = target.localName;
+ prop = src1stProps[tag];
if ( prop === undefined ) { continue; }
src = target[prop];
- if ( typeof src !== 'string' || src.length === 0 ) { continue; }
- }
- collapsed = cachedBlockedMap.get(tagToTypeMap[tag] + ' ' + src);
- if ( collapsed === undefined ) { continue; }
- if ( collapsed ) {
- target.style.setProperty('display', 'none', 'important');
- target.hidden = true;
- continue;
- }
- switch ( tag ) {
- case 'iframe':
- if ( placeholders.frame !== true ) { break; }
- docurl =
- 'data:text/html,' +
- encodeURIComponent(
- placeholders.frameDocument.replace(
- reURLPlaceholder,
- src
- )
- );
- replaced = false;
- // Using contentWindow.location prevent tainting browser
- // history -- i.e. breaking back button (seen on Chromium).
- if ( target.contentWindow ) {
- try {
- target.contentWindow.location.replace(docurl);
- replaced = true;
- } catch(ex) {
+ if ( typeof src !== 'string' || src.length === 0 ) {
+ prop = src2ndProps[tag];
+ if ( prop === undefined ) { continue; }
+ src = target[prop];
+ if ( typeof src !== 'string' || src.length === 0 ) { continue; }
+ }
+ collapsed = cachedBlockedMap.get(tagToTypeMap[tag] + ' ' + src);
+ if ( collapsed === undefined ) { continue; }
+ if ( collapsed ) {
+ target.style.setProperty('display', 'none', 'important');
+ target.hidden = true;
+ continue;
+ }
+ switch ( tag ) {
+ case 'iframe':
+ if ( placeholders.frame !== true ) { break; }
+ docurl =
+ 'data:text/html,' +
+ encodeURIComponent(
+ placeholders.frameDocument.replace(
+ reURLPlaceholder,
+ src
+ )
+ );
+ replaced = false;
+ // Using contentWindow.location prevent tainting browser
+ // history -- i.e. breaking back button (seen on Chromium).
+ if ( target.contentWindow ) {
+ try {
+ target.contentWindow.location.replace(docurl);
+ replaced = true;
+ } catch(ex) {
+ }
}
+ if ( !replaced ) {
+ target.setAttribute('src', docurl);
+ }
+ break;
+ case 'img':
+ if ( placeholders.image !== true ) { break; }
+ target.style.setProperty('display', 'inline-block');
+ target.style.setProperty('min-width', '20px', 'important');
+ target.style.setProperty('min-height', '20px', 'important');
+ target.style.setProperty(
+ 'border',
+ placeholders.imageBorder,
+ 'important'
+ );
+ target.style.setProperty(
+ 'background',
+ placeholders.imageBackground,
+ 'important'
+ );
+ break;
}
- if ( !replaced ) {
- target.setAttribute('src', docurl);
+ }
+ };
+
+ var send = function() {
+ processTimer = undefined;
+ toCollapse.set(resquestIdGenerator, toProcess);
+ var msg = {
+ what: 'lookupBlockedCollapsibles',
+ id: resquestIdGenerator,
+ toFilter: toFilter,
+ hash: cachedBlockedMapHash
+ };
+ vAPI.messaging.send('contentscript.js', msg, onProcessed);
+ toProcess = [];
+ toFilter = [];
+ resquestIdGenerator += 1;
+ };
+
+ var process = function(delay) {
+ if ( toProcess.length === 0 ) { return; }
+ if ( delay === 0 ) {
+ if ( processTimer !== undefined ) {
+ clearTimeout(processTimer);
}
- break;
- case 'img':
- if ( placeholders.image !== true ) { break; }
- target.style.setProperty('display', 'inline-block');
- target.style.setProperty('min-width', '20px', 'important');
- target.style.setProperty('min-height', '20px', 'important');
- target.style.setProperty(
- 'border',
- placeholders.imageBorder,
- 'important'
- );
- target.style.setProperty(
- 'background',
- placeholders.imageBackground,
- 'important'
- );
- break;
+ send();
+ } else if ( processTimer === undefined ) {
+ processTimer = vAPI.setTimeout(send, delay || 47);
}
- }
- };
-
- var send = function() {
- processTimer = undefined;
- toCollapse.set(resquestIdGenerator, toProcess);
- var msg = {
- what: 'lookupBlockedCollapsibles',
- id: resquestIdGenerator,
- toFilter: toFilter,
- hash: cachedBlockedMapHash
};
- vAPI.messaging.send('contentscript.js', msg, onProcessed);
- toProcess = [];
- toFilter = [];
- resquestIdGenerator += 1;
- };
-
- var process = function(delay) {
- if ( toProcess.length === 0 ) { return; }
- if ( delay === 0 ) {
- if ( processTimer !== undefined ) {
- clearTimeout(processTimer);
+
+ var add = function(target) {
+ toProcess.push(target);
+ };
+
+ var addMany = function(targets) {
+ var i = targets.length;
+ while ( i-- ) {
+ toProcess.push(targets[i]);
}
- send();
- } else if ( processTimer === undefined ) {
- processTimer = vAPI.setTimeout(send, delay || 47);
- }
- };
+ };
- var add = function(target) {
- toProcess.push(target);
- };
+ var iframeSourceModified = function(mutations) {
+ var i = mutations.length;
+ while ( i-- ) {
+ addIFrame(mutations[i].target, true);
+ }
+ process();
+ };
+ var iframeSourceObserver;
+ var iframeSourceObserverOptions = {
+ attributes: true,
+ attributeFilter: [ 'src' ]
+ };
- var addMany = function(targets) {
- var i = targets.length;
- while ( i-- ) {
- toProcess.push(targets[i]);
- }
- };
+ var addIFrame = function(iframe, dontObserve) {
+ // https://github.com/gorhill/uBlock/issues/162
+ // Be prepared to deal with possible change of src attribute.
+ if ( dontObserve !== true ) {
+ if ( iframeSourceObserver === undefined ) {
+ iframeSourceObserver = new MutationObserver(iframeSourceModified);
+ }
+ iframeSourceObserver.observe(iframe, iframeSourceObserverOptions);
+ }
+ var src = iframe.src;
+ if ( src === '' || typeof src !== 'string' ) { return; }
+ if ( src.startsWith('http') === false ) { return; }
+ toFilter.push({ type: 'frame', url: iframe.src });
+ add(iframe);
+ };
- var iframeSourceModified = function(mutations) {
- var i = mutations.length;
- while ( i-- ) {
- addIFrame(mutations[i].target, true);
- }
- process();
- };
- var iframeSourceObserver;
- var iframeSourceObserverOptions = {
- attributes: true,
- attributeFilter: [ 'src' ]
- };
-
- var addIFrame = function(iframe, dontObserve) {
- // https://github.com/gorhill/uBlock/issues/162
- // Be prepared to deal with possible change of src attribute.
- if ( dontObserve !== true ) {
- if ( iframeSourceObserver === undefined ) {
- iframeSourceObserver = new MutationObserver(iframeSourceModified);
+ var addIFrames = function(iframes) {
+ var i = iframes.length;
+ while ( i-- ) {
+ addIFrame(iframes[i]);
}
- iframeSourceObserver.observe(iframe, iframeSourceObserverOptions);
- }
- var src = iframe.src;
- if ( src === '' || typeof src !== 'string' ) { return; }
- if ( src.startsWith('http') === false ) { return; }
- toFilter.push({ type: 'frame', url: iframe.src });
- add(iframe);
- };
-
- var addIFrames = function(iframes) {
- var i = iframes.length;
- while ( i-- ) {
- addIFrame(iframes[i]);
- }
- };
-
- var addNodeList = function(nodeList) {
- var node,
- i = nodeList.length;
- while ( i-- ) {
- node = nodeList[i];
- if ( node.nodeType !== 1 ) { continue; }
- if ( node.localName === 'iframe' ) {
- addIFrame(node);
+ };
+
+ var addNodeList = function(nodeList) {
+ var node,
+ i = nodeList.length;
+ while ( i-- ) {
+ node = nodeList[i];
+ if ( node.nodeType !== 1 ) { continue; }
+ if ( node.localName === 'iframe' ) {
+ addIFrame(node);
+ }
+ if ( node.childElementCount !== 0 ) {
+ addIFrames(node.querySelectorAll('iframe'));
+ }
+ }
+ };
+
+ var onResourceFailed = function(ev) {
+ if ( tagToTypeMap[ev.target.localName] !== undefined ) {
+ add(ev.target);
+ process();
}
- if ( node.childElementCount !== 0 ) {
- addIFrames(node.querySelectorAll('iframe'));
+ };
+ document.addEventListener('error', onResourceFailed, true);
+
+ vAPI.shutdown.add(function() {
+ document.removeEventListener('error', onResourceFailed, true);
+ if ( iframeSourceObserver !== undefined ) {
+ iframeSourceObserver.disconnect();
+ iframeSourceObserver = undefined;
}
- }
- };
+ if ( processTimer !== undefined ) {
+ clearTimeout(processTimer);
+ processTimer = undefined;
+ }
+ });
- var onResourceFailed = function(ev) {
- if ( tagToTypeMap[ev.target.localName] !== undefined ) {
- add(ev.target);
- process();
- }
- };
- document.addEventListener('error', onResourceFailed, true);
-
- vAPI.shutdown.add(function() {
- document.removeEventListener('error', onResourceFailed, true);
- if ( iframeSourceObserver !== undefined ) {
- iframeSourceObserver.disconnect();
- iframeSourceObserver = undefined;
- }
- if ( processTimer !== undefined ) {
- clearTimeout(processTimer);
- processTimer = undefined;
- }
- });
-
- return {
- addMany: addMany,
- addIFrames: addIFrames,
- addNodeList: addNodeList,
- process: process
- };
-})();
+ return {
+ addMany: addMany,
+ addIFrames: addIFrames,
+ addNodeList: addNodeList,
+ process: process
+ };
+ })();
-/******************************************************************************/
-/******************************************************************************/
+ /******************************************************************************/
+ /******************************************************************************/
-// Observe changes in the DOM
+ // Observe changes in the DOM
-// Added node lists will be cumulated here before being processed
+ // Added node lists will be cumulated here before being processed
-(function() {
- // This fixes http://acid3.acidtests.org/
- if ( !document.body ) { return; }
+ (function() {
+ // This fixes http://acid3.acidtests.org/
+ if ( !document.body ) { return; }
- var addedNodeLists = [];
- var addedNodeListsTimer;
+ var addedNodeLists = [];
+ var addedNodeListsTimer;
- var treeMutationObservedHandler = function() {
- addedNodeListsTimer = undefined;
- var i = addedNodeLists.length;
- while ( i-- ) {
- collapser.addNodeList(addedNodeLists[i]);
- }
- collapser.process();
- addedNodeLists = [];
- };
-
- // https://github.com/gorhill/uBlock/issues/205
- // Do not handle added node directly from within mutation observer.
- var treeMutationObservedHandlerAsync = function(mutations) {
- var iMutation = mutations.length,
- nodeList;
- while ( iMutation-- ) {
- nodeList = mutations[iMutation].addedNodes;
- if ( nodeList.length !== 0 ) {
- addedNodeLists.push(nodeList);
- }
- }
- if ( addedNodeListsTimer === undefined ) {
- addedNodeListsTimer = vAPI.setTimeout(treeMutationObservedHandler, 47);
- }
- };
-
- // https://github.com/gorhill/httpswitchboard/issues/176
- var treeObserver = new MutationObserver(treeMutationObservedHandlerAsync);
- treeObserver.observe(document.body, {
- childList: true,
- subtree: true
- });
-
- vAPI.shutdown.add(function() {
- if ( addedNodeListsTimer !== undefined ) {
- clearTimeout(addedNodeListsTimer);
+ var treeMutationObservedHandler = function() {
addedNodeListsTimer = undefined;
- }
- if ( treeObserver !== null ) {
- treeObserver.disconnect();
- treeObserver = undefined;
- }
- addedNodeLists = [];
- });
-})();
-
-/******************************************************************************/
-/******************************************************************************/
+ var i = addedNodeLists.length;
+ while ( i-- ) {
+ collapser.addNodeList(addedNodeLists[i]);
+ }
+ collapser.process();
+ addedNodeLists = [];
+ };
-// Executed only once.
-//
-// https://github.com/gorhill/httpswitchboard/issues/25
-//
-// https://github.com/gorhill/httpswitchboard/issues/131
-// Looks for inline javascript also in at least one a[href] element.
-//
-// https://github.com/gorhill/uMatrix/issues/485
-// Mind "on..." attributes.
-//
-// https://github.com/gorhill/uMatrix/issues/924
-// Report inline styles.
+ // https://github.com/gorhill/uBlock/issues/205
+ // Do not handle added node directly from within mutation observer.
+ var treeMutationObservedHandlerAsync = function(mutations) {
+ var iMutation = mutations.length,
+ nodeList;
+ while ( iMutation-- ) {
+ nodeList = mutations[iMutation].addedNodes;
+ if ( nodeList.length !== 0 ) {
+ addedNodeLists.push(nodeList);
+ }
+ }
+ if ( addedNodeListsTimer === undefined ) {
+ addedNodeListsTimer = vAPI.setTimeout(treeMutationObservedHandler, 47);
+ }
+ };
-(function() {
- if (
- document.querySelector('script:not([src])') !== null ||
- document.querySelector('a[href^="javascript:"]') !== null ||
- document.querySelector('[onabort],[onblur],[oncancel],[oncanplay],[oncanplaythrough],[onchange],[onclick],[onclose],[oncontextmenu],[oncuechange],[ondblclick],[ondrag],[ondragend],[ondragenter],[ondragexit],[ondragleave],[ondragover],[ondragstart],[ondrop],[ondurationchange],[onemptied],[onended],[onerror],[onfocus],[oninput],[oninvalid],[onkeydown],[onkeypress],[onkeyup],[onload],[onloadeddata],[onloadedmetadata],[onloadstart],[onmousedown],[onmouseenter],[onmouseleave],[onmousemove],[onmouseout],[onmouseover],[onmouseup],[onwheel],[onpause],[onplay],[onplaying],[onprogress],[onratechange],[onreset],[onresize],[onscroll],[onseeked],[onseeking],[onselect],[onshow],[onstalled],[onsubmit],[onsuspend],[ontimeupdate],[ontoggle],[onvolumechange],[onwaiting],[onafterprint],[onbeforeprint],[onbeforeunload],[onhashchange],[onlanguagechange],[onmessage],[onoffline],[ononline],[onpagehide],[onpageshow],[onrejectionhandled],[onpopstate],[onstorage],[onunhandledrejection],[onunload],[oncopy],[oncut],[onpaste]') !== null
- ) {
- vAPI.messaging.send('contentscript.js', {
- what: 'securityPolicyViolation',
- directive: 'script-src',
- documentURI: window.location.href
+ // https://github.com/gorhill/httpswitchboard/issues/176
+ var treeObserver = new MutationObserver(treeMutationObservedHandlerAsync);
+ treeObserver.observe(document.body, {
+ childList: true,
+ subtree: true
});
- }
- if ( document.querySelector('style,[style]') !== null ) {
- vAPI.messaging.send('contentscript.js', {
- what: 'securityPolicyViolation',
- directive: 'style-src',
- documentURI: window.location.href
+ vAPI.shutdown.add(function() {
+ if ( addedNodeListsTimer !== undefined ) {
+ clearTimeout(addedNodeListsTimer);
+ addedNodeListsTimer = undefined;
+ }
+ if ( treeObserver !== null ) {
+ treeObserver.disconnect();
+ treeObserver = undefined;
+ }
+ addedNodeLists = [];
});
- }
-
- collapser.addMany(document.querySelectorAll('img'));
- collapser.addIFrames(document.querySelectorAll('iframe'));
- collapser.process();
-})();
+ })();
+
+ /******************************************************************************/
+ /******************************************************************************/
+
+ // Executed only once.
+ //
+ // https://github.com/gorhill/httpswitchboard/issues/25
+ //
+ // https://github.com/gorhill/httpswitchboard/issues/131
+ // Looks for inline javascript also in at least one a[href] element.
+ //
+ // https://github.com/gorhill/uMatrix/issues/485
+ // Mind "on..." attributes.
+ //
+ // https://github.com/gorhill/uMatrix/issues/924
+ // Report inline styles.
+
+ (function() {
+ if (
+ document.querySelector('script:not([src])') !== null ||
+ document.querySelector('a[href^="javascript:"]') !== null ||
+ document.querySelector('[onabort],[onblur],[oncancel],[oncanplay],[oncanplaythrough],[onchange],[onclick],[onclose],[oncontextmenu],[oncuechange],[ondblclick],[ondrag],[ondragend],[ondragenter],[ondragexit],[ondragleave],[ondragover],[ondragstart],[ondrop],[ondurationchange],[onemptied],[onended],[onerror],[onfocus],[oninput],[oninvalid],[onkeydown],[onkeypress],[onkeyup],[onload],[onloadeddata],[onloadedmetadata],[onloadstart],[onmousedown],[onmouseenter],[onmouseleave],[onmousemove],[onmouseout],[onmouseover],[onmouseup],[onwheel],[onpause],[onplay],[onplaying],[onprogress],[onratechange],[onreset],[onresize],[onscroll],[onseeked],[onseeking],[onselect],[onshow],[onstalled],[onsubmit],[onsuspend],[ontimeupdate],[ontoggle],[onvolumechange],[onwaiting],[onafterprint],[onbeforeprint],[onbeforeunload],[onhashchange],[onlanguagechange],[onmessage],[onoffline],[ononline],[onpagehide],[onpageshow],[onrejectionhandled],[onpopstate],[onstorage],[onunhandledrejection],[onunload],[oncopy],[oncut],[onpaste]') !== null
+ ) {
+ vAPI.messaging.send('contentscript.js', {
+ what: 'securityPolicyViolation',
+ directive: 'script-src',
+ documentURI: window.location.href
+ });
+ }
-/******************************************************************************/
-/******************************************************************************/
+ if ( document.querySelector('style,[style]') !== null ) {
+ vAPI.messaging.send('contentscript.js', {
+ what: 'securityPolicyViolation',
+ directive: 'style-src',
+ documentURI: window.location.href
+ });
+ }
-// Executed only once.
+ collapser.addMany(document.querySelectorAll('img'));
+ collapser.addIFrames(document.querySelectorAll('iframe'));
+ collapser.process();
+ })();
+
+ /******************************************************************************/
+ /******************************************************************************/
+
+ // Executed only once.
+
+ // https://github.com/gorhill/uMatrix/issues/232
+ // Force `display` property, Firefox is still affected by the issue.
+
+ (function() {
+ var noscripts = document.querySelectorAll('noscript');
+ if ( noscripts.length === 0 ) { return; }
+
+ var redirectTimer,
+ reMetaContent = /^\s*(\d+)\s*;\s*url=(['"]?)([^'"]+)\2/i,
+ reSafeURL = /^https?:\/\//;
+
+ var autoRefresh = function(root) {
+ var meta = root.querySelector('meta[http-equiv="refresh"][content]');
+ if ( meta === null ) { return; }
+ var match = reMetaContent.exec(meta.getAttribute('content'));
+ if ( match === null || match[3].trim() === '' ) { return; }
+ var url = new URL(match[3], document.baseURI);
+ if ( reSafeURL.test(url.href) === false ) { return; }
+ redirectTimer = setTimeout(
+ function() {
+ location.assign(url.href);
+ },
+ parseInt(match[1], 10) * 1000 + 1
+ );
+ meta.parentNode.removeChild(meta);
+ };
-// https://github.com/gorhill/uMatrix/issues/232
-// Force `display` property, Firefox is still affected by the issue.
+ var morphNoscript = function(from) {
+ if ( /^application\/(?:xhtml\+)?xml/.test(document.contentType) ) {
+ var to = document.createElement('span');
+ while ( from.firstChild !== null ) {
+ to.appendChild(from.firstChild);
+ }
+ return to;
+ }
+ var parser = new DOMParser();
+ var doc = parser.parseFromString(
+ '<span>' + from.textContent + '</span>',
+ 'text/html'
+ );
+ return document.adoptNode(doc.querySelector('span'));
+ };
-(function() {
- var noscripts = document.querySelectorAll('noscript');
- if ( noscripts.length === 0 ) { return; }
-
- var redirectTimer,
- reMetaContent = /^\s*(\d+)\s*;\s*url=(['"]?)([^'"]+)\2/i,
- reSafeURL = /^https?:\/\//;
-
- var autoRefresh = function(root) {
- var meta = root.querySelector('meta[http-equiv="refresh"][content]');
- if ( meta === null ) { return; }
- var match = reMetaContent.exec(meta.getAttribute('content'));
- if ( match === null || match[3].trim() === '' ) { return; }
- var url = new URL(match[3], document.baseURI);
- if ( reSafeURL.test(url.href) === false ) { return; }
- redirectTimer = setTimeout(
- function() {
- location.assign(url.href);
- },
- parseInt(match[1], 10) * 1000 + 1
- );
- meta.parentNode.removeChild(meta);
- };
-
- var morphNoscript = function(from) {
- if ( /^application\/(?:xhtml\+)?xml/.test(document.contentType) ) {
- var to = document.createElement('span');
- while ( from.firstChild !== null ) {
- to.appendChild(from.firstChild);
+ var renderNoscriptTags = function(response) {
+ if ( response !== true ) { return; }
+ var parent, span;
+ for ( var noscript of noscripts ) {
+ parent = noscript.parentNode;
+ if ( parent === null ) { continue; }
+ span = morphNoscript(noscript);
+ span.style.setProperty('display', 'inline', 'important');
+ if ( redirectTimer === undefined ) {
+ autoRefresh(span);
+ }
+ parent.replaceChild(span, noscript);
}
- return to;
- }
- var parser = new DOMParser();
- var doc = parser.parseFromString(
- '<span>' + from.textContent + '</span>',
- 'text/html'
+ };
+
+ vAPI.messaging.send(
+ 'contentscript.js',
+ { what: 'mustRenderNoscriptTags?' },
+ renderNoscriptTags
);
- return document.adoptNode(doc.querySelector('span'));
- };
-
- var renderNoscriptTags = function(response) {
- if ( response !== true ) { return; }
- var parent, span;
- for ( var noscript of noscripts ) {
- parent = noscript.parentNode;
- if ( parent === null ) { continue; }
- span = morphNoscript(noscript);
- span.style.setProperty('display', 'inline', 'important');
- if ( redirectTimer === undefined ) {
- autoRefresh(span);
- }
- parent.replaceChild(span, noscript);
- }
- };
+ })();
+
+ /******************************************************************************/
+ /******************************************************************************/
vAPI.messaging.send(
'contentscript.js',
- { what: 'mustRenderNoscriptTags?' },
- renderNoscriptTags
- );
-})();
-
-/******************************************************************************/
-/******************************************************************************/
-
-vAPI.messaging.send(
- 'contentscript.js',
- { what: 'shutdown?' },
- function(response) {
- if ( response === true ) {
- vAPI.shutdown.exec();
+ { what: 'shutdown?' },
+ function(response) {
+ if ( response === true ) {
+ vAPI.shutdown.exec();
+ }
}
- }
-);
+ );
-/******************************************************************************/
-/******************************************************************************/
+ /******************************************************************************/
+ /******************************************************************************/
})();
diff --git a/js/cookies.js b/js/cookies.js
index 4aa76c0..ee271ca 100644
--- a/js/cookies.js
+++ b/js/cookies.js
@@ -37,517 +37,516 @@
ηMatrix.cookieHunter = (function() {
-/******************************************************************************/
+ /******************************************************************************/
-var ηm = ηMatrix;
+ var ηm = ηMatrix;
-var recordPageCookiesQueue = new Map();
-var removePageCookiesQueue = new Map();
-var removeCookieQueue = new Set();
-var cookieDict = new Map();
-var cookieEntryJunkyard = [];
-var processRemoveQueuePeriod = 2 * 60 * 1000;
-var processCleanPeriod = 10 * 60 * 1000;
-var processPageRecordQueueTimer = null;
-var processPageRemoveQueueTimer = null;
+ var recordPageCookiesQueue = new Map();
+ var removePageCookiesQueue = new Map();
+ var removeCookieQueue = new Set();
+ var cookieDict = new Map();
+ var cookieEntryJunkyard = [];
+ var processRemoveQueuePeriod = 2 * 60 * 1000;
+ var processCleanPeriod = 10 * 60 * 1000;
+ var processPageRecordQueueTimer = null;
+ var processPageRemoveQueueTimer = null;
-/******************************************************************************/
+ /******************************************************************************/
-var CookieEntry = function(cookie) {
- this.usedOn = new Set();
- this.init(cookie);
-};
-
-CookieEntry.prototype.init = function(cookie) {
- this.secure = cookie.secure;
- this.session = cookie.session;
- this.anySubdomain = cookie.domain.charAt(0) === '.';
- this.hostname = this.anySubdomain ? cookie.domain.slice(1) : cookie.domain;
- this.domain = ηm.URI.domainFromHostname(this.hostname) || this.hostname;
- this.path = cookie.path;
- this.name = cookie.name;
- this.value = cookie.value;
- this.tstamp = Date.now();
- this.usedOn.clear();
- return this;
-};
-
-// Release anything which may consume too much memory
-
-CookieEntry.prototype.dispose = function() {
- this.hostname = '';
- this.domain = '';
- this.path = '';
- this.name = '';
- this.value = '';
- this.usedOn.clear();
- return this;
-};
+ var CookieEntry = function(cookie) {
+ this.usedOn = new Set();
+ this.init(cookie);
+ };
-/******************************************************************************/
+ CookieEntry.prototype.init = function(cookie) {
+ this.secure = cookie.secure;
+ this.session = cookie.session;
+ this.anySubdomain = cookie.domain.charAt(0) === '.';
+ this.hostname = this.anySubdomain ? cookie.domain.slice(1) : cookie.domain;
+ this.domain = ηm.URI.domainFromHostname(this.hostname) || this.hostname;
+ this.path = cookie.path;
+ this.name = cookie.name;
+ this.value = cookie.value;
+ this.tstamp = Date.now();
+ this.usedOn.clear();
+ return this;
+ };
-var addCookieToDict = function(cookie) {
- var cookieKey = cookieKeyFromCookie(cookie),
- cookieEntry = cookieDict.get(cookieKey);
- if ( cookieEntry === undefined ) {
- cookieEntry = cookieEntryJunkyard.pop();
- if ( cookieEntry ) {
- cookieEntry.init(cookie);
- } else {
- cookieEntry = new CookieEntry(cookie);
+ // Release anything which may consume too much memory
+
+ CookieEntry.prototype.dispose = function() {
+ this.hostname = '';
+ this.domain = '';
+ this.path = '';
+ this.name = '';
+ this.value = '';
+ this.usedOn.clear();
+ return this;
+ };
+
+ /******************************************************************************/
+
+ var addCookieToDict = function(cookie) {
+ var cookieKey = cookieKeyFromCookie(cookie),
+ cookieEntry = cookieDict.get(cookieKey);
+ if ( cookieEntry === undefined ) {
+ cookieEntry = cookieEntryJunkyard.pop();
+ if ( cookieEntry ) {
+ cookieEntry.init(cookie);
+ } else {
+ cookieEntry = new CookieEntry(cookie);
+ }
+ cookieDict.set(cookieKey, cookieEntry);
}
- cookieDict.set(cookieKey, cookieEntry);
- }
- return cookieEntry;
-};
+ return cookieEntry;
+ };
-/******************************************************************************/
+ /******************************************************************************/
-var addCookiesToDict = function(cookies) {
- var i = cookies.length;
- while ( i-- ) {
- addCookieToDict(cookies[i]);
- }
-};
+ var addCookiesToDict = function(cookies) {
+ var i = cookies.length;
+ while ( i-- ) {
+ addCookieToDict(cookies[i]);
+ }
+ };
-/******************************************************************************/
+ /******************************************************************************/
-var removeCookieFromDict = function(cookieKey) {
- var cookieEntry = cookieDict.get(cookieKey);
- if ( cookieEntry === undefined ) { return false; }
- cookieDict.delete(cookieKey);
- if ( cookieEntryJunkyard.length < 25 ) {
- cookieEntryJunkyard.push(cookieEntry.dispose());
- }
- return true;
-};
+ var removeCookieFromDict = function(cookieKey) {
+ var cookieEntry = cookieDict.get(cookieKey);
+ if ( cookieEntry === undefined ) { return false; }
+ cookieDict.delete(cookieKey);
+ if ( cookieEntryJunkyard.length < 25 ) {
+ cookieEntryJunkyard.push(cookieEntry.dispose());
+ }
+ return true;
+ };
-/******************************************************************************/
+ /******************************************************************************/
+
+ var cookieKeyBuilder = [
+ '', // 0 = scheme
+ '://',
+ '', // 2 = domain
+ '', // 3 = path
+ '{',
+ '', // 5 = persistent or session
+ '-cookie:',
+ '', // 7 = name
+ '}'
+ ];
+
+ var cookieKeyFromCookie = function(cookie) {
+ var cb = cookieKeyBuilder;
+ cb[0] = cookie.secure ? 'https' : 'http';
+ cb[2] = cookie.domain.charAt(0) === '.' ? cookie.domain.slice(1) : cookie.domain;
+ cb[3] = cookie.path;
+ cb[5] = cookie.session ? 'session' : 'persistent';
+ cb[7] = cookie.name;
+ return cb.join('');
+ };
-var cookieKeyBuilder = [
- '', // 0 = scheme
- '://',
- '', // 2 = domain
- '', // 3 = path
- '{',
- '', // 5 = persistent or session
- '-cookie:',
- '', // 7 = name
- '}'
-];
-
-var cookieKeyFromCookie = function(cookie) {
- var cb = cookieKeyBuilder;
- cb[0] = cookie.secure ? 'https' : 'http';
- cb[2] = cookie.domain.charAt(0) === '.' ? cookie.domain.slice(1) : cookie.domain;
- cb[3] = cookie.path;
- cb[5] = cookie.session ? 'session' : 'persistent';
- cb[7] = cookie.name;
- return cb.join('');
-};
-
-var cookieKeyFromCookieURL = function(url, type, name) {
- var ηmuri = ηm.URI.set(url);
- var cb = cookieKeyBuilder;
- cb[0] = ηmuri.scheme;
- cb[2] = ηmuri.hostname;
- cb[3] = ηmuri.path;
- cb[5] = type;
- cb[7] = name;
- return cb.join('');
-};
+ var cookieKeyFromCookieURL = function(url, type, name) {
+ var ηmuri = ηm.URI.set(url);
+ var cb = cookieKeyBuilder;
+ cb[0] = ηmuri.scheme;
+ cb[2] = ηmuri.hostname;
+ cb[3] = ηmuri.path;
+ cb[5] = type;
+ cb[7] = name;
+ return cb.join('');
+ };
-/******************************************************************************/
+ /******************************************************************************/
-var cookieURLFromCookieEntry = function(entry) {
- if ( !entry ) {
- return '';
- }
- return (entry.secure ? 'https://' : 'http://') + entry.hostname + entry.path;
-};
+ var cookieURLFromCookieEntry = function(entry) {
+ if ( !entry ) {
+ return '';
+ }
+ return (entry.secure ? 'https://' : 'http://') + entry.hostname + entry.path;
+ };
-/******************************************************************************/
+ /******************************************************************************/
-var cookieMatchDomains = function(cookieKey, allHostnamesString) {
- var cookieEntry = cookieDict.get(cookieKey);
- if ( cookieEntry === undefined ) { return false; }
- if ( allHostnamesString.indexOf(' ' + cookieEntry.hostname + ' ') < 0 ) {
- if ( !cookieEntry.anySubdomain ) {
- return false;
+ var cookieMatchDomains = function(cookieKey, allHostnamesString) {
+ var cookieEntry = cookieDict.get(cookieKey);
+ if ( cookieEntry === undefined ) { return false; }
+ if ( allHostnamesString.indexOf(' ' + cookieEntry.hostname + ' ') < 0 ) {
+ if ( !cookieEntry.anySubdomain ) {
+ return false;
+ }
+ if ( allHostnamesString.indexOf('.' + cookieEntry.hostname + ' ') < 0 ) {
+ return false;
+ }
}
- if ( allHostnamesString.indexOf('.' + cookieEntry.hostname + ' ') < 0 ) {
- return false;
+ return true;
+ };
+
+ /******************************************************************************/
+
+ // Look for cookies to record for a specific web page
+
+ var recordPageCookiesAsync = function(pageStats) {
+ // Store the page stats objects so that it doesn't go away
+ // before we handle the job.
+ // rhill 2013-10-19: pageStats could be nil, for example, this can
+ // happens if a file:// ... makes an xmlHttpRequest
+ if ( !pageStats ) {
+ return;
}
- }
- return true;
-};
+ recordPageCookiesQueue.set(pageStats.pageUrl, pageStats);
+ if ( processPageRecordQueueTimer === null ) {
+ processPageRecordQueueTimer = vAPI.setTimeout(processPageRecordQueue, 1000);
+ }
+ };
-/******************************************************************************/
+ /******************************************************************************/
-// Look for cookies to record for a specific web page
-
-var recordPageCookiesAsync = function(pageStats) {
- // Store the page stats objects so that it doesn't go away
- // before we handle the job.
- // rhill 2013-10-19: pageStats could be nil, for example, this can
- // happens if a file:// ... makes an xmlHttpRequest
- if ( !pageStats ) {
- return;
- }
- recordPageCookiesQueue.set(pageStats.pageUrl, pageStats);
- if ( processPageRecordQueueTimer === null ) {
- processPageRecordQueueTimer = vAPI.setTimeout(processPageRecordQueue, 1000);
- }
-};
+ var cookieLogEntryBuilder = [
+ '',
+ '{',
+ '',
+ '-cookie:',
+ '',
+ '}'
+ ];
-/******************************************************************************/
+ var recordPageCookie = function(pageStore, cookieKey) {
+ if ( vAPI.isBehindTheSceneTabId(pageStore.tabId) ) { return; }
-var cookieLogEntryBuilder = [
- '',
- '{',
- '',
- '-cookie:',
- '',
- '}'
-];
-
-var recordPageCookie = function(pageStore, cookieKey) {
- if ( vAPI.isBehindTheSceneTabId(pageStore.tabId) ) { return; }
-
- var cookieEntry = cookieDict.get(cookieKey);
- var pageHostname = pageStore.pageHostname;
- var block = ηm.mustBlock(pageHostname, cookieEntry.hostname, 'cookie');
-
- cookieLogEntryBuilder[0] = cookieURLFromCookieEntry(cookieEntry);
- cookieLogEntryBuilder[2] = cookieEntry.session ? 'session' : 'persistent';
- cookieLogEntryBuilder[4] = encodeURIComponent(cookieEntry.name);
-
- var cookieURL = cookieLogEntryBuilder.join('');
-
- // rhill 2013-11-20:
- // https://github.com/gorhill/httpswitchboard/issues/60
- // Need to URL-encode cookie name
- pageStore.recordRequest('cookie', cookieURL, block);
- ηm.logger.writeOne(pageStore.tabId, 'net', pageHostname, cookieURL, 'cookie', block);
-
- cookieEntry.usedOn.add(pageHostname);
-
- // rhill 2013-11-21:
- // https://github.com/gorhill/httpswitchboard/issues/65
- // Leave alone cookies from behind-the-scene requests if
- // behind-the-scene processing is disabled.
- if ( !block ) {
- return;
- }
- if ( !ηm.userSettings.deleteCookies ) {
- return;
- }
- removeCookieAsync(cookieKey);
-};
+ var cookieEntry = cookieDict.get(cookieKey);
+ var pageHostname = pageStore.pageHostname;
+ var block = ηm.mustBlock(pageHostname, cookieEntry.hostname, 'cookie');
-/******************************************************************************/
+ cookieLogEntryBuilder[0] = cookieURLFromCookieEntry(cookieEntry);
+ cookieLogEntryBuilder[2] = cookieEntry.session ? 'session' : 'persistent';
+ cookieLogEntryBuilder[4] = encodeURIComponent(cookieEntry.name);
-// Look for cookies to potentially remove for a specific web page
-
-var removePageCookiesAsync = function(pageStats) {
- // Hold onto pageStats objects so that it doesn't go away
- // before we handle the job.
- // rhill 2013-10-19: pageStats could be nil, for example, this can
- // happens if a file:// ... makes an xmlHttpRequest
- if ( !pageStats ) {
- return;
- }
- removePageCookiesQueue.set(pageStats.pageUrl, pageStats);
- if ( processPageRemoveQueueTimer === null ) {
- processPageRemoveQueueTimer = vAPI.setTimeout(processPageRemoveQueue, 15 * 1000);
- }
-};
+ var cookieURL = cookieLogEntryBuilder.join('');
-/******************************************************************************/
+ // rhill 2013-11-20:
+ // https://github.com/gorhill/httpswitchboard/issues/60
+ // Need to URL-encode cookie name
+ pageStore.recordRequest('cookie', cookieURL, block);
+ ηm.logger.writeOne(pageStore.tabId, 'net', pageHostname, cookieURL, 'cookie', block);
-// Candidate for removal
+ cookieEntry.usedOn.add(pageHostname);
-var removeCookieAsync = function(cookieKey) {
- removeCookieQueue.add(cookieKey);
-};
+ // rhill 2013-11-21:
+ // https://github.com/gorhill/httpswitchboard/issues/65
+ // Leave alone cookies from behind-the-scene requests if
+ // behind-the-scene processing is disabled.
+ if ( !block ) {
+ return;
+ }
+ if ( !ηm.userSettings.deleteCookies ) {
+ return;
+ }
+ removeCookieAsync(cookieKey);
+ };
-/******************************************************************************/
+ /******************************************************************************/
-var chromeCookieRemove = function(cookieEntry, name) {
- var url = cookieURLFromCookieEntry(cookieEntry);
- if ( url === '' ) {
- return;
- }
- var sessionCookieKey = cookieKeyFromCookieURL(url, 'session', name);
- var persistCookieKey = cookieKeyFromCookieURL(url, 'persistent', name);
- var callback = function(details) {
- var success = !!details;
- var template = success ? i18nCookieDeleteSuccess : i18nCookieDeleteFailure;
- if ( removeCookieFromDict(sessionCookieKey) ) {
- if ( success ) {
- ηm.cookieRemovedCounter += 1;
- }
- ηm.logger.writeOne('', 'info', 'cookie', template.replace('{{value}}', sessionCookieKey));
+ // Look for cookies to potentially remove for a specific web page
+
+ var removePageCookiesAsync = function(pageStats) {
+ // Hold onto pageStats objects so that it doesn't go away
+ // before we handle the job.
+ // rhill 2013-10-19: pageStats could be nil, for example, this can
+ // happens if a file:// ... makes an xmlHttpRequest
+ if ( !pageStats ) {
+ return;
}
- if ( removeCookieFromDict(persistCookieKey) ) {
- if ( success ) {
- ηm.cookieRemovedCounter += 1;
- }
- ηm.logger.writeOne('', 'info', 'cookie', template.replace('{{value}}', persistCookieKey));
+ removePageCookiesQueue.set(pageStats.pageUrl, pageStats);
+ if ( processPageRemoveQueueTimer === null ) {
+ processPageRemoveQueueTimer = vAPI.setTimeout(processPageRemoveQueue, 15 * 1000);
}
};
- vAPI.cookies.remove({ url: url, name: name }, callback);
-};
+ /******************************************************************************/
-var i18nCookieDeleteSuccess = vAPI.i18n('loggerEntryCookieDeleted');
-var i18nCookieDeleteFailure = vAPI.i18n('loggerEntryDeleteCookieError');
+ // Candidate for removal
-/******************************************************************************/
+ var removeCookieAsync = function(cookieKey) {
+ removeCookieQueue.add(cookieKey);
+ };
-var processPageRecordQueue = function() {
- processPageRecordQueueTimer = null;
+ /******************************************************************************/
- for ( var pageStore of recordPageCookiesQueue.values() ) {
- findAndRecordPageCookies(pageStore);
- }
- recordPageCookiesQueue.clear();
-};
+ var chromeCookieRemove = function(cookieEntry, name) {
+ var url = cookieURLFromCookieEntry(cookieEntry);
+ if ( url === '' ) {
+ return;
+ }
+ var sessionCookieKey = cookieKeyFromCookieURL(url, 'session', name);
+ var persistCookieKey = cookieKeyFromCookieURL(url, 'persistent', name);
+ var callback = function(details) {
+ var success = !!details;
+ var template = success ? i18nCookieDeleteSuccess : i18nCookieDeleteFailure;
+ if ( removeCookieFromDict(sessionCookieKey) ) {
+ if ( success ) {
+ ηm.cookieRemovedCounter += 1;
+ }
+ ηm.logger.writeOne('', 'info', 'cookie', template.replace('{{value}}', sessionCookieKey));
+ }
+ if ( removeCookieFromDict(persistCookieKey) ) {
+ if ( success ) {
+ ηm.cookieRemovedCounter += 1;
+ }
+ ηm.logger.writeOne('', 'info', 'cookie', template.replace('{{value}}', persistCookieKey));
+ }
+ };
-/******************************************************************************/
+ vAPI.cookies.remove({ url: url, name: name }, callback);
+ };
-var processPageRemoveQueue = function() {
- processPageRemoveQueueTimer = null;
+ var i18nCookieDeleteSuccess = vAPI.i18n('loggerEntryCookieDeleted');
+ var i18nCookieDeleteFailure = vAPI.i18n('loggerEntryDeleteCookieError');
- for ( var pageStore of removePageCookiesQueue.values() ) {
- findAndRemovePageCookies(pageStore);
- }
- removePageCookiesQueue.clear();
-};
+ /******************************************************************************/
-/******************************************************************************/
+ var processPageRecordQueue = function() {
+ processPageRecordQueueTimer = null;
-// Effectively remove cookies.
+ for ( var pageStore of recordPageCookiesQueue.values() ) {
+ findAndRecordPageCookies(pageStore);
+ }
+ recordPageCookiesQueue.clear();
+ };
-var processRemoveQueue = function() {
- var userSettings = ηm.userSettings;
- var deleteCookies = userSettings.deleteCookies;
+ /******************************************************************************/
- // Session cookies which timestamp is *after* tstampObsolete will
- // be left untouched
- // https://github.com/gorhill/httpswitchboard/issues/257
- var tstampObsolete = userSettings.deleteUnusedSessionCookies ?
- Date.now() - userSettings.deleteUnusedSessionCookiesAfter * 60 * 1000 :
- 0;
+ var processPageRemoveQueue = function() {
+ processPageRemoveQueueTimer = null;
- var srcHostnames;
- var cookieEntry;
+ for ( var pageStore of removePageCookiesQueue.values() ) {
+ findAndRemovePageCookies(pageStore);
+ }
+ removePageCookiesQueue.clear();
+ };
- for ( var cookieKey of removeCookieQueue ) {
- // rhill 2014-05-12: Apparently this can happen. I have to
- // investigate how (A session cookie has same name as a
- // persistent cookie?)
- cookieEntry = cookieDict.get(cookieKey);
- if ( cookieEntry === undefined ) { continue; }
+ /******************************************************************************/
+
+ // Effectively remove cookies.
+
+ var processRemoveQueue = function() {
+ var userSettings = ηm.userSettings;
+ var deleteCookies = userSettings.deleteCookies;
+
+ // Session cookies which timestamp is *after* tstampObsolete will
+ // be left untouched
+ // https://github.com/gorhill/httpswitchboard/issues/257
+ var tstampObsolete = userSettings.deleteUnusedSessionCookies ?
+ Date.now() - userSettings.deleteUnusedSessionCookiesAfter * 60 * 1000 :
+ 0;
+
+ var srcHostnames;
+ var cookieEntry;
+
+ for ( var cookieKey of removeCookieQueue ) {
+ // rhill 2014-05-12: Apparently this can happen. I have to
+ // investigate how (A session cookie has same name as a
+ // persistent cookie?)
+ cookieEntry = cookieDict.get(cookieKey);
+ if ( cookieEntry === undefined ) { continue; }
+
+ // Delete obsolete session cookies: enabled.
+ if ( tstampObsolete !== 0 && cookieEntry.session ) {
+ if ( cookieEntry.tstamp < tstampObsolete ) {
+ chromeCookieRemove(cookieEntry, cookieEntry.name);
+ continue;
+ }
+ }
- // Delete obsolete session cookies: enabled.
- if ( tstampObsolete !== 0 && cookieEntry.session ) {
- if ( cookieEntry.tstamp < tstampObsolete ) {
- chromeCookieRemove(cookieEntry, cookieEntry.name);
+ // Delete all blocked cookies: disabled.
+ if ( deleteCookies === false ) {
continue;
}
- }
-
- // Delete all blocked cookies: disabled.
- if ( deleteCookies === false ) {
- continue;
- }
- // Query scopes only if we are going to use them
- if ( srcHostnames === undefined ) {
- srcHostnames = ηm.tMatrix.extractAllSourceHostnames();
- }
+ // Query scopes only if we are going to use them
+ if ( srcHostnames === undefined ) {
+ srcHostnames = ηm.tMatrix.extractAllSourceHostnames();
+ }
- // Ensure cookie is not allowed on ALL current web pages: It can
- // happen that a cookie is blacklisted on one web page while
- // being whitelisted on another (because of per-page permissions).
- if ( canRemoveCookie(cookieKey, srcHostnames) ) {
- chromeCookieRemove(cookieEntry, cookieEntry.name);
+ // Ensure cookie is not allowed on ALL current web pages: It can
+ // happen that a cookie is blacklisted on one web page while
+ // being whitelisted on another (because of per-page permissions).
+ if ( canRemoveCookie(cookieKey, srcHostnames) ) {
+ chromeCookieRemove(cookieEntry, cookieEntry.name);
+ }
}
- }
- removeCookieQueue.clear();
+ removeCookieQueue.clear();
- vAPI.setTimeout(processRemoveQueue, processRemoveQueuePeriod);
-};
-
-/******************************************************************************/
+ vAPI.setTimeout(processRemoveQueue, processRemoveQueuePeriod);
+ };
-// Once in a while, we go ahead and clean everything that might have been
-// left behind.
-
-// Remove only some of the cookies which are candidate for removal: who knows,
-// maybe a user has 1000s of cookies sitting in his browser...
-
-var processClean = function() {
- var us = ηm.userSettings;
- if ( us.deleteCookies || us.deleteUnusedSessionCookies ) {
- var cookieKeys = Array.from(cookieDict.keys()),
- len = cookieKeys.length,
- step, offset, n;
- if ( len > 25 ) {
- step = len / 25;
- offset = Math.floor(Math.random() * len);
- n = 25;
- } else {
- step = 1;
- offset = 0;
- n = len;
- }
- var i = offset;
- while ( n-- ) {
- removeCookieAsync(cookieKeys[Math.floor(i % len)]);
- i += step;
+ /******************************************************************************/
+
+ // Once in a while, we go ahead and clean everything that might have been
+ // left behind.
+
+ // Remove only some of the cookies which are candidate for removal: who knows,
+ // maybe a user has 1000s of cookies sitting in his browser...
+
+ var processClean = function() {
+ var us = ηm.userSettings;
+ if ( us.deleteCookies || us.deleteUnusedSessionCookies ) {
+ var cookieKeys = Array.from(cookieDict.keys()),
+ len = cookieKeys.length,
+ step, offset, n;
+ if ( len > 25 ) {
+ step = len / 25;
+ offset = Math.floor(Math.random() * len);
+ n = 25;
+ } else {
+ step = 1;
+ offset = 0;
+ n = len;
+ }
+ var i = offset;
+ while ( n-- ) {
+ removeCookieAsync(cookieKeys[Math.floor(i % len)]);
+ i += step;
+ }
}
- }
- vAPI.setTimeout(processClean, processCleanPeriod);
-};
+ vAPI.setTimeout(processClean, processCleanPeriod);
+ };
-/******************************************************************************/
+ /******************************************************************************/
-var findAndRecordPageCookies = function(pageStore) {
- for ( var cookieKey of cookieDict.keys() ) {
- if ( cookieMatchDomains(cookieKey, pageStore.allHostnamesString) ) {
- recordPageCookie(pageStore, cookieKey);
+ var findAndRecordPageCookies = function(pageStore) {
+ for ( var cookieKey of cookieDict.keys() ) {
+ if ( cookieMatchDomains(cookieKey, pageStore.allHostnamesString) ) {
+ recordPageCookie(pageStore, cookieKey);
+ }
}
- }
-};
+ };
-/******************************************************************************/
+ /******************************************************************************/
-var findAndRemovePageCookies = function(pageStore) {
- for ( var cookieKey of cookieDict.keys() ) {
- if ( cookieMatchDomains(cookieKey, pageStore.allHostnamesString) ) {
- removeCookieAsync(cookieKey);
+ var findAndRemovePageCookies = function(pageStore) {
+ for ( var cookieKey of cookieDict.keys() ) {
+ if ( cookieMatchDomains(cookieKey, pageStore.allHostnamesString) ) {
+ removeCookieAsync(cookieKey);
+ }
}
- }
-};
+ };
-/******************************************************************************/
+ /******************************************************************************/
-var canRemoveCookie = function(cookieKey, srcHostnames) {
- var cookieEntry = cookieDict.get(cookieKey);
- if ( cookieEntry === undefined ) { return false; }
+ var canRemoveCookie = function(cookieKey, srcHostnames) {
+ var cookieEntry = cookieDict.get(cookieKey);
+ if ( cookieEntry === undefined ) { return false; }
- var cookieHostname = cookieEntry.hostname;
- var srcHostname;
+ var cookieHostname = cookieEntry.hostname;
+ var srcHostname;
- for ( srcHostname of cookieEntry.usedOn ) {
- if ( ηm.mustAllow(srcHostname, cookieHostname, 'cookie') ) {
- return false;
- }
- }
- // Maybe there is a scope in which the cookie is 1st-party-allowed.
- // For example, if I am logged in into `github.com`, I do not want to be
- // logged out just because I did not yet open a `github.com` page after
- // re-starting the browser.
- srcHostname = cookieHostname;
- var pos;
- for (;;) {
- if ( srcHostnames.has(srcHostname) ) {
+ for ( srcHostname of cookieEntry.usedOn ) {
if ( ηm.mustAllow(srcHostname, cookieHostname, 'cookie') ) {
return false;
}
}
- if ( srcHostname === cookieEntry.domain ) {
- break;
- }
- pos = srcHostname.indexOf('.');
- if ( pos === -1 ) {
- break;
+ // Maybe there is a scope in which the cookie is 1st-party-allowed.
+ // For example, if I am logged in into `github.com`, I do not want to be
+ // logged out just because I did not yet open a `github.com` page after
+ // re-starting the browser.
+ srcHostname = cookieHostname;
+ var pos;
+ for (;;) {
+ if ( srcHostnames.has(srcHostname) ) {
+ if ( ηm.mustAllow(srcHostname, cookieHostname, 'cookie') ) {
+ return false;
+ }
+ }
+ if ( srcHostname === cookieEntry.domain ) {
+ break;
+ }
+ pos = srcHostname.indexOf('.');
+ if ( pos === -1 ) {
+ break;
+ }
+ srcHostname = srcHostname.slice(pos + 1);
}
- srcHostname = srcHostname.slice(pos + 1);
- }
- return true;
-};
+ return true;
+ };
-/******************************************************************************/
+ /******************************************************************************/
+
+ // Listen to any change in cookieland, we will update page stats accordingly.
-// Listen to any change in cookieland, we will update page stats accordingly.
-
-vAPI.cookies.onChanged = function(cookie) {
- // rhill 2013-12-11: If cookie value didn't change, no need to record.
- // https://github.com/gorhill/httpswitchboard/issues/79
- var cookieKey = cookieKeyFromCookie(cookie);
- var cookieEntry = cookieDict.get(cookieKey);
- if ( cookieEntry === undefined ) {
- cookieEntry = addCookieToDict(cookie);
- } else {
- cookieEntry.tstamp = Date.now();
- if ( cookie.value === cookieEntry.value ) { return; }
- cookieEntry.value = cookie.value;
- }
-
- // Go through all pages and update if needed, as one cookie can be used
- // by many web pages, so they need to be recorded for all these pages.
- var pageStores = ηm.pageStores;
- var pageStore;
- for ( var tabId in pageStores ) {
- if ( pageStores.hasOwnProperty(tabId) === false ) {
- continue;
+ vAPI.cookies.onChanged = function(cookie) {
+ // rhill 2013-12-11: If cookie value didn't change, no need to record.
+ // https://github.com/gorhill/httpswitchboard/issues/79
+ var cookieKey = cookieKeyFromCookie(cookie);
+ var cookieEntry = cookieDict.get(cookieKey);
+ if ( cookieEntry === undefined ) {
+ cookieEntry = addCookieToDict(cookie);
+ } else {
+ cookieEntry.tstamp = Date.now();
+ if ( cookie.value === cookieEntry.value ) { return; }
+ cookieEntry.value = cookie.value;
}
- pageStore = pageStores[tabId];
- if ( !cookieMatchDomains(cookieKey, pageStore.allHostnamesString) ) {
- continue;
+
+ // Go through all pages and update if needed, as one cookie can be used
+ // by many web pages, so they need to be recorded for all these pages.
+ var pageStores = ηm.pageStores;
+ var pageStore;
+ for ( var tabId in pageStores ) {
+ if ( pageStores.hasOwnProperty(tabId) === false ) {
+ continue;
+ }
+ pageStore = pageStores[tabId];
+ if ( !cookieMatchDomains(cookieKey, pageStore.allHostnamesString) ) {
+ continue;
+ }
+ recordPageCookie(pageStore, cookieKey);
}
- recordPageCookie(pageStore, cookieKey);
- }
-};
+ };
-/******************************************************************************/
+ /******************************************************************************/
-// Listen to any change in cookieland, we will update page stats accordingly.
+ // Listen to any change in cookieland, we will update page stats accordingly.
-vAPI.cookies.onRemoved = function(cookie) {
- var cookieKey = cookieKeyFromCookie(cookie);
- if ( removeCookieFromDict(cookieKey) ) {
- ηm.logger.writeOne('', 'info', 'cookie', i18nCookieDeleteSuccess.replace('{{value}}', cookieKey));
- }
-};
+ vAPI.cookies.onRemoved = function(cookie) {
+ var cookieKey = cookieKeyFromCookie(cookie);
+ if ( removeCookieFromDict(cookieKey) ) {
+ ηm.logger.writeOne('', 'info', 'cookie', i18nCookieDeleteSuccess.replace('{{value}}', cookieKey));
+ }
+ };
-/******************************************************************************/
+ /******************************************************************************/
-// Listen to any change in cookieland, we will update page stats accordingly.
+ // Listen to any change in cookieland, we will update page stats accordingly.
-vAPI.cookies.onAllRemoved = function() {
- for ( var cookieKey of cookieDict.keys() ) {
- if ( removeCookieFromDict(cookieKey) ) {
- ηm.logger.writeOne('', 'info', 'cookie', i18nCookieDeleteSuccess.replace('{{value}}', cookieKey));
+ vAPI.cookies.onAllRemoved = function() {
+ for ( var cookieKey of cookieDict.keys() ) {
+ if ( removeCookieFromDict(cookieKey) ) {
+ ηm.logger.writeOne('', 'info', 'cookie', i18nCookieDeleteSuccess.replace('{{value}}', cookieKey));
+ }
}
- }
-};
+ };
-/******************************************************************************/
+ /******************************************************************************/
-vAPI.cookies.getAll(addCookiesToDict);
-vAPI.cookies.start();
+ vAPI.cookies.getAll(addCookiesToDict);
+ vAPI.cookies.start();
-vAPI.setTimeout(processRemoveQueue, processRemoveQueuePeriod);
-vAPI.setTimeout(processClean, processCleanPeriod);
+ vAPI.setTimeout(processRemoveQueue, processRemoveQueuePeriod);
+ vAPI.setTimeout(processClean, processCleanPeriod);
-/******************************************************************************/
+ /******************************************************************************/
-// Expose only what is necessary
+ // Expose only what is necessary
-return {
- recordPageCookies: recordPageCookiesAsync,
- removePageCookies: removePageCookiesAsync
-};
+ return {
+ recordPageCookies: recordPageCookiesAsync,
+ removePageCookies: removePageCookiesAsync
+ };
-/******************************************************************************/
+ /******************************************************************************/
})();
/******************************************************************************/
-
diff --git a/js/dashboard-common.js b/js/dashboard-common.js
index abb53b6..68ba7d5 100644
--- a/js/dashboard-common.js
+++ b/js/dashboard-common.js
@@ -27,18 +27,18 @@
uDom.onLoad(function() {
-/******************************************************************************/
+ /******************************************************************************/
-// Open links in the proper window
-uDom('a').attr('target', '_blank');
-uDom('a[href*="dashboard.html"]').attr('target', '_parent');
-uDom('.whatisthis').on('click', function() {
- uDom(this).parent()
- .descendants('.whatisthis-expandable')
- .toggleClass('whatisthis-expanded');
-});
+ // Open links in the proper window
+ uDom('a').attr('target', '_blank');
+ uDom('a[href*="dashboard.html"]').attr('target', '_parent');
+ uDom('.whatisthis').on('click', function() {
+ uDom(this).parent()
+ .descendants('.whatisthis-expandable')
+ .toggleClass('whatisthis-expanded');
+ });
-/******************************************************************************/
+ /******************************************************************************/
});
diff --git a/js/hosts-files.js b/js/hosts-files.js
index de7f1ee..8c65648 100644
--- a/js/hosts-files.js
+++ b/js/hosts-files.js
@@ -29,364 +29,363 @@
(function() {
-/******************************************************************************/
-
-var listDetails = {},
- lastUpdateTemplateString = vAPI.i18n('hostsFilesLastUpdate'),
- hostsFilesSettingsHash,
- reValidExternalList = /[a-z-]+:\/\/\S*\/\S+/;
-
-/******************************************************************************/
-
-vAPI.messaging.addListener(function onMessage(msg) {
- switch ( msg.what ) {
- case 'assetUpdated':
- updateAssetStatus(msg);
- break;
- case 'assetsUpdated':
- document.body.classList.remove('updating');
- break;
- case 'loadHostsFilesCompleted':
- renderHostsFiles();
- break;
- default:
- break;
- }
-});
-
-/******************************************************************************/
-
-var renderNumber = function(value) {
- return value.toLocaleString();
-};
+ /******************************************************************************/
+
+ var listDetails = {},
+ lastUpdateTemplateString = vAPI.i18n('hostsFilesLastUpdate'),
+ hostsFilesSettingsHash,
+ reValidExternalList = /[a-z-]+:\/\/\S*\/\S+/;
+
+ /******************************************************************************/
+
+ vAPI.messaging.addListener(function onMessage(msg) {
+ switch ( msg.what ) {
+ case 'assetUpdated':
+ updateAssetStatus(msg);
+ break;
+ case 'assetsUpdated':
+ document.body.classList.remove('updating');
+ break;
+ case 'loadHostsFilesCompleted':
+ renderHostsFiles();
+ break;
+ default:
+ break;
+ }
+ });
-/******************************************************************************/
+ /******************************************************************************/
-var renderHostsFiles = function(soft) {
- var listEntryTemplate = uDom('#templates .listEntry'),
- listStatsTemplate = vAPI.i18n('hostsFilesPerFileStats'),
- renderElapsedTimeToString = vAPI.i18n.renderElapsedTimeToString,
- reExternalHostFile = /^https?:/;
-
- // Assemble a pretty list name if possible
- var listNameFromListKey = function(listKey) {
- var list = listDetails.current[listKey] || listDetails.available[listKey];
- var listTitle = list ? list.title : '';
- if ( listTitle === '' ) { return listKey; }
- return listTitle;
+ var renderNumber = function(value) {
+ return value.toLocaleString();
};
- var liFromListEntry = function(listKey, li) {
- var entry = listDetails.available[listKey],
- elem;
- if ( !li ) {
- li = listEntryTemplate.clone().nodeAt(0);
- }
- if ( li.getAttribute('data-listkey') !== listKey ) {
- li.setAttribute('data-listkey', listKey);
- elem = li.querySelector('input[type="checkbox"]');
- elem.checked = entry.off !== true;
- elem = li.querySelector('a:nth-of-type(1)');
- elem.setAttribute('href', 'asset-viewer.html?url=' + encodeURI(listKey));
- elem.setAttribute('type', 'text/html');
- elem.textContent = listNameFromListKey(listKey);
- li.classList.remove('toRemove');
- if ( entry.supportName ) {
- li.classList.add('support');
- elem = li.querySelector('a.support');
- elem.setAttribute('href', entry.supportURL);
- elem.setAttribute('title', entry.supportName);
- } else {
- li.classList.remove('support');
+ /******************************************************************************/
+
+ var renderHostsFiles = function(soft) {
+ var listEntryTemplate = uDom('#templates .listEntry'),
+ listStatsTemplate = vAPI.i18n('hostsFilesPerFileStats'),
+ renderElapsedTimeToString = vAPI.i18n.renderElapsedTimeToString,
+ reExternalHostFile = /^https?:/;
+
+ // Assemble a pretty list name if possible
+ var listNameFromListKey = function(listKey) {
+ var list = listDetails.current[listKey] || listDetails.available[listKey];
+ var listTitle = list ? list.title : '';
+ if ( listTitle === '' ) { return listKey; }
+ return listTitle;
+ };
+
+ var liFromListEntry = function(listKey, li) {
+ var entry = listDetails.available[listKey],
+ elem;
+ if ( !li ) {
+ li = listEntryTemplate.clone().nodeAt(0);
}
- if ( entry.external ) {
- li.classList.add('external');
- } else {
- li.classList.remove('external');
+ if ( li.getAttribute('data-listkey') !== listKey ) {
+ li.setAttribute('data-listkey', listKey);
+ elem = li.querySelector('input[type="checkbox"]');
+ elem.checked = entry.off !== true;
+ elem = li.querySelector('a:nth-of-type(1)');
+ elem.setAttribute('href', 'asset-viewer.html?url=' + encodeURI(listKey));
+ elem.setAttribute('type', 'text/html');
+ elem.textContent = listNameFromListKey(listKey);
+ li.classList.remove('toRemove');
+ if ( entry.supportName ) {
+ li.classList.add('support');
+ elem = li.querySelector('a.support');
+ elem.setAttribute('href', entry.supportURL);
+ elem.setAttribute('title', entry.supportName);
+ } else {
+ li.classList.remove('support');
+ }
+ if ( entry.external ) {
+ li.classList.add('external');
+ } else {
+ li.classList.remove('external');
+ }
+ if ( entry.instructionURL ) {
+ li.classList.add('mustread');
+ elem = li.querySelector('a.mustread');
+ elem.setAttribute('href', entry.instructionURL);
+ } else {
+ li.classList.remove('mustread');
+ }
}
- if ( entry.instructionURL ) {
- li.classList.add('mustread');
- elem = li.querySelector('a.mustread');
- elem.setAttribute('href', entry.instructionURL);
- } else {
- li.classList.remove('mustread');
+ // https://github.com/gorhill/uBlock/issues/1429
+ if ( !soft ) {
+ elem = li.querySelector('input[type="checkbox"]');
+ elem.checked = entry.off !== true;
}
- }
- // https://github.com/gorhill/uBlock/issues/1429
- if ( !soft ) {
- elem = li.querySelector('input[type="checkbox"]');
- elem.checked = entry.off !== true;
- }
- elem = li.querySelector('span.counts');
- var text = '';
- if ( !isNaN(+entry.entryUsedCount) && !isNaN(+entry.entryCount) ) {
- text = listStatsTemplate
- .replace('{{used}}', renderNumber(entry.off ? 0 : entry.entryUsedCount))
- .replace('{{total}}', renderNumber(entry.entryCount));
- }
- elem.textContent = text;
- // https://github.com/chrisaljoudi/uBlock/issues/104
- var asset = listDetails.cache[listKey] || {};
- var remoteURL = asset.remoteURL;
- li.classList.toggle(
- 'unsecure',
- typeof remoteURL === 'string' && remoteURL.lastIndexOf('http:', 0) === 0
- );
- li.classList.toggle('failed', asset.error !== undefined);
- li.classList.toggle('obsolete', asset.obsolete === true);
- li.classList.toggle('cached', asset.cached === true && asset.writeTime > 0);
- if ( asset.cached ) {
- li.querySelector('.status.cache').setAttribute(
- 'title',
- lastUpdateTemplateString.replace('{{ago}}', renderElapsedTimeToString(asset.writeTime))
+ elem = li.querySelector('span.counts');
+ var text = '';
+ if ( !isNaN(+entry.entryUsedCount) && !isNaN(+entry.entryCount) ) {
+ text = listStatsTemplate
+ .replace('{{used}}', renderNumber(entry.off ? 0 : entry.entryUsedCount))
+ .replace('{{total}}', renderNumber(entry.entryCount));
+ }
+ elem.textContent = text;
+ // https://github.com/chrisaljoudi/uBlock/issues/104
+ var asset = listDetails.cache[listKey] || {};
+ var remoteURL = asset.remoteURL;
+ li.classList.toggle(
+ 'unsecure',
+ typeof remoteURL === 'string' && remoteURL.lastIndexOf('http:', 0) === 0
);
- }
- li.classList.remove('discard');
- return li;
- };
+ li.classList.toggle('failed', asset.error !== undefined);
+ li.classList.toggle('obsolete', asset.obsolete === true);
+ li.classList.toggle('cached', asset.cached === true && asset.writeTime > 0);
+ if ( asset.cached ) {
+ li.querySelector('.status.cache').setAttribute(
+ 'title',
+ lastUpdateTemplateString.replace('{{ago}}', renderElapsedTimeToString(asset.writeTime))
+ );
+ }
+ li.classList.remove('discard');
+ return li;
+ };
+
+ var onListsReceived = function(details) {
+ // Before all, set context vars
+ listDetails = details;
+
+ // Incremental rendering: this will allow us to easily discard unused
+ // DOM list entries.
+ uDom('#lists .listEntry').addClass('discard');
+
+ var availableLists = details.available,
+ listKeys = Object.keys(details.available);
+
+ // Sort works this way:
+ // - Send /^https?:/ items at the end (custom hosts file URL)
+ listKeys.sort(function(a, b) {
+ var ta = availableLists[a].title || a,
+ tb = availableLists[b].title || b;
+ if ( reExternalHostFile.test(ta) === reExternalHostFile.test(tb) ) {
+ return ta.localeCompare(tb);
+ }
+ return reExternalHostFile.test(tb) ? -1 : 1;
+ });
+
+ var ulList = document.querySelector('#lists');
+ for ( var i = 0; i < listKeys.length; i++ ) {
+ var liEntry = liFromListEntry(listKeys[i], ulList.children[i]);
+ if ( liEntry.parentElement === null ) {
+ ulList.appendChild(liEntry);
+ }
+ }
- var onListsReceived = function(details) {
- // Before all, set context vars
- listDetails = details;
+ uDom('#lists .listEntry.discard').remove();
+ uDom('#listsOfBlockedHostsPrompt').text(
+ vAPI.i18n('hostsFilesStats').replace(
+ '{{blockedHostnameCount}}',
+ renderNumber(details.blockedHostnameCount)
+ )
+ );
+ uDom('#autoUpdate').prop('checked', listDetails.autoUpdate === true);
- // Incremental rendering: this will allow us to easily discard unused
- // DOM list entries.
- uDom('#lists .listEntry').addClass('discard');
+ if ( !soft ) {
+ hostsFilesSettingsHash = hashFromCurrentFromSettings();
+ }
+ renderWidgets();
+ };
- var availableLists = details.available,
- listKeys = Object.keys(details.available);
+ vAPI.messaging.send('hosts-files.js', { what: 'getLists' }, onListsReceived);
+ };
- // Sort works this way:
- // - Send /^https?:/ items at the end (custom hosts file URL)
- listKeys.sort(function(a, b) {
- var ta = availableLists[a].title || a,
- tb = availableLists[b].title || b;
- if ( reExternalHostFile.test(ta) === reExternalHostFile.test(tb) ) {
- return ta.localeCompare(tb);
- }
- return reExternalHostFile.test(tb) ? -1 : 1;
- });
+ /******************************************************************************/
- var ulList = document.querySelector('#lists');
- for ( var i = 0; i < listKeys.length; i++ ) {
- var liEntry = liFromListEntry(listKeys[i], ulList.children[i]);
- if ( liEntry.parentElement === null ) {
- ulList.appendChild(liEntry);
- }
- }
+ var renderWidgets = function() {
+ uDom('#buttonUpdate').toggleClass('disabled', document.querySelector('body:not(.updating) #lists .listEntry.obsolete > input[type="checkbox"]:checked') === null);
+ uDom('#buttonPurgeAll').toggleClass('disabled', document.querySelector('#lists .listEntry.cached') === null);
+ uDom('#buttonApply').toggleClass('disabled', hostsFilesSettingsHash === hashFromCurrentFromSettings());
+ };
- uDom('#lists .listEntry.discard').remove();
- uDom('#listsOfBlockedHostsPrompt').text(
- vAPI.i18n('hostsFilesStats').replace(
- '{{blockedHostnameCount}}',
- renderNumber(details.blockedHostnameCount)
- )
- );
- uDom('#autoUpdate').prop('checked', listDetails.autoUpdate === true);
+ /******************************************************************************/
- if ( !soft ) {
- hostsFilesSettingsHash = hashFromCurrentFromSettings();
+ var updateAssetStatus = function(details) {
+ var li = document.querySelector('#lists .listEntry[data-listkey="' + details.key + '"]');
+ if ( li === null ) { return; }
+ li.classList.toggle('failed', !!details.failed);
+ li.classList.toggle('obsolete', !details.cached);
+ li.classList.toggle('cached', !!details.cached);
+ if ( details.cached ) {
+ li.querySelector('.status.cache').setAttribute(
+ 'title',
+ lastUpdateTemplateString.replace(
+ '{{ago}}',
+ vAPI.i18n.renderElapsedTimeToString(Date.now())
+ )
+ );
}
renderWidgets();
};
- vAPI.messaging.send('hosts-files.js', { what: 'getLists' }, onListsReceived);
-};
-
-/******************************************************************************/
-
-var renderWidgets = function() {
- uDom('#buttonUpdate').toggleClass('disabled', document.querySelector('body:not(.updating) #lists .listEntry.obsolete > input[type="checkbox"]:checked') === null);
- uDom('#buttonPurgeAll').toggleClass('disabled', document.querySelector('#lists .listEntry.cached') === null);
- uDom('#buttonApply').toggleClass('disabled', hostsFilesSettingsHash === hashFromCurrentFromSettings());
-};
-
-/******************************************************************************/
-
-var updateAssetStatus = function(details) {
- var li = document.querySelector('#lists .listEntry[data-listkey="' + details.key + '"]');
- if ( li === null ) { return; }
- li.classList.toggle('failed', !!details.failed);
- li.classList.toggle('obsolete', !details.cached);
- li.classList.toggle('cached', !!details.cached);
- if ( details.cached ) {
- li.querySelector('.status.cache').setAttribute(
- 'title',
- lastUpdateTemplateString.replace(
- '{{ago}}',
- vAPI.i18n.renderElapsedTimeToString(Date.now())
- )
- );
- }
- renderWidgets();
-};
-
-/*******************************************************************************
+ /*******************************************************************************
Compute a hash from all the settings affecting how filter lists are loaded
in memory.
-**/
-
-var hashFromCurrentFromSettings = function() {
- var hash = [],
- listHash = [],
- listEntries = document.querySelectorAll('#lists .listEntry[data-listkey]:not(.toRemove)'),
- liEntry,
- i = listEntries.length;
- while ( i-- ) {
- liEntry = listEntries[i];
- if ( liEntry.querySelector('input[type="checkbox"]:checked') !== null ) {
- listHash.push(liEntry.getAttribute('data-listkey'));
+ **/
+
+ var hashFromCurrentFromSettings = function() {
+ var hash = [],
+ listHash = [],
+ listEntries = document.querySelectorAll('#lists .listEntry[data-listkey]:not(.toRemove)'),
+ liEntry,
+ i = listEntries.length;
+ while ( i-- ) {
+ liEntry = listEntries[i];
+ if ( liEntry.querySelector('input[type="checkbox"]:checked') !== null ) {
+ listHash.push(liEntry.getAttribute('data-listkey'));
+ }
}
- }
- hash.push(
- listHash.sort().join(),
- reValidExternalList.test(document.getElementById('externalHostsFiles').value),
- document.querySelector('#lists .listEntry.toRemove') !== null
- );
- return hash.join();
-};
+ hash.push(
+ listHash.sort().join(),
+ reValidExternalList.test(document.getElementById('externalHostsFiles').value),
+ document.querySelector('#lists .listEntry.toRemove') !== null
+ );
+ return hash.join();
+ };
-/******************************************************************************/
+ /******************************************************************************/
-var onHostsFilesSettingsChanged = function() {
- renderWidgets();
-};
+ var onHostsFilesSettingsChanged = function() {
+ renderWidgets();
+ };
-/******************************************************************************/
+ /******************************************************************************/
-var onRemoveExternalHostsFile = function(ev) {
- var liEntry = uDom(this).ancestors('[data-listkey]'),
- listKey = liEntry.attr('data-listkey');
- if ( listKey ) {
- liEntry.toggleClass('toRemove');
- renderWidgets();
- }
- ev.preventDefault();
-};
+ var onRemoveExternalHostsFile = function(ev) {
+ var liEntry = uDom(this).ancestors('[data-listkey]'),
+ listKey = liEntry.attr('data-listkey');
+ if ( listKey ) {
+ liEntry.toggleClass('toRemove');
+ renderWidgets();
+ }
+ ev.preventDefault();
+ };
-/******************************************************************************/
+ /******************************************************************************/
-var onPurgeClicked = function() {
- var button = uDom(this),
- liEntry = button.ancestors('[data-listkey]'),
- listKey = liEntry.attr('data-listkey');
- if ( !listKey ) { return; }
+ var onPurgeClicked = function() {
+ var button = uDom(this),
+ liEntry = button.ancestors('[data-listkey]'),
+ listKey = liEntry.attr('data-listkey');
+ if ( !listKey ) { return; }
- vAPI.messaging.send('hosts-files.js', { what: 'purgeCache', assetKey: listKey });
- liEntry.addClass('obsolete');
- liEntry.removeClass('cached');
+ vAPI.messaging.send('hosts-files.js', { what: 'purgeCache', assetKey: listKey });
+ liEntry.addClass('obsolete');
+ liEntry.removeClass('cached');
- if ( liEntry.descendants('input').first().prop('checked') ) {
- renderWidgets();
- }
-};
+ if ( liEntry.descendants('input').first().prop('checked') ) {
+ renderWidgets();
+ }
+ };
-/******************************************************************************/
+ /******************************************************************************/
+
+ var selectHostsFiles = function(callback) {
+ // Hosts files to select
+ var toSelect = [],
+ liEntries = document.querySelectorAll('#lists .listEntry[data-listkey]:not(.toRemove)'),
+ i = liEntries.length,
+ liEntry;
+ while ( i-- ) {
+ liEntry = liEntries[i];
+ if ( liEntry.querySelector('input[type="checkbox"]:checked') !== null ) {
+ toSelect.push(liEntry.getAttribute('data-listkey'));
+ }
+ }
-var selectHostsFiles = function(callback) {
- // Hosts files to select
- var toSelect = [],
- liEntries = document.querySelectorAll('#lists .listEntry[data-listkey]:not(.toRemove)'),
- i = liEntries.length,
- liEntry;
- while ( i-- ) {
- liEntry = liEntries[i];
- if ( liEntry.querySelector('input[type="checkbox"]:checked') !== null ) {
- toSelect.push(liEntry.getAttribute('data-listkey'));
+ // External hosts files to remove
+ var toRemove = [];
+ liEntries = document.querySelectorAll('#lists .listEntry.toRemove[data-listkey]');
+ i = liEntries.length;
+ while ( i-- ) {
+ toRemove.push(liEntries[i].getAttribute('data-listkey'));
}
- }
-
- // External hosts files to remove
- var toRemove = [];
- liEntries = document.querySelectorAll('#lists .listEntry.toRemove[data-listkey]');
- i = liEntries.length;
- while ( i-- ) {
- toRemove.push(liEntries[i].getAttribute('data-listkey'));
- }
-
- // External hosts files to import
- var externalListsElem = document.getElementById('externalHostsFiles'),
- toImport = externalListsElem.value.trim();
- externalListsElem.value = '';
-
- vAPI.messaging.send(
- 'hosts-files.js',
- {
- what: 'selectHostsFiles',
- toSelect: toSelect,
- toImport: toImport,
- toRemove: toRemove
- },
- callback
- );
-
- hostsFilesSettingsHash = hashFromCurrentFromSettings();
-};
-/******************************************************************************/
+ // External hosts files to import
+ var externalListsElem = document.getElementById('externalHostsFiles'),
+ toImport = externalListsElem.value.trim();
+ externalListsElem.value = '';
+
+ vAPI.messaging.send(
+ 'hosts-files.js',
+ {
+ what: 'selectHostsFiles',
+ toSelect: toSelect,
+ toImport: toImport,
+ toRemove: toRemove
+ },
+ callback
+ );
-var buttonApplyHandler = function() {
- uDom('#buttonApply').removeClass('enabled');
- selectHostsFiles(function() {
- vAPI.messaging.send('hosts-files.js', { what: 'reloadHostsFiles' });
- });
- renderWidgets();
-};
+ hostsFilesSettingsHash = hashFromCurrentFromSettings();
+ };
-/******************************************************************************/
+ /******************************************************************************/
-var buttonUpdateHandler = function() {
- uDom('#buttonUpdate').removeClass('enabled');
- selectHostsFiles(function() {
- document.body.classList.add('updating');
- vAPI.messaging.send('hosts-files.js', { what: 'forceUpdateAssets' });
+ var buttonApplyHandler = function() {
+ uDom('#buttonApply').removeClass('enabled');
+ selectHostsFiles(function() {
+ vAPI.messaging.send('hosts-files.js', { what: 'reloadHostsFiles' });
+ });
renderWidgets();
- });
- renderWidgets();
-};
+ };
-/******************************************************************************/
+ /******************************************************************************/
-var buttonPurgeAllHandler = function() {
- uDom('#buttonPurgeAll').removeClass('enabled');
- vAPI.messaging.send(
- 'hosts-files.js',
- { what: 'purgeAllCaches' },
- function() {
- renderHostsFiles(true);
- }
- );
-};
+ var buttonUpdateHandler = function() {
+ uDom('#buttonUpdate').removeClass('enabled');
+ selectHostsFiles(function() {
+ document.body.classList.add('updating');
+ vAPI.messaging.send('hosts-files.js', { what: 'forceUpdateAssets' });
+ renderWidgets();
+ });
+ renderWidgets();
+ };
-/******************************************************************************/
+ /******************************************************************************/
-var autoUpdateCheckboxChanged = function() {
- vAPI.messaging.send(
- 'hosts-files.js',
- {
- what: 'userSettings',
- name: 'autoUpdate',
- value: this.checked
- }
- );
-};
+ var buttonPurgeAllHandler = function() {
+ uDom('#buttonPurgeAll').removeClass('enabled');
+ vAPI.messaging.send(
+ 'hosts-files.js',
+ { what: 'purgeAllCaches' },
+ function() {
+ renderHostsFiles(true);
+ }
+ );
+ };
-/******************************************************************************/
+ /******************************************************************************/
+
+ var autoUpdateCheckboxChanged = function() {
+ vAPI.messaging.send(
+ 'hosts-files.js',
+ {
+ what: 'userSettings',
+ name: 'autoUpdate',
+ value: this.checked
+ }
+ );
+ };
-uDom('#autoUpdate').on('change', autoUpdateCheckboxChanged);
-uDom('#buttonApply').on('click', buttonApplyHandler);
-uDom('#buttonUpdate').on('click', buttonUpdateHandler);
-uDom('#buttonPurgeAll').on('click', buttonPurgeAllHandler);
-uDom('#lists').on('change', '.listEntry > input', onHostsFilesSettingsChanged);
-uDom('#lists').on('click', '.listEntry > a.remove', onRemoveExternalHostsFile);
-uDom('#lists').on('click', 'span.cache', onPurgeClicked);
-uDom('#externalHostsFiles').on('input', onHostsFilesSettingsChanged);
+ /******************************************************************************/
-renderHostsFiles();
+ uDom('#autoUpdate').on('change', autoUpdateCheckboxChanged);
+ uDom('#buttonApply').on('click', buttonApplyHandler);
+ uDom('#buttonUpdate').on('click', buttonUpdateHandler);
+ uDom('#buttonPurgeAll').on('click', buttonPurgeAllHandler);
+ uDom('#lists').on('change', '.listEntry > input', onHostsFilesSettingsChanged);
+ uDom('#lists').on('click', '.listEntry > a.remove', onRemoveExternalHostsFile);
+ uDom('#lists').on('click', 'span.cache', onPurgeClicked);
+ uDom('#externalHostsFiles').on('input', onHostsFilesSettingsChanged);
-/******************************************************************************/
+ renderHostsFiles();
-})();
+ /******************************************************************************/
+})();
diff --git a/js/i18n.js b/js/i18n.js
index 95e6d4c..1f1dbb7 100644
--- a/js/i18n.js
+++ b/js/i18n.js
@@ -32,178 +32,178 @@
(function() {
-/******************************************************************************/
-
-// https://github.com/gorhill/uBlock/issues/2084
-// Anything else than <a>, <b>, <code>, <em>, <i>, <input>, and <span> will
-// be rendered as plain text.
-// For <input>, only the type attribute is allowed.
-// For <a>, only href attribute must be present, and it MUST starts with
-// `https://`, and includes no single- or double-quotes.
-// No HTML entities are allowed, there is code to handle existing HTML
-// entities already present in translation files until they are all gone.
-
-var reSafeTags = /^([\s\S]*?)<(b|blockquote|code|em|i|kbd|span|sup)>(.+?)<\/\2>([\s\S]*)$/,
- reSafeInput = /^([\s\S]*?)<(input type="[^"]+")>(.*?)([\s\S]*)$/,
- reInput = /^input type=(['"])([a-z]+)\1$/,
- reSafeLink = /^([\s\S]*?)<(a href=['"]https?:\/\/[^'" <>]+['"])>(.+?)<\/a>([\s\S]*)$/,
- reLink = /^a href=(['"])(https?:\/\/[^'"]+)\1$/;
-
-var safeTextToTagNode = function(text) {
- var matches, node;
- if ( text.lastIndexOf('a ', 0) === 0 ) {
- matches = reLink.exec(text);
- if ( matches === null ) { return null; }
- node = document.createElement('a');
- node.setAttribute('href', matches[2]);
- return node;
- }
- if ( text.lastIndexOf('input ', 0) === 0 ) {
- matches = reInput.exec(text);
- if ( matches === null ) { return null; }
- node = document.createElement('input');
- node.setAttribute('type', matches[2]);
- return node;
- }
- // Firefox extension validator warns if using a variable as argument for
- // document.createElement().
- switch ( text ) {
- case 'b':
- return document.createElement('b');
- case 'blockquote':
- return document.createElement('blockquote');
- case 'code':
- return document.createElement('code');
- case 'em':
- return document.createElement('em');
- case 'i':
- return document.createElement('i');
- case 'kbd':
- return document.createElement('kbd');
- case 'span':
- return document.createElement('span');
- case 'sup':
- return document.createElement('sup');
- default:
- break;
- }
-};
-
-var safeTextToTextNode = function(text) {
- // TODO: remove once no more HTML entities in translation files.
- if ( text.indexOf('&') !== -1 ) {
- text = text.replace(/&ldquo;/g, '“')
- .replace(/&rdquo;/g, '”')
- .replace(/&lsquo;/g, '‘')
- .replace(/&rsquo;/g, '’');
- }
- return document.createTextNode(text);
-};
-
-var safeTextToDOM = function(text, parent) {
- if ( text === '' ) { return; }
- // Fast path (most common).
- if ( text.indexOf('<') === -1 ) {
- return parent.appendChild(safeTextToTextNode(text));
- }
- // Slow path.
- // `<p>` no longer allowed. Code below can be remove once all <p>'s are
- // gone from translation files.
- text = text.replace(/^<p>|<\/p>/g, '')
- .replace(/<p>/g, '\n\n');
- // Parse allowed HTML tags.
- var matches,
- matches1 = reSafeTags.exec(text),
- matches2 = reSafeLink.exec(text);
- if ( matches1 !== null && matches2 !== null ) {
- matches = matches1.index < matches2.index ? matches1 : matches2;
- } else if ( matches1 !== null ) {
- matches = matches1;
- } else if ( matches2 !== null ) {
- matches = matches2;
- } else {
- matches = reSafeInput.exec(text);
- }
- if ( matches === null ) {
- parent.appendChild(safeTextToTextNode(text));
- return;
- }
- safeTextToDOM(matches[1], parent);
- var node = safeTextToTagNode(matches[2]) || parent;
- safeTextToDOM(matches[3], node);
- parent.appendChild(node);
- safeTextToDOM(matches[4], parent);
-};
-
-/******************************************************************************/
-
-// Helper to deal with the i18n'ing of HTML files.
-vAPI.i18n.render = function(context) {
- var docu = document,
- root = context || docu,
- elems, n, i, elem, text;
-
- elems = root.querySelectorAll('[data-i18n]');
- n = elems.length;
- for ( i = 0; i < n; i++ ) {
- elem = elems[i];
- text = vAPI.i18n(elem.getAttribute('data-i18n'));
- if ( !text ) { continue; }
- // TODO: remove once it's all replaced with <input type="...">
- if ( text.indexOf('{') !== -1 ) {
- text = text.replace(/\{\{input:([^}]+)\}\}/g, '<input type="$1">');
+ /******************************************************************************/
+
+ // https://github.com/gorhill/uBlock/issues/2084
+ // Anything else than <a>, <b>, <code>, <em>, <i>, <input>, and <span> will
+ // be rendered as plain text.
+ // For <input>, only the type attribute is allowed.
+ // For <a>, only href attribute must be present, and it MUST starts with
+ // `https://`, and includes no single- or double-quotes.
+ // No HTML entities are allowed, there is code to handle existing HTML
+ // entities already present in translation files until they are all gone.
+
+ var reSafeTags = /^([\s\S]*?)<(b|blockquote|code|em|i|kbd|span|sup)>(.+?)<\/\2>([\s\S]*)$/,
+ reSafeInput = /^([\s\S]*?)<(input type="[^"]+")>(.*?)([\s\S]*)$/,
+ reInput = /^input type=(['"])([a-z]+)\1$/,
+ reSafeLink = /^([\s\S]*?)<(a href=['"]https?:\/\/[^'" <>]+['"])>(.+?)<\/a>([\s\S]*)$/,
+ reLink = /^a href=(['"])(https?:\/\/[^'"]+)\1$/;
+
+ var safeTextToTagNode = function(text) {
+ var matches, node;
+ if ( text.lastIndexOf('a ', 0) === 0 ) {
+ matches = reLink.exec(text);
+ if ( matches === null ) { return null; }
+ node = document.createElement('a');
+ node.setAttribute('href', matches[2]);
+ return node;
}
- safeTextToDOM(text, elem);
- }
-
- uDom('[title]', context).forEach(function(elem) {
- var title = vAPI.i18n(elem.attr('title'));
- if ( title ) {
- elem.attr('title', title);
+ if ( text.lastIndexOf('input ', 0) === 0 ) {
+ matches = reInput.exec(text);
+ if ( matches === null ) { return null; }
+ node = document.createElement('input');
+ node.setAttribute('type', matches[2]);
+ return node;
+ }
+ // Firefox extension validator warns if using a variable as argument for
+ // document.createElement().
+ switch ( text ) {
+ case 'b':
+ return document.createElement('b');
+ case 'blockquote':
+ return document.createElement('blockquote');
+ case 'code':
+ return document.createElement('code');
+ case 'em':
+ return document.createElement('em');
+ case 'i':
+ return document.createElement('i');
+ case 'kbd':
+ return document.createElement('kbd');
+ case 'span':
+ return document.createElement('span');
+ case 'sup':
+ return document.createElement('sup');
+ default:
+ break;
+ }
+ };
+
+ var safeTextToTextNode = function(text) {
+ // TODO: remove once no more HTML entities in translation files.
+ if ( text.indexOf('&') !== -1 ) {
+ text = text.replace(/&ldquo;/g, '“')
+ .replace(/&rdquo;/g, '”')
+ .replace(/&lsquo;/g, '‘')
+ .replace(/&rsquo;/g, '’');
+ }
+ return document.createTextNode(text);
+ };
+
+ var safeTextToDOM = function(text, parent) {
+ if ( text === '' ) { return; }
+ // Fast path (most common).
+ if ( text.indexOf('<') === -1 ) {
+ return parent.appendChild(safeTextToTextNode(text));
+ }
+ // Slow path.
+ // `<p>` no longer allowed. Code below can be remove once all <p>'s are
+ // gone from translation files.
+ text = text.replace(/^<p>|<\/p>/g, '')
+ .replace(/<p>/g, '\n\n');
+ // Parse allowed HTML tags.
+ var matches,
+ matches1 = reSafeTags.exec(text),
+ matches2 = reSafeLink.exec(text);
+ if ( matches1 !== null && matches2 !== null ) {
+ matches = matches1.index < matches2.index ? matches1 : matches2;
+ } else if ( matches1 !== null ) {
+ matches = matches1;
+ } else if ( matches2 !== null ) {
+ matches = matches2;
+ } else {
+ matches = reSafeInput.exec(text);
+ }
+ if ( matches === null ) {
+ parent.appendChild(safeTextToTextNode(text));
+ return;
+ }
+ safeTextToDOM(matches[1], parent);
+ var node = safeTextToTagNode(matches[2]) || parent;
+ safeTextToDOM(matches[3], node);
+ parent.appendChild(node);
+ safeTextToDOM(matches[4], parent);
+ };
+
+ /******************************************************************************/
+
+ // Helper to deal with the i18n'ing of HTML files.
+ vAPI.i18n.render = function(context) {
+ var docu = document,
+ root = context || docu,
+ elems, n, i, elem, text;
+
+ elems = root.querySelectorAll('[data-i18n]');
+ n = elems.length;
+ for ( i = 0; i < n; i++ ) {
+ elem = elems[i];
+ text = vAPI.i18n(elem.getAttribute('data-i18n'));
+ if ( !text ) { continue; }
+ // TODO: remove once it's all replaced with <input type="...">
+ if ( text.indexOf('{') !== -1 ) {
+ text = text.replace(/\{\{input:([^}]+)\}\}/g, '<input type="$1">');
+ }
+ safeTextToDOM(text, elem);
}
- });
-
- uDom('[placeholder]', context).forEach(function(elem) {
- elem.attr('placeholder', vAPI.i18n(elem.attr('placeholder')));
- });
-
- uDom('[data-i18n-tip]', context).forEach(function(elem) {
- elem.attr(
- 'data-tip',
- vAPI.i18n(elem.attr('data-i18n-tip'))
- .replace(/<br>/g, '\n')
- .replace(/\n{3,}/g, '\n\n')
- );
- });
-};
-
-vAPI.i18n.render();
-
-/******************************************************************************/
-vAPI.i18n.renderElapsedTimeToString = function(tstamp) {
- var value = (Date.now() - tstamp) / 60000;
- if ( value < 2 ) {
- return vAPI.i18n('elapsedOneMinuteAgo');
- }
- if ( value < 60 ) {
- return vAPI.i18n('elapsedManyMinutesAgo').replace('{{value}}', Math.floor(value).toLocaleString());
- }
- value /= 60;
- if ( value < 2 ) {
- return vAPI.i18n('elapsedOneHourAgo');
- }
- if ( value < 24 ) {
- return vAPI.i18n('elapsedManyHoursAgo').replace('{{value}}', Math.floor(value).toLocaleString());
- }
- value /= 24;
- if ( value < 2 ) {
- return vAPI.i18n('elapsedOneDayAgo');
- }
- return vAPI.i18n('elapsedManyDaysAgo').replace('{{value}}', Math.floor(value).toLocaleString());
-};
+ uDom('[title]', context).forEach(function(elem) {
+ var title = vAPI.i18n(elem.attr('title'));
+ if ( title ) {
+ elem.attr('title', title);
+ }
+ });
+
+ uDom('[placeholder]', context).forEach(function(elem) {
+ elem.attr('placeholder', vAPI.i18n(elem.attr('placeholder')));
+ });
+
+ uDom('[data-i18n-tip]', context).forEach(function(elem) {
+ elem.attr(
+ 'data-tip',
+ vAPI.i18n(elem.attr('data-i18n-tip'))
+ .replace(/<br>/g, '\n')
+ .replace(/\n{3,}/g, '\n\n')
+ );
+ });
+ };
+
+ vAPI.i18n.render();
+
+ /******************************************************************************/
+
+ vAPI.i18n.renderElapsedTimeToString = function(tstamp) {
+ var value = (Date.now() - tstamp) / 60000;
+ if ( value < 2 ) {
+ return vAPI.i18n('elapsedOneMinuteAgo');
+ }
+ if ( value < 60 ) {
+ return vAPI.i18n('elapsedManyMinutesAgo').replace('{{value}}', Math.floor(value).toLocaleString());
+ }
+ value /= 60;
+ if ( value < 2 ) {
+ return vAPI.i18n('elapsedOneHourAgo');
+ }
+ if ( value < 24 ) {
+ return vAPI.i18n('elapsedManyHoursAgo').replace('{{value}}', Math.floor(value).toLocaleString());
+ }
+ value /= 24;
+ if ( value < 2 ) {
+ return vAPI.i18n('elapsedOneDayAgo');
+ }
+ return vAPI.i18n('elapsedManyDaysAgo').replace('{{value}}', Math.floor(value).toLocaleString());
+ };
-/******************************************************************************/
+ /******************************************************************************/
})();
diff --git a/js/liquid-dict.js b/js/liquid-dict.js
index 8e38a75..3284aed 100644
--- a/js/liquid-dict.js
+++ b/js/liquid-dict.js
@@ -27,176 +27,176 @@
ηMatrix.LiquidDict = (function() {
-/******************************************************************************/
-
-var LiquidDict = function() {
- this.dict = {};
- this.count = 0;
- this.duplicateCount = 0;
- this.bucketCount = 0;
- this.frozenBucketCount = 0;
-
- // Somewhat arbitrary: I need to come up with hard data to know at which
- // point binary search is better than indexOf.
- this.cutoff = 500;
-};
-
-/******************************************************************************/
-
-var meltBucket = function(ldict, len, bucket) {
- ldict.frozenBucketCount -= 1;
- var map = {};
- if ( bucket.charAt(0) === ' ' ) {
- bucket.trim().split(' ').map(function(k) {
- map[k] = true;
- });
- } else {
- var offset = 0;
- while ( offset < bucket.length ) {
- map[bucket.substring(offset, len)] = true;
- offset += len;
+ /******************************************************************************/
+
+ var LiquidDict = function() {
+ this.dict = {};
+ this.count = 0;
+ this.duplicateCount = 0;
+ this.bucketCount = 0;
+ this.frozenBucketCount = 0;
+
+ // Somewhat arbitrary: I need to come up with hard data to know at which
+ // point binary search is better than indexOf.
+ this.cutoff = 500;
+ };
+
+ /******************************************************************************/
+
+ var meltBucket = function(ldict, len, bucket) {
+ ldict.frozenBucketCount -= 1;
+ var map = {};
+ if ( bucket.charAt(0) === ' ' ) {
+ bucket.trim().split(' ').map(function(k) {
+ map[k] = true;
+ });
+ } else {
+ var offset = 0;
+ while ( offset < bucket.length ) {
+ map[bucket.substring(offset, len)] = true;
+ offset += len;
+ }
}
- }
- return map;
-};
-
-/******************************************************************************/
-
-var melt = function(ldict) {
- var buckets = ldict.dict;
- var bucket;
- for ( var key in buckets ) {
- bucket = buckets[key];
- if ( typeof bucket === 'string' ) {
- buckets[key] = meltBucket(ldict, key.charCodeAt(0) & 0xFF, bucket);
+ return map;
+ };
+
+ /******************************************************************************/
+
+ var melt = function(ldict) {
+ var buckets = ldict.dict;
+ var bucket;
+ for ( var key in buckets ) {
+ bucket = buckets[key];
+ if ( typeof bucket === 'string' ) {
+ buckets[key] = meltBucket(ldict, key.charCodeAt(0) & 0xFF, bucket);
+ }
}
- }
-};
+ };
-/******************************************************************************/
-
-var freezeBucket = function(ldict, bucket) {
- ldict.frozenBucketCount += 1;
- var words = Object.keys(bucket);
- var wordLen = words[0].length;
- if ( wordLen * words.length < ldict.cutoff ) {
- return ' ' + words.join(' ') + ' ';
- }
- return words.sort().join('');
-};
+ /******************************************************************************/
-/******************************************************************************/
-
-// How the key is derived dictates the number and size of buckets.
-//
-// http://jsperf.com/makekey-concat-vs-join/3
-//
-// Question: Why is using a prototyped function better than a standalone
-// helper function?
-
-LiquidDict.prototype.makeKey = function(word) {
- var len = word.length;
- if ( len > 255 ) {
- len = 255;
- }
- var i = len >> 2;
- return String.fromCharCode(
- (word.charCodeAt( 0) & 0x03) << 14 |
- (word.charCodeAt( i) & 0x03) << 12 |
- (word.charCodeAt( i+i) & 0x03) << 10 |
- (word.charCodeAt(i+i+i) & 0x03) << 8 |
- len
- );
-};
+ var freezeBucket = function(ldict, bucket) {
+ ldict.frozenBucketCount += 1;
+ var words = Object.keys(bucket);
+ var wordLen = words[0].length;
+ if ( wordLen * words.length < ldict.cutoff ) {
+ return ' ' + words.join(' ') + ' ';
+ }
+ return words.sort().join('');
+ };
+
+ /******************************************************************************/
+
+ // How the key is derived dictates the number and size of buckets.
+ //
+ // http://jsperf.com/makekey-concat-vs-join/3
+ //
+ // Question: Why is using a prototyped function better than a standalone
+ // helper function?
+
+ LiquidDict.prototype.makeKey = function(word) {
+ var len = word.length;
+ if ( len > 255 ) {
+ len = 255;
+ }
+ var i = len >> 2;
+ return String.fromCharCode(
+ (word.charCodeAt( 0) & 0x03) << 14 |
+ (word.charCodeAt( i) & 0x03) << 12 |
+ (word.charCodeAt( i+i) & 0x03) << 10 |
+ (word.charCodeAt(i+i+i) & 0x03) << 8 |
+ len
+ );
+ };
+
+ /******************************************************************************/
+
+ LiquidDict.prototype.test = function(word) {
+ var key = this.makeKey(word);
+ var bucket = this.dict[key];
+ if ( bucket === undefined ) {
+ return false;
+ }
+ if ( typeof bucket === 'object' ) {
+ return bucket[word] !== undefined;
+ }
+ if ( bucket.charAt(0) === ' ' ) {
+ return bucket.indexOf(' ' + word + ' ') >= 0;
+ }
+ // binary search
+ var len = word.length;
+ var left = 0;
+ // http://jsperf.com/or-vs-floor/3
+ var right = ~~(bucket.length / len + 0.5);
+ var i, needle;
+ while ( left < right ) {
+ i = left + right >> 1;
+ needle = bucket.substr( len * i, len );
+ if ( word < needle ) {
+ right = i;
+ } else if ( word > needle ) {
+ left = i + 1;
+ } else {
+ return true;
+ }
+ }
+ return false;
+ };
-/******************************************************************************/
+ /******************************************************************************/
-LiquidDict.prototype.test = function(word) {
- var key = this.makeKey(word);
- var bucket = this.dict[key];
- if ( bucket === undefined ) {
- return false;
- }
- if ( typeof bucket === 'object' ) {
- return bucket[word] !== undefined;
- }
- if ( bucket.charAt(0) === ' ' ) {
- return bucket.indexOf(' ' + word + ' ') >= 0;
- }
- // binary search
- var len = word.length;
- var left = 0;
- // http://jsperf.com/or-vs-floor/3
- var right = ~~(bucket.length / len + 0.5);
- var i, needle;
- while ( left < right ) {
- i = left + right >> 1;
- needle = bucket.substr( len * i, len );
- if ( word < needle ) {
- right = i;
- } else if ( word > needle ) {
- left = i + 1;
- } else {
+ LiquidDict.prototype.add = function(word) {
+ var key = this.makeKey(word);
+ if ( key === undefined ) {
+ return false;
+ }
+ var bucket = this.dict[key];
+ if ( bucket === undefined ) {
+ this.dict[key] = bucket = {};
+ this.bucketCount += 1;
+ bucket[word] = true;
+ this.count += 1;
return true;
+ } else if ( typeof bucket === 'string' ) {
+ this.dict[key] = bucket = meltBucket(this, word.len, bucket);
}
- }
- return false;
-};
-
-/******************************************************************************/
-
-LiquidDict.prototype.add = function(word) {
- var key = this.makeKey(word);
- if ( key === undefined ) {
+ if ( bucket[word] === undefined ) {
+ bucket[word] = true;
+ this.count += 1;
+ return true;
+ }
+ this.duplicateCount += 1;
return false;
- }
- var bucket = this.dict[key];
- if ( bucket === undefined ) {
- this.dict[key] = bucket = {};
- this.bucketCount += 1;
- bucket[word] = true;
- this.count += 1;
- return true;
- } else if ( typeof bucket === 'string' ) {
- this.dict[key] = bucket = meltBucket(this, word.len, bucket);
- }
- if ( bucket[word] === undefined ) {
- bucket[word] = true;
- this.count += 1;
- return true;
- }
- this.duplicateCount += 1;
- return false;
-};
-
-/******************************************************************************/
-
-LiquidDict.prototype.freeze = function() {
- var buckets = this.dict;
- var bucket;
- for ( var key in buckets ) {
- bucket = buckets[key];
- if ( typeof bucket === 'object' ) {
- buckets[key] = freezeBucket(this, bucket);
+ };
+
+ /******************************************************************************/
+
+ LiquidDict.prototype.freeze = function() {
+ var buckets = this.dict;
+ var bucket;
+ for ( var key in buckets ) {
+ bucket = buckets[key];
+ if ( typeof bucket === 'object' ) {
+ buckets[key] = freezeBucket(this, bucket);
+ }
}
- }
-};
+ };
-/******************************************************************************/
+ /******************************************************************************/
-LiquidDict.prototype.reset = function() {
- this.dict = {};
- this.count = 0;
- this.duplicateCount = 0;
- this.bucketCount = 0;
- this.frozenBucketCount = 0;
-};
+ LiquidDict.prototype.reset = function() {
+ this.dict = {};
+ this.count = 0;
+ this.duplicateCount = 0;
+ this.bucketCount = 0;
+ this.frozenBucketCount = 0;
+ };
-/******************************************************************************/
+ /******************************************************************************/
-return LiquidDict;
+ return LiquidDict;
-/******************************************************************************/
+ /******************************************************************************/
})();
diff --git a/js/logger-ui.js b/js/logger-ui.js
index ed2c313..cb8e4f1 100644
--- a/js/logger-ui.js
+++ b/js/logger-ui.js
@@ -27,883 +27,900 @@
/******************************************************************************/
-(function() {
+(function () {
-/******************************************************************************/
+ /******************************************************************************/
-var tbody = document.querySelector('#content tbody');
-var trJunkyard = [];
-var tdJunkyard = [];
-var firstVarDataCol = 2; // currently, column 2 (0-based index)
-var lastVarDataIndex = 3; // currently, d0-d3
-var maxEntries = 0;
-var noTabId = '';
-var allTabIds = {};
-var allTabIdsToken;
-var ownerId = Date.now();
-
-var emphasizeTemplate = document.querySelector('#emphasizeTemplate > span');
-var hiddenTemplate = document.querySelector('#hiddenTemplate > span');
-
-var prettyRequestTypes = {
- 'main_frame': 'doc',
- 'stylesheet': 'css',
- 'sub_frame': 'frame',
- 'xmlhttprequest': 'xhr'
-};
-
-var dontEmphasizeSet = new Set([
- 'COOKIE',
- 'CSP',
- 'REFERER'
-]);
+ var tbody = document.querySelector('#content tbody');
+ var trJunkyard = [];
+ var tdJunkyard = [];
+ var firstVarDataCol = 2; // currently, column 2 (0-based index)
+ var lastVarDataIndex = 3; // currently, d0-d3
+ var maxEntries = 0;
+ var noTabId = '';
+ var allTabIds = {};
+ var allTabIdsToken;
+ var ownerId = Date.now();
+
+ var emphasizeTemplate = document.querySelector('#emphasizeTemplate > span');
+ var hiddenTemplate = document.querySelector('#hiddenTemplate > span');
+
+ var prettyRequestTypes = {
+ 'main_frame': 'doc',
+ 'stylesheet': 'css',
+ 'sub_frame': 'frame',
+ 'xmlhttprequest': 'xhr'
+ };
-/******************************************************************************/
+ var dontEmphasizeSet = new Set([
+ 'COOKIE',
+ 'CSP',
+ 'REFERER'
+ ]);
-// Adjust top padding of content table, to match that of toolbar height.
+ /******************************************************************************/
-document.getElementById('content').style.setProperty(
- 'margin-top',
- document.getElementById('toolbar').clientHeight + 'px'
-);
+ // Adjust top padding of content table, to match that of toolbar height.
-/******************************************************************************/
-
-var classNameFromTabId = function(tabId) {
- if ( tabId === noTabId ) {
- return 'tab_bts';
- }
- if ( tabId !== '' ) {
- return 'tab_' + tabId;
- }
- return '';
-};
+ document.getElementById('content').style.setProperty(
+ 'margin-top',
+ document.getElementById('toolbar').clientHeight + 'px'
+ );
-/******************************************************************************/
+ /******************************************************************************/
-// Emphasize hostname and cookie name.
+ var classNameFromTabId = function (tabId) {
+ if (tabId === noTabId) {
+ return 'tab_bts';
+ }
+ if (tabId !== '') {
+ return 'tab_' + tabId;
+ }
+ return '';
+ };
-var emphasizeCookie = function(s) {
- var pnode = emphasizeHostname(s);
- if ( pnode.childNodes.length !== 3 ) {
- return pnode;
- }
- var prefix = '-cookie:';
- var text = pnode.childNodes[2].textContent;
- var beg = text.indexOf(prefix);
- if ( beg === -1 ) {
- return pnode;
- }
- beg += prefix.length;
- var end = text.indexOf('}', beg);
- if ( end === -1 ) {
+ /******************************************************************************/
+
+ // Emphasize hostname and cookie name.
+
+ var emphasizeCookie = function (s) {
+ var pnode = emphasizeHostname(s);
+ if (pnode.childNodes.length !== 3) {
+ return pnode;
+ }
+ var prefix = '-cookie:';
+ var text = pnode.childNodes[2].textContent;
+ var beg = text.indexOf(prefix);
+ if (beg === -1) {
+ return pnode;
+ }
+ beg += prefix.length;
+ var end = text.indexOf('}', beg);
+ if (end === -1) {
+ return pnode;
+ }
+ var cnode = emphasizeTemplate.cloneNode(true);
+ cnode.childNodes[0].textContent = text.slice(0, beg);
+ cnode.childNodes[1].textContent = text.slice(beg, end);
+ cnode.childNodes[2].textContent = text.slice(end);
+ pnode.replaceChild(cnode.childNodes[0], pnode.childNodes[2]);
+ pnode.appendChild(cnode.childNodes[0]);
+ pnode.appendChild(cnode.childNodes[0]);
return pnode;
- }
- var cnode = emphasizeTemplate.cloneNode(true);
- cnode.childNodes[0].textContent = text.slice(0, beg);
- cnode.childNodes[1].textContent = text.slice(beg, end);
- cnode.childNodes[2].textContent = text.slice(end);
- pnode.replaceChild(cnode.childNodes[0], pnode.childNodes[2]);
- pnode.appendChild(cnode.childNodes[0]);
- pnode.appendChild(cnode.childNodes[0]);
- return pnode;
-};
+ };
-/******************************************************************************/
+ /******************************************************************************/
-// Emphasize hostname in URL.
+ // Emphasize hostname in URL.
-var emphasizeHostname = function(url) {
- var hnbeg = url.indexOf('://');
- if ( hnbeg === -1 ) {
- return document.createTextNode(url);
- }
- hnbeg += 3;
+ var emphasizeHostname = function (url) {
+ var hnbeg = url.indexOf('://');
+ if (hnbeg === -1) {
+ return document.createTextNode(url);
+ }
+ hnbeg += 3;
- var hnend = url.indexOf('/', hnbeg);
- if ( hnend === -1 ) {
- hnend = url.slice(hnbeg).search(/\?#/);
- if ( hnend !== -1 ) {
- hnend += hnbeg;
- } else {
- hnend = url.length;
+ var hnend = url.indexOf('/', hnbeg);
+ if (hnend === -1) {
+ hnend = url.slice(hnbeg).search(/\?#/);
+ if (hnend !== -1) {
+ hnend += hnbeg;
+ } else {
+ hnend = url.length;
+ }
}
- }
- var node = emphasizeTemplate.cloneNode(true);
- node.childNodes[0].textContent = url.slice(0, hnbeg);
- node.childNodes[1].textContent = url.slice(hnbeg, hnend);
- node.childNodes[2].textContent = url.slice(hnend);
- return node;
-};
+ var node = emphasizeTemplate.cloneNode(true);
+ node.childNodes[0].textContent = url.slice(0, hnbeg);
+ node.childNodes[1].textContent = url.slice(hnbeg, hnend);
+ node.childNodes[2].textContent = url.slice(hnend);
+ return node;
+ };
-/******************************************************************************/
+ /******************************************************************************/
-var createCellAt = function(tr, index) {
- var td = tr.cells[index];
- var mustAppend = !td;
- if ( mustAppend ) {
- td = tdJunkyard.pop();
- }
- if ( td ) {
- td.removeAttribute('colspan');
- td.textContent = '';
- } else {
- td = document.createElement('td');
- }
- if ( mustAppend ) {
- tr.appendChild(td);
- }
- return td;
-};
+ var createCellAt = function (tr, index) {
+ var td = tr.cells[index];
+ var mustAppend = !td;
+ if (mustAppend) {
+ td = tdJunkyard.pop();
+ }
+ if (td) {
+ td.removeAttribute('colspan');
+ td.textContent = '';
+ } else {
+ td = document.createElement('td');
+ }
+ if (mustAppend) {
+ tr.appendChild(td);
+ }
+ return td;
+ };
-/******************************************************************************/
+ /******************************************************************************/
-var createRow = function(layout) {
- var tr = trJunkyard.pop();
- if ( tr ) {
- tr.className = '';
- } else {
- tr = document.createElement('tr');
- }
- for ( var index = 0; index < firstVarDataCol; index++ ) {
- createCellAt(tr, index);
- }
- var i = 1, span = 1, td;
- for (;;) {
- td = createCellAt(tr, index);
- if ( i === lastVarDataIndex ) {
- break;
- }
- if ( layout.charAt(i) !== '1' ) {
- span += 1;
+ var createRow = function (layout) {
+ var tr = trJunkyard.pop();
+ if (tr) {
+ tr.className = '';
} else {
- if ( span !== 1 ) {
- td.setAttribute('colspan', span);
- }
- index += 1;
- span = 1;
- }
- i += 1;
- }
- if ( span !== 1 ) {
- td.setAttribute('colspan', span);
- }
- index += 1;
- while ( (td = tr.cells[index]) ) {
- tdJunkyard.push(tr.removeChild(td));
- }
- return tr;
-};
-
-/******************************************************************************/
+ tr = document.createElement('tr');
+ }
+ for (var index = 0; index < firstVarDataCol; index++) {
+ createCellAt(tr, index);
+ }
+ var i = 1,
+ span = 1,
+ td;
+ for (;;) {
+ td = createCellAt(tr, index);
+ if (i === lastVarDataIndex) {
+ break;
+ }
+ if (layout.charAt(i) !== '1') {
+ span += 1;
+ } else {
+ if (span !== 1) {
+ td.setAttribute('colspan', span);
+ }
+ index += 1;
+ span = 1;
+ }
+ i += 1;
+ }
+ if (span !== 1) {
+ td.setAttribute('colspan', span);
+ }
+ index += 1;
+ while ((td = tr.cells[index])) {
+ tdJunkyard.push(tr.removeChild(td));
+ }
+ return tr;
+ };
-var createHiddenTextNode = function(text) {
- var node = hiddenTemplate.cloneNode(true);
- node.textContent = text;
- return node;
-};
+ /******************************************************************************/
-/******************************************************************************/
+ var createHiddenTextNode = function (text) {
+ var node = hiddenTemplate.cloneNode(true);
+ node.textContent = text;
+ return node;
+ };
-var padTo2 = function(v) {
- return v < 10 ? '0' + v : v;
-};
+ /******************************************************************************/
-/******************************************************************************/
+ var padTo2 = function (v) {
+ return v < 10 ? '0' + v : v;
+ };
-var createGap = function(tabId, url) {
- var tr = createRow('1');
- tr.classList.add('doc');
- tr.classList.add('tab');
- tr.classList.add('canMtx');
- tr.classList.add('tab_' + tabId);
- tr.cells[firstVarDataCol].textContent = url;
- tbody.insertBefore(tr, tbody.firstChild);
-};
+ /******************************************************************************/
-/******************************************************************************/
+ var createGap = function (tabId, url) {
+ var tr = createRow('1');
+ tr.classList.add('doc');
+ tr.classList.add('tab');
+ tr.classList.add('canMtx');
+ tr.classList.add('tab_' + tabId);
+ tr.cells[firstVarDataCol].textContent = url;
+ tbody.insertBefore(tr, tbody.firstChild);
+ };
-var renderLogEntry = function(entry) {
- var tr;
- var fvdc = firstVarDataCol;
+ /******************************************************************************/
- switch ( entry.cat ) {
- case 'error':
- case 'info':
- tr = createRow('1');
- if ( entry.d0 === 'cookie' ) {
- tr.cells[fvdc].appendChild(emphasizeCookie(entry.d1));
- } else {
- tr.cells[fvdc].textContent = entry.d0;
- }
- break;
+ var renderLogEntry = function (entry) {
+ var tr;
+ var fvdc = firstVarDataCol;
- case 'net':
- tr = createRow('111');
- tr.classList.add('canMtx');
- // If the request is that of a root frame, insert a gap in the table
- // in order to visually separate entries for different documents.
- if ( entry.d2 === 'doc' && entry.tab !== noTabId ) {
- createGap(entry.tab, entry.d1);
- }
- if ( entry.d3 ) {
- tr.classList.add('blocked');
- tr.cells[fvdc].textContent = '--';
- } else {
- tr.cells[fvdc].textContent = '';
+ switch (entry.cat) {
+ case 'error':
+ case 'info':
+ tr = createRow('1');
+ if (entry.d0 === 'cookie') {
+ tr.cells[fvdc].appendChild(emphasizeCookie(entry.d1));
+ } else {
+ tr.cells[fvdc].textContent = entry.d0;
+ }
+ break;
+
+ case 'net':
+ tr = createRow('111');
+ tr.classList.add('canMtx');
+ // If the request is that of a root frame, insert a gap in the table
+ // in order to visually separate entries for different documents.
+ if (entry.d2 === 'doc' && entry.tab !== noTabId) {
+ createGap(entry.tab, entry.d1);
+ }
+ if (entry.d3) {
+ tr.classList.add('blocked');
+ tr.cells[fvdc].textContent = '--';
+ } else {
+ tr.cells[fvdc].textContent = '';
+ }
+ tr.cells[fvdc + 1].textContent = (prettyRequestTypes[entry.d2] || entry.d2);
+ if (dontEmphasizeSet.has(entry.d2)) {
+ tr.cells[fvdc + 2].textContent = entry.d1;
+ } else if (entry.d2 === 'cookie') {
+ tr.cells[fvdc + 2].appendChild(emphasizeCookie(entry.d1));
+ } else {
+ tr.cells[fvdc + 2].appendChild(emphasizeHostname(entry.d1));
+ }
+ break;
+
+ default:
+ tr = createRow('1');
+ tr.cells[fvdc].textContent = entry.d0;
+ break;
+ }
+
+ // Fields common to all rows.
+ var time = logDate;
+ time.setTime(entry.tstamp - logDateTimezoneOffset);
+ tr.cells[0].textContent = padTo2(time.getUTCHours()) + ':' +
+ padTo2(time.getUTCMinutes()) + ':' +
+ padTo2(time.getSeconds());
+
+ if (entry.tab) {
+ tr.classList.add('tab');
+ tr.classList.add(classNameFromTabId(entry.tab));
+ if (entry.tab === noTabId) {
+ tr.cells[1].appendChild(createHiddenTextNode('bts'));
+ }
}
- tr.cells[fvdc+1].textContent = (prettyRequestTypes[entry.d2] || entry.d2);
- if ( dontEmphasizeSet.has(entry.d2) ) {
- tr.cells[fvdc+2].textContent = entry.d1;
- } else if ( entry.d2 === 'cookie' ) {
- tr.cells[fvdc+2].appendChild(emphasizeCookie(entry.d1));
- } else {
- tr.cells[fvdc+2].appendChild(emphasizeHostname(entry.d1));
+ if (entry.cat !== '') {
+ tr.classList.add('cat_' + entry.cat);
}
- break;
- default:
- tr = createRow('1');
- tr.cells[fvdc].textContent = entry.d0;
- break;
- }
+ rowFilterer.filterOne(tr, true);
- // Fields common to all rows.
- var time = logDate;
- time.setTime(entry.tstamp - logDateTimezoneOffset);
- tr.cells[0].textContent = padTo2(time.getUTCHours()) + ':' +
- padTo2(time.getUTCMinutes()) + ':' +
- padTo2(time.getSeconds());
+ tbody.insertBefore(tr, tbody.firstChild);
+ };
- if ( entry.tab ) {
- tr.classList.add('tab');
- tr.classList.add(classNameFromTabId(entry.tab));
- if ( entry.tab === noTabId ) {
- tr.cells[1].appendChild(createHiddenTextNode('bts'));
+ // Reuse date objects.
+ var logDate = new Date(),
+ logDateTimezoneOffset = logDate.getTimezoneOffset() * 60000;
+
+ /******************************************************************************/
+
+ var renderLogEntries = function (response) {
+ var entries = response.entries;
+ if (entries.length === 0) {
+ return;
}
- }
- if ( entry.cat !== '' ) {
- tr.classList.add('cat_' + entry.cat);
- }
- rowFilterer.filterOne(tr, true);
+ // Preserve scroll position
+ var height = tbody.offsetHeight;
+
+ var tabIds = response.tabIds;
+ var n = entries.length;
+ var entry;
+ for (var i = 0; i < n; i++) {
+ entry = entries[i];
+ // Unlikely, but it may happen
+ if (entry.tab && tabIds.hasOwnProperty(entry.tab) === false) {
+ continue;
+ }
+ renderLogEntry(entries[i]);
+ }
- tbody.insertBefore(tr, tbody.firstChild);
-};
+ // Prevent logger from growing infinitely and eating all memory. For
+ // instance someone could forget that it is left opened for some
+ // dynamically refreshed pages.
+ truncateLog(maxEntries);
-// Reuse date objects.
-var logDate = new Date(),
- logDateTimezoneOffset = logDate.getTimezoneOffset() * 60000;
+ var yDelta = tbody.offsetHeight - height;
+ if (yDelta === 0) {
+ return;
+ }
-/******************************************************************************/
+ // Chromium:
+ // body.scrollTop = good value
+ // body.parentNode.scrollTop = 0
+ if (document.body.scrollTop !== 0) {
+ document.body.scrollTop += yDelta;
+ return;
+ }
-var renderLogEntries = function(response) {
- var entries = response.entries;
- if ( entries.length === 0 ) {
- return;
- }
-
- // Preserve scroll position
- var height = tbody.offsetHeight;
-
- var tabIds = response.tabIds;
- var n = entries.length;
- var entry;
- for ( var i = 0; i < n; i++ ) {
- entry = entries[i];
- // Unlikely, but it may happen
- if ( entry.tab && tabIds.hasOwnProperty(entry.tab) === false ) {
- continue;
- }
- renderLogEntry(entries[i]);
- }
-
- // Prevent logger from growing infinitely and eating all memory. For
- // instance someone could forget that it is left opened for some
- // dynamically refreshed pages.
- truncateLog(maxEntries);
-
- var yDelta = tbody.offsetHeight - height;
- if ( yDelta === 0 ) {
- return;
- }
-
- // Chromium:
- // body.scrollTop = good value
- // body.parentNode.scrollTop = 0
- if ( document.body.scrollTop !== 0 ) {
- document.body.scrollTop += yDelta;
- return;
- }
-
- // Firefox:
- // body.scrollTop = 0
- // body.parentNode.scrollTop = good value
- var parentNode = document.body.parentNode;
- if ( parentNode && parentNode.scrollTop !== 0 ) {
- parentNode.scrollTop += yDelta;
- }
-};
+ // Firefox:
+ // body.scrollTop = 0
+ // body.parentNode.scrollTop = good value
+ var parentNode = document.body.parentNode;
+ if (parentNode && parentNode.scrollTop !== 0) {
+ parentNode.scrollTop += yDelta;
+ }
+ };
-/******************************************************************************/
+ /******************************************************************************/
-var synchronizeTabIds = function(newTabIds) {
- var oldTabIds = allTabIds;
- var autoDeleteVoidRows = !!vAPI.localStorage.getItem('loggerAutoDeleteVoidRows');
- var rowVoided = false;
- var trs;
- for ( var tabId in oldTabIds ) {
- if ( oldTabIds.hasOwnProperty(tabId) === false ) {
- continue;
- }
- if ( newTabIds.hasOwnProperty(tabId) ) {
- continue;
- }
- // Mark or remove voided rows
- trs = uDom('.tab_' + tabId);
- if ( autoDeleteVoidRows ) {
- toJunkyard(trs);
- } else {
- trs.removeClass('canMtx');
- rowVoided = true;
- }
- // Remove popup if it is currently bound to a removed tab.
- if ( tabId === popupManager.tabId ) {
- popupManager.toggleOff();
- }
- }
-
- var select = document.getElementById('pageSelector');
- var selectValue = select.value;
- var tabIds = Object.keys(newTabIds).sort(function(a, b) {
- return newTabIds[a].localeCompare(newTabIds[b]);
- });
- var option;
- for ( var i = 0, j = 2; i < tabIds.length; i++ ) {
- tabId = tabIds[i];
- if ( tabId === noTabId ) {
- continue;
- }
- option = select.options[j];
- j += 1;
- if ( !option ) {
- option = document.createElement('option');
- select.appendChild(option);
- }
- option.textContent = newTabIds[tabId];
- option.value = classNameFromTabId(tabId);
- if ( option.value === selectValue ) {
- option.setAttribute('selected', '');
- } else {
- option.removeAttribute('selected');
+ var synchronizeTabIds = function (newTabIds) {
+ var oldTabIds = allTabIds;
+ var autoDeleteVoidRows = !!vAPI.localStorage.getItem('loggerAutoDeleteVoidRows');
+ var rowVoided = false;
+ var trs;
+ for (var tabId in oldTabIds) {
+ if (oldTabIds.hasOwnProperty(tabId) === false) {
+ continue;
+ }
+ if (newTabIds.hasOwnProperty(tabId)) {
+ continue;
+ }
+ // Mark or remove voided rows
+ trs = uDom('.tab_' + tabId);
+ if (autoDeleteVoidRows) {
+ toJunkyard(trs);
+ } else {
+ trs.removeClass('canMtx');
+ rowVoided = true;
+ }
+ // Remove popup if it is currently bound to a removed tab.
+ if (tabId === popupManager.tabId) {
+ popupManager.toggleOff();
+ }
}
- }
- while ( j < select.options.length ) {
- select.removeChild(select.options[j]);
- }
- if ( select.value !== selectValue ) {
- select.selectedIndex = 0;
- select.value = '';
- select.options[0].setAttribute('selected', '');
- pageSelectorChanged();
- }
- allTabIds = newTabIds;
+ var select = document.getElementById('pageSelector');
+ var selectValue = select.value;
+ var tabIds = Object.keys(newTabIds).sort(function (a, b) {
+ return newTabIds[a].localeCompare(newTabIds[b]);
+ });
+ var option;
+ for (var i = 0, j = 2; i < tabIds.length; i++) {
+ tabId = tabIds[i];
+ if (tabId === noTabId) {
+ continue;
+ }
+ option = select.options[j];
+ j += 1;
+ if (!option) {
+ option = document.createElement('option');
+ select.appendChild(option);
+ }
+ option.textContent = newTabIds[tabId];
+ option.value = classNameFromTabId(tabId);
+ if (option.value === selectValue) {
+ option.setAttribute('selected', '');
+ } else {
+ option.removeAttribute('selected');
+ }
+ }
+ while (j < select.options.length) {
+ select.removeChild(select.options[j]);
+ }
+ if (select.value !== selectValue) {
+ select.selectedIndex = 0;
+ select.value = '';
+ select.options[0].setAttribute('selected', '');
+ pageSelectorChanged();
+ }
- return rowVoided;
-};
+ allTabIds = newTabIds;
-/******************************************************************************/
+ return rowVoided;
+ };
-var truncateLog = function(size) {
- if ( size === 0 ) {
- size = 5000;
- }
- var tbody = document.querySelector('#content tbody');
- size = Math.min(size, 10000);
- var tr;
- while ( tbody.childElementCount > size ) {
- tr = tbody.lastElementChild;
- trJunkyard.push(tbody.removeChild(tr));
- }
-};
+ /******************************************************************************/
-/******************************************************************************/
+ var truncateLog = function (size) {
+ if (size === 0) {
+ size = 5000;
+ }
+ var tbody = document.querySelector('#content tbody');
+ size = Math.min(size, 10000);
+ var tr;
+ while (tbody.childElementCount > size) {
+ tr = tbody.lastElementChild;
+ trJunkyard.push(tbody.removeChild(tr));
+ }
+ };
-var onLogBufferRead = function(response) {
- if ( !response || response.unavailable ) {
- readLogBufferAsync();
- return;
- }
+ /******************************************************************************/
- // This tells us the behind-the-scene tab id
- noTabId = response.noTabId;
+ var onLogBufferRead = function (response) {
+ if (!response || response.unavailable) {
+ readLogBufferAsync();
+ return;
+ }
- // This may have changed meanwhile
- if ( response.maxLoggedRequests !== maxEntries ) {
- maxEntries = response.maxLoggedRequests;
- uDom('#maxEntries').val(maxEntries || '');
- }
+ // This tells us the behind-the-scene tab id
+ noTabId = response.noTabId;
- // Neuter rows for which a tab does not exist anymore
- var rowVoided = false;
- if ( response.tabIdsToken !== allTabIdsToken ) {
- rowVoided = synchronizeTabIds(response.tabIds);
- allTabIdsToken = response.tabIdsToken;
- }
+ // This may have changed meanwhile
+ if (response.maxLoggedRequests !== maxEntries) {
+ maxEntries = response.maxLoggedRequests;
+ uDom('#maxEntries').val(maxEntries || '');
+ }
- renderLogEntries(response);
+ // Neuter rows for which a tab does not exist anymore
+ var rowVoided = false;
+ if (response.tabIdsToken !== allTabIdsToken) {
+ rowVoided = synchronizeTabIds(response.tabIds);
+ allTabIdsToken = response.tabIdsToken;
+ }
+
+ renderLogEntries(response);
+
+ if (rowVoided) {
+ uDom('#clean').toggleClass(
+ 'disabled',
+ tbody.querySelector('tr.tab:not(.canMtx)') === null
+ );
+ }
- if ( rowVoided ) {
- uDom('#clean').toggleClass(
+ // Synchronize toolbar with content of log
+ uDom('#clear').toggleClass(
'disabled',
- tbody.querySelector('tr.tab:not(.canMtx)') === null
+ tbody.querySelector('tr') === null
);
- }
- // Synchronize toolbar with content of log
- uDom('#clear').toggleClass(
- 'disabled',
- tbody.querySelector('tr') === null
- );
-
- readLogBufferAsync();
-};
+ readLogBufferAsync();
+ };
-/******************************************************************************/
+ /******************************************************************************/
-// This can be called only once, at init time. After that, this will be called
-// automatically. If called after init time, this will be messy, and this would
-// require a bit more code to ensure no multi time out events.
+ // This can be called only once, at init time. After that, this will be called
+ // automatically. If called after init time, this will be messy, and this would
+ // require a bit more code to ensure no multi time out events.
-var readLogBuffer = function() {
- if ( ownerId === undefined ) { return; }
- vAPI.messaging.send(
- 'logger-ui.js',
- { what: 'readMany', ownerId: ownerId },
- onLogBufferRead
- );
-};
+ var readLogBuffer = function () {
+ if (ownerId === undefined) {
+ return;
+ }
+ vAPI.messaging.send(
+ 'logger-ui.js', {
+ what: 'readMany',
+ ownerId: ownerId
+ },
+ onLogBufferRead
+ );
+ };
-var readLogBufferAsync = function() {
- if ( ownerId === undefined ) { return; }
- vAPI.setTimeout(readLogBuffer, 1200);
-};
+ var readLogBufferAsync = function () {
+ if (ownerId === undefined) {
+ return;
+ }
+ vAPI.setTimeout(readLogBuffer, 1200);
+ };
-/******************************************************************************/
+ /******************************************************************************/
-var pageSelectorChanged = function() {
- var style = document.getElementById('tabFilterer');
- var tabClass = document.getElementById('pageSelector').value;
- var sheet = style.sheet;
- while ( sheet.cssRules.length !== 0 ) {
- sheet.deleteRule(0);
- }
- if ( tabClass !== '' ) {
- sheet.insertRule(
- '#content table tr:not(.' + tabClass + ') { display: none; }',
- 0
+ var pageSelectorChanged = function () {
+ var style = document.getElementById('tabFilterer');
+ var tabClass = document.getElementById('pageSelector').value;
+ var sheet = style.sheet;
+ while (sheet.cssRules.length !== 0) {
+ sheet.deleteRule(0);
+ }
+ if (tabClass !== '') {
+ sheet.insertRule(
+ '#content table tr:not(.' + tabClass + ') { display: none; }',
+ 0
+ );
+ }
+ uDom('#refresh').toggleClass(
+ 'disabled',
+ tabClass === '' || tabClass === 'tab_bts'
);
- }
- uDom('#refresh').toggleClass(
- 'disabled',
- tabClass === '' || tabClass === 'tab_bts'
- );
-};
+ };
-/******************************************************************************/
+ /******************************************************************************/
-var refreshTab = function() {
- var tabClass = document.getElementById('pageSelector').value;
- var matches = tabClass.match(/^tab_(.+)$/);
- if ( matches === null ) {
- return;
- }
- if ( matches[1] === 'bts' ) {
- return;
- }
- vAPI.messaging.send(
- 'logger-ui.js',
- { what: 'forceReloadTab', tabId: matches[1] }
- );
-};
+ var refreshTab = function () {
+ var tabClass = document.getElementById('pageSelector').value;
+ var matches = tabClass.match(/^tab_(.+)$/);
+ if (matches === null) {
+ return;
+ }
+ if (matches[1] === 'bts') {
+ return;
+ }
+ vAPI.messaging.send(
+ 'logger-ui.js', {
+ what: 'forceReloadTab',
+ tabId: matches[1]
+ }
+ );
+ };
-/******************************************************************************/
+ /******************************************************************************/
-var onMaxEntriesChanged = function() {
- var raw = uDom(this).val();
- try {
- maxEntries = parseInt(raw, 10);
- if ( isNaN(maxEntries) ) {
+ var onMaxEntriesChanged = function () {
+ var raw = uDom(this).val();
+ try {
+ maxEntries = parseInt(raw, 10);
+ if (isNaN(maxEntries)) {
+ maxEntries = 0;
+ }
+ } catch (e) {
maxEntries = 0;
}
- } catch (e) {
- maxEntries = 0;
- }
-
- vAPI.messaging.send('logger-ui.js', {
- what: 'userSettings',
- name: 'maxLoggedRequests',
- value: maxEntries
- });
- truncateLog(maxEntries);
-};
+ vAPI.messaging.send('logger-ui.js', {
+ what: 'userSettings',
+ name: 'maxLoggedRequests',
+ value: maxEntries
+ });
-/******************************************************************************/
+ truncateLog(maxEntries);
+ };
-var rowFilterer = (function() {
- var filters = [];
-
- var parseInput = function() {
- filters = [];
-
- var rawPart, hardBeg, hardEnd;
- var raw = uDom('#filterInput').val().trim();
- var rawParts = raw.split(/\s+/);
- var reStr, reStrs = [], not = false;
- var n = rawParts.length;
- for ( var i = 0; i < n; i++ ) {
- rawPart = rawParts[i];
- if ( rawPart.charAt(0) === '!' ) {
- if ( reStrs.length === 0 ) {
- not = true;
+ /******************************************************************************/
+
+ var rowFilterer = (function () {
+ var filters = [];
+
+ var parseInput = function () {
+ filters = [];
+
+ var rawPart, hardBeg, hardEnd;
+ var raw = uDom('#filterInput').val().trim();
+ var rawParts = raw.split(/\s+/);
+ var reStr, reStrs = [],
+ not = false;
+ var n = rawParts.length;
+ for (var i = 0; i < n; i++) {
+ rawPart = rawParts[i];
+ if (rawPart.charAt(0) === '!') {
+ if (reStrs.length === 0) {
+ not = true;
+ }
+ rawPart = rawPart.slice(1);
}
- rawPart = rawPart.slice(1);
- }
- hardBeg = rawPart.charAt(0) === '|';
- if ( hardBeg ) {
- rawPart = rawPart.slice(1);
- }
- hardEnd = rawPart.slice(-1) === '|';
- if ( hardEnd ) {
- rawPart = rawPart.slice(0, -1);
- }
- if ( rawPart === '' ) {
- continue;
+ hardBeg = rawPart.charAt(0) === '|';
+ if (hardBeg) {
+ rawPart = rawPart.slice(1);
+ }
+ hardEnd = rawPart.slice(-1) === '|';
+ if (hardEnd) {
+ rawPart = rawPart.slice(0, -1);
+ }
+ if (rawPart === '') {
+ continue;
+ }
+ // https://developer.mozilla.org/en/docs/Web/JavaScript/Guide/Regular_Expressions
+ reStr = rawPart.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
+ if (hardBeg) {
+ reStr = '(?:^|\\s)' + reStr;
+ }
+ if (hardEnd) {
+ reStr += '(?:\\s|$)';
+ }
+ reStrs.push(reStr);
+ if (i < (n - 1) && rawParts[i + 1] === '||') {
+ i += 1;
+ continue;
+ }
+ reStr = reStrs.length === 1 ? reStrs[0] : reStrs.join('|');
+ filters.push({
+ re: new RegExp(reStr, 'i'),
+ r: !not
+ });
+ reStrs = [];
+ not = false;
}
- // https://developer.mozilla.org/en/docs/Web/JavaScript/Guide/Regular_Expressions
- reStr = rawPart.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
- if ( hardBeg ) {
- reStr = '(?:^|\\s)' + reStr;
+ };
+
+ var filterOne = function (tr, clean) {
+ var ff = filters;
+ var fcount = ff.length;
+ if (fcount === 0 && clean === true) {
+ return;
}
- if ( hardEnd ) {
- reStr += '(?:\\s|$)';
+ // do not filter out doc boundaries, they help separate important
+ // section of log.
+ var cl = tr.classList;
+ if (cl.contains('doc')) {
+ return;
}
- reStrs.push(reStr);
- if ( i < (n - 1) && rawParts[i + 1] === '||' ) {
- i += 1;
- continue;
+ if (fcount === 0) {
+ cl.remove('f');
+ return;
}
- reStr = reStrs.length === 1 ? reStrs[0] : reStrs.join('|');
- filters.push({
- re: new RegExp(reStr, 'i'),
- r: !not
- });
- reStrs = [];
- not = false;
- }
- };
-
- var filterOne = function(tr, clean) {
- var ff = filters;
- var fcount = ff.length;
- if ( fcount === 0 && clean === true ) {
- return;
- }
- // do not filter out doc boundaries, they help separate important
- // section of log.
- var cl = tr.classList;
- if ( cl.contains('doc') ) {
- return;
- }
- if ( fcount === 0 ) {
- cl.remove('f');
- return;
- }
- var cc = tr.cells;
- var ccount = cc.length;
- var hit, j, f;
- // each filter expression must hit (implicit and-op)
- // if...
- // positive filter expression = there must one hit on any field
- // negative filter expression = there must be no hit on all fields
- for ( var i = 0; i < fcount; i++ ) {
- f = ff[i];
- hit = !f.r;
- for ( j = 0; j < ccount; j++ ) {
- if ( f.re.test(cc[j].textContent) ) {
- hit = f.r;
- break;
+ var cc = tr.cells;
+ var ccount = cc.length;
+ var hit, j, f;
+ // each filter expression must hit (implicit and-op)
+ // if...
+ // positive filter expression = there must one hit on any field
+ // negative filter expression = there must be no hit on all fields
+ for (var i = 0; i < fcount; i++) {
+ f = ff[i];
+ hit = !f.r;
+ for (j = 0; j < ccount; j++) {
+ if (f.re.test(cc[j].textContent)) {
+ hit = f.r;
+ break;
+ }
+ }
+ if (!hit) {
+ cl.add('f');
+ return;
}
}
- if ( !hit ) {
- cl.add('f');
+ cl.remove('f');
+ };
+
+ var filterAll = function () {
+ // Special case: no filter
+ if (filters.length === 0) {
+ uDom('#content tr').removeClass('f');
return;
}
- }
- cl.remove('f');
- };
-
- var filterAll = function() {
- // Special case: no filter
- if ( filters.length === 0 ) {
- uDom('#content tr').removeClass('f');
- return;
- }
- var tbody = document.querySelector('#content tbody');
- var rows = tbody.rows;
- var i = rows.length;
- while ( i-- ) {
- filterOne(rows[i]);
- }
- };
-
- var onFilterChangedAsync = (function() {
- var timer = null;
- var commit = function() {
- timer = null;
- parseInput();
- filterAll();
- };
- return function() {
- if ( timer !== null ) {
- clearTimeout(timer);
+ var tbody = document.querySelector('#content tbody');
+ var rows = tbody.rows;
+ var i = rows.length;
+ while (i--) {
+ filterOne(rows[i]);
}
- timer = vAPI.setTimeout(commit, 750);
};
- })();
- var onFilterButton = function() {
- var cl = document.body.classList;
- cl.toggle('f', cl.contains('f') === false);
- };
+ var onFilterChangedAsync = (function () {
+ var timer = null;
+ var commit = function () {
+ timer = null;
+ parseInput();
+ filterAll();
+ };
+ return function () {
+ if (timer !== null) {
+ clearTimeout(timer);
+ }
+ timer = vAPI.setTimeout(commit, 750);
+ };
+ })();
- uDom('#filterButton').on('click', onFilterButton);
- uDom('#filterInput').on('input', onFilterChangedAsync);
+ var onFilterButton = function () {
+ var cl = document.body.classList;
+ cl.toggle('f', cl.contains('f') === false);
+ };
- return {
- filterOne: filterOne,
- filterAll: filterAll
- };
-})();
+ uDom('#filterButton').on('click', onFilterButton);
+ uDom('#filterInput').on('input', onFilterChangedAsync);
-/******************************************************************************/
+ return {
+ filterOne: filterOne,
+ filterAll: filterAll
+ };
+ })();
-var toJunkyard = function(trs) {
- trs.remove();
- var i = trs.length;
- while ( i-- ) {
- trJunkyard.push(trs.nodeAt(i));
- }
-};
+ /******************************************************************************/
-/******************************************************************************/
+ var toJunkyard = function (trs) {
+ trs.remove();
+ var i = trs.length;
+ while (i--) {
+ trJunkyard.push(trs.nodeAt(i));
+ }
+ };
-var clearBuffer = function() {
- var tbody = document.querySelector('#content tbody');
- var tr;
- while ( tbody.firstChild !== null ) {
- tr = tbody.lastElementChild;
- trJunkyard.push(tbody.removeChild(tr));
- }
- uDom('#clear').addClass('disabled');
- uDom('#clean').addClass('disabled');
-};
+ /******************************************************************************/
-/******************************************************************************/
+ var clearBuffer = function () {
+ var tbody = document.querySelector('#content tbody');
+ var tr;
+ while (tbody.firstChild !== null) {
+ tr = tbody.lastElementChild;
+ trJunkyard.push(tbody.removeChild(tr));
+ }
+ uDom('#clear').addClass('disabled');
+ uDom('#clean').addClass('disabled');
+ };
-var cleanBuffer = function() {
- var rows = uDom('#content tr.tab:not(.canMtx)').remove();
- var i = rows.length;
- while ( i-- ) {
- trJunkyard.push(rows.nodeAt(i));
- }
- uDom('#clean').addClass('disabled');
-};
+ /******************************************************************************/
-/******************************************************************************/
+ var cleanBuffer = function () {
+ var rows = uDom('#content tr.tab:not(.canMtx)').remove();
+ var i = rows.length;
+ while (i--) {
+ trJunkyard.push(rows.nodeAt(i));
+ }
+ uDom('#clean').addClass('disabled');
+ };
-var toggleCompactView = function() {
- document.body.classList.toggle('compactView');
- uDom('#content table .vExpanded').removeClass('vExpanded');
-};
+ /******************************************************************************/
-var toggleCompactRow = function(ev) {
- ev.target.parentElement.classList.toggle('vExpanded');
-};
+ var toggleCompactView = function () {
+ document.body.classList.toggle('compactView');
+ uDom('#content table .vExpanded').removeClass('vExpanded');
+ };
-/******************************************************************************/
+ var toggleCompactRow = function (ev) {
+ ev.target.parentElement.classList.toggle('vExpanded');
+ };
-var popupManager = (function() {
- var realTabId = null;
- var localTabId = null;
- var container = null;
- var popup = null;
- var popupObserver = null;
- var style = null;
- var styleTemplate = [
- 'tr:not(.tab_{{tabId}}) {',
+ /******************************************************************************/
+
+ var popupManager = (function () {
+ var realTabId = null;
+ var localTabId = null;
+ var container = null;
+ var popup = null;
+ var popupObserver = null;
+ var style = null;
+ var styleTemplate = [
+ 'tr:not(.tab_{{tabId}}) {',
'cursor: not-allowed;',
'opacity: 0.2;',
- '}'
- ].join('\n');
+ '}'
+ ].join('\n');
- var resizePopup = function() {
- if ( popup === null ) {
- return;
- }
- var popupBody = popup.contentWindow.document.body;
- if ( popupBody.clientWidth !== 0 && container.clientWidth !== popupBody.clientWidth ) {
- container.style.setProperty('width', popupBody.clientWidth + 'px');
- }
- popup.style.removeProperty('height');
- if ( popupBody.clientHeight !== 0 && popup.clientHeight !== popupBody.clientHeight ) {
- popup.style.setProperty('height', popupBody.clientHeight + 'px');
- }
- var ph = document.documentElement.clientHeight;
- var crect = container.getBoundingClientRect();
- if ( crect.height > ph ) {
- popup.style.setProperty('height', 'calc(' + ph + 'px - 1.8em)');
- }
- // Adjust width for presence/absence of vertical scroll bar which may
- // have appeared as a result of last operation.
- var cw = container.clientWidth;
- var dw = popup.contentWindow.document.documentElement.clientWidth;
- if ( cw !== dw ) {
- container.style.setProperty('width', (2 * cw - dw) + 'px');
- }
- };
+ var resizePopup = function () {
+ if (popup === null) {
+ return;
+ }
+ var popupBody = popup.contentWindow.document.body;
+ if (popupBody.clientWidth !== 0 && container.clientWidth !== popupBody.clientWidth) {
+ container.style.setProperty('width', popupBody.clientWidth + 'px');
+ }
+ popup.style.removeProperty('height');
+ if (popupBody.clientHeight !== 0 && popup.clientHeight !== popupBody.clientHeight) {
+ popup.style.setProperty('height', popupBody.clientHeight + 'px');
+ }
+ var ph = document.documentElement.clientHeight;
+ var crect = container.getBoundingClientRect();
+ if (crect.height > ph) {
+ popup.style.setProperty('height', 'calc(' + ph + 'px - 1.8em)');
+ }
+ // Adjust width for presence/absence of vertical scroll bar which may
+ // have appeared as a result of last operation.
+ var cw = container.clientWidth;
+ var dw = popup.contentWindow.document.documentElement.clientWidth;
+ if (cw !== dw) {
+ container.style.setProperty('width', (2 * cw - dw) + 'px');
+ }
+ };
- var toggleSize = function() {
- container.classList.toggle('hide');
- };
+ var toggleSize = function () {
+ container.classList.toggle('hide');
+ };
- var onResizeRequested = function() {
- var popupBody = popup.contentWindow.document.body;
- if ( popupBody.hasAttribute('data-resize-popup') === false ) {
- return;
- }
- popupBody.removeAttribute('data-resize-popup');
- resizePopup();
- };
+ var onResizeRequested = function () {
+ var popupBody = popup.contentWindow.document.body;
+ if (popupBody.hasAttribute('data-resize-popup') === false) {
+ return;
+ }
+ popupBody.removeAttribute('data-resize-popup');
+ resizePopup();
+ };
- var onLoad = function() {
- resizePopup();
- var popupBody = popup.contentDocument.body;
- popupBody.removeAttribute('data-resize-popup');
- popupObserver.observe(popupBody, {
- attributes: true,
- attributesFilter: [ 'data-resize-popup' ]
- });
- };
+ var onLoad = function () {
+ resizePopup();
+ var popupBody = popup.contentDocument.body;
+ popupBody.removeAttribute('data-resize-popup');
+ popupObserver.observe(popupBody, {
+ attributes: true,
+ attributesFilter: ['data-resize-popup']
+ });
+ };
- var toggleOn = function(td) {
- var tr = td.parentNode;
- var matches = tr.className.match(/(?:^| )tab_([^ ]+)/);
- if ( matches === null ) {
- return;
- }
- realTabId = localTabId = matches[1];
- if ( localTabId === 'bts' ) {
- realTabId = noTabId;
- }
+ var toggleOn = function (td) {
+ var tr = td.parentNode;
+ var matches = tr.className.match(/(?:^| )tab_([^ ]+)/);
+ if (matches === null) {
+ return;
+ }
+ realTabId = localTabId = matches[1];
+ if (localTabId === 'bts') {
+ realTabId = noTabId;
+ }
- container = document.getElementById('popupContainer');
+ container = document.getElementById('popupContainer');
- container.querySelector('div > span:nth-of-type(1)').addEventListener('click', toggleSize);
- container.querySelector('div > span:nth-of-type(2)').addEventListener('click', toggleOff);
+ container.querySelector('div > span:nth-of-type(1)').addEventListener('click', toggleSize);
+ container.querySelector('div > span:nth-of-type(2)').addEventListener('click', toggleOff);
- popup = document.createElement('iframe');
- popup.addEventListener('load', onLoad);
- popup.setAttribute('src', 'popup.html?tabId=' + realTabId);
- popupObserver = new MutationObserver(onResizeRequested);
- container.appendChild(popup);
+ popup = document.createElement('iframe');
+ popup.addEventListener('load', onLoad);
+ popup.setAttribute('src', 'popup.html?tabId=' + realTabId);
+ popupObserver = new MutationObserver(onResizeRequested);
+ container.appendChild(popup);
- style = document.getElementById('popupFilterer');
- style.textContent = styleTemplate.replace('{{tabId}}', localTabId);
+ style = document.getElementById('popupFilterer');
+ style.textContent = styleTemplate.replace('{{tabId}}', localTabId);
- document.body.classList.add('popupOn');
- };
+ document.body.classList.add('popupOn');
+ };
- var toggleOff = function() {
- document.body.classList.remove('popupOn');
+ var toggleOff = function () {
+ document.body.classList.remove('popupOn');
- container.querySelector('div > span:nth-of-type(1)').removeEventListener('click', toggleSize);
- container.querySelector('div > span:nth-of-type(2)').removeEventListener('click', toggleOff);
- container.classList.remove('hide');
+ container.querySelector('div > span:nth-of-type(1)').removeEventListener('click', toggleSize);
+ container.querySelector('div > span:nth-of-type(2)').removeEventListener('click', toggleOff);
+ container.classList.remove('hide');
- popup.removeEventListener('load', onLoad);
- popupObserver.disconnect();
- popupObserver = null;
- popup.setAttribute('src', '');
- container.removeChild(popup);
- popup = null;
+ popup.removeEventListener('load', onLoad);
+ popupObserver.disconnect();
+ popupObserver = null;
+ popup.setAttribute('src', '');
+ container.removeChild(popup);
+ popup = null;
- style.textContent = '';
- style = null;
+ style.textContent = '';
+ style = null;
- container = null;
- realTabId = null;
- };
+ container = null;
+ realTabId = null;
+ };
- var exports = {
- toggleOn: function(ev) {
- if ( realTabId === null ) {
- toggleOn(ev.target);
+ var exports = {
+ toggleOn: function (ev) {
+ if (realTabId === null) {
+ toggleOn(ev.target);
+ }
+ },
+ toggleOff: function () {
+ if (realTabId !== null) {
+ toggleOff();
+ }
}
- },
- toggleOff: function() {
- if ( realTabId !== null ) {
- toggleOff();
+ };
+
+ Object.defineProperty(exports, 'tabId', {
+ get: function () {
+ return realTabId || 0;
}
- }
- };
+ });
- Object.defineProperty(exports, 'tabId', {
- get: function() { return realTabId || 0; }
- });
+ return exports;
+ })();
- return exports;
-})();
+ /******************************************************************************/
-/******************************************************************************/
+ var grabView = function () {
+ if (ownerId === undefined) {
+ ownerId = Date.now();
+ }
+ readLogBufferAsync();
+ };
-var grabView = function() {
- if ( ownerId === undefined ) {
- ownerId = Date.now();
- }
- readLogBufferAsync();
-};
-
-var releaseView = function() {
- if ( ownerId === undefined ) { return; }
- vAPI.messaging.send(
- 'logger-ui.js',
- { what: 'releaseView', ownerId: ownerId }
- );
- ownerId = undefined;
-};
+ var releaseView = function () {
+ if (ownerId === undefined) {
+ return;
+ }
+ vAPI.messaging.send(
+ 'logger-ui.js', {
+ what: 'releaseView',
+ ownerId: ownerId
+ }
+ );
+ ownerId = undefined;
+ };
-window.addEventListener('pagehide', releaseView);
-window.addEventListener('pageshow', grabView);
-// https://bugzilla.mozilla.org/show_bug.cgi?id=1398625
-window.addEventListener('beforeunload', releaseView);
+ window.addEventListener('pagehide', releaseView);
+ window.addEventListener('pageshow', grabView);
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=1398625
+ window.addEventListener('beforeunload', releaseView);
-/******************************************************************************/
+ /******************************************************************************/
-readLogBuffer();
+ readLogBuffer();
-uDom('#pageSelector').on('change', pageSelectorChanged);
-uDom('#refresh').on('click', refreshTab);
-uDom('#compactViewToggler').on('click', toggleCompactView);
-uDom('#clean').on('click', cleanBuffer);
-uDom('#clear').on('click', clearBuffer);
-uDom('#maxEntries').on('change', onMaxEntriesChanged);
-uDom('#content table').on('click', 'tr > td:nth-of-type(1)', toggleCompactRow);
-uDom('#content table').on('click', 'tr.canMtx > td:nth-of-type(2)', popupManager.toggleOn);
+ uDom('#pageSelector').on('change', pageSelectorChanged);
+ uDom('#refresh').on('click', refreshTab);
+ uDom('#compactViewToggler').on('click', toggleCompactView);
+ uDom('#clean').on('click', cleanBuffer);
+ uDom('#clear').on('click', clearBuffer);
+ uDom('#maxEntries').on('change', onMaxEntriesChanged);
+ uDom('#content table').on('click', 'tr > td:nth-of-type(1)', toggleCompactRow);
+ uDom('#content table').on('click', 'tr.canMtx > td:nth-of-type(2)', popupManager.toggleOn);
-/******************************************************************************/
+ /******************************************************************************/
})();
diff --git a/js/logger.js b/js/logger.js
index 0a7acd2..6f742b3 100644
--- a/js/logger.js
+++ b/js/logger.js
@@ -53,7 +53,7 @@
var janitor = function() {
if (
buffer !== null &&
- lastReadTime < (Date.now() - logBufferObsoleteAfter)
+ lastReadTime < (Date.now() - logBufferObsoleteAfter)
) {
buffer = null;
writePtr = 0;
diff --git a/js/main-blocked.js b/js/main-blocked.js
index 6e0edbc..80ac9d3 100644
--- a/js/main-blocked.js
+++ b/js/main-blocked.js
@@ -29,148 +29,148 @@
(function() {
-/******************************************************************************/
-
-var details = {};
+ /******************************************************************************/
-(function() {
- var matches = /details=([^&]+)/.exec(window.location.search);
- if ( matches === null ) { return; }
- try {
- details = JSON.parse(atob(matches[1]));
- } catch(ex) {
- }
-})();
+ var details = {};
-/******************************************************************************/
+ (function() {
+ var matches = /details=([^&]+)/.exec(window.location.search);
+ if ( matches === null ) { return; }
+ try {
+ details = JSON.parse(atob(matches[1]));
+ } catch(ex) {
+ }
+ })();
-uDom('.what').text(details.url);
-// uDom('#why').text(details.why.slice(3));
+ /******************************************************************************/
-/******************************************************************************/
+ uDom('.what').text(details.url);
+ // uDom('#why').text(details.why.slice(3));
-// https://github.com/gorhill/uMatrix/issues/502
-// Code below originally imported from:
-// https://github.com/gorhill/uBlock/blob/master/src/js/document-blocked.js
+ /******************************************************************************/
-(function() {
- if ( typeof URL !== 'function' ) { return; }
+ // https://github.com/gorhill/uMatrix/issues/502
+ // Code below originally imported from:
+ // https://github.com/gorhill/uBlock/blob/master/src/js/document-blocked.js
- var reURL = /^https?:\/\//;
+ (function() {
+ if ( typeof URL !== 'function' ) { return; }
- var liFromParam = function(name, value) {
- if ( value === '' ) {
- value = name;
- name = '';
- }
- var li = document.createElement('li');
- var span = document.createElement('span');
- span.textContent = name;
- li.appendChild(span);
- if ( name !== '' && value !== '' ) {
- li.appendChild(document.createTextNode(' = '));
- }
- span = document.createElement('span');
- if ( reURL.test(value) ) {
- var a = document.createElement('a');
- a.href = a.textContent = value;
- span.appendChild(a);
- } else {
- span.textContent = value;
- }
- li.appendChild(span);
- return li;
- };
+ var reURL = /^https?:\/\//;
- var safeDecodeURIComponent = function(s) {
- try {
- s = decodeURIComponent(s);
- } catch (ex) {
- }
- return s;
- };
-
- var renderParams = function(parentNode, rawURL) {
- var a = document.createElement('a');
- a.href = rawURL;
- if ( a.search.length === 0 ) { return false; }
-
- var pos = rawURL.indexOf('?');
- var li = liFromParam(
- vAPI.i18n('docblockedNoParamsPrompt'),
- rawURL.slice(0, pos)
- );
- parentNode.appendChild(li);
-
- var params = a.search.slice(1).split('&');
- var param, name, value, ul;
- for ( var i = 0; i < params.length; i++ ) {
- param = params[i];
- pos = param.indexOf('=');
- if ( pos === -1 ) {
- pos = param.length;
+ var liFromParam = function(name, value) {
+ if ( value === '' ) {
+ value = name;
+ name = '';
+ }
+ var li = document.createElement('li');
+ var span = document.createElement('span');
+ span.textContent = name;
+ li.appendChild(span);
+ if ( name !== '' && value !== '' ) {
+ li.appendChild(document.createTextNode(' = '));
}
- name = safeDecodeURIComponent(param.slice(0, pos));
- value = safeDecodeURIComponent(param.slice(pos + 1));
- li = liFromParam(name, value);
+ span = document.createElement('span');
if ( reURL.test(value) ) {
- ul = document.createElement('ul');
- renderParams(ul, value);
- li.appendChild(ul);
+ var a = document.createElement('a');
+ a.href = a.textContent = value;
+ span.appendChild(a);
+ } else {
+ span.textContent = value;
}
+ li.appendChild(span);
+ return li;
+ };
+
+ var safeDecodeURIComponent = function(s) {
+ try {
+ s = decodeURIComponent(s);
+ } catch (ex) {
+ }
+ return s;
+ };
+
+ var renderParams = function(parentNode, rawURL) {
+ var a = document.createElement('a');
+ a.href = rawURL;
+ if ( a.search.length === 0 ) { return false; }
+
+ var pos = rawURL.indexOf('?');
+ var li = liFromParam(
+ vAPI.i18n('docblockedNoParamsPrompt'),
+ rawURL.slice(0, pos)
+ );
parentNode.appendChild(li);
- }
- return true;
- };
- if ( renderParams(uDom.nodeFromId('parsed'), details.url) === false ) {
- return;
- }
+ var params = a.search.slice(1).split('&');
+ var param, name, value, ul;
+ for ( var i = 0; i < params.length; i++ ) {
+ param = params[i];
+ pos = param.indexOf('=');
+ if ( pos === -1 ) {
+ pos = param.length;
+ }
+ name = safeDecodeURIComponent(param.slice(0, pos));
+ value = safeDecodeURIComponent(param.slice(pos + 1));
+ li = liFromParam(name, value);
+ if ( reURL.test(value) ) {
+ ul = document.createElement('ul');
+ renderParams(ul, value);
+ li.appendChild(ul);
+ }
+ parentNode.appendChild(li);
+ }
+ return true;
+ };
- var toggler = document.createElement('span');
- toggler.className = 'fa';
- uDom('#theURL > p').append(toggler);
+ if ( renderParams(uDom.nodeFromId('parsed'), details.url) === false ) {
+ return;
+ }
- uDom(toggler).on('click', function() {
- var collapsed = uDom.nodeFromId('theURL').classList.toggle('collapsed');
- vAPI.localStorage.setItem(
- 'document-blocked-collapse-url',
- collapsed.toString()
+ var toggler = document.createElement('span');
+ toggler.className = 'fa';
+ uDom('#theURL > p').append(toggler);
+
+ uDom(toggler).on('click', function() {
+ var collapsed = uDom.nodeFromId('theURL').classList.toggle('collapsed');
+ vAPI.localStorage.setItem(
+ 'document-blocked-collapse-url',
+ collapsed.toString()
+ );
+ });
+
+ uDom.nodeFromId('theURL').classList.toggle(
+ 'collapsed',
+ vAPI.localStorage.getItem('document-blocked-collapse-url') === 'true'
);
- });
+ })();
- uDom.nodeFromId('theURL').classList.toggle(
- 'collapsed',
- vAPI.localStorage.getItem('document-blocked-collapse-url') === 'true'
- );
-})();
+ /******************************************************************************/
-/******************************************************************************/
-
-if ( window.history.length > 1 ) {
- uDom('#back').on('click', function() { window.history.back(); });
- uDom('#bye').css('display', 'none');
-} else {
- uDom('#bye').on('click', function() { window.close(); });
- uDom('#back').css('display', 'none');
-}
+ if ( window.history.length > 1 ) {
+ uDom('#back').on('click', function() { window.history.back(); });
+ uDom('#bye').css('display', 'none');
+ } else {
+ uDom('#bye').on('click', function() { window.close(); });
+ uDom('#back').css('display', 'none');
+ }
-/******************************************************************************/
+ /******************************************************************************/
-// See if the target hostname is still blacklisted, and if not, navigate to it.
+ // See if the target hostname is still blacklisted, and if not, navigate to it.
-vAPI.messaging.send('main-blocked.js', {
- what: 'mustBlock',
- scope: details.hn,
- hostname: details.hn,
- type: 'doc'
-}, function(response) {
- if ( response === false ) {
- window.location.replace(details.url);
- }
-});
+ vAPI.messaging.send('main-blocked.js', {
+ what: 'mustBlock',
+ scope: details.hn,
+ hostname: details.hn,
+ type: 'doc'
+ }, function(response) {
+ if ( response === false ) {
+ window.location.replace(details.url);
+ }
+ });
-/******************************************************************************/
+ /******************************************************************************/
})();
diff --git a/js/matrix.js b/js/matrix.js
index 9f2de51..6af5ab1 100644
--- a/js/matrix.js
+++ b/js/matrix.js
@@ -30,844 +30,844 @@
ηMatrix.Matrix = (function() {
-/******************************************************************************/
-
-var ηm = ηMatrix;
-var magicId = 'axyorpwxtmnf';
-var uniqueIdGenerator = 1;
+ /******************************************************************************/
-/******************************************************************************/
+ var ηm = ηMatrix;
+ var magicId = 'axyorpwxtmnf';
+ var uniqueIdGenerator = 1;
-var Matrix = function() {
- this.id = uniqueIdGenerator++;
- this.reset();
- this.sourceRegister = '';
- this.decomposedSourceRegister = [''];
- this.specificityRegister = 0;
-};
+ /******************************************************************************/
-/******************************************************************************/
-
-Matrix.Transparent = 0;
-Matrix.Red = 1;
-Matrix.Green = 2;
-Matrix.Gray = 3;
+ var Matrix = function() {
+ this.id = uniqueIdGenerator++;
+ this.reset();
+ this.sourceRegister = '';
+ this.decomposedSourceRegister = [''];
+ this.specificityRegister = 0;
+ };
-Matrix.Indirect = 0x00;
-Matrix.Direct = 0x80;
+ /******************************************************************************/
+
+ Matrix.Transparent = 0;
+ Matrix.Red = 1;
+ Matrix.Green = 2;
+ Matrix.Gray = 3;
+
+ Matrix.Indirect = 0x00;
+ Matrix.Direct = 0x80;
+
+ Matrix.RedDirect = Matrix.Red | Matrix.Direct;
+ Matrix.RedIndirect = Matrix.Red | Matrix.Indirect;
+ Matrix.GreenDirect = Matrix.Green | Matrix.Direct;
+ Matrix.GreenIndirect = Matrix.Green | Matrix.Indirect;
+ Matrix.GrayDirect = Matrix.Gray | Matrix.Direct;
+ Matrix.GrayIndirect = Matrix.Gray | Matrix.Indirect;
+
+ /******************************************************************************/
+
+ var typeBitOffsets = new Map([
+ [ '*', 0 ],
+ [ 'doc', 2 ],
+ [ 'cookie', 4 ],
+ [ 'css', 6 ],
+ [ 'image', 8 ],
+ [ 'media', 10 ],
+ [ 'script', 12 ],
+ [ 'xhr', 14 ],
+ [ 'frame', 16 ],
+ [ 'other', 18 ]
+ ]);
+
+ var stateToNameMap = new Map([
+ [ 1, 'block' ],
+ [ 2, 'allow' ],
+ [ 3, 'inherit' ]
+ ]);
+
+ var nameToStateMap = {
+ 'block': 1,
+ 'allow': 2,
+ 'noop': 2,
+ 'inherit': 3
+ };
-Matrix.RedDirect = Matrix.Red | Matrix.Direct;
-Matrix.RedIndirect = Matrix.Red | Matrix.Indirect;
-Matrix.GreenDirect = Matrix.Green | Matrix.Direct;
-Matrix.GreenIndirect = Matrix.Green | Matrix.Indirect;
-Matrix.GrayDirect = Matrix.Gray | Matrix.Direct;
-Matrix.GrayIndirect = Matrix.Gray | Matrix.Indirect;
+ var switchBitOffsets = new Map([
+ [ 'matrix-off', 0 ],
+ [ 'https-strict', 2 ],
+ /* 4 is now unused, formerly assigned to UA spoofing */
+ [ 'referrer-spoof', 6 ],
+ [ 'noscript-spoof', 8 ],
+ [ 'no-workers', 10 ]
+ ]);
+
+ var switchStateToNameMap = new Map([
+ [ 1, 'true' ],
+ [ 2, 'false' ]
+ ]);
+
+ var nameToSwitchStateMap = {
+ 'true': 1,
+ 'false': 2
+ };
-/******************************************************************************/
+ /******************************************************************************/
-var typeBitOffsets = new Map([
- [ '*', 0 ],
- [ 'doc', 2 ],
- [ 'cookie', 4 ],
- [ 'css', 6 ],
- [ 'image', 8 ],
- [ 'media', 10 ],
- [ 'script', 12 ],
- [ 'xhr', 14 ],
- [ 'frame', 16 ],
- [ 'other', 18 ]
-]);
-
-var stateToNameMap = new Map([
- [ 1, 'block' ],
- [ 2, 'allow' ],
- [ 3, 'inherit' ]
-]);
-
-var nameToStateMap = {
- 'block': 1,
- 'allow': 2,
- 'noop': 2,
- 'inherit': 3
-};
-
-var switchBitOffsets = new Map([
- [ 'matrix-off', 0 ],
- [ 'https-strict', 2 ],
- /* 4 is now unused, formerly assigned to UA spoofing */
- [ 'referrer-spoof', 6 ],
- [ 'noscript-spoof', 8 ],
- [ 'no-workers', 10 ]
-]);
-
-var switchStateToNameMap = new Map([
- [ 1, 'true' ],
- [ 2, 'false' ]
-]);
-
-var nameToSwitchStateMap = {
- 'true': 1,
- 'false': 2
-};
+ Matrix.columnHeaderIndices = (function() {
+ var out = new Map(),
+ i = 0;
+ for ( var type of typeBitOffsets.keys() ) {
+ out.set(type, i++);
+ }
+ return out;
+ })();
-/******************************************************************************/
-Matrix.columnHeaderIndices = (function() {
- var out = new Map(),
- i = 0;
- for ( var type of typeBitOffsets.keys() ) {
- out.set(type, i++);
- }
- return out;
-})();
+ Matrix.switchNames = new Set(switchBitOffsets.keys());
+ /******************************************************************************/
-Matrix.switchNames = new Set(switchBitOffsets.keys());
+ // For performance purpose, as simple tests as possible
+ var reHostnameVeryCoarse = /[g-z_-]/;
+ var reIPv4VeryCoarse = /\.\d+$/;
-/******************************************************************************/
+ // http://tools.ietf.org/html/rfc5952
+ // 4.3: "MUST be represented in lowercase"
+ // Also: http://en.wikipedia.org/wiki/IPv6_address#Literal_IPv6_addresses_in_network_resource_identifiers
-// For performance purpose, as simple tests as possible
-var reHostnameVeryCoarse = /[g-z_-]/;
-var reIPv4VeryCoarse = /\.\d+$/;
+ var isIPAddress = function(hostname) {
+ if ( reHostnameVeryCoarse.test(hostname) ) {
+ return false;
+ }
+ if ( reIPv4VeryCoarse.test(hostname) ) {
+ return true;
+ }
+ return hostname.charAt(0) === '[';
+ };
-// http://tools.ietf.org/html/rfc5952
-// 4.3: "MUST be represented in lowercase"
-// Also: http://en.wikipedia.org/wiki/IPv6_address#Literal_IPv6_addresses_in_network_resource_identifiers
+ /******************************************************************************/
-var isIPAddress = function(hostname) {
- if ( reHostnameVeryCoarse.test(hostname) ) {
- return false;
- }
- if ( reIPv4VeryCoarse.test(hostname) ) {
- return true;
- }
- return hostname.charAt(0) === '[';
-};
+ var toBroaderHostname = function(hostname) {
+ if ( hostname === '*' ) { return ''; }
+ if ( isIPAddress(hostname) ) {
+ return toBroaderIPAddress(hostname);
+ }
+ var pos = hostname.indexOf('.');
+ if ( pos === -1 ) {
+ return '*';
+ }
+ return hostname.slice(pos + 1);
+ };
-/******************************************************************************/
+ var toBroaderIPAddress = function(ipaddress) {
+ // Can't broaden IPv6 (for now)
+ if ( ipaddress.charAt(0) === '[' ) {
+ return '*';
+ }
+ var pos = ipaddress.lastIndexOf('.');
+ return pos !== -1 ? ipaddress.slice(0, pos) : '*';
+ };
-var toBroaderHostname = function(hostname) {
- if ( hostname === '*' ) { return ''; }
- if ( isIPAddress(hostname) ) {
- return toBroaderIPAddress(hostname);
- }
- var pos = hostname.indexOf('.');
- if ( pos === -1 ) {
- return '*';
- }
- return hostname.slice(pos + 1);
-};
-
-var toBroaderIPAddress = function(ipaddress) {
- // Can't broaden IPv6 (for now)
- if ( ipaddress.charAt(0) === '[' ) {
- return '*';
- }
- var pos = ipaddress.lastIndexOf('.');
- return pos !== -1 ? ipaddress.slice(0, pos) : '*';
-};
-
-Matrix.toBroaderHostname = toBroaderHostname;
+ Matrix.toBroaderHostname = toBroaderHostname;
-/******************************************************************************/
+ /******************************************************************************/
-// Find out src-des relationship, using coarse-to-fine grained tests for
-// speed. If desHostname is 1st-party to srcHostname, the domain is returned,
-// otherwise the empty string.
+ // Find out src-des relationship, using coarse-to-fine grained tests for
+ // speed. If desHostname is 1st-party to srcHostname, the domain is returned,
+ // otherwise the empty string.
-var extractFirstPartyDesDomain = function(srcHostname, desHostname) {
- if ( srcHostname === '*' || desHostname === '*' || desHostname === '1st-party' ) {
- return '';
- }
- var ηmuri = ηm.URI;
- var srcDomain = ηmuri.domainFromHostname(srcHostname) || srcHostname;
- var desDomain = ηmuri.domainFromHostname(desHostname) || desHostname;
- return desDomain === srcDomain ? desDomain : '';
-};
+ var extractFirstPartyDesDomain = function(srcHostname, desHostname) {
+ if ( srcHostname === '*' || desHostname === '*' || desHostname === '1st-party' ) {
+ return '';
+ }
+ var ηmuri = ηm.URI;
+ var srcDomain = ηmuri.domainFromHostname(srcHostname) || srcHostname;
+ var desDomain = ηmuri.domainFromHostname(desHostname) || desHostname;
+ return desDomain === srcDomain ? desDomain : '';
+ };
-/******************************************************************************/
+ /******************************************************************************/
-Matrix.prototype.reset = function() {
- this.switches = new Map();
- this.rules = new Map();
- this.rootValue = Matrix.RedIndirect;
- this.modifiedTime = 0;
-};
+ Matrix.prototype.reset = function() {
+ this.switches = new Map();
+ this.rules = new Map();
+ this.rootValue = Matrix.RedIndirect;
+ this.modifiedTime = 0;
+ };
-/******************************************************************************/
+ /******************************************************************************/
-Matrix.prototype.decomposeSource = function(srcHostname) {
- if ( srcHostname === this.sourceRegister ) { return; }
- var hn = srcHostname;
- this.decomposedSourceRegister[0] = this.sourceRegister = hn;
- var i = 1;
- for (;;) {
- hn = toBroaderHostname(hn);
- this.decomposedSourceRegister[i++] = hn;
- if ( hn === '' ) { break; }
- }
-};
+ Matrix.prototype.decomposeSource = function(srcHostname) {
+ if ( srcHostname === this.sourceRegister ) { return; }
+ var hn = srcHostname;
+ this.decomposedSourceRegister[0] = this.sourceRegister = hn;
+ var i = 1;
+ for (;;) {
+ hn = toBroaderHostname(hn);
+ this.decomposedSourceRegister[i++] = hn;
+ if ( hn === '' ) { break; }
+ }
+ };
-/******************************************************************************/
+ /******************************************************************************/
-// Copy another matrix to self. Do this incrementally to minimize impact on
-// a live matrix.
+ // Copy another matrix to self. Do this incrementally to minimize impact on
+ // a live matrix.
-Matrix.prototype.assign = function(other) {
- var k, entry;
- // Remove rules not in other
- for ( k of this.rules.keys() ) {
- if ( other.rules.has(k) === false ) {
- this.rules.delete(k);
+ Matrix.prototype.assign = function(other) {
+ var k, entry;
+ // Remove rules not in other
+ for ( k of this.rules.keys() ) {
+ if ( other.rules.has(k) === false ) {
+ this.rules.delete(k);
+ }
+ }
+ // Remove switches not in other
+ for ( k of this.switches.keys() ) {
+ if ( other.switches.has(k) === false ) {
+ this.switches.delete(k);
+ }
+ }
+ // Add/change rules in other
+ for ( entry of other.rules ) {
+ this.rules.set(entry[0], entry[1]);
}
- }
- // Remove switches not in other
- for ( k of this.switches.keys() ) {
- if ( other.switches.has(k) === false ) {
- this.switches.delete(k);
- }
- }
- // Add/change rules in other
- for ( entry of other.rules ) {
- this.rules.set(entry[0], entry[1]);
- }
- // Add/change switches in other
- for ( entry of other.switches ) {
- this.switches.set(entry[0], entry[1]);
- }
- this.modifiedTime = other.modifiedTime;
- return this;
-};
-
-// https://www.youtube.com/watch?v=e9RS4biqyAc
+ // Add/change switches in other
+ for ( entry of other.switches ) {
+ this.switches.set(entry[0], entry[1]);
+ }
+ this.modifiedTime = other.modifiedTime;
+ return this;
+ };
-/******************************************************************************/
+ // https://www.youtube.com/watch?v=e9RS4biqyAc
-// If value is undefined, the switch is removed
+ /******************************************************************************/
-Matrix.prototype.setSwitch = function(switchName, srcHostname, newVal) {
- var bitOffset = switchBitOffsets.get(switchName);
- if ( bitOffset === undefined ) {
- return false;
- }
- if ( newVal === this.evaluateSwitch(switchName, srcHostname) ) {
- return false;
- }
- var bits = this.switches.get(srcHostname) || 0;
- bits &= ~(3 << bitOffset);
- bits |= newVal << bitOffset;
- if ( bits === 0 ) {
- this.switches.delete(srcHostname);
- } else {
- this.switches.set(srcHostname, bits);
- }
- this.modifiedTime = Date.now();
- return true;
-};
+ // If value is undefined, the switch is removed
-/******************************************************************************/
+ Matrix.prototype.setSwitch = function(switchName, srcHostname, newVal) {
+ var bitOffset = switchBitOffsets.get(switchName);
+ if ( bitOffset === undefined ) {
+ return false;
+ }
+ if ( newVal === this.evaluateSwitch(switchName, srcHostname) ) {
+ return false;
+ }
+ var bits = this.switches.get(srcHostname) || 0;
+ bits &= ~(3 << bitOffset);
+ bits |= newVal << bitOffset;
+ if ( bits === 0 ) {
+ this.switches.delete(srcHostname);
+ } else {
+ this.switches.set(srcHostname, bits);
+ }
+ this.modifiedTime = Date.now();
+ return true;
+ };
-Matrix.prototype.setCell = function(srcHostname, desHostname, type, state) {
- var bitOffset = typeBitOffsets.get(type),
- k = srcHostname + ' ' + desHostname,
- oldBitmap = this.rules.get(k);
- if ( oldBitmap === undefined ) {
- oldBitmap = 0;
- }
- var newBitmap = oldBitmap & ~(3 << bitOffset) | (state << bitOffset);
- if ( newBitmap === oldBitmap ) {
- return false;
- }
- if ( newBitmap === 0 ) {
- this.rules.delete(k);
- } else {
- this.rules.set(k, newBitmap);
- }
- this.modifiedTime = Date.now();
- return true;
-};
+ /******************************************************************************/
-/******************************************************************************/
+ Matrix.prototype.setCell = function(srcHostname, desHostname, type, state) {
+ var bitOffset = typeBitOffsets.get(type),
+ k = srcHostname + ' ' + desHostname,
+ oldBitmap = this.rules.get(k);
+ if ( oldBitmap === undefined ) {
+ oldBitmap = 0;
+ }
+ var newBitmap = oldBitmap & ~(3 << bitOffset) | (state << bitOffset);
+ if ( newBitmap === oldBitmap ) {
+ return false;
+ }
+ if ( newBitmap === 0 ) {
+ this.rules.delete(k);
+ } else {
+ this.rules.set(k, newBitmap);
+ }
+ this.modifiedTime = Date.now();
+ return true;
+ };
-Matrix.prototype.blacklistCell = function(srcHostname, desHostname, type) {
- var r = this.evaluateCellZ(srcHostname, desHostname, type);
- if ( r === 1 ) {
- return false;
- }
- this.setCell(srcHostname, desHostname, type, 0);
- r = this.evaluateCellZ(srcHostname, desHostname, type);
- if ( r === 1 ) {
+ /******************************************************************************/
+
+ Matrix.prototype.blacklistCell = function(srcHostname, desHostname, type) {
+ var r = this.evaluateCellZ(srcHostname, desHostname, type);
+ if ( r === 1 ) {
+ return false;
+ }
+ this.setCell(srcHostname, desHostname, type, 0);
+ r = this.evaluateCellZ(srcHostname, desHostname, type);
+ if ( r === 1 ) {
+ return true;
+ }
+ this.setCell(srcHostname, desHostname, type, 1);
return true;
- }
- this.setCell(srcHostname, desHostname, type, 1);
- return true;
-};
+ };
-/******************************************************************************/
+ /******************************************************************************/
-Matrix.prototype.whitelistCell = function(srcHostname, desHostname, type) {
- var r = this.evaluateCellZ(srcHostname, desHostname, type);
- if ( r === 2 ) {
- return false;
- }
- this.setCell(srcHostname, desHostname, type, 0);
- r = this.evaluateCellZ(srcHostname, desHostname, type);
- if ( r === 2 ) {
+ Matrix.prototype.whitelistCell = function(srcHostname, desHostname, type) {
+ var r = this.evaluateCellZ(srcHostname, desHostname, type);
+ if ( r === 2 ) {
+ return false;
+ }
+ this.setCell(srcHostname, desHostname, type, 0);
+ r = this.evaluateCellZ(srcHostname, desHostname, type);
+ if ( r === 2 ) {
+ return true;
+ }
+ this.setCell(srcHostname, desHostname, type, 2);
return true;
- }
- this.setCell(srcHostname, desHostname, type, 2);
- return true;
-};
+ };
-/******************************************************************************/
+ /******************************************************************************/
-Matrix.prototype.graylistCell = function(srcHostname, desHostname, type) {
- var r = this.evaluateCellZ(srcHostname, desHostname, type);
- if ( r === 0 || r === 3 ) {
- return false;
- }
- this.setCell(srcHostname, desHostname, type, 0);
- r = this.evaluateCellZ(srcHostname, desHostname, type);
- if ( r === 0 || r === 3 ) {
+ Matrix.prototype.graylistCell = function(srcHostname, desHostname, type) {
+ var r = this.evaluateCellZ(srcHostname, desHostname, type);
+ if ( r === 0 || r === 3 ) {
+ return false;
+ }
+ this.setCell(srcHostname, desHostname, type, 0);
+ r = this.evaluateCellZ(srcHostname, desHostname, type);
+ if ( r === 0 || r === 3 ) {
+ return true;
+ }
+ this.setCell(srcHostname, desHostname, type, 3);
return true;
- }
- this.setCell(srcHostname, desHostname, type, 3);
- return true;
-};
+ };
-/******************************************************************************/
+ /******************************************************************************/
-Matrix.prototype.evaluateCell = function(srcHostname, desHostname, type) {
- var key = srcHostname + ' ' + desHostname;
- var bitmap = this.rules.get(key);
- if ( bitmap === undefined ) {
- return 0;
- }
- return bitmap >> typeBitOffsets.get(type) & 3;
-};
+ Matrix.prototype.evaluateCell = function(srcHostname, desHostname, type) {
+ var key = srcHostname + ' ' + desHostname;
+ var bitmap = this.rules.get(key);
+ if ( bitmap === undefined ) {
+ return 0;
+ }
+ return bitmap >> typeBitOffsets.get(type) & 3;
+ };
-/******************************************************************************/
+ /******************************************************************************/
+
+ Matrix.prototype.evaluateCellZ = function(srcHostname, desHostname, type) {
+ this.decomposeSource(srcHostname);
-Matrix.prototype.evaluateCellZ = function(srcHostname, desHostname, type) {
- this.decomposeSource(srcHostname);
-
- var bitOffset = typeBitOffsets.get(type),
- s, v, i = 0;
- for (;;) {
- s = this.decomposedSourceRegister[i++];
- if ( s === '' ) { break; }
- v = this.rules.get(s + ' ' + desHostname);
- if ( v !== undefined ) {
- v = v >> bitOffset & 3;
- if ( v !== 0 ) {
- return v;
+ var bitOffset = typeBitOffsets.get(type),
+ s, v, i = 0;
+ for (;;) {
+ s = this.decomposedSourceRegister[i++];
+ if ( s === '' ) { break; }
+ v = this.rules.get(s + ' ' + desHostname);
+ if ( v !== undefined ) {
+ v = v >> bitOffset & 3;
+ if ( v !== 0 ) {
+ return v;
+ }
}
}
- }
- // srcHostname is '*' at this point
+ // srcHostname is '*' at this point
- // Preset blacklisted hostnames are blacklisted in global scope
- if ( type === '*' && ηm.ubiquitousBlacklist.test(desHostname) ) {
- return 1;
- }
+ // Preset blacklisted hostnames are blacklisted in global scope
+ if ( type === '*' && ηm.ubiquitousBlacklist.test(desHostname) ) {
+ return 1;
+ }
- // https://github.com/gorhill/uMatrix/issues/65
- // Hardcoded global `doc` rule
- if ( type === 'doc' && desHostname === '*' ) {
- return 2;
- }
+ // https://github.com/gorhill/uMatrix/issues/65
+ // Hardcoded global `doc` rule
+ if ( type === 'doc' && desHostname === '*' ) {
+ return 2;
+ }
- return 0;
-};
+ return 0;
+ };
-/******************************************************************************/
+ /******************************************************************************/
-Matrix.prototype.evaluateCellZXY = function(srcHostname, desHostname, type) {
- // Matrix filtering switch
- this.specificityRegister = 0;
- if ( this.evaluateSwitchZ('matrix-off', srcHostname) ) {
- return Matrix.GreenIndirect;
- }
-
- // TODO: There are cells evaluated twice when the type is '*'. Unsure
- // whether it's worth trying to avoid that, as this could introduce
- // overhead which may not be gained back by skipping the redundant tests.
- // And this happens *only* when building the matrix UI, not when
- // evaluating net requests.
-
- // Specific-hostname specific-type cell
- this.specificityRegister = 1;
- var r = this.evaluateCellZ(srcHostname, desHostname, type);
- if ( r === 1 ) { return Matrix.RedDirect; }
- if ( r === 2 ) { return Matrix.GreenDirect; }
-
- // Specific-hostname any-type cell
- this.specificityRegister = 2;
- var rl = this.evaluateCellZ(srcHostname, desHostname, '*');
- if ( rl === 1 ) { return Matrix.RedIndirect; }
-
- var d = desHostname;
- var firstPartyDesDomain = extractFirstPartyDesDomain(srcHostname, desHostname);
-
- // Ancestor cells, up to 1st-party destination domain
- if ( firstPartyDesDomain !== '' ) {
- this.specificityRegister = 3;
+ Matrix.prototype.evaluateCellZXY = function(srcHostname, desHostname, type) {
+ // Matrix filtering switch
+ this.specificityRegister = 0;
+ if ( this.evaluateSwitchZ('matrix-off', srcHostname) ) {
+ return Matrix.GreenIndirect;
+ }
+
+ // TODO: There are cells evaluated twice when the type is '*'. Unsure
+ // whether it's worth trying to avoid that, as this could introduce
+ // overhead which may not be gained back by skipping the redundant tests.
+ // And this happens *only* when building the matrix UI, not when
+ // evaluating net requests.
+
+ // Specific-hostname specific-type cell
+ this.specificityRegister = 1;
+ var r = this.evaluateCellZ(srcHostname, desHostname, type);
+ if ( r === 1 ) { return Matrix.RedDirect; }
+ if ( r === 2 ) { return Matrix.GreenDirect; }
+
+ // Specific-hostname any-type cell
+ this.specificityRegister = 2;
+ var rl = this.evaluateCellZ(srcHostname, desHostname, '*');
+ if ( rl === 1 ) { return Matrix.RedIndirect; }
+
+ var d = desHostname;
+ var firstPartyDesDomain = extractFirstPartyDesDomain(srcHostname, desHostname);
+
+ // Ancestor cells, up to 1st-party destination domain
+ if ( firstPartyDesDomain !== '' ) {
+ this.specificityRegister = 3;
+ for (;;) {
+ if ( d === firstPartyDesDomain ) { break; }
+ d = d.slice(d.indexOf('.') + 1);
+
+ // specific-hostname specific-type cell
+ r = this.evaluateCellZ(srcHostname, d, type);
+ if ( r === 1 ) { return Matrix.RedIndirect; }
+ if ( r === 2 ) { return Matrix.GreenIndirect; }
+ // Do not override a narrower rule
+ if ( rl !== 2 ) {
+ rl = this.evaluateCellZ(srcHostname, d, '*');
+ if ( rl === 1 ) { return Matrix.RedIndirect; }
+ }
+ }
+
+ // 1st-party specific-type cell: it's a special row, looked up only
+ // when destination is 1st-party to source.
+ r = this.evaluateCellZ(srcHostname, '1st-party', type);
+ if ( r === 1 ) { return Matrix.RedIndirect; }
+ if ( r === 2 ) { return Matrix.GreenIndirect; }
+ // Do not override narrower rule
+ if ( rl !== 2 ) {
+ rl = this.evaluateCellZ(srcHostname, '1st-party', '*');
+ if ( rl === 1 ) { return Matrix.RedIndirect; }
+ }
+ }
+
+ // Keep going, up to root
+ this.specificityRegister = 4;
for (;;) {
- if ( d === firstPartyDesDomain ) { break; }
- d = d.slice(d.indexOf('.') + 1);
+ d = toBroaderHostname(d);
+ if ( d === '*' ) { break; }
// specific-hostname specific-type cell
r = this.evaluateCellZ(srcHostname, d, type);
if ( r === 1 ) { return Matrix.RedIndirect; }
if ( r === 2 ) { return Matrix.GreenIndirect; }
- // Do not override a narrower rule
+ // Do not override narrower rule
if ( rl !== 2 ) {
rl = this.evaluateCellZ(srcHostname, d, '*');
if ( rl === 1 ) { return Matrix.RedIndirect; }
}
}
- // 1st-party specific-type cell: it's a special row, looked up only
- // when destination is 1st-party to source.
- r = this.evaluateCellZ(srcHostname, '1st-party', type);
+ // Any-hostname specific-type cells
+ this.specificityRegister = 5;
+ r = this.evaluateCellZ(srcHostname, '*', type);
+ // Line below is strict-blocking
if ( r === 1 ) { return Matrix.RedIndirect; }
+ // Narrower rule wins
+ if ( rl === 2 ) { return Matrix.GreenIndirect; }
if ( r === 2 ) { return Matrix.GreenIndirect; }
- // Do not override narrower rule
- if ( rl !== 2 ) {
- rl = this.evaluateCellZ(srcHostname, '1st-party', '*');
- if ( rl === 1 ) { return Matrix.RedIndirect; }
- }
- }
- // Keep going, up to root
- this.specificityRegister = 4;
- for (;;) {
- d = toBroaderHostname(d);
- if ( d === '*' ) { break; }
-
- // specific-hostname specific-type cell
- r = this.evaluateCellZ(srcHostname, d, type);
+ // Any-hostname any-type cell
+ this.specificityRegister = 6;
+ r = this.evaluateCellZ(srcHostname, '*', '*');
if ( r === 1 ) { return Matrix.RedIndirect; }
if ( r === 2 ) { return Matrix.GreenIndirect; }
- // Do not override narrower rule
- if ( rl !== 2 ) {
- rl = this.evaluateCellZ(srcHostname, d, '*');
- if ( rl === 1 ) { return Matrix.RedIndirect; }
- }
- }
-
- // Any-hostname specific-type cells
- this.specificityRegister = 5;
- r = this.evaluateCellZ(srcHostname, '*', type);
- // Line below is strict-blocking
- if ( r === 1 ) { return Matrix.RedIndirect; }
- // Narrower rule wins
- if ( rl === 2 ) { return Matrix.GreenIndirect; }
- if ( r === 2 ) { return Matrix.GreenIndirect; }
-
- // Any-hostname any-type cell
- this.specificityRegister = 6;
- r = this.evaluateCellZ(srcHostname, '*', '*');
- if ( r === 1 ) { return Matrix.RedIndirect; }
- if ( r === 2 ) { return Matrix.GreenIndirect; }
- return this.rootValue;
-};
-
-// https://www.youtube.com/watch?v=4C5ZkwrnVfM
+ return this.rootValue;
+ };
-/******************************************************************************/
+ // https://www.youtube.com/watch?v=4C5ZkwrnVfM
-Matrix.prototype.evaluateRowZXY = function(srcHostname, desHostname) {
- var out = [];
- for ( var type of typeBitOffsets.keys() ) {
- out.push(this.evaluateCellZXY(srcHostname, desHostname, type));
- }
- return out;
-};
+ /******************************************************************************/
-/******************************************************************************/
+ Matrix.prototype.evaluateRowZXY = function(srcHostname, desHostname) {
+ var out = [];
+ for ( var type of typeBitOffsets.keys() ) {
+ out.push(this.evaluateCellZXY(srcHostname, desHostname, type));
+ }
+ return out;
+ };
-Matrix.prototype.mustBlock = function(srcHostname, desHostname, type) {
- return (this.evaluateCellZXY(srcHostname, desHostname, type) & 3) === Matrix.Red;
-};
+ /******************************************************************************/
-/******************************************************************************/
+ Matrix.prototype.mustBlock = function(srcHostname, desHostname, type) {
+ return (this.evaluateCellZXY(srcHostname, desHostname, type) & 3) === Matrix.Red;
+ };
-Matrix.prototype.srcHostnameFromRule = function(rule) {
- return rule.slice(0, rule.indexOf(' '));
-};
+ /******************************************************************************/
-/******************************************************************************/
+ Matrix.prototype.srcHostnameFromRule = function(rule) {
+ return rule.slice(0, rule.indexOf(' '));
+ };
-Matrix.prototype.desHostnameFromRule = function(rule) {
- return rule.slice(rule.indexOf(' ') + 1);
-};
+ /******************************************************************************/
-/******************************************************************************/
+ Matrix.prototype.desHostnameFromRule = function(rule) {
+ return rule.slice(rule.indexOf(' ') + 1);
+ };
-Matrix.prototype.setSwitchZ = function(switchName, srcHostname, newState) {
- var bitOffset = switchBitOffsets.get(switchName);
- if ( bitOffset === undefined ) {
- return false;
- }
- var state = this.evaluateSwitchZ(switchName, srcHostname);
- if ( newState === state ) {
- return false;
- }
- if ( newState === undefined ) {
- newState = !state;
- }
- var bits = this.switches.get(srcHostname) || 0;
- bits &= ~(3 << bitOffset);
- if ( bits === 0 ) {
- this.switches.delete(srcHostname);
- } else {
- this.switches.set(srcHostname, bits);
- }
- this.modifiedTime = Date.now();
- state = this.evaluateSwitchZ(switchName, srcHostname);
- if ( state === newState ) {
+ /******************************************************************************/
+
+ Matrix.prototype.setSwitchZ = function(switchName, srcHostname, newState) {
+ var bitOffset = switchBitOffsets.get(switchName);
+ if ( bitOffset === undefined ) {
+ return false;
+ }
+ var state = this.evaluateSwitchZ(switchName, srcHostname);
+ if ( newState === state ) {
+ return false;
+ }
+ if ( newState === undefined ) {
+ newState = !state;
+ }
+ var bits = this.switches.get(srcHostname) || 0;
+ bits &= ~(3 << bitOffset);
+ if ( bits === 0 ) {
+ this.switches.delete(srcHostname);
+ } else {
+ this.switches.set(srcHostname, bits);
+ }
+ this.modifiedTime = Date.now();
+ state = this.evaluateSwitchZ(switchName, srcHostname);
+ if ( state === newState ) {
+ return true;
+ }
+ this.switches.set(srcHostname, bits | ((newState ? 1 : 2) << bitOffset));
return true;
- }
- this.switches.set(srcHostname, bits | ((newState ? 1 : 2) << bitOffset));
- return true;
-};
+ };
-/******************************************************************************/
+ /******************************************************************************/
-// 0 = inherit from broader scope, up to default state
-// 1 = non-default state
-// 2 = forced default state (to override a broader non-default state)
+ // 0 = inherit from broader scope, up to default state
+ // 1 = non-default state
+ // 2 = forced default state (to override a broader non-default state)
-Matrix.prototype.evaluateSwitch = function(switchName, srcHostname) {
- var bits = this.switches.get(srcHostname) || 0;
- if ( bits === 0 ) {
- return 0;
- }
- var bitOffset = switchBitOffsets.get(switchName);
- if ( bitOffset === undefined ) {
- return 0;
- }
- return (bits >> bitOffset) & 3;
-};
+ Matrix.prototype.evaluateSwitch = function(switchName, srcHostname) {
+ var bits = this.switches.get(srcHostname) || 0;
+ if ( bits === 0 ) {
+ return 0;
+ }
+ var bitOffset = switchBitOffsets.get(switchName);
+ if ( bitOffset === undefined ) {
+ return 0;
+ }
+ return (bits >> bitOffset) & 3;
+ };
-/******************************************************************************/
+ /******************************************************************************/
-Matrix.prototype.evaluateSwitchZ = function(switchName, srcHostname) {
- var bitOffset = switchBitOffsets.get(switchName);
- if ( bitOffset === undefined ) { return false; }
+ Matrix.prototype.evaluateSwitchZ = function(switchName, srcHostname) {
+ var bitOffset = switchBitOffsets.get(switchName);
+ if ( bitOffset === undefined ) { return false; }
- this.decomposeSource(srcHostname);
+ this.decomposeSource(srcHostname);
- var s, bits, i = 0;
- for (;;) {
- s = this.decomposedSourceRegister[i++];
- if ( s === '' ) { break; }
- bits = this.switches.get(s) || 0;
- if ( bits !== 0 ) {
- bits = bits >> bitOffset & 3;
+ var s, bits, i = 0;
+ for (;;) {
+ s = this.decomposedSourceRegister[i++];
+ if ( s === '' ) { break; }
+ bits = this.switches.get(s) || 0;
if ( bits !== 0 ) {
- return bits === 1;
+ bits = bits >> bitOffset & 3;
+ if ( bits !== 0 ) {
+ return bits === 1;
+ }
}
}
- }
- return false;
-};
-
-/******************************************************************************/
-
-Matrix.prototype.extractAllSourceHostnames = (function() {
- var cachedResult = new Set();
- var matrixId = 0;
- var readTime = 0;
+ return false;
+ };
- return function() {
- if ( matrixId !== this.id || readTime !== this.modifiedTime ) {
- cachedResult.clear();
- for ( var rule of this.rules.keys() ) {
- cachedResult.add(rule.slice(0, rule.indexOf(' ')));
+ /******************************************************************************/
+
+ Matrix.prototype.extractAllSourceHostnames = (function() {
+ var cachedResult = new Set();
+ var matrixId = 0;
+ var readTime = 0;
+
+ return function() {
+ if ( matrixId !== this.id || readTime !== this.modifiedTime ) {
+ cachedResult.clear();
+ for ( var rule of this.rules.keys() ) {
+ cachedResult.add(rule.slice(0, rule.indexOf(' ')));
+ }
+ matrixId = this.id;
+ readTime = this.modifiedTime;
+ }
+ return cachedResult;
+ };
+ })();
+
+ /******************************************************************************/
+
+ Matrix.prototype.toString = function() {
+ var out = [];
+ var rule, type, switchName, val;
+ var srcHostname, desHostname;
+ for ( rule of this.rules.keys() ) {
+ srcHostname = this.srcHostnameFromRule(rule);
+ desHostname = this.desHostnameFromRule(rule);
+ for ( type of typeBitOffsets.keys() ) {
+ val = this.evaluateCell(srcHostname, desHostname, type);
+ if ( val === 0 ) { continue; }
+ out.push(
+ punycode.toUnicode(srcHostname) + ' ' +
+ punycode.toUnicode(desHostname) + ' ' +
+ type + ' ' +
+ stateToNameMap.get(val)
+ );
+ }
+ }
+ for ( srcHostname of this.switches.keys() ) {
+ for ( switchName of switchBitOffsets.keys() ) {
+ val = this.evaluateSwitch(switchName, srcHostname);
+ if ( val === 0 ) { continue; }
+ out.push(switchName + ': ' + srcHostname + ' ' + switchStateToNameMap.get(val));
}
- matrixId = this.id;
- readTime = this.modifiedTime;
}
- return cachedResult;
+ return out.sort().join('\n');
};
-})();
-
-/******************************************************************************/
-Matrix.prototype.toString = function() {
- var out = [];
- var rule, type, switchName, val;
- var srcHostname, desHostname;
- for ( rule of this.rules.keys() ) {
- srcHostname = this.srcHostnameFromRule(rule);
- desHostname = this.desHostnameFromRule(rule);
- for ( type of typeBitOffsets.keys() ) {
- val = this.evaluateCell(srcHostname, desHostname, type);
- if ( val === 0 ) { continue; }
- out.push(
- punycode.toUnicode(srcHostname) + ' ' +
- punycode.toUnicode(desHostname) + ' ' +
- type + ' ' +
- stateToNameMap.get(val)
- );
- }
- }
- for ( srcHostname of this.switches.keys() ) {
- for ( switchName of switchBitOffsets.keys() ) {
- val = this.evaluateSwitch(switchName, srcHostname);
- if ( val === 0 ) { continue; }
- out.push(switchName + ': ' + srcHostname + ' ' + switchStateToNameMap.get(val));
- }
- }
- return out.sort().join('\n');
-};
-
-/******************************************************************************/
-
-Matrix.prototype.fromString = function(text, append) {
- var matrix = append ? this : new Matrix();
- var textEnd = text.length;
- var lineBeg = 0, lineEnd;
- var line, pos;
- var fields, fieldVal;
- var switchName;
- var srcHostname = '';
- var desHostname = '';
- var type, state;
-
- while ( lineBeg < textEnd ) {
- lineEnd = text.indexOf('\n', lineBeg);
- if ( lineEnd < 0 ) {
- lineEnd = text.indexOf('\r', lineBeg);
+ /******************************************************************************/
+
+ Matrix.prototype.fromString = function(text, append) {
+ var matrix = append ? this : new Matrix();
+ var textEnd = text.length;
+ var lineBeg = 0, lineEnd;
+ var line, pos;
+ var fields, fieldVal;
+ var switchName;
+ var srcHostname = '';
+ var desHostname = '';
+ var type, state;
+
+ while ( lineBeg < textEnd ) {
+ lineEnd = text.indexOf('\n', lineBeg);
if ( lineEnd < 0 ) {
- lineEnd = textEnd;
+ lineEnd = text.indexOf('\r', lineBeg);
+ if ( lineEnd < 0 ) {
+ lineEnd = textEnd;
+ }
}
- }
- line = text.slice(lineBeg, lineEnd).trim();
- lineBeg = lineEnd + 1;
-
- pos = line.indexOf('# ');
- if ( pos !== -1 ) {
- line = line.slice(0, pos).trim();
- }
- if ( line === '' ) {
- continue;
- }
+ line = text.slice(lineBeg, lineEnd).trim();
+ lineBeg = lineEnd + 1;
- fields = line.split(/\s+/);
+ pos = line.indexOf('# ');
+ if ( pos !== -1 ) {
+ line = line.slice(0, pos).trim();
+ }
+ if ( line === '' ) {
+ continue;
+ }
- // Less than 2 fields makes no sense
- if ( fields.length < 2 ) {
- continue;
- }
+ fields = line.split(/\s+/);
- fieldVal = fields[0];
+ // Less than 2 fields makes no sense
+ if ( fields.length < 2 ) {
+ continue;
+ }
- // Special directives:
+ fieldVal = fields[0];
- // title
- pos = fieldVal.indexOf('title:');
- if ( pos !== -1 ) {
- // TODO
- continue;
- }
+ // Special directives:
- // Name
- pos = fieldVal.indexOf('name:');
- if ( pos !== -1 ) {
- // TODO
- continue;
- }
+ // title
+ pos = fieldVal.indexOf('title:');
+ if ( pos !== -1 ) {
+ // TODO
+ continue;
+ }
- // Switch on/off
+ // Name
+ pos = fieldVal.indexOf('name:');
+ if ( pos !== -1 ) {
+ // TODO
+ continue;
+ }
- // `switch:` srcHostname state
- // state = [`true`, `false`]
- switchName = '';
- if ( fieldVal === 'switch:' || fieldVal === 'matrix:' ) {
- fieldVal = 'matrix-off:';
- }
- pos = fieldVal.indexOf(':');
- if ( pos !== -1 ) {
- switchName = fieldVal.slice(0, pos);
- }
- if ( switchBitOffsets.has(switchName) ) {
- srcHostname = punycode.toASCII(fields[1]);
+ // Switch on/off
- // No state field: reject
- fieldVal = fields[2];
- if ( fieldVal === null ) {
- continue;
+ // `switch:` srcHostname state
+ // state = [`true`, `false`]
+ switchName = '';
+ if ( fieldVal === 'switch:' || fieldVal === 'matrix:' ) {
+ fieldVal = 'matrix-off:';
+ }
+ pos = fieldVal.indexOf(':');
+ if ( pos !== -1 ) {
+ switchName = fieldVal.slice(0, pos);
}
- // Unknown state: reject
- if ( nameToSwitchStateMap.hasOwnProperty(fieldVal) === false ) {
+ if ( switchBitOffsets.has(switchName) ) {
+ srcHostname = punycode.toASCII(fields[1]);
+
+ // No state field: reject
+ fieldVal = fields[2];
+ if ( fieldVal === null ) {
+ continue;
+ }
+ // Unknown state: reject
+ if ( nameToSwitchStateMap.hasOwnProperty(fieldVal) === false ) {
+ continue;
+ }
+
+ // Backward compatibility:
+ // `chromium-behind-the-scene` is now `behind-the-scene`
+ if ( srcHostname === 'chromium-behind-the-scene' ) {
+ srcHostname = 'behind-the-scene';
+ }
+
+ matrix.setSwitch(switchName, srcHostname, nameToSwitchStateMap[fieldVal]);
continue;
}
- // Backward compatibility:
- // `chromium-behind-the-scene` is now `behind-the-scene`
- if ( srcHostname === 'chromium-behind-the-scene' ) {
- srcHostname = 'behind-the-scene';
+ // Unknown directive
+ if ( fieldVal.endsWith(':') ) {
+ continue;
}
- matrix.setSwitch(switchName, srcHostname, nameToSwitchStateMap[fieldVal]);
- continue;
- }
-
- // Unknown directive
- if ( fieldVal.endsWith(':') ) {
- continue;
- }
+ // Valid rule syntax:
- // Valid rule syntax:
+ // srcHostname desHostname [type [state]]
+ // type = a valid request type
+ // state = [`block`, `allow`, `inherit`]
- // srcHostname desHostname [type [state]]
- // type = a valid request type
- // state = [`block`, `allow`, `inherit`]
+ // srcHostname desHostname type
+ // type = a valid request type
+ // state = `allow`
- // srcHostname desHostname type
- // type = a valid request type
- // state = `allow`
+ // srcHostname desHostname
+ // type = `*`
+ // state = `allow`
- // srcHostname desHostname
- // type = `*`
- // state = `allow`
+ // Lines with invalid syntax silently ignored
- // Lines with invalid syntax silently ignored
+ srcHostname = punycode.toASCII(fields[0]);
+ desHostname = punycode.toASCII(fields[1]);
- srcHostname = punycode.toASCII(fields[0]);
- desHostname = punycode.toASCII(fields[1]);
-
- fieldVal = fields[2];
+ fieldVal = fields[2];
- if ( fieldVal !== undefined ) {
- type = fieldVal;
- // https://github.com/gorhill/uMatrix/issues/759
- // Backward compatibility.
- if ( type === 'plugin' ) {
- type = 'media';
- }
- // Unknown type: reject
- if ( typeBitOffsets.has(type) === false ) {
- continue;
+ if ( fieldVal !== undefined ) {
+ type = fieldVal;
+ // https://github.com/gorhill/uMatrix/issues/759
+ // Backward compatibility.
+ if ( type === 'plugin' ) {
+ type = 'media';
+ }
+ // Unknown type: reject
+ if ( typeBitOffsets.has(type) === false ) {
+ continue;
+ }
+ } else {
+ type = '*';
}
- } else {
- type = '*';
- }
- fieldVal = fields[3];
+ fieldVal = fields[3];
- if ( fieldVal !== undefined ) {
- // Unknown state: reject
- if ( nameToStateMap.hasOwnProperty(fieldVal) === false ) {
- continue;
+ if ( fieldVal !== undefined ) {
+ // Unknown state: reject
+ if ( nameToStateMap.hasOwnProperty(fieldVal) === false ) {
+ continue;
+ }
+ state = nameToStateMap[fieldVal];
+ } else {
+ state = 2;
}
- state = nameToStateMap[fieldVal];
- } else {
- state = 2;
- }
- matrix.setCell(srcHostname, desHostname, type, state);
- }
+ matrix.setCell(srcHostname, desHostname, type, state);
+ }
- if ( !append ) {
- this.assign(matrix);
- }
+ if ( !append ) {
+ this.assign(matrix);
+ }
- this.modifiedTime = Date.now();
-};
+ this.modifiedTime = Date.now();
+ };
-/******************************************************************************/
+ /******************************************************************************/
-Matrix.prototype.toSelfie = function() {
- return {
- magicId: magicId,
- switches: Array.from(this.switches),
- rules: Array.from(this.rules)
+ Matrix.prototype.toSelfie = function() {
+ return {
+ magicId: magicId,
+ switches: Array.from(this.switches),
+ rules: Array.from(this.rules)
+ };
};
-};
-/******************************************************************************/
+ /******************************************************************************/
-Matrix.prototype.fromSelfie = function(selfie) {
- if ( selfie.magicId !== magicId ) { return false; }
- this.switches = new Map(selfie.switches);
- this.rules = new Map(selfie.rules);
- this.modifiedTime = Date.now();
- return true;
-};
+ Matrix.prototype.fromSelfie = function(selfie) {
+ if ( selfie.magicId !== magicId ) { return false; }
+ this.switches = new Map(selfie.switches);
+ this.rules = new Map(selfie.rules);
+ this.modifiedTime = Date.now();
+ return true;
+ };
-/******************************************************************************/
+ /******************************************************************************/
-Matrix.prototype.diff = function(other, srcHostname, desHostnames) {
- var out = [];
- var desHostname, type;
- var switchName, i, thisVal, otherVal;
- for (;;) {
- for ( switchName of switchBitOffsets.keys() ) {
- thisVal = this.evaluateSwitch(switchName, srcHostname);
- otherVal = other.evaluateSwitch(switchName, srcHostname);
- if ( thisVal !== otherVal ) {
- out.push({
- 'what': switchName,
- 'src': srcHostname
- });
+ Matrix.prototype.diff = function(other, srcHostname, desHostnames) {
+ var out = [];
+ var desHostname, type;
+ var switchName, i, thisVal, otherVal;
+ for (;;) {
+ for ( switchName of switchBitOffsets.keys() ) {
+ thisVal = this.evaluateSwitch(switchName, srcHostname);
+ otherVal = other.evaluateSwitch(switchName, srcHostname);
+ if ( thisVal !== otherVal ) {
+ out.push({
+ 'what': switchName,
+ 'src': srcHostname
+ });
+ }
}
- }
- i = desHostnames.length;
- while ( i-- ) {
- desHostname = desHostnames[i];
- for ( type of typeBitOffsets.keys() ) {
- thisVal = this.evaluateCell(srcHostname, desHostname, type);
- otherVal = other.evaluateCell(srcHostname, desHostname, type);
- if ( thisVal === otherVal ) { continue; }
- out.push({
- 'what': 'rule',
- 'src': srcHostname,
- 'des': desHostname,
- 'type': type
- });
+ i = desHostnames.length;
+ while ( i-- ) {
+ desHostname = desHostnames[i];
+ for ( type of typeBitOffsets.keys() ) {
+ thisVal = this.evaluateCell(srcHostname, desHostname, type);
+ otherVal = other.evaluateCell(srcHostname, desHostname, type);
+ if ( thisVal === otherVal ) { continue; }
+ out.push({
+ 'what': 'rule',
+ 'src': srcHostname,
+ 'des': desHostname,
+ 'type': type
+ });
+ }
+ }
+ srcHostname = toBroaderHostname(srcHostname);
+ if ( srcHostname === '' ) {
+ break;
}
}
- srcHostname = toBroaderHostname(srcHostname);
- if ( srcHostname === '' ) {
- break;
- }
- }
- return out;
-};
+ return out;
+ };
-/******************************************************************************/
+ /******************************************************************************/
-Matrix.prototype.applyDiff = function(diff, from) {
- var changed = false;
- var i = diff.length;
- var action, val;
- while ( i-- ) {
- action = diff[i];
- if ( action.what === 'rule' ) {
- val = from.evaluateCell(action.src, action.des, action.type);
- changed = this.setCell(action.src, action.des, action.type, val) || changed;
- continue;
- }
- if ( switchBitOffsets.has(action.what) ) {
- val = from.evaluateSwitch(action.what, action.src);
- changed = this.setSwitch(action.what, action.src, val) || changed;
- continue;
- }
- }
- return changed;
-};
+ Matrix.prototype.applyDiff = function(diff, from) {
+ var changed = false;
+ var i = diff.length;
+ var action, val;
+ while ( i-- ) {
+ action = diff[i];
+ if ( action.what === 'rule' ) {
+ val = from.evaluateCell(action.src, action.des, action.type);
+ changed = this.setCell(action.src, action.des, action.type, val) || changed;
+ continue;
+ }
+ if ( switchBitOffsets.has(action.what) ) {
+ val = from.evaluateSwitch(action.what, action.src);
+ changed = this.setSwitch(action.what, action.src, val) || changed;
+ continue;
+ }
+ }
+ return changed;
+ };
-/******************************************************************************/
+ /******************************************************************************/
-return Matrix;
+ return Matrix;
-/******************************************************************************/
+ /******************************************************************************/
-// https://www.youtube.com/watch?v=wlNrQGmj6oQ
+ // https://www.youtube.com/watch?v=wlNrQGmj6oQ
})();
diff --git a/js/messaging.js b/js/messaging.js
index 45d4f70..3252872 100644
--- a/js/messaging.js
+++ b/js/messaging.js
@@ -30,105 +30,105 @@
(function() {
-var ηm = ηMatrix;
+ var ηm = ηMatrix;
-/******************************************************************************/
+ /******************************************************************************/
-// Default is for commonly used message.
+ // Default is for commonly used message.
-function onMessage(request, sender, callback) {
- // Async
- switch ( request.what ) {
- case 'getAssetContent':
- ηm.assets.get(request.url, { dontCache: true }, callback);
- return;
+ function onMessage(request, sender, callback) {
+ // Async
+ switch ( request.what ) {
+ case 'getAssetContent':
+ ηm.assets.get(request.url, { dontCache: true }, callback);
+ return;
- case 'selectHostsFiles':
- ηm.selectHostsFiles(request, callback);
- return;
+ case 'selectHostsFiles':
+ ηm.selectHostsFiles(request, callback);
+ return;
- default:
- break;
- }
+ default:
+ break;
+ }
- // Sync
- var response;
-
- switch ( request.what ) {
- case 'forceReloadTab':
- ηm.forceReload(request.tabId, request.bypassCache);
- break;
-
- case 'forceUpdateAssets':
- ηm.scheduleAssetUpdater(0);
- ηm.assets.updateStart({ delay: 2000 });
- break;
-
- case 'getUserSettings':
- response = {
- userSettings: ηm.userSettings,
- matrixSwitches: {
- 'https-strict': ηm.pMatrix.evaluateSwitch('https-strict', '*') === 1,
- 'referrer-spoof': ηm.pMatrix.evaluateSwitch('referrer-spoof', '*') === 1,
- 'noscript-spoof': ηm.pMatrix.evaluateSwitch('noscript-spoof', '*') === 1
- }
- };
- break;
+ // Sync
+ var response;
- case 'gotoExtensionURL':
- ηm.gotoExtensionURL(request);
- break;
+ switch ( request.what ) {
+ case 'forceReloadTab':
+ ηm.forceReload(request.tabId, request.bypassCache);
+ break;
- case 'gotoURL':
- ηm.gotoURL(request);
- break;
+ case 'forceUpdateAssets':
+ ηm.scheduleAssetUpdater(0);
+ ηm.assets.updateStart({ delay: 2000 });
+ break;
- case 'mustBlock':
- response = ηm.mustBlock(
- request.scope,
- request.hostname,
- request.type
- );
- break;
+ case 'getUserSettings':
+ response = {
+ userSettings: ηm.userSettings,
+ matrixSwitches: {
+ 'https-strict': ηm.pMatrix.evaluateSwitch('https-strict', '*') === 1,
+ 'referrer-spoof': ηm.pMatrix.evaluateSwitch('referrer-spoof', '*') === 1,
+ 'noscript-spoof': ηm.pMatrix.evaluateSwitch('noscript-spoof', '*') === 1
+ }
+ };
+ break;
- case 'readRawSettings':
- response = ηm.stringFromRawSettings();
- break;
+ case 'gotoExtensionURL':
+ ηm.gotoExtensionURL(request);
+ break;
- case 'reloadHostsFiles':
- ηm.reloadHostsFiles();
- break;
+ case 'gotoURL':
+ ηm.gotoURL(request);
+ break;
- case 'setMatrixSwitch':
- ηm.tMatrix.setSwitch(request.switchName, '*', request.state);
- if ( ηm.pMatrix.setSwitch(request.switchName, '*', request.state) ) {
- ηm.saveMatrix();
- }
- break;
+ case 'mustBlock':
+ response = ηm.mustBlock(
+ request.scope,
+ request.hostname,
+ request.type
+ );
+ break;
- case 'userSettings':
- if ( request.hasOwnProperty('value') === false ) {
- request.value = undefined;
- }
- response = ηm.changeUserSettings(request.name, request.value);
- break;
+ case 'readRawSettings':
+ response = ηm.stringFromRawSettings();
+ break;
+
+ case 'reloadHostsFiles':
+ ηm.reloadHostsFiles();
+ break;
- case 'writeRawSettings':
- ηm.rawSettingsFromString(request.content);
- break;
+ case 'setMatrixSwitch':
+ ηm.tMatrix.setSwitch(request.switchName, '*', request.state);
+ if ( ηm.pMatrix.setSwitch(request.switchName, '*', request.state) ) {
+ ηm.saveMatrix();
+ }
+ break;
- default:
- return vAPI.messaging.UNHANDLED;
- }
+ case 'userSettings':
+ if ( request.hasOwnProperty('value') === false ) {
+ request.value = undefined;
+ }
+ response = ηm.changeUserSettings(request.name, request.value);
+ break;
- callback(response);
-}
+ case 'writeRawSettings':
+ ηm.rawSettingsFromString(request.content);
+ break;
-/******************************************************************************/
+ default:
+ return vAPI.messaging.UNHANDLED;
+ }
-vAPI.messaging.setup(onMessage);
+ callback(response);
+ }
-/******************************************************************************/
+ /******************************************************************************/
+
+ vAPI.messaging.setup(onMessage);
+
+ /******************************************************************************/
})();
@@ -137,256 +137,256 @@ vAPI.messaging.setup(onMessage);
(function() {
-// popup.js
+ // popup.js
-var ηm = ηMatrix;
-
-/******************************************************************************/
+ var ηm = ηMatrix;
-// Constructor is faster than object literal
+ /******************************************************************************/
-var RowSnapshot = function(srcHostname, desHostname, desDomain) {
- this.domain = desDomain;
- this.temporary = ηm.tMatrix.evaluateRowZXY(srcHostname, desHostname);
- this.permanent = ηm.pMatrix.evaluateRowZXY(srcHostname, desHostname);
- this.counts = RowSnapshot.counts.slice();
- this.totals = RowSnapshot.counts.slice();
-};
+ // Constructor is faster than object literal
-RowSnapshot.counts = (function() {
- var aa = [];
- for ( var i = 0, n = ηm.Matrix.columnHeaderIndices.size; i < n; i++ ) {
- aa[i] = 0;
- }
- return aa;
-})();
+ var RowSnapshot = function(srcHostname, desHostname, desDomain) {
+ this.domain = desDomain;
+ this.temporary = ηm.tMatrix.evaluateRowZXY(srcHostname, desHostname);
+ this.permanent = ηm.pMatrix.evaluateRowZXY(srcHostname, desHostname);
+ this.counts = RowSnapshot.counts.slice();
+ this.totals = RowSnapshot.counts.slice();
+ };
-/******************************************************************************/
+ RowSnapshot.counts = (function() {
+ var aa = [];
+ for ( var i = 0, n = ηm.Matrix.columnHeaderIndices.size; i < n; i++ ) {
+ aa[i] = 0;
+ }
+ return aa;
+ })();
+
+ /******************************************************************************/
+
+ var matrixSnapshot = function(pageStore, details) {
+ var ηmuser = ηm.userSettings;
+ var headerIndices = ηm.Matrix.columnHeaderIndices;
+
+ var r = {
+ appVersion: vAPI.app.version,
+ blockedCount: pageStore.requestStats.blocked.all,
+ collapseAllDomains: ηmuser.popupCollapseAllDomains,
+ collapseBlacklistedDomains: ηmuser.popupCollapseBlacklistedDomains,
+ diff: [],
+ domain: pageStore.pageDomain,
+ has3pReferrer: pageStore.has3pReferrer,
+ hasMixedContent: pageStore.hasMixedContent,
+ hasNoscriptTags: pageStore.hasNoscriptTags,
+ hasWebWorkers: pageStore.hasWebWorkers,
+ headerIndices: Array.from(headerIndices),
+ hostname: pageStore.pageHostname,
+ mtxContentModified: pageStore.mtxContentModifiedTime !== details.mtxContentModifiedTime,
+ mtxCountModified: pageStore.mtxCountModifiedTime !== details.mtxCountModifiedTime,
+ mtxContentModifiedTime: pageStore.mtxContentModifiedTime,
+ mtxCountModifiedTime: pageStore.mtxCountModifiedTime,
+ pMatrixModified: ηm.pMatrix.modifiedTime !== details.pMatrixModifiedTime,
+ pMatrixModifiedTime: ηm.pMatrix.modifiedTime,
+ pSwitches: {},
+ rows: {},
+ rowCount: 0,
+ scope: '*',
+ tabId: pageStore.tabId,
+ tMatrixModified: ηm.tMatrix.modifiedTime !== details.tMatrixModifiedTime,
+ tMatrixModifiedTime: ηm.tMatrix.modifiedTime,
+ tSwitches: {},
+ url: pageStore.pageUrl,
+ userSettings: {
+ colorBlindFriendly: ηmuser.colorBlindFriendly,
+ displayTextSize: ηmuser.displayTextSize,
+ popupScopeLevel: ηmuser.popupScopeLevel
+ }
+ };
-var matrixSnapshot = function(pageStore, details) {
- var ηmuser = ηm.userSettings;
- var headerIndices = ηm.Matrix.columnHeaderIndices;
-
- var r = {
- appVersion: vAPI.app.version,
- blockedCount: pageStore.requestStats.blocked.all,
- collapseAllDomains: ηmuser.popupCollapseAllDomains,
- collapseBlacklistedDomains: ηmuser.popupCollapseBlacklistedDomains,
- diff: [],
- domain: pageStore.pageDomain,
- has3pReferrer: pageStore.has3pReferrer,
- hasMixedContent: pageStore.hasMixedContent,
- hasNoscriptTags: pageStore.hasNoscriptTags,
- hasWebWorkers: pageStore.hasWebWorkers,
- headerIndices: Array.from(headerIndices),
- hostname: pageStore.pageHostname,
- mtxContentModified: pageStore.mtxContentModifiedTime !== details.mtxContentModifiedTime,
- mtxCountModified: pageStore.mtxCountModifiedTime !== details.mtxCountModifiedTime,
- mtxContentModifiedTime: pageStore.mtxContentModifiedTime,
- mtxCountModifiedTime: pageStore.mtxCountModifiedTime,
- pMatrixModified: ηm.pMatrix.modifiedTime !== details.pMatrixModifiedTime,
- pMatrixModifiedTime: ηm.pMatrix.modifiedTime,
- pSwitches: {},
- rows: {},
- rowCount: 0,
- scope: '*',
- tabId: pageStore.tabId,
- tMatrixModified: ηm.tMatrix.modifiedTime !== details.tMatrixModifiedTime,
- tMatrixModifiedTime: ηm.tMatrix.modifiedTime,
- tSwitches: {},
- url: pageStore.pageUrl,
- userSettings: {
- colorBlindFriendly: ηmuser.colorBlindFriendly,
- displayTextSize: ηmuser.displayTextSize,
- popupScopeLevel: ηmuser.popupScopeLevel
+ if ( typeof details.scope === 'string' ) {
+ r.scope = details.scope;
+ } else if ( ηmuser.popupScopeLevel === 'site' ) {
+ r.scope = r.hostname;
+ } else if ( ηmuser.popupScopeLevel === 'domain' ) {
+ r.scope = r.domain;
}
- };
- if ( typeof details.scope === 'string' ) {
- r.scope = details.scope;
- } else if ( ηmuser.popupScopeLevel === 'site' ) {
- r.scope = r.hostname;
- } else if ( ηmuser.popupScopeLevel === 'domain' ) {
- r.scope = r.domain;
- }
+ for ( var switchName of ηm.Matrix.switchNames ) {
+ r.tSwitches[switchName] = ηm.tMatrix.evaluateSwitchZ(switchName, r.scope);
+ r.pSwitches[switchName] = ηm.pMatrix.evaluateSwitchZ(switchName, r.scope);
+ }
- for ( var switchName of ηm.Matrix.switchNames ) {
- r.tSwitches[switchName] = ηm.tMatrix.evaluateSwitchZ(switchName, r.scope);
- r.pSwitches[switchName] = ηm.pMatrix.evaluateSwitchZ(switchName, r.scope);
- }
+ // These rows always exist
+ r.rows['*'] = new RowSnapshot(r.scope, '*', '*');
+ r.rows['1st-party'] = new RowSnapshot(r.scope, '1st-party', '1st-party');
+ r.rowCount += 1;
+
+ var ηmuri = ηm.URI;
+ var reqType, reqHostname, reqDomain;
+ var desHostname;
+ var row, typeIndex;
+ var anyIndex = headerIndices.get('*');
+ var pos, count;
+
+ for ( var entry of pageStore.hostnameTypeCells ) {
+ pos = entry[0].indexOf(' ');
+ reqHostname = entry[0].slice(0, pos);
+ reqType = entry[0].slice(pos + 1);
+ // rhill 2013-10-23: hostname can be empty if the request is a data url
+ // https://github.com/gorhill/httpswitchboard/issues/26
+ if ( reqHostname === '' ) {
+ reqHostname = pageStore.pageHostname;
+ }
+ reqDomain = ηmuri.domainFromHostname(reqHostname) || reqHostname;
+
+ // We want rows of self and ancestors
+ desHostname = reqHostname;
+ for (;;) {
+ // If row exists, ancestors exist
+ if ( r.rows.hasOwnProperty(desHostname) !== false ) { break; }
+ r.rows[desHostname] = new RowSnapshot(r.scope, desHostname, reqDomain);
+ r.rowCount += 1;
+ if ( desHostname === reqDomain ) { break; }
+ pos = desHostname.indexOf('.');
+ if ( pos === -1 ) { break; }
+ desHostname = desHostname.slice(pos + 1);
+ }
- // These rows always exist
- r.rows['*'] = new RowSnapshot(r.scope, '*', '*');
- r.rows['1st-party'] = new RowSnapshot(r.scope, '1st-party', '1st-party');
- r.rowCount += 1;
-
- var ηmuri = ηm.URI;
- var reqType, reqHostname, reqDomain;
- var desHostname;
- var row, typeIndex;
- var anyIndex = headerIndices.get('*');
- var pos, count;
-
- for ( var entry of pageStore.hostnameTypeCells ) {
- pos = entry[0].indexOf(' ');
- reqHostname = entry[0].slice(0, pos);
- reqType = entry[0].slice(pos + 1);
- // rhill 2013-10-23: hostname can be empty if the request is a data url
- // https://github.com/gorhill/httpswitchboard/issues/26
- if ( reqHostname === '' ) {
- reqHostname = pageStore.pageHostname;
- }
- reqDomain = ηmuri.domainFromHostname(reqHostname) || reqHostname;
-
- // We want rows of self and ancestors
- desHostname = reqHostname;
- for (;;) {
- // If row exists, ancestors exist
- if ( r.rows.hasOwnProperty(desHostname) !== false ) { break; }
- r.rows[desHostname] = new RowSnapshot(r.scope, desHostname, reqDomain);
- r.rowCount += 1;
- if ( desHostname === reqDomain ) { break; }
- pos = desHostname.indexOf('.');
- if ( pos === -1 ) { break; }
- desHostname = desHostname.slice(pos + 1);
+ count = entry[1].size;
+ typeIndex = headerIndices.get(reqType);
+ row = r.rows[reqHostname];
+ row.counts[typeIndex] += count;
+ row.counts[anyIndex] += count;
+ row = r.rows[reqDomain];
+ row.totals[typeIndex] += count;
+ row.totals[anyIndex] += count;
+ row = r.rows['*'];
+ row.totals[typeIndex] += count;
+ row.totals[anyIndex] += count;
}
- count = entry[1].size;
- typeIndex = headerIndices.get(reqType);
- row = r.rows[reqHostname];
- row.counts[typeIndex] += count;
- row.counts[anyIndex] += count;
- row = r.rows[reqDomain];
- row.totals[typeIndex] += count;
- row.totals[anyIndex] += count;
- row = r.rows['*'];
- row.totals[typeIndex] += count;
- row.totals[anyIndex] += count;
- }
+ r.diff = ηm.tMatrix.diff(ηm.pMatrix, r.hostname, Object.keys(r.rows));
- r.diff = ηm.tMatrix.diff(ηm.pMatrix, r.hostname, Object.keys(r.rows));
+ return r;
+ };
- return r;
-};
+ /******************************************************************************/
-/******************************************************************************/
+ var matrixSnapshotFromTabId = function(details, callback) {
+ var matrixSnapshotIf = function(tabId, details) {
+ var pageStore = ηm.pageStoreFromTabId(tabId);
+ if ( pageStore === null ) {
+ callback('ENOTFOUND');
+ return;
+ }
-var matrixSnapshotFromTabId = function(details, callback) {
- var matrixSnapshotIf = function(tabId, details) {
- var pageStore = ηm.pageStoreFromTabId(tabId);
- if ( pageStore === null ) {
- callback('ENOTFOUND');
+ // First verify whether we must return data or not.
+ if (
+ ηm.tMatrix.modifiedTime === details.tMatrixModifiedTime &&
+ ηm.pMatrix.modifiedTime === details.pMatrixModifiedTime &&
+ pageStore.mtxContentModifiedTime === details.mtxContentModifiedTime &&
+ pageStore.mtxCountModifiedTime === details.mtxCountModifiedTime
+ ) {
+ callback('ENOCHANGE');
+ return ;
+ }
+
+ callback(matrixSnapshot(pageStore, details));
+ };
+
+ // Specific tab id requested?
+ if ( details.tabId ) {
+ matrixSnapshotIf(details.tabId, details);
return;
}
- // First verify whether we must return data or not.
- if (
- ηm.tMatrix.modifiedTime === details.tMatrixModifiedTime &&
- ηm.pMatrix.modifiedTime === details.pMatrixModifiedTime &&
- pageStore.mtxContentModifiedTime === details.mtxContentModifiedTime &&
- pageStore.mtxCountModifiedTime === details.mtxCountModifiedTime
- ) {
- callback('ENOCHANGE');
- return ;
- }
+ // Fall back to currently active tab
+ var onTabReady = function(tab) {
+ if ( tab instanceof Object === false ) {
+ callback('ENOTFOUND');
+ return;
+ }
+
+ // Allow examination of behind-the-scene requests
+ var tabId = tab.url.lastIndexOf(vAPI.getURL('dashboard.html'), 0) !== 0 ?
+ tab.id :
+ vAPI.noTabId;
+ matrixSnapshotIf(tabId, details);
+ };
- callback(matrixSnapshot(pageStore, details));
+ vAPI.tabs.get(null, onTabReady);
};
- // Specific tab id requested?
- if ( details.tabId ) {
- matrixSnapshotIf(details.tabId, details);
- return;
- }
+ /******************************************************************************/
- // Fall back to currently active tab
- var onTabReady = function(tab) {
- if ( tab instanceof Object === false ) {
- callback('ENOTFOUND');
+ var onMessage = function(request, sender, callback) {
+ // Async
+ switch ( request.what ) {
+ case 'matrixSnapshot':
+ matrixSnapshotFromTabId(request, callback);
return;
- }
-
- // Allow examination of behind-the-scene requests
- var tabId = tab.url.lastIndexOf(vAPI.getURL('dashboard.html'), 0) !== 0 ?
- tab.id :
- vAPI.noTabId;
- matrixSnapshotIf(tabId, details);
- };
- vAPI.tabs.get(null, onTabReady);
-};
+ default:
+ break;
+ }
-/******************************************************************************/
+ // Sync
+ var response;
-var onMessage = function(request, sender, callback) {
- // Async
- switch ( request.what ) {
- case 'matrixSnapshot':
- matrixSnapshotFromTabId(request, callback);
- return;
+ switch ( request.what ) {
+ case 'toggleMatrixSwitch':
+ ηm.tMatrix.setSwitchZ(
+ request.switchName,
+ request.srcHostname,
+ ηm.tMatrix.evaluateSwitchZ(request.switchName, request.srcHostname) === false
+ );
+ break;
- default:
- break;
- }
+ case 'blacklistMatrixCell':
+ ηm.tMatrix.blacklistCell(
+ request.srcHostname,
+ request.desHostname,
+ request.type
+ );
+ break;
- // Sync
- var response;
+ case 'whitelistMatrixCell':
+ ηm.tMatrix.whitelistCell(
+ request.srcHostname,
+ request.desHostname,
+ request.type
+ );
+ break;
- switch ( request.what ) {
- case 'toggleMatrixSwitch':
- ηm.tMatrix.setSwitchZ(
- request.switchName,
- request.srcHostname,
- ηm.tMatrix.evaluateSwitchZ(request.switchName, request.srcHostname) === false
- );
- break;
+ case 'graylistMatrixCell':
+ ηm.tMatrix.graylistCell(
+ request.srcHostname,
+ request.desHostname,
+ request.type
+ );
+ break;
- case 'blacklistMatrixCell':
- ηm.tMatrix.blacklistCell(
- request.srcHostname,
- request.desHostname,
- request.type
- );
- break;
+ case 'applyDiffToPermanentMatrix': // aka "persist"
+ if ( ηm.pMatrix.applyDiff(request.diff, ηm.tMatrix) ) {
+ ηm.saveMatrix();
+ }
+ break;
- case 'whitelistMatrixCell':
- ηm.tMatrix.whitelistCell(
- request.srcHostname,
- request.desHostname,
- request.type
- );
- break;
+ case 'applyDiffToTemporaryMatrix': // aka "revert"
+ ηm.tMatrix.applyDiff(request.diff, ηm.pMatrix);
+ break;
- case 'graylistMatrixCell':
- ηm.tMatrix.graylistCell(
- request.srcHostname,
- request.desHostname,
- request.type
- );
- break;
+ case 'revertTemporaryMatrix':
+ ηm.tMatrix.assign(ηm.pMatrix);
+ break;
- case 'applyDiffToPermanentMatrix': // aka "persist"
- if ( ηm.pMatrix.applyDiff(request.diff, ηm.tMatrix) ) {
- ηm.saveMatrix();
+ default:
+ return vAPI.messaging.UNHANDLED;
}
- break;
-
- case 'applyDiffToTemporaryMatrix': // aka "revert"
- ηm.tMatrix.applyDiff(request.diff, ηm.pMatrix);
- break;
-
- case 'revertTemporaryMatrix':
- ηm.tMatrix.assign(ηm.pMatrix);
- break;
- default:
- return vAPI.messaging.UNHANDLED;
- }
-
- callback(response);
-};
+ callback(response);
+ };
-vAPI.messaging.listen('popup.js', onMessage);
+ vAPI.messaging.listen('popup.js', onMessage);
})();
@@ -397,195 +397,195 @@ vAPI.messaging.listen('popup.js', onMessage);
(function() {
-var ηm = ηMatrix;
+ var ηm = ηMatrix;
-/******************************************************************************/
+ /******************************************************************************/
-var foundInlineCode = function(tabId, pageStore, details, type) {
- if ( pageStore === null ) { return; }
+ var foundInlineCode = function(tabId, pageStore, details, type) {
+ if ( pageStore === null ) { return; }
- var pageHostname = pageStore.pageHostname,
- ηmuri = ηm.URI.set(details.documentURI),
- frameURL = ηmuri.normalizedURI();
+ var pageHostname = pageStore.pageHostname,
+ ηmuri = ηm.URI.set(details.documentURI),
+ frameURL = ηmuri.normalizedURI();
- var blocked = details.blocked;
- if ( blocked === undefined ) {
- blocked = ηm.mustBlock(pageHostname, ηmuri.hostname, type);
- }
+ var blocked = details.blocked;
+ if ( blocked === undefined ) {
+ blocked = ηm.mustBlock(pageHostname, ηmuri.hostname, type);
+ }
- var mapTo = {
- css: 'style',
- script: 'script'
+ var mapTo = {
+ css: 'style',
+ script: 'script'
+ };
+
+ // https://github.com/gorhill/httpswitchboard/issues/333
+ // Look-up here whether inline scripting is blocked for the frame.
+ var url = frameURL + '{inline_' + mapTo[type] + '}';
+ pageStore.recordRequest(type, url, blocked);
+ ηm.logger.writeOne(tabId, 'net', pageHostname, url, type, blocked);
};
- // https://github.com/gorhill/httpswitchboard/issues/333
- // Look-up here whether inline scripting is blocked for the frame.
- var url = frameURL + '{inline_' + mapTo[type] + '}';
- pageStore.recordRequest(type, url, blocked);
- ηm.logger.writeOne(tabId, 'net', pageHostname, url, type, blocked);
-};
+ /******************************************************************************/
-/******************************************************************************/
+ var contentScriptLocalStorageHandler = function(tabId, originURL) {
+ var tabContext = ηm.tabContextManager.lookup(tabId);
+ if ( tabContext === null ) { return; }
-var contentScriptLocalStorageHandler = function(tabId, originURL) {
- var tabContext = ηm.tabContextManager.lookup(tabId);
- if ( tabContext === null ) { return; }
-
- var blocked = ηm.mustBlock(
- tabContext.rootHostname,
- ηm.URI.hostnameFromURI(originURL),
- 'cookie'
- );
-
- var pageStore = ηm.pageStoreFromTabId(tabId);
- if ( pageStore !== null ) {
- var requestURL = originURL + '/{localStorage}';
- pageStore.recordRequest('cookie', requestURL, blocked);
- ηm.logger.writeOne(tabId, 'net', tabContext.rootHostname, requestURL, 'cookie', blocked);
- }
+ var blocked = ηm.mustBlock(
+ tabContext.rootHostname,
+ ηm.URI.hostnameFromURI(originURL),
+ 'cookie'
+ );
- var removeStorage = blocked && ηm.userSettings.deleteLocalStorage;
- if ( removeStorage ) {
- ηm.localStorageRemovedCounter++;
- }
+ var pageStore = ηm.pageStoreFromTabId(tabId);
+ if ( pageStore !== null ) {
+ var requestURL = originURL + '/{localStorage}';
+ pageStore.recordRequest('cookie', requestURL, blocked);
+ ηm.logger.writeOne(tabId, 'net', tabContext.rootHostname, requestURL, 'cookie', blocked);
+ }
- return removeStorage;
-};
+ var removeStorage = blocked && ηm.userSettings.deleteLocalStorage;
+ if ( removeStorage ) {
+ ηm.localStorageRemovedCounter++;
+ }
-/******************************************************************************/
+ return removeStorage;
+ };
-// Evaluate many URLs against the matrix.
+ /******************************************************************************/
-var lookupBlockedCollapsibles = function(tabId, requests) {
- if ( placeholdersReadTime < ηm.rawSettingsWriteTime ) {
- placeholders = undefined;
- }
+ // Evaluate many URLs against the matrix.
- if ( placeholders === undefined ) {
- placeholders = {
- frame: ηm.rawSettings.framePlaceholder,
- image: ηm.rawSettings.imagePlaceholder
- };
- if ( placeholders.frame ) {
- placeholders.frameDocument =
- ηm.rawSettings.framePlaceholderDocument.replace(
- '{{bg}}',
- ηm.rawSettings.framePlaceholderBackground !== 'default' ?
- ηm.rawSettings.framePlaceholderBackground :
- ηm.rawSettings.placeholderBackground
- );
+ var lookupBlockedCollapsibles = function(tabId, requests) {
+ if ( placeholdersReadTime < ηm.rawSettingsWriteTime ) {
+ placeholders = undefined;
}
- if ( placeholders.image ) {
- placeholders.imageBorder =
- ηm.rawSettings.imagePlaceholderBorder !== 'default' ?
+
+ if ( placeholders === undefined ) {
+ placeholders = {
+ frame: ηm.rawSettings.framePlaceholder,
+ image: ηm.rawSettings.imagePlaceholder
+ };
+ if ( placeholders.frame ) {
+ placeholders.frameDocument =
+ ηm.rawSettings.framePlaceholderDocument.replace(
+ '{{bg}}',
+ ηm.rawSettings.framePlaceholderBackground !== 'default' ?
+ ηm.rawSettings.framePlaceholderBackground :
+ ηm.rawSettings.placeholderBackground
+ );
+ }
+ if ( placeholders.image ) {
+ placeholders.imageBorder =
+ ηm.rawSettings.imagePlaceholderBorder !== 'default' ?
ηm.rawSettings.imagePlaceholderBorder :
ηm.rawSettings.placeholderBorder;
- placeholders.imageBackground =
- ηm.rawSettings.imagePlaceholderBackground !== 'default' ?
+ placeholders.imageBackground =
+ ηm.rawSettings.imagePlaceholderBackground !== 'default' ?
ηm.rawSettings.imagePlaceholderBackground :
ηm.rawSettings.placeholderBackground;
+ }
+ placeholdersReadTime = Date.now();
}
- placeholdersReadTime = Date.now();
- }
- var response = {
- blockedResources: [],
- hash: requests.hash,
- id: requests.id,
- placeholders: placeholders
- };
+ var response = {
+ blockedResources: [],
+ hash: requests.hash,
+ id: requests.id,
+ placeholders: placeholders
+ };
- var tabContext = ηm.tabContextManager.lookup(tabId);
- if ( tabContext === null ) {
- return response;
- }
+ var tabContext = ηm.tabContextManager.lookup(tabId);
+ if ( tabContext === null ) {
+ return response;
+ }
- var pageStore = ηm.pageStoreFromTabId(tabId);
- if ( pageStore !== null ) {
- pageStore.lookupBlockedCollapsibles(requests, response);
- }
+ var pageStore = ηm.pageStoreFromTabId(tabId);
+ if ( pageStore !== null ) {
+ pageStore.lookupBlockedCollapsibles(requests, response);
+ }
- return response;
-};
+ return response;
+ };
-var placeholders,
- placeholdersReadTime = 0;
+ var placeholders,
+ placeholdersReadTime = 0;
-/******************************************************************************/
+ /******************************************************************************/
-var onMessage = function(request, sender, callback) {
- // Async
- switch ( request.what ) {
- default:
- break;
- }
+ var onMessage = function(request, sender, callback) {
+ // Async
+ switch ( request.what ) {
+ default:
+ break;
+ }
- var tabId = sender && sender.tab ? sender.tab.id || 0 : 0,
- tabContext = ηm.tabContextManager.lookup(tabId),
- rootHostname = tabContext && tabContext.rootHostname,
- pageStore = ηm.pageStoreFromTabId(tabId);
+ var tabId = sender && sender.tab ? sender.tab.id || 0 : 0,
+ tabContext = ηm.tabContextManager.lookup(tabId),
+ rootHostname = tabContext && tabContext.rootHostname,
+ pageStore = ηm.pageStoreFromTabId(tabId);
- // Sync
- var response;
+ // Sync
+ var response;
- switch ( request.what ) {
- case 'contentScriptHasLocalStorage':
- response = contentScriptLocalStorageHandler(tabId, request.originURL);
- break;
+ switch ( request.what ) {
+ case 'contentScriptHasLocalStorage':
+ response = contentScriptLocalStorageHandler(tabId, request.originURL);
+ break;
- case 'lookupBlockedCollapsibles':
- response = lookupBlockedCollapsibles(tabId, request);
- break;
+ case 'lookupBlockedCollapsibles':
+ response = lookupBlockedCollapsibles(tabId, request);
+ break;
- case 'mustRenderNoscriptTags?':
- if ( tabContext === null ) { break; }
- response =
- ηm.tMatrix.mustBlock(rootHostname, rootHostname, 'script') &&
- ηm.tMatrix.evaluateSwitchZ('noscript-spoof', rootHostname);
- if ( pageStore !== null ) {
- pageStore.hasNoscriptTags = true;
- }
- // https://github.com/gorhill/uMatrix/issues/225
- // A good place to force an update of the page title, as at
- // this point the DOM has been loaded.
- ηm.updateTitle(tabId);
- break;
-
- case 'securityPolicyViolation':
- if ( request.directive === 'worker-src' ) {
- var url = ηm.URI.hostnameFromURI(request.blockedURI) !== '' ?
- request.blockedURI :
- request.documentURI;
+ case 'mustRenderNoscriptTags?':
+ if ( tabContext === null ) { break; }
+ response =
+ ηm.tMatrix.mustBlock(rootHostname, rootHostname, 'script') &&
+ ηm.tMatrix.evaluateSwitchZ('noscript-spoof', rootHostname);
if ( pageStore !== null ) {
- pageStore.hasWebWorkers = true;
- pageStore.recordRequest('script', url, request.blocked);
+ pageStore.hasNoscriptTags = true;
}
+ // https://github.com/gorhill/uMatrix/issues/225
+ // A good place to force an update of the page title, as at
+ // this point the DOM has been loaded.
+ ηm.updateTitle(tabId);
+ break;
+
+ case 'securityPolicyViolation':
+ if ( request.directive === 'worker-src' ) {
+ var url = ηm.URI.hostnameFromURI(request.blockedURI) !== '' ?
+ request.blockedURI :
+ request.documentURI;
+ if ( pageStore !== null ) {
+ pageStore.hasWebWorkers = true;
+ pageStore.recordRequest('script', url, request.blocked);
+ }
+ if ( tabContext !== null ) {
+ ηm.logger.writeOne(tabId, 'net', rootHostname, url, 'worker', request.blocked);
+ }
+ } else if ( request.directive === 'script-src' ) {
+ foundInlineCode(tabId, pageStore, request, 'script');
+ } else if ( request.directive === 'style-src' ) {
+ foundInlineCode(tabId, pageStore, request, 'css');
+ }
+ break;
+
+ case 'shutdown?':
if ( tabContext !== null ) {
- ηm.logger.writeOne(tabId, 'net', rootHostname, url, 'worker', request.blocked);
+ response = ηm.tMatrix.evaluateSwitchZ('matrix-off', rootHostname);
}
- } else if ( request.directive === 'script-src' ) {
- foundInlineCode(tabId, pageStore, request, 'script');
- } else if ( request.directive === 'style-src' ) {
- foundInlineCode(tabId, pageStore, request, 'css');
- }
- break;
+ break;
- case 'shutdown?':
- if ( tabContext !== null ) {
- response = ηm.tMatrix.evaluateSwitchZ('matrix-off', rootHostname);
+ default:
+ return vAPI.messaging.UNHANDLED;
}
- break;
- default:
- return vAPI.messaging.UNHANDLED;
- }
-
- callback(response);
-};
+ callback(response);
+ };
-vAPI.messaging.listen('contentscript.js', onMessage);
+ vAPI.messaging.listen('contentscript.js', onMessage);
-/******************************************************************************/
+ /******************************************************************************/
})();
@@ -596,56 +596,56 @@ vAPI.messaging.listen('contentscript.js', onMessage);
(function() {
-/******************************************************************************/
+ /******************************************************************************/
-var ηm = ηMatrix;
+ var ηm = ηMatrix;
-/******************************************************************************/
+ /******************************************************************************/
-var onMessage = function(request, sender, callback) {
- // Async
- switch ( request.what ) {
- case 'cloudGetOptions':
- vAPI.cloud.getOptions(function(options) {
- options.enabled = ηm.userSettings.cloudStorageEnabled === true;
- callback(options);
- });
- return;
+ var onMessage = function(request, sender, callback) {
+ // Async
+ switch ( request.what ) {
+ case 'cloudGetOptions':
+ vAPI.cloud.getOptions(function(options) {
+ options.enabled = ηm.userSettings.cloudStorageEnabled === true;
+ callback(options);
+ });
+ return;
- case 'cloudSetOptions':
- vAPI.cloud.setOptions(request.options, callback);
- return;
+ case 'cloudSetOptions':
+ vAPI.cloud.setOptions(request.options, callback);
+ return;
- case 'cloudPull':
- return vAPI.cloud.pull(request.datakey, callback);
+ case 'cloudPull':
+ return vAPI.cloud.pull(request.datakey, callback);
- case 'cloudPush':
- return vAPI.cloud.push(request.datakey, request.data, callback);
+ case 'cloudPush':
+ return vAPI.cloud.push(request.datakey, request.data, callback);
- default:
- break;
- }
+ default:
+ break;
+ }
- // Sync
- var response;
+ // Sync
+ var response;
- switch ( request.what ) {
- // For when cloud storage is disabled.
- case 'cloudPull':
- // fallthrough
- case 'cloudPush':
- break;
+ switch ( request.what ) {
+ // For when cloud storage is disabled.
+ case 'cloudPull':
+ // fallthrough
+ case 'cloudPush':
+ break;
- default:
- return vAPI.messaging.UNHANDLED;
- }
+ default:
+ return vAPI.messaging.UNHANDLED;
+ }
- callback(response);
-};
+ callback(response);
+ };
-/******************************************************************************/
+ /******************************************************************************/
-vAPI.messaging.listen('cloud-ui.js', onMessage);
+ vAPI.messaging.listen('cloud-ui.js', onMessage);
})();
@@ -656,51 +656,51 @@ vAPI.messaging.listen('cloud-ui.js', onMessage);
(function() {
-var ηm = ηMatrix;
+ var ηm = ηMatrix;
-/******************************************************************************/
+ /******************************************************************************/
-var onMessage = function(request, sender, callback) {
+ var onMessage = function(request, sender, callback) {
- // Async
- switch ( request.what ) {
- default:
- break;
- }
+ // Async
+ switch ( request.what ) {
+ default:
+ break;
+ }
- // Sync
- var response;
+ // Sync
+ var response;
- switch ( request.what ) {
- case 'getUserRules':
- response = {
- temporaryRules: ηm.tMatrix.toString(),
- permanentRules: ηm.pMatrix.toString()
- };
- break;
+ switch ( request.what ) {
+ case 'getUserRules':
+ response = {
+ temporaryRules: ηm.tMatrix.toString(),
+ permanentRules: ηm.pMatrix.toString()
+ };
+ break;
- case 'setUserRules':
- if ( typeof request.temporaryRules === 'string' ) {
- ηm.tMatrix.fromString(request.temporaryRules);
- }
- if ( typeof request.permanentRules === 'string' ) {
- ηm.pMatrix.fromString(request.permanentRules);
- ηm.saveMatrix();
- }
- response = {
- temporaryRules: ηm.tMatrix.toString(),
- permanentRules: ηm.pMatrix.toString()
- };
- break;
+ case 'setUserRules':
+ if ( typeof request.temporaryRules === 'string' ) {
+ ηm.tMatrix.fromString(request.temporaryRules);
+ }
+ if ( typeof request.permanentRules === 'string' ) {
+ ηm.pMatrix.fromString(request.permanentRules);
+ ηm.saveMatrix();
+ }
+ response = {
+ temporaryRules: ηm.tMatrix.toString(),
+ permanentRules: ηm.pMatrix.toString()
+ };
+ break;
- default:
- return vAPI.messaging.UNHANDLED;
- }
+ default:
+ return vAPI.messaging.UNHANDLED;
+ }
- callback(response);
-};
+ callback(response);
+ };
-vAPI.messaging.listen('user-rules.js', onMessage);
+ vAPI.messaging.listen('user-rules.js', onMessage);
})();
@@ -711,87 +711,87 @@ vAPI.messaging.listen('user-rules.js', onMessage);
(function() {
-var ηm = ηMatrix;
+ var ηm = ηMatrix;
-/******************************************************************************/
+ /******************************************************************************/
-var prepEntries = function(entries) {
- var ηmuri = ηm.URI;
- var entry;
- for ( var k in entries ) {
- if ( entries.hasOwnProperty(k) === false ) {
- continue;
- }
- entry = entries[k];
- if ( typeof entry.homeURL === 'string' ) {
- entry.homeHostname = ηmuri.hostnameFromURI(entry.homeURL);
- entry.homeDomain = ηmuri.domainFromHostname(entry.homeHostname);
+ var prepEntries = function(entries) {
+ var ηmuri = ηm.URI;
+ var entry;
+ for ( var k in entries ) {
+ if ( entries.hasOwnProperty(k) === false ) {
+ continue;
+ }
+ entry = entries[k];
+ if ( typeof entry.homeURL === 'string' ) {
+ entry.homeHostname = ηmuri.hostnameFromURI(entry.homeURL);
+ entry.homeDomain = ηmuri.domainFromHostname(entry.homeHostname);
+ }
}
- }
-};
+ };
-/******************************************************************************/
+ /******************************************************************************/
-var getLists = function(callback) {
- var r = {
- autoUpdate: ηm.userSettings.autoUpdate,
- available: null,
- cache: null,
- current: ηm.liveHostsFiles,
- blockedHostnameCount: ηm.ubiquitousBlacklist.count
- };
- var onMetadataReady = function(entries) {
- r.cache = entries;
- prepEntries(r.cache);
- callback(r);
- };
- var onAvailableHostsFilesReady = function(lists) {
- r.available = lists;
- prepEntries(r.available);
- ηm.assets.metadata(onMetadataReady);
+ var getLists = function(callback) {
+ var r = {
+ autoUpdate: ηm.userSettings.autoUpdate,
+ available: null,
+ cache: null,
+ current: ηm.liveHostsFiles,
+ blockedHostnameCount: ηm.ubiquitousBlacklist.count
+ };
+ var onMetadataReady = function(entries) {
+ r.cache = entries;
+ prepEntries(r.cache);
+ callback(r);
+ };
+ var onAvailableHostsFilesReady = function(lists) {
+ r.available = lists;
+ prepEntries(r.available);
+ ηm.assets.metadata(onMetadataReady);
+ };
+ ηm.getAvailableHostsFiles(onAvailableHostsFilesReady);
};
- ηm.getAvailableHostsFiles(onAvailableHostsFilesReady);
-};
-/******************************************************************************/
+ /******************************************************************************/
-var onMessage = function(request, sender, callback) {
- var ηm = ηMatrix;
+ var onMessage = function(request, sender, callback) {
+ var ηm = ηMatrix;
- // Async
- switch ( request.what ) {
- case 'getLists':
- return getLists(callback);
+ // Async
+ switch ( request.what ) {
+ case 'getLists':
+ return getLists(callback);
- default:
- break;
- }
+ default:
+ break;
+ }
- // Sync
- var response;
+ // Sync
+ var response;
- switch ( request.what ) {
- case 'purgeCache':
- ηm.assets.purge(request.assetKey);
- ηm.assets.remove('compiled/' + request.assetKey);
- break;
+ switch ( request.what ) {
+ case 'purgeCache':
+ ηm.assets.purge(request.assetKey);
+ ηm.assets.remove('compiled/' + request.assetKey);
+ break;
- case 'purgeAllCaches':
- if ( request.hard ) {
- ηm.assets.remove(/./);
- } else {
- ηm.assets.purge(/./, 'public_suffix_list.dat');
- }
- break;
+ case 'purgeAllCaches':
+ if ( request.hard ) {
+ ηm.assets.remove(/./);
+ } else {
+ ηm.assets.purge(/./, 'public_suffix_list.dat');
+ }
+ break;
- default:
- return vAPI.messaging.UNHANDLED;
- }
+ default:
+ return vAPI.messaging.UNHANDLED;
+ }
- callback(response);
-};
+ callback(response);
+ };
-vAPI.messaging.listen('hosts-files.js', onMessage);
+ vAPI.messaging.listen('hosts-files.js', onMessage);
})();
@@ -802,162 +802,162 @@ vAPI.messaging.listen('hosts-files.js', onMessage);
(function() {
-var ηm = ηMatrix;
-
-/******************************************************************************/
-
-var restoreUserData = function(userData) {
- var countdown = 4;
- var onCountdown = function() {
- countdown -= 1;
- if ( countdown === 0 ) {
- vAPI.app.restart();
- }
- };
+ var ηm = ηMatrix;
- var onAllRemoved = function() {
- vAPI.storage.set(userData.settings, onCountdown);
- vAPI.storage.set({ userMatrix: userData.rules }, onCountdown);
- vAPI.storage.set({ liveHostsFiles: userData.hostsFiles }, onCountdown);
- if ( userData.rawSettings instanceof Object ) {
- ηMatrix.saveRawSettings(userData.rawSettings, onCountdown);
- }
- };
+ /******************************************************************************/
- // If we are going to restore all, might as well wipe out clean local
- // storage
- ηm.XAL.keyvalRemoveAll(onAllRemoved);
-};
+ var restoreUserData = function(userData) {
+ var countdown = 4;
+ var onCountdown = function() {
+ countdown -= 1;
+ if ( countdown === 0 ) {
+ vAPI.app.restart();
+ }
+ };
-/******************************************************************************/
+ var onAllRemoved = function() {
+ vAPI.storage.set(userData.settings, onCountdown);
+ vAPI.storage.set({ userMatrix: userData.rules }, onCountdown);
+ vAPI.storage.set({ liveHostsFiles: userData.hostsFiles }, onCountdown);
+ if ( userData.rawSettings instanceof Object ) {
+ ηMatrix.saveRawSettings(userData.rawSettings, onCountdown);
+ }
+ };
-var resetUserData = function() {
- var onAllRemoved = function() {
- vAPI.app.restart();
+ // If we are going to restore all, might as well wipe out clean local
+ // storage
+ ηm.XAL.keyvalRemoveAll(onAllRemoved);
};
- ηm.XAL.keyvalRemoveAll(onAllRemoved);
-};
-
-/******************************************************************************/
-var onMessage = function(request, sender, callback) {
- // Async
- switch ( request.what ) {
- default:
- break;
- }
+ /******************************************************************************/
- // Sync
- var response;
-
- switch ( request.what ) {
- case 'getAllUserData':
- response = {
- app: vAPI.app.name,
- version: vAPI.app.version,
- when: Date.now(),
- settings: ηm.userSettings,
- rules: ηm.pMatrix.toString(),
- hostsFiles: ηm.liveHostsFiles,
- rawSettings: ηm.rawSettings
+ var resetUserData = function() {
+ var onAllRemoved = function() {
+ vAPI.app.restart();
};
- break;
+ ηm.XAL.keyvalRemoveAll(onAllRemoved);
+ };
- case 'getSomeStats':
- response = {
- version: vAPI.app.version,
- storageUsed: ηm.storageUsed
- };
- break;
+ /******************************************************************************/
- case 'restoreAllUserData':
- restoreUserData(request.userData);
- break;
+ var onMessage = function(request, sender, callback) {
+ // Async
+ switch ( request.what ) {
+ default:
+ break;
+ }
- case 'resetAllUserData':
- resetUserData();
- break;
+ // Sync
+ var response;
+
+ switch ( request.what ) {
+ case 'getAllUserData':
+ response = {
+ app: vAPI.app.name,
+ version: vAPI.app.version,
+ when: Date.now(),
+ settings: ηm.userSettings,
+ rules: ηm.pMatrix.toString(),
+ hostsFiles: ηm.liveHostsFiles,
+ rawSettings: ηm.rawSettings
+ };
+ break;
- default:
- return vAPI.messaging.UNHANDLED;
- }
+ case 'getSomeStats':
+ response = {
+ version: vAPI.app.version,
+ storageUsed: ηm.storageUsed
+ };
+ break;
- callback(response);
-};
+ case 'restoreAllUserData':
+ restoreUserData(request.userData);
+ break;
-vAPI.messaging.listen('about.js', onMessage);
+ case 'resetAllUserData':
+ resetUserData();
+ break;
-/******************************************************************************/
-/******************************************************************************/
+ default:
+ return vAPI.messaging.UNHANDLED;
+ }
-// logger-ui.js
+ callback(response);
+ };
-(function() {
+ vAPI.messaging.listen('about.js', onMessage);
-/******************************************************************************/
+ /******************************************************************************/
+ /******************************************************************************/
-var ηm = ηMatrix,
- loggerURL = vAPI.getURL('logger-ui.html');
+ // logger-ui.js
-/******************************************************************************/
+ (function() {
-var onMessage = function(request, sender, callback) {
- // Async
- switch ( request.what ) {
- default:
- break;
- }
+ /******************************************************************************/
- // Sync
- var response;
+ var ηm = ηMatrix,
+ loggerURL = vAPI.getURL('logger-ui.html');
- switch ( request.what ) {
- case 'readMany':
- if (
- ηm.logger.ownerId !== undefined &&
- request.ownerId !== ηm.logger.ownerId
- ) {
- response = { unavailable: true };
- break;
- }
- var tabIds = {};
- for ( var tabId in ηm.pageStores ) {
- var pageStore = ηm.pageStoreFromTabId(tabId);
- if ( pageStore === null ) { continue; }
- if ( pageStore.rawUrl.startsWith(loggerURL) ) { continue; }
- tabIds[tabId] = pageStore.title || pageStore.rawUrl;
- }
- response = {
- colorBlind: false,
- entries: ηm.logger.readAll(request.ownerId),
- maxLoggedRequests: ηm.userSettings.maxLoggedRequests,
- noTabId: vAPI.noTabId,
- tabIds: tabIds,
- tabIdsToken: ηm.pageStoresToken
- };
- break;
+ /******************************************************************************/
- case 'releaseView':
- if ( request.ownerId === ηm.logger.ownerId ) {
- ηm.logger.ownerId = undefined;
- }
- break;
+ var onMessage = function(request, sender, callback) {
+ // Async
+ switch ( request.what ) {
+ default:
+ break;
+ }
- default:
- return vAPI.messaging.UNHANDLED;
- }
+ // Sync
+ var response;
+
+ switch ( request.what ) {
+ case 'readMany':
+ if (
+ ηm.logger.ownerId !== undefined &&
+ request.ownerId !== ηm.logger.ownerId
+ ) {
+ response = { unavailable: true };
+ break;
+ }
+ var tabIds = {};
+ for ( var tabId in ηm.pageStores ) {
+ var pageStore = ηm.pageStoreFromTabId(tabId);
+ if ( pageStore === null ) { continue; }
+ if ( pageStore.rawUrl.startsWith(loggerURL) ) { continue; }
+ tabIds[tabId] = pageStore.title || pageStore.rawUrl;
+ }
+ response = {
+ colorBlind: false,
+ entries: ηm.logger.readAll(request.ownerId),
+ maxLoggedRequests: ηm.userSettings.maxLoggedRequests,
+ noTabId: vAPI.noTabId,
+ tabIds: tabIds,
+ tabIdsToken: ηm.pageStoresToken
+ };
+ break;
+
+ case 'releaseView':
+ if ( request.ownerId === ηm.logger.ownerId ) {
+ ηm.logger.ownerId = undefined;
+ }
+ break;
+
+ default:
+ return vAPI.messaging.UNHANDLED;
+ }
- callback(response);
-};
+ callback(response);
+ };
-vAPI.messaging.listen('logger-ui.js', onMessage);
+ vAPI.messaging.listen('logger-ui.js', onMessage);
-/******************************************************************************/
+ /******************************************************************************/
-})();
+ })();
-/******************************************************************************/
-/******************************************************************************/
+ /******************************************************************************/
+ /******************************************************************************/
})();
diff --git a/js/pagestats.js b/js/pagestats.js
index 646bf01..276f099 100644
--- a/js/pagestats.js
+++ b/js/pagestats.js
@@ -27,248 +27,248 @@
ηMatrix.pageStoreFactory = (function() {
-/******************************************************************************/
+ /******************************************************************************/
-var ηm = ηMatrix;
+ var ηm = ηMatrix;
-/******************************************************************************/
+ /******************************************************************************/
-var BlockedCollapsibles = function() {
- this.boundPruneAsyncCallback = this.pruneAsyncCallback.bind(this);
- this.blocked = new Map();
- this.hash = 0;
- this.timer = null;
-};
-
-BlockedCollapsibles.prototype = {
-
- shelfLife: 10 * 1000,
-
- add: function(type, url, isSpecific) {
- if ( this.blocked.size === 0 ) { this.pruneAsync(); }
- var now = Date.now() / 1000 | 0;
- // The following "trick" is to encode the specifity into the lsb of the
- // time stamp so as to avoid to have to allocate a memory structure to
- // store both time stamp and specificity.
- if ( isSpecific ) {
- now |= 0x00000001;
- } else {
- now &= 0xFFFFFFFE;
- }
- this.blocked.set(type + ' ' + url, now);
- this.hash = now;
- },
-
- reset: function() {
- this.blocked.clear();
+ var BlockedCollapsibles = function() {
+ this.boundPruneAsyncCallback = this.pruneAsyncCallback.bind(this);
+ this.blocked = new Map();
this.hash = 0;
- if ( this.timer !== null ) {
- clearTimeout(this.timer);
- this.timer = null;
- }
- },
-
- pruneAsync: function() {
- if ( this.timer === null ) {
- this.timer = vAPI.setTimeout(
- this.boundPruneAsyncCallback,
- this.shelfLife * 2
- );
- }
- },
-
- pruneAsyncCallback: function() {
this.timer = null;
- var obsolete = Date.now() - this.shelfLife;
- for ( var entry of this.blocked ) {
- if ( entry[1] <= obsolete ) {
- this.blocked.delete(entry[0]);
+ };
+
+ BlockedCollapsibles.prototype = {
+
+ shelfLife: 10 * 1000,
+
+ add: function(type, url, isSpecific) {
+ if ( this.blocked.size === 0 ) { this.pruneAsync(); }
+ var now = Date.now() / 1000 | 0;
+ // The following "trick" is to encode the specifity into the lsb of the
+ // time stamp so as to avoid to have to allocate a memory structure to
+ // store both time stamp and specificity.
+ if ( isSpecific ) {
+ now |= 0x00000001;
+ } else {
+ now &= 0xFFFFFFFE;
}
- }
- if ( this.blocked.size !== 0 ) { this.pruneAsync(); }
- }
-};
-
-/******************************************************************************/
+ this.blocked.set(type + ' ' + url, now);
+ this.hash = now;
+ },
+
+ reset: function() {
+ this.blocked.clear();
+ this.hash = 0;
+ if ( this.timer !== null ) {
+ clearTimeout(this.timer);
+ this.timer = null;
+ }
+ },
+
+ pruneAsync: function() {
+ if ( this.timer === null ) {
+ this.timer = vAPI.setTimeout(
+ this.boundPruneAsyncCallback,
+ this.shelfLife * 2
+ );
+ }
+ },
-// Ref: Given a URL, returns a (somewhat) unique 32-bit value
-// Based on: FNV32a
-// http://www.isthe.com/chongo/tech/comp/fnv/index.html#FNV-reference-source
-// The rest is custom, suited for uMatrix.
-
-var PageStore = function(tabContext) {
- this.hostnameTypeCells = new Map();
- this.domains = new Set();
- this.blockedCollapsibles = new BlockedCollapsibles();
- this.requestStats = ηm.requestStatsFactory();
- this.off = false;
- this.init(tabContext);
-};
-
-PageStore.prototype = {
-
- collapsibleTypes: new Set([ 'image' ]),
- pageStoreJunkyard: [],
-
- init: function(tabContext) {
- this.tabId = tabContext.tabId;
- this.rawUrl = tabContext.rawURL;
- this.pageUrl = tabContext.normalURL;
- this.pageHostname = tabContext.rootHostname;
- this.pageDomain = tabContext.rootDomain;
- this.title = '';
- this.hostnameTypeCells.clear();
- this.domains.clear();
- this.allHostnamesString = ' ';
- this.blockedCollapsibles.reset();
- this.requestStats.reset();
- this.distinctRequestCount = 0;
- this.perLoadAllowedRequestCount = 0;
- this.perLoadBlockedRequestCount = 0;
- this.has3pReferrer = false;
- this.hasMixedContent = false;
- this.hasNoscriptTags = false;
- this.hasWebWorkers = false;
- this.incinerationTimer = null;
- this.mtxContentModifiedTime = 0;
- this.mtxCountModifiedTime = 0;
- return this;
- },
-
- dispose: function() {
- this.rawUrl = '';
- this.pageUrl = '';
- this.pageHostname = '';
- this.pageDomain = '';
- this.title = '';
- this.hostnameTypeCells.clear();
- this.domains.clear();
- this.allHostnamesString = ' ';
- this.blockedCollapsibles.reset();
- if ( this.incinerationTimer !== null ) {
- clearTimeout(this.incinerationTimer);
- this.incinerationTimer = null;
- }
- if ( this.pageStoreJunkyard.length < 8 ) {
- this.pageStoreJunkyard.push(this);
- }
- },
-
- cacheBlockedCollapsible: function(type, url, specificity) {
- if ( this.collapsibleTypes.has(type) ) {
- this.blockedCollapsibles.add(
- type,
- url,
- specificity !== 0 && specificity < 5
- );
+ pruneAsyncCallback: function() {
+ this.timer = null;
+ var obsolete = Date.now() - this.shelfLife;
+ for ( var entry of this.blocked ) {
+ if ( entry[1] <= obsolete ) {
+ this.blocked.delete(entry[0]);
+ }
+ }
+ if ( this.blocked.size !== 0 ) { this.pruneAsync(); }
}
- },
-
- lookupBlockedCollapsibles: function(request, response) {
- var tabContext = ηm.tabContextManager.lookup(this.tabId);
- if ( tabContext === null ) { return; }
-
- var collapseBlacklisted = ηm.userSettings.collapseBlacklisted,
- collapseBlocked = ηm.userSettings.collapseBlocked,
- entry;
-
- var blockedResources = response.blockedResources;
-
- if (
- Array.isArray(request.toFilter) &&
- request.toFilter.length !== 0
- ) {
- var roothn = tabContext.rootHostname,
- hnFromURI = ηm.URI.hostnameFromURI,
- tMatrix = ηm.tMatrix;
- for ( entry of request.toFilter ) {
- if ( tMatrix.mustBlock(roothn, hnFromURI(entry.url), entry.type) === false ) {
- continue;
+ };
+
+ /******************************************************************************/
+
+ // Ref: Given a URL, returns a (somewhat) unique 32-bit value
+ // Based on: FNV32a
+ // http://www.isthe.com/chongo/tech/comp/fnv/index.html#FNV-reference-source
+ // The rest is custom, suited for uMatrix.
+
+ var PageStore = function(tabContext) {
+ this.hostnameTypeCells = new Map();
+ this.domains = new Set();
+ this.blockedCollapsibles = new BlockedCollapsibles();
+ this.requestStats = ηm.requestStatsFactory();
+ this.off = false;
+ this.init(tabContext);
+ };
+
+ PageStore.prototype = {
+
+ collapsibleTypes: new Set([ 'image' ]),
+ pageStoreJunkyard: [],
+
+ init: function(tabContext) {
+ this.tabId = tabContext.tabId;
+ this.rawUrl = tabContext.rawURL;
+ this.pageUrl = tabContext.normalURL;
+ this.pageHostname = tabContext.rootHostname;
+ this.pageDomain = tabContext.rootDomain;
+ this.title = '';
+ this.hostnameTypeCells.clear();
+ this.domains.clear();
+ this.allHostnamesString = ' ';
+ this.blockedCollapsibles.reset();
+ this.requestStats.reset();
+ this.distinctRequestCount = 0;
+ this.perLoadAllowedRequestCount = 0;
+ this.perLoadBlockedRequestCount = 0;
+ this.has3pReferrer = false;
+ this.hasMixedContent = false;
+ this.hasNoscriptTags = false;
+ this.hasWebWorkers = false;
+ this.incinerationTimer = null;
+ this.mtxContentModifiedTime = 0;
+ this.mtxCountModifiedTime = 0;
+ return this;
+ },
+
+ dispose: function() {
+ this.rawUrl = '';
+ this.pageUrl = '';
+ this.pageHostname = '';
+ this.pageDomain = '';
+ this.title = '';
+ this.hostnameTypeCells.clear();
+ this.domains.clear();
+ this.allHostnamesString = ' ';
+ this.blockedCollapsibles.reset();
+ if ( this.incinerationTimer !== null ) {
+ clearTimeout(this.incinerationTimer);
+ this.incinerationTimer = null;
+ }
+ if ( this.pageStoreJunkyard.length < 8 ) {
+ this.pageStoreJunkyard.push(this);
+ }
+ },
+
+ cacheBlockedCollapsible: function(type, url, specificity) {
+ if ( this.collapsibleTypes.has(type) ) {
+ this.blockedCollapsibles.add(
+ type,
+ url,
+ specificity !== 0 && specificity < 5
+ );
+ }
+ },
+
+ lookupBlockedCollapsibles: function(request, response) {
+ var tabContext = ηm.tabContextManager.lookup(this.tabId);
+ if ( tabContext === null ) { return; }
+
+ var collapseBlacklisted = ηm.userSettings.collapseBlacklisted,
+ collapseBlocked = ηm.userSettings.collapseBlocked,
+ entry;
+
+ var blockedResources = response.blockedResources;
+
+ if (
+ Array.isArray(request.toFilter) &&
+ request.toFilter.length !== 0
+ ) {
+ var roothn = tabContext.rootHostname,
+ hnFromURI = ηm.URI.hostnameFromURI,
+ tMatrix = ηm.tMatrix;
+ for ( entry of request.toFilter ) {
+ if ( tMatrix.mustBlock(roothn, hnFromURI(entry.url), entry.type) === false ) {
+ continue;
+ }
+ blockedResources.push([
+ entry.type + ' ' + entry.url,
+ collapseBlocked ||
+ collapseBlacklisted && tMatrix.specificityRegister !== 0 &&
+ tMatrix.specificityRegister < 5
+ ]);
}
+ }
+
+ if ( this.blockedCollapsibles.hash === response.hash ) { return; }
+ response.hash = this.blockedCollapsibles.hash;
+
+ for ( entry of this.blockedCollapsibles.blocked ) {
blockedResources.push([
- entry.type + ' ' + entry.url,
- collapseBlocked ||
- collapseBlacklisted && tMatrix.specificityRegister !== 0 &&
- tMatrix.specificityRegister < 5
+ entry[0],
+ collapseBlocked || collapseBlacklisted && (entry[1] & 1) !== 0
]);
}
- }
-
- if ( this.blockedCollapsibles.hash === response.hash ) { return; }
- response.hash = this.blockedCollapsibles.hash;
+ },
- for ( entry of this.blockedCollapsibles.blocked ) {
- blockedResources.push([
- entry[0],
- collapseBlocked || collapseBlacklisted && (entry[1] & 1) !== 0
- ]);
- }
- },
+ recordRequest: function(type, url, block) {
+ if ( block !== false ) {
+ this.perLoadBlockedRequestCount++;
+ } else {
+ this.perLoadAllowedRequestCount++;
+ }
- recordRequest: function(type, url, block) {
- if ( block !== false ) {
- this.perLoadBlockedRequestCount++;
- } else {
- this.perLoadAllowedRequestCount++;
- }
-
- // Store distinct network requests. This is used to:
- // - remember which hostname/type were seen
- // - count the number of distinct URLs for any given
- // hostname-type pair
- var hostname = ηm.URI.hostnameFromURI(url),
- key = hostname + ' ' + type,
- uids = this.hostnameTypeCells.get(key);
- if ( uids === undefined ) {
- this.hostnameTypeCells.set(key, (uids = new Set()));
- } else if ( uids.size > 99 ) {
- return;
- }
- var uid = this.uidFromURL(url);
- if ( uids.has(uid) ) { return; }
- uids.add(uid);
+ // Store distinct network requests. This is used to:
+ // - remember which hostname/type were seen
+ // - count the number of distinct URLs for any given
+ // hostname-type pair
+ var hostname = ηm.URI.hostnameFromURI(url),
+ key = hostname + ' ' + type,
+ uids = this.hostnameTypeCells.get(key);
+ if ( uids === undefined ) {
+ this.hostnameTypeCells.set(key, (uids = new Set()));
+ } else if ( uids.size > 99 ) {
+ return;
+ }
+ var uid = this.uidFromURL(url);
+ if ( uids.has(uid) ) { return; }
+ uids.add(uid);
- // Count blocked/allowed requests
- this.requestStats.record(type, block);
+ // Count blocked/allowed requests
+ this.requestStats.record(type, block);
- // https://github.com/gorhill/httpswitchboard/issues/306
- // If it is recorded locally, record globally
- ηm.requestStats.record(type, block);
- ηm.updateBadgeAsync(this.tabId);
+ // https://github.com/gorhill/httpswitchboard/issues/306
+ // If it is recorded locally, record globally
+ ηm.requestStats.record(type, block);
+ ηm.updateBadgeAsync(this.tabId);
- // this.distinctRequestCount++;
- this.mtxCountModifiedTime = Date.now();
+ // this.distinctRequestCount++;
+ this.mtxCountModifiedTime = Date.now();
- if ( this.domains.has(hostname) === false ) {
- this.domains.add(hostname);
- this.allHostnamesString += hostname + ' ';
- this.mtxContentModifiedTime = Date.now();
- }
- },
-
- uidFromURL: function(uri) {
- var hint = 0x811c9dc5,
- i = uri.length;
- while ( i-- ) {
- hint ^= uri.charCodeAt(i) | 0;
- hint += (hint<<1) + (hint<<4) + (hint<<7) + (hint<<8) + (hint<<24) | 0;
- hint >>>= 0;
+ if ( this.domains.has(hostname) === false ) {
+ this.domains.add(hostname);
+ this.allHostnamesString += hostname + ' ';
+ this.mtxContentModifiedTime = Date.now();
+ }
+ },
+
+ uidFromURL: function(uri) {
+ var hint = 0x811c9dc5,
+ i = uri.length;
+ while ( i-- ) {
+ hint ^= uri.charCodeAt(i) | 0;
+ hint += (hint<<1) + (hint<<4) + (hint<<7) + (hint<<8) + (hint<<24) | 0;
+ hint >>>= 0;
+ }
+ return hint;
}
- return hint;
- }
-};
+ };
-/******************************************************************************/
+ /******************************************************************************/
-return function pageStoreFactory(tabContext) {
- var entry = PageStore.prototype.pageStoreJunkyard.pop();
- if ( entry ) {
- return entry.init(tabContext);
- }
- return new PageStore(tabContext);
-};
+ return function pageStoreFactory(tabContext) {
+ var entry = PageStore.prototype.pageStoreJunkyard.pop();
+ if ( entry ) {
+ return entry.init(tabContext);
+ }
+ return new PageStore(tabContext);
+ };
-/******************************************************************************/
+ /******************************************************************************/
})();
diff --git a/js/popup.js b/js/popup.js
index 5ece83a..2197c5c 100644
--- a/js/popup.js
+++ b/js/popup.js
@@ -31,1509 +31,1509 @@
(function() {
-/******************************************************************************/
-/******************************************************************************/
-
-// Stuff which is good to do very early so as to avoid visual glitches.
+ /******************************************************************************/
+ /******************************************************************************/
-(function() {
- var paneContentPaddingTop = vAPI.localStorage.getItem('paneContentPaddingTop'),
- touchDevice = vAPI.localStorage.getItem('touchDevice');
+ // Stuff which is good to do very early so as to avoid visual glitches.
- if ( typeof paneContentPaddingTop === 'string' ) {
- document.querySelector('.paneContent').style.setProperty(
- 'padding-top',
- paneContentPaddingTop
- );
- }
- if ( touchDevice === 'true' ) {
- document.body.setAttribute('data-touch', 'true');
- } else {
- document.addEventListener('touchstart', function onTouched(ev) {
- document.removeEventListener(ev.type, onTouched);
+ (function() {
+ var paneContentPaddingTop = vAPI.localStorage.getItem('paneContentPaddingTop'),
+ touchDevice = vAPI.localStorage.getItem('touchDevice');
+
+ if ( typeof paneContentPaddingTop === 'string' ) {
+ document.querySelector('.paneContent').style.setProperty(
+ 'padding-top',
+ paneContentPaddingTop
+ );
+ }
+ if ( touchDevice === 'true' ) {
document.body.setAttribute('data-touch', 'true');
- vAPI.localStorage.setItem('touchDevice', 'true');
- resizePopup();
- });
- }
-})();
-
-var popupWasResized = function() {
- document.body.setAttribute('data-resize-popup', '');
-};
-
-var resizePopup = (function() {
- var timer;
- var fix = function() {
- timer = undefined;
- var doc = document;
- // Manually adjust the position of the main matrix according to the
- // height of the toolbar/matrix header.
- var paddingTop = (doc.querySelector('.paneHead').clientHeight + 2) + 'px',
- paneContent = doc.querySelector('.paneContent');
- if ( paddingTop !== paneContent.style.paddingTop ) {
- paneContent.style.setProperty('padding-top', paddingTop);
- vAPI.localStorage.setItem('paneContentPaddingTop', paddingTop);
+ } else {
+ document.addEventListener('touchstart', function onTouched(ev) {
+ document.removeEventListener(ev.type, onTouched);
+ document.body.setAttribute('data-touch', 'true');
+ vAPI.localStorage.setItem('touchDevice', 'true');
+ resizePopup();
+ });
}
- document.body.classList.toggle(
- 'hConstrained',
- window.innerWidth < document.body.clientWidth
- );
- popupWasResized();
+ })();
+
+ var popupWasResized = function() {
+ document.body.setAttribute('data-resize-popup', '');
};
- return function() {
- if ( timer !== undefined ) {
- clearTimeout(timer);
- }
- timer = vAPI.setTimeout(fix, 97);
+
+ var resizePopup = (function() {
+ var timer;
+ var fix = function() {
+ timer = undefined;
+ var doc = document;
+ // Manually adjust the position of the main matrix according to the
+ // height of the toolbar/matrix header.
+ var paddingTop = (doc.querySelector('.paneHead').clientHeight + 2) + 'px',
+ paneContent = doc.querySelector('.paneContent');
+ if ( paddingTop !== paneContent.style.paddingTop ) {
+ paneContent.style.setProperty('padding-top', paddingTop);
+ vAPI.localStorage.setItem('paneContentPaddingTop', paddingTop);
+ }
+ document.body.classList.toggle(
+ 'hConstrained',
+ window.innerWidth < document.body.clientWidth
+ );
+ popupWasResized();
+ };
+ return function() {
+ if ( timer !== undefined ) {
+ clearTimeout(timer);
+ }
+ timer = vAPI.setTimeout(fix, 97);
+ };
+ })();
+
+ /******************************************************************************/
+ /******************************************************************************/
+
+ // Must be consistent with definitions in matrix.js
+ var Dark = 0x80;
+ var Red = 1;
+ var Green = 2;
+ var DarkRed = Dark | Red;
+ var DarkGreen = Dark | Green;
+
+ var matrixSnapshot = {};
+ var groupsSnapshot = [];
+ var allHostnamesSnapshot = 'do not leave this initial string empty';
+
+ var matrixCellHotspots = null;
+
+ var matrixHeaderPrettyNames = {
+ 'all': '',
+ 'cookie': '',
+ 'css': '',
+ 'image': '',
+ 'media': '',
+ 'script': '',
+ 'xhr': '',
+ 'frame': '',
+ 'other': ''
};
-})();
-/******************************************************************************/
-/******************************************************************************/
+ var firstPartyLabel = '';
+ var blacklistedHostnamesLabel = '';
-// Must be consistent with definitions in matrix.js
-var Dark = 0x80;
-var Red = 1;
-var Green = 2;
-var DarkRed = Dark | Red;
-var DarkGreen = Dark | Green;
-
-var matrixSnapshot = {};
-var groupsSnapshot = [];
-var allHostnamesSnapshot = 'do not leave this initial string empty';
-
-var matrixCellHotspots = null;
-
-var matrixHeaderPrettyNames = {
- 'all': '',
- 'cookie': '',
- 'css': '',
- 'image': '',
- 'media': '',
- 'script': '',
- 'xhr': '',
- 'frame': '',
- 'other': ''
-};
-
-var firstPartyLabel = '';
-var blacklistedHostnamesLabel = '';
-
-var expandosIdGenerator = 1;
-var nodeToExpandosMap = (function() {
- if ( typeof window.Map === 'function' ) {
- return new window.Map();
- }
-})();
+ var expandosIdGenerator = 1;
+ var nodeToExpandosMap = (function() {
+ if ( typeof window.Map === 'function' ) {
+ return new window.Map();
+ }
+ })();
-var expandosFromNode = function(node) {
- if (
- node instanceof HTMLElement === false &&
- typeof node.nodeAt === 'function'
- ) {
- node = node.nodeAt(0);
- }
- if ( nodeToExpandosMap ) {
- var expandosId = node.getAttribute('data-expandos');
- if ( !expandosId ) {
- expandosId = '' + (expandosIdGenerator++);
- node.setAttribute('data-expandos', expandosId);
+ var expandosFromNode = function(node) {
+ if (
+ node instanceof HTMLElement === false &&
+ typeof node.nodeAt === 'function'
+ ) {
+ node = node.nodeAt(0);
}
- var expandos = nodeToExpandosMap.get(expandosId);
- if ( expandos === undefined ) {
- nodeToExpandosMap.set(expandosId, (expandos = Object.create(null)));
+ if ( nodeToExpandosMap ) {
+ var expandosId = node.getAttribute('data-expandos');
+ if ( !expandosId ) {
+ expandosId = '' + (expandosIdGenerator++);
+ node.setAttribute('data-expandos', expandosId);
+ }
+ var expandos = nodeToExpandosMap.get(expandosId);
+ if ( expandos === undefined ) {
+ nodeToExpandosMap.set(expandosId, (expandos = Object.create(null)));
+ }
+ return expandos;
}
- return expandos;
- }
- return node;
-};
+ return node;
+ };
-/******************************************************************************/
-/******************************************************************************/
+ /******************************************************************************/
+ /******************************************************************************/
-function getUserSetting(setting) {
+ function getUserSetting(setting) {
return matrixSnapshot.userSettings[setting];
}
-function setUserSetting(setting, value) {
- matrixSnapshot.userSettings[setting] = value;
- vAPI.messaging.send('popup.js', {
- what: 'userSettings',
- name: setting,
- value: value
- });
-}
+ function setUserSetting(setting, value) {
+ matrixSnapshot.userSettings[setting] = value;
+ vAPI.messaging.send('popup.js', {
+ what: 'userSettings',
+ name: setting,
+ value: value
+ });
+ }
-/******************************************************************************/
+ /******************************************************************************/
-function getUISetting(setting) {
- var r = vAPI.localStorage.getItem(setting);
- if ( typeof r !== 'string' ) {
- return undefined;
+ function getUISetting(setting) {
+ var r = vAPI.localStorage.getItem(setting);
+ if ( typeof r !== 'string' ) {
+ return undefined;
+ }
+ return JSON.parse(r);
}
- return JSON.parse(r);
-}
-function setUISetting(setting, value) {
- vAPI.localStorage.setItem(
- setting,
- JSON.stringify(value)
- );
-}
+ function setUISetting(setting, value) {
+ vAPI.localStorage.setItem(
+ setting,
+ JSON.stringify(value)
+ );
+ }
-/******************************************************************************/
+ /******************************************************************************/
-function updateMatrixSnapshot() {
- matrixSnapshotPoller.pollNow();
-}
+ function updateMatrixSnapshot() {
+ matrixSnapshotPoller.pollNow();
+ }
-/******************************************************************************/
+ /******************************************************************************/
+
+ // For display purpose, create four distinct groups of rows:
+ // 0th: literal "1st-party" row
+ // 1st: page domain's related
+ // 2nd: whitelisted
+ // 3rd: graylisted
+ // 4th: blacklisted
-// For display purpose, create four distinct groups of rows:
-// 0th: literal "1st-party" row
-// 1st: page domain's related
-// 2nd: whitelisted
-// 3rd: graylisted
-// 4th: blacklisted
-
-function getGroupStats() {
-
- // Try to not reshuffle groups around while popup is opened if
- // no new hostname added.
- var latestDomainListSnapshot = Object.keys(matrixSnapshot.rows).sort().join();
- if ( latestDomainListSnapshot === allHostnamesSnapshot ) {
- return groupsSnapshot;
- }
- allHostnamesSnapshot = latestDomainListSnapshot;
-
- // First, group according to whether at least one node in the domain
- // hierarchy is white or blacklisted
- var pageDomain = matrixSnapshot.domain;
- var rows = matrixSnapshot.rows;
- var anyTypeOffset = matrixSnapshot.headerIndices.get('*');
- var hostname, domain;
- var row, color, count, groupIndex;
- var domainToGroupMap = {};
-
- // These have hard-coded position which cannot be overriden
- domainToGroupMap['1st-party'] = 0;
- domainToGroupMap[pageDomain] = 1;
-
- // 1st pass: domain wins if it has an explicit rule or a count
- for ( hostname in rows ) {
- if ( rows.hasOwnProperty(hostname) === false ) {
- continue;
+ function getGroupStats() {
+
+ // Try to not reshuffle groups around while popup is opened if
+ // no new hostname added.
+ var latestDomainListSnapshot = Object.keys(matrixSnapshot.rows).sort().join();
+ if ( latestDomainListSnapshot === allHostnamesSnapshot ) {
+ return groupsSnapshot;
}
- if ( hostname === '*' || hostname === '1st-party' ) {
- continue;
+ allHostnamesSnapshot = latestDomainListSnapshot;
+
+ // First, group according to whether at least one node in the domain
+ // hierarchy is white or blacklisted
+ var pageDomain = matrixSnapshot.domain;
+ var rows = matrixSnapshot.rows;
+ var anyTypeOffset = matrixSnapshot.headerIndices.get('*');
+ var hostname, domain;
+ var row, color, count, groupIndex;
+ var domainToGroupMap = {};
+
+ // These have hard-coded position which cannot be overriden
+ domainToGroupMap['1st-party'] = 0;
+ domainToGroupMap[pageDomain] = 1;
+
+ // 1st pass: domain wins if it has an explicit rule or a count
+ for ( hostname in rows ) {
+ if ( rows.hasOwnProperty(hostname) === false ) {
+ continue;
+ }
+ if ( hostname === '*' || hostname === '1st-party' ) {
+ continue;
+ }
+ domain = rows[hostname].domain;
+ if ( domain === pageDomain || hostname !== domain ) {
+ continue;
+ }
+ row = rows[domain];
+ color = row.temporary[anyTypeOffset];
+ if ( color === DarkGreen ) {
+ domainToGroupMap[domain] = 2;
+ continue;
+ }
+ if ( color === DarkRed ) {
+ domainToGroupMap[domain] = 4;
+ continue;
+ }
+ count = row.counts[anyTypeOffset];
+ if ( count !== 0 ) {
+ domainToGroupMap[domain] = 3;
+ continue;
+ }
}
- domain = rows[hostname].domain;
- if ( domain === pageDomain || hostname !== domain ) {
- continue;
+ // 2nd pass: green wins
+ for ( hostname in rows ) {
+ if ( rows.hasOwnProperty(hostname) === false ) {
+ continue;
+ }
+ row = rows[hostname];
+ domain = row.domain;
+ if ( domainToGroupMap.hasOwnProperty(domain) ) {
+ continue;
+ }
+ color = row.temporary[anyTypeOffset];
+ if ( color === DarkGreen ) {
+ domainToGroupMap[domain] = 2;
+ }
}
- row = rows[domain];
- color = row.temporary[anyTypeOffset];
- if ( color === DarkGreen ) {
- domainToGroupMap[domain] = 2;
- continue;
+ // 3rd pass: gray with count wins
+ for ( hostname in rows ) {
+ if ( rows.hasOwnProperty(hostname) === false ) {
+ continue;
+ }
+ row = rows[hostname];
+ domain = row.domain;
+ if ( domainToGroupMap.hasOwnProperty(domain) ) {
+ continue;
+ }
+ color = row.temporary[anyTypeOffset];
+ count = row.counts[anyTypeOffset];
+ if ( color !== DarkRed && count !== 0 ) {
+ domainToGroupMap[domain] = 3;
+ }
}
- if ( color === DarkRed ) {
- domainToGroupMap[domain] = 4;
- continue;
+ // 4th pass: red wins whatever is left
+ for ( hostname in rows ) {
+ if ( rows.hasOwnProperty(hostname) === false ) {
+ continue;
+ }
+ row = rows[hostname];
+ domain = row.domain;
+ if ( domainToGroupMap.hasOwnProperty(domain) ) {
+ continue;
+ }
+ color = row.temporary[anyTypeOffset];
+ if ( color === DarkRed ) {
+ domainToGroupMap[domain] = 4;
+ }
}
- count = row.counts[anyTypeOffset];
- if ( count !== 0 ) {
+ // 5th pass: gray wins whatever is left
+ for ( hostname in rows ) {
+ if ( rows.hasOwnProperty(hostname) === false ) {
+ continue;
+ }
+ domain = rows[hostname].domain;
+ if ( domainToGroupMap.hasOwnProperty(domain) ) {
+ continue;
+ }
domainToGroupMap[domain] = 3;
- continue;
- }
- }
- // 2nd pass: green wins
- for ( hostname in rows ) {
- if ( rows.hasOwnProperty(hostname) === false ) {
- continue;
}
- row = rows[hostname];
- domain = row.domain;
- if ( domainToGroupMap.hasOwnProperty(domain) ) {
- continue;
- }
- color = row.temporary[anyTypeOffset];
- if ( color === DarkGreen ) {
- domainToGroupMap[domain] = 2;
+
+ // Last pass: put each domain in a group
+ var groups = [ {}, {}, {}, {}, {} ];
+ var group;
+ for ( hostname in rows ) {
+ if ( rows.hasOwnProperty(hostname) === false ) {
+ continue;
+ }
+ if ( hostname === '*' ) {
+ continue;
+ }
+ domain = rows[hostname].domain;
+ groupIndex = domainToGroupMap[domain];
+ group = groups[groupIndex];
+ if ( group.hasOwnProperty(domain) === false ) {
+ group[domain] = {};
+ }
+ group[domain][hostname] = true;
}
+
+ groupsSnapshot = groups;
+
+ return groups;
}
- // 3rd pass: gray with count wins
- for ( hostname in rows ) {
- if ( rows.hasOwnProperty(hostname) === false ) {
- continue;
- }
- row = rows[hostname];
- domain = row.domain;
- if ( domainToGroupMap.hasOwnProperty(domain) ) {
- continue;
- }
- color = row.temporary[anyTypeOffset];
- count = row.counts[anyTypeOffset];
- if ( color !== DarkRed && count !== 0 ) {
- domainToGroupMap[domain] = 3;
- }
+
+ /******************************************************************************/
+
+ // helpers
+
+ function getTemporaryColor(hostname, type) {
+ return matrixSnapshot.rows[hostname].temporary[matrixSnapshot.headerIndices.get(type)];
}
- // 4th pass: red wins whatever is left
- for ( hostname in rows ) {
- if ( rows.hasOwnProperty(hostname) === false ) {
- continue;
- }
- row = rows[hostname];
- domain = row.domain;
- if ( domainToGroupMap.hasOwnProperty(domain) ) {
- continue;
- }
- color = row.temporary[anyTypeOffset];
- if ( color === DarkRed ) {
- domainToGroupMap[domain] = 4;
- }
+
+ function getPermanentColor(hostname, type) {
+ return matrixSnapshot.rows[hostname].permanent[matrixSnapshot.headerIndices.get(type)];
+ }
+
+ function addCellClass(cell, hostname, type) {
+ var cl = cell.classList;
+ cl.add('matCell');
+ cl.add('t' + getTemporaryColor(hostname, type).toString(16));
+ cl.add('p' + getPermanentColor(hostname, type).toString(16));
}
- // 5th pass: gray wins whatever is left
- for ( hostname in rows ) {
- if ( rows.hasOwnProperty(hostname) === false ) {
- continue;
+
+ /******************************************************************************/
+
+ // This is required for when we update the matrix while it is open:
+ // the user might have collapsed/expanded one or more domains, and we don't
+ // want to lose all his hardwork.
+
+ function getCollapseState(domain) {
+ var states = getUISetting('popupCollapseSpecificDomains');
+ if ( typeof states === 'object' && states[domain] !== undefined ) {
+ return states[domain];
}
- domain = rows[hostname].domain;
- if ( domainToGroupMap.hasOwnProperty(domain) ) {
- continue;
+ return matrixSnapshot.collapseAllDomains === true;
+ }
+
+ function toggleCollapseState(elem) {
+ if ( elem.ancestors('#matHead.collapsible').length > 0 ) {
+ toggleMainCollapseState(elem);
+ } else {
+ toggleSpecificCollapseState(elem);
}
- domainToGroupMap[domain] = 3;
+ popupWasResized();
}
- // Last pass: put each domain in a group
- var groups = [ {}, {}, {}, {}, {} ];
- var group;
- for ( hostname in rows ) {
- if ( rows.hasOwnProperty(hostname) === false ) {
- continue;
+ function toggleMainCollapseState(uelem) {
+ var matHead = uelem.ancestors('#matHead.collapsible').toggleClass('collapsed');
+ var collapsed = matrixSnapshot.collapseAllDomains = matHead.hasClass('collapsed');
+ uDom('#matList .matSection.collapsible').toggleClass('collapsed', collapsed);
+ setUserSetting('popupCollapseAllDomains', collapsed);
+
+ var specificCollapseStates = getUISetting('popupCollapseSpecificDomains') || {};
+ var domains = Object.keys(specificCollapseStates);
+ var i = domains.length;
+ var domain;
+ while ( i-- ) {
+ domain = domains[i];
+ if ( specificCollapseStates[domain] === collapsed ) {
+ delete specificCollapseStates[domain];
+ }
}
- if ( hostname === '*' ) {
- continue;
+ setUISetting('popupCollapseSpecificDomains', specificCollapseStates);
+ }
+
+ function toggleSpecificCollapseState(uelem) {
+ // Remember collapse state forever, but only if it is different
+ // from main collapse switch.
+ var section = uelem.ancestors('.matSection.collapsible').toggleClass('collapsed'),
+ domain = expandosFromNode(section).domain,
+ collapsed = section.hasClass('collapsed'),
+ mainCollapseState = matrixSnapshot.collapseAllDomains === true,
+ specificCollapseStates = getUISetting('popupCollapseSpecificDomains') || {};
+ if ( collapsed !== mainCollapseState ) {
+ specificCollapseStates[domain] = collapsed;
+ setUISetting('popupCollapseSpecificDomains', specificCollapseStates);
+ } else if ( specificCollapseStates[domain] !== undefined ) {
+ delete specificCollapseStates[domain];
+ setUISetting('popupCollapseSpecificDomains', specificCollapseStates);
}
- domain = rows[hostname].domain;
- groupIndex = domainToGroupMap[domain];
- group = groups[groupIndex];
- if ( group.hasOwnProperty(domain) === false ) {
- group[domain] = {};
+ }
+
+ /******************************************************************************/
+
+ // Update count value of matrix cells(s)
+
+ function updateMatrixCounts() {
+ var matCells = uDom('.matrix .matRow.rw > .matCell'),
+ i = matCells.length,
+ matRow, matCell, count, counts,
+ headerIndices = matrixSnapshot.headerIndices,
+ rows = matrixSnapshot.rows,
+ expandos;
+ while ( i-- ) {
+ matCell = matCells.nodeAt(i);
+ expandos = expandosFromNode(matCell);
+ if ( expandos.hostname === '*' || expandos.reqType === '*' ) {
+ continue;
+ }
+ matRow = matCell.parentNode;
+ counts = matRow.classList.contains('meta') ? 'totals' : 'counts';
+ count = rows[expandos.hostname][counts][headerIndices.get(expandos.reqType)];
+ if ( count === expandos.count ) { continue; }
+ expandos.count = count;
+ matCell.textContent = cellTextFromCount(count);
}
- group[domain][hostname] = true;
}
- groupsSnapshot = groups;
+ function cellTextFromCount(count) {
+ if ( count === 0 ) { return '\u00A0'; }
+ if ( count < 100 ) { return count; }
+ return '99+';
+ }
- return groups;
-}
+ /******************************************************************************/
-/******************************************************************************/
+ // Update color of matrix cells(s)
+ // Color changes when rules change
-// helpers
+ function updateMatrixColors() {
+ var cells = uDom('.matrix .matRow.rw > .matCell').removeClass(),
+ i = cells.length,
+ cell, expandos;
+ while ( i-- ) {
+ cell = cells.nodeAt(i);
+ expandos = expandosFromNode(cell);
+ addCellClass(cell, expandos.hostname, expandos.reqType);
+ }
+ popupWasResized();
+ }
-function getTemporaryColor(hostname, type) {
- return matrixSnapshot.rows[hostname].temporary[matrixSnapshot.headerIndices.get(type)];
-}
+ /******************************************************************************/
+
+ // Update behavior of matrix:
+ // - Whether a section is collapsible or not. It is collapsible if:
+ // - It has at least one subdomain AND
+ // - There is no explicit rule anywhere in the subdomain cells AND
+ // - It is not part of group 3 (blacklisted hostnames)
+
+ function updateMatrixBehavior() {
+ matrixList = matrixList || uDom('#matList');
+ var sections = matrixList.descendants('.matSection');
+ var i = sections.length;
+ var section, subdomainRows, j, subdomainRow;
+ while ( i-- ) {
+ section = sections.at(i);
+ subdomainRows = section.descendants('.l2:not(.g4)');
+ j = subdomainRows.length;
+ while ( j-- ) {
+ subdomainRow = subdomainRows.at(j);
+ subdomainRow.toggleClass('collapsible', subdomainRow.descendants('.t81,.t82').length === 0);
+ }
+ section.toggleClass('collapsible', subdomainRows.filter('.collapsible').length > 0);
+ }
+ }
-function getPermanentColor(hostname, type) {
- return matrixSnapshot.rows[hostname].permanent[matrixSnapshot.headerIndices.get(type)];
-}
+ /******************************************************************************/
-function addCellClass(cell, hostname, type) {
- var cl = cell.classList;
- cl.add('matCell');
- cl.add('t' + getTemporaryColor(hostname, type).toString(16));
- cl.add('p' + getPermanentColor(hostname, type).toString(16));
-}
+ // handle user interaction with filters
-/******************************************************************************/
+ function getCellAction(hostname, type, leaning) {
+ var temporaryColor = getTemporaryColor(hostname, type);
+ var hue = temporaryColor & 0x03;
+ // Special case: root toggle only between two states
+ if ( type === '*' && hostname === '*' ) {
+ return hue === Green ? 'blacklistMatrixCell' : 'whitelistMatrixCell';
+ }
+ // When explicitly blocked/allowed, can only graylist
+ var saturation = temporaryColor & 0x80;
+ if ( saturation === Dark ) {
+ return 'graylistMatrixCell';
+ }
+ return leaning === 'whitelisting' ? 'whitelistMatrixCell' : 'blacklistMatrixCell';
+ }
-// This is required for when we update the matrix while it is open:
-// the user might have collapsed/expanded one or more domains, and we don't
-// want to lose all his hardwork.
-
-function getCollapseState(domain) {
- var states = getUISetting('popupCollapseSpecificDomains');
- if ( typeof states === 'object' && states[domain] !== undefined ) {
- return states[domain];
- }
- return matrixSnapshot.collapseAllDomains === true;
-}
-
-function toggleCollapseState(elem) {
- if ( elem.ancestors('#matHead.collapsible').length > 0 ) {
- toggleMainCollapseState(elem);
- } else {
- toggleSpecificCollapseState(elem);
- }
- popupWasResized();
-}
-
-function toggleMainCollapseState(uelem) {
- var matHead = uelem.ancestors('#matHead.collapsible').toggleClass('collapsed');
- var collapsed = matrixSnapshot.collapseAllDomains = matHead.hasClass('collapsed');
- uDom('#matList .matSection.collapsible').toggleClass('collapsed', collapsed);
- setUserSetting('popupCollapseAllDomains', collapsed);
-
- var specificCollapseStates = getUISetting('popupCollapseSpecificDomains') || {};
- var domains = Object.keys(specificCollapseStates);
- var i = domains.length;
- var domain;
- while ( i-- ) {
- domain = domains[i];
- if ( specificCollapseStates[domain] === collapsed ) {
- delete specificCollapseStates[domain];
+ function handleFilter(button, leaning) {
+ // our parent cell knows who we are
+ var cell = button.ancestors('div.matCell'),
+ expandos = expandosFromNode(cell),
+ type = expandos.reqType,
+ desHostname = expandos.hostname;
+ // https://github.com/gorhill/uMatrix/issues/24
+ // No hostname can happen -- like with blacklist meta row
+ if ( desHostname === '' ) {
+ return;
}
+ var request = {
+ what: getCellAction(desHostname, type, leaning),
+ srcHostname: matrixSnapshot.scope,
+ desHostname: desHostname,
+ type: type
+ };
+ vAPI.messaging.send('popup.js', request, updateMatrixSnapshot);
}
- setUISetting('popupCollapseSpecificDomains', specificCollapseStates);
-}
-
-function toggleSpecificCollapseState(uelem) {
- // Remember collapse state forever, but only if it is different
- // from main collapse switch.
- var section = uelem.ancestors('.matSection.collapsible').toggleClass('collapsed'),
- domain = expandosFromNode(section).domain,
- collapsed = section.hasClass('collapsed'),
- mainCollapseState = matrixSnapshot.collapseAllDomains === true,
- specificCollapseStates = getUISetting('popupCollapseSpecificDomains') || {};
- if ( collapsed !== mainCollapseState ) {
- specificCollapseStates[domain] = collapsed;
- setUISetting('popupCollapseSpecificDomains', specificCollapseStates);
- } else if ( specificCollapseStates[domain] !== undefined ) {
- delete specificCollapseStates[domain];
- setUISetting('popupCollapseSpecificDomains', specificCollapseStates);
+
+ function handleWhitelistFilter(button) {
+ handleFilter(button, 'whitelisting');
}
-}
-/******************************************************************************/
+ function handleBlacklistFilter(button) {
+ handleFilter(button, 'blacklisting');
+ }
-// Update count value of matrix cells(s)
-
-function updateMatrixCounts() {
- var matCells = uDom('.matrix .matRow.rw > .matCell'),
- i = matCells.length,
- matRow, matCell, count, counts,
- headerIndices = matrixSnapshot.headerIndices,
- rows = matrixSnapshot.rows,
- expandos;
- while ( i-- ) {
- matCell = matCells.nodeAt(i);
- expandos = expandosFromNode(matCell);
- if ( expandos.hostname === '*' || expandos.reqType === '*' ) {
- continue;
+ /******************************************************************************/
+
+ var matrixRowPool = [];
+ var matrixSectionPool = [];
+ var matrixGroupPool = [];
+ var matrixRowTemplate = null;
+ var matrixList = null;
+
+ var startMatrixUpdate = function() {
+ matrixList = matrixList || uDom('#matList');
+ matrixList.detach();
+ var rows = matrixList.descendants('.matRow');
+ rows.detach();
+ matrixRowPool = matrixRowPool.concat(rows.toArray());
+ var sections = matrixList.descendants('.matSection');
+ sections.detach();
+ matrixSectionPool = matrixSectionPool.concat(sections.toArray());
+ var groups = matrixList.descendants('.matGroup');
+ groups.detach();
+ matrixGroupPool = matrixGroupPool.concat(groups.toArray());
+ };
+
+ var endMatrixUpdate = function() {
+ // https://github.com/gorhill/httpswitchboard/issues/246
+ // If the matrix has no rows, we need to insert a dummy one, invisible,
+ // to ensure the extension pop-up is properly sized. This is needed because
+ // the header pane's `position` property is `fixed`, which means it doesn't
+ // affect layout size, hence the matrix header row will be truncated.
+ if ( matrixSnapshot.rowCount <= 1 ) {
+ matrixList.append(createMatrixRow().css('visibility', 'hidden'));
}
- matRow = matCell.parentNode;
- counts = matRow.classList.contains('meta') ? 'totals' : 'counts';
- count = rows[expandos.hostname][counts][headerIndices.get(expandos.reqType)];
- if ( count === expandos.count ) { continue; }
- expandos.count = count;
- matCell.textContent = cellTextFromCount(count);
- }
-}
+ updateMatrixBehavior();
+ matrixList.css('display', '');
+ matrixList.appendTo('.paneContent');
+ };
-function cellTextFromCount(count) {
- if ( count === 0 ) { return '\u00A0'; }
- if ( count < 100 ) { return count; }
- return '99+';
-}
+ var createMatrixGroup = function() {
+ var group = matrixGroupPool.pop();
+ if ( group ) {
+ return uDom(group).removeClass().addClass('matGroup');
+ }
+ return uDom(document.createElement('div')).addClass('matGroup');
+ };
-/******************************************************************************/
+ var createMatrixSection = function() {
+ var section = matrixSectionPool.pop();
+ if ( section ) {
+ return uDom(section).removeClass().addClass('matSection');
+ }
+ return uDom(document.createElement('div')).addClass('matSection');
+ };
+
+ var createMatrixRow = function() {
+ var row = matrixRowPool.pop();
+ if ( row ) {
+ row.style.visibility = '';
+ row = uDom(row);
+ row.descendants('.matCell').removeClass().addClass('matCell');
+ row.removeClass().addClass('matRow');
+ return row;
+ }
+ if ( matrixRowTemplate === null ) {
+ matrixRowTemplate = uDom('#templates .matRow');
+ }
+ return matrixRowTemplate.clone();
+ };
-// Update color of matrix cells(s)
-// Color changes when rules change
+ /******************************************************************************/
-function updateMatrixColors() {
- var cells = uDom('.matrix .matRow.rw > .matCell').removeClass(),
- i = cells.length,
- cell, expandos;
- while ( i-- ) {
- cell = cells.nodeAt(i);
+ function renderMatrixHeaderRow() {
+ var matHead = uDom('#matHead.collapsible');
+ matHead.toggleClass('collapsed', matrixSnapshot.collapseAllDomains === true);
+ var cells = matHead.descendants('.matCell'), cell, expandos;
+ cell = cells.nodeAt(0);
+ expandos = expandosFromNode(cell);
+ expandos.reqType = '*';
+ expandos.hostname = '*';
+ addCellClass(cell, '*', '*');
+ cell = cells.nodeAt(1);
+ expandos = expandosFromNode(cell);
+ expandos.reqType = 'cookie';
+ expandos.hostname = '*';
+ addCellClass(cell, '*', 'cookie');
+ cell = cells.nodeAt(2);
+ expandos = expandosFromNode(cell);
+ expandos.reqType = 'css';
+ expandos.hostname = '*';
+ addCellClass(cell, '*', 'css');
+ cell = cells.nodeAt(3);
expandos = expandosFromNode(cell);
- addCellClass(cell, expandos.hostname, expandos.reqType);
+ expandos.reqType = 'image';
+ expandos.hostname = '*';
+ addCellClass(cell, '*', 'image');
+ cell = cells.nodeAt(4);
+ expandos = expandosFromNode(cell);
+ expandos.reqType = 'media';
+ expandos.hostname = '*';
+ addCellClass(cell, '*', 'media');
+ cell = cells.nodeAt(5);
+ expandos = expandosFromNode(cell);
+ expandos.reqType = 'script';
+ expandos.hostname = '*';
+ addCellClass(cell, '*', 'script');
+ cell = cells.nodeAt(6);
+ expandos = expandosFromNode(cell);
+ expandos.reqType = 'xhr';
+ expandos.hostname = '*';
+ addCellClass(cell, '*', 'xhr');
+ cell = cells.nodeAt(7);
+ expandos = expandosFromNode(cell);
+ expandos.reqType = 'frame';
+ expandos.hostname = '*';
+ addCellClass(cell, '*', 'frame');
+ cell = cells.nodeAt(8);
+ expandos = expandosFromNode(cell);
+ expandos.reqType = 'other';
+ expandos.hostname = '*';
+ addCellClass(cell, '*', 'other');
+ uDom('#matHead .matRow').css('display', '');
}
- popupWasResized();
-}
-/******************************************************************************/
-
-// Update behavior of matrix:
-// - Whether a section is collapsible or not. It is collapsible if:
-// - It has at least one subdomain AND
-// - There is no explicit rule anywhere in the subdomain cells AND
-// - It is not part of group 3 (blacklisted hostnames)
-
-function updateMatrixBehavior() {
- matrixList = matrixList || uDom('#matList');
- var sections = matrixList.descendants('.matSection');
- var i = sections.length;
- var section, subdomainRows, j, subdomainRow;
- while ( i-- ) {
- section = sections.at(i);
- subdomainRows = section.descendants('.l2:not(.g4)');
- j = subdomainRows.length;
- while ( j-- ) {
- subdomainRow = subdomainRows.at(j);
- subdomainRow.toggleClass('collapsible', subdomainRow.descendants('.t81,.t82').length === 0);
- }
- section.toggleClass('collapsible', subdomainRows.filter('.collapsible').length > 0);
+ /******************************************************************************/
+
+ function renderMatrixCellDomain(cell, domain) {
+ var expandos = expandosFromNode(cell);
+ expandos.hostname = domain;
+ expandos.reqType = '*';
+ addCellClass(cell.nodeAt(0), domain, '*');
+ var contents = cell.contents();
+ contents.nodeAt(0).textContent = domain === '1st-party' ?
+ firstPartyLabel :
+ punycode.toUnicode(domain);
+ contents.nodeAt(1).textContent = ' ';
}
-}
-/******************************************************************************/
+ function renderMatrixCellSubdomain(cell, domain, subomain) {
+ var expandos = expandosFromNode(cell);
+ expandos.hostname = subomain;
+ expandos.reqType = '*';
+ addCellClass(cell.nodeAt(0), subomain, '*');
+ var contents = cell.contents();
+ contents.nodeAt(0).textContent = punycode.toUnicode(subomain.slice(0, subomain.lastIndexOf(domain)-1)) + '.';
+ contents.nodeAt(1).textContent = punycode.toUnicode(domain);
+ }
-// handle user interaction with filters
-
-function getCellAction(hostname, type, leaning) {
- var temporaryColor = getTemporaryColor(hostname, type);
- var hue = temporaryColor & 0x03;
- // Special case: root toggle only between two states
- if ( type === '*' && hostname === '*' ) {
- return hue === Green ? 'blacklistMatrixCell' : 'whitelistMatrixCell';
- }
- // When explicitly blocked/allowed, can only graylist
- var saturation = temporaryColor & 0x80;
- if ( saturation === Dark ) {
- return 'graylistMatrixCell';
- }
- return leaning === 'whitelisting' ? 'whitelistMatrixCell' : 'blacklistMatrixCell';
-}
-
-function handleFilter(button, leaning) {
- // our parent cell knows who we are
- var cell = button.ancestors('div.matCell'),
- expandos = expandosFromNode(cell),
- type = expandos.reqType,
- desHostname = expandos.hostname;
- // https://github.com/gorhill/uMatrix/issues/24
- // No hostname can happen -- like with blacklist meta row
- if ( desHostname === '' ) {
- return;
- }
- var request = {
- what: getCellAction(desHostname, type, leaning),
- srcHostname: matrixSnapshot.scope,
- desHostname: desHostname,
- type: type
- };
- vAPI.messaging.send('popup.js', request, updateMatrixSnapshot);
-}
+ function renderMatrixMetaCellDomain(cell, domain) {
+ var expandos = expandosFromNode(cell);
+ expandos.hostname = domain;
+ expandos.reqType = '*';
+ addCellClass(cell.nodeAt(0), domain, '*');
+ var contents = cell.contents();
+ contents.nodeAt(0).textContent = '\u2217.' + punycode.toUnicode(domain);
+ contents.nodeAt(1).textContent = ' ';
+ }
-function handleWhitelistFilter(button) {
- handleFilter(button, 'whitelisting');
-}
+ function renderMatrixCellType(cell, hostname, type, count) {
+ var node = cell.nodeAt(0),
+ expandos = expandosFromNode(node);
+ expandos.hostname = hostname;
+ expandos.reqType = type;
+ expandos.count = count;
+ addCellClass(node, hostname, type);
+ node.textContent = cellTextFromCount(count);
+ }
-function handleBlacklistFilter(button) {
- handleFilter(button, 'blacklisting');
-}
+ function renderMatrixCellTypes(cells, hostname, countName) {
+ var counts = matrixSnapshot.rows[hostname][countName];
+ var headerIndices = matrixSnapshot.headerIndices;
+ renderMatrixCellType(cells.at(1), hostname, 'cookie', counts[headerIndices.get('cookie')]);
+ renderMatrixCellType(cells.at(2), hostname, 'css', counts[headerIndices.get('css')]);
+ renderMatrixCellType(cells.at(3), hostname, 'image', counts[headerIndices.get('image')]);
+ renderMatrixCellType(cells.at(4), hostname, 'media', counts[headerIndices.get('media')]);
+ renderMatrixCellType(cells.at(5), hostname, 'script', counts[headerIndices.get('script')]);
+ renderMatrixCellType(cells.at(6), hostname, 'xhr', counts[headerIndices.get('xhr')]);
+ renderMatrixCellType(cells.at(7), hostname, 'frame', counts[headerIndices.get('frame')]);
+ renderMatrixCellType(cells.at(8), hostname, 'other', counts[headerIndices.get('other')]);
+ }
-/******************************************************************************/
+ /******************************************************************************/
-var matrixRowPool = [];
-var matrixSectionPool = [];
-var matrixGroupPool = [];
-var matrixRowTemplate = null;
-var matrixList = null;
-
-var startMatrixUpdate = function() {
- matrixList = matrixList || uDom('#matList');
- matrixList.detach();
- var rows = matrixList.descendants('.matRow');
- rows.detach();
- matrixRowPool = matrixRowPool.concat(rows.toArray());
- var sections = matrixList.descendants('.matSection');
- sections.detach();
- matrixSectionPool = matrixSectionPool.concat(sections.toArray());
- var groups = matrixList.descendants('.matGroup');
- groups.detach();
- matrixGroupPool = matrixGroupPool.concat(groups.toArray());
-};
-
-var endMatrixUpdate = function() {
- // https://github.com/gorhill/httpswitchboard/issues/246
- // If the matrix has no rows, we need to insert a dummy one, invisible,
- // to ensure the extension pop-up is properly sized. This is needed because
- // the header pane's `position` property is `fixed`, which means it doesn't
- // affect layout size, hence the matrix header row will be truncated.
- if ( matrixSnapshot.rowCount <= 1 ) {
- matrixList.append(createMatrixRow().css('visibility', 'hidden'));
- }
- updateMatrixBehavior();
- matrixList.css('display', '');
- matrixList.appendTo('.paneContent');
-};
-
-var createMatrixGroup = function() {
- var group = matrixGroupPool.pop();
- if ( group ) {
- return uDom(group).removeClass().addClass('matGroup');
- }
- return uDom(document.createElement('div')).addClass('matGroup');
-};
-
-var createMatrixSection = function() {
- var section = matrixSectionPool.pop();
- if ( section ) {
- return uDom(section).removeClass().addClass('matSection');
- }
- return uDom(document.createElement('div')).addClass('matSection');
-};
-
-var createMatrixRow = function() {
- var row = matrixRowPool.pop();
- if ( row ) {
- row.style.visibility = '';
- row = uDom(row);
- row.descendants('.matCell').removeClass().addClass('matCell');
- row.removeClass().addClass('matRow');
- return row;
- }
- if ( matrixRowTemplate === null ) {
- matrixRowTemplate = uDom('#templates .matRow');
- }
- return matrixRowTemplate.clone();
-};
+ function makeMatrixRowDomain(domain) {
+ var matrixRow = createMatrixRow().addClass('rw');
+ var cells = matrixRow.descendants('.matCell');
+ renderMatrixCellDomain(cells.at(0), domain);
+ renderMatrixCellTypes(cells, domain, 'counts');
+ return matrixRow;
+ }
-/******************************************************************************/
+ function makeMatrixRowSubdomain(domain, subdomain) {
+ var matrixRow = createMatrixRow().addClass('rw');
+ var cells = matrixRow.descendants('.matCell');
+ renderMatrixCellSubdomain(cells.at(0), domain, subdomain);
+ renderMatrixCellTypes(cells, subdomain, 'counts');
+ return matrixRow;
+ }
-function renderMatrixHeaderRow() {
- var matHead = uDom('#matHead.collapsible');
- matHead.toggleClass('collapsed', matrixSnapshot.collapseAllDomains === true);
- var cells = matHead.descendants('.matCell'), cell, expandos;
- cell = cells.nodeAt(0);
- expandos = expandosFromNode(cell);
- expandos.reqType = '*';
- expandos.hostname = '*';
- addCellClass(cell, '*', '*');
- cell = cells.nodeAt(1);
- expandos = expandosFromNode(cell);
- expandos.reqType = 'cookie';
- expandos.hostname = '*';
- addCellClass(cell, '*', 'cookie');
- cell = cells.nodeAt(2);
- expandos = expandosFromNode(cell);
- expandos.reqType = 'css';
- expandos.hostname = '*';
- addCellClass(cell, '*', 'css');
- cell = cells.nodeAt(3);
- expandos = expandosFromNode(cell);
- expandos.reqType = 'image';
- expandos.hostname = '*';
- addCellClass(cell, '*', 'image');
- cell = cells.nodeAt(4);
- expandos = expandosFromNode(cell);
- expandos.reqType = 'media';
- expandos.hostname = '*';
- addCellClass(cell, '*', 'media');
- cell = cells.nodeAt(5);
- expandos = expandosFromNode(cell);
- expandos.reqType = 'script';
- expandos.hostname = '*';
- addCellClass(cell, '*', 'script');
- cell = cells.nodeAt(6);
- expandos = expandosFromNode(cell);
- expandos.reqType = 'xhr';
- expandos.hostname = '*';
- addCellClass(cell, '*', 'xhr');
- cell = cells.nodeAt(7);
- expandos = expandosFromNode(cell);
- expandos.reqType = 'frame';
- expandos.hostname = '*';
- addCellClass(cell, '*', 'frame');
- cell = cells.nodeAt(8);
- expandos = expandosFromNode(cell);
- expandos.reqType = 'other';
- expandos.hostname = '*';
- addCellClass(cell, '*', 'other');
- uDom('#matHead .matRow').css('display', '');
-}
+ function makeMatrixMetaRowDomain(domain) {
+ var matrixRow = createMatrixRow().addClass('rw');
+ var cells = matrixRow.descendants('.matCell');
+ renderMatrixMetaCellDomain(cells.at(0), domain);
+ renderMatrixCellTypes(cells, domain, 'totals');
+ return matrixRow;
+ }
-/******************************************************************************/
+ /******************************************************************************/
-function renderMatrixCellDomain(cell, domain) {
- var expandos = expandosFromNode(cell);
- expandos.hostname = domain;
- expandos.reqType = '*';
- addCellClass(cell.nodeAt(0), domain, '*');
- var contents = cell.contents();
- contents.nodeAt(0).textContent = domain === '1st-party' ?
- firstPartyLabel :
- punycode.toUnicode(domain);
- contents.nodeAt(1).textContent = ' ';
-}
-
-function renderMatrixCellSubdomain(cell, domain, subomain) {
- var expandos = expandosFromNode(cell);
- expandos.hostname = subomain;
- expandos.reqType = '*';
- addCellClass(cell.nodeAt(0), subomain, '*');
- var contents = cell.contents();
- contents.nodeAt(0).textContent = punycode.toUnicode(subomain.slice(0, subomain.lastIndexOf(domain)-1)) + '.';
- contents.nodeAt(1).textContent = punycode.toUnicode(domain);
-}
-
-function renderMatrixMetaCellDomain(cell, domain) {
- var expandos = expandosFromNode(cell);
- expandos.hostname = domain;
- expandos.reqType = '*';
- addCellClass(cell.nodeAt(0), domain, '*');
- var contents = cell.contents();
- contents.nodeAt(0).textContent = '\u2217.' + punycode.toUnicode(domain);
- contents.nodeAt(1).textContent = ' ';
-}
-
-function renderMatrixCellType(cell, hostname, type, count) {
- var node = cell.nodeAt(0),
- expandos = expandosFromNode(node);
- expandos.hostname = hostname;
- expandos.reqType = type;
- expandos.count = count;
- addCellClass(node, hostname, type);
- node.textContent = cellTextFromCount(count);
-}
-
-function renderMatrixCellTypes(cells, hostname, countName) {
- var counts = matrixSnapshot.rows[hostname][countName];
- var headerIndices = matrixSnapshot.headerIndices;
- renderMatrixCellType(cells.at(1), hostname, 'cookie', counts[headerIndices.get('cookie')]);
- renderMatrixCellType(cells.at(2), hostname, 'css', counts[headerIndices.get('css')]);
- renderMatrixCellType(cells.at(3), hostname, 'image', counts[headerIndices.get('image')]);
- renderMatrixCellType(cells.at(4), hostname, 'media', counts[headerIndices.get('media')]);
- renderMatrixCellType(cells.at(5), hostname, 'script', counts[headerIndices.get('script')]);
- renderMatrixCellType(cells.at(6), hostname, 'xhr', counts[headerIndices.get('xhr')]);
- renderMatrixCellType(cells.at(7), hostname, 'frame', counts[headerIndices.get('frame')]);
- renderMatrixCellType(cells.at(8), hostname, 'other', counts[headerIndices.get('other')]);
-}
+ function renderMatrixMetaCellType(cell, count) {
+ // https://github.com/gorhill/uMatrix/issues/24
+ // Don't forget to reset cell properties
+ var node = cell.nodeAt(0),
+ expandos = expandosFromNode(node);
+ expandos.hostname = '';
+ expandos.reqType = '';
+ expandos.count = count;
+ cell.addClass('t1');
+ node.textContent = cellTextFromCount(count);
+ }
-/******************************************************************************/
+ function makeMatrixMetaRow(totals) {
+ var headerIndices = matrixSnapshot.headerIndices,
+ matrixRow = createMatrixRow().at(0).addClass('ro'),
+ cells = matrixRow.descendants('.matCell'),
+ contents = cells.at(0).addClass('t81').contents(),
+ expandos = expandosFromNode(cells.nodeAt(0));
+ expandos.hostname = '';
+ expandos.reqType = '*';
+ contents.nodeAt(0).textContent = ' ';
+ contents.nodeAt(1).textContent = blacklistedHostnamesLabel.replace(
+ '{{count}}',
+ totals[headerIndices.get('*')].toLocaleString()
+ );
+ renderMatrixMetaCellType(cells.at(1), totals[headerIndices.get('cookie')]);
+ renderMatrixMetaCellType(cells.at(2), totals[headerIndices.get('css')]);
+ renderMatrixMetaCellType(cells.at(3), totals[headerIndices.get('image')]);
+ renderMatrixMetaCellType(cells.at(4), totals[headerIndices.get('media')]);
+ renderMatrixMetaCellType(cells.at(5), totals[headerIndices.get('script')]);
+ renderMatrixMetaCellType(cells.at(6), totals[headerIndices.get('xhr')]);
+ renderMatrixMetaCellType(cells.at(7), totals[headerIndices.get('frame')]);
+ renderMatrixMetaCellType(cells.at(8), totals[headerIndices.get('other')]);
+ return matrixRow;
+ }
-function makeMatrixRowDomain(domain) {
- var matrixRow = createMatrixRow().addClass('rw');
- var cells = matrixRow.descendants('.matCell');
- renderMatrixCellDomain(cells.at(0), domain);
- renderMatrixCellTypes(cells, domain, 'counts');
- return matrixRow;
-}
-
-function makeMatrixRowSubdomain(domain, subdomain) {
- var matrixRow = createMatrixRow().addClass('rw');
- var cells = matrixRow.descendants('.matCell');
- renderMatrixCellSubdomain(cells.at(0), domain, subdomain);
- renderMatrixCellTypes(cells, subdomain, 'counts');
- return matrixRow;
-}
-
-function makeMatrixMetaRowDomain(domain) {
- var matrixRow = createMatrixRow().addClass('rw');
- var cells = matrixRow.descendants('.matCell');
- renderMatrixMetaCellDomain(cells.at(0), domain);
- renderMatrixCellTypes(cells, domain, 'totals');
- return matrixRow;
-}
+ /******************************************************************************/
-/******************************************************************************/
+ function computeMatrixGroupMetaStats(group) {
+ var headerIndices = matrixSnapshot.headerIndices,
+ anyTypeIndex = headerIndices.get('*'),
+ n = headerIndices.size,
+ totals = new Array(n),
+ i = n;
+ while ( i-- ) {
+ totals[i] = 0;
+ }
+ var rows = matrixSnapshot.rows, row;
+ for ( var hostname in rows ) {
+ if ( rows.hasOwnProperty(hostname) === false ) {
+ continue;
+ }
+ row = rows[hostname];
+ if ( group.hasOwnProperty(row.domain) === false ) {
+ continue;
+ }
+ if ( row.counts[anyTypeIndex] === 0 ) {
+ continue;
+ }
+ totals[0] += 1;
+ for ( i = 1; i < n; i++ ) {
+ totals[i] += row.counts[i];
+ }
+ }
+ return totals;
+ }
-function renderMatrixMetaCellType(cell, count) {
- // https://github.com/gorhill/uMatrix/issues/24
- // Don't forget to reset cell properties
- var node = cell.nodeAt(0),
- expandos = expandosFromNode(node);
- expandos.hostname = '';
- expandos.reqType = '';
- expandos.count = count;
- cell.addClass('t1');
- node.textContent = cellTextFromCount(count);
-}
-
-function makeMatrixMetaRow(totals) {
- var headerIndices = matrixSnapshot.headerIndices,
- matrixRow = createMatrixRow().at(0).addClass('ro'),
- cells = matrixRow.descendants('.matCell'),
- contents = cells.at(0).addClass('t81').contents(),
- expandos = expandosFromNode(cells.nodeAt(0));
- expandos.hostname = '';
- expandos.reqType = '*';
- contents.nodeAt(0).textContent = ' ';
- contents.nodeAt(1).textContent = blacklistedHostnamesLabel.replace(
- '{{count}}',
- totals[headerIndices.get('*')].toLocaleString()
- );
- renderMatrixMetaCellType(cells.at(1), totals[headerIndices.get('cookie')]);
- renderMatrixMetaCellType(cells.at(2), totals[headerIndices.get('css')]);
- renderMatrixMetaCellType(cells.at(3), totals[headerIndices.get('image')]);
- renderMatrixMetaCellType(cells.at(4), totals[headerIndices.get('media')]);
- renderMatrixMetaCellType(cells.at(5), totals[headerIndices.get('script')]);
- renderMatrixMetaCellType(cells.at(6), totals[headerIndices.get('xhr')]);
- renderMatrixMetaCellType(cells.at(7), totals[headerIndices.get('frame')]);
- renderMatrixMetaCellType(cells.at(8), totals[headerIndices.get('other')]);
- return matrixRow;
-}
+ /******************************************************************************/
-/******************************************************************************/
+ // Compare hostname helper, to order hostname in a logical manner:
+ // top-most < bottom-most, take into account whether IP address or
+ // named hostname
-function computeMatrixGroupMetaStats(group) {
- var headerIndices = matrixSnapshot.headerIndices,
- anyTypeIndex = headerIndices.get('*'),
- n = headerIndices.size,
- totals = new Array(n),
- i = n;
- while ( i-- ) {
- totals[i] = 0;
- }
- var rows = matrixSnapshot.rows, row;
- for ( var hostname in rows ) {
- if ( rows.hasOwnProperty(hostname) === false ) {
- continue;
+ function hostnameCompare(a,b) {
+ // Normalize: most significant parts first
+ if ( !a.match(/^\d+(\.\d+){1,3}$/) ) {
+ var aa = a.split('.');
+ a = aa.slice(-2).concat(aa.slice(0,-2).reverse()).join('.');
}
- row = rows[hostname];
- if ( group.hasOwnProperty(row.domain) === false ) {
- continue;
+ if ( !b.match(/^\d+(\.\d+){1,3}$/) ) {
+ var bb = b.split('.');
+ b = bb.slice(-2).concat(bb.slice(0,-2).reverse()).join('.');
}
- if ( row.counts[anyTypeIndex] === 0 ) {
- continue;
- }
- totals[0] += 1;
- for ( i = 1; i < n; i++ ) {
- totals[i] += row.counts[i];
+ return a.localeCompare(b);
+ }
+
+ /******************************************************************************/
+
+ function makeMatrixGroup0SectionDomain() {
+ return makeMatrixRowDomain('1st-party').addClass('g0 l1');
+ }
+
+ function makeMatrixGroup0Section() {
+ var domainDiv = createMatrixSection();
+ expandosFromNode(domainDiv).domain = '1st-party';
+ makeMatrixGroup0SectionDomain().appendTo(domainDiv);
+ return domainDiv;
+ }
+
+ function makeMatrixGroup0() {
+ // Show literal "1st-party" row only if there is
+ // at least one 1st-party hostname
+ if ( Object.keys(groupsSnapshot[1]).length === 0 ) {
+ return;
}
+ var groupDiv = createMatrixGroup().addClass('g0');
+ makeMatrixGroup0Section().appendTo(groupDiv);
+ groupDiv.appendTo(matrixList);
}
- return totals;
-}
-/******************************************************************************/
+ /******************************************************************************/
-// Compare hostname helper, to order hostname in a logical manner:
-// top-most < bottom-most, take into account whether IP address or
-// named hostname
+ function makeMatrixGroup1SectionDomain(domain) {
+ return makeMatrixRowDomain(domain)
+ .addClass('g1 l1');
+ }
-function hostnameCompare(a,b) {
- // Normalize: most significant parts first
- if ( !a.match(/^\d+(\.\d+){1,3}$/) ) {
- var aa = a.split('.');
- a = aa.slice(-2).concat(aa.slice(0,-2).reverse()).join('.');
+ function makeMatrixGroup1SectionSubomain(domain, subdomain) {
+ return makeMatrixRowSubdomain(domain, subdomain)
+ .addClass('g1 l2');
}
- if ( !b.match(/^\d+(\.\d+){1,3}$/) ) {
- var bb = b.split('.');
- b = bb.slice(-2).concat(bb.slice(0,-2).reverse()).join('.');
+
+ function makeMatrixGroup1SectionMetaDomain(domain) {
+ return makeMatrixMetaRowDomain(domain).addClass('g1 l1 meta');
}
- return a.localeCompare(b);
-}
-/******************************************************************************/
+ function makeMatrixGroup1Section(hostnames) {
+ var domain = hostnames[0];
+ var domainDiv = createMatrixSection()
+ .toggleClass('collapsed', getCollapseState(domain));
+ expandosFromNode(domainDiv).domain = domain;
+ if ( hostnames.length > 1 ) {
+ makeMatrixGroup1SectionMetaDomain(domain)
+ .appendTo(domainDiv);
+ }
+ makeMatrixGroup1SectionDomain(domain)
+ .appendTo(domainDiv);
+ for ( var i = 1; i < hostnames.length; i++ ) {
+ makeMatrixGroup1SectionSubomain(domain, hostnames[i])
+ .appendTo(domainDiv);
+ }
+ return domainDiv;
+ }
-function makeMatrixGroup0SectionDomain() {
- return makeMatrixRowDomain('1st-party').addClass('g0 l1');
-}
+ function makeMatrixGroup1(group) {
+ var domains = Object.keys(group).sort(hostnameCompare);
+ if ( domains.length ) {
+ var groupDiv = createMatrixGroup().addClass('g1');
+ makeMatrixGroup1Section(Object.keys(group[domains[0]]).sort(hostnameCompare))
+ .appendTo(groupDiv);
+ for ( var i = 1; i < domains.length; i++ ) {
+ makeMatrixGroup1Section(Object.keys(group[domains[i]]).sort(hostnameCompare))
+ .appendTo(groupDiv);
+ }
+ groupDiv.appendTo(matrixList);
+ }
+ }
-function makeMatrixGroup0Section() {
- var domainDiv = createMatrixSection();
- expandosFromNode(domainDiv).domain = '1st-party';
- makeMatrixGroup0SectionDomain().appendTo(domainDiv);
- return domainDiv;
-}
+ /******************************************************************************/
-function makeMatrixGroup0() {
- // Show literal "1st-party" row only if there is
- // at least one 1st-party hostname
- if ( Object.keys(groupsSnapshot[1]).length === 0 ) {
- return;
+ function makeMatrixGroup2SectionDomain(domain) {
+ return makeMatrixRowDomain(domain)
+ .addClass('g2 l1');
}
- var groupDiv = createMatrixGroup().addClass('g0');
- makeMatrixGroup0Section().appendTo(groupDiv);
- groupDiv.appendTo(matrixList);
-}
-/******************************************************************************/
+ function makeMatrixGroup2SectionSubomain(domain, subdomain) {
+ return makeMatrixRowSubdomain(domain, subdomain)
+ .addClass('g2 l2');
+ }
-function makeMatrixGroup1SectionDomain(domain) {
- return makeMatrixRowDomain(domain)
- .addClass('g1 l1');
-}
-
-function makeMatrixGroup1SectionSubomain(domain, subdomain) {
- return makeMatrixRowSubdomain(domain, subdomain)
- .addClass('g1 l2');
-}
-
-function makeMatrixGroup1SectionMetaDomain(domain) {
- return makeMatrixMetaRowDomain(domain).addClass('g1 l1 meta');
-}
-
-function makeMatrixGroup1Section(hostnames) {
- var domain = hostnames[0];
- var domainDiv = createMatrixSection()
- .toggleClass('collapsed', getCollapseState(domain));
- expandosFromNode(domainDiv).domain = domain;
- if ( hostnames.length > 1 ) {
- makeMatrixGroup1SectionMetaDomain(domain)
- .appendTo(domainDiv);
+ function makeMatrixGroup2SectionMetaDomain(domain) {
+ return makeMatrixMetaRowDomain(domain).addClass('g2 l1 meta');
}
- makeMatrixGroup1SectionDomain(domain)
- .appendTo(domainDiv);
- for ( var i = 1; i < hostnames.length; i++ ) {
- makeMatrixGroup1SectionSubomain(domain, hostnames[i])
+
+ function makeMatrixGroup2Section(hostnames) {
+ var domain = hostnames[0];
+ var domainDiv = createMatrixSection()
+ .toggleClass('collapsed', getCollapseState(domain));
+ expandosFromNode(domainDiv).domain = domain;
+ if ( hostnames.length > 1 ) {
+ makeMatrixGroup2SectionMetaDomain(domain).appendTo(domainDiv);
+ }
+ makeMatrixGroup2SectionDomain(domain)
.appendTo(domainDiv);
+ for ( var i = 1; i < hostnames.length; i++ ) {
+ makeMatrixGroup2SectionSubomain(domain, hostnames[i])
+ .appendTo(domainDiv);
+ }
+ return domainDiv;
}
- return domainDiv;
-}
-function makeMatrixGroup1(group) {
- var domains = Object.keys(group).sort(hostnameCompare);
- if ( domains.length ) {
- var groupDiv = createMatrixGroup().addClass('g1');
- makeMatrixGroup1Section(Object.keys(group[domains[0]]).sort(hostnameCompare))
- .appendTo(groupDiv);
- for ( var i = 1; i < domains.length; i++ ) {
- makeMatrixGroup1Section(Object.keys(group[domains[i]]).sort(hostnameCompare))
+ function makeMatrixGroup2(group) {
+ var domains = Object.keys(group).sort(hostnameCompare);
+ if ( domains.length) {
+ var groupDiv = createMatrixGroup()
+ .addClass('g2');
+ makeMatrixGroup2Section(Object.keys(group[domains[0]]).sort(hostnameCompare))
.appendTo(groupDiv);
+ for ( var i = 1; i < domains.length; i++ ) {
+ makeMatrixGroup2Section(Object.keys(group[domains[i]]).sort(hostnameCompare))
+ .appendTo(groupDiv);
+ }
+ groupDiv.appendTo(matrixList);
}
- groupDiv.appendTo(matrixList);
}
-}
-/******************************************************************************/
+ /******************************************************************************/
+
+ function makeMatrixGroup3SectionDomain(domain) {
+ return makeMatrixRowDomain(domain)
+ .addClass('g3 l1');
+ }
+
+ function makeMatrixGroup3SectionSubomain(domain, subdomain) {
+ return makeMatrixRowSubdomain(domain, subdomain)
+ .addClass('g3 l2');
+ }
-function makeMatrixGroup2SectionDomain(domain) {
- return makeMatrixRowDomain(domain)
- .addClass('g2 l1');
-}
-
-function makeMatrixGroup2SectionSubomain(domain, subdomain) {
- return makeMatrixRowSubdomain(domain, subdomain)
- .addClass('g2 l2');
-}
-
-function makeMatrixGroup2SectionMetaDomain(domain) {
- return makeMatrixMetaRowDomain(domain).addClass('g2 l1 meta');
-}
-
-function makeMatrixGroup2Section(hostnames) {
- var domain = hostnames[0];
- var domainDiv = createMatrixSection()
- .toggleClass('collapsed', getCollapseState(domain));
- expandosFromNode(domainDiv).domain = domain;
- if ( hostnames.length > 1 ) {
- makeMatrixGroup2SectionMetaDomain(domain).appendTo(domainDiv);
- }
- makeMatrixGroup2SectionDomain(domain)
- .appendTo(domainDiv);
- for ( var i = 1; i < hostnames.length; i++ ) {
- makeMatrixGroup2SectionSubomain(domain, hostnames[i])
+ function makeMatrixGroup3SectionMetaDomain(domain) {
+ return makeMatrixMetaRowDomain(domain).addClass('g3 l1 meta');
+ }
+
+ function makeMatrixGroup3Section(hostnames) {
+ var domain = hostnames[0];
+ var domainDiv = createMatrixSection()
+ .toggleClass('collapsed', getCollapseState(domain));
+ expandosFromNode(domainDiv).domain = domain;
+ if ( hostnames.length > 1 ) {
+ makeMatrixGroup3SectionMetaDomain(domain).appendTo(domainDiv);
+ }
+ makeMatrixGroup3SectionDomain(domain)
.appendTo(domainDiv);
+ for ( var i = 1; i < hostnames.length; i++ ) {
+ makeMatrixGroup3SectionSubomain(domain, hostnames[i])
+ .appendTo(domainDiv);
+ }
+ return domainDiv;
}
- return domainDiv;
-}
-function makeMatrixGroup2(group) {
- var domains = Object.keys(group).sort(hostnameCompare);
- if ( domains.length) {
- var groupDiv = createMatrixGroup()
- .addClass('g2');
- makeMatrixGroup2Section(Object.keys(group[domains[0]]).sort(hostnameCompare))
- .appendTo(groupDiv);
- for ( var i = 1; i < domains.length; i++ ) {
- makeMatrixGroup2Section(Object.keys(group[domains[i]]).sort(hostnameCompare))
+ function makeMatrixGroup3(group) {
+ var domains = Object.keys(group).sort(hostnameCompare);
+ if ( domains.length) {
+ var groupDiv = createMatrixGroup()
+ .addClass('g3');
+ makeMatrixGroup3Section(Object.keys(group[domains[0]]).sort(hostnameCompare))
.appendTo(groupDiv);
+ for ( var i = 1; i < domains.length; i++ ) {
+ makeMatrixGroup3Section(Object.keys(group[domains[i]]).sort(hostnameCompare))
+ .appendTo(groupDiv);
+ }
+ groupDiv.appendTo(matrixList);
}
- groupDiv.appendTo(matrixList);
}
-}
-/******************************************************************************/
+ /******************************************************************************/
-function makeMatrixGroup3SectionDomain(domain) {
- return makeMatrixRowDomain(domain)
- .addClass('g3 l1');
-}
-
-function makeMatrixGroup3SectionSubomain(domain, subdomain) {
- return makeMatrixRowSubdomain(domain, subdomain)
- .addClass('g3 l2');
-}
-
-function makeMatrixGroup3SectionMetaDomain(domain) {
- return makeMatrixMetaRowDomain(domain).addClass('g3 l1 meta');
-}
-
-function makeMatrixGroup3Section(hostnames) {
- var domain = hostnames[0];
- var domainDiv = createMatrixSection()
- .toggleClass('collapsed', getCollapseState(domain));
- expandosFromNode(domainDiv).domain = domain;
- if ( hostnames.length > 1 ) {
- makeMatrixGroup3SectionMetaDomain(domain).appendTo(domainDiv);
- }
- makeMatrixGroup3SectionDomain(domain)
- .appendTo(domainDiv);
- for ( var i = 1; i < hostnames.length; i++ ) {
- makeMatrixGroup3SectionSubomain(domain, hostnames[i])
+ function makeMatrixGroup4SectionDomain(domain) {
+ return makeMatrixRowDomain(domain)
+ .addClass('g4 l1');
+ }
+
+ function makeMatrixGroup4SectionSubomain(domain, subdomain) {
+ return makeMatrixRowSubdomain(domain, subdomain)
+ .addClass('g4 l2');
+ }
+
+ function makeMatrixGroup4Section(hostnames) {
+ var domain = hostnames[0];
+ var domainDiv = createMatrixSection();
+ expandosFromNode(domainDiv).domain = domain;
+ makeMatrixGroup4SectionDomain(domain)
.appendTo(domainDiv);
+ for ( var i = 1; i < hostnames.length; i++ ) {
+ makeMatrixGroup4SectionSubomain(domain, hostnames[i])
+ .appendTo(domainDiv);
+ }
+ return domainDiv;
}
- return domainDiv;
-}
-function makeMatrixGroup3(group) {
- var domains = Object.keys(group).sort(hostnameCompare);
- if ( domains.length) {
- var groupDiv = createMatrixGroup()
- .addClass('g3');
- makeMatrixGroup3Section(Object.keys(group[domains[0]]).sort(hostnameCompare))
+ function makeMatrixGroup4(group) {
+ var domains = Object.keys(group).sort(hostnameCompare);
+ if ( domains.length === 0 ) {
+ return;
+ }
+ var groupDiv = createMatrixGroup().addClass('g4');
+ createMatrixSection()
+ .addClass('g4Meta')
+ .toggleClass('g4Collapsed', !!matrixSnapshot.collapseBlacklistedDomains)
+ .appendTo(groupDiv);
+ makeMatrixMetaRow(computeMatrixGroupMetaStats(group), 'g4')
+ .appendTo(groupDiv);
+ makeMatrixGroup4Section(Object.keys(group[domains[0]]).sort(hostnameCompare))
.appendTo(groupDiv);
for ( var i = 1; i < domains.length; i++ ) {
- makeMatrixGroup3Section(Object.keys(group[domains[i]]).sort(hostnameCompare))
+ makeMatrixGroup4Section(Object.keys(group[domains[i]]).sort(hostnameCompare))
.appendTo(groupDiv);
}
groupDiv.appendTo(matrixList);
}
-}
-/******************************************************************************/
+ /******************************************************************************/
-function makeMatrixGroup4SectionDomain(domain) {
- return makeMatrixRowDomain(domain)
- .addClass('g4 l1');
-}
-
-function makeMatrixGroup4SectionSubomain(domain, subdomain) {
- return makeMatrixRowSubdomain(domain, subdomain)
- .addClass('g4 l2');
-}
-
-function makeMatrixGroup4Section(hostnames) {
- var domain = hostnames[0];
- var domainDiv = createMatrixSection();
- expandosFromNode(domainDiv).domain = domain;
- makeMatrixGroup4SectionDomain(domain)
- .appendTo(domainDiv);
- for ( var i = 1; i < hostnames.length; i++ ) {
- makeMatrixGroup4SectionSubomain(domain, hostnames[i])
- .appendTo(domainDiv);
- }
- return domainDiv;
-}
-
-function makeMatrixGroup4(group) {
- var domains = Object.keys(group).sort(hostnameCompare);
- if ( domains.length === 0 ) {
- return;
- }
- var groupDiv = createMatrixGroup().addClass('g4');
- createMatrixSection()
- .addClass('g4Meta')
- .toggleClass('g4Collapsed', !!matrixSnapshot.collapseBlacklistedDomains)
- .appendTo(groupDiv);
- makeMatrixMetaRow(computeMatrixGroupMetaStats(group), 'g4')
- .appendTo(groupDiv);
- makeMatrixGroup4Section(Object.keys(group[domains[0]]).sort(hostnameCompare))
- .appendTo(groupDiv);
- for ( var i = 1; i < domains.length; i++ ) {
- makeMatrixGroup4Section(Object.keys(group[domains[i]]).sort(hostnameCompare))
- .appendTo(groupDiv);
- }
- groupDiv.appendTo(matrixList);
-}
+ var makeMenu = function() {
+ var groupStats = getGroupStats();
-/******************************************************************************/
+ if ( Object.keys(groupStats).length === 0 ) { return; }
-var makeMenu = function() {
- var groupStats = getGroupStats();
+ // https://github.com/gorhill/httpswitchboard/issues/31
+ if ( matrixCellHotspots ) {
+ matrixCellHotspots.detach();
+ }
- if ( Object.keys(groupStats).length === 0 ) { return; }
+ renderMatrixHeaderRow();
- // https://github.com/gorhill/httpswitchboard/issues/31
- if ( matrixCellHotspots ) {
- matrixCellHotspots.detach();
+ startMatrixUpdate();
+ makeMatrixGroup0(groupStats[0]);
+ makeMatrixGroup1(groupStats[1]);
+ makeMatrixGroup2(groupStats[2]);
+ makeMatrixGroup3(groupStats[3]);
+ makeMatrixGroup4(groupStats[4]);
+ endMatrixUpdate();
+
+ initScopeCell();
+ updateMatrixButtons();
+ resizePopup();
+ };
+
+ /******************************************************************************/
+
+ // Do all the stuff that needs to be done before building menu et al.
+
+ function initMenuEnvironment() {
+ document.body.style.setProperty(
+ 'font-size',
+ getUserSetting('displayTextSize')
+ );
+ document.body.classList.toggle(
+ 'colorblind',
+ getUserSetting('colorBlindFriendly')
+ );
+ uDom.nodeFromId('version').textContent = matrixSnapshot.appVersion || '';
+
+ var prettyNames = matrixHeaderPrettyNames;
+ var keys = Object.keys(prettyNames);
+ var i = keys.length;
+ var cell, key, text;
+ while ( i-- ) {
+ key = keys[i];
+ cell = uDom('#matHead .matCell[data-req-type="'+ key +'"]');
+ text = vAPI.i18n(key + 'PrettyName');
+ cell.text(text);
+ prettyNames[key] = text;
+ }
+
+ firstPartyLabel = uDom('[data-i18n="matrix1stPartyLabel"]').text();
+ blacklistedHostnamesLabel = uDom('[data-i18n="matrixBlacklistedHostnames"]').text();
}
- renderMatrixHeaderRow();
+ /******************************************************************************/
- startMatrixUpdate();
- makeMatrixGroup0(groupStats[0]);
- makeMatrixGroup1(groupStats[1]);
- makeMatrixGroup2(groupStats[2]);
- makeMatrixGroup3(groupStats[3]);
- makeMatrixGroup4(groupStats[4]);
- endMatrixUpdate();
+ // Create page scopes for the web page
- initScopeCell();
- updateMatrixButtons();
- resizePopup();
-};
+ function selectGlobalScope() {
+ if ( matrixSnapshot.scope === '*' ) { return; }
+ matrixSnapshot.scope = '*';
+ document.body.classList.add('globalScope');
+ matrixSnapshot.tMatrixModifiedTime = undefined;
+ updateMatrixSnapshot();
+ dropDownMenuHide();
+ }
-/******************************************************************************/
+ function selectSpecificScope(ev) {
+ var newScope = ev.target.getAttribute('data-scope');
+ if ( !newScope || matrixSnapshot.scope === newScope ) { return; }
+ document.body.classList.remove('globalScope');
+ matrixSnapshot.scope = newScope;
+ matrixSnapshot.tMatrixModifiedTime = undefined;
+ updateMatrixSnapshot();
+ dropDownMenuHide();
+ }
-// Do all the stuff that needs to be done before building menu et al.
-
-function initMenuEnvironment() {
- document.body.style.setProperty(
- 'font-size',
- getUserSetting('displayTextSize')
- );
- document.body.classList.toggle(
- 'colorblind',
- getUserSetting('colorBlindFriendly')
- );
- uDom.nodeFromId('version').textContent = matrixSnapshot.appVersion || '';
-
- var prettyNames = matrixHeaderPrettyNames;
- var keys = Object.keys(prettyNames);
- var i = keys.length;
- var cell, key, text;
- while ( i-- ) {
- key = keys[i];
- cell = uDom('#matHead .matCell[data-req-type="'+ key +'"]');
- text = vAPI.i18n(key + 'PrettyName');
- cell.text(text);
- prettyNames[key] = text;
- }
-
- firstPartyLabel = uDom('[data-i18n="matrix1stPartyLabel"]').text();
- blacklistedHostnamesLabel = uDom('[data-i18n="matrixBlacklistedHostnames"]').text();
-}
+ function initScopeCell() {
+ // It's possible there is no page URL at this point: some pages cannot
+ // be filtered by uMatrix.
+ if ( matrixSnapshot.url === '' ) { return; }
+ var specificScope = uDom.nodeFromId('specificScope');
-/******************************************************************************/
+ while ( specificScope.firstChild !== null ) {
+ specificScope.removeChild(specificScope.firstChild);
+ }
-// Create page scopes for the web page
-
-function selectGlobalScope() {
- if ( matrixSnapshot.scope === '*' ) { return; }
- matrixSnapshot.scope = '*';
- document.body.classList.add('globalScope');
- matrixSnapshot.tMatrixModifiedTime = undefined;
- updateMatrixSnapshot();
- dropDownMenuHide();
-}
-
-function selectSpecificScope(ev) {
- var newScope = ev.target.getAttribute('data-scope');
- if ( !newScope || matrixSnapshot.scope === newScope ) { return; }
- document.body.classList.remove('globalScope');
- matrixSnapshot.scope = newScope;
- matrixSnapshot.tMatrixModifiedTime = undefined;
- updateMatrixSnapshot();
- dropDownMenuHide();
-}
-
-function initScopeCell() {
- // It's possible there is no page URL at this point: some pages cannot
- // be filtered by uMatrix.
- if ( matrixSnapshot.url === '' ) { return; }
- var specificScope = uDom.nodeFromId('specificScope');
-
- while ( specificScope.firstChild !== null ) {
- specificScope.removeChild(specificScope.firstChild);
- }
-
- // Fill in the scope menu entries
- var pos = matrixSnapshot.domain.indexOf('.');
- var tld, labels;
- if ( pos === -1 ) {
- tld = '';
- labels = matrixSnapshot.hostname;
- } else {
- tld = matrixSnapshot.domain.slice(pos + 1);
- labels = matrixSnapshot.hostname.slice(0, -tld.length);
- }
- var beg = 0, span, label;
- while ( beg < labels.length ) {
- pos = labels.indexOf('.', beg);
+ // Fill in the scope menu entries
+ var pos = matrixSnapshot.domain.indexOf('.');
+ var tld, labels;
if ( pos === -1 ) {
- pos = labels.length;
+ tld = '';
+ labels = matrixSnapshot.hostname;
} else {
- pos += 1;
+ tld = matrixSnapshot.domain.slice(pos + 1);
+ labels = matrixSnapshot.hostname.slice(0, -tld.length);
}
- label = document.createElement('span');
- label.appendChild(
- document.createTextNode(punycode.toUnicode(labels.slice(beg, pos)))
- );
- span = document.createElement('span');
- span.setAttribute('data-scope', labels.slice(beg) + tld);
- span.appendChild(label);
- specificScope.appendChild(span);
- beg = pos;
- }
- if ( tld !== '' ) {
- label = document.createElement('span');
- label.appendChild(document.createTextNode(punycode.toUnicode(tld)));
- span = document.createElement('span');
- span.setAttribute('data-scope', tld);
- span.appendChild(label);
- specificScope.appendChild(span);
- }
- updateScopeCell();
-}
-
-function updateScopeCell() {
- var specificScope = uDom.nodeFromId('specificScope'),
- isGlobal = matrixSnapshot.scope === '*';
- document.body.classList.toggle('globalScope', isGlobal);
- specificScope.classList.toggle('on', !isGlobal);
- uDom.nodeFromId('globalScope').classList.toggle('on', isGlobal);
- for ( var node of specificScope.children ) {
- node.classList.toggle(
- 'on',
- !isGlobal &&
- matrixSnapshot.scope.endsWith(node.getAttribute('data-scope'))
- );
+ var beg = 0, span, label;
+ while ( beg < labels.length ) {
+ pos = labels.indexOf('.', beg);
+ if ( pos === -1 ) {
+ pos = labels.length;
+ } else {
+ pos += 1;
+ }
+ label = document.createElement('span');
+ label.appendChild(
+ document.createTextNode(punycode.toUnicode(labels.slice(beg, pos)))
+ );
+ span = document.createElement('span');
+ span.setAttribute('data-scope', labels.slice(beg) + tld);
+ span.appendChild(label);
+ specificScope.appendChild(span);
+ beg = pos;
+ }
+ if ( tld !== '' ) {
+ label = document.createElement('span');
+ label.appendChild(document.createTextNode(punycode.toUnicode(tld)));
+ span = document.createElement('span');
+ span.setAttribute('data-scope', tld);
+ span.appendChild(label);
+ specificScope.appendChild(span);
+ }
+ updateScopeCell();
}
-}
-/******************************************************************************/
-
-function updateMatrixSwitches() {
- var count = 0,
- enabled,
- switches = matrixSnapshot.tSwitches;
- for ( var switchName in switches ) {
- if ( switches.hasOwnProperty(switchName) === false ) { continue; }
- enabled = switches[switchName];
- if ( enabled && switchName !== 'matrix-off' ) {
- count += 1;
+ function updateScopeCell() {
+ var specificScope = uDom.nodeFromId('specificScope'),
+ isGlobal = matrixSnapshot.scope === '*';
+ document.body.classList.toggle('globalScope', isGlobal);
+ specificScope.classList.toggle('on', !isGlobal);
+ uDom.nodeFromId('globalScope').classList.toggle('on', isGlobal);
+ for ( var node of specificScope.children ) {
+ node.classList.toggle(
+ 'on',
+ !isGlobal &&
+ matrixSnapshot.scope.endsWith(node.getAttribute('data-scope'))
+ );
}
- uDom('#mtxSwitch_' + switchName).toggleClass('switchTrue', enabled);
- }
- uDom.nodeFromId('mtxSwitch_https-strict').classList.toggle(
- 'relevant',
- matrixSnapshot.hasMixedContent
- );
- uDom.nodeFromId('mtxSwitch_no-workers').classList.toggle(
- 'relevant',
- matrixSnapshot.hasWebWorkers
- );
- uDom.nodeFromId('mtxSwitch_referrer-spoof').classList.toggle(
- 'relevant',
- matrixSnapshot.has3pReferrer
- );
- uDom.nodeFromId('mtxSwitch_noscript-spoof').classList.toggle(
- 'relevant',
- matrixSnapshot.hasNoscriptTags
- );
- uDom.nodeFromSelector('#buttonMtxSwitches span.badge').textContent =
- count.toLocaleString();
- uDom.nodeFromSelector('#mtxSwitch_matrix-off span.badge').textContent =
- matrixSnapshot.blockedCount.toLocaleString();
- document.body.classList.toggle('powerOff', switches['matrix-off']);
-}
-
-function toggleMatrixSwitch(ev) {
- if ( ev.target.localName === 'a' ) { return; }
- var elem = ev.currentTarget;
- var pos = elem.id.indexOf('_');
- if ( pos === -1 ) { return; }
- var switchName = elem.id.slice(pos + 1);
- var request = {
- what: 'toggleMatrixSwitch',
- switchName: switchName,
- srcHostname: matrixSnapshot.scope
- };
- vAPI.messaging.send('popup.js', request, updateMatrixSnapshot);
-}
-
-/******************************************************************************/
-
-function updatePersistButton() {
- var diffCount = matrixSnapshot.diff.length;
- var button = uDom('#buttonPersist');
- button.contents()
- .filter(function(){return this.nodeType===3;})
- .first()
- .text(diffCount > 0 ? '\uf13e' : '\uf023');
- button.descendants('span.badge').text(diffCount > 0 ? diffCount : '');
- var disabled = diffCount === 0;
- button.toggleClass('disabled', disabled);
- uDom('#buttonRevertScope').toggleClass('disabled', disabled);
-}
-
-/******************************************************************************/
-
-function persistMatrix() {
- var request = {
- what: 'applyDiffToPermanentMatrix',
- diff: matrixSnapshot.diff
- };
- vAPI.messaging.send('popup.js', request, updateMatrixSnapshot);
-}
+ }
-/******************************************************************************/
+ /******************************************************************************/
+
+ function updateMatrixSwitches() {
+ var count = 0,
+ enabled,
+ switches = matrixSnapshot.tSwitches;
+ for ( var switchName in switches ) {
+ if ( switches.hasOwnProperty(switchName) === false ) { continue; }
+ enabled = switches[switchName];
+ if ( enabled && switchName !== 'matrix-off' ) {
+ count += 1;
+ }
+ uDom('#mtxSwitch_' + switchName).toggleClass('switchTrue', enabled);
+ }
+ uDom.nodeFromId('mtxSwitch_https-strict').classList.toggle(
+ 'relevant',
+ matrixSnapshot.hasMixedContent
+ );
+ uDom.nodeFromId('mtxSwitch_no-workers').classList.toggle(
+ 'relevant',
+ matrixSnapshot.hasWebWorkers
+ );
+ uDom.nodeFromId('mtxSwitch_referrer-spoof').classList.toggle(
+ 'relevant',
+ matrixSnapshot.has3pReferrer
+ );
+ uDom.nodeFromId('mtxSwitch_noscript-spoof').classList.toggle(
+ 'relevant',
+ matrixSnapshot.hasNoscriptTags
+ );
+ uDom.nodeFromSelector('#buttonMtxSwitches span.badge').textContent =
+ count.toLocaleString();
+ uDom.nodeFromSelector('#mtxSwitch_matrix-off span.badge').textContent =
+ matrixSnapshot.blockedCount.toLocaleString();
+ document.body.classList.toggle('powerOff', switches['matrix-off']);
+ }
-// rhill 2014-03-12: revert completely ALL changes related to the
-// current page, including scopes.
+ function toggleMatrixSwitch(ev) {
+ if ( ev.target.localName === 'a' ) { return; }
+ var elem = ev.currentTarget;
+ var pos = elem.id.indexOf('_');
+ if ( pos === -1 ) { return; }
+ var switchName = elem.id.slice(pos + 1);
+ var request = {
+ what: 'toggleMatrixSwitch',
+ switchName: switchName,
+ srcHostname: matrixSnapshot.scope
+ };
+ vAPI.messaging.send('popup.js', request, updateMatrixSnapshot);
+ }
-function revertMatrix() {
- var request = {
- what: 'applyDiffToTemporaryMatrix',
- diff: matrixSnapshot.diff
- };
- vAPI.messaging.send('popup.js', request, updateMatrixSnapshot);
-}
+ /******************************************************************************/
+
+ function updatePersistButton() {
+ var diffCount = matrixSnapshot.diff.length;
+ var button = uDom('#buttonPersist');
+ button.contents()
+ .filter(function(){return this.nodeType===3;})
+ .first()
+ .text(diffCount > 0 ? '\uf13e' : '\uf023');
+ button.descendants('span.badge').text(diffCount > 0 ? diffCount : '');
+ var disabled = diffCount === 0;
+ button.toggleClass('disabled', disabled);
+ uDom('#buttonRevertScope').toggleClass('disabled', disabled);
+ }
-/******************************************************************************/
+ /******************************************************************************/
-// Buttons which are affected by any changes in the matrix
+ function persistMatrix() {
+ var request = {
+ what: 'applyDiffToPermanentMatrix',
+ diff: matrixSnapshot.diff
+ };
+ vAPI.messaging.send('popup.js', request, updateMatrixSnapshot);
+ }
-function updateMatrixButtons() {
- updateScopeCell();
- updateMatrixSwitches();
- updatePersistButton();
-}
+ /******************************************************************************/
-/******************************************************************************/
+ // rhill 2014-03-12: revert completely ALL changes related to the
+ // current page, including scopes.
-function revertAll() {
- var request = {
- what: 'revertTemporaryMatrix'
- };
- vAPI.messaging.send('popup.js', request, updateMatrixSnapshot);
- dropDownMenuHide();
-}
+ function revertMatrix() {
+ var request = {
+ what: 'applyDiffToTemporaryMatrix',
+ diff: matrixSnapshot.diff
+ };
+ vAPI.messaging.send('popup.js', request, updateMatrixSnapshot);
+ }
-/******************************************************************************/
+ /******************************************************************************/
-function buttonReloadHandler(ev) {
- vAPI.messaging.send('popup.js', {
- what: 'forceReloadTab',
- tabId: matrixSnapshot.tabId,
- bypassCache: ev.ctrlKey || ev.metaKey || ev.shiftKey
- });
-}
+ // Buttons which are affected by any changes in the matrix
-/******************************************************************************/
+ function updateMatrixButtons() {
+ updateScopeCell();
+ updateMatrixSwitches();
+ updatePersistButton();
+ }
-function mouseenterMatrixCellHandler(ev) {
- matrixCellHotspots.appendTo(ev.target);
-}
+ /******************************************************************************/
-function mouseleaveMatrixCellHandler() {
- matrixCellHotspots.detach();
-}
+ function revertAll() {
+ var request = {
+ what: 'revertTemporaryMatrix'
+ };
+ vAPI.messaging.send('popup.js', request, updateMatrixSnapshot);
+ dropDownMenuHide();
+ }
-/******************************************************************************/
+ /******************************************************************************/
-function gotoExtensionURL(ev) {
- var url = uDom(ev.currentTarget).attr('data-extension-url');
- if ( url ) {
+ function buttonReloadHandler(ev) {
vAPI.messaging.send('popup.js', {
- what: 'gotoExtensionURL',
- url: url,
- shiftKey: ev.shiftKey
+ what: 'forceReloadTab',
+ tabId: matrixSnapshot.tabId,
+ bypassCache: ev.ctrlKey || ev.metaKey || ev.shiftKey
});
}
- dropDownMenuHide();
- vAPI.closePopup();
-}
-
-/******************************************************************************/
-function dropDownMenuShow(ev) {
- var button = ev.target;
- var menuOverlay = document.getElementById(button.getAttribute('data-dropdown-menu'));
- var butnRect = button.getBoundingClientRect();
- var viewRect = document.body.getBoundingClientRect();
- var butnNormalLeft = butnRect.left / (viewRect.width - butnRect.width);
- menuOverlay.classList.add('show');
- var menu = menuOverlay.querySelector('.dropdown-menu');
- var menuRect = menu.getBoundingClientRect();
- var menuLeft = butnNormalLeft * (viewRect.width - menuRect.width);
- menu.style.left = menuLeft.toFixed(0) + 'px';
- menu.style.top = butnRect.bottom + 'px';
-}
-
-function dropDownMenuHide() {
- uDom('.dropdown-menu-capture').removeClass('show');
-}
+ /******************************************************************************/
-/******************************************************************************/
-
-var onMatrixSnapshotReady = function(response) {
- if ( response === 'ENOTFOUND' ) {
- uDom.nodeFromId('noTabFound').textContent =
- vAPI.i18n('matrixNoTabFound');
- document.body.classList.add('noTabFound');
- return;
+ function mouseenterMatrixCellHandler(ev) {
+ matrixCellHotspots.appendTo(ev.target);
}
- // Now that tabId and pageURL are set, we can build our menu
- initMenuEnvironment();
- makeMenu();
+ function mouseleaveMatrixCellHandler() {
+ matrixCellHotspots.detach();
+ }
- // After popup menu is built, check whether there is a non-empty matrix
- if ( matrixSnapshot.url === '' ) {
- uDom('#matHead').remove();
- uDom('#toolbarContainer').remove();
+ /******************************************************************************/
- // https://github.com/gorhill/httpswitchboard/issues/191
- uDom('#noNetTrafficPrompt').text(vAPI.i18n('matrixNoNetTrafficPrompt'));
- uDom('#noNetTrafficPrompt').css('display', '');
+ function gotoExtensionURL(ev) {
+ var url = uDom(ev.currentTarget).attr('data-extension-url');
+ if ( url ) {
+ vAPI.messaging.send('popup.js', {
+ what: 'gotoExtensionURL',
+ url: url,
+ shiftKey: ev.shiftKey
+ });
+ }
+ dropDownMenuHide();
+ vAPI.closePopup();
}
- // Create a hash to find out whether the reload button needs to be
- // highlighted.
- // TODO:
-};
+ /******************************************************************************/
+
+ function dropDownMenuShow(ev) {
+ var button = ev.target;
+ var menuOverlay = document.getElementById(button.getAttribute('data-dropdown-menu'));
+ var butnRect = button.getBoundingClientRect();
+ var viewRect = document.body.getBoundingClientRect();
+ var butnNormalLeft = butnRect.left / (viewRect.width - butnRect.width);
+ menuOverlay.classList.add('show');
+ var menu = menuOverlay.querySelector('.dropdown-menu');
+ var menuRect = menu.getBoundingClientRect();
+ var menuLeft = butnNormalLeft * (viewRect.width - menuRect.width);
+ menu.style.left = menuLeft.toFixed(0) + 'px';
+ menu.style.top = butnRect.bottom + 'px';
+ }
-/******************************************************************************/
+ function dropDownMenuHide() {
+ uDom('.dropdown-menu-capture').removeClass('show');
+ }
-var matrixSnapshotPoller = (function() {
- var timer = null;
+ /******************************************************************************/
- var preprocessMatrixSnapshot = function(snapshot) {
- if ( Array.isArray(snapshot.headerIndices) ) {
- snapshot.headerIndices = new Map(snapshot.headerIndices);
- }
- return snapshot;
- };
-
- var processPollResult = function(response) {
- if ( typeof response !== 'object' ) {
- return;
- }
- if (
- response.mtxContentModified === false &&
- response.mtxCountModified === false &&
- response.pMatrixModified === false &&
- response.tMatrixModified === false
- ) {
+ var onMatrixSnapshotReady = function(response) {
+ if ( response === 'ENOTFOUND' ) {
+ uDom.nodeFromId('noTabFound').textContent =
+ vAPI.i18n('matrixNoTabFound');
+ document.body.classList.add('noTabFound');
return;
}
- matrixSnapshot = preprocessMatrixSnapshot(response);
- if ( response.mtxContentModified ) {
- makeMenu();
- return;
- }
- if ( response.mtxCountModified ) {
- updateMatrixCounts();
- }
- if (
- response.pMatrixModified ||
- response.tMatrixModified ||
- response.scopeModified
- ) {
- updateMatrixColors();
- updateMatrixBehavior();
- updateMatrixButtons();
+ // Now that tabId and pageURL are set, we can build our menu
+ initMenuEnvironment();
+ makeMenu();
+
+ // After popup menu is built, check whether there is a non-empty matrix
+ if ( matrixSnapshot.url === '' ) {
+ uDom('#matHead').remove();
+ uDom('#toolbarContainer').remove();
+
+ // https://github.com/gorhill/httpswitchboard/issues/191
+ uDom('#noNetTrafficPrompt').text(vAPI.i18n('matrixNoNetTrafficPrompt'));
+ uDom('#noNetTrafficPrompt').css('display', '');
}
- };
- var onPolled = function(response) {
- processPollResult(response);
- pollAsync();
+ // Create a hash to find out whether the reload button needs to be
+ // highlighted.
+ // TODO:
};
- var pollNow = function() {
- unpollAsync();
- vAPI.messaging.send('popup.js', {
- what: 'matrixSnapshot',
- tabId: matrixSnapshot.tabId,
- scope: matrixSnapshot.scope,
- mtxContentModifiedTime: matrixSnapshot.mtxContentModifiedTime,
- mtxCountModifiedTime: matrixSnapshot.mtxCountModifiedTime,
- mtxDiffCount: matrixSnapshot.diff.length,
- pMatrixModifiedTime: matrixSnapshot.pMatrixModifiedTime,
- tMatrixModifiedTime: matrixSnapshot.tMatrixModifiedTime,
- }, onPolled);
- };
+ /******************************************************************************/
- var poll = function() {
- timer = null;
- pollNow();
- };
+ var matrixSnapshotPoller = (function() {
+ var timer = null;
- var pollAsync = function() {
- if ( timer !== null ) {
- return;
- }
- if ( document.defaultView === null ) {
- return;
- }
- timer = vAPI.setTimeout(poll, 1414);
- };
+ var preprocessMatrixSnapshot = function(snapshot) {
+ if ( Array.isArray(snapshot.headerIndices) ) {
+ snapshot.headerIndices = new Map(snapshot.headerIndices);
+ }
+ return snapshot;
+ };
+
+ var processPollResult = function(response) {
+ if ( typeof response !== 'object' ) {
+ return;
+ }
+ if (
+ response.mtxContentModified === false &&
+ response.mtxCountModified === false &&
+ response.pMatrixModified === false &&
+ response.tMatrixModified === false
+ ) {
+ return;
+ }
+ matrixSnapshot = preprocessMatrixSnapshot(response);
+
+ if ( response.mtxContentModified ) {
+ makeMenu();
+ return;
+ }
+ if ( response.mtxCountModified ) {
+ updateMatrixCounts();
+ }
+ if (
+ response.pMatrixModified ||
+ response.tMatrixModified ||
+ response.scopeModified
+ ) {
+ updateMatrixColors();
+ updateMatrixBehavior();
+ updateMatrixButtons();
+ }
+ };
+
+ var onPolled = function(response) {
+ processPollResult(response);
+ pollAsync();
+ };
+
+ var pollNow = function() {
+ unpollAsync();
+ vAPI.messaging.send('popup.js', {
+ what: 'matrixSnapshot',
+ tabId: matrixSnapshot.tabId,
+ scope: matrixSnapshot.scope,
+ mtxContentModifiedTime: matrixSnapshot.mtxContentModifiedTime,
+ mtxCountModifiedTime: matrixSnapshot.mtxCountModifiedTime,
+ mtxDiffCount: matrixSnapshot.diff.length,
+ pMatrixModifiedTime: matrixSnapshot.pMatrixModifiedTime,
+ tMatrixModifiedTime: matrixSnapshot.tMatrixModifiedTime,
+ }, onPolled);
+ };
- var unpollAsync = function() {
- if ( timer !== null ) {
- clearTimeout(timer);
+ var poll = function() {
timer = null;
- }
- };
+ pollNow();
+ };
- (function() {
- var tabId = matrixSnapshot.tabId;
-
- // If no tab id yet, see if there is one specified in our URL
- if ( tabId === undefined ) {
- var matches = window.location.search.match(/(?:\?|&)tabId=([^&]+)/);
- if ( matches !== null ) {
- tabId = matches[1];
- // No need for logger button when embedded in logger
- uDom('[data-extension-url="logger-ui.html"]').remove();
+ var pollAsync = function() {
+ if ( timer !== null ) {
+ return;
}
- }
+ if ( document.defaultView === null ) {
+ return;
+ }
+ timer = vAPI.setTimeout(poll, 1414);
+ };
- var snapshotFetched = function(response) {
- if ( typeof response === 'object' ) {
- matrixSnapshot = preprocessMatrixSnapshot(response);
+ var unpollAsync = function() {
+ if ( timer !== null ) {
+ clearTimeout(timer);
+ timer = null;
}
- onMatrixSnapshotReady(response);
- pollAsync();
};
- vAPI.messaging.send('popup.js', {
- what: 'matrixSnapshot',
- tabId: tabId
- }, snapshotFetched);
- })();
+ (function() {
+ var tabId = matrixSnapshot.tabId;
+
+ // If no tab id yet, see if there is one specified in our URL
+ if ( tabId === undefined ) {
+ var matches = window.location.search.match(/(?:\?|&)tabId=([^&]+)/);
+ if ( matches !== null ) {
+ tabId = matches[1];
+ // No need for logger button when embedded in logger
+ uDom('[data-extension-url="logger-ui.html"]').remove();
+ }
+ }
- return {
- pollNow: pollNow
- };
-})();
+ var snapshotFetched = function(response) {
+ if ( typeof response === 'object' ) {
+ matrixSnapshot = preprocessMatrixSnapshot(response);
+ }
+ onMatrixSnapshotReady(response);
+ pollAsync();
+ };
+
+ vAPI.messaging.send('popup.js', {
+ what: 'matrixSnapshot',
+ tabId: tabId
+ }, snapshotFetched);
+ })();
+
+ return {
+ pollNow: pollNow
+ };
+ })();
-/******************************************************************************/
+ /******************************************************************************/
-// Below is UI stuff which is not key to make the menu, so this can
-// be done without having to wait for a tab to be bound to the menu.
+ // Below is UI stuff which is not key to make the menu, so this can
+ // be done without having to wait for a tab to be bound to the menu.
-// We reuse for all cells the one and only cell hotspots.
-uDom('#whitelist').on('click', function() {
+ // We reuse for all cells the one and only cell hotspots.
+ uDom('#whitelist').on('click', function() {
handleWhitelistFilter(uDom(this));
return false;
});
-uDom('#blacklist').on('click', function() {
+ uDom('#blacklist').on('click', function() {
handleBlacklistFilter(uDom(this));
return false;
});
-uDom('#domainOnly').on('click', function() {
+ uDom('#domainOnly').on('click', function() {
toggleCollapseState(uDom(this));
return false;
});
-matrixCellHotspots = uDom('#cellHotspots').detach();
-uDom('body')
- .on('mouseenter', '.matCell', mouseenterMatrixCellHandler)
- .on('mouseleave', '.matCell', mouseleaveMatrixCellHandler);
-uDom('#specificScope').on('click', selectSpecificScope);
-uDom('#globalScope').on('click', selectGlobalScope);
-uDom('[id^="mtxSwitch_"]').on('click', toggleMatrixSwitch);
-uDom('#buttonPersist').on('click', persistMatrix);
-uDom('#buttonRevertScope').on('click', revertMatrix);
-
-uDom('#buttonRevertAll').on('click', revertAll);
-uDom('#buttonReload').on('click', buttonReloadHandler);
-uDom('.extensionURL').on('click', gotoExtensionURL);
-
-uDom('body').on('click', '[data-dropdown-menu]', dropDownMenuShow);
-uDom('body').on('click', '.dropdown-menu-capture', dropDownMenuHide);
-
-uDom('#matList').on('click', '.g4Meta', function(ev) {
- matrixSnapshot.collapseBlacklistedDomains =
- ev.target.classList.toggle('g4Collapsed');
- setUserSetting(
- 'popupCollapseBlacklistedDomains',
- matrixSnapshot.collapseBlacklistedDomains
- );
-});
+ matrixCellHotspots = uDom('#cellHotspots').detach();
+ uDom('body')
+ .on('mouseenter', '.matCell', mouseenterMatrixCellHandler)
+ .on('mouseleave', '.matCell', mouseleaveMatrixCellHandler);
+ uDom('#specificScope').on('click', selectSpecificScope);
+ uDom('#globalScope').on('click', selectGlobalScope);
+ uDom('[id^="mtxSwitch_"]').on('click', toggleMatrixSwitch);
+ uDom('#buttonPersist').on('click', persistMatrix);
+ uDom('#buttonRevertScope').on('click', revertMatrix);
+
+ uDom('#buttonRevertAll').on('click', revertAll);
+ uDom('#buttonReload').on('click', buttonReloadHandler);
+ uDom('.extensionURL').on('click', gotoExtensionURL);
+
+ uDom('body').on('click', '[data-dropdown-menu]', dropDownMenuShow);
+ uDom('body').on('click', '.dropdown-menu-capture', dropDownMenuHide);
+
+ uDom('#matList').on('click', '.g4Meta', function(ev) {
+ matrixSnapshot.collapseBlacklistedDomains =
+ ev.target.classList.toggle('g4Collapsed');
+ setUserSetting(
+ 'popupCollapseBlacklistedDomains',
+ matrixSnapshot.collapseBlacklistedDomains
+ );
+ });
-/******************************************************************************/
+ /******************************************************************************/
})();
diff --git a/js/raw-settings.js b/js/raw-settings.js
index 98032f6..6c3bb70 100644
--- a/js/raw-settings.js
+++ b/js/raw-settings.js
@@ -29,89 +29,89 @@
(function() {
-/******************************************************************************/
+ /******************************************************************************/
-var messaging = vAPI.messaging;
-var cachedData = '';
-var rawSettingsInput = uDom.nodeFromId('rawSettings');
+ var messaging = vAPI.messaging;
+ var cachedData = '';
+ var rawSettingsInput = uDom.nodeFromId('rawSettings');
-/******************************************************************************/
+ /******************************************************************************/
-var hashFromRawSettings = function(raw) {
- return raw.trim().replace(/\s+/g, '|');
-};
+ var hashFromRawSettings = function(raw) {
+ return raw.trim().replace(/\s+/g, '|');
+ };
-/******************************************************************************/
+ /******************************************************************************/
-// This is to give a visual hint that the content of user blacklist has changed.
+ // This is to give a visual hint that the content of user blacklist has changed.
-var rawSettingsChanged = (function () {
- var timer = null;
+ var rawSettingsChanged = (function () {
+ var timer = null;
- var handler = function() {
- timer = null;
- var changed =
- hashFromRawSettings(rawSettingsInput.value) !== cachedData;
- uDom.nodeFromId('rawSettingsApply').disabled = !changed;
- };
+ var handler = function() {
+ timer = null;
+ var changed =
+ hashFromRawSettings(rawSettingsInput.value) !== cachedData;
+ uDom.nodeFromId('rawSettingsApply').disabled = !changed;
+ };
- return function() {
- if ( timer !== null ) {
- clearTimeout(timer);
- }
- timer = vAPI.setTimeout(handler, 100);
- };
-})();
-
-/******************************************************************************/
-
-function renderRawSettings() {
- var onRead = function(raw) {
- cachedData = hashFromRawSettings(raw);
- var pretty = [],
- whitespaces = ' ',
- lines = raw.split('\n'),
- max = 0,
- pos,
- i, n = lines.length;
- for ( i = 0; i < n; i++ ) {
- pos = lines[i].indexOf(' ');
- if ( pos > max ) {
- max = pos;
+ return function() {
+ if ( timer !== null ) {
+ clearTimeout(timer);
+ }
+ timer = vAPI.setTimeout(handler, 100);
+ };
+ })();
+
+ /******************************************************************************/
+
+ function renderRawSettings() {
+ var onRead = function(raw) {
+ cachedData = hashFromRawSettings(raw);
+ var pretty = [],
+ whitespaces = ' ',
+ lines = raw.split('\n'),
+ max = 0,
+ pos,
+ i, n = lines.length;
+ for ( i = 0; i < n; i++ ) {
+ pos = lines[i].indexOf(' ');
+ if ( pos > max ) {
+ max = pos;
+ }
}
- }
- for ( i = 0; i < n; i++ ) {
- pos = lines[i].indexOf(' ');
- pretty.push(whitespaces.slice(0, max - pos) + lines[i]);
- }
- rawSettingsInput.value = pretty.join('\n') + '\n';
- rawSettingsChanged();
- rawSettingsInput.focus();
+ for ( i = 0; i < n; i++ ) {
+ pos = lines[i].indexOf(' ');
+ pretty.push(whitespaces.slice(0, max - pos) + lines[i]);
+ }
+ rawSettingsInput.value = pretty.join('\n') + '\n';
+ rawSettingsChanged();
+ rawSettingsInput.focus();
+ };
+ messaging.send('dashboard', { what: 'readRawSettings' }, onRead);
+ }
+
+ /******************************************************************************/
+
+ var applyChanges = function() {
+ messaging.send(
+ 'dashboard',
+ {
+ what: 'writeRawSettings',
+ content: rawSettingsInput.value
+ },
+ renderRawSettings
+ );
};
- messaging.send('dashboard', { what: 'readRawSettings' }, onRead);
-}
-
-/******************************************************************************/
-
-var applyChanges = function() {
- messaging.send(
- 'dashboard',
- {
- what: 'writeRawSettings',
- content: rawSettingsInput.value
- },
- renderRawSettings
- );
-};
-/******************************************************************************/
+ /******************************************************************************/
-// Handle user interaction
-uDom('#rawSettings').on('input', rawSettingsChanged);
-uDom('#rawSettingsApply').on('click', applyChanges);
+ // Handle user interaction
+ uDom('#rawSettings').on('input', rawSettingsChanged);
+ uDom('#rawSettingsApply').on('click', applyChanges);
-renderRawSettings();
+ renderRawSettings();
-/******************************************************************************/
+ /******************************************************************************/
})();
diff --git a/js/settings.js b/js/settings.js
index f147e74..da8b6df 100644
--- a/js/settings.js
+++ b/js/settings.js
@@ -29,168 +29,168 @@
(function() {
-/******************************************************************************/
-
-var cachedSettings = {};
-
-/******************************************************************************/
+ /******************************************************************************/
-function changeUserSettings(name, value) {
- vAPI.messaging.send('settings.js', {
- what: 'userSettings',
- name: name,
- value: value
- });
-}
+ var cachedSettings = {};
-/******************************************************************************/
+ /******************************************************************************/
-function changeMatrixSwitch(name, state) {
- vAPI.messaging.send('settings.js', {
- what: 'setMatrixSwitch',
- switchName: name,
- state: state
- });
-}
+ function changeUserSettings(name, value) {
+ vAPI.messaging.send('settings.js', {
+ what: 'userSettings',
+ name: name,
+ value: value
+ });
+ }
-/******************************************************************************/
+ /******************************************************************************/
-function onChangeValueHandler(elem, setting, min, max) {
- var oldVal = cachedSettings.userSettings[setting];
- var newVal = Math.round(parseFloat(elem.value));
- if ( typeof newVal !== 'number' ) {
- newVal = oldVal;
- } else {
- newVal = Math.max(newVal, min);
- newVal = Math.min(newVal, max);
- }
- elem.value = newVal;
- if ( newVal !== oldVal ) {
- changeUserSettings(setting, newVal);
+ function changeMatrixSwitch(name, state) {
+ vAPI.messaging.send('settings.js', {
+ what: 'setMatrixSwitch',
+ switchName: name,
+ state: state
+ });
}
-}
-
-/******************************************************************************/
-function prepareToDie() {
- onChangeValueHandler(
- uDom.nodeFromId('deleteUnusedSessionCookiesAfter'),
- 'deleteUnusedSessionCookiesAfter',
- 15, 1440
- );
- onChangeValueHandler(
- uDom.nodeFromId('clearBrowserCacheAfter'),
- 'clearBrowserCacheAfter',
- 15, 1440
- );
-}
+ /******************************************************************************/
+
+ function onChangeValueHandler(elem, setting, min, max) {
+ var oldVal = cachedSettings.userSettings[setting];
+ var newVal = Math.round(parseFloat(elem.value));
+ if ( typeof newVal !== 'number' ) {
+ newVal = oldVal;
+ } else {
+ newVal = Math.max(newVal, min);
+ newVal = Math.min(newVal, max);
+ }
+ elem.value = newVal;
+ if ( newVal !== oldVal ) {
+ changeUserSettings(setting, newVal);
+ }
+ }
-/******************************************************************************/
+ /******************************************************************************/
-function onInputChanged(ev) {
- var target = ev.target;
-
- switch ( target.id ) {
- case 'displayTextSize':
- changeUserSettings('displayTextSize', target.value + 'px');
- break;
- case 'clearBrowserCache':
- case 'cloudStorageEnabled':
- case 'collapseBlacklisted':
- case 'collapseBlocked':
- case 'colorBlindFriendly':
- case 'deleteCookies':
- case 'deleteLocalStorage':
- case 'deleteUnusedSessionCookies':
- case 'iconBadgeEnabled':
- case 'processHyperlinkAuditing':
- changeUserSettings(target.id, target.checked);
- break;
- case 'noMixedContent':
- case 'noscriptTagsSpoofed':
- case 'processReferer':
- changeMatrixSwitch(
- target.getAttribute('data-matrix-switch'),
- target.checked
+ function prepareToDie() {
+ onChangeValueHandler(
+ uDom.nodeFromId('deleteUnusedSessionCookiesAfter'),
+ 'deleteUnusedSessionCookiesAfter',
+ 15, 1440
+ );
+ onChangeValueHandler(
+ uDom.nodeFromId('clearBrowserCacheAfter'),
+ 'clearBrowserCacheAfter',
+ 15, 1440
);
- break;
- case 'deleteUnusedSessionCookiesAfter':
- onChangeValueHandler(target, 'deleteUnusedSessionCookiesAfter', 15, 1440);
- break;
- case 'clearBrowserCacheAfter':
- onChangeValueHandler(target, 'clearBrowserCacheAfter', 15, 1440);
- break;
- case 'popupScopeLevel':
- changeUserSettings('popupScopeLevel', target.value);
- break;
- default:
- break;
}
- switch ( target.id ) {
- case 'collapseBlocked':
- synchronizeWidgets();
- break;
- default:
- break;
+ /******************************************************************************/
+
+ function onInputChanged(ev) {
+ var target = ev.target;
+
+ switch ( target.id ) {
+ case 'displayTextSize':
+ changeUserSettings('displayTextSize', target.value + 'px');
+ break;
+ case 'clearBrowserCache':
+ case 'cloudStorageEnabled':
+ case 'collapseBlacklisted':
+ case 'collapseBlocked':
+ case 'colorBlindFriendly':
+ case 'deleteCookies':
+ case 'deleteLocalStorage':
+ case 'deleteUnusedSessionCookies':
+ case 'iconBadgeEnabled':
+ case 'processHyperlinkAuditing':
+ changeUserSettings(target.id, target.checked);
+ break;
+ case 'noMixedContent':
+ case 'noscriptTagsSpoofed':
+ case 'processReferer':
+ changeMatrixSwitch(
+ target.getAttribute('data-matrix-switch'),
+ target.checked
+ );
+ break;
+ case 'deleteUnusedSessionCookiesAfter':
+ onChangeValueHandler(target, 'deleteUnusedSessionCookiesAfter', 15, 1440);
+ break;
+ case 'clearBrowserCacheAfter':
+ onChangeValueHandler(target, 'clearBrowserCacheAfter', 15, 1440);
+ break;
+ case 'popupScopeLevel':
+ changeUserSettings('popupScopeLevel', target.value);
+ break;
+ default:
+ break;
+ }
+
+ switch ( target.id ) {
+ case 'collapseBlocked':
+ synchronizeWidgets();
+ break;
+ default:
+ break;
+ }
}
-}
-/******************************************************************************/
+ /******************************************************************************/
-function synchronizeWidgets() {
- var e1, e2;
+ function synchronizeWidgets() {
+ var e1, e2;
- e1 = uDom.nodeFromId('collapseBlocked');
- e2 = uDom.nodeFromId('collapseBlacklisted');
- if ( e1.checked ) {
- e2.setAttribute('disabled', '');
- } else {
- e2.removeAttribute('disabled');
+ e1 = uDom.nodeFromId('collapseBlocked');
+ e2 = uDom.nodeFromId('collapseBlacklisted');
+ if ( e1.checked ) {
+ e2.setAttribute('disabled', '');
+ } else {
+ e2.removeAttribute('disabled');
+ }
}
-}
-/******************************************************************************/
+ /******************************************************************************/
-vAPI.messaging.send(
- 'settings.js',
- { what: 'getUserSettings' },
- function onSettingsReceived(settings) {
- // Cache copy
- cachedSettings = settings;
+ vAPI.messaging.send(
+ 'settings.js',
+ { what: 'getUserSettings' },
+ function onSettingsReceived(settings) {
+ // Cache copy
+ cachedSettings = settings;
- var userSettings = settings.userSettings;
- var matrixSwitches = settings.matrixSwitches;
+ var userSettings = settings.userSettings;
+ var matrixSwitches = settings.matrixSwitches;
- uDom('[data-setting-bool]').forEach(function(elem){
- elem.prop('checked', userSettings[elem.prop('id')] === true);
- });
+ uDom('[data-setting-bool]').forEach(function(elem){
+ elem.prop('checked', userSettings[elem.prop('id')] === true);
+ });
- uDom('[data-matrix-switch]').forEach(function(elem){
- var switchName = elem.attr('data-matrix-switch');
- if ( typeof switchName === 'string' && switchName !== '' ) {
- elem.prop('checked', matrixSwitches[switchName] === true);
- }
- });
+ uDom('[data-matrix-switch]').forEach(function(elem){
+ var switchName = elem.attr('data-matrix-switch');
+ if ( typeof switchName === 'string' && switchName !== '' ) {
+ elem.prop('checked', matrixSwitches[switchName] === true);
+ }
+ });
- uDom.nodeFromId('displayTextSize').value =
- parseInt(userSettings.displayTextSize, 10) || 14;
+ uDom.nodeFromId('displayTextSize').value =
+ parseInt(userSettings.displayTextSize, 10) || 14;
- uDom.nodeFromId('popupScopeLevel').value = userSettings.popupScopeLevel;
- uDom.nodeFromId('deleteUnusedSessionCookiesAfter').value =
- userSettings.deleteUnusedSessionCookiesAfter;
- uDom.nodeFromId('clearBrowserCacheAfter').value =
- userSettings.clearBrowserCacheAfter;
+ uDom.nodeFromId('popupScopeLevel').value = userSettings.popupScopeLevel;
+ uDom.nodeFromId('deleteUnusedSessionCookiesAfter').value =
+ userSettings.deleteUnusedSessionCookiesAfter;
+ uDom.nodeFromId('clearBrowserCacheAfter').value =
+ userSettings.clearBrowserCacheAfter;
- synchronizeWidgets();
+ synchronizeWidgets();
- document.addEventListener('change', onInputChanged);
+ document.addEventListener('change', onInputChanged);
- // https://github.com/gorhill/httpswitchboard/issues/197
- uDom(window).on('beforeunload', prepareToDie);
- }
-);
+ // https://github.com/gorhill/httpswitchboard/issues/197
+ uDom(window).on('beforeunload', prepareToDie);
+ }
+ );
-/******************************************************************************/
+ /******************************************************************************/
})();
diff --git a/js/start.js b/js/start.js
index 34ad071..31a89d8 100644
--- a/js/start.js
+++ b/js/start.js
@@ -31,78 +31,78 @@
(function() {
-/******************************************************************************/
-
-var ηm = ηMatrix;
-
-/******************************************************************************/
-
-var processCallbackQueue = function(queue, callback) {
- var processOne = function() {
- var fn = queue.pop();
- if ( fn ) {
- fn(processOne);
- } else if ( typeof callback === 'function' ) {
- callback();
- }
+ /******************************************************************************/
+
+ var ηm = ηMatrix;
+
+ /******************************************************************************/
+
+ var processCallbackQueue = function(queue, callback) {
+ var processOne = function() {
+ var fn = queue.pop();
+ if ( fn ) {
+ fn(processOne);
+ } else if ( typeof callback === 'function' ) {
+ callback();
+ }
+ };
+ processOne();
};
- processOne();
-};
-/******************************************************************************/
+ /******************************************************************************/
-var onAllDone = function() {
- ηm.webRequest.start();
+ var onAllDone = function() {
+ ηm.webRequest.start();
- ηm.assets.addObserver(ηm.assetObserver.bind(ηm));
- ηm.scheduleAssetUpdater(ηm.userSettings.autoUpdate ? 7 * 60 * 1000 : 0);
+ ηm.assets.addObserver(ηm.assetObserver.bind(ηm));
+ ηm.scheduleAssetUpdater(ηm.userSettings.autoUpdate ? 7 * 60 * 1000 : 0);
- vAPI.cloud.start([ 'myRulesPane' ]);
-};
+ vAPI.cloud.start([ 'myRulesPane' ]);
+ };
-/******************************************************************************/
+ /******************************************************************************/
-var onTabsReady = function(tabs) {
- var tab;
- var i = tabs.length;
- // console.debug('start.js > binding %d tabs', i);
- while ( i-- ) {
- tab = tabs[i];
- ηm.tabContextManager.push(tab.id, tab.url, 'newURL');
- }
+ var onTabsReady = function(tabs) {
+ var tab;
+ var i = tabs.length;
+ // console.debug('start.js > binding %d tabs', i);
+ while ( i-- ) {
+ tab = tabs[i];
+ ηm.tabContextManager.push(tab.id, tab.url, 'newURL');
+ }
- onAllDone();
-};
+ onAllDone();
+ };
-/******************************************************************************/
+ /******************************************************************************/
-var onUserSettingsLoaded = function() {
- ηm.loadHostsFiles();
-};
+ var onUserSettingsLoaded = function() {
+ ηm.loadHostsFiles();
+ };
-/******************************************************************************/
+ /******************************************************************************/
-var onPSLReady = function() {
- ηm.loadUserSettings(onUserSettingsLoaded);
- ηm.loadRawSettings();
- ηm.loadMatrix();
+ var onPSLReady = function() {
+ ηm.loadUserSettings(onUserSettingsLoaded);
+ ηm.loadRawSettings();
+ ηm.loadMatrix();
- // rhill 2013-11-24: bind behind-the-scene virtual tab/url manually, since the
- // normal way forbid binding behind the scene tab.
- // https://github.com/gorhill/httpswitchboard/issues/67
- ηm.pageStores[vAPI.noTabId] = ηm.pageStoreFactory(ηm.tabContextManager.mustLookup(vAPI.noTabId));
- ηm.pageStores[vAPI.noTabId].title = vAPI.i18n('statsPageDetailedBehindTheScenePage');
+ // rhill 2013-11-24: bind behind-the-scene virtual tab/url manually, since the
+ // normal way forbid binding behind the scene tab.
+ // https://github.com/gorhill/httpswitchboard/issues/67
+ ηm.pageStores[vAPI.noTabId] = ηm.pageStoreFactory(ηm.tabContextManager.mustLookup(vAPI.noTabId));
+ ηm.pageStores[vAPI.noTabId].title = vAPI.i18n('statsPageDetailedBehindTheScenePage');
- vAPI.tabs.getAll(onTabsReady);
-};
+ vAPI.tabs.getAll(onTabsReady);
+ };
-/******************************************************************************/
+ /******************************************************************************/
-processCallbackQueue(ηm.onBeforeStartQueue, function() {
- ηm.loadPublicSuffixList(onPSLReady);
-});
+ processCallbackQueue(ηm.onBeforeStartQueue, function() {
+ ηm.loadPublicSuffixList(onPSLReady);
+ });
-/******************************************************************************/
+ /******************************************************************************/
})();
diff --git a/js/storage.js b/js/storage.js
index 3302de0..a96caf3 100644
--- a/js/storage.js
+++ b/js/storage.js
@@ -27,13 +27,13 @@
/******************************************************************************/
-ηMatrix.getBytesInUse = function() {
+ηMatrix.getBytesInUse = function () {
var ηm = this;
- var getBytesInUseHandler = function(bytesInUse) {
+ var getBytesInUseHandler = function (bytesInUse) {
ηm.storageUsed = bytesInUse;
};
// Not all WebExtension implementations support getBytesInUse().
- if ( typeof vAPI.storage.getBytesInUse === 'function' ) {
+ if (typeof vAPI.storage.getBytesInUse === 'function') {
vAPI.storage.getBytesInUse(null, getBytesInUseHandler);
} else {
ηm.storageUsed = undefined;
@@ -42,21 +42,21 @@
/******************************************************************************/
-ηMatrix.saveUserSettings = function() {
+ηMatrix.saveUserSettings = function () {
this.XAL.keyvalSetMany(
this.userSettings,
this.getBytesInUse.bind(this)
);
};
-ηMatrix.loadUserSettings = function(callback) {
+ηMatrix.loadUserSettings = function (callback) {
var ηm = this;
- if ( typeof callback !== 'function' ) {
+ if (typeof callback !== 'function') {
callback = this.noopFunc;
}
- var settingsLoaded = function(store) {
+ var settingsLoaded = function (store) {
// console.log('storage.js > loaded user settings');
ηm.userSettings = store;
@@ -69,12 +69,14 @@
/******************************************************************************/
-ηMatrix.loadRawSettings = function() {
+ηMatrix.loadRawSettings = function () {
var ηm = this;
- var onLoaded = function(bin) {
- if ( !bin || bin.rawSettings instanceof Object === false ) { return; }
- for ( var key of Object.keys(bin.rawSettings) ) {
+ var onLoaded = function (bin) {
+ if (!bin || bin.rawSettings instanceof Object === false) {
+ return;
+ }
+ for (var key of Object.keys(bin.rawSettings)) {
if (
ηm.rawSettings.hasOwnProperty(key) === false ||
typeof bin.rawSettings[key] !== typeof ηm.rawSettings[key]
@@ -89,15 +91,15 @@
vAPI.storage.get('rawSettings', onLoaded);
};
-ηMatrix.saveRawSettings = function(rawSettings, callback) {
+ηMatrix.saveRawSettings = function (rawSettings, callback) {
var keys = Object.keys(rawSettings);
- if ( keys.length === 0 ) {
- if ( typeof callback === 'function' ) {
+ if (keys.length === 0) {
+ if (typeof callback === 'function') {
callback();
}
return;
}
- for ( var key of keys ) {
+ for (var key of keys) {
if (
this.rawSettingsDefault.hasOwnProperty(key) &&
typeof rawSettings[key] === typeof this.rawSettingsDefault[key]
@@ -105,57 +107,61 @@
this.rawSettings[key] = rawSettings[key];
}
}
- vAPI.storage.set({ rawSettings: this.rawSettings }, callback);
+ vAPI.storage.set({
+ rawSettings: this.rawSettings
+ }, callback);
this.rawSettingsWriteTime = Date.now();
};
-ηMatrix.rawSettingsFromString = function(raw) {
+ηMatrix.rawSettingsFromString = function (raw) {
var result = {},
lineIter = new this.LineIterator(raw),
line, matches, name, value;
- while ( lineIter.eot() === false ) {
+ while (lineIter.eot() === false) {
line = lineIter.next().trim();
matches = /^(\S+)(\s+(.+))?$/.exec(line);
- if ( matches === null ) { continue; }
+ if (matches === null) {
+ continue;
+ }
name = matches[1];
- if ( this.rawSettingsDefault.hasOwnProperty(name) === false ) {
+ if (this.rawSettingsDefault.hasOwnProperty(name) === false) {
continue;
}
value = (matches[2] || '').trim();
- switch ( typeof this.rawSettingsDefault[name] ) {
- case 'boolean':
- if ( value === 'true' ) {
- value = true;
- } else if ( value === 'false' ) {
- value = false;
- } else {
- value = this.rawSettingsDefault[name];
- }
- break;
- case 'string':
- if ( value === '' ) {
- value = this.rawSettingsDefault[name];
- }
- break;
- case 'number':
- value = parseInt(value, 10);
- if ( isNaN(value) ) {
- value = this.rawSettingsDefault[name];
- }
- break;
- default:
- break;
+ switch (typeof this.rawSettingsDefault[name]) {
+ case 'boolean':
+ if (value === 'true') {
+ value = true;
+ } else if (value === 'false') {
+ value = false;
+ } else {
+ value = this.rawSettingsDefault[name];
+ }
+ break;
+ case 'string':
+ if (value === '') {
+ value = this.rawSettingsDefault[name];
+ }
+ break;
+ case 'number':
+ value = parseInt(value, 10);
+ if (isNaN(value)) {
+ value = this.rawSettingsDefault[name];
+ }
+ break;
+ default:
+ break;
}
- if ( this.rawSettings[name] !== value ) {
+ if (this.rawSettings[name] !== value) {
result[name] = value;
}
}
this.saveRawSettings(result);
};
-ηMatrix.stringFromRawSettings = function() {
+ηMatrix.stringFromRawSettings = function () {
var out = [];
- for ( var key of Object.keys(this.rawSettings).sort() ) {
+ for (var key of Object.keys(this.rawSettings).sort()) {
out.push(key + ' ' + this.rawSettings[key]);
}
return out.join('\n');
@@ -164,19 +170,19 @@
/******************************************************************************/
// save white/blacklist
-ηMatrix.saveMatrix = function() {
+ηMatrix.saveMatrix = function () {
ηMatrix.XAL.keyvalSetOne('userMatrix', this.pMatrix.toString());
};
/******************************************************************************/
-ηMatrix.loadMatrix = function(callback) {
- if ( typeof callback !== 'function' ) {
+ηMatrix.loadMatrix = function (callback) {
+ if (typeof callback !== 'function') {
callback = this.noopFunc;
}
var ηm = this;
- var onLoaded = function(bin) {
- if ( bin.hasOwnProperty('userMatrix') ) {
+ var onLoaded = function (bin) {
+ if (bin.hasOwnProperty('userMatrix')) {
ηm.pMatrix.fromString(bin.userMatrix);
ηm.tMatrix.assign(ηm.pMatrix);
callback();
@@ -187,15 +193,17 @@
/******************************************************************************/
-ηMatrix.listKeysFromCustomHostsFiles = function(raw) {
+ηMatrix.listKeysFromCustomHostsFiles = function (raw) {
var out = new Set(),
reIgnore = /^[!#]/,
reValid = /^[a-z-]+:\/\/\S+/,
lineIter = new this.LineIterator(raw),
location;
- while ( lineIter.eot() === false ) {
+ while (lineIter.eot() === false) {
location = lineIter.next().trim();
- if ( reIgnore.test(location) || !reValid.test(location) ) { continue; }
+ if (reIgnore.test(location) || !reValid.test(location)) {
+ continue;
+ }
out.add(location);
}
return this.setToArray(out);
@@ -203,7 +211,7 @@
/******************************************************************************/
-ηMatrix.getAvailableHostsFiles = function(callback) {
+ηMatrix.getAvailableHostsFiles = function (callback) {
var ηm = this,
availableHostsFiles = {};
@@ -211,7 +219,7 @@
var importedListKeys = this.listKeysFromCustomHostsFiles(ηm.userSettings.externalHostsFiles),
i = importedListKeys.length,
listKey, entry;
- while ( i-- ) {
+ while (i--) {
listKey = importedListKeys[i];
entry = {
content: 'filters',
@@ -225,31 +233,37 @@
}
// selected lists
- var onSelectedHostsFilesLoaded = function(bin) {
+ var onSelectedHostsFilesLoaded = function (bin) {
// Now get user's selection of lists
- for ( var assetKey in bin.liveHostsFiles ) {
+ for (var assetKey in bin.liveHostsFiles) {
var availableEntry = availableHostsFiles[assetKey];
- if ( availableEntry === undefined ) { continue; }
+ if (availableEntry === undefined) {
+ continue;
+ }
var liveEntry = bin.liveHostsFiles[assetKey];
availableEntry.off = liveEntry.off || false;
- if ( liveEntry.entryCount !== undefined ) {
+ if (liveEntry.entryCount !== undefined) {
availableEntry.entryCount = liveEntry.entryCount;
}
- if ( liveEntry.entryUsedCount !== undefined ) {
+ if (liveEntry.entryUsedCount !== undefined) {
availableEntry.entryUsedCount = liveEntry.entryUsedCount;
}
// This may happen if the list name was pulled from the list content
- if ( availableEntry.title === '' && liveEntry.title !== undefined ) {
+ if (availableEntry.title === '' && liveEntry.title !== undefined) {
availableEntry.title = liveEntry.title;
}
}
// Remove unreferenced imported filter lists.
var dict = new Set(importedListKeys);
- for ( assetKey in availableHostsFiles ) {
+ for (assetKey in availableHostsFiles) {
var entry = availableHostsFiles[assetKey];
- if ( entry.submitter !== 'user' ) { continue; }
- if ( dict.has(assetKey) ) { continue; }
+ if (entry.submitter !== 'user') {
+ continue;
+ }
+ if (dict.has(assetKey)) {
+ continue;
+ }
delete availableHostsFiles[assetKey];
ηm.assets.unregisterAssetSource(assetKey);
ηm.assets.remove(assetKey);
@@ -259,18 +273,23 @@
};
// built-in lists
- var onBuiltinHostsFilesLoaded = function(entries) {
- for ( var assetKey in entries ) {
- if ( entries.hasOwnProperty(assetKey) === false ) { continue; }
+ var onBuiltinHostsFilesLoaded = function (entries) {
+ for (var assetKey in entries) {
+ if (entries.hasOwnProperty(assetKey) === false) {
+ continue;
+ }
entry = entries[assetKey];
- if ( entry.content !== 'filters' ) { continue; }
+ if (entry.content !== 'filters') {
+ continue;
+ }
availableHostsFiles[assetKey] = objectAssign({}, entry);
}
// Now get user's selection of lists
- vAPI.storage.get(
- { 'liveHostsFiles': availableHostsFiles },
- onSelectedHostsFilesLoaded
+ vAPI.storage.get({
+ 'liveHostsFiles': availableHostsFiles
+ },
+ onSelectedHostsFilesLoaded
);
};
@@ -279,31 +298,35 @@
/******************************************************************************/
-ηMatrix.loadHostsFiles = function(callback) {
+ηMatrix.loadHostsFiles = function (callback) {
var ηm = ηMatrix;
var hostsFileLoadCount;
- if ( typeof callback !== 'function' ) {
+ if (typeof callback !== 'function') {
callback = this.noopFunc;
}
- var loadHostsFilesEnd = function() {
+ var loadHostsFilesEnd = function () {
ηm.ubiquitousBlacklist.freeze();
- vAPI.storage.set({ 'liveHostsFiles': ηm.liveHostsFiles });
- vAPI.messaging.broadcast({ what: 'loadHostsFilesCompleted' });
+ vAPI.storage.set({
+ 'liveHostsFiles': ηm.liveHostsFiles
+ });
+ vAPI.messaging.broadcast({
+ what: 'loadHostsFilesCompleted'
+ });
ηm.getBytesInUse();
callback();
};
- var mergeHostsFile = function(details) {
+ var mergeHostsFile = function (details) {
ηm.mergeHostsFile(details);
hostsFileLoadCount -= 1;
- if ( hostsFileLoadCount === 0 ) {
+ if (hostsFileLoadCount === 0) {
loadHostsFilesEnd();
}
};
- var loadHostsFilesStart = function(hostsFiles) {
+ var loadHostsFilesStart = function (hostsFiles) {
ηm.liveHostsFiles = hostsFiles;
ηm.ubiquitousBlacklist.reset();
var locations = Object.keys(hostsFiles);
@@ -311,8 +334,8 @@
// Load all hosts file which are not disabled.
var location;
- while ( (location = locations.pop()) ) {
- if ( hostsFiles[location].off ) {
+ while ((location = locations.pop())) {
+ if (hostsFiles[location].off) {
hostsFileLoadCount -= 1;
continue;
}
@@ -320,7 +343,7 @@
}
// https://github.com/gorhill/uMatrix/issues/2
- if ( hostsFileLoadCount === 0 ) {
+ if (hostsFileLoadCount === 0) {
loadHostsFilesEnd();
return;
}
@@ -331,7 +354,7 @@
/******************************************************************************/
-ηMatrix.mergeHostsFile = function(details) {
+ηMatrix.mergeHostsFile = function (details) {
var usedCount = this.ubiquitousBlacklist.count;
var duplicateCount = this.ubiquitousBlacklist.duplicateCount;
@@ -347,20 +370,21 @@
/******************************************************************************/
-ηMatrix.mergeHostsFileContent = function(rawText) {
+ηMatrix.mergeHostsFileContent = function (rawText) {
var rawEnd = rawText.length;
var ubiquitousBlacklist = this.ubiquitousBlacklist;
var reLocalhost = /(^|\s)(localhost\.localdomain|localhost|local|broadcasthost|0\.0\.0\.0|127\.0\.0\.1|::1|fe80::1%lo0)(?=\s|$)/g;
var reAsciiSegment = /^[\x21-\x7e]+$/;
var matches;
- var lineBeg = 0, lineEnd;
+ var lineBeg = 0,
+ lineEnd;
var line;
- while ( lineBeg < rawEnd ) {
+ while (lineBeg < rawEnd) {
lineEnd = rawText.indexOf('\n', lineBeg);
- if ( lineEnd < 0 ) {
+ if (lineEnd < 0) {
lineEnd = rawText.indexOf('\r', lineBeg);
- if ( lineEnd < 0 ) {
+ if (lineEnd < 0) {
lineEnd = rawEnd;
}
}
@@ -382,19 +406,19 @@
// The filter is whatever sequence of printable ascii character without
// whitespaces
matches = reAsciiSegment.exec(line);
- if ( !matches || matches.length === 0 ) {
+ if (!matches || matches.length === 0) {
continue;
}
// Bypass anomalies
// For example, when a filter contains whitespace characters, or
// whatever else outside the range of printable ascii characters.
- if ( matches[0] !== line ) {
+ if (matches[0] !== line) {
continue;
}
line = matches[0];
- if ( line === '' ) {
+ if (line === '') {
continue;
}
@@ -406,28 +430,28 @@
// `switches` contains the filter lists for which the switch must be revisited.
-ηMatrix.selectHostsFiles = function(details, callback) {
+ηMatrix.selectHostsFiles = function (details, callback) {
var ηm = this,
externalHostsFiles = this.userSettings.externalHostsFiles,
i, n, assetKey;
// Hosts file to select
- if ( Array.isArray(details.toSelect) ) {
- for ( assetKey in this.liveHostsFiles ) {
- if ( this.liveHostsFiles.hasOwnProperty(assetKey) === false ) {
+ if (Array.isArray(details.toSelect)) {
+ for (assetKey in this.liveHostsFiles) {
+ if (this.liveHostsFiles.hasOwnProperty(assetKey) === false) {
continue;
}
- if ( details.toSelect.indexOf(assetKey) !== -1 ) {
+ if (details.toSelect.indexOf(assetKey) !== -1) {
this.liveHostsFiles[assetKey].off = false;
- } else if ( details.merge !== true ) {
+ } else if (details.merge !== true) {
this.liveHostsFiles[assetKey].off = true;
}
}
}
// Imported hosts files to remove
- if ( Array.isArray(details.toRemove) ) {
- var removeURLFromHaystack = function(haystack, needle) {
+ if (Array.isArray(details.toRemove)) {
+ var removeURLFromHaystack = function (haystack, needle) {
return haystack.replace(
new RegExp(
'(^|\\n)' +
@@ -436,7 +460,7 @@
'\n'
).trim();
};
- for ( i = 0, n = details.toRemove.length; i < n; i++ ) {
+ for (i = 0, n = details.toRemove.length; i < n; i++) {
assetKey = details.toRemove[i];
delete this.liveHostsFiles[assetKey];
externalHostsFiles = removeURLFromHaystack(externalHostsFiles, assetKey);
@@ -445,23 +469,30 @@
}
// Hosts file to import
- if ( typeof details.toImport === 'string' ) {
+ if (typeof details.toImport === 'string') {
// https://github.com/gorhill/uBlock/issues/1181
// Try mapping the URL of an imported filter list to the assetKey of an
// existing stock list.
- var assetKeyFromURL = function(url) {
+ var assetKeyFromURL = function (url) {
var needle = url.replace(/^https?:/, '');
- var assets = ηm.liveHostsFiles, asset;
- for ( var assetKey in assets ) {
+ var assets = ηm.liveHostsFiles,
+ asset;
+ for (var assetKey in assets) {
asset = assets[assetKey];
- if ( asset.content !== 'filters' ) { continue; }
- if ( typeof asset.contentURL === 'string' ) {
- if ( asset.contentURL.endsWith(needle) ) { return assetKey; }
+ if (asset.content !== 'filters') {
continue;
}
- if ( Array.isArray(asset.contentURL) === false ) { continue; }
- for ( i = 0, n = asset.contentURL.length; i < n; i++ ) {
- if ( asset.contentURL[i].endsWith(needle) ) {
+ if (typeof asset.contentURL === 'string') {
+ if (asset.contentURL.endsWith(needle)) {
+ return assetKey;
+ }
+ continue;
+ }
+ if (Array.isArray(asset.contentURL) === false) {
+ continue;
+ }
+ for (i = 0, n = asset.contentURL.length; i < n; i++) {
+ if (asset.contentURL[i].endsWith(needle)) {
return assetKey;
}
}
@@ -473,28 +504,36 @@
iter = toImportSet.values();
for (;;) {
var entry = iter.next();
- if ( entry.done ) { break; }
- if ( importedSet.has(entry.value) ) { continue; }
+ if (entry.done) {
+ break;
+ }
+ if (importedSet.has(entry.value)) {
+ continue;
+ }
assetKey = assetKeyFromURL(entry.value);
- if ( assetKey === entry.value ) {
+ if (assetKey === entry.value) {
importedSet.add(entry.value);
}
this.liveHostsFiles[assetKey] = {
content: 'filters',
- contentURL: [ assetKey ],
+ contentURL: [assetKey],
title: assetKey
};
}
externalHostsFiles = this.setToArray(importedSet).sort().join('\n');
}
- if ( externalHostsFiles !== this.userSettings.externalHostsFiles ) {
+ if (externalHostsFiles !== this.userSettings.externalHostsFiles) {
this.userSettings.externalHostsFiles = externalHostsFiles;
- vAPI.storage.set({ externalHostsFiles: externalHostsFiles });
+ vAPI.storage.set({
+ externalHostsFiles: externalHostsFiles
+ });
}
- vAPI.storage.set({ 'liveHostsFiles': this.liveHostsFiles });
+ vAPI.storage.set({
+ 'liveHostsFiles': this.liveHostsFiles
+ });
- if ( typeof callback === 'function' ) {
+ if (typeof callback === 'function') {
callback();
}
};
@@ -504,19 +543,19 @@
// `switches` contains the preset blacklists for which the switch must be
// revisited.
-ηMatrix.reloadHostsFiles = function() {
+ηMatrix.reloadHostsFiles = function () {
this.loadHostsFiles();
};
/******************************************************************************/
-ηMatrix.loadPublicSuffixList = function(callback) {
- if ( typeof callback !== 'function' ) {
+ηMatrix.loadPublicSuffixList = function (callback) {
+ if (typeof callback !== 'function') {
callback = this.noopFunc;
}
- var applyPublicSuffixList = function(details) {
- if ( !details.error ) {
+ var applyPublicSuffixList = function (details) {
+ if (!details.error) {
publicSuffixList.parse(details.content, punycode.toASCII);
}
callback();
@@ -527,37 +566,39 @@
/******************************************************************************/
-ηMatrix.scheduleAssetUpdater = (function() {
+ηMatrix.scheduleAssetUpdater = (function () {
var timer, next = 0;
- return function(updateDelay) {
- if ( timer ) {
+ return function (updateDelay) {
+ if (timer) {
clearTimeout(timer);
timer = undefined;
}
- if ( updateDelay === 0 ) {
+ if (updateDelay === 0) {
next = 0;
return;
}
var now = Date.now();
// Use the new schedule if and only if it is earlier than the previous
// one.
- if ( next !== 0 ) {
+ if (next !== 0) {
updateDelay = Math.min(updateDelay, Math.max(next - now, 0));
}
next = now + updateDelay;
- timer = vAPI.setTimeout(function() {
+ timer = vAPI.setTimeout(function () {
timer = undefined;
next = 0;
- ηMatrix.assets.updateStart({ delay: 120000 });
+ ηMatrix.assets.updateStart({
+ delay: 120000
+ });
}, updateDelay);
};
})();
/******************************************************************************/
-ηMatrix.assetObserver = function(topic, details) {
+ηMatrix.assetObserver = function (topic, details) {
// Do not update filter list if not in use.
- if ( topic === 'before-asset-updated' ) {
+ if (topic === 'before-asset-updated') {
if (
this.liveHostsFiles.hasOwnProperty(details.assetKey) === false ||
this.liveHostsFiles[details.assetKey].off === true
@@ -567,7 +608,7 @@
return true;
}
- if ( topic === 'after-asset-updated' ) {
+ if (topic === 'after-asset-updated') {
vAPI.messaging.broadcast({
what: 'assetUpdated',
key: details.assetKey,
@@ -577,7 +618,7 @@
}
// Update failed.
- if ( topic === 'asset-update-failed' ) {
+ if (topic === 'asset-update-failed') {
vAPI.messaging.broadcast({
what: 'assetUpdated',
key: details.assetKey,
@@ -587,11 +628,11 @@
}
// Reload all filter lists if needed.
- if ( topic === 'after-assets-updated' ) {
- if ( details.assetKeys.length !== 0 ) {
+ if (topic === 'after-assets-updated') {
+ if (details.assetKeys.length !== 0) {
this.loadHostsFiles();
}
- if ( this.userSettings.autoUpdate ) {
+ if (this.userSettings.autoUpdate) {
this.scheduleAssetUpdater(25200000);
} else {
this.scheduleAssetUpdater(0);
@@ -605,10 +646,10 @@
// New asset source became available, if it's a filter list, should we
// auto-select it?
- if ( topic === 'builtin-asset-source-added' ) {
- if ( details.entry.content === 'filters' ) {
- if ( details.entry.off !== true ) {
- this.saveSelectedFilterLists([ details.assetKey ], true);
+ if (topic === 'builtin-asset-source-added') {
+ if (details.entry.content === 'filters') {
+ if (details.entry.off !== true) {
+ this.saveSelectedFilterLists([details.assetKey], true);
}
}
return;
diff --git a/js/tab.js b/js/tab.js
index d6998b1..008ea30 100644
--- a/js/tab.js
+++ b/js/tab.js
@@ -26,71 +26,71 @@
(function() {
-'use strict';
+ 'use strict';
-/******************************************************************************/
+ /******************************************************************************/
-var ηm = ηMatrix;
+ var ηm = ηMatrix;
-// https://github.com/gorhill/httpswitchboard/issues/303
-// Some kind of trick going on here:
-// Any scheme other than 'http' and 'https' is remapped into a fake
-// URL which trick the rest of ηMatrix into being able to process an
-// otherwise unmanageable scheme. ηMatrix needs web page to have a proper
-// hostname to work properly, so just like the 'behind-the-scene'
-// fake domain name, we map unknown schemes into a fake '{scheme}-scheme'
-// hostname. This way, for a specific scheme you can create scope with
-// rules which will apply only to that scheme.
+ // https://github.com/gorhill/httpswitchboard/issues/303
+ // Some kind of trick going on here:
+ // Any scheme other than 'http' and 'https' is remapped into a fake
+ // URL which trick the rest of ηMatrix into being able to process an
+ // otherwise unmanageable scheme. ηMatrix needs web page to have a proper
+ // hostname to work properly, so just like the 'behind-the-scene'
+ // fake domain name, we map unknown schemes into a fake '{scheme}-scheme'
+ // hostname. This way, for a specific scheme you can create scope with
+ // rules which will apply only to that scheme.
-/******************************************************************************/
-/******************************************************************************/
+ /******************************************************************************/
+ /******************************************************************************/
-ηm.normalizePageURL = function(tabId, pageURL) {
- if ( vAPI.isBehindTheSceneTabId(tabId) ) {
- return 'http://' + this.behindTheSceneScope + '/';
- }
-
- // https://github.com/gorhill/uMatrix/issues/992
- if (pageURL.startsWith('wyciwyg:')) {
- // Matches strings like 'wyciwyg://101/'
- let filter = /^wyciwyg:\/\/\d+\//.exec(pageURL);
- if (filter) {
- pageURL = pageURL.slice(filter[0].length);
- }
- }
-
- // If the URL is that of our "blocked page" document, return the URL of
- // the blocked page.
- if ( pageURL.lastIndexOf(vAPI.getURL('main-blocked.html'), 0) === 0 ) {
- var matches = /main-blocked\.html\?details=([^&]+)/.exec(pageURL);
- if ( matches && matches.length === 2 ) {
- try {
- var details = JSON.parse(atob(matches[1]));
- pageURL = details.url;
- } catch (e) {
+ ηm.normalizePageURL = function(tabId, pageURL) {
+ if ( vAPI.isBehindTheSceneTabId(tabId) ) {
+ return 'http://' + this.behindTheSceneScope + '/';
+ }
+
+ // https://github.com/gorhill/uMatrix/issues/992
+ if (pageURL.startsWith('wyciwyg:')) {
+ // Matches strings like 'wyciwyg://101/'
+ let filter = /^wyciwyg:\/\/\d+\//.exec(pageURL);
+ if (filter) {
+ pageURL = pageURL.slice(filter[0].length);
+ }
+ }
+
+ // If the URL is that of our "blocked page" document, return the URL of
+ // the blocked page.
+ if ( pageURL.lastIndexOf(vAPI.getURL('main-blocked.html'), 0) === 0 ) {
+ var matches = /main-blocked\.html\?details=([^&]+)/.exec(pageURL);
+ if ( matches && matches.length === 2 ) {
+ try {
+ var details = JSON.parse(atob(matches[1]));
+ pageURL = details.url;
+ } catch (e) {
+ }
}
}
- }
- var uri = this.URI.set(pageURL);
- var scheme = uri.scheme;
- if ( scheme === 'https' || scheme === 'http' ) {
- return uri.normalizedURI();
- }
+ var uri = this.URI.set(pageURL);
+ var scheme = uri.scheme;
+ if ( scheme === 'https' || scheme === 'http' ) {
+ return uri.normalizedURI();
+ }
- var fakeHostname = scheme + '-scheme';
+ var fakeHostname = scheme + '-scheme';
- if ( uri.hostname !== '' ) {
- fakeHostname = uri.hostname + '.' + fakeHostname;
- } else if ( scheme === 'about' ) {
- fakeHostname = uri.path + '.' + fakeHostname;
- }
+ if ( uri.hostname !== '' ) {
+ fakeHostname = uri.hostname + '.' + fakeHostname;
+ } else if ( scheme === 'about' ) {
+ fakeHostname = uri.path + '.' + fakeHostname;
+ }
- return 'http://' + fakeHostname + '/';
-};
+ return 'http://' + fakeHostname + '/';
+ };
-/******************************************************************************/
-/******************************************************************************
+ /******************************************************************************/
+ /******************************************************************************
To keep track from which context *exactly* network requests are made. This is
often tricky for various reasons, and the challenge is not specific to one
@@ -142,579 +142,579 @@ master switch and dynamic filtering rules can be evaluated now properly even
in the absence of a PageStore object, this was not the case before.
Also, the TabContext object will try its best to find a good candidate root
-document URL for when none exists. This takes care of
+document URL for when none exists. This takes care of
<https://github.com/chrisaljoudi/uBlock/issues/1001>.
The TabContext manager is self-contained, and it takes care to properly
housekeep itself.
-*/
+ */
-ηm.tabContextManager = (function() {
- var tabContexts = Object.create(null);
+ ηm.tabContextManager = (function() {
+ var tabContexts = Object.create(null);
- // https://github.com/chrisaljoudi/uBlock/issues/1001
- // This is to be used as last-resort fallback in case a tab is found to not
- // be bound while network requests are fired for the tab.
- var mostRecentRootDocURL = '';
- var mostRecentRootDocURLTimestamp = 0;
+ // https://github.com/chrisaljoudi/uBlock/issues/1001
+ // This is to be used as last-resort fallback in case a tab is found to not
+ // be bound while network requests are fired for the tab.
+ var mostRecentRootDocURL = '';
+ var mostRecentRootDocURLTimestamp = 0;
+
+ var gcPeriod = 31 * 60 * 1000; // every 31 minutes
+
+ // A pushed entry is removed from the stack unless it is committed with
+ // a set time.
+ var StackEntry = function(url, commit) {
+ this.url = url;
+ this.committed = commit;
+ this.tstamp = Date.now();
+ };
- var gcPeriod = 31 * 60 * 1000; // every 31 minutes
+ var TabContext = function(tabId) {
+ this.tabId = tabId;
+ this.stack = [];
+ this.rawURL =
+ this.normalURL =
+ this.scheme =
+ this.rootHostname =
+ this.rootDomain = '';
+ this.secure = false;
+ this.commitTimer = null;
+ this.gcTimer = null;
- // A pushed entry is removed from the stack unless it is committed with
- // a set time.
- var StackEntry = function(url, commit) {
- this.url = url;
- this.committed = commit;
- this.tstamp = Date.now();
- };
+ tabContexts[tabId] = this;
+ };
- var TabContext = function(tabId) {
- this.tabId = tabId;
- this.stack = [];
- this.rawURL =
- this.normalURL =
- this.scheme =
- this.rootHostname =
- this.rootDomain = '';
- this.secure = false;
- this.commitTimer = null;
- this.gcTimer = null;
-
- tabContexts[tabId] = this;
- };
+ TabContext.prototype.destroy = function() {
+ if ( vAPI.isBehindTheSceneTabId(this.tabId) ) {
+ return;
+ }
+ if ( this.gcTimer !== null ) {
+ clearTimeout(this.gcTimer);
+ this.gcTimer = null;
+ }
+ delete tabContexts[this.tabId];
+ };
- TabContext.prototype.destroy = function() {
- if ( vAPI.isBehindTheSceneTabId(this.tabId) ) {
- return;
- }
- if ( this.gcTimer !== null ) {
- clearTimeout(this.gcTimer);
+ TabContext.prototype.onTab = function(tab) {
+ if ( tab ) {
+ this.gcTimer = vAPI.setTimeout(this.onGC.bind(this), gcPeriod);
+ } else {
+ this.destroy();
+ }
+ };
+
+ TabContext.prototype.onGC = function() {
this.gcTimer = null;
- }
- delete tabContexts[this.tabId];
- };
+ if ( vAPI.isBehindTheSceneTabId(this.tabId) ) {
+ return;
+ }
+ vAPI.tabs.get(this.tabId, this.onTab.bind(this));
+ };
+
+ // https://github.com/gorhill/uBlock/issues/248
+ // Stack entries have to be committed to stick. Non-committed stack
+ // entries are removed after a set delay.
+ TabContext.prototype.onCommit = function() {
+ if ( vAPI.isBehindTheSceneTabId(this.tabId) ) {
+ return;
+ }
+ this.commitTimer = null;
+ // Remove uncommitted entries at the top of the stack.
+ var i = this.stack.length;
+ while ( i-- ) {
+ if ( this.stack[i].committed ) {
+ break;
+ }
+ }
+ // https://github.com/gorhill/uBlock/issues/300
+ // If no committed entry was found, fall back on the bottom-most one
+ // as being the committed one by default.
+ if ( i === -1 && this.stack.length !== 0 ) {
+ this.stack[0].committed = true;
+ i = 0;
+ }
+ i += 1;
+ if ( i < this.stack.length ) {
+ this.stack.length = i;
+ this.update();
+ ηm.bindTabToPageStats(this.tabId, 'newURL');
+ }
+ };
- TabContext.prototype.onTab = function(tab) {
- if ( tab ) {
+ // This takes care of orphanized tab contexts. Can't be started for all
+ // contexts, as the behind-the-scene context is permanent -- so we do not
+ // want to flush it.
+ TabContext.prototype.autodestroy = function() {
+ if ( vAPI.isBehindTheSceneTabId(this.tabId) ) {
+ return;
+ }
this.gcTimer = vAPI.setTimeout(this.onGC.bind(this), gcPeriod);
- } else {
- this.destroy();
- }
- };
+ };
- TabContext.prototype.onGC = function() {
- this.gcTimer = null;
- if ( vAPI.isBehindTheSceneTabId(this.tabId) ) {
- return;
- }
- vAPI.tabs.get(this.tabId, this.onTab.bind(this));
- };
+ // Update just force all properties to be updated to match the most recent
+ // root URL.
+ TabContext.prototype.update = function() {
+ if ( this.stack.length === 0 ) {
+ this.rawURL = this.normalURL = this.scheme =
+ this.rootHostname = this.rootDomain = '';
+ this.secure = false;
+ return;
+ }
+ this.rawURL = this.stack[this.stack.length - 1].url;
+ this.normalURL = ηm.normalizePageURL(this.tabId, this.rawURL);
+ this.scheme = ηm.URI.schemeFromURI(this.rawURL);
+ this.rootHostname = ηm.URI.hostnameFromURI(this.normalURL);
+ this.rootDomain = ηm.URI.domainFromHostname(this.rootHostname) || this.rootHostname;
+ this.secure = ηm.URI.isSecureScheme(this.scheme);
+ };
- // https://github.com/gorhill/uBlock/issues/248
- // Stack entries have to be committed to stick. Non-committed stack
- // entries are removed after a set delay.
- TabContext.prototype.onCommit = function() {
- if ( vAPI.isBehindTheSceneTabId(this.tabId) ) {
- return;
- }
- this.commitTimer = null;
- // Remove uncommitted entries at the top of the stack.
- var i = this.stack.length;
- while ( i-- ) {
- if ( this.stack[i].committed ) {
- break;
+ // Called whenever a candidate root URL is spotted for the tab.
+ TabContext.prototype.push = function(url, context) {
+ if ( vAPI.isBehindTheSceneTabId(this.tabId) ) { return; }
+ var committed = context !== undefined;
+ var count = this.stack.length;
+ var topEntry = this.stack[count - 1];
+ if ( topEntry && topEntry.url === url ) {
+ if ( committed ) {
+ topEntry.committed = true;
+ }
+ return;
+ }
+ if ( this.commitTimer !== null ) {
+ clearTimeout(this.commitTimer);
+ }
+ if ( committed ) {
+ this.stack = [new StackEntry(url, true)];
+ } else {
+ this.stack.push(new StackEntry(url));
+ this.commitTimer = vAPI.setTimeout(this.onCommit.bind(this), 1000);
}
- }
- // https://github.com/gorhill/uBlock/issues/300
- // If no committed entry was found, fall back on the bottom-most one
- // as being the committed one by default.
- if ( i === -1 && this.stack.length !== 0 ) {
- this.stack[0].committed = true;
- i = 0;
- }
- i += 1;
- if ( i < this.stack.length ) {
- this.stack.length = i;
this.update();
- ηm.bindTabToPageStats(this.tabId, 'newURL');
- }
- };
+ ηm.bindTabToPageStats(this.tabId, context);
+ };
- // This takes care of orphanized tab contexts. Can't be started for all
- // contexts, as the behind-the-scene context is permanent -- so we do not
- // want to flush it.
- TabContext.prototype.autodestroy = function() {
- if ( vAPI.isBehindTheSceneTabId(this.tabId) ) {
- return;
- }
- this.gcTimer = vAPI.setTimeout(this.onGC.bind(this), gcPeriod);
- };
+ // These are to be used for the API of the tab context manager.
- // Update just force all properties to be updated to match the most recent
- // root URL.
- TabContext.prototype.update = function() {
- if ( this.stack.length === 0 ) {
- this.rawURL = this.normalURL = this.scheme =
- this.rootHostname = this.rootDomain = '';
- this.secure = false;
- return;
- }
- this.rawURL = this.stack[this.stack.length - 1].url;
- this.normalURL = ηm.normalizePageURL(this.tabId, this.rawURL);
- this.scheme = ηm.URI.schemeFromURI(this.rawURL);
- this.rootHostname = ηm.URI.hostnameFromURI(this.normalURL);
- this.rootDomain = ηm.URI.domainFromHostname(this.rootHostname) || this.rootHostname;
- this.secure = ηm.URI.isSecureScheme(this.scheme);
- };
+ var push = function(tabId, url, context) {
+ var entry = tabContexts[tabId];
+ if ( entry === undefined ) {
+ entry = new TabContext(tabId);
+ entry.autodestroy();
+ }
+ entry.push(url, context);
+ mostRecentRootDocURL = url;
+ mostRecentRootDocURLTimestamp = Date.now();
+ return entry;
+ };
- // Called whenever a candidate root URL is spotted for the tab.
- TabContext.prototype.push = function(url, context) {
- if ( vAPI.isBehindTheSceneTabId(this.tabId) ) { return; }
- var committed = context !== undefined;
- var count = this.stack.length;
- var topEntry = this.stack[count - 1];
- if ( topEntry && topEntry.url === url ) {
- if ( committed ) {
- topEntry.committed = true;
+ // Find a tab context for a specific tab. If none is found, attempt to
+ // fix this. When all fail, the behind-the-scene context is returned.
+ var mustLookup = function(tabId, url) {
+ var entry;
+ if ( url !== undefined ) {
+ entry = push(tabId, url);
+ } else {
+ entry = tabContexts[tabId];
}
- return;
- }
- if ( this.commitTimer !== null ) {
- clearTimeout(this.commitTimer);
- }
- if ( committed ) {
- this.stack = [new StackEntry(url, true)];
- } else {
- this.stack.push(new StackEntry(url));
- this.commitTimer = vAPI.setTimeout(this.onCommit.bind(this), 1000);
- }
- this.update();
- ηm.bindTabToPageStats(this.tabId, context);
- };
+ if ( entry !== undefined ) {
+ return entry;
+ }
+ // https://github.com/chrisaljoudi/uBlock/issues/1025
+ // Google Hangout popup opens without a root frame. So for now we will
+ // just discard that best-guess root frame if it is too far in the
+ // future, at which point it ceases to be a "best guess".
+ if ( mostRecentRootDocURL !== '' && mostRecentRootDocURLTimestamp + 500 < Date.now() ) {
+ mostRecentRootDocURL = '';
+ }
+ // https://github.com/chrisaljoudi/uBlock/issues/1001
+ // Not a behind-the-scene request, yet no page store found for the
+ // tab id: we will thus bind the last-seen root document to the
+ // unbound tab. It's a guess, but better than ending up filtering
+ // nothing at all.
+ if ( mostRecentRootDocURL !== '' ) {
+ return push(tabId, mostRecentRootDocURL);
+ }
+ // If all else fail at finding a page store, re-categorize the
+ // request as behind-the-scene. At least this ensures that ultimately
+ // the user can still inspect/filter those net requests which were
+ // about to fall through the cracks.
+ // Example: Chromium + case #12 at
+ // http://raymondhill.net/ublock/popup.html
+ return tabContexts[vAPI.noTabId];
+ };
- // These are to be used for the API of the tab context manager.
+ var lookup = function(tabId) {
+ return tabContexts[tabId] || null;
+ };
- var push = function(tabId, url, context) {
- var entry = tabContexts[tabId];
- if ( entry === undefined ) {
- entry = new TabContext(tabId);
- entry.autodestroy();
- }
- entry.push(url, context);
- mostRecentRootDocURL = url;
- mostRecentRootDocURLTimestamp = Date.now();
- return entry;
- };
+ // Behind-the-scene tab context
+ (function() {
+ var entry = new TabContext(vAPI.noTabId);
+ entry.stack.push(new StackEntry('', true));
+ entry.rawURL = '';
+ entry.normalURL = ηm.normalizePageURL(entry.tabId);
+ entry.rootHostname = ηm.URI.hostnameFromURI(entry.normalURL);
+ entry.rootDomain = ηm.URI.domainFromHostname(entry.rootHostname) || entry.rootHostname;
+ })();
+
+ // https://github.com/gorhill/uMatrix/issues/513
+ // Force a badge update here, it could happen that all the subsequent
+ // network requests are already in the page store, which would cause
+ // the badge to no be updated for these network requests.
+
+ vAPI.tabs.onNavigation = function(details) {
+ var tabId = details.tabId;
+ if ( vAPI.isBehindTheSceneTabId(tabId) ) { return; }
+ push(tabId, details.url, 'newURL');
+ ηm.updateBadgeAsync(tabId);
+ };
- // Find a tab context for a specific tab. If none is found, attempt to
- // fix this. When all fail, the behind-the-scene context is returned.
- var mustLookup = function(tabId, url) {
- var entry;
- if ( url !== undefined ) {
- entry = push(tabId, url);
- } else {
- entry = tabContexts[tabId];
- }
- if ( entry !== undefined ) {
- return entry;
- }
- // https://github.com/chrisaljoudi/uBlock/issues/1025
- // Google Hangout popup opens without a root frame. So for now we will
- // just discard that best-guess root frame if it is too far in the
- // future, at which point it ceases to be a "best guess".
- if ( mostRecentRootDocURL !== '' && mostRecentRootDocURLTimestamp + 500 < Date.now() ) {
- mostRecentRootDocURL = '';
- }
- // https://github.com/chrisaljoudi/uBlock/issues/1001
- // Not a behind-the-scene request, yet no page store found for the
- // tab id: we will thus bind the last-seen root document to the
- // unbound tab. It's a guess, but better than ending up filtering
- // nothing at all.
- if ( mostRecentRootDocURL !== '' ) {
- return push(tabId, mostRecentRootDocURL);
- }
- // If all else fail at finding a page store, re-categorize the
- // request as behind-the-scene. At least this ensures that ultimately
- // the user can still inspect/filter those net requests which were
- // about to fall through the cracks.
- // Example: Chromium + case #12 at
- // http://raymondhill.net/ublock/popup.html
- return tabContexts[vAPI.noTabId];
- };
+ // https://github.com/gorhill/uMatrix/issues/872
+ // `changeInfo.url` may not always be available (Firefox).
- var lookup = function(tabId) {
- return tabContexts[tabId] || null;
- };
+ vAPI.tabs.onUpdated = function(tabId, changeInfo, tab) {
+ if ( vAPI.isBehindTheSceneTabId(tabId) ) { return; }
+ if ( typeof tab.url !== 'string' || tab.url === '' ) { return; }
+ var url = changeInfo.url || tab.url;
+ if ( url ) {
+ push(tabId, url, 'updateURL');
+ }
+ };
- // Behind-the-scene tab context
- (function() {
- var entry = new TabContext(vAPI.noTabId);
- entry.stack.push(new StackEntry('', true));
- entry.rawURL = '';
- entry.normalURL = ηm.normalizePageURL(entry.tabId);
- entry.rootHostname = ηm.URI.hostnameFromURI(entry.normalURL);
- entry.rootDomain = ηm.URI.domainFromHostname(entry.rootHostname) || entry.rootHostname;
+ vAPI.tabs.onClosed = function(tabId) {
+ ηm.unbindTabFromPageStats(tabId);
+ var entry = tabContexts[tabId];
+ if ( entry instanceof TabContext ) {
+ entry.destroy();
+ }
+ };
+
+ return {
+ push: push,
+ lookup: lookup,
+ mustLookup: mustLookup
+ };
})();
- // https://github.com/gorhill/uMatrix/issues/513
- // Force a badge update here, it could happen that all the subsequent
- // network requests are already in the page store, which would cause
- // the badge to no be updated for these network requests.
+ vAPI.tabs.registerListeners();
- vAPI.tabs.onNavigation = function(details) {
- var tabId = details.tabId;
- if ( vAPI.isBehindTheSceneTabId(tabId) ) { return; }
- push(tabId, details.url, 'newURL');
- ηm.updateBadgeAsync(tabId);
- };
+ /******************************************************************************/
+ /******************************************************************************/
- // https://github.com/gorhill/uMatrix/issues/872
- // `changeInfo.url` may not always be available (Firefox).
+ // Create an entry for the tab if it doesn't exist
- vAPI.tabs.onUpdated = function(tabId, changeInfo, tab) {
- if ( vAPI.isBehindTheSceneTabId(tabId) ) { return; }
- if ( typeof tab.url !== 'string' || tab.url === '' ) { return; }
- var url = changeInfo.url || tab.url;
- if ( url ) {
- push(tabId, url, 'updateURL');
+ ηm.bindTabToPageStats = function(tabId, context) {
+ this.updateBadgeAsync(tabId);
+
+ // Do not create a page store for URLs which are of no interests
+ // Example: dev console
+ var tabContext = this.tabContextManager.lookup(tabId);
+ if ( tabContext === null ) {
+ throw new Error('Unmanaged tab id: ' + tabId);
}
- };
- vAPI.tabs.onClosed = function(tabId) {
- ηm.unbindTabFromPageStats(tabId);
- var entry = tabContexts[tabId];
- if ( entry instanceof TabContext ) {
- entry.destroy();
+ // rhill 2013-11-24: Never ever rebind behind-the-scene
+ // virtual tab.
+ // https://github.com/gorhill/httpswitchboard/issues/67
+ if ( vAPI.isBehindTheSceneTabId(tabId) ) {
+ return this.pageStores[tabId];
}
- };
- return {
- push: push,
- lookup: lookup,
- mustLookup: mustLookup
- };
-})();
+ var normalURL = tabContext.normalURL;
+ var pageStore = this.pageStores[tabId] || null;
-vAPI.tabs.registerListeners();
+ // The previous page URL, if any, associated with the tab
+ if ( pageStore !== null ) {
+ // No change, do not rebind
+ if ( pageStore.pageUrl === normalURL ) {
+ return pageStore;
+ }
-/******************************************************************************/
-/******************************************************************************/
+ // https://github.com/gorhill/uMatrix/issues/37
+ // Just rebind whenever possible: the URL changed, but the document
+ // maybe is the same.
+ // Example: Google Maps, Github
+ // https://github.com/gorhill/uMatrix/issues/72
+ // Need to double-check that the new scope is same as old scope
+ if ( context === 'updateURL' && pageStore.pageHostname === tabContext.rootHostname ) {
+ pageStore.rawURL = tabContext.rawURL;
+ pageStore.normalURL = normalURL;
+ this.updateTitle(tabId);
+ this.pageStoresToken = Date.now();
+ return pageStore;
+ }
-// Create an entry for the tab if it doesn't exist
-
-ηm.bindTabToPageStats = function(tabId, context) {
- this.updateBadgeAsync(tabId);
-
- // Do not create a page store for URLs which are of no interests
- // Example: dev console
- var tabContext = this.tabContextManager.lookup(tabId);
- if ( tabContext === null ) {
- throw new Error('Unmanaged tab id: ' + tabId);
- }
-
- // rhill 2013-11-24: Never ever rebind behind-the-scene
- // virtual tab.
- // https://github.com/gorhill/httpswitchboard/issues/67
- if ( vAPI.isBehindTheSceneTabId(tabId) ) {
- return this.pageStores[tabId];
- }
-
- var normalURL = tabContext.normalURL;
- var pageStore = this.pageStores[tabId] || null;
-
- // The previous page URL, if any, associated with the tab
- if ( pageStore !== null ) {
- // No change, do not rebind
- if ( pageStore.pageUrl === normalURL ) {
- return pageStore;
+ // We won't be reusing this page store.
+ this.unbindTabFromPageStats(tabId);
}
- // https://github.com/gorhill/uMatrix/issues/37
- // Just rebind whenever possible: the URL changed, but the document
- // maybe is the same.
- // Example: Google Maps, Github
- // https://github.com/gorhill/uMatrix/issues/72
- // Need to double-check that the new scope is same as old scope
- if ( context === 'updateURL' && pageStore.pageHostname === tabContext.rootHostname ) {
- pageStore.rawURL = tabContext.rawURL;
- pageStore.normalURL = normalURL;
- this.updateTitle(tabId);
- this.pageStoresToken = Date.now();
- return pageStore;
+ // Try to resurrect first.
+ pageStore = this.resurrectPageStore(tabId, normalURL);
+ if ( pageStore === null ) {
+ pageStore = this.pageStoreFactory(tabContext);
}
+ this.pageStores[tabId] = pageStore;
+ this.updateTitle(tabId);
+ this.pageStoresToken = Date.now();
- // We won't be reusing this page store.
- this.unbindTabFromPageStats(tabId);
- }
+ // console.debug('tab.js > bindTabToPageStats(): dispatching traffic in tab id %d to page store "%s"', tabId, pageUrl);
- // Try to resurrect first.
- pageStore = this.resurrectPageStore(tabId, normalURL);
- if ( pageStore === null ) {
- pageStore = this.pageStoreFactory(tabContext);
- }
- this.pageStores[tabId] = pageStore;
- this.updateTitle(tabId);
- this.pageStoresToken = Date.now();
+ return pageStore;
+ };
- // console.debug('tab.js > bindTabToPageStats(): dispatching traffic in tab id %d to page store "%s"', tabId, pageUrl);
+ /******************************************************************************/
- return pageStore;
-};
+ ηm.unbindTabFromPageStats = function(tabId) {
+ // Never unbind behind-the-scene page store.
+ if ( vAPI.isBehindTheSceneTabId(tabId) ) {
+ return;
+ }
-/******************************************************************************/
+ var pageStore = this.pageStores[tabId] || null;
+ if ( pageStore === null ) {
+ return;
+ }
-ηm.unbindTabFromPageStats = function(tabId) {
- // Never unbind behind-the-scene page store.
- if ( vAPI.isBehindTheSceneTabId(tabId) ) {
- return;
- }
+ delete this.pageStores[tabId];
+ this.pageStoresToken = Date.now();
- var pageStore = this.pageStores[tabId] || null;
- if ( pageStore === null ) {
- return;
- }
+ if ( pageStore.incinerationTimer ) {
+ clearTimeout(pageStore.incinerationTimer);
+ pageStore.incinerationTimer = null;
+ }
- delete this.pageStores[tabId];
- this.pageStoresToken = Date.now();
+ if ( this.pageStoreCemetery.hasOwnProperty(tabId) === false ) {
+ this.pageStoreCemetery[tabId] = {};
+ }
+ var pageStoreCrypt = this.pageStoreCemetery[tabId];
- if ( pageStore.incinerationTimer ) {
- clearTimeout(pageStore.incinerationTimer);
- pageStore.incinerationTimer = null;
- }
+ var pageURL = pageStore.pageUrl;
+ pageStoreCrypt[pageURL] = pageStore;
- if ( this.pageStoreCemetery.hasOwnProperty(tabId) === false ) {
- this.pageStoreCemetery[tabId] = {};
- }
- var pageStoreCrypt = this.pageStoreCemetery[tabId];
+ pageStore.incinerationTimer = vAPI.setTimeout(
+ this.incineratePageStore.bind(this, tabId, pageURL),
+ 4 * 60 * 1000
+ );
+ };
- var pageURL = pageStore.pageUrl;
- pageStoreCrypt[pageURL] = pageStore;
+ /******************************************************************************/
- pageStore.incinerationTimer = vAPI.setTimeout(
- this.incineratePageStore.bind(this, tabId, pageURL),
- 4 * 60 * 1000
- );
-};
+ ηm.resurrectPageStore = function(tabId, pageURL) {
+ if ( this.pageStoreCemetery.hasOwnProperty(tabId) === false ) {
+ return null;
+ }
+ var pageStoreCrypt = this.pageStoreCemetery[tabId];
-/******************************************************************************/
+ if ( pageStoreCrypt.hasOwnProperty(pageURL) === false ) {
+ return null;
+ }
-ηm.resurrectPageStore = function(tabId, pageURL) {
- if ( this.pageStoreCemetery.hasOwnProperty(tabId) === false ) {
- return null;
- }
- var pageStoreCrypt = this.pageStoreCemetery[tabId];
+ var pageStore = pageStoreCrypt[pageURL];
- if ( pageStoreCrypt.hasOwnProperty(pageURL) === false ) {
- return null;
- }
+ if ( pageStore.incinerationTimer !== null ) {
+ clearTimeout(pageStore.incinerationTimer);
+ pageStore.incinerationTimer = null;
+ }
- var pageStore = pageStoreCrypt[pageURL];
+ delete pageStoreCrypt[pageURL];
+ if ( Object.keys(pageStoreCrypt).length === 0 ) {
+ delete this.pageStoreCemetery[tabId];
+ }
- if ( pageStore.incinerationTimer !== null ) {
- clearTimeout(pageStore.incinerationTimer);
- pageStore.incinerationTimer = null;
- }
+ return pageStore;
+ };
- delete pageStoreCrypt[pageURL];
- if ( Object.keys(pageStoreCrypt).length === 0 ) {
- delete this.pageStoreCemetery[tabId];
- }
+ /******************************************************************************/
- return pageStore;
-};
+ ηm.incineratePageStore = function(tabId, pageURL) {
+ if ( this.pageStoreCemetery.hasOwnProperty(tabId) === false ) {
+ return;
+ }
+ var pageStoreCrypt = this.pageStoreCemetery[tabId];
-/******************************************************************************/
+ if ( pageStoreCrypt.hasOwnProperty(pageURL) === false ) {
+ return;
+ }
-ηm.incineratePageStore = function(tabId, pageURL) {
- if ( this.pageStoreCemetery.hasOwnProperty(tabId) === false ) {
- return;
- }
- var pageStoreCrypt = this.pageStoreCemetery[tabId];
+ var pageStore = pageStoreCrypt[pageURL];
+ if ( pageStore.incinerationTimer !== null ) {
+ clearTimeout(pageStore.incinerationTimer);
+ pageStore.incinerationTimer = null;
+ }
- if ( pageStoreCrypt.hasOwnProperty(pageURL) === false ) {
- return;
- }
+ delete pageStoreCrypt[pageURL];
+ if ( Object.keys(pageStoreCrypt).length === 0 ) {
+ delete this.pageStoreCemetery[tabId];
+ }
- var pageStore = pageStoreCrypt[pageURL];
- if ( pageStore.incinerationTimer !== null ) {
- clearTimeout(pageStore.incinerationTimer);
- pageStore.incinerationTimer = null;
- }
+ pageStore.dispose();
+ };
- delete pageStoreCrypt[pageURL];
- if ( Object.keys(pageStoreCrypt).length === 0 ) {
- delete this.pageStoreCemetery[tabId];
- }
+ /******************************************************************************/
- pageStore.dispose();
-};
+ ηm.pageStoreFromTabId = function(tabId) {
+ return this.pageStores[tabId] || null;
+ };
-/******************************************************************************/
+ // Never return null
+ ηm.mustPageStoreFromTabId = function(tabId) {
+ return this.pageStores[tabId] || this.pageStores[vAPI.noTabId];
+ };
-ηm.pageStoreFromTabId = function(tabId) {
- return this.pageStores[tabId] || null;
-};
+ /******************************************************************************/
-// Never return null
-ηm.mustPageStoreFromTabId = function(tabId) {
- return this.pageStores[tabId] || this.pageStores[vAPI.noTabId];
-};
+ ηm.forceReload = function(tabId, bypassCache) {
+ vAPI.tabs.reload(tabId, bypassCache);
+ };
-/******************************************************************************/
+ /******************************************************************************/
-ηm.forceReload = function(tabId, bypassCache) {
- vAPI.tabs.reload(tabId, bypassCache);
-};
+ // Update badge
-/******************************************************************************/
+ // rhill 2013-11-09: well this sucks, I can't update icon/badge
+ // incrementally, as chromium overwrite the icon at some point without
+ // notifying me, and this causes internal cached state to be out of sync.
-// Update badge
+ ηm.updateBadgeAsync = (function() {
+ var tabIdToTimer = Object.create(null);
-// rhill 2013-11-09: well this sucks, I can't update icon/badge
-// incrementally, as chromium overwrite the icon at some point without
-// notifying me, and this causes internal cached state to be out of sync.
+ var updateBadge = function(tabId) {
+ delete tabIdToTimer[tabId];
-ηm.updateBadgeAsync = (function() {
- var tabIdToTimer = Object.create(null);
+ var iconId = null;
+ var badgeStr = '';
- var updateBadge = function(tabId) {
- delete tabIdToTimer[tabId];
+ var pageStore = this.pageStoreFromTabId(tabId);
+ if ( pageStore !== null ) {
+ var total = pageStore.perLoadAllowedRequestCount +
+ pageStore.perLoadBlockedRequestCount;
+ if ( total ) {
+ var squareSize = 19;
+ var greenSize = squareSize * Math.sqrt(pageStore.perLoadAllowedRequestCount / total);
+ iconId = greenSize < squareSize/2 ? Math.ceil(greenSize) : Math.floor(greenSize);
+ }
+ if ( this.userSettings.iconBadgeEnabled && pageStore.perLoadBlockedRequestCount !== 0) {
+ badgeStr = this.formatCount(pageStore.perLoadBlockedRequestCount);
+ }
+ }
- var iconId = null;
- var badgeStr = '';
+ vAPI.setIcon(tabId, iconId, badgeStr);
+ };
- var pageStore = this.pageStoreFromTabId(tabId);
- if ( pageStore !== null ) {
- var total = pageStore.perLoadAllowedRequestCount +
- pageStore.perLoadBlockedRequestCount;
- if ( total ) {
- var squareSize = 19;
- var greenSize = squareSize * Math.sqrt(pageStore.perLoadAllowedRequestCount / total);
- iconId = greenSize < squareSize/2 ? Math.ceil(greenSize) : Math.floor(greenSize);
+ return function(tabId) {
+ if ( tabIdToTimer[tabId] ) {
+ return;
}
- if ( this.userSettings.iconBadgeEnabled && pageStore.perLoadBlockedRequestCount !== 0) {
- badgeStr = this.formatCount(pageStore.perLoadBlockedRequestCount);
+ if ( vAPI.isBehindTheSceneTabId(tabId) ) {
+ return;
}
- }
-
- vAPI.setIcon(tabId, iconId, badgeStr);
- };
-
- return function(tabId) {
- if ( tabIdToTimer[tabId] ) {
- return;
- }
- if ( vAPI.isBehindTheSceneTabId(tabId) ) {
- return;
- }
- tabIdToTimer[tabId] = vAPI.setTimeout(updateBadge.bind(this, tabId), 750);
- };
-})();
-
-/******************************************************************************/
+ tabIdToTimer[tabId] = vAPI.setTimeout(updateBadge.bind(this, tabId), 750);
+ };
+ })();
-ηm.updateTitle = (function() {
- var tabIdToTimer = Object.create(null);
- var tabIdToTryCount = Object.create(null);
- var delay = 499;
+ /******************************************************************************/
- var tryNoMore = function(tabId) {
- delete tabIdToTryCount[tabId];
- };
+ ηm.updateTitle = (function() {
+ var tabIdToTimer = Object.create(null);
+ var tabIdToTryCount = Object.create(null);
+ var delay = 499;
- var tryAgain = function(tabId) {
- var count = tabIdToTryCount[tabId];
- if ( count === undefined ) {
- return false;
- }
- if ( count === 1 ) {
+ var tryNoMore = function(tabId) {
delete tabIdToTryCount[tabId];
- return false;
- }
- tabIdToTryCount[tabId] = count - 1;
- tabIdToTimer[tabId] = vAPI.setTimeout(updateTitle.bind(ηm, tabId), delay);
- return true;
- };
+ };
- var onTabReady = function(tabId, tab) {
- if ( !tab ) {
- return tryNoMore(tabId);
- }
- var pageStore = this.pageStoreFromTabId(tabId);
- if ( pageStore === null ) {
- return tryNoMore(tabId);
- }
- if ( !tab.title && tryAgain(tabId) ) {
- return;
- }
- // https://github.com/gorhill/uMatrix/issues/225
- // Sometimes title changes while page is loading.
- var settled = tab.title && tab.title === pageStore.title;
- pageStore.title = tab.title || tab.url || '';
- this.pageStoresToken = Date.now();
- if ( settled || !tryAgain(tabId) ) {
- tryNoMore(tabId);
- }
- };
+ var tryAgain = function(tabId) {
+ var count = tabIdToTryCount[tabId];
+ if ( count === undefined ) {
+ return false;
+ }
+ if ( count === 1 ) {
+ delete tabIdToTryCount[tabId];
+ return false;
+ }
+ tabIdToTryCount[tabId] = count - 1;
+ tabIdToTimer[tabId] = vAPI.setTimeout(updateTitle.bind(ηm, tabId), delay);
+ return true;
+ };
- var updateTitle = function(tabId) {
- delete tabIdToTimer[tabId];
- vAPI.tabs.get(tabId, onTabReady.bind(this, tabId));
- };
+ var onTabReady = function(tabId, tab) {
+ if ( !tab ) {
+ return tryNoMore(tabId);
+ }
+ var pageStore = this.pageStoreFromTabId(tabId);
+ if ( pageStore === null ) {
+ return tryNoMore(tabId);
+ }
+ if ( !tab.title && tryAgain(tabId) ) {
+ return;
+ }
+ // https://github.com/gorhill/uMatrix/issues/225
+ // Sometimes title changes while page is loading.
+ var settled = tab.title && tab.title === pageStore.title;
+ pageStore.title = tab.title || tab.url || '';
+ this.pageStoresToken = Date.now();
+ if ( settled || !tryAgain(tabId) ) {
+ tryNoMore(tabId);
+ }
+ };
- return function(tabId) {
- if ( vAPI.isBehindTheSceneTabId(tabId) ) {
- return;
- }
- if ( tabIdToTimer[tabId] ) {
- clearTimeout(tabIdToTimer[tabId]);
- }
- tabIdToTimer[tabId] = vAPI.setTimeout(updateTitle.bind(this, tabId), delay);
- tabIdToTryCount[tabId] = 5;
- };
-})();
+ var updateTitle = function(tabId) {
+ delete tabIdToTimer[tabId];
+ vAPI.tabs.get(tabId, onTabReady.bind(this, tabId));
+ };
-/******************************************************************************/
+ return function(tabId) {
+ if ( vAPI.isBehindTheSceneTabId(tabId) ) {
+ return;
+ }
+ if ( tabIdToTimer[tabId] ) {
+ clearTimeout(tabIdToTimer[tabId]);
+ }
+ tabIdToTimer[tabId] = vAPI.setTimeout(updateTitle.bind(this, tabId), delay);
+ tabIdToTryCount[tabId] = 5;
+ };
+ })();
-// Stale page store entries janitor
-// https://github.com/chrisaljoudi/uBlock/issues/455
+ /******************************************************************************/
-(function() {
- var cleanupPeriod = 7 * 60 * 1000;
- var cleanupSampleAt = 0;
- var cleanupSampleSize = 11;
-
- var cleanup = function() {
- var vapiTabs = vAPI.tabs;
- var tabIds = Object.keys(ηm.pageStores).sort();
- var checkTab = function(tabId) {
- vapiTabs.get(tabId, function(tab) {
- if ( !tab ) {
- ηm.unbindTabFromPageStats(tabId);
+ // Stale page store entries janitor
+ // https://github.com/chrisaljoudi/uBlock/issues/455
+
+ (function() {
+ var cleanupPeriod = 7 * 60 * 1000;
+ var cleanupSampleAt = 0;
+ var cleanupSampleSize = 11;
+
+ var cleanup = function() {
+ var vapiTabs = vAPI.tabs;
+ var tabIds = Object.keys(ηm.pageStores).sort();
+ var checkTab = function(tabId) {
+ vapiTabs.get(tabId, function(tab) {
+ if ( !tab ) {
+ ηm.unbindTabFromPageStats(tabId);
+ }
+ });
+ };
+ if ( cleanupSampleAt >= tabIds.length ) {
+ cleanupSampleAt = 0;
+ }
+ var tabId;
+ var n = Math.min(cleanupSampleAt + cleanupSampleSize, tabIds.length);
+ for ( var i = cleanupSampleAt; i < n; i++ ) {
+ tabId = tabIds[i];
+ if ( vAPI.isBehindTheSceneTabId(tabId) ) {
+ continue;
}
- });
- };
- if ( cleanupSampleAt >= tabIds.length ) {
- cleanupSampleAt = 0;
- }
- var tabId;
- var n = Math.min(cleanupSampleAt + cleanupSampleSize, tabIds.length);
- for ( var i = cleanupSampleAt; i < n; i++ ) {
- tabId = tabIds[i];
- if ( vAPI.isBehindTheSceneTabId(tabId) ) {
- continue;
+ checkTab(tabId);
}
- checkTab(tabId);
- }
- cleanupSampleAt = n;
+ cleanupSampleAt = n;
- vAPI.setTimeout(cleanup, cleanupPeriod);
- };
+ vAPI.setTimeout(cleanup, cleanupPeriod);
+ };
- vAPI.setTimeout(cleanup, cleanupPeriod);
-})();
+ vAPI.setTimeout(cleanup, cleanupPeriod);
+ })();
-/******************************************************************************/
+ /******************************************************************************/
})();
diff --git a/js/traffic.js b/js/traffic.js
index 03846b1..6a6f700 100644
--- a/js/traffic.js
+++ b/js/traffic.js
@@ -29,417 +29,416 @@
ηMatrix.webRequest = (function() {
-/******************************************************************************/
+ /******************************************************************************/
+
+ // Intercept and filter web requests according to white and black lists.
+
+ var onBeforeRootFrameRequestHandler = function(details) {
+ var ηm = ηMatrix;
+ var requestURL = details.url;
+ var requestHostname = ηm.URI.hostnameFromURI(requestURL);
+ var tabId = details.tabId;
-// Intercept and filter web requests according to white and black lists.
+ ηm.tabContextManager.push(tabId, requestURL);
-var onBeforeRootFrameRequestHandler = function(details) {
- var ηm = ηMatrix;
- var requestURL = details.url;
- var requestHostname = ηm.URI.hostnameFromURI(requestURL);
- var tabId = details.tabId;
+ var tabContext = ηm.tabContextManager.mustLookup(tabId);
+ var rootHostname = tabContext.rootHostname;
- ηm.tabContextManager.push(tabId, requestURL);
+ // Disallow request as per matrix?
+ var block = ηm.mustBlock(rootHostname, requestHostname, 'doc');
- var tabContext = ηm.tabContextManager.mustLookup(tabId);
- var rootHostname = tabContext.rootHostname;
+ var pageStore = ηm.pageStoreFromTabId(tabId);
+ pageStore.recordRequest('doc', requestURL, block);
+ ηm.logger.writeOne(tabId, 'net', rootHostname, requestURL, 'doc', block);
- // Disallow request as per matrix?
- var block = ηm.mustBlock(rootHostname, requestHostname, 'doc');
+ // Not blocked
+ if ( !block ) {
+ // rhill 2013-11-07: Senseless to do this for behind-the-scene requests.
+ ηm.cookieHunter.recordPageCookies(pageStore);
+ return;
+ }
- var pageStore = ηm.pageStoreFromTabId(tabId);
- pageStore.recordRequest('doc', requestURL, block);
- ηm.logger.writeOne(tabId, 'net', rootHostname, requestURL, 'doc', block);
+ // Blocked
+ var query = btoa(JSON.stringify({
+ url: requestURL,
+ hn: requestHostname,
+ why: '?'
+ }));
- // Not blocked
- if ( !block ) {
- // rhill 2013-11-07: Senseless to do this for behind-the-scene requests.
- ηm.cookieHunter.recordPageCookies(pageStore);
- return;
- }
+ vAPI.tabs.replace(tabId, vAPI.getURL('main-blocked.html?details=') + query);
- // Blocked
- var query = btoa(JSON.stringify({
- url: requestURL,
- hn: requestHostname,
- why: '?'
- }));
+ return { cancel: true };
+ };
- vAPI.tabs.replace(tabId, vAPI.getURL('main-blocked.html?details=') + query);
+ /******************************************************************************/
- return { cancel: true };
-};
+ // Intercept and filter web requests according to white and black lists.
-/******************************************************************************/
+ var onBeforeRequestHandler = function(details) {
+ var ηm = ηMatrix,
+ ηmuri = ηm.URI,
+ requestURL = details.url,
+ requestScheme = ηmuri.schemeFromURI(requestURL);
-// Intercept and filter web requests according to white and black lists.
-
-var onBeforeRequestHandler = function(details) {
- var ηm = ηMatrix,
- ηmuri = ηm.URI,
- requestURL = details.url,
- requestScheme = ηmuri.schemeFromURI(requestURL);
-
- if ( ηmuri.isNetworkScheme(requestScheme) === false ) { return; }
-
- var requestType = requestTypeNormalizer[details.type] || 'other';
-
- // https://github.com/gorhill/httpswitchboard/issues/303
- // Wherever the main doc comes from, create a receiver page URL: synthetize
- // one if needed.
- if ( requestType === 'doc' && details.parentFrameId === -1 ) {
- return onBeforeRootFrameRequestHandler(details);
- }
-
- // Re-classify orphan HTTP requests as behind-the-scene requests. There is
- // not much else which can be done, because there are URLs
- // which cannot be handled by ηMatrix, i.e. `opera://startpage`,
- // as this would lead to complications with no obvious solution, like how
- // to scope on unknown scheme? Etc.
- // https://github.com/gorhill/httpswitchboard/issues/191
- // https://github.com/gorhill/httpswitchboard/issues/91#issuecomment-37180275
- var tabContext = ηm.tabContextManager.mustLookup(details.tabId),
- tabId = tabContext.tabId,
- rootHostname = tabContext.rootHostname,
- specificity = 0;
-
- // Filter through matrix
- var block = ηm.tMatrix.mustBlock(
- rootHostname,
- ηmuri.hostnameFromURI(requestURL),
- requestType
- );
- if ( block ) {
- specificity = ηm.tMatrix.specificityRegister;
- }
-
- // Record request.
- // https://github.com/gorhill/httpswitchboard/issues/342
- // The way requests are handled now, it may happen at this point some
- // processing has already been performed, and that a synthetic URL has
- // been constructed for logging purpose. Use this synthetic URL if
- // it is available.
- var pageStore = ηm.mustPageStoreFromTabId(tabId);
-
- // Enforce strict secure connection?
- if ( tabContext.secure && ηmuri.isSecureScheme(requestScheme) === false ) {
- pageStore.hasMixedContent = true;
- if ( block === false ) {
- block = ηm.tMatrix.evaluateSwitchZ('https-strict', rootHostname);
+ if ( ηmuri.isNetworkScheme(requestScheme) === false ) { return; }
+
+ var requestType = requestTypeNormalizer[details.type] || 'other';
+
+ // https://github.com/gorhill/httpswitchboard/issues/303
+ // Wherever the main doc comes from, create a receiver page URL: synthetize
+ // one if needed.
+ if ( requestType === 'doc' && details.parentFrameId === -1 ) {
+ return onBeforeRootFrameRequestHandler(details);
}
- }
- pageStore.recordRequest(requestType, requestURL, block);
- ηm.logger.writeOne(tabId, 'net', rootHostname, requestURL, details.type, block);
+ // Re-classify orphan HTTP requests as behind-the-scene requests. There is
+ // not much else which can be done, because there are URLs
+ // which cannot be handled by ηMatrix, i.e. `opera://startpage`,
+ // as this would lead to complications with no obvious solution, like how
+ // to scope on unknown scheme? Etc.
+ // https://github.com/gorhill/httpswitchboard/issues/191
+ // https://github.com/gorhill/httpswitchboard/issues/91#issuecomment-37180275
+ var tabContext = ηm.tabContextManager.mustLookup(details.tabId),
+ tabId = tabContext.tabId,
+ rootHostname = tabContext.rootHostname,
+ specificity = 0;
+
+ // Filter through matrix
+ var block = ηm.tMatrix.mustBlock(
+ rootHostname,
+ ηmuri.hostnameFromURI(requestURL),
+ requestType
+ );
+ if ( block ) {
+ specificity = ηm.tMatrix.specificityRegister;
+ }
- if ( block ) {
- pageStore.cacheBlockedCollapsible(requestType, requestURL, specificity);
- return { 'cancel': true };
- }
-};
+ // Record request.
+ // https://github.com/gorhill/httpswitchboard/issues/342
+ // The way requests are handled now, it may happen at this point some
+ // processing has already been performed, and that a synthetic URL has
+ // been constructed for logging purpose. Use this synthetic URL if
+ // it is available.
+ var pageStore = ηm.mustPageStoreFromTabId(tabId);
+
+ // Enforce strict secure connection?
+ if ( tabContext.secure && ηmuri.isSecureScheme(requestScheme) === false ) {
+ pageStore.hasMixedContent = true;
+ if ( block === false ) {
+ block = ηm.tMatrix.evaluateSwitchZ('https-strict', rootHostname);
+ }
+ }
-/******************************************************************************/
+ pageStore.recordRequest(requestType, requestURL, block);
+ ηm.logger.writeOne(tabId, 'net', rootHostname, requestURL, details.type, block);
-// Sanitize outgoing headers as per user settings.
-
-var onBeforeSendHeadersHandler = function(details) {
- var ηm = ηMatrix,
- ηmuri = ηm.URI,
- requestURL = details.url,
- requestScheme = ηmuri.schemeFromURI(requestURL);
-
- // Ignore non-network schemes
- if ( ηmuri.isNetworkScheme(requestScheme) === false ) { return; }
-
- // Re-classify orphan HTTP requests as behind-the-scene requests. There is
- // not much else which can be done, because there are URLs
- // which cannot be handled by HTTP Switchboard, i.e. `opera://startpage`,
- // as this would lead to complications with no obvious solution, like how
- // to scope on unknown scheme? Etc.
- // https://github.com/gorhill/httpswitchboard/issues/191
- // https://github.com/gorhill/httpswitchboard/issues/91#issuecomment-37180275
- var tabId = details.tabId,
- pageStore = ηm.mustPageStoreFromTabId(tabId),
- requestType = requestTypeNormalizer[details.type] || 'other',
- requestHeaders = details.requestHeaders,
- headerIndex, headerValue;
-
- // https://github.com/gorhill/httpswitchboard/issues/342
- // Is this hyperlink auditing?
- // If yes, create a synthetic URL for reporting hyperlink auditing
- // in request log. This way the user is better informed of what went
- // on.
-
- // https://html.spec.whatwg.org/multipage/links.html#hyperlink-auditing
- //
- // Target URL = the href of the link
- // Doc URL = URL of the document containing the target URL
- // Ping URLs = servers which will be told that user clicked target URL
- //
- // `Content-Type` = `text/ping` (always present)
- // `Ping-To` = target URL (always present)
- // `Ping-From` = doc URL
- // `Referer` = doc URL
- // request URL = URL which will receive the information
- //
- // With hyperlink-auditing, removing header(s) is pointless, the whole
- // request must be cancelled.
-
- headerIndex = headerIndexFromName('ping-to', requestHeaders);
- if ( headerIndex !== -1 ) {
- headerValue = requestHeaders[headerIndex].value;
- if ( headerValue !== '' ) {
- var block = ηm.userSettings.processHyperlinkAuditing;
- pageStore.recordRequest('other', requestURL + '{Ping-To:' + headerValue + '}', block);
- ηm.logger.writeOne(tabId, 'net', '', requestURL, 'ping', block);
- if ( block ) {
- ηm.hyperlinkAuditingFoiledCounter += 1;
- return { 'cancel': true };
+ if ( block ) {
+ pageStore.cacheBlockedCollapsible(requestType, requestURL, specificity);
+ return { 'cancel': true };
+ }
+ };
+
+ /******************************************************************************/
+
+ // Sanitize outgoing headers as per user settings.
+
+ var onBeforeSendHeadersHandler = function(details) {
+ var ηm = ηMatrix,
+ ηmuri = ηm.URI,
+ requestURL = details.url,
+ requestScheme = ηmuri.schemeFromURI(requestURL);
+
+ // Ignore non-network schemes
+ if ( ηmuri.isNetworkScheme(requestScheme) === false ) { return; }
+
+ // Re-classify orphan HTTP requests as behind-the-scene requests. There is
+ // not much else which can be done, because there are URLs
+ // which cannot be handled by HTTP Switchboard, i.e. `opera://startpage`,
+ // as this would lead to complications with no obvious solution, like how
+ // to scope on unknown scheme? Etc.
+ // https://github.com/gorhill/httpswitchboard/issues/191
+ // https://github.com/gorhill/httpswitchboard/issues/91#issuecomment-37180275
+ var tabId = details.tabId,
+ pageStore = ηm.mustPageStoreFromTabId(tabId),
+ requestType = requestTypeNormalizer[details.type] || 'other',
+ requestHeaders = details.requestHeaders,
+ headerIndex, headerValue;
+
+ // https://github.com/gorhill/httpswitchboard/issues/342
+ // Is this hyperlink auditing?
+ // If yes, create a synthetic URL for reporting hyperlink auditing
+ // in request log. This way the user is better informed of what went
+ // on.
+
+ // https://html.spec.whatwg.org/multipage/links.html#hyperlink-auditing
+ //
+ // Target URL = the href of the link
+ // Doc URL = URL of the document containing the target URL
+ // Ping URLs = servers which will be told that user clicked target URL
+ //
+ // `Content-Type` = `text/ping` (always present)
+ // `Ping-To` = target URL (always present)
+ // `Ping-From` = doc URL
+ // `Referer` = doc URL
+ // request URL = URL which will receive the information
+ //
+ // With hyperlink-auditing, removing header(s) is pointless, the whole
+ // request must be cancelled.
+
+ headerIndex = headerIndexFromName('ping-to', requestHeaders);
+ if ( headerIndex !== -1 ) {
+ headerValue = requestHeaders[headerIndex].value;
+ if ( headerValue !== '' ) {
+ var block = ηm.userSettings.processHyperlinkAuditing;
+ pageStore.recordRequest('other', requestURL + '{Ping-To:' + headerValue + '}', block);
+ ηm.logger.writeOne(tabId, 'net', '', requestURL, 'ping', block);
+ if ( block ) {
+ ηm.hyperlinkAuditingFoiledCounter += 1;
+ return { 'cancel': true };
+ }
}
}
- }
-
- // If we reach this point, request is not blocked, so what is left to do
- // is to sanitize headers.
-
- var rootHostname = pageStore.pageHostname,
- requestHostname = ηmuri.hostnameFromURI(requestURL),
- modified = false;
-
- // Process `Cookie` header.
-
- headerIndex = headerIndexFromName('cookie', requestHeaders);
- if (
- headerIndex !== -1 &&
- ηm.mustBlock(rootHostname, requestHostname, 'cookie')
- ) {
- modified = true;
- headerValue = requestHeaders[headerIndex].value;
- requestHeaders.splice(headerIndex, 1);
- ηm.cookieHeaderFoiledCounter++;
- if ( requestType === 'doc' ) {
- ηm.logger.writeOne(tabId, 'net', '', headerValue, 'COOKIE', true);
+
+ // If we reach this point, request is not blocked, so what is left to do
+ // is to sanitize headers.
+
+ var rootHostname = pageStore.pageHostname,
+ requestHostname = ηmuri.hostnameFromURI(requestURL),
+ modified = false;
+
+ // Process `Cookie` header.
+
+ headerIndex = headerIndexFromName('cookie', requestHeaders);
+ if (
+ headerIndex !== -1 &&
+ ηm.mustBlock(rootHostname, requestHostname, 'cookie')
+ ) {
+ modified = true;
+ headerValue = requestHeaders[headerIndex].value;
+ requestHeaders.splice(headerIndex, 1);
+ ηm.cookieHeaderFoiledCounter++;
+ if ( requestType === 'doc' ) {
+ ηm.logger.writeOne(tabId, 'net', '', headerValue, 'COOKIE', true);
+ }
}
- }
-
- // Process `Referer` header.
-
- // https://github.com/gorhill/httpswitchboard/issues/222#issuecomment-44828402
-
- // https://github.com/gorhill/uMatrix/issues/320
- // http://tools.ietf.org/html/rfc6454#section-7.3
- // "The user agent MAY include an Origin header field in any HTTP
- // "request.
- // "The user agent MUST NOT include more than one Origin header field in
- // "any HTTP request.
- // "Whenever a user agent issues an HTTP request from a "privacy-
- // "sensitive" context, the user agent MUST send the value "null" in the
- // "Origin header field."
-
- // https://github.com/gorhill/uMatrix/issues/358
- // Do not spoof `Origin` header for the time being.
-
- // https://github.com/gorhill/uMatrix/issues/773
- // For non-GET requests, remove `Referer` header instead of spoofing it.
-
- headerIndex = headerIndexFromName('referer', requestHeaders);
- if ( headerIndex !== -1 ) {
- headerValue = requestHeaders[headerIndex].value;
- if ( headerValue !== '' ) {
- var toDomain = ηmuri.domainFromHostname(requestHostname);
- if ( toDomain !== '' && toDomain !== ηmuri.domainFromURI(headerValue) ) {
- pageStore.has3pReferrer = true;
- if ( ηm.tMatrix.evaluateSwitchZ('referrer-spoof', rootHostname) ) {
- modified = true;
- var newValue;
- if ( details.method === 'GET' ) {
- newValue = requestHeaders[headerIndex].value =
- requestScheme + '://' + requestHostname + '/';
- } else {
- requestHeaders.splice(headerIndex, 1);
- }
- ηm.refererHeaderFoiledCounter++;
- if ( requestType === 'doc' ) {
- ηm.logger.writeOne(tabId, 'net', '', headerValue, 'REFERER', true);
- if ( newValue !== undefined ) {
- ηm.logger.writeOne(tabId, 'net', '', newValue, 'REFERER', false);
+
+ // Process `Referer` header.
+
+ // https://github.com/gorhill/httpswitchboard/issues/222#issuecomment-44828402
+
+ // https://github.com/gorhill/uMatrix/issues/320
+ // http://tools.ietf.org/html/rfc6454#section-7.3
+ // "The user agent MAY include an Origin header field in any HTTP
+ // "request.
+ // "The user agent MUST NOT include more than one Origin header field in
+ // "any HTTP request.
+ // "Whenever a user agent issues an HTTP request from a "privacy-
+ // "sensitive" context, the user agent MUST send the value "null" in the
+ // "Origin header field."
+
+ // https://github.com/gorhill/uMatrix/issues/358
+ // Do not spoof `Origin` header for the time being.
+
+ // https://github.com/gorhill/uMatrix/issues/773
+ // For non-GET requests, remove `Referer` header instead of spoofing it.
+
+ headerIndex = headerIndexFromName('referer', requestHeaders);
+ if ( headerIndex !== -1 ) {
+ headerValue = requestHeaders[headerIndex].value;
+ if ( headerValue !== '' ) {
+ var toDomain = ηmuri.domainFromHostname(requestHostname);
+ if ( toDomain !== '' && toDomain !== ηmuri.domainFromURI(headerValue) ) {
+ pageStore.has3pReferrer = true;
+ if ( ηm.tMatrix.evaluateSwitchZ('referrer-spoof', rootHostname) ) {
+ modified = true;
+ var newValue;
+ if ( details.method === 'GET' ) {
+ newValue = requestHeaders[headerIndex].value =
+ requestScheme + '://' + requestHostname + '/';
+ } else {
+ requestHeaders.splice(headerIndex, 1);
+ }
+ ηm.refererHeaderFoiledCounter++;
+ if ( requestType === 'doc' ) {
+ ηm.logger.writeOne(tabId, 'net', '', headerValue, 'REFERER', true);
+ if ( newValue !== undefined ) {
+ ηm.logger.writeOne(tabId, 'net', '', newValue, 'REFERER', false);
+ }
}
}
}
}
}
- }
- if ( modified ) {
- return { requestHeaders: requestHeaders };
- }
-};
+ if ( modified ) {
+ return { requestHeaders: requestHeaders };
+ }
+ };
-/******************************************************************************/
+ /******************************************************************************/
-// To prevent inline javascript from being executed.
+ // To prevent inline javascript from being executed.
-// Prevent inline scripting using `Content-Security-Policy`:
-// https://dvcs.w3.org/hg/content-security-policy/raw-file/tip/csp-specification.dev.html
+ // Prevent inline scripting using `Content-Security-Policy`:
+ // https://dvcs.w3.org/hg/content-security-policy/raw-file/tip/csp-specification.dev.html
-// This fixes:
-// https://github.com/gorhill/httpswitchboard/issues/35
+ // This fixes:
+ // https://github.com/gorhill/httpswitchboard/issues/35
-var onHeadersReceived = function(details) {
- // Ignore schemes other than 'http...'
- var ηm = ηMatrix,
- tabId = details.tabId,
- requestURL = details.url,
- requestType = requestTypeNormalizer[details.type] || 'other';
+ var onHeadersReceived = function(details) {
+ // Ignore schemes other than 'http...'
+ var ηm = ηMatrix,
+ tabId = details.tabId,
+ requestURL = details.url,
+ requestType = requestTypeNormalizer[details.type] || 'other';
- // https://github.com/gorhill/uMatrix/issues/145
- // Check if the main_frame is a download
- if ( requestType === 'doc' ) {
- ηm.tabContextManager.push(tabId, requestURL);
- }
-
- var tabContext = ηm.tabContextManager.lookup(tabId);
- if ( tabContext === null ) { return; }
-
- var csp = [],
- cspReport = [],
- rootHostname = tabContext.rootHostname,
- requestHostname = ηm.URI.hostnameFromURI(requestURL);
-
- // Inline script tags.
- if ( ηm.mustAllow(rootHostname, requestHostname, 'script' ) !== true ) {
- csp.push(ηm.cspNoInlineScript);
- }
-
- // Inline style tags.
- if ( ηm.mustAllow(rootHostname, requestHostname, 'css' ) !== true ) {
- csp.push(ηm.cspNoInlineStyle);
- }
-
- // https://bugzilla.mozilla.org/show_bug.cgi?id=1302667
- var cspNoWorker = ηm.cspNoWorker;
- if ( cspNoWorker === undefined ) {
- cspNoWorker = cspNoWorkerInit();
- }
-
- if ( ηm.tMatrix.evaluateSwitchZ('no-workers', rootHostname) ) {
- csp.push(cspNoWorker);
- } else if ( ηm.rawSettings.disableCSPReportInjection === false ) {
- cspReport.push(cspNoWorker);
- }
-
- var headers = details.responseHeaders,
- cspDirectives, i;
-
- if ( csp.length !== 0 ) {
- cspDirectives = csp.join(',');
- i = headerIndexFromName('content-security-policy', headers);
- if ( i !== -1 ) {
- headers[i].value += ',' + cspDirectives;
- } else {
- headers.push({
- name: 'Content-Security-Policy',
- value: cspDirectives
- });
- }
+ // https://github.com/gorhill/uMatrix/issues/145
+ // Check if the main_frame is a download
if ( requestType === 'doc' ) {
- ηm.logger.writeOne(tabId, 'net', '', cspDirectives, 'CSP', false);
- }
- }
-
- if ( cspReport.length !== 0 ) {
- cspDirectives = cspReport.join(',');
- i = headerIndexFromName('content-security-policy-report-only', headers);
- if ( i !== -1 ) {
- headers[i].value += ',' + cspDirectives;
- } else {
- headers.push({
- name: 'Content-Security-Policy-Report-Only',
- value: cspDirectives
- });
+ ηm.tabContextManager.push(tabId, requestURL);
}
- }
-
- return { responseHeaders: headers };
-};
-/******************************************************************************/
-
-var cspNoWorkerInit = function() {
- if (ηMatrix.cspNoWorker === undefined) {
- ηMatrix.cspNoWorker = "worker-src 'none'; "
- +"frame-src data: blob: *; "
- +"report-uri about:blank";
- }
-
- return ηMatrix.cspNoWorker;
-};
+ var tabContext = ηm.tabContextManager.lookup(tabId);
+ if ( tabContext === null ) { return; }
-/******************************************************************************/
+ var csp = [],
+ cspReport = [],
+ rootHostname = tabContext.rootHostname,
+ requestHostname = ηm.URI.hostnameFromURI(requestURL);
-// Caller must ensure headerName is normalized to lower case.
+ // Inline script tags.
+ if ( ηm.mustAllow(rootHostname, requestHostname, 'script' ) !== true ) {
+ csp.push(ηm.cspNoInlineScript);
+ }
-var headerIndexFromName = function(headerName, headers) {
- var i = headers.length;
- while ( i-- ) {
- if ( headers[i].name.toLowerCase() === headerName ) {
- return i;
+ // Inline style tags.
+ if ( ηm.mustAllow(rootHostname, requestHostname, 'css' ) !== true ) {
+ csp.push(ηm.cspNoInlineStyle);
}
- }
- return -1;
-};
-/******************************************************************************/
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=1302667
+ var cspNoWorker = ηm.cspNoWorker;
+ if ( cspNoWorker === undefined ) {
+ cspNoWorker = cspNoWorkerInit();
+ }
-var requestTypeNormalizer = {
- 'font' : 'css',
- 'image' : 'image',
- 'imageset' : 'image',
- 'main_frame' : 'doc',
- 'media' : 'media',
- 'object' : 'media',
- 'other' : 'other',
- 'script' : 'script',
- 'stylesheet' : 'css',
- 'sub_frame' : 'frame',
- 'websocket' : 'xhr',
- 'xmlhttprequest': 'xhr'
-};
+ if ( ηm.tMatrix.evaluateSwitchZ('no-workers', rootHostname) ) {
+ csp.push(cspNoWorker);
+ } else if ( ηm.rawSettings.disableCSPReportInjection === false ) {
+ cspReport.push(cspNoWorker);
+ }
-/******************************************************************************/
+ var headers = details.responseHeaders,
+ cspDirectives, i;
+
+ if ( csp.length !== 0 ) {
+ cspDirectives = csp.join(',');
+ i = headerIndexFromName('content-security-policy', headers);
+ if ( i !== -1 ) {
+ headers[i].value += ',' + cspDirectives;
+ } else {
+ headers.push({
+ name: 'Content-Security-Policy',
+ value: cspDirectives
+ });
+ }
+ if ( requestType === 'doc' ) {
+ ηm.logger.writeOne(tabId, 'net', '', cspDirectives, 'CSP', false);
+ }
+ }
-vAPI.net.onBeforeRequest = {
- extra: [ 'blocking' ],
- callback: onBeforeRequestHandler
-};
+ if ( cspReport.length !== 0 ) {
+ cspDirectives = cspReport.join(',');
+ i = headerIndexFromName('content-security-policy-report-only', headers);
+ if ( i !== -1 ) {
+ headers[i].value += ',' + cspDirectives;
+ } else {
+ headers.push({
+ name: 'Content-Security-Policy-Report-Only',
+ value: cspDirectives
+ });
+ }
+ }
-vAPI.net.onBeforeSendHeaders = {
- extra: [ 'blocking', 'requestHeaders' ],
- callback: onBeforeSendHeadersHandler
-};
+ return { responseHeaders: headers };
+ };
-vAPI.net.onHeadersReceived = {
- urls: [ 'http://*/*', 'https://*/*' ],
- types: [ 'main_frame', 'sub_frame' ],
- extra: [ 'blocking', 'responseHeaders' ],
- callback: onHeadersReceived
-};
+ /******************************************************************************/
-/******************************************************************************/
+ var cspNoWorkerInit = function() {
+ if (ηMatrix.cspNoWorker === undefined) {
+ ηMatrix.cspNoWorker = "worker-src 'none'; "
+ +"frame-src data: blob: *; "
+ +"report-uri about:blank";
+ }
-var start = function() {
- vAPI.net.registerListeners();
-};
+ return ηMatrix.cspNoWorker;
+ };
-/******************************************************************************/
+ /******************************************************************************/
-return {
- start: start
-};
+ // Caller must ensure headerName is normalized to lower case.
-/******************************************************************************/
+ var headerIndexFromName = function(headerName, headers) {
+ var i = headers.length;
+ while ( i-- ) {
+ if ( headers[i].name.toLowerCase() === headerName ) {
+ return i;
+ }
+ }
+ return -1;
+ };
+
+ /******************************************************************************/
+
+ var requestTypeNormalizer = {
+ 'font' : 'css',
+ 'image' : 'image',
+ 'imageset' : 'image',
+ 'main_frame' : 'doc',
+ 'media' : 'media',
+ 'object' : 'media',
+ 'other' : 'other',
+ 'script' : 'script',
+ 'stylesheet' : 'css',
+ 'sub_frame' : 'frame',
+ 'websocket' : 'xhr',
+ 'xmlhttprequest': 'xhr'
+ };
+
+ /******************************************************************************/
+
+ vAPI.net.onBeforeRequest = {
+ extra: [ 'blocking' ],
+ callback: onBeforeRequestHandler
+ };
+
+ vAPI.net.onBeforeSendHeaders = {
+ extra: [ 'blocking', 'requestHeaders' ],
+ callback: onBeforeSendHeadersHandler
+ };
+
+ vAPI.net.onHeadersReceived = {
+ urls: [ 'http://*/*', 'https://*/*' ],
+ types: [ 'main_frame', 'sub_frame' ],
+ extra: [ 'blocking', 'responseHeaders' ],
+ callback: onHeadersReceived
+ };
+
+ /******************************************************************************/
+
+ var start = function() {
+ vAPI.net.registerListeners();
+ };
+
+ /******************************************************************************/
+
+ return {
+ start: start
+ };
+
+ /******************************************************************************/
})();
/******************************************************************************/
-
diff --git a/js/udom.js b/js/udom.js
index 94ac5b9..f33a958 100644
--- a/js/udom.js
+++ b/js/udom.js
@@ -36,695 +36,695 @@
var uDom = (function() {
-/******************************************************************************/
+ /******************************************************************************/
-var DOMList = function() {
- this.nodes = [];
-};
+ var DOMList = function() {
+ this.nodes = [];
+ };
-/******************************************************************************/
+ /******************************************************************************/
-Object.defineProperty(
- DOMList.prototype,
- 'length',
- {
- get: function() {
- return this.nodes.length;
+ Object.defineProperty(
+ DOMList.prototype,
+ 'length',
+ {
+ get: function() {
+ return this.nodes.length;
+ }
}
- }
-);
+ );
-/******************************************************************************/
+ /******************************************************************************/
-var DOMListFactory = function(selector, context) {
- var r = new DOMList();
- if ( typeof selector === 'string' ) {
- selector = selector.trim();
- if ( selector !== '' ) {
- return addSelectorToList(r, selector, context);
- }
- }
- if ( selector instanceof Node ) {
- return addNodeToList(r, selector);
- }
- if ( selector instanceof NodeList ) {
- return addNodeListToList(r, selector);
- }
- if ( selector instanceof DOMList ) {
- return addListToList(r, selector);
- }
- return r;
-};
+ var DOMListFactory = function(selector, context) {
+ var r = new DOMList();
+ if ( typeof selector === 'string' ) {
+ selector = selector.trim();
+ if ( selector !== '' ) {
+ return addSelectorToList(r, selector, context);
+ }
+ }
+ if ( selector instanceof Node ) {
+ return addNodeToList(r, selector);
+ }
+ if ( selector instanceof NodeList ) {
+ return addNodeListToList(r, selector);
+ }
+ if ( selector instanceof DOMList ) {
+ return addListToList(r, selector);
+ }
+ return r;
+ };
-/******************************************************************************/
+ /******************************************************************************/
-DOMListFactory.onLoad = function(callback) {
- window.addEventListener('load', callback);
-};
+ DOMListFactory.onLoad = function(callback) {
+ window.addEventListener('load', callback);
+ };
-/******************************************************************************/
+ /******************************************************************************/
-DOMListFactory.nodeFromId = function(id) {
- return document.getElementById(id);
-};
+ DOMListFactory.nodeFromId = function(id) {
+ return document.getElementById(id);
+ };
-DOMListFactory.nodeFromSelector = function(selector) {
- return document.querySelector(selector);
-};
+ DOMListFactory.nodeFromSelector = function(selector) {
+ return document.querySelector(selector);
+ };
-/******************************************************************************/
+ /******************************************************************************/
-var addNodeToList = function(list, node) {
- if ( node ) {
- list.nodes.push(node);
- }
- return list;
-};
+ var addNodeToList = function(list, node) {
+ if ( node ) {
+ list.nodes.push(node);
+ }
+ return list;
+ };
-/******************************************************************************/
+ /******************************************************************************/
-var addNodeListToList = function(list, nodelist) {
- if ( nodelist ) {
- var n = nodelist.length;
- for ( var i = 0; i < n; i++ ) {
- list.nodes.push(nodelist[i]);
+ var addNodeListToList = function(list, nodelist) {
+ if ( nodelist ) {
+ var n = nodelist.length;
+ for ( var i = 0; i < n; i++ ) {
+ list.nodes.push(nodelist[i]);
+ }
}
- }
- return list;
-};
+ return list;
+ };
-/******************************************************************************/
+ /******************************************************************************/
-var addListToList = function(list, other) {
- list.nodes = list.nodes.concat(other.nodes);
- return list;
-};
+ var addListToList = function(list, other) {
+ list.nodes = list.nodes.concat(other.nodes);
+ return list;
+ };
-/******************************************************************************/
+ /******************************************************************************/
-var addSelectorToList = function(list, selector, context) {
- var p = context || document;
- var r = p.querySelectorAll(selector);
- var n = r.length;
- for ( var i = 0; i < n; i++ ) {
- list.nodes.push(r[i]);
- }
- return list;
-};
+ var addSelectorToList = function(list, selector, context) {
+ var p = context || document;
+ var r = p.querySelectorAll(selector);
+ var n = r.length;
+ for ( var i = 0; i < n; i++ ) {
+ list.nodes.push(r[i]);
+ }
+ return list;
+ };
-/******************************************************************************/
+ /******************************************************************************/
-var nodeInNodeList = function(node, nodeList) {
- var i = nodeList.length;
- while ( i-- ) {
- if ( nodeList[i] === node ) {
- return true;
+ var nodeInNodeList = function(node, nodeList) {
+ var i = nodeList.length;
+ while ( i-- ) {
+ if ( nodeList[i] === node ) {
+ return true;
+ }
}
- }
- return false;
-};
+ return false;
+ };
-/******************************************************************************/
+ /******************************************************************************/
-var doesMatchSelector = function(node, selector) {
- if ( !node ) {
- return false;
- }
- if ( node.nodeType !== 1 ) {
- return false;
- }
- if ( selector === undefined ) {
- return true;
- }
- var parentNode = node.parentNode;
- if ( !parentNode || !parentNode.setAttribute ) {
- return false;
- }
- var doesMatch = false;
- parentNode.setAttribute('uDom-32kXc6xEZA7o73AMB8vLbLct1RZOkeoO', '');
- var grandpaNode = parentNode.parentNode || document;
- var nl = grandpaNode.querySelectorAll('[uDom-32kXc6xEZA7o73AMB8vLbLct1RZOkeoO] > ' + selector);
- var i = nl.length;
- while ( doesMatch === false && i-- ) {
- doesMatch = nl[i] === node;
- }
- parentNode.removeAttribute('uDom-32kXc6xEZA7o73AMB8vLbLct1RZOkeoO');
- return doesMatch;
-};
+ var doesMatchSelector = function(node, selector) {
+ if ( !node ) {
+ return false;
+ }
+ if ( node.nodeType !== 1 ) {
+ return false;
+ }
+ if ( selector === undefined ) {
+ return true;
+ }
+ var parentNode = node.parentNode;
+ if ( !parentNode || !parentNode.setAttribute ) {
+ return false;
+ }
+ var doesMatch = false;
+ parentNode.setAttribute('uDom-32kXc6xEZA7o73AMB8vLbLct1RZOkeoO', '');
+ var grandpaNode = parentNode.parentNode || document;
+ var nl = grandpaNode.querySelectorAll('[uDom-32kXc6xEZA7o73AMB8vLbLct1RZOkeoO] > ' + selector);
+ var i = nl.length;
+ while ( doesMatch === false && i-- ) {
+ doesMatch = nl[i] === node;
+ }
+ parentNode.removeAttribute('uDom-32kXc6xEZA7o73AMB8vLbLct1RZOkeoO');
+ return doesMatch;
+ };
-/******************************************************************************/
+ /******************************************************************************/
-DOMList.prototype.nodeAt = function(i) {
- return this.nodes[i] || null;
-};
+ DOMList.prototype.nodeAt = function(i) {
+ return this.nodes[i] || null;
+ };
-DOMList.prototype.at = function(i) {
- return addNodeToList(new DOMList(), this.nodes[i]);
-};
+ DOMList.prototype.at = function(i) {
+ return addNodeToList(new DOMList(), this.nodes[i]);
+ };
-/******************************************************************************/
+ /******************************************************************************/
-DOMList.prototype.toArray = function() {
- return this.nodes.slice();
-};
+ DOMList.prototype.toArray = function() {
+ return this.nodes.slice();
+ };
-/******************************************************************************/
+ /******************************************************************************/
-DOMList.prototype.pop = function() {
- return addNodeToList(new DOMList(), this.nodes.pop());
-};
+ DOMList.prototype.pop = function() {
+ return addNodeToList(new DOMList(), this.nodes.pop());
+ };
-/******************************************************************************/
+ /******************************************************************************/
-DOMList.prototype.forEach = function(fn) {
- var n = this.nodes.length;
- for ( var i = 0; i < n; i++ ) {
- fn(this.at(i), i);
- }
- return this;
-};
+ DOMList.prototype.forEach = function(fn) {
+ var n = this.nodes.length;
+ for ( var i = 0; i < n; i++ ) {
+ fn(this.at(i), i);
+ }
+ return this;
+ };
-/******************************************************************************/
+ /******************************************************************************/
-DOMList.prototype.subset = function(i, l) {
- var r = new DOMList();
- var n = l !== undefined ? l : this.nodes.length;
- var j = Math.min(i + n, this.nodes.length);
- if ( i < j ) {
- r.nodes = this.nodes.slice(i, j);
- }
- return r;
-};
+ DOMList.prototype.subset = function(i, l) {
+ var r = new DOMList();
+ var n = l !== undefined ? l : this.nodes.length;
+ var j = Math.min(i + n, this.nodes.length);
+ if ( i < j ) {
+ r.nodes = this.nodes.slice(i, j);
+ }
+ return r;
+ };
-/******************************************************************************/
+ /******************************************************************************/
-DOMList.prototype.first = function() {
- return this.subset(0, 1);
-};
+ DOMList.prototype.first = function() {
+ return this.subset(0, 1);
+ };
-/******************************************************************************/
+ /******************************************************************************/
-DOMList.prototype.next = function(selector) {
- var r = new DOMList();
- var n = this.nodes.length;
- var node;
- for ( var i = 0; i < n; i++ ) {
- node = this.nodes[i];
- while ( node.nextSibling !== null ) {
- node = node.nextSibling;
- if ( node.nodeType !== 1 ) {
- continue;
+ DOMList.prototype.next = function(selector) {
+ var r = new DOMList();
+ var n = this.nodes.length;
+ var node;
+ for ( var i = 0; i < n; i++ ) {
+ node = this.nodes[i];
+ while ( node.nextSibling !== null ) {
+ node = node.nextSibling;
+ if ( node.nodeType !== 1 ) {
+ continue;
+ }
+ if ( doesMatchSelector(node, selector) === false ) {
+ continue;
+ }
+ addNodeToList(r, node);
+ break;
}
- if ( doesMatchSelector(node, selector) === false ) {
- continue;
+ }
+ return r;
+ };
+
+ /******************************************************************************/
+
+ DOMList.prototype.parent = function() {
+ var r = new DOMList();
+ if ( this.nodes.length ) {
+ addNodeToList(r, this.nodes[0].parentNode);
+ }
+ return r;
+ };
+
+ /******************************************************************************/
+
+ DOMList.prototype.filter = function(filter) {
+ var r = new DOMList();
+ var filterFunc;
+ if ( typeof filter === 'string' ) {
+ filterFunc = function() {
+ return doesMatchSelector(this, filter);
+ };
+ } else if ( typeof filter === 'function' ) {
+ filterFunc = filter;
+ } else {
+ filterFunc = function(){
+ return true;
+ };
+ }
+ var n = this.nodes.length;
+ var node;
+ for ( var i = 0; i < n; i++ ) {
+ node = this.nodes[i];
+ if ( filterFunc.apply(node) ) {
+ addNodeToList(r, node);
}
- addNodeToList(r, node);
- break;
}
- }
- return r;
-};
+ return r;
+ };
-/******************************************************************************/
+ /******************************************************************************/
-DOMList.prototype.parent = function() {
- var r = new DOMList();
- if ( this.nodes.length ) {
- addNodeToList(r, this.nodes[0].parentNode);
- }
- return r;
-};
+ // TODO: Avoid possible duplicates
-/******************************************************************************/
+ DOMList.prototype.ancestors = function(selector) {
+ var r = new DOMList();
+ var n = this.nodes.length;
+ var node;
+ for ( var i = 0; i < n; i++ ) {
+ node = this.nodes[i].parentNode;
+ while ( node ) {
+ if ( doesMatchSelector(node, selector) ) {
+ addNodeToList(r, node);
+ }
+ node = node.parentNode;
+ }
+ }
+ return r;
+ };
-DOMList.prototype.filter = function(filter) {
- var r = new DOMList();
- var filterFunc;
- if ( typeof filter === 'string' ) {
- filterFunc = function() {
- return doesMatchSelector(this, filter);
- };
- } else if ( typeof filter === 'function' ) {
- filterFunc = filter;
- } else {
- filterFunc = function(){
- return true;
- };
- }
- var n = this.nodes.length;
- var node;
- for ( var i = 0; i < n; i++ ) {
- node = this.nodes[i];
- if ( filterFunc.apply(node) ) {
- addNodeToList(r, node);
- }
- }
- return r;
-};
+ /******************************************************************************/
-/******************************************************************************/
+ DOMList.prototype.descendants = function(selector) {
+ var r = new DOMList();
+ var n = this.nodes.length;
+ var nl;
+ for ( var i = 0; i < n; i++ ) {
+ nl = this.nodes[i].querySelectorAll(selector);
+ addNodeListToList(r, nl);
+ }
+ return r;
+ };
-// TODO: Avoid possible duplicates
+ /******************************************************************************/
-DOMList.prototype.ancestors = function(selector) {
- var r = new DOMList();
- var n = this.nodes.length;
- var node;
- for ( var i = 0; i < n; i++ ) {
- node = this.nodes[i].parentNode;
- while ( node ) {
- if ( doesMatchSelector(node, selector) ) {
- addNodeToList(r, node);
+ DOMList.prototype.contents = function() {
+ var r = new DOMList();
+ var cnodes, cn, ci;
+ var n = this.nodes.length;
+ for ( var i = 0; i < n; i++ ) {
+ cnodes = this.nodes[i].childNodes;
+ cn = cnodes.length;
+ for ( ci = 0; ci < cn; ci++ ) {
+ addNodeToList(r, cnodes.item(ci));
}
- node = node.parentNode;
}
- }
- return r;
-};
+ return r;
+ };
-/******************************************************************************/
+ /******************************************************************************/
+
+ DOMList.prototype.remove = function() {
+ var cn, p;
+ var i = this.nodes.length;
+ while ( i-- ) {
+ cn = this.nodes[i];
+ if ( (p = cn.parentNode) ) {
+ p.removeChild(cn);
+ }
+ }
+ return this;
+ };
-DOMList.prototype.descendants = function(selector) {
- var r = new DOMList();
- var n = this.nodes.length;
- var nl;
- for ( var i = 0; i < n; i++ ) {
- nl = this.nodes[i].querySelectorAll(selector);
- addNodeListToList(r, nl);
- }
- return r;
-};
+ DOMList.prototype.detach = DOMList.prototype.remove;
-/******************************************************************************/
+ /******************************************************************************/
-DOMList.prototype.contents = function() {
- var r = new DOMList();
- var cnodes, cn, ci;
- var n = this.nodes.length;
- for ( var i = 0; i < n; i++ ) {
- cnodes = this.nodes[i].childNodes;
- cn = cnodes.length;
- for ( ci = 0; ci < cn; ci++ ) {
- addNodeToList(r, cnodes.item(ci));
+ DOMList.prototype.empty = function() {
+ var node;
+ var i = this.nodes.length;
+ while ( i-- ) {
+ node = this.nodes[i];
+ while ( node.firstChild ) {
+ node.removeChild(node.firstChild);
+ }
}
- }
- return r;
-};
+ return this;
+ };
-/******************************************************************************/
+ /******************************************************************************/
-DOMList.prototype.remove = function() {
- var cn, p;
- var i = this.nodes.length;
- while ( i-- ) {
- cn = this.nodes[i];
- if ( (p = cn.parentNode) ) {
- p.removeChild(cn);
+ DOMList.prototype.append = function(selector, context) {
+ var p = this.nodes[0];
+ if ( p ) {
+ var c = DOMListFactory(selector, context);
+ var n = c.nodes.length;
+ for ( var i = 0; i < n; i++ ) {
+ p.appendChild(c.nodes[i]);
+ }
}
- }
- return this;
-};
+ return this;
+ };
-DOMList.prototype.detach = DOMList.prototype.remove;
+ /******************************************************************************/
-/******************************************************************************/
+ DOMList.prototype.prepend = function(selector, context) {
+ var p = this.nodes[0];
+ if ( p ) {
+ var c = DOMListFactory(selector, context);
+ var i = c.nodes.length;
+ while ( i-- ) {
+ p.insertBefore(c.nodes[i], p.firstChild);
+ }
+ }
+ return this;
+ };
+
+ /******************************************************************************/
-DOMList.prototype.empty = function() {
- var node;
- var i = this.nodes.length;
- while ( i-- ) {
- node = this.nodes[i];
- while ( node.firstChild ) {
- node.removeChild(node.firstChild);
+ DOMList.prototype.appendTo = function(selector, context) {
+ var p = selector instanceof DOMListFactory ? selector : DOMListFactory(selector, context);
+ var n = p.length;
+ for ( var i = 0; i < n; i++ ) {
+ p.nodes[0].appendChild(this.nodes[i]);
}
- }
- return this;
-};
+ return this;
+ };
-/******************************************************************************/
+ /******************************************************************************/
-DOMList.prototype.append = function(selector, context) {
- var p = this.nodes[0];
- if ( p ) {
+ DOMList.prototype.insertAfter = function(selector, context) {
+ if ( this.nodes.length === 0 ) {
+ return this;
+ }
+ var p = this.nodes[0].parentNode;
+ if ( !p ) {
+ return this;
+ }
var c = DOMListFactory(selector, context);
var n = c.nodes.length;
for ( var i = 0; i < n; i++ ) {
p.appendChild(c.nodes[i]);
}
- }
- return this;
-};
+ return this;
+ };
-/******************************************************************************/
+ /******************************************************************************/
-DOMList.prototype.prepend = function(selector, context) {
- var p = this.nodes[0];
- if ( p ) {
- var c = DOMListFactory(selector, context);
- var i = c.nodes.length;
- while ( i-- ) {
- p.insertBefore(c.nodes[i], p.firstChild);
+ DOMList.prototype.insertBefore = function(selector, context) {
+ if ( this.nodes.length === 0 ) {
+ return this;
+ }
+ var referenceNodes = DOMListFactory(selector, context);
+ if ( referenceNodes.nodes.length === 0 ) {
+ return this;
+ }
+ var referenceNode = referenceNodes.nodes[0];
+ var parentNode = referenceNode.parentNode;
+ if ( !parentNode ) {
+ return this;
}
- }
- return this;
-};
+ var n = this.nodes.length;
+ for ( var i = 0; i < n; i++ ) {
+ parentNode.insertBefore(this.nodes[i], referenceNode);
+ }
+ return this;
+ };
-/******************************************************************************/
+ /******************************************************************************/
-DOMList.prototype.appendTo = function(selector, context) {
- var p = selector instanceof DOMListFactory ? selector : DOMListFactory(selector, context);
- var n = p.length;
- for ( var i = 0; i < n; i++ ) {
- p.nodes[0].appendChild(this.nodes[i]);
- }
- return this;
-};
+ DOMList.prototype.clone = function(notDeep) {
+ var r = new DOMList();
+ var n = this.nodes.length;
+ for ( var i = 0; i < n; i++ ) {
+ addNodeToList(r, this.nodes[i].cloneNode(!notDeep));
+ }
+ return r;
+ };
-/******************************************************************************/
+ /******************************************************************************/
-DOMList.prototype.insertAfter = function(selector, context) {
- if ( this.nodes.length === 0 ) {
- return this;
- }
- var p = this.nodes[0].parentNode;
- if ( !p ) {
- return this;
- }
- var c = DOMListFactory(selector, context);
- var n = c.nodes.length;
- for ( var i = 0; i < n; i++ ) {
- p.appendChild(c.nodes[i]);
- }
- return this;
-};
+ DOMList.prototype.nthOfType = function() {
+ if ( this.nodes.length === 0 ) {
+ return 0;
+ }
+ var node = this.nodes[0];
+ var tagName = node.tagName;
+ var i = 1;
+ while ( node.previousElementSibling !== null ) {
+ node = node.previousElementSibling;
+ if ( typeof node.tagName !== 'string' ) {
+ continue;
+ }
+ if ( node.tagName !== tagName ) {
+ continue;
+ }
+ i++;
+ }
+ return i;
+ };
-/******************************************************************************/
+ /******************************************************************************/
-DOMList.prototype.insertBefore = function(selector, context) {
- if ( this.nodes.length === 0 ) {
- return this;
- }
- var referenceNodes = DOMListFactory(selector, context);
- if ( referenceNodes.nodes.length === 0 ) {
- return this;
- }
- var referenceNode = referenceNodes.nodes[0];
- var parentNode = referenceNode.parentNode;
- if ( !parentNode ) {
+ DOMList.prototype.attr = function(attr, value) {
+ var i = this.nodes.length;
+ if ( value === undefined && typeof attr !== 'object' ) {
+ return i ? this.nodes[0].getAttribute(attr) : undefined;
+ }
+ if ( typeof attr === 'object' ) {
+ var attrNames = Object.keys(attr);
+ var node, j, attrName;
+ while ( i-- ) {
+ node = this.nodes[i];
+ j = attrNames.length;
+ while ( j-- ) {
+ attrName = attrNames[j];
+ node.setAttribute(attrName, attr[attrName]);
+ }
+ }
+ } else {
+ while ( i-- ) {
+ this.nodes[i].setAttribute(attr, value);
+ }
+ }
return this;
- }
- var n = this.nodes.length;
- for ( var i = 0; i < n; i++ ) {
- parentNode.insertBefore(this.nodes[i], referenceNode);
- }
- return this;
-};
-
-/******************************************************************************/
-
-DOMList.prototype.clone = function(notDeep) {
- var r = new DOMList();
- var n = this.nodes.length;
- for ( var i = 0; i < n; i++ ) {
- addNodeToList(r, this.nodes[i].cloneNode(!notDeep));
- }
- return r;
-};
+ };
-/******************************************************************************/
+ /******************************************************************************/
-DOMList.prototype.nthOfType = function() {
- if ( this.nodes.length === 0 ) {
- return 0;
- }
- var node = this.nodes[0];
- var tagName = node.tagName;
- var i = 1;
- while ( node.previousElementSibling !== null ) {
- node = node.previousElementSibling;
- if ( typeof node.tagName !== 'string' ) {
- continue;
- }
- if ( node.tagName !== tagName ) {
- continue;
- }
- i++;
- }
- return i;
-};
+ DOMList.prototype.prop = function(prop, value) {
+ var i = this.nodes.length;
+ if ( value === undefined ) {
+ return i !== 0 ? this.nodes[0][prop] : undefined;
+ }
+ while ( i-- ) {
+ this.nodes[i][prop] = value;
+ }
+ return this;
+ };
-/******************************************************************************/
+ /******************************************************************************/
-DOMList.prototype.attr = function(attr, value) {
- var i = this.nodes.length;
- if ( value === undefined && typeof attr !== 'object' ) {
- return i ? this.nodes[0].getAttribute(attr) : undefined;
- }
- if ( typeof attr === 'object' ) {
- var attrNames = Object.keys(attr);
- var node, j, attrName;
- while ( i-- ) {
- node = this.nodes[i];
- j = attrNames.length;
- while ( j-- ) {
- attrName = attrNames[j];
- node.setAttribute(attrName, attr[attrName]);
+ DOMList.prototype.css = function(prop, value) {
+ var i = this.nodes.length;
+ if ( value === undefined ) {
+ return i ? this.nodes[0].style[prop] : undefined;
+ }
+ if ( value !== '' ) {
+ while ( i-- ) {
+ this.nodes[i].style.setProperty(prop, value);
}
+ return this;
}
- } else {
while ( i-- ) {
- this.nodes[i].setAttribute(attr, value);
+ this.nodes[i].style.removeProperty(prop);
}
- }
- return this;
-};
+ return this;
+ };
-/******************************************************************************/
+ /******************************************************************************/
-DOMList.prototype.prop = function(prop, value) {
- var i = this.nodes.length;
- if ( value === undefined ) {
- return i !== 0 ? this.nodes[0][prop] : undefined;
- }
- while ( i-- ) {
- this.nodes[i][prop] = value;
- }
- return this;
-};
+ DOMList.prototype.val = function(value) {
+ return this.prop('value', value);
+ };
-/******************************************************************************/
+ /******************************************************************************/
-DOMList.prototype.css = function(prop, value) {
- var i = this.nodes.length;
- if ( value === undefined ) {
- return i ? this.nodes[0].style[prop] : undefined;
- }
- if ( value !== '' ) {
+ DOMList.prototype.html = function(html) {
+ var i = this.nodes.length;
+ if ( html === undefined ) {
+ return i ? this.nodes[0].innerHTML : '';
+ }
while ( i-- ) {
- this.nodes[i].style.setProperty(prop, value);
+ vAPI.insertHTML(this.nodes[i], html);
}
return this;
- }
- while ( i-- ) {
- this.nodes[i].style.removeProperty(prop);
- }
- return this;
-};
-
-/******************************************************************************/
-
-DOMList.prototype.val = function(value) {
- return this.prop('value', value);
-};
+ };
-/******************************************************************************/
+ /******************************************************************************/
-DOMList.prototype.html = function(html) {
- var i = this.nodes.length;
- if ( html === undefined ) {
- return i ? this.nodes[0].innerHTML : '';
- }
- while ( i-- ) {
- vAPI.insertHTML(this.nodes[i], html);
- }
- return this;
-};
+ DOMList.prototype.text = function(text) {
+ var i = this.nodes.length;
+ if ( text === undefined ) {
+ return i ? this.nodes[0].textContent : '';
+ }
+ while ( i-- ) {
+ this.nodes[i].textContent = text;
+ }
+ return this;
+ };
-/******************************************************************************/
+ /******************************************************************************/
-DOMList.prototype.text = function(text) {
- var i = this.nodes.length;
- if ( text === undefined ) {
- return i ? this.nodes[0].textContent : '';
- }
- while ( i-- ) {
- this.nodes[i].textContent = text;
- }
- return this;
-};
+ var toggleClass = function(node, className, targetState) {
+ var tokenList = node.classList;
+ if ( tokenList instanceof DOMTokenList === false ) {
+ return;
+ }
+ var currentState = tokenList.contains(className);
+ var newState = targetState;
+ if ( newState === undefined ) {
+ newState = !currentState;
+ }
+ if ( newState === currentState ) {
+ return;
+ }
+ tokenList.toggle(className, newState);
+ };
-/******************************************************************************/
+ /******************************************************************************/
-var toggleClass = function(node, className, targetState) {
- var tokenList = node.classList;
- if ( tokenList instanceof DOMTokenList === false ) {
- return;
- }
- var currentState = tokenList.contains(className);
- var newState = targetState;
- if ( newState === undefined ) {
- newState = !currentState;
- }
- if ( newState === currentState ) {
- return;
- }
- tokenList.toggle(className, newState);
-};
+ DOMList.prototype.hasClass = function(className) {
+ if ( !this.nodes.length ) {
+ return false;
+ }
+ var tokenList = this.nodes[0].classList;
+ return tokenList instanceof DOMTokenList &&
+ tokenList.contains(className);
+ };
+ DOMList.prototype.hasClassName = DOMList.prototype.hasClass;
-/******************************************************************************/
+ DOMList.prototype.addClass = function(className) {
+ return this.toggleClass(className, true);
+ };
-DOMList.prototype.hasClass = function(className) {
- if ( !this.nodes.length ) {
- return false;
- }
- var tokenList = this.nodes[0].classList;
- return tokenList instanceof DOMTokenList &&
- tokenList.contains(className);
-};
-DOMList.prototype.hasClassName = DOMList.prototype.hasClass;
-
-DOMList.prototype.addClass = function(className) {
- return this.toggleClass(className, true);
-};
-
-DOMList.prototype.removeClass = function(className) {
- if ( className !== undefined ) {
- return this.toggleClass(className, false);
- }
- var i = this.nodes.length;
- while ( i-- ) {
- this.nodes[i].className = '';
- }
- return this;
-};
+ DOMList.prototype.removeClass = function(className) {
+ if ( className !== undefined ) {
+ return this.toggleClass(className, false);
+ }
+ var i = this.nodes.length;
+ while ( i-- ) {
+ this.nodes[i].className = '';
+ }
+ return this;
+ };
-/******************************************************************************/
+ /******************************************************************************/
-DOMList.prototype.toggleClass = function(className, targetState) {
- if ( className.indexOf(' ') !== -1 ) {
- return this.toggleClasses(className, targetState);
- }
- var i = this.nodes.length;
- while ( i-- ) {
- toggleClass(this.nodes[i], className, targetState);
- }
- return this;
-};
+ DOMList.prototype.toggleClass = function(className, targetState) {
+ if ( className.indexOf(' ') !== -1 ) {
+ return this.toggleClasses(className, targetState);
+ }
+ var i = this.nodes.length;
+ while ( i-- ) {
+ toggleClass(this.nodes[i], className, targetState);
+ }
+ return this;
+ };
-/******************************************************************************/
+ /******************************************************************************/
-DOMList.prototype.toggleClasses = function(classNames, targetState) {
- var tokens = classNames.split(/\s+/);
- var i = this.nodes.length;
- var node, j;
- while ( i-- ) {
- node = this.nodes[i];
- j = tokens.length;
- while ( j-- ) {
- toggleClass(node, tokens[j], targetState);
+ DOMList.prototype.toggleClasses = function(classNames, targetState) {
+ var tokens = classNames.split(/\s+/);
+ var i = this.nodes.length;
+ var node, j;
+ while ( i-- ) {
+ node = this.nodes[i];
+ j = tokens.length;
+ while ( j-- ) {
+ toggleClass(node, tokens[j], targetState);
+ }
}
- }
- return this;
-};
+ return this;
+ };
-/******************************************************************************/
+ /******************************************************************************/
-var listenerEntries = [];
+ var listenerEntries = [];
-var ListenerEntry = function(target, type, capture, callback) {
- this.target = target;
- this.type = type;
- this.capture = capture;
- this.callback = callback;
- target.addEventListener(type, callback, capture);
-};
+ var ListenerEntry = function(target, type, capture, callback) {
+ this.target = target;
+ this.type = type;
+ this.capture = capture;
+ this.callback = callback;
+ target.addEventListener(type, callback, capture);
+ };
-ListenerEntry.prototype.dispose = function() {
- this.target.removeEventListener(this.type, this.callback, this.capture);
- this.target = null;
- this.callback = null;
-};
+ ListenerEntry.prototype.dispose = function() {
+ this.target.removeEventListener(this.type, this.callback, this.capture);
+ this.target = null;
+ this.callback = null;
+ };
-/******************************************************************************/
+ /******************************************************************************/
-var makeEventHandler = function(selector, callback) {
- return function(event) {
- var dispatcher = event.currentTarget;
- if ( !dispatcher || typeof dispatcher.querySelectorAll !== 'function' ) {
- return;
- }
- var receiver = event.target;
- if ( nodeInNodeList(receiver, dispatcher.querySelectorAll(selector)) ) {
- callback.call(receiver, event);
- }
+ var makeEventHandler = function(selector, callback) {
+ return function(event) {
+ var dispatcher = event.currentTarget;
+ if ( !dispatcher || typeof dispatcher.querySelectorAll !== 'function' ) {
+ return;
+ }
+ var receiver = event.target;
+ if ( nodeInNodeList(receiver, dispatcher.querySelectorAll(selector)) ) {
+ callback.call(receiver, event);
+ }
+ };
};
-};
-DOMList.prototype.on = function(etype, selector, callback) {
- if ( typeof selector === 'function' ) {
- callback = selector;
- selector = undefined;
- } else {
- callback = makeEventHandler(selector, callback);
- }
+ DOMList.prototype.on = function(etype, selector, callback) {
+ if ( typeof selector === 'function' ) {
+ callback = selector;
+ selector = undefined;
+ } else {
+ callback = makeEventHandler(selector, callback);
+ }
- var i = this.nodes.length;
- while ( i-- ) {
- listenerEntries.push(new ListenerEntry(this.nodes[i], etype, selector !== undefined, callback));
- }
- return this;
-};
+ var i = this.nodes.length;
+ while ( i-- ) {
+ listenerEntries.push(new ListenerEntry(this.nodes[i], etype, selector !== undefined, callback));
+ }
+ return this;
+ };
-/******************************************************************************/
+ /******************************************************************************/
-// TODO: Won't work for delegated handlers. Need to figure
-// what needs to be done.
+ // TODO: Won't work for delegated handlers. Need to figure
+ // what needs to be done.
-DOMList.prototype.off = function(evtype, callback) {
- var i = this.nodes.length;
- while ( i-- ) {
- this.nodes[i].removeEventListener(evtype, callback);
- }
- return this;
-};
+ DOMList.prototype.off = function(evtype, callback) {
+ var i = this.nodes.length;
+ while ( i-- ) {
+ this.nodes[i].removeEventListener(evtype, callback);
+ }
+ return this;
+ };
-/******************************************************************************/
+ /******************************************************************************/
-DOMList.prototype.trigger = function(etype) {
- var ev = new CustomEvent(etype);
- var i = this.nodes.length;
- while ( i-- ) {
- this.nodes[i].dispatchEvent(ev);
- }
- return this;
-};
+ DOMList.prototype.trigger = function(etype) {
+ var ev = new CustomEvent(etype);
+ var i = this.nodes.length;
+ while ( i-- ) {
+ this.nodes[i].dispatchEvent(ev);
+ }
+ return this;
+ };
-/******************************************************************************/
+ /******************************************************************************/
-// Cleanup
+ // Cleanup
-var onBeforeUnload = function() {
- var entry;
- while ( (entry = listenerEntries.pop()) ) {
- entry.dispose();
- }
- window.removeEventListener('beforeunload', onBeforeUnload);
-};
+ var onBeforeUnload = function() {
+ var entry;
+ while ( (entry = listenerEntries.pop()) ) {
+ entry.dispose();
+ }
+ window.removeEventListener('beforeunload', onBeforeUnload);
+ };
-window.addEventListener('beforeunload', onBeforeUnload);
+ window.addEventListener('beforeunload', onBeforeUnload);
-/******************************************************************************/
+ /******************************************************************************/
-return DOMListFactory;
+ return DOMListFactory;
})();
diff --git a/js/uritools.js b/js/uritools.js
index 88d77de..d60af04 100644
--- a/js/uritools.js
+++ b/js/uritools.js
@@ -37,502 +37,501 @@ Naming convention from https://en.wikipedia.org/wiki/URI_scheme#Examples
ηMatrix.URI = (function() {
-/******************************************************************************/
+ /******************************************************************************/
+
+ // Favorite regex tool: http://regex101.com/
+
+ // Ref: <http://tools.ietf.org/html/rfc3986#page-50>
+ // I removed redundant capture groups: capture less = peform faster. See
+ // <http://jsperf.com/old-uritools-vs-new-uritools>
+ // Performance improvements welcomed.
+ // jsperf: <http://jsperf.com/old-uritools-vs-new-uritools>
+ var reRFC3986 = /^([^:\/?#]+:)?(\/\/[^\/?#]*)?([^?#]*)(\?[^#]*)?(#.*)?/;
+
+ // Derived
+ var reSchemeFromURI = /^[^:\/?#]+:/;
+ var reAuthorityFromURI = /^(?:[^:\/?#]+:)?(\/\/[^\/?#]+)/;
+ var reOriginFromURI = /^(?:[^:\/?#]+:)?(?:\/\/[^\/?#]+)/;
+ var reCommonHostnameFromURL = /^https?:\/\/([0-9a-z_][0-9a-z._-]*[0-9a-z])\//;
+ var rePathFromURI = /^(?:[^:\/?#]+:)?(?:\/\/[^\/?#]*)?([^?#]*)/;
+ var reMustNormalizeHostname = /[^0-9a-z._-]/;
+
+ // These are to parse authority field, not parsed by above official regex
+ // IPv6 is seen as an exception: a non-compatible IPv6 is first tried, and
+ // if it fails, the IPv6 compatible regex istr used. This helps
+ // peformance by avoiding the use of a too complicated regex first.
+
+ // https://github.com/gorhill/httpswitchboard/issues/211
+ // "While a hostname may not contain other characters, such as the
+ // "underscore character (_), other DNS names may contain the underscore"
+ var reHostPortFromAuthority = /^(?:[^@]*@)?([^:]*)(:\d*)?$/;
+ var reIPv6PortFromAuthority = /^(?:[^@]*@)?(\[[0-9a-f:]*\])(:\d*)?$/i;
+
+ var reHostFromNakedAuthority = /^[0-9a-z._-]+[0-9a-z]$/i;
+ var reHostFromAuthority = /^(?:[^@]*@)?([^:]+)(?::\d*)?$/;
+ var reIPv6FromAuthority = /^(?:[^@]*@)?(\[[0-9a-f:]+\])(?::\d*)?$/i;
+
+ // Coarse (but fast) tests
+ var reValidHostname = /^([a-z\d]+(-*[a-z\d]+)*)(\.[a-z\d]+(-*[a-z\d])*)*$/;
+ var reIPAddressNaive = /^\d+\.\d+\.\d+\.\d+$|^\[[\da-zA-Z:]+\]$/;
+
+ // Accurate tests
+ // Source.: http://stackoverflow.com/questions/5284147/validating-ipv4-addresses-with-regexp/5284410#5284410
+ //var reIPv4 = /^((25[0-5]|2[0-4]\d|[01]?\d\d?)(\.|$)){4}/;
+
+ // Source: http://forums.intermapper.com/viewtopic.php?p=1096#1096
+ //var reIPv6 = /^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/;
+
+ /******************************************************************************/
+
+ var reset = function(o) {
+ o.scheme = '';
+ o.hostname = '';
+ o._ipv4 = undefined;
+ o._ipv6 = undefined;
+ o.port = '';
+ o.path = '';
+ o.query = '';
+ o.fragment = '';
+ return o;
+ };
+
+ var resetAuthority = function(o) {
+ o.hostname = '';
+ o._ipv4 = undefined;
+ o._ipv6 = undefined;
+ o.port = '';
+ return o;
+ };
+
+ /******************************************************************************/
+
+ // This will be exported
+
+ var URI = {
+ scheme: '',
+ authority: '',
+ hostname: '',
+ _ipv4: undefined,
+ _ipv6: undefined,
+ port: '',
+ domain: undefined,
+ path: '',
+ query: '',
+ fragment: '',
+ schemeBit: (1 << 0),
+ userBit: (1 << 1),
+ passwordBit: (1 << 2),
+ hostnameBit: (1 << 3),
+ portBit: (1 << 4),
+ pathBit: (1 << 5),
+ queryBit: (1 << 6),
+ fragmentBit: (1 << 7),
+ allBits: (0xFFFF)
+ };
+
+ URI.authorityBit = (URI.userBit | URI.passwordBit | URI.hostnameBit | URI.portBit);
+ URI.normalizeBits = (URI.schemeBit | URI.hostnameBit | URI.pathBit | URI.queryBit);
+
+ /******************************************************************************/
+
+ // See: https://en.wikipedia.org/wiki/URI_scheme#Examples
+ // URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
+ //
+ // foo://example.com:8042/over/there?name=ferret#nose
+ // \_/ \______________/\_________/ \_________/ \__/
+ // | | | | |
+ // scheme authority path query fragment
+ // | _____________________|__
+ // / \ / \
+ // urn:example:animal:ferret:nose
+
+ URI.set = function(uri) {
+ if ( uri === undefined ) {
+ return reset(URI);
+ }
+ var matches = reRFC3986.exec(uri);
+ if ( !matches ) {
+ return reset(URI);
+ }
+ this.scheme = matches[1] !== undefined ? matches[1].slice(0, -1) : '';
+ this.authority = matches[2] !== undefined ? matches[2].slice(2).toLowerCase() : '';
+ this.path = matches[3] !== undefined ? matches[3] : '';
+
+ // <http://tools.ietf.org/html/rfc3986#section-6.2.3>
+ // "In general, a URI that uses the generic syntax for authority
+ // "with an empty path should be normalized to a path of '/'."
+ if ( this.authority !== '' && this.path === '' ) {
+ this.path = '/';
+ }
+ this.query = matches[4] !== undefined ? matches[4].slice(1) : '';
+ this.fragment = matches[5] !== undefined ? matches[5].slice(1) : '';
+
+ // Assume very simple authority, i.e. just a hostname (highest likelihood
+ // case for ηMatrix)
+ if ( reHostFromNakedAuthority.test(this.authority) ) {
+ this.hostname = this.authority;
+ this.port = '';
+ return this;
+ }
+ // Authority contains more than just a hostname
+ matches = reHostPortFromAuthority.exec(this.authority);
+ if ( !matches ) {
+ matches = reIPv6PortFromAuthority.exec(this.authority);
+ if ( !matches ) {
+ return resetAuthority(URI);
+ }
+ }
+ this.hostname = matches[1] !== undefined ? matches[1] : '';
+ // http://en.wikipedia.org/wiki/FQDN
+ if ( this.hostname.slice(-1) === '.' ) {
+ this.hostname = this.hostname.slice(0, -1);
+ }
+ this.port = matches[2] !== undefined ? matches[2].slice(1) : '';
+ return this;
+ };
+
+ /******************************************************************************/
+
+ // URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
+ //
+ // foo://example.com:8042/over/there?name=ferret#nose
+ // \_/ \______________/\_________/ \_________/ \__/
+ // | | | | |
+ // scheme authority path query fragment
+ // | _____________________|__
+ // / \ / \
+ // urn:example:animal:ferret:nose
+
+ URI.assemble = function(bits) {
+ if ( bits === undefined ) {
+ bits = this.allBits;
+ }
+ var s = [];
+ if ( this.scheme && (bits & this.schemeBit) ) {
+ s.push(this.scheme, ':');
+ }
+ if ( this.hostname && (bits & this.hostnameBit) ) {
+ s.push('//', this.hostname);
+ }
+ if ( this.port && (bits & this.portBit) ) {
+ s.push(':', this.port);
+ }
+ if ( this.path && (bits & this.pathBit) ) {
+ s.push(this.path);
+ }
+ if ( this.query && (bits & this.queryBit) ) {
+ s.push('?', this.query);
+ }
+ if ( this.fragment && (bits & this.fragmentBit) ) {
+ s.push('#', this.fragment);
+ }
+ return s.join('');
+ };
-// Favorite regex tool: http://regex101.com/
-
-// Ref: <http://tools.ietf.org/html/rfc3986#page-50>
-// I removed redundant capture groups: capture less = peform faster. See
-// <http://jsperf.com/old-uritools-vs-new-uritools>
-// Performance improvements welcomed.
-// jsperf: <http://jsperf.com/old-uritools-vs-new-uritools>
-var reRFC3986 = /^([^:\/?#]+:)?(\/\/[^\/?#]*)?([^?#]*)(\?[^#]*)?(#.*)?/;
-
-// Derived
-var reSchemeFromURI = /^[^:\/?#]+:/;
-var reAuthorityFromURI = /^(?:[^:\/?#]+:)?(\/\/[^\/?#]+)/;
-var reOriginFromURI = /^(?:[^:\/?#]+:)?(?:\/\/[^\/?#]+)/;
-var reCommonHostnameFromURL = /^https?:\/\/([0-9a-z_][0-9a-z._-]*[0-9a-z])\//;
-var rePathFromURI = /^(?:[^:\/?#]+:)?(?:\/\/[^\/?#]*)?([^?#]*)/;
-var reMustNormalizeHostname = /[^0-9a-z._-]/;
-
-// These are to parse authority field, not parsed by above official regex
-// IPv6 is seen as an exception: a non-compatible IPv6 is first tried, and
-// if it fails, the IPv6 compatible regex istr used. This helps
-// peformance by avoiding the use of a too complicated regex first.
-
-// https://github.com/gorhill/httpswitchboard/issues/211
-// "While a hostname may not contain other characters, such as the
-// "underscore character (_), other DNS names may contain the underscore"
-var reHostPortFromAuthority = /^(?:[^@]*@)?([^:]*)(:\d*)?$/;
-var reIPv6PortFromAuthority = /^(?:[^@]*@)?(\[[0-9a-f:]*\])(:\d*)?$/i;
-
-var reHostFromNakedAuthority = /^[0-9a-z._-]+[0-9a-z]$/i;
-var reHostFromAuthority = /^(?:[^@]*@)?([^:]+)(?::\d*)?$/;
-var reIPv6FromAuthority = /^(?:[^@]*@)?(\[[0-9a-f:]+\])(?::\d*)?$/i;
-
-// Coarse (but fast) tests
-var reValidHostname = /^([a-z\d]+(-*[a-z\d]+)*)(\.[a-z\d]+(-*[a-z\d])*)*$/;
-var reIPAddressNaive = /^\d+\.\d+\.\d+\.\d+$|^\[[\da-zA-Z:]+\]$/;
-
-// Accurate tests
-// Source.: http://stackoverflow.com/questions/5284147/validating-ipv4-addresses-with-regexp/5284410#5284410
-//var reIPv4 = /^((25[0-5]|2[0-4]\d|[01]?\d\d?)(\.|$)){4}/;
-
-// Source: http://forums.intermapper.com/viewtopic.php?p=1096#1096
-//var reIPv6 = /^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/;
+ /******************************************************************************/
-/******************************************************************************/
+ URI.originFromURI = function(uri) {
+ var matches = reOriginFromURI.exec(uri);
+ return matches !== null ? matches[0].toLowerCase() : '';
+ };
-var reset = function(o) {
- o.scheme = '';
- o.hostname = '';
- o._ipv4 = undefined;
- o._ipv6 = undefined;
- o.port = '';
- o.path = '';
- o.query = '';
- o.fragment = '';
- return o;
-};
-
-var resetAuthority = function(o) {
- o.hostname = '';
- o._ipv4 = undefined;
- o._ipv6 = undefined;
- o.port = '';
- return o;
-};
+ /******************************************************************************/
-/******************************************************************************/
+ URI.schemeFromURI = function(uri) {
+ var matches = reSchemeFromURI.exec(uri);
+ if ( matches === null ) {
+ return '';
+ }
+ return matches[0].slice(0, -1).toLowerCase();
+ };
-// This will be exported
-
-var URI = {
- scheme: '',
- authority: '',
- hostname: '',
- _ipv4: undefined,
- _ipv6: undefined,
- port: '',
- domain: undefined,
- path: '',
- query: '',
- fragment: '',
- schemeBit: (1 << 0),
- userBit: (1 << 1),
- passwordBit: (1 << 2),
- hostnameBit: (1 << 3),
- portBit: (1 << 4),
- pathBit: (1 << 5),
- queryBit: (1 << 6),
- fragmentBit: (1 << 7),
- allBits: (0xFFFF)
-};
-
-URI.authorityBit = (URI.userBit | URI.passwordBit | URI.hostnameBit | URI.portBit);
-URI.normalizeBits = (URI.schemeBit | URI.hostnameBit | URI.pathBit | URI.queryBit);
+ /******************************************************************************/
-/******************************************************************************/
+ URI.isNetworkScheme = function(scheme) {
+ return this.reNetworkScheme.test(scheme);
+ };
-// See: https://en.wikipedia.org/wiki/URI_scheme#Examples
-// URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
-//
-// foo://example.com:8042/over/there?name=ferret#nose
-// \_/ \______________/\_________/ \_________/ \__/
-// | | | | |
-// scheme authority path query fragment
-// | _____________________|__
-// / \ / \
-// urn:example:animal:ferret:nose
-
-URI.set = function(uri) {
- if ( uri === undefined ) {
- return reset(URI);
- }
- var matches = reRFC3986.exec(uri);
- if ( !matches ) {
- return reset(URI);
- }
- this.scheme = matches[1] !== undefined ? matches[1].slice(0, -1) : '';
- this.authority = matches[2] !== undefined ? matches[2].slice(2).toLowerCase() : '';
- this.path = matches[3] !== undefined ? matches[3] : '';
-
- // <http://tools.ietf.org/html/rfc3986#section-6.2.3>
- // "In general, a URI that uses the generic syntax for authority
- // "with an empty path should be normalized to a path of '/'."
- if ( this.authority !== '' && this.path === '' ) {
- this.path = '/';
- }
- this.query = matches[4] !== undefined ? matches[4].slice(1) : '';
- this.fragment = matches[5] !== undefined ? matches[5].slice(1) : '';
-
- // Assume very simple authority, i.e. just a hostname (highest likelihood
- // case for ηMatrix)
- if ( reHostFromNakedAuthority.test(this.authority) ) {
- this.hostname = this.authority;
- this.port = '';
- return this;
- }
- // Authority contains more than just a hostname
- matches = reHostPortFromAuthority.exec(this.authority);
- if ( !matches ) {
- matches = reIPv6PortFromAuthority.exec(this.authority);
- if ( !matches ) {
- return resetAuthority(URI);
- }
- }
- this.hostname = matches[1] !== undefined ? matches[1] : '';
- // http://en.wikipedia.org/wiki/FQDN
- if ( this.hostname.slice(-1) === '.' ) {
- this.hostname = this.hostname.slice(0, -1);
- }
- this.port = matches[2] !== undefined ? matches[2].slice(1) : '';
- return this;
-};
+ URI.reNetworkScheme = /^(?:https?|wss?|ftps?)\b/;
-/******************************************************************************/
+ /******************************************************************************/
-// URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
-//
-// foo://example.com:8042/over/there?name=ferret#nose
-// \_/ \______________/\_________/ \_________/ \__/
-// | | | | |
-// scheme authority path query fragment
-// | _____________________|__
-// / \ / \
-// urn:example:animal:ferret:nose
-
-URI.assemble = function(bits) {
- if ( bits === undefined ) {
- bits = this.allBits;
- }
- var s = [];
- if ( this.scheme && (bits & this.schemeBit) ) {
- s.push(this.scheme, ':');
- }
- if ( this.hostname && (bits & this.hostnameBit) ) {
- s.push('//', this.hostname);
- }
- if ( this.port && (bits & this.portBit) ) {
- s.push(':', this.port);
- }
- if ( this.path && (bits & this.pathBit) ) {
- s.push(this.path);
- }
- if ( this.query && (bits & this.queryBit) ) {
- s.push('?', this.query);
- }
- if ( this.fragment && (bits & this.fragmentBit) ) {
- s.push('#', this.fragment);
- }
- return s.join('');
-};
+ URI.isSecureScheme = function(scheme) {
+ return this.reSecureScheme.test(scheme);
+ };
-/******************************************************************************/
+ URI.reSecureScheme = /^(?:https|wss|ftps)\b/;
-URI.originFromURI = function(uri) {
- var matches = reOriginFromURI.exec(uri);
- return matches !== null ? matches[0].toLowerCase() : '';
-};
+ /******************************************************************************/
-/******************************************************************************/
+ URI.authorityFromURI = function(uri) {
+ var matches = reAuthorityFromURI.exec(uri);
+ if ( !matches ) {
+ return '';
+ }
+ return matches[1].slice(2).toLowerCase();
+ };
-URI.schemeFromURI = function(uri) {
- var matches = reSchemeFromURI.exec(uri);
- if ( matches === null ) {
- return '';
- }
- return matches[0].slice(0, -1).toLowerCase();
-};
+ /******************************************************************************/
-/******************************************************************************/
+ // The most used function, so it better be fast.
-URI.isNetworkScheme = function(scheme) {
- return this.reNetworkScheme.test(scheme);
-};
+ // https://github.com/gorhill/uBlock/issues/1559
+ // See http://en.wikipedia.org/wiki/FQDN
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=1360285
+ // Revisit punycode dependency when above issue is fixed in Firefox.
-URI.reNetworkScheme = /^(?:https?|wss?|ftps?)\b/;
+ URI.hostnameFromURI = function(uri) {
+ var matches = reCommonHostnameFromURL.exec(uri);
+ if ( matches !== null ) { return matches[1]; }
+ matches = reAuthorityFromURI.exec(uri);
+ if ( matches === null ) { return ''; }
+ var authority = matches[1].slice(2);
+ // Assume very simple authority (most common case for ηBlock)
+ if ( reHostFromNakedAuthority.test(authority) ) {
+ return authority.toLowerCase();
+ }
+ matches = reHostFromAuthority.exec(authority);
+ if ( matches === null ) {
+ matches = reIPv6FromAuthority.exec(authority);
+ if ( matches === null ) { return ''; }
+ }
+ var hostname = matches[1];
+ while ( hostname.endsWith('.') ) {
+ hostname = hostname.slice(0, -1);
+ }
+ if ( reMustNormalizeHostname.test(hostname) ) {
+ hostname = punycode.toASCII(hostname.toLowerCase());
+ }
+ return hostname;
+ };
-/******************************************************************************/
+ /******************************************************************************/
-URI.isSecureScheme = function(scheme) {
- return this.reSecureScheme.test(scheme);
-};
+ URI.domainFromHostname = function(hostname) {
+ // Try to skip looking up the PSL database
+ var entry = domainCache.get(hostname);
+ if ( entry !== undefined ) {
+ entry.tstamp = Date.now();
+ return entry.domain;
+ }
+ // Meh.. will have to search it
+ if ( reIPAddressNaive.test(hostname) === false ) {
+ return domainCacheAdd(hostname, psl.getDomain(hostname));
+ }
+ return domainCacheAdd(hostname, hostname);
+ };
-URI.reSecureScheme = /^(?:https|wss|ftps)\b/;
+ URI.domain = function() {
+ return this.domainFromHostname(this.hostname);
+ };
-/******************************************************************************/
+ // It is expected that there is higher-scoped `publicSuffixList` lingering
+ // somewhere. Cache it. See <https://github.com/gorhill/publicsuffixlist.js>.
+ var psl = publicSuffixList;
-URI.authorityFromURI = function(uri) {
- var matches = reAuthorityFromURI.exec(uri);
- if ( !matches ) {
- return '';
- }
- return matches[1].slice(2).toLowerCase();
-};
+ /******************************************************************************/
-/******************************************************************************/
+ URI.pathFromURI = function(uri) {
+ var matches = rePathFromURI.exec(uri);
+ return matches !== null ? matches[1] : '';
+ };
-// The most used function, so it better be fast.
-
-// https://github.com/gorhill/uBlock/issues/1559
-// See http://en.wikipedia.org/wiki/FQDN
-// https://bugzilla.mozilla.org/show_bug.cgi?id=1360285
-// Revisit punycode dependency when above issue is fixed in Firefox.
-
-URI.hostnameFromURI = function(uri) {
- var matches = reCommonHostnameFromURL.exec(uri);
- if ( matches !== null ) { return matches[1]; }
- matches = reAuthorityFromURI.exec(uri);
- if ( matches === null ) { return ''; }
- var authority = matches[1].slice(2);
- // Assume very simple authority (most common case for ηBlock)
- if ( reHostFromNakedAuthority.test(authority) ) {
- return authority.toLowerCase();
- }
- matches = reHostFromAuthority.exec(authority);
- if ( matches === null ) {
- matches = reIPv6FromAuthority.exec(authority);
- if ( matches === null ) { return ''; }
- }
- var hostname = matches[1];
- while ( hostname.endsWith('.') ) {
- hostname = hostname.slice(0, -1);
- }
- if ( reMustNormalizeHostname.test(hostname) ) {
- hostname = punycode.toASCII(hostname.toLowerCase());
- }
- return hostname;
-};
+ /******************************************************************************/
-/******************************************************************************/
+ // Trying to alleviate the worries of looking up too often the domain name from
+ // a hostname. With a cache, uBlock benefits given that it deals with a
+ // specific set of hostnames within a narrow time span -- in other words, I
+ // believe probability of cache hit are high in uBlock.
-URI.domainFromHostname = function(hostname) {
- // Try to skip looking up the PSL database
- var entry = domainCache.get(hostname);
- if ( entry !== undefined ) {
- entry.tstamp = Date.now();
- return entry.domain;
- }
- // Meh.. will have to search it
- if ( reIPAddressNaive.test(hostname) === false ) {
- return domainCacheAdd(hostname, psl.getDomain(hostname));
- }
- return domainCacheAdd(hostname, hostname);
-};
-
-URI.domain = function() {
- return this.domainFromHostname(this.hostname);
-};
-
-// It is expected that there is higher-scoped `publicSuffixList` lingering
-// somewhere. Cache it. See <https://github.com/gorhill/publicsuffixlist.js>.
-var psl = publicSuffixList;
+ var domainCache = new Map();
+ var domainCacheCountLowWaterMark = 75;
+ var domainCacheCountHighWaterMark = 100;
+ var domainCacheEntryJunkyard = [];
+ var domainCacheEntryJunkyardMax = domainCacheCountHighWaterMark - domainCacheCountLowWaterMark;
-/******************************************************************************/
+ var DomainCacheEntry = function(domain) {
+ this.init(domain);
+ };
-URI.pathFromURI = function(uri) {
- var matches = rePathFromURI.exec(uri);
- return matches !== null ? matches[1] : '';
-};
-
-/******************************************************************************/
+ DomainCacheEntry.prototype.init = function(domain) {
+ this.domain = domain;
+ this.tstamp = Date.now();
+ return this;
+ };
- // Trying to alleviate the worries of looking up too often the domain name from
-// a hostname. With a cache, uBlock benefits given that it deals with a
-// specific set of hostnames within a narrow time span -- in other words, I
-// believe probability of cache hit are high in uBlock.
-
-var domainCache = new Map();
-var domainCacheCountLowWaterMark = 75;
-var domainCacheCountHighWaterMark = 100;
-var domainCacheEntryJunkyard = [];
-var domainCacheEntryJunkyardMax = domainCacheCountHighWaterMark - domainCacheCountLowWaterMark;
-
-var DomainCacheEntry = function(domain) {
- this.init(domain);
-};
-
-DomainCacheEntry.prototype.init = function(domain) {
- this.domain = domain;
- this.tstamp = Date.now();
- return this;
-};
-
-DomainCacheEntry.prototype.dispose = function() {
- this.domain = '';
- if ( domainCacheEntryJunkyard.length < domainCacheEntryJunkyardMax ) {
- domainCacheEntryJunkyard.push(this);
- }
-};
-
-var domainCacheEntryFactory = function(domain) {
- var entry = domainCacheEntryJunkyard.pop();
- if ( entry ) {
- return entry.init(domain);
- }
- return new DomainCacheEntry(domain);
-};
-
-var domainCacheAdd = function(hostname, domain) {
- var entry = domainCache.get(hostname);
- if ( entry !== undefined ) {
- entry.tstamp = Date.now();
- } else {
- domainCache.set(hostname, domainCacheEntryFactory(domain));
- if ( domainCache.size === domainCacheCountHighWaterMark ) {
- domainCachePrune();
- }
- }
- return domain;
-};
-
-var domainCacheEntrySort = function(a, b) {
- return domainCache.get(b).tstamp - domainCache.get(a).tstamp;
-};
-
-var domainCachePrune = function() {
- var hostnames = Array.from(domainCache.keys())
- .sort(domainCacheEntrySort)
- .slice(domainCacheCountLowWaterMark);
- var i = hostnames.length;
- var hostname;
- while ( i-- ) {
- hostname = hostnames[i];
- domainCache.get(hostname).dispose();
- domainCache.delete(hostname);
- }
-};
-
-var domainCacheReset = function() {
- domainCache.clear();
-};
-
-psl.onChanged.addListener(domainCacheReset);
+ DomainCacheEntry.prototype.dispose = function() {
+ this.domain = '';
+ if ( domainCacheEntryJunkyard.length < domainCacheEntryJunkyardMax ) {
+ domainCacheEntryJunkyard.push(this);
+ }
+ };
-/******************************************************************************/
+ var domainCacheEntryFactory = function(domain) {
+ var entry = domainCacheEntryJunkyard.pop();
+ if ( entry ) {
+ return entry.init(domain);
+ }
+ return new DomainCacheEntry(domain);
+ };
+
+ var domainCacheAdd = function(hostname, domain) {
+ var entry = domainCache.get(hostname);
+ if ( entry !== undefined ) {
+ entry.tstamp = Date.now();
+ } else {
+ domainCache.set(hostname, domainCacheEntryFactory(domain));
+ if ( domainCache.size === domainCacheCountHighWaterMark ) {
+ domainCachePrune();
+ }
+ }
+ return domain;
+ };
+
+ var domainCacheEntrySort = function(a, b) {
+ return domainCache.get(b).tstamp - domainCache.get(a).tstamp;
+ };
+
+ var domainCachePrune = function() {
+ var hostnames = Array.from(domainCache.keys())
+ .sort(domainCacheEntrySort)
+ .slice(domainCacheCountLowWaterMark);
+ var i = hostnames.length;
+ var hostname;
+ while ( i-- ) {
+ hostname = hostnames[i];
+ domainCache.get(hostname).dispose();
+ domainCache.delete(hostname);
+ }
+ };
-URI.domainFromURI = function(uri) {
- if ( !uri ) {
- return '';
- }
- return this.domainFromHostname(this.hostnameFromURI(uri));
-};
+ var domainCacheReset = function() {
+ domainCache.clear();
+ };
-/******************************************************************************/
+ psl.onChanged.addListener(domainCacheReset);
-// Normalize the way ηMatrix expects it
+ /******************************************************************************/
-URI.normalizedURI = function() {
- // Will be removed:
- // - port
- // - user id/password
- // - fragment
- return this.assemble(this.normalizeBits);
-};
+ URI.domainFromURI = function(uri) {
+ if ( !uri ) {
+ return '';
+ }
+ return this.domainFromHostname(this.hostnameFromURI(uri));
+ };
-/******************************************************************************/
+ /******************************************************************************/
-URI.rootURL = function() {
- if ( !this.hostname ) {
- return '';
- }
- return this.assemble(this.schemeBit | this.hostnameBit);
-};
+ // Normalize the way ηMatrix expects it
-/******************************************************************************/
+ URI.normalizedURI = function() {
+ // Will be removed:
+ // - port
+ // - user id/password
+ // - fragment
+ return this.assemble(this.normalizeBits);
+ };
-URI.isValidHostname = function(hostname) {
- var r;
- try {
- r = reValidHostname.test(hostname);
- }
- catch (e) {
- return false;
- }
- return r;
-};
+ /******************************************************************************/
-/******************************************************************************/
+ URI.rootURL = function() {
+ if ( !this.hostname ) {
+ return '';
+ }
+ return this.assemble(this.schemeBit | this.hostnameBit);
+ };
-// Return the parent domain. For IP address, there is no parent domain.
+ /******************************************************************************/
-URI.parentHostnameFromHostname = function(hostname) {
- // `locahost` => ``
- // `example.org` => `example.org`
- // `www.example.org` => `example.org`
- // `tomato.www.example.org` => `example.org`
- var domain = this.domainFromHostname(hostname);
+ URI.isValidHostname = function(hostname) {
+ var r;
+ try {
+ r = reValidHostname.test(hostname);
+ }
+ catch (e) {
+ return false;
+ }
+ return r;
+ };
+
+ /******************************************************************************/
+
+ // Return the parent domain. For IP address, there is no parent domain.
+
+ URI.parentHostnameFromHostname = function(hostname) {
+ // `locahost` => ``
+ // `example.org` => `example.org`
+ // `www.example.org` => `example.org`
+ // `tomato.www.example.org` => `example.org`
+ var domain = this.domainFromHostname(hostname);
+
+ // `locahost` === `` => bye
+ // `example.org` === `example.org` => bye
+ // `www.example.org` !== `example.org` => stay
+ // `tomato.www.example.org` !== `example.org` => stay
+ if ( domain === '' || domain === hostname ) {
+ return undefined;
+ }
- // `locahost` === `` => bye
- // `example.org` === `example.org` => bye
- // `www.example.org` !== `example.org` => stay
- // `tomato.www.example.org` !== `example.org` => stay
- if ( domain === '' || domain === hostname ) {
- return undefined;
- }
+ // Parent is hostname minus first label
+ return hostname.slice(hostname.indexOf('.') + 1);
+ };
- // Parent is hostname minus first label
- return hostname.slice(hostname.indexOf('.') + 1);
-};
+ /******************************************************************************/
-/******************************************************************************/
+ // Return all possible parent hostnames which can be derived from `hostname`,
+ // ordered from direct parent up to domain inclusively.
-// Return all possible parent hostnames which can be derived from `hostname`,
-// ordered from direct parent up to domain inclusively.
-
-URI.parentHostnamesFromHostname = function(hostname) {
- // TODO: I should create an object which is optimized to receive
- // the list of hostnames by making it reusable (junkyard etc.) and which
- // has its own element counter property in order to avoid memory
- // alloc/dealloc.
- var domain = this.domainFromHostname(hostname);
- if ( domain === '' || domain === hostname ) {
- return [];
- }
- var nodes = [];
- var pos;
- for (;;) {
- pos = hostname.indexOf('.');
- if ( pos < 0 ) {
- break;
- }
- hostname = hostname.slice(pos + 1);
- nodes.push(hostname);
- if ( hostname === domain ) {
- break;
- }
- }
- return nodes;
-};
+ URI.parentHostnamesFromHostname = function(hostname) {
+ // TODO: I should create an object which is optimized to receive
+ // the list of hostnames by making it reusable (junkyard etc.) and which
+ // has its own element counter property in order to avoid memory
+ // alloc/dealloc.
+ var domain = this.domainFromHostname(hostname);
+ if ( domain === '' || domain === hostname ) {
+ return [];
+ }
+ var nodes = [];
+ var pos;
+ for (;;) {
+ pos = hostname.indexOf('.');
+ if ( pos < 0 ) {
+ break;
+ }
+ hostname = hostname.slice(pos + 1);
+ nodes.push(hostname);
+ if ( hostname === domain ) {
+ break;
+ }
+ }
+ return nodes;
+ };
-/******************************************************************************/
+ /******************************************************************************/
-// Return all possible hostnames which can be derived from `hostname`,
-// ordered from self up to domain inclusively.
+ // Return all possible hostnames which can be derived from `hostname`,
+ // ordered from self up to domain inclusively.
-URI.allHostnamesFromHostname = function(hostname) {
- var nodes = this.parentHostnamesFromHostname(hostname);
- nodes.unshift(hostname);
- return nodes;
-};
+ URI.allHostnamesFromHostname = function(hostname) {
+ var nodes = this.parentHostnamesFromHostname(hostname);
+ nodes.unshift(hostname);
+ return nodes;
+ };
-/******************************************************************************/
+ /******************************************************************************/
-URI.toString = function() {
- return this.assemble();
-};
+ URI.toString = function() {
+ return this.assemble();
+ };
-/******************************************************************************/
+ /******************************************************************************/
-// Export
+ // Export
-return URI;
+ return URI;
-/******************************************************************************/
+ /******************************************************************************/
})();
/******************************************************************************/
-
diff --git a/js/user-rules.js b/js/user-rules.js
index 3197284..02ff787 100644
--- a/js/user-rules.js
+++ b/js/user-rules.js
@@ -29,314 +29,313 @@
(function() {
-/******************************************************************************/
-
-// Switches before, rules after
+ /******************************************************************************/
-var directiveSort = function(a, b) {
- var aIsSwitch = a.indexOf(':') !== -1;
- var bIsSwitch = b.indexOf(':') !== -1;
- if ( aIsSwitch === bIsSwitch ) {
- return a.localeCompare(b);
- }
- return aIsSwitch ? -1 : 1;
-};
+ // Switches before, rules after
-/******************************************************************************/
+ var directiveSort = function(a, b) {
+ var aIsSwitch = a.indexOf(':') !== -1;
+ var bIsSwitch = b.indexOf(':') !== -1;
+ if ( aIsSwitch === bIsSwitch ) {
+ return a.localeCompare(b);
+ }
+ return aIsSwitch ? -1 : 1;
+ };
-var processUserRules = function(response) {
- var rules, rule, i;
- var allRules = {};
- var permanentRules = {};
- var temporaryRules = {};
- var onLeft, onRight;
-
- rules = response.permanentRules.split(/\n+/);
- i = rules.length;
- while ( i-- ) {
- rule = rules[i].trim();
- if ( rule.length !== 0 ) {
- permanentRules[rule] = allRules[rule] = true;
+ /******************************************************************************/
+
+ var processUserRules = function(response) {
+ var rules, rule, i;
+ var allRules = {};
+ var permanentRules = {};
+ var temporaryRules = {};
+ var onLeft, onRight;
+
+ rules = response.permanentRules.split(/\n+/);
+ i = rules.length;
+ while ( i-- ) {
+ rule = rules[i].trim();
+ if ( rule.length !== 0 ) {
+ permanentRules[rule] = allRules[rule] = true;
+ }
}
- }
- rules = response.temporaryRules.split(/\n+/);
- i = rules.length;
- while ( i-- ) {
- rule = rules[i].trim();
- if ( rule.length !== 0 ) {
- temporaryRules[rule] = allRules[rule] = true;
+ rules = response.temporaryRules.split(/\n+/);
+ i = rules.length;
+ while ( i-- ) {
+ rule = rules[i].trim();
+ if ( rule.length !== 0 ) {
+ temporaryRules[rule] = allRules[rule] = true;
+ }
}
- }
- var permanentList = document.createDocumentFragment(),
- temporaryList = document.createDocumentFragment(),
- li;
-
- rules = Object.keys(allRules).sort(directiveSort);
- for ( i = 0; i < rules.length; i++ ) {
- rule = rules[i];
- onLeft = permanentRules.hasOwnProperty(rule);
- onRight = temporaryRules.hasOwnProperty(rule);
- if ( onLeft && onRight ) {
- li = document.createElement('li');
- li.textContent = rule;
- permanentList.appendChild(li);
- li = document.createElement('li');
- li.textContent = rule;
- temporaryList.appendChild(li);
- } else if ( onLeft ) {
- li = document.createElement('li');
- li.textContent = rule;
- permanentList.appendChild(li);
- li = document.createElement('li');
- li.textContent = rule;
- li.className = 'notRight toRemove';
- temporaryList.appendChild(li);
- } else if ( onRight ) {
- li = document.createElement('li');
- li.textContent = '\xA0';
- permanentList.appendChild(li);
- li = document.createElement('li');
- li.textContent = rule;
- li.className = 'notLeft';
- temporaryList.appendChild(li);
+ var permanentList = document.createDocumentFragment(),
+ temporaryList = document.createDocumentFragment(),
+ li;
+
+ rules = Object.keys(allRules).sort(directiveSort);
+ for ( i = 0; i < rules.length; i++ ) {
+ rule = rules[i];
+ onLeft = permanentRules.hasOwnProperty(rule);
+ onRight = temporaryRules.hasOwnProperty(rule);
+ if ( onLeft && onRight ) {
+ li = document.createElement('li');
+ li.textContent = rule;
+ permanentList.appendChild(li);
+ li = document.createElement('li');
+ li.textContent = rule;
+ temporaryList.appendChild(li);
+ } else if ( onLeft ) {
+ li = document.createElement('li');
+ li.textContent = rule;
+ permanentList.appendChild(li);
+ li = document.createElement('li');
+ li.textContent = rule;
+ li.className = 'notRight toRemove';
+ temporaryList.appendChild(li);
+ } else if ( onRight ) {
+ li = document.createElement('li');
+ li.textContent = '\xA0';
+ permanentList.appendChild(li);
+ li = document.createElement('li');
+ li.textContent = rule;
+ li.className = 'notLeft';
+ temporaryList.appendChild(li);
+ }
}
- }
- // TODO: build incrementally.
+ // TODO: build incrementally.
- uDom('#diff > .left > ul > li').remove();
- document.querySelector('#diff > .left > ul').appendChild(permanentList);
- uDom('#diff > .right > ul > li').remove();
- document.querySelector('#diff > .right > ul').appendChild(temporaryList);
- uDom('#diff').toggleClass('dirty', response.temporaryRules !== response.permanentRules);
-};
+ uDom('#diff > .left > ul > li').remove();
+ document.querySelector('#diff > .left > ul').appendChild(permanentList);
+ uDom('#diff > .right > ul > li').remove();
+ document.querySelector('#diff > .right > ul').appendChild(temporaryList);
+ uDom('#diff').toggleClass('dirty', response.temporaryRules !== response.permanentRules);
+ };
-/******************************************************************************/
+ /******************************************************************************/
-// https://github.com/chrisaljoudi/uBlock/issues/757
-// Support RequestPolicy rule syntax
+ // https://github.com/chrisaljoudi/uBlock/issues/757
+ // Support RequestPolicy rule syntax
-var fromRequestPolicy = function(content) {
- var matches = /\[origins-to-destinations\]([^\[]+)/.exec(content);
- if ( matches === null || matches.length !== 2 ) {
- return;
- }
- return matches[1].trim()
- .replace(/\|/g, ' ')
- .replace(/\n/g, ' * allow\n');
-};
+ var fromRequestPolicy = function(content) {
+ var matches = /\[origins-to-destinations\]([^\[]+)/.exec(content);
+ if ( matches === null || matches.length !== 2 ) {
+ return;
+ }
+ return matches[1].trim()
+ .replace(/\|/g, ' ')
+ .replace(/\n/g, ' * allow\n');
+ };
-/******************************************************************************/
+ /******************************************************************************/
-// https://github.com/gorhill/uMatrix/issues/270
+ // https://github.com/gorhill/uMatrix/issues/270
-var fromNoScript = function(content) {
- var noscript = null;
- try {
- noscript = JSON.parse(content);
- } catch (e) {
- }
- if (
- noscript === null ||
- typeof noscript !== 'object' ||
- typeof noscript.prefs !== 'object' ||
- typeof noscript.prefs.clearClick === 'undefined' ||
- typeof noscript.whitelist !== 'string' ||
- typeof noscript.V !== 'string'
- ) {
- return;
- }
- var out = new Set();
- var reBad = /[a-z]+:\w*$/;
- var reURL = /[a-z]+:\/\/([0-9a-z.-]+)/;
- var directives = noscript.whitelist.split(/\s+/);
- var i = directives.length;
- var directive, matches;
- while ( i-- ) {
- directive = directives[i].trim();
- if ( directive === '' ) {
- continue;
+ var fromNoScript = function(content) {
+ var noscript = null;
+ try {
+ noscript = JSON.parse(content);
+ } catch (e) {
}
- if ( reBad.test(directive) ) {
- continue;
+ if (
+ noscript === null ||
+ typeof noscript !== 'object' ||
+ typeof noscript.prefs !== 'object' ||
+ typeof noscript.prefs.clearClick === 'undefined' ||
+ typeof noscript.whitelist !== 'string' ||
+ typeof noscript.V !== 'string'
+ ) {
+ return;
}
- matches = reURL.exec(directive);
- if ( matches !== null ) {
- directive = matches[1];
+ var out = new Set();
+ var reBad = /[a-z]+:\w*$/;
+ var reURL = /[a-z]+:\/\/([0-9a-z.-]+)/;
+ var directives = noscript.whitelist.split(/\s+/);
+ var i = directives.length;
+ var directive, matches;
+ while ( i-- ) {
+ directive = directives[i].trim();
+ if ( directive === '' ) {
+ continue;
+ }
+ if ( reBad.test(directive) ) {
+ continue;
+ }
+ matches = reURL.exec(directive);
+ if ( matches !== null ) {
+ directive = matches[1];
+ }
+ out.add('* ' + directive + ' * allow');
+ out.add('* ' + directive + ' script allow');
+ out.add('* ' + directive + ' frame allow');
}
- out.add('* ' + directive + ' * allow');
- out.add('* ' + directive + ' script allow');
- out.add('* ' + directive + ' frame allow');
- }
- return Array.from(out).join('\n');
-};
+ return Array.from(out).join('\n');
+ };
-/******************************************************************************/
+ /******************************************************************************/
-var handleImportFilePicker = function() {
- var fileReaderOnLoadHandler = function() {
- if ( typeof this.result !== 'string' || this.result === '' ) {
- return;
- }
- var result = fromRequestPolicy(this.result);
- if ( result === undefined ) {
- result = fromNoScript(this.result);
+ var handleImportFilePicker = function() {
+ var fileReaderOnLoadHandler = function() {
+ if ( typeof this.result !== 'string' || this.result === '' ) {
+ return;
+ }
+ var result = fromRequestPolicy(this.result);
if ( result === undefined ) {
- result = this.result;
+ result = fromNoScript(this.result);
+ if ( result === undefined ) {
+ result = this.result;
+ }
}
- }
- if ( this.result === '' ) { return; }
- var request = {
- 'what': 'setUserRules',
- 'temporaryRules': rulesFromHTML('#diff .right li') + '\n' + result
+ if ( this.result === '' ) { return; }
+ var request = {
+ 'what': 'setUserRules',
+ 'temporaryRules': rulesFromHTML('#diff .right li') + '\n' + result
+ };
+ vAPI.messaging.send('user-rules.js', request, processUserRules);
};
- vAPI.messaging.send('user-rules.js', request, processUserRules);
+ var file = this.files[0];
+ if ( file === undefined || file.name === '' ) {
+ return;
+ }
+ if ( file.type.indexOf('text') !== 0 && file.type !== 'application/json') {
+ return;
+ }
+ var fr = new FileReader();
+ fr.onload = fileReaderOnLoadHandler;
+ fr.readAsText(file);
};
- var file = this.files[0];
- if ( file === undefined || file.name === '' ) {
- return;
- }
- if ( file.type.indexOf('text') !== 0 && file.type !== 'application/json') {
- return;
- }
- var fr = new FileReader();
- fr.onload = fileReaderOnLoadHandler;
- fr.readAsText(file);
-};
-/******************************************************************************/
+ /******************************************************************************/
-var startImportFilePicker = function() {
- var input = document.getElementById('importFilePicker');
- // Reset to empty string, this will ensure an change event is properly
- // triggered if the user pick a file, even if it is the same as the last
- // one picked.
- input.value = '';
- input.click();
-};
-
-/******************************************************************************/
+ var startImportFilePicker = function() {
+ var input = document.getElementById('importFilePicker');
+ // Reset to empty string, this will ensure an change event is properly
+ // triggered if the user pick a file, even if it is the same as the last
+ // one picked.
+ input.value = '';
+ input.click();
+ };
-function exportUserRulesToFile() {
- vAPI.download({
- 'url': 'data:text/plain,' + encodeURIComponent(rulesFromHTML('#diff .left li') + '\n'),
- 'filename': uDom('[data-i18n="userRulesDefaultFileName"]').text()
- });
-}
+ /******************************************************************************/
-/******************************************************************************/
+ function exportUserRulesToFile() {
+ vAPI.download({
+ 'url': 'data:text/plain,' + encodeURIComponent(rulesFromHTML('#diff .left li') + '\n'),
+ 'filename': uDom('[data-i18n="userRulesDefaultFileName"]').text()
+ });
+ }
-var rulesFromHTML = function(selector) {
- var rules = [];
- var lis = uDom(selector);
- var li;
- for ( var i = 0; i < lis.length; i++ ) {
- li = lis.at(i);
- if ( li.hasClassName('toRemove') ) {
- rules.push('');
- } else {
- rules.push(li.text());
+ /******************************************************************************/
+
+ var rulesFromHTML = function(selector) {
+ var rules = [];
+ var lis = uDom(selector);
+ var li;
+ for ( var i = 0; i < lis.length; i++ ) {
+ li = lis.at(i);
+ if ( li.hasClassName('toRemove') ) {
+ rules.push('');
+ } else {
+ rules.push(li.text());
+ }
}
- }
- return rules.join('\n');
-};
+ return rules.join('\n');
+ };
-/******************************************************************************/
+ /******************************************************************************/
-var revertHandler = function() {
- var request = {
- 'what': 'setUserRules',
- 'temporaryRules': rulesFromHTML('#diff .left li')
+ var revertHandler = function() {
+ var request = {
+ 'what': 'setUserRules',
+ 'temporaryRules': rulesFromHTML('#diff .left li')
+ };
+ vAPI.messaging.send('user-rules.js', request, processUserRules);
};
- vAPI.messaging.send('user-rules.js', request, processUserRules);
-};
-/******************************************************************************/
+ /******************************************************************************/
-var commitHandler = function() {
- var request = {
- 'what': 'setUserRules',
- 'permanentRules': rulesFromHTML('#diff .right li')
+ var commitHandler = function() {
+ var request = {
+ 'what': 'setUserRules',
+ 'permanentRules': rulesFromHTML('#diff .right li')
+ };
+ vAPI.messaging.send('user-rules.js', request, processUserRules);
};
- vAPI.messaging.send('user-rules.js', request, processUserRules);
-};
-/******************************************************************************/
+ /******************************************************************************/
-var editStartHandler = function() {
- uDom('#diff .right textarea').val(rulesFromHTML('#diff .right li'));
- var parent = uDom(this).ancestors('#diff');
- parent.toggleClass('edit', true);
-};
+ var editStartHandler = function() {
+ uDom('#diff .right textarea').val(rulesFromHTML('#diff .right li'));
+ var parent = uDom(this).ancestors('#diff');
+ parent.toggleClass('edit', true);
+ };
-/******************************************************************************/
+ /******************************************************************************/
-var editStopHandler = function() {
- var parent = uDom(this).ancestors('#diff');
- parent.toggleClass('edit', false);
- var request = {
- 'what': 'setUserRules',
- 'temporaryRules': uDom('#diff .right textarea').val()
+ var editStopHandler = function() {
+ var parent = uDom(this).ancestors('#diff');
+ parent.toggleClass('edit', false);
+ var request = {
+ 'what': 'setUserRules',
+ 'temporaryRules': uDom('#diff .right textarea').val()
+ };
+ vAPI.messaging.send('user-rules.js', request, processUserRules);
};
- vAPI.messaging.send('user-rules.js', request, processUserRules);
-};
-/******************************************************************************/
+ /******************************************************************************/
-var editCancelHandler = function() {
- var parent = uDom(this).ancestors('#diff');
- parent.toggleClass('edit', false);
-};
+ var editCancelHandler = function() {
+ var parent = uDom(this).ancestors('#diff');
+ parent.toggleClass('edit', false);
+ };
-/******************************************************************************/
+ /******************************************************************************/
-var temporaryRulesToggler = function() {
- var li = uDom(this);
- li.toggleClass('toRemove');
- var request = {
- 'what': 'setUserRules',
- 'temporaryRules': rulesFromHTML('#diff .right li')
+ var temporaryRulesToggler = function() {
+ var li = uDom(this);
+ li.toggleClass('toRemove');
+ var request = {
+ 'what': 'setUserRules',
+ 'temporaryRules': rulesFromHTML('#diff .right li')
+ };
+ vAPI.messaging.send('user-rules.js', request, processUserRules);
};
- vAPI.messaging.send('user-rules.js', request, processUserRules);
-};
-
-/******************************************************************************/
-self.cloud.onPush = function() {
- return rulesFromHTML('#diff .left li');
-};
+ /******************************************************************************/
-self.cloud.onPull = function(data, append) {
- if ( typeof data !== 'string' ) { return; }
- if ( append ) {
- data = rulesFromHTML('#diff .right li') + '\n' + data;
- }
- var request = {
- 'what': 'setUserRules',
- 'temporaryRules': data
+ self.cloud.onPush = function() {
+ return rulesFromHTML('#diff .left li');
};
- vAPI.messaging.send('user-rules.js', request, processUserRules);
-};
-/******************************************************************************/
+ self.cloud.onPull = function(data, append) {
+ if ( typeof data !== 'string' ) { return; }
+ if ( append ) {
+ data = rulesFromHTML('#diff .right li') + '\n' + data;
+ }
+ var request = {
+ 'what': 'setUserRules',
+ 'temporaryRules': data
+ };
+ vAPI.messaging.send('user-rules.js', request, processUserRules);
+ };
-uDom.onLoad(function() {
- // Handle user interaction
- uDom('#importButton').on('click', startImportFilePicker);
- uDom('#importFilePicker').on('change', handleImportFilePicker);
- uDom('#exportButton').on('click', exportUserRulesToFile);
- uDom('#revertButton').on('click', revertHandler);
- uDom('#commitButton').on('click', commitHandler);
- uDom('#editEnterButton').on('click', editStartHandler);
- uDom('#editStopButton').on('click', editStopHandler);
- uDom('#editCancelButton').on('click', editCancelHandler);
- uDom('#diff > .right > ul').on('click', 'li', temporaryRulesToggler);
-
- vAPI.messaging.send('user-rules.js', { what: 'getUserRules' }, processUserRules);
-});
+ /******************************************************************************/
+
+ uDom.onLoad(function() {
+ // Handle user interaction
+ uDom('#importButton').on('click', startImportFilePicker);
+ uDom('#importFilePicker').on('change', handleImportFilePicker);
+ uDom('#exportButton').on('click', exportUserRulesToFile);
+ uDom('#revertButton').on('click', revertHandler);
+ uDom('#commitButton').on('click', commitHandler);
+ uDom('#editEnterButton').on('click', editStartHandler);
+ uDom('#editStopButton').on('click', editStopHandler);
+ uDom('#editCancelButton').on('click', editCancelHandler);
+ uDom('#diff > .right > ul').on('click', 'li', temporaryRulesToggler);
+
+ vAPI.messaging.send('user-rules.js', { what: 'getUserRules' }, processUserRules);
+ });
-/******************************************************************************/
+ /******************************************************************************/
})();
-
diff --git a/js/usersettings.js b/js/usersettings.js
index e6b3e6e..e2655b6 100644
--- a/js/usersettings.js
+++ b/js/usersettings.js
@@ -41,8 +41,8 @@
// Pre-change
switch ( name ) {
-
- default:
+
+ default:
break;
}
@@ -51,8 +51,8 @@
// Post-change
switch ( name ) {
-
- default:
+
+ default:
break;
}
diff --git a/js/vapi-background.js b/js/vapi-background.js
index 1064153..7686bc9 100644
--- a/js/vapi-background.js
+++ b/js/vapi-background.js
@@ -35,53 +35,53 @@
// Icon-related stuff
vAPI.setIcon = function (tabId, iconId, badge) {
- // If badge is undefined, then setIcon was called from the
- // TabSelect event
- let win;
- if (badge === undefined) {
+ // If badge is undefined, then setIcon was called from the
+ // TabSelect event
+ let win;
+ if (badge === undefined) {
win = iconId;
- } else {
+ } else {
win = vAPI.window.getCurrentWindow();
- }
-
- let tabBrowser = vAPI.browser.getTabBrowser(win);
- if (tabBrowser === null) {
+ }
+
+ let tabBrowser = vAPI.browser.getTabBrowser(win);
+ if (tabBrowser === null) {
return;
- }
-
- let curTabId = vAPI.tabs.manager.tabIdFromTarget(tabBrowser.selectedTab);
- let tb = vAPI.toolbarButton;
+ }
+
+ let curTabId = vAPI.tabs.manager.tabIdFromTarget(tabBrowser.selectedTab);
+ let tb = vAPI.toolbarButton;
- // from 'TabSelect' event
- if (tabId === undefined) {
+ // from 'TabSelect' event
+ if (tabId === undefined) {
tabId = curTabId;
- } else if (badge !== undefined) {
+ } else if (badge !== undefined) {
tb.tabs[tabId] = {
- badge: badge, img: iconId
- };
- }
+ badge: badge, img: iconId
+ };
+ }
- if (tabId === curTabId) {
+ if (tabId === curTabId) {
tb.updateState(win, tabId);
- }
+ }
};
vAPI.httpObserver = {
- classDescription: 'net-channel-event-sinks for ' + location.host,
- classID: Components.ID('{5d2e2797-6d68-42e2-8aeb-81ce6ba16b95}'),
- contractID: '@' + location.host + '/net-channel-event-sinks;1',
- REQDATAKEY: location.host + 'reqdata',
- ABORT: Components.results.NS_BINDING_ABORTED,
- ACCEPT: Components.results.NS_SUCCEEDED,
- // Request types:
- // https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIContentPolicy#Constants
- // eMatrix: we can just use nsIContentPolicy's built-in
- // constants, can we?
- frameTypeMap: {
+ classDescription: 'net-channel-event-sinks for ' + location.host,
+ classID: Components.ID('{5d2e2797-6d68-42e2-8aeb-81ce6ba16b95}'),
+ contractID: '@' + location.host + '/net-channel-event-sinks;1',
+ REQDATAKEY: location.host + 'reqdata',
+ ABORT: Components.results.NS_BINDING_ABORTED,
+ ACCEPT: Components.results.NS_SUCCEEDED,
+ // Request types:
+ // https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIContentPolicy#Constants
+ // eMatrix: we can just use nsIContentPolicy's built-in
+ // constants, can we?
+ frameTypeMap: {
6: 'main_frame',
7: 'sub_frame'
- },
- typeMap: {
+ },
+ typeMap: {
1: 'other',
2: 'script',
3: 'image',
@@ -103,359 +103,359 @@
20: 'xmlhttprequest',
21: 'imageset',
22: 'web_manifest'
- },
- mimeTypeMap: {
+ },
+ mimeTypeMap: {
'audio': 15,
'video': 15
- },
- get componentRegistrar () {
+ },
+ get componentRegistrar () {
return Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
- },
- get categoryManager () {
+ },
+ get categoryManager () {
return Cc['@mozilla.org/categorymanager;1']
.getService(Ci.nsICategoryManager);
- },
- QueryInterface: (function () {
+ },
+ QueryInterface: (function () {
var {XPCOMUtils} =
- Cu.import('resource://gre/modules/XPCOMUtils.jsm', null);
+ Cu.import('resource://gre/modules/XPCOMUtils.jsm', null);
return XPCOMUtils.generateQI([
- Ci.nsIFactory,
- Ci.nsIObserver,
- Ci.nsIChannelEventSink,
- Ci.nsISupportsWeakReference
+ Ci.nsIFactory,
+ Ci.nsIObserver,
+ Ci.nsIChannelEventSink,
+ Ci.nsISupportsWeakReference
]);
- })(),
- createInstance: function (outer, iid) {
+ })(),
+ createInstance: function (outer, iid) {
if (outer) {
- throw Components.results.NS_ERROR_NO_AGGREGATION;
+ throw Components.results.NS_ERROR_NO_AGGREGATION;
}
return this.QueryInterface(iid);
- },
- register: function () {
+ },
+ register: function () {
this.pendingRingBufferInit();
-
+
Services.obs.addObserver(this, 'http-on-modify-request', true);
Services.obs.addObserver(this, 'http-on-examine-response', true);
Services.obs.addObserver(this, 'http-on-examine-cached-response', true);
// Guard against stale instances not having been unregistered
if (this.componentRegistrar.isCIDRegistered(this.classID)) {
- try {
+ try {
this.componentRegistrar
- .unregisterFactory(this.classID,
- Components.manager
- .getClassObject(this.classID,
- Ci.nsIFactory));
- } catch (ex) {
+ .unregisterFactory(this.classID,
+ Components.manager
+ .getClassObject(this.classID,
+ Ci.nsIFactory));
+ } catch (ex) {
console.error('eMatrix> httpObserver > '
- +'unable to unregister stale instance: ', ex);
- }
+ +'unable to unregister stale instance: ', ex);
+ }
}
this.componentRegistrar.registerFactory(this.classID,
- this.classDescription,
- this.contractID,
- this);
+ this.classDescription,
+ this.contractID,
+ this);
this.categoryManager.addCategoryEntry('net-channel-event-sinks',
- this.contractID,
- this.contractID,
- false,
- true);
- },
- unregister: function () {
+ this.contractID,
+ this.contractID,
+ false,
+ true);
+ },
+ unregister: function () {
Services.obs.removeObserver(this, 'http-on-modify-request');
Services.obs.removeObserver(this, 'http-on-examine-response');
Services.obs.removeObserver(this, 'http-on-examine-cached-response');
this.componentRegistrar.unregisterFactory(this.classID, this);
this.categoryManager.deleteCategoryEntry('net-channel-event-sinks',
- this.contractID,
- false);
- },
- PendingRequest: function () {
+ this.contractID,
+ false);
+ },
+ PendingRequest: function () {
this.rawType = 0;
this.tabId = 0;
this._key = ''; // key is url, from URI.spec
- },
- // If all work fine, this map should not grow indefinitely. It
- // can have stale items in it, but these will be taken care of
- // when entries in the ring buffer are overwritten.
- pendingURLToIndex: new Map(),
- pendingWritePointer: 0,
- pendingRingBuffer: new Array(256),
- pendingRingBufferInit: function () {
+ },
+ // If all work fine, this map should not grow indefinitely. It
+ // can have stale items in it, but these will be taken care of
+ // when entries in the ring buffer are overwritten.
+ pendingURLToIndex: new Map(),
+ pendingWritePointer: 0,
+ pendingRingBuffer: new Array(256),
+ pendingRingBufferInit: function () {
// Use and reuse pre-allocated PendingRequest objects =
// less memory churning.
for (let i=this.pendingRingBuffer.length-1; i>=0; --i) {
- this.pendingRingBuffer[i] = new this.PendingRequest();
+ this.pendingRingBuffer[i] = new this.PendingRequest();
}
- },
- createPendingRequest: function (url) {
- // Pending request ring buffer:
- // +-------+-------+-------+-------+-------+-------+-------
- // |0 |1 |2 |3 |4 |5 |...
- // +-------+-------+-------+-------+-------+-------+-------
- //
- // URL to ring buffer index map:
- // { k = URL, s = ring buffer indices }
- //
- // s is a string which character codes map to ring buffer
- // indices -- for when the same URL is received multiple times
- // by shouldLoadListener() before the existing one is serviced
- // by the network request observer. I believe the use of a
- // string in lieu of an array reduces memory churning.
+ },
+ createPendingRequest: function (url) {
+ // Pending request ring buffer:
+ // +-------+-------+-------+-------+-------+-------+-------
+ // |0 |1 |2 |3 |4 |5 |...
+ // +-------+-------+-------+-------+-------+-------+-------
+ //
+ // URL to ring buffer index map:
+ // { k = URL, s = ring buffer indices }
+ //
+ // s is a string which character codes map to ring buffer
+ // indices -- for when the same URL is received multiple times
+ // by shouldLoadListener() before the existing one is serviced
+ // by the network request observer. I believe the use of a
+ // string in lieu of an array reduces memory churning.
let bucket;
let i = this.pendingWritePointer;
this.pendingWritePointer = i + 1 & 255;
-
+
let preq = this.pendingRingBuffer[i];
let si = String.fromCharCode(i);
-
+
// Cleanup unserviced pending request
if (preq._key !== '') {
- bucket = this.pendingURLToIndex.get(preq._key);
- if (bucket.length === 1) {
+ bucket = this.pendingURLToIndex.get(preq._key);
+ if (bucket.length === 1) {
this.pendingURLToIndex.delete(preq._key);
- } else {
+ } else {
let pos = bucket.indexOf(si);
this.pendingURLToIndex.set(preq._key,
- bucket.slice(0, pos)
- + bucket.slice(pos + 1));
- }
+ bucket.slice(0, pos)
+ + bucket.slice(pos + 1));
+ }
}
-
+
bucket = this.pendingURLToIndex.get(url);
this.pendingURLToIndex.set(url, bucket === undefined
- ? si
- : bucket + si);
+ ? si
+ : bucket + si);
preq._key = url;
return preq;
- },
- lookupPendingRequest: function (url) {
+ },
+ lookupPendingRequest: function (url) {
let bucket = this.pendingURLToIndex.get(url);
if (bucket === undefined) {
- return null;
+ return null;
}
-
+
let i = bucket.charCodeAt(0);
if (bucket.length === 1) {
- this.pendingURLToIndex.delete(url);
+ this.pendingURLToIndex.delete(url);
} else {
- this.pendingURLToIndex.set(url, bucket.slice(1));
+ this.pendingURLToIndex.set(url, bucket.slice(1));
}
-
+
let preq = this.pendingRingBuffer[i];
preq._key = ''; // mark as "serviced"
return preq;
- },
- handleRequest: function (channel, URI, tabId, rawType) {
+ },
+ handleRequest: function (channel, URI, tabId, rawType) {
let type = this.typeMap[rawType] || 'other';
let onBeforeRequest = vAPI.net.onBeforeRequest;
if (onBeforeRequest.types === null
- || onBeforeRequest.types.has(type)) {
- let result = onBeforeRequest.callback({
+ || onBeforeRequest.types.has(type)) {
+ let result = onBeforeRequest.callback({
parentFrameId: type === 'main_frame' ? -1 : 0,
tabId: tabId,
type: type,
url: URI.asciiSpec
- });
+ });
- if (typeof result === 'object') {
+ if (typeof result === 'object') {
channel.cancel(this.ABORT);
return true;
- }
+ }
}
let onBeforeSendHeaders = vAPI.net.onBeforeSendHeaders;
if (onBeforeSendHeaders.types === null
- || onBeforeSendHeaders.types.has(type)) {
- let requestHeaders = HTTPRequestHeaders.factory(channel);
- let newHeaders = onBeforeSendHeaders.callback({
+ || onBeforeSendHeaders.types.has(type)) {
+ let requestHeaders = HTTPRequestHeaders.factory(channel);
+ let newHeaders = onBeforeSendHeaders.callback({
parentFrameId: type === 'main_frame' ? -1 : 0,
requestHeaders: requestHeaders.headers,
tabId: tabId,
type: type,
url: URI.asciiSpec,
method: channel.requestMethod
- });
-
- if (newHeaders) {
+ });
+
+ if (newHeaders) {
requestHeaders.update();
- }
- requestHeaders.dispose();
+ }
+ requestHeaders.dispose();
}
return false;
- },
- channelDataFromChannel: function (channel) {
+ },
+ channelDataFromChannel: function (channel) {
if (channel instanceof Ci.nsIWritablePropertyBag) {
- try {
+ try {
return channel.getProperty(this.REQDATAKEY) || null;
- } catch (ex) {
- // Ignore
- }
+ } catch (ex) {
+ // Ignore
+ }
}
-
+
return null;
- },
- // https://github.com/gorhill/uMatrix/issues/165
- // https://developer.mozilla.org/en-US/Firefox/Releases/3.5/Updating_extensions#Getting_a_load_context_from_a_request
- // Not sure `ematrix:shouldLoad` is still needed, eMatrix does
- // not care about embedded frames topography.
- // Also:
- // https://developer.mozilla.org/en-US/Firefox/Multiprocess_Firefox/Limitations_of_chrome_scripts
- tabIdFromChannel: function (channel) {
+ },
+ // https://github.com/gorhill/uMatrix/issues/165
+ // https://developer.mozilla.org/en-US/Firefox/Releases/3.5/Updating_extensions#Getting_a_load_context_from_a_request
+ // Not sure `ematrix:shouldLoad` is still needed, eMatrix does
+ // not care about embedded frames topography.
+ // Also:
+ // https://developer.mozilla.org/en-US/Firefox/Multiprocess_Firefox/Limitations_of_chrome_scripts
+ tabIdFromChannel: function (channel) {
let lc;
try {
- lc = channel.notificationCallbacks.getInterface(Ci.nsILoadContext);
+ lc = channel.notificationCallbacks.getInterface(Ci.nsILoadContext);
} catch(ex) {
- // Ignore
+ // Ignore
}
-
+
if (!lc) {
- try {
+ try {
lc = channel.loadGroup.notificationCallbacks
- .getInterface(Ci.nsILoadContext);
- } catch(ex) {
- // Ignore
- }
-
- if (!lc) {
+ .getInterface(Ci.nsILoadContext);
+ } catch(ex) {
+ // Ignore
+ }
+
+ if (!lc) {
return vAPI.noTabId;
- }
+ }
}
-
+
if (lc.topFrameElement) {
- return vAPI.tabs.manager.tabIdFromTarget(lc.topFrameElement);
+ return vAPI.tabs.manager.tabIdFromTarget(lc.topFrameElement);
}
-
+
let win;
try {
- win = lc.associatedWindow;
+ win = lc.associatedWindow;
} catch (ex) {
- // Ignore
- }
-
+ // Ignore
+ }
+
if (!win) {
- return vAPI.noTabId;
+ return vAPI.noTabId;
}
-
+
if (win.top) {
- win = win.top;
+ win = win.top;
}
-
+
let tabBrowser;
try {
- tabBrowser =
- vAPI.browser.getTabBrowser
- (win.QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIWebNavigation)
- .QueryInterface(Ci.nsIDocShell).rootTreeItem
- .QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIDOMWindow));
+ tabBrowser =
+ vAPI.browser.getTabBrowser
+ (win.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsIDocShell).rootTreeItem
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDOMWindow));
} catch (ex) {
- // Ignore
- }
-
+ // Ignore
+ }
+
if (!tabBrowser) {
- return vAPI.noTabId;
+ return vAPI.noTabId;
}
-
+
if (tabBrowser.getBrowserForContentWindow) {
- return vAPI.tabs.manager
- .tabIdFromTarget(tabBrowser.getBrowserForContentWindow(win));
+ return vAPI.tabs.manager
+ .tabIdFromTarget(tabBrowser.getBrowserForContentWindow(win));
}
-
+
// Falling back onto _getTabForContentWindow to ensure older
// versions of Firefox work well.
return tabBrowser._getTabForContentWindow
- ? vAPI.tabs.manager
- .tabIdFromTarget(tabBrowser._getTabForContentWindow(win))
- : vAPI.noTabId;
- },
- rawtypeFromContentType: function (channel) {
+ ? vAPI.tabs.manager
+ .tabIdFromTarget(tabBrowser._getTabForContentWindow(win))
+ : vAPI.noTabId;
+ },
+ rawtypeFromContentType: function (channel) {
let mime = channel.contentType;
if (!mime) {
- return 0;
+ return 0;
}
-
+
let pos = mime.indexOf('/');
if (pos === -1) {
- pos = mime.length;
+ pos = mime.length;
}
-
+
return this.mimeTypeMap[mime.slice(0, pos)] || 0;
- },
- observe: function (channel, topic) {
+ },
+ observe: function (channel, topic) {
if (channel instanceof Ci.nsIHttpChannel === false) {
- return;
+ return;
}
let URI = channel.URI;
let channelData = this.channelDataFromChannel(channel);
if (topic.lastIndexOf('http-on-examine-', 0) === 0) {
- if (channelData === null) {
+ if (channelData === null) {
return;
- }
+ }
- let type = this.frameTypeMap[channelData[1]];
- if (!type) {
+ let type = this.frameTypeMap[channelData[1]];
+ if (!type) {
return;
- }
-
- // topic = ['Content-Security-Policy',
- // 'Content-Security-Policy-Report-Only'];
- //
- // Can send empty responseHeaders as these headers are
- // only added to and then merged.
- //
- // TODO: Find better place for this, needs to be set
- // before onHeadersReceived.callback. Web workers not
- // blocked in Pale Moon as child-src currently
- // unavailable, see:
- //
- // https://github.com/MoonchildProductions/Pale-Moon/issues/949
- //
- // eMatrix: as of Pale Moon 28 it seems child-src is
- // available and depracated(?)
- if (ηMatrix.cspNoWorker === undefined) {
+ }
+
+ // topic = ['Content-Security-Policy',
+ // 'Content-Security-Policy-Report-Only'];
+ //
+ // Can send empty responseHeaders as these headers are
+ // only added to and then merged.
+ //
+ // TODO: Find better place for this, needs to be set
+ // before onHeadersReceived.callback. Web workers not
+ // blocked in Pale Moon as child-src currently
+ // unavailable, see:
+ //
+ // https://github.com/MoonchildProductions/Pale-Moon/issues/949
+ //
+ // eMatrix: as of Pale Moon 28 it seems child-src is
+ // available and depracated(?)
+ if (ηMatrix.cspNoWorker === undefined) {
// ηMatrix.cspNoWorker = "child-src 'none'; "
- // +"frame-src data: blob: *; "
- // +"report-uri about:blank";
- ηMatrix.cspNoWorker = "worker-src 'none'; "
- +"frame-src data: blob: *; "
- +"report-uri about:blank";
- }
-
- let result = vAPI.net.onHeadersReceived.callback({
+ // +"frame-src data: blob: *; "
+ // +"report-uri about:blank";
+ ηMatrix.cspNoWorker = "worker-src 'none'; "
+ +"frame-src data: blob: *; "
+ +"report-uri about:blank";
+ }
+
+ let result = vAPI.net.onHeadersReceived.callback({
parentFrameId: type === 'main_frame' ? -1 : 0,
responseHeaders: [],
tabId: channelData[0],
type: type,
url: URI.asciiSpec
- });
+ });
- if (result) {
+ if (result) {
for (let header of result.responseHeaders) {
- channel.setResponseHeader(header.name,
- header.value,
- true);
+ channel.setResponseHeader(header.name,
+ header.value,
+ true);
}
- }
-
- return;
+ }
+
+ return;
}
// http-on-modify-request
// The channel was previously serviced.
if (channelData !== null) {
- this.handleRequest(channel, URI,
- channelData[0], channelData[1]);
- return;
+ this.handleRequest(channel, URI,
+ channelData[0], channelData[1]);
+ return;
}
// The channel was never serviced.
@@ -466,105 +466,105 @@
// https://github.com/gorhill/uMatrix/issues/390#issuecomment-155717004
if (loadInfo) {
- rawType = (loadInfo.externalContentPolicyType !== undefined)
- ? loadInfo.externalContentPolicyType
- : loadInfo.contentPolicyType;
- if (!rawType) {
+ rawType = (loadInfo.externalContentPolicyType !== undefined)
+ ? loadInfo.externalContentPolicyType
+ : loadInfo.contentPolicyType;
+ if (!rawType) {
rawType = 1;
- }
+ }
}
if (pendingRequest !== null) {
- tabId = pendingRequest.tabId;
- // https://github.com/gorhill/uBlock/issues/654
- // Use the request type from the HTTP observer point of view.
- if (rawType !== 1) {
+ tabId = pendingRequest.tabId;
+ // https://github.com/gorhill/uBlock/issues/654
+ // Use the request type from the HTTP observer point of view.
+ if (rawType !== 1) {
pendingRequest.rawType = rawType;
- } else {
+ } else {
rawType = pendingRequest.rawType;
- }
+ }
} else {
- tabId = this.tabIdFromChannel(channel);
+ tabId = this.tabIdFromChannel(channel);
}
if (this.handleRequest(channel, URI, tabId, rawType)) {
- return;
+ return;
}
if (channel instanceof Ci.nsIWritablePropertyBag === false) {
- return;
+ return;
}
// Carry data for behind-the-scene redirects
channel.setProperty(this.REQDATAKEY, [tabId, rawType]);
- },
- asyncOnChannelRedirect: function (oldChannel, newChannel,
- flags, callback) {
- // contentPolicy.shouldLoad doesn't detect redirects, this
- // needs to be used
+ },
+ asyncOnChannelRedirect: function (oldChannel, newChannel,
+ flags, callback) {
+ // contentPolicy.shouldLoad doesn't detect redirects, this
+ // needs to be used
// If error thrown, the redirect will fail
try {
- let URI = newChannel.URI;
- if (!URI.schemeIs('http') && !URI.schemeIs('https')) {
+ let URI = newChannel.URI;
+ if (!URI.schemeIs('http') && !URI.schemeIs('https')) {
return;
- }
+ }
- if (newChannel instanceof Ci.nsIWritablePropertyBag === false) {
+ if (newChannel instanceof Ci.nsIWritablePropertyBag === false) {
return;
- }
+ }
- let channelData = this.channelDataFromChannel(oldChannel);
- if (channelData === null) {
+ let channelData = this.channelDataFromChannel(oldChannel);
+ if (channelData === null) {
return;
- }
+ }
- // Carry the data on in case of multiple redirects
- newChannel.setProperty(this.REQDATAKEY, channelData);
+ // Carry the data on in case of multiple redirects
+ newChannel.setProperty(this.REQDATAKEY, channelData);
} catch (ex) {
- // console.error(ex);
- // Ignore
+ // console.error(ex);
+ // Ignore
} finally {
- callback.onRedirectVerifyCallback(this.ACCEPT);
+ callback.onRedirectVerifyCallback(this.ACCEPT);
}
- }
+ }
};
vAPI.toolbarButton = {
- id: location.host + '-button',
- type: 'view',
- viewId: location.host + '-panel',
- label: vAPI.app.name,
- tooltiptext: vAPI.app.name,
- tabs: {/*tabId: {badge: 0, img: boolean}*/},
- init: null,
- codePath: ''
+ id: location.host + '-button',
+ type: 'view',
+ viewId: location.host + '-panel',
+ label: vAPI.app.name,
+ tooltiptext: vAPI.app.name,
+ tabs: {/*tabId: {badge: 0, img: boolean}*/},
+ init: null,
+ codePath: ''
};
// Non-Fennec: common code paths.
(function () {
- if (vAPI.fennec) {
+ if (vAPI.fennec) {
return;
- }
+ }
- let tbb = vAPI.toolbarButton;
- let popupCommittedWidth = 0;
- let popupCommittedHeight = 0;
+ let tbb = vAPI.toolbarButton;
+ let popupCommittedWidth = 0;
+ let popupCommittedHeight = 0;
- tbb.onViewShowing = function ({target}) {
+ tbb.onViewShowing = function ({target}) {
popupCommittedWidth = popupCommittedHeight = 0;
target.firstChild.setAttribute('src', vAPI.getURL('popup.html'));
- };
+ };
- tbb.onViewHiding = function ({target}) {
+ tbb.onViewHiding = function ({target}) {
target.parentNode.style.maxWidth = '';
target.firstChild.setAttribute('src', 'about:blank');
- };
+ };
- tbb.updateState = function (win, tabId) {
+ tbb.updateState = function (win, tabId) {
let button = win.document.getElementById(this.id);
if (!button) {
- return;
+ return;
}
let icon = this.tabs[tabId];
@@ -574,9 +574,9 @@
let iconId = icon && icon.img ? icon.img : 'off';
icon = 'url(' + vAPI.getURL('img/browsericons/icon19-' + iconId + '.png') + ')';
button.style.listStyleImage = icon;
- };
+ };
- tbb.populatePanel = function (doc, panel) {
+ tbb.populatePanel = function (doc, panel) {
panel.setAttribute('id', this.viewId);
let iframe = doc.createElement('iframe');
@@ -585,135 +585,135 @@
panel.appendChild(iframe);
let toPx = function (pixels) {
- return pixels.toString() + 'px';
+ return pixels.toString() + 'px';
};
let scrollBarWidth = 0;
let resizeTimer = null;
let resizePopupDelayed = function (attempts) {
- if (resizeTimer !== null) {
+ if (resizeTimer !== null) {
return false;
- }
+ }
- // Sanity check
- attempts = (attempts || 0) + 1;
- if ( attempts > 1/*000*/ ) {
+ // Sanity check
+ attempts = (attempts || 0) + 1;
+ if ( attempts > 1/*000*/ ) {
//console.error('eMatrix> resizePopupDelayed: giving up after too many attempts');
return false;
- }
+ }
- resizeTimer = vAPI.setTimeout(resizePopup, 10, attempts);
- return true;
+ resizeTimer = vAPI.setTimeout(resizePopup, 10, attempts);
+ return true;
};
let resizePopup = function (attempts) {
- resizeTimer = null;
+ resizeTimer = null;
- panel.parentNode.style.maxWidth = 'none';
- let body = iframe.contentDocument.body;
+ panel.parentNode.style.maxWidth = 'none';
+ let body = iframe.contentDocument.body;
- // https://github.com/gorhill/uMatrix/issues/301
- // Don't resize if committed size did not change.
- if (popupCommittedWidth === body.clientWidth
- && popupCommittedHeight === body.clientHeight) {
+ // https://github.com/gorhill/uMatrix/issues/301
+ // Don't resize if committed size did not change.
+ if (popupCommittedWidth === body.clientWidth
+ && popupCommittedHeight === body.clientHeight) {
return;
- }
+ }
- // We set a limit for height
- let height = Math.min(body.clientHeight, 600);
+ // We set a limit for height
+ let height = Math.min(body.clientHeight, 600);
- // https://github.com/chrisaljoudi/uBlock/issues/730
- // Voodoo programming: this recipe works
- panel.style.setProperty('height', toPx(height));
- iframe.style.setProperty('height', toPx(height));
+ // https://github.com/chrisaljoudi/uBlock/issues/730
+ // Voodoo programming: this recipe works
+ panel.style.setProperty('height', toPx(height));
+ iframe.style.setProperty('height', toPx(height));
- // Adjust width for presence/absence of vertical scroll bar which may
- // have appeared as a result of last operation.
- let contentWindow = iframe.contentWindow;
- let width = body.clientWidth;
- if (contentWindow.scrollMaxY !== 0) {
+ // Adjust width for presence/absence of vertical scroll bar which may
+ // have appeared as a result of last operation.
+ let contentWindow = iframe.contentWindow;
+ let width = body.clientWidth;
+ if (contentWindow.scrollMaxY !== 0) {
width += scrollBarWidth;
- }
- panel.style.setProperty('width', toPx(width));
+ }
+ panel.style.setProperty('width', toPx(width));
- // scrollMaxX should always be zero once we know the scrollbar width
- if (contentWindow.scrollMaxX !== 0) {
+ // scrollMaxX should always be zero once we know the scrollbar width
+ if (contentWindow.scrollMaxX !== 0) {
scrollBarWidth = contentWindow.scrollMaxX;
width += scrollBarWidth;
panel.style.setProperty('width', toPx(width));
- }
+ }
- if (iframe.clientHeight !== height
- || panel.clientWidth !== width) {
+ if (iframe.clientHeight !== height
+ || panel.clientWidth !== width) {
if (resizePopupDelayed(attempts)) {
- return;
+ return;
}
// resizePopupDelayed won't be called again, so commit
// dimentsions.
- }
+ }
- popupCommittedWidth = body.clientWidth;
- popupCommittedHeight = body.clientHeight;
+ popupCommittedWidth = body.clientWidth;
+ popupCommittedHeight = body.clientHeight;
};
let onResizeRequested = function () {
- let body = iframe.contentDocument.body;
- if (body.getAttribute('data-resize-popup') !== 'true') {
+ let body = iframe.contentDocument.body;
+ if (body.getAttribute('data-resize-popup') !== 'true') {
return;
- }
- body.removeAttribute('data-resize-popup');
- resizePopupDelayed();
+ }
+ body.removeAttribute('data-resize-popup');
+ resizePopupDelayed();
};
let onPopupReady = function () {
- let win = this.contentWindow;
+ let win = this.contentWindow;
- if (!win || win.location.host !== location.host) {
+ if (!win || win.location.host !== location.host) {
return;
- }
+ }
- if (typeof tbb.onBeforePopupReady === 'function') {
+ if (typeof tbb.onBeforePopupReady === 'function') {
tbb.onBeforePopupReady.call(this);
- }
+ }
- resizePopupDelayed();
+ resizePopupDelayed();
- let body = win.document.body;
- body.removeAttribute('data-resize-popup');
-
- let mutationObserver =
- new win.MutationObserver(onResizeRequested);
-
- mutationObserver.observe(body, {
+ let body = win.document.body;
+ body.removeAttribute('data-resize-popup');
+
+ let mutationObserver =
+ new win.MutationObserver(onResizeRequested);
+
+ mutationObserver.observe(body, {
attributes: true,
attributeFilter: [ 'data-resize-popup' ]
- });
+ });
};
iframe.addEventListener('load', onPopupReady, true);
- };
+ };
})();
/******************************************************************************/
(function () {
- // Add toolbar button for not-Basilisk
- if (Services.appinfo.ID === "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}") {
- return;
- }
-
- let tbb = vAPI.toolbarButton;
- if (tbb.init !== null) {
+ // Add toolbar button for not-Basilisk
+ if (Services.appinfo.ID === "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}") {
return;
- }
+ }
- tbb.codePath = 'legacy';
- tbb.viewId = tbb.id + '-panel';
+ let tbb = vAPI.toolbarButton;
+ if (tbb.init !== null) {
+ return;
+ }
- let styleSheetUri = null;
+ tbb.codePath = 'legacy';
+ tbb.viewId = tbb.id + '-panel';
- let createToolbarButton = function (window) {
+ let styleSheetUri = null;
+
+ let createToolbarButton = function (window) {
let document = window.document;
let toolbarButton = document.createElement('toolbarbutton');
@@ -722,40 +722,40 @@
toolbarButton.setAttribute('type', 'menu');
toolbarButton.setAttribute('removable', 'true');
toolbarButton.setAttribute('class', 'toolbarbutton-1 '
- +'chromeclass-toolbar-additional');
+ +'chromeclass-toolbar-additional');
toolbarButton.setAttribute('label', tbb.label);
- toolbarButton.setAttribute('tooltiptext', tbb.tooltiptext);
+ toolbarButton.setAttribute('tooltiptext', tbb.tooltiptext);
let toolbarButtonPanel = document.createElement('panel');
// NOTE: Setting level to parent breaks the popup for PaleMoon under
// linux (mouse pointer misaligned with content). For some reason.
- // eMatrix: TODO check if it's still true
+ // eMatrix: TODO check if it's still true
// toolbarButtonPanel.setAttribute('level', 'parent');
tbb.populatePanel(document, toolbarButtonPanel);
toolbarButtonPanel.addEventListener('popupshowing',
- tbb.onViewShowing);
+ tbb.onViewShowing);
toolbarButtonPanel.addEventListener('popuphiding',
- tbb.onViewHiding);
+ tbb.onViewHiding);
toolbarButton.appendChild(toolbarButtonPanel);
- toolbarButtonPanel.setAttribute('tooltiptext', '');
+ toolbarButtonPanel.setAttribute('tooltiptext', '');
return toolbarButton;
- };
+ };
- let addLegacyToolbarButton = function (window) {
+ let addLegacyToolbarButton = function (window) {
// eMatrix's stylesheet lazily added.
if (styleSheetUri === null) {
- var sss = Cc["@mozilla.org/content/style-sheet-service;1"]
+ var sss = Cc["@mozilla.org/content/style-sheet-service;1"]
.getService(Ci.nsIStyleSheetService);
- styleSheetUri = Services.io
- .newURI(vAPI.getURL("css/legacy-toolbar-button.css"),
- null, null);
+ styleSheetUri = Services.io
+ .newURI(vAPI.getURL("css/legacy-toolbar-button.css"),
+ null, null);
- // Register global so it works in all windows, including palette
- if (!sss.sheetRegistered(styleSheetUri, sss.AUTHOR_SHEET)) {
+ // Register global so it works in all windows, including palette
+ if (!sss.sheetRegistered(styleSheetUri, sss.AUTHOR_SHEET)) {
sss.loadAndRegisterSheet(styleSheetUri, sss.AUTHOR_SHEET);
- }
+ }
}
let document = window.document;
@@ -763,21 +763,21 @@
// https://github.com/gorhill/uMatrix/issues/357
// Already installed?
if (document.getElementById(tbb.id) !== null) {
- return;
+ return;
}
let toolbox = document.getElementById('navigator-toolbox')
- || document.getElementById('mail-toolbox');
-
+ || document.getElementById('mail-toolbox');
+
if (toolbox === null) {
- return;
+ return;
}
let toolbarButton = createToolbarButton(window);
let palette = toolbox.palette;
if (palette && palette.querySelector('#' + tbb.id) === null) {
- palette.appendChild(toolbarButton);
+ palette.appendChild(toolbarButton);
}
// Find the place to put the button.
@@ -785,75 +785,75 @@
// undefined. Seen while testing popup test number 3:
// http://raymondhill.net/ublock/popup.html
let toolbars = toolbox.externalToolbars
- ? toolbox.externalToolbars.slice()
- : [];
-
+ ? toolbox.externalToolbars.slice()
+ : [];
+
for (let child of toolbox.children) {
- if (child.localName === 'toolbar') {
+ if (child.localName === 'toolbar') {
toolbars.push(child);
- }
+ }
}
for (let toolbar of toolbars) {
- let currentsetString = toolbar.getAttribute('currentset');
- if (!currentsetString) {
+ let currentsetString = toolbar.getAttribute('currentset');
+ if (!currentsetString) {
continue;
- }
-
- let currentset = currentsetString.split(/\s*,\s*/);
- let index = currentset.indexOf(tbb.id);
- if (index === -1) {
+ }
+
+ let currentset = currentsetString.split(/\s*,\s*/);
+ let index = currentset.indexOf(tbb.id);
+ if (index === -1) {
continue;
- }
-
- // This can occur with Pale Moon:
- // "TypeError: toolbar.insertItem is not a function"
- if (typeof toolbar.insertItem !== 'function') {
+ }
+
+ // This can occur with Pale Moon:
+ // "TypeError: toolbar.insertItem is not a function"
+ if (typeof toolbar.insertItem !== 'function') {
continue;
- }
-
- // Found our button on this toolbar - but where on it?
- let before = null;
- for (let i = index+1; i<currentset.length; ++i) {
- // The [id=...] notation doesn't work on
- // space elements as they get a random ID each session
- // (or something like that)
- // https://libregit.org/heckyel/ematrix/issues/5
- // https://libregit.org/heckyel/ematrix/issues/6
-
- // Based on JustOff's snippet from the Pale Moon
- // forum. It was reorganized because I find it
- // more readable like this, but he did most of the
- // work.
- let space = /^(spring|spacer|separator)$/.exec(currentset[i]);
- if (space !== null) {
- let elems = toolbar.querySelectorAll('toolbar'+space[1]);
-
- let count = currentset.slice(i-currentset.length)
- .filter(function (x) {return x == space[1];})
- .length;
-
- before =
- toolbar.querySelector('[id="'
- + elems[elems.length-count].id
- + '"]');
- } else {
- before = toolbar.querySelector('[id="'+currentset[i]+'"]');
- }
+ }
+
+ // Found our button on this toolbar - but where on it?
+ let before = null;
+ for (let i = index+1; i<currentset.length; ++i) {
+ // The [id=...] notation doesn't work on
+ // space elements as they get a random ID each session
+ // (or something like that)
+ // https://libregit.org/heckyel/ematrix/issues/5
+ // https://libregit.org/heckyel/ematrix/issues/6
+
+ // Based on JustOff's snippet from the Pale Moon
+ // forum. It was reorganized because I find it
+ // more readable like this, but he did most of the
+ // work.
+ let space = /^(spring|spacer|separator)$/.exec(currentset[i]);
+ if (space !== null) {
+ let elems = toolbar.querySelectorAll('toolbar'+space[1]);
+
+ let count = currentset.slice(i-currentset.length)
+ .filter(function (x) {return x == space[1];})
+ .length;
+
+ before =
+ toolbar.querySelector('[id="'
+ + elems[elems.length-count].id
+ + '"]');
+ } else {
+ before = toolbar.querySelector('[id="'+currentset[i]+'"]');
+ }
if ( before !== null ) {
- break;
+ break;
}
- }
+ }
- toolbar.insertItem(tbb.id, before);
- break;
+ toolbar.insertItem(tbb.id, before);
+ break;
}
// https://github.com/gorhill/uBlock/issues/763
// We are done if our toolbar button is already installed
// in one of the toolbar.
if (palette !== null && toolbarButton.parentElement !== palette) {
- return;
+ return;
}
// No button yet so give it a default location. If forcing
@@ -862,181 +862,181 @@
// available or visible!)
let navbar = document.getElementById('nav-bar');
if (navbar !== null
- && !vAPI.localStorage.getBool('legacyToolbarButtonAdded')) {
- // https://github.com/gorhill/uBlock/issues/264
- // Find a child customizable palette, if any.
- navbar = navbar.querySelector('.customization-target') || navbar;
- navbar.appendChild(toolbarButton);
- navbar.setAttribute('currentset', navbar.currentSet);
- document.persist(navbar.id, 'currentset');
- vAPI.localStorage.setBool('legacyToolbarButtonAdded', 'true');
+ && !vAPI.localStorage.getBool('legacyToolbarButtonAdded')) {
+ // https://github.com/gorhill/uBlock/issues/264
+ // Find a child customizable palette, if any.
+ navbar = navbar.querySelector('.customization-target') || navbar;
+ navbar.appendChild(toolbarButton);
+ navbar.setAttribute('currentset', navbar.currentSet);
+ document.persist(navbar.id, 'currentset');
+ vAPI.localStorage.setBool('legacyToolbarButtonAdded', 'true');
}
- };
+ };
- let canAddLegacyToolbarButton = function (window) {
+ let canAddLegacyToolbarButton = function (window) {
let document = window.document;
if (!document
- || document.readyState !== 'complete'
- || document.getElementById('nav-bar') === null) {
- return false;
+ || document.readyState !== 'complete'
+ || document.getElementById('nav-bar') === null) {
+ return false;
}
-
+
let toolbox = document.getElementById('navigator-toolbox')
- || document.getElementById('mail-toolbox');
+ || document.getElementById('mail-toolbox');
return toolbox !== null && !!toolbox.palette;
- };
+ };
- let onPopupCloseRequested = function ({target}) {
+ let onPopupCloseRequested = function ({target}) {
let document = target.ownerDocument;
if (!document) {
- return;
+ return;
}
-
+
let toolbarButtonPanel = document.getElementById(tbb.viewId);
if (toolbarButtonPanel === null) {
- return;
+ return;
}
-
+
// `hidePopup` reported as not existing while testing
// legacy button on FF 41.0.2.
// https://bugzilla.mozilla.org/show_bug.cgi?id=1151796
if (typeof toolbarButtonPanel.hidePopup === 'function') {
- toolbarButtonPanel.hidePopup();
+ toolbarButtonPanel.hidePopup();
}
- };
+ };
- let shutdown = function () {
+ let shutdown = function () {
for (let win of vAPI.window.getWindows()) {
- let toolbarButton = win.document.getElementById(tbb.id);
- if (toolbarButton) {
+ let toolbarButton = win.document.getElementById(tbb.id);
+ if (toolbarButton) {
toolbarButton.parentNode.removeChild(toolbarButton);
- }
+ }
}
if (styleSheetUri !== null) {
- var sss = Cc["@mozilla.org/content/style-sheet-service;1"]
+ var sss = Cc["@mozilla.org/content/style-sheet-service;1"]
.getService(Ci.nsIStyleSheetService);
- if (sss.sheetRegistered(styleSheetUri, sss.AUTHOR_SHEET)) {
+ if (sss.sheetRegistered(styleSheetUri, sss.AUTHOR_SHEET)) {
sss.unregisterSheet(styleSheetUri, sss.AUTHOR_SHEET);
- }
- styleSheetUri = null;
+ }
+ styleSheetUri = null;
}
vAPI.messaging.globalMessageManager
- .removeMessageListener(location.host + ':closePopup',
- onPopupCloseRequested);
- };
+ .removeMessageListener(location.host + ':closePopup',
+ onPopupCloseRequested);
+ };
- tbb.attachToNewWindow = function (win) {
+ tbb.attachToNewWindow = function (win) {
vAPI.deferUntil(canAddLegacyToolbarButton.bind(null, win),
- addLegacyToolbarButton.bind(null, win));
- };
+ addLegacyToolbarButton.bind(null, win));
+ };
- tbb.init = function () {
+ tbb.init = function () {
vAPI.messaging.globalMessageManager
- .addMessageListener(location.host + ':closePopup',
- onPopupCloseRequested);
-
+ .addMessageListener(location.host + ':closePopup',
+ onPopupCloseRequested);
+
vAPI.addCleanUpTask(shutdown);
- };
+ };
})();
(function() {
- // Add toolbar button for Basilisk
- if (Services.appinfo.ID !== "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}") {
- return;
- }
-
- let tbb = vAPI.toolbarButton;
- if (tbb.init !== null) {
+ // Add toolbar button for Basilisk
+ if (Services.appinfo.ID !== "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}") {
return;
- }
- // if ( Services.vc.compare(Services.appinfo.version, '36.0') < 0 ) {
- // return null;
- // }
- let CustomizableUI = null;
- try {
+ }
+
+ let tbb = vAPI.toolbarButton;
+ if (tbb.init !== null) {
+ return;
+ }
+ // if ( Services.vc.compare(Services.appinfo.version, '36.0') < 0 ) {
+ // return null;
+ // }
+ let CustomizableUI = null;
+ try {
CustomizableUI =
- Cu.import('resource:///modules/CustomizableUI.jsm', null)
- .CustomizableUI;
- } catch (ex) {
- // Ignore
- }
- if (CustomizableUI === null) {
+ Cu.import('resource:///modules/CustomizableUI.jsm', null)
+ .CustomizableUI;
+ } catch (ex) {
+ // Ignore
+ }
+ if (CustomizableUI === null) {
return null;
- }
- tbb.codePath = 'australis';
- tbb.CustomizableUI = CustomizableUI;
- tbb.defaultArea = CustomizableUI.AREA_NAVBAR;
+ }
+ tbb.codePath = 'australis';
+ tbb.CustomizableUI = CustomizableUI;
+ tbb.defaultArea = CustomizableUI.AREA_NAVBAR;
- let CUIEvents = {};
+ let CUIEvents = {};
- let badgeCSSRules = 'background: #000;color: #fff';
+ let badgeCSSRules = 'background: #000;color: #fff';
- let updateBadgeStyle = function () {
+ let updateBadgeStyle = function () {
for (let win of vAPI.window.getWindows()) {
- let button = win.document.getElementById(tbb.id);
- if (button === null) {
+ let button = win.document.getElementById(tbb.id);
+ if (button === null) {
continue;
- }
- let badge = button.ownerDocument
- .getAnonymousElementByAttribute(button,
- 'class',
- 'toolbarbutton-badge');
- if (!badge) {
+ }
+ let badge = button.ownerDocument
+ .getAnonymousElementByAttribute(button,
+ 'class',
+ 'toolbarbutton-badge');
+ if (!badge) {
continue;
- }
+ }
- badge.style.cssText = badgeCSSRules;
+ badge.style.cssText = badgeCSSRules;
}
- };
+ };
- let updateBadge = function () {
+ let updateBadge = function () {
let wId = tbb.id;
let buttonInPanel =
- CustomizableUI.getWidget(wId).areaType
- === CustomizableUI.TYPE_MENU_PANEL;
+ CustomizableUI.getWidget(wId).areaType
+ === CustomizableUI.TYPE_MENU_PANEL;
for (let win of vAPI.window.getWindows()) {
- let button = win.document.getElementById(wId);
- if (button === null) {
+ let button = win.document.getElementById(wId);
+ if (button === null) {
continue;
- }
-
- if (buttonInPanel) {
+ }
+
+ if (buttonInPanel) {
button.classList.remove('badged-button');
continue;
- }
-
- button.classList.add('badged-button');
+ }
+
+ button.classList.add('badged-button');
}
if (buttonInPanel) {
- return;
+ return;
}
// Anonymous elements need some time to be reachable
vAPI.setTimeout(updateBadgeStyle, 250);
- }.bind(CUIEvents);
+ }.bind(CUIEvents);
- CUIEvents.onCustomizeEnd = updateBadge;
- CUIEvents.onWidgetAdded = updateBadge;
- CUIEvents.onWidgetUnderflow = updateBadge;
+ CUIEvents.onCustomizeEnd = updateBadge;
+ CUIEvents.onWidgetAdded = updateBadge;
+ CUIEvents.onWidgetUnderflow = updateBadge;
- let onPopupCloseRequested = function ({target}) {
+ let onPopupCloseRequested = function ({target}) {
if (typeof tbb.closePopup === 'function') {
- tbb.closePopup(target);
+ tbb.closePopup(target);
}
- };
+ };
- let shutdown = function () {
+ let shutdown = function () {
for (let win of vAPI.window.getWindows()) {
- let panel = win.document.getElementById(tbb.viewId);
- if (panel !== null && panel.parentNode !== null) {
+ let panel = win.document.getElementById(tbb.viewId);
+ if (panel !== null && panel.parentNode !== null) {
panel.parentNode.removeChild(panel);
- }
-
- win.QueryInterface(Ci.nsIInterfaceRequestor)
+ }
+
+ win.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils)
.removeSheet(styleURI, 1);
}
@@ -1045,13 +1045,13 @@
CustomizableUI.destroyWidget(tbb.id);
vAPI.messaging.globalMessageManager
- .removeMessageListener(location.host + ':closePopup',
- onPopupCloseRequested);
- };
+ .removeMessageListener(location.host + ':closePopup',
+ onPopupCloseRequested);
+ };
- let styleURI = null;
+ let styleURI = null;
- tbb.onBeforeCreated = function (doc) {
+ tbb.onBeforeCreated = function (doc) {
let panel = doc.createElement('panelview');
this.populatePanel(doc, panel);
@@ -1059,189 +1059,189 @@
doc.getElementById('PanelUI-multiView').appendChild(panel);
doc.defaultView.QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIDOMWindowUtils)
- .loadSheet(styleURI, 1);
- };
+ .getInterface(Ci.nsIDOMWindowUtils)
+ .loadSheet(styleURI, 1);
+ };
- tbb.onCreated = function (button) {
+ tbb.onCreated = function (button) {
button.setAttribute('badge', '');
vAPI.setTimeout(updateBadge, 250);
- };
+ };
- tbb.onBeforePopupReady = function () {
+ tbb.onBeforePopupReady = function () {
// https://github.com/gorhill/uBlock/issues/83
// Add `portrait` class if width is constrained.
try {
- this.contentDocument.body
- .classList.toggle('portrait',
- CustomizableUI.getWidget(tbb.id).areaType
- === CustomizableUI.TYPE_MENU_PANEL);
+ this.contentDocument.body
+ .classList.toggle('portrait',
+ CustomizableUI.getWidget(tbb.id).areaType
+ === CustomizableUI.TYPE_MENU_PANEL);
} catch (ex) {
- // Ignore
+ // Ignore
}
- };
+ };
- tbb.closePopup = function (tabBrowser) {
+ tbb.closePopup = function (tabBrowser) {
CustomizableUI.hidePanelForNode(tabBrowser
- .ownerDocument
- .getElementById(tbb.viewId));
- };
+ .ownerDocument
+ .getElementById(tbb.viewId));
+ };
- tbb.init = function () {
+ tbb.init = function () {
vAPI.messaging.globalMessageManager
- .addMessageListener(location.host + ':closePopup',
- onPopupCloseRequested);
+ .addMessageListener(location.host + ':closePopup',
+ onPopupCloseRequested);
CustomizableUI.addListener(CUIEvents);
var style = [
- '#' + this.id + '.off {',
+ '#' + this.id + '.off {',
'list-style-image: url(',
vAPI.getURL('img/browsericons/icon19-off.png'),
');',
- '}',
- '#' + this.id + ' {',
+ '}',
+ '#' + this.id + ' {',
'list-style-image: url(',
vAPI.getURL('img/browsericons/icon19-19.png'),
');',
- '}',
- '#' + this.viewId + ', #' + this.viewId + ' > iframe {',
+ '}',
+ '#' + this.viewId + ', #' + this.viewId + ' > iframe {',
'height: 290px;',
'max-width: none !important;',
'min-width: 0 !important;',
'overflow: hidden !important;',
'padding: 0 !important;',
'width: 160px;',
- '}'
+ '}'
];
styleURI =
- Services.io.newURI('data:text/css,'
- +encodeURIComponent(style.join('')),
- null,
- null);
+ Services.io.newURI('data:text/css,'
+ +encodeURIComponent(style.join('')),
+ null,
+ null);
CustomizableUI.createWidget(this);
vAPI.addCleanUpTask(shutdown);
- };
+ };
})();
// No toolbar button.
(function () {
- // Just to ensure the number of cleanup tasks is as expected: toolbar
- // button code is one single cleanup task regardless of platform.
- // eMatrix: might not be needed anymore
- if (vAPI.toolbarButton.init === null) {
+ // Just to ensure the number of cleanup tasks is as expected: toolbar
+ // button code is one single cleanup task regardless of platform.
+ // eMatrix: might not be needed anymore
+ if (vAPI.toolbarButton.init === null) {
vAPI.addCleanUpTask(function(){});
- }
+ }
})();
if (vAPI.toolbarButton.init !== null) {
- vAPI.toolbarButton.init();
+ vAPI.toolbarButton.init();
}
let optionsObserver = (function () {
- let addonId = 'eMatrix@vannilla.org';
+ let addonId = 'eMatrix@vannilla.org';
- let commandHandler = function () {
+ let commandHandler = function () {
switch (this.id) {
case 'showDashboardButton':
- vAPI.tabs.open({
- url: 'dashboard.html',
- index: -1,
- });
- break;
+ vAPI.tabs.open({
+ url: 'dashboard.html',
+ index: -1,
+ });
+ break;
case 'showLoggerButton':
- vAPI.tabs.open({
- url: 'logger-ui.html',
- index: -1,
- });
- break;
+ vAPI.tabs.open({
+ url: 'logger-ui.html',
+ index: -1,
+ });
+ break;
default:
- break;
+ break;
}
- };
+ };
- let setupOptionsButton = function (doc, id) {
+ let setupOptionsButton = function (doc, id) {
let button = doc.getElementById(id);
if (button === null) {
- return;
+ return;
}
button.addEventListener('command', commandHandler);
button.label = vAPI.i18n(id);
- };
+ };
- let setupOptionsButtons = function (doc) {
+ let setupOptionsButtons = function (doc) {
setupOptionsButton(doc, 'showDashboardButton');
setupOptionsButton(doc, 'showLoggerButton');
- };
+ };
- let observer = {
+ let observer = {
observe: function (doc, topic, id) {
- if (id !== addonId) {
+ if (id !== addonId) {
return;
- }
+ }
- setupOptionsButtons(doc);
+ setupOptionsButtons(doc);
}
- };
+ };
- var canInit = function() {
- // https://github.com/gorhill/uBlock/issues/948
- // Older versions of Firefox can throw here when looking
- // up `currentURI`.
+ var canInit = function() {
+ // https://github.com/gorhill/uBlock/issues/948
+ // Older versions of Firefox can throw here when looking
+ // up `currentURI`.
try {
- let tabBrowser = vAPI.tabs.manager.currentBrowser();
- return tabBrowser
- && tabBrowser.currentURI
- && tabBrowser.currentURI.spec === 'about:addons'
- && tabBrowser.contentDocument
- && tabBrowser.contentDocument.readyState === 'complete';
+ let tabBrowser = vAPI.tabs.manager.currentBrowser();
+ return tabBrowser
+ && tabBrowser.currentURI
+ && tabBrowser.currentURI.spec === 'about:addons'
+ && tabBrowser.contentDocument
+ && tabBrowser.contentDocument.readyState === 'complete';
} catch (ex) {
- // Ignore
+ // Ignore
}
- };
+ };
- // Manually add the buttons if the `about:addons` page is
- // already opened.
- let init = function () {
+ // Manually add the buttons if the `about:addons` page is
+ // already opened.
+ let init = function () {
if (canInit()) {
- setupOptionsButtons(vAPI.tabs.manager
- .currentBrowser().contentDocument);
+ setupOptionsButtons(vAPI.tabs.manager
+ .currentBrowser().contentDocument);
}
- };
+ };
- let unregister = function () {
+ let unregister = function () {
Services.obs.removeObserver(observer, 'addon-options-displayed');
- };
+ };
- let register = function () {
+ let register = function () {
Services.obs.addObserver(observer,
- 'addon-options-displayed',
- false);
+ 'addon-options-displayed',
+ false);
vAPI.addCleanUpTask(unregister);
vAPI.deferUntil(canInit, init, { next: 463 });
- };
+ };
- return {
+ return {
register: register,
unregister: unregister
- };
+ };
})();
optionsObserver.register();
vAPI.onLoadAllCompleted = function() {
- // This is called only once, when everything has been loaded
- // in memory after the extension was launched. It can be used
- // to inject content scripts in already opened web pages, to
- // remove whatever nuisance could make it to the web pages
- // before uBlock was ready.
- for (let browser of vAPI.tabs.manager.browsers()) {
+ // This is called only once, when everything has been loaded
+ // in memory after the extension was launched. It can be used
+ // to inject content scripts in already opened web pages, to
+ // remove whatever nuisance could make it to the web pages
+ // before uBlock was ready.
+ for (let browser of vAPI.tabs.manager.browsers()) {
browser.messageManager
- .sendAsyncMessage(location.host + '-load-completed');
- }
+ .sendAsyncMessage(location.host + '-load-completed');
+ }
};
// Likelihood is that we do not have to punycode: given punycode overhead,
@@ -1250,16 +1250,16 @@
var isNotASCII = /[^\x21-\x7F]/;
vAPI.punycodeHostname = function (hostname) {
- return isNotASCII.test(hostname)
- ? punycodeHostname(hostname)
- : hostname;
+ return isNotASCII.test(hostname)
+ ? punycodeHostname(hostname)
+ : hostname;
};
vAPI.punycodeURL = function (url) {
- if (isNotASCII.test(url)) {
+ if (isNotASCII.test(url)) {
return Services.io.newURI(url, null, null).asciiSpec;
- }
-
- return url;
+ }
+
+ return url;
};
})();
diff --git a/js/vapi-browser.js b/js/vapi-browser.js
index 0a39830..7d4d4e2 100644
--- a/js/vapi-browser.js
+++ b/js/vapi-browser.js
@@ -27,54 +27,54 @@
(function () {
vAPI.browser = {};
-
+
vAPI.browser.getTabBrowser = function (win) {
- return win && win.gBrowser || null;
+ return win && win.gBrowser || null;
};
vAPI.browser.getOwnerWindow = function (target) {
- if (target.ownerDocument) {
+ if (target.ownerDocument) {
return target.ownerDocument.defaultView;
- }
-
- return null;
+ }
+
+ return null;
};
vAPI.browser.settings = {
- // For now, only booleans.
- originalValues: {},
+ // For now, only booleans.
+ originalValues: {},
- rememberOriginalValue: function (path, setting) {
+ rememberOriginalValue: function (path, setting) {
let key = path + '.' + setting;
if (this.originalValues.hasOwnProperty(key)) {
- return;
+ return;
}
-
+
let hasUserValue;
let branch = Services.prefs.getBranch(path + '.');
-
+
try {
- hasUserValue = branch.prefHasUserValue(setting);
+ hasUserValue = branch.prefHasUserValue(setting);
} catch (ex) {
- // Ignore
+ // Ignore
}
-
+
if (hasUserValue !== undefined) {
- this.originalValues[key] = hasUserValue
- ? this.getValue(path, setting)
- : undefined;
+ this.originalValues[key] = hasUserValue
+ ? this.getValue(path, setting)
+ : undefined;
}
- },
- clear: function (path, setting) {
+ },
+ clear: function (path, setting) {
let key = path + '.' + setting;
// Value was not overriden -- nothing to restore
if (this.originalValues.hasOwnProperty(key) === false) {
- return;
+ return;
}
let value = this.originalValues[key];
-
+
// https://github.com/gorhill/uBlock/issues/292#issuecomment-109621979
// Forget the value immediately, it may change outside of
// uBlock control.
@@ -82,160 +82,160 @@
// Original value was a default one
if (value === undefined) {
- try {
+ try {
Services.prefs.getBranch(path + '.').clearUserPref(setting);
- } catch (ex) {
- // Ignore
- }
- return;
+ } catch (ex) {
+ // Ignore
+ }
+ return;
}
// Reset to original value
this.setValue(path, setting, value);
- },
- getValue: function (path, setting) {
+ },
+ getValue: function (path, setting) {
let branch = Services.prefs.getBranch(path + '.');
- try {
- switch (branch.getPrefType(setting)) {
- case branch.PREF_INT:
- return branch.getIntPref(setting);
- case branch.PREF_BOOL:
- return branch.getBoolPref(setting);
- default:
- // not supported
- return;
- }
- } catch (e) {
- // Ignore
- }
- },
- setValue: function (path, setting, value) {
- let branch = Services.prefs.getBranch(path + '.');
-
- try {
- switch (typeof value) {
- case 'number':
- return branch.setIntPref(setting, value);
- case 'boolean':
- return branch.setBoolPref(setting, value);
- default:
- // not supported
- return;
- }
- } catch (e) {
- // Ignore
- }
- },
- setSetting: function (setting, value) {
+ try {
+ switch (branch.getPrefType(setting)) {
+ case branch.PREF_INT:
+ return branch.getIntPref(setting);
+ case branch.PREF_BOOL:
+ return branch.getBoolPref(setting);
+ default:
+ // not supported
+ return;
+ }
+ } catch (e) {
+ // Ignore
+ }
+ },
+ setValue: function (path, setting, value) {
+ let branch = Services.prefs.getBranch(path + '.');
+
+ try {
+ switch (typeof value) {
+ case 'number':
+ return branch.setIntPref(setting, value);
+ case 'boolean':
+ return branch.setBoolPref(setting, value);
+ default:
+ // not supported
+ return;
+ }
+ } catch (e) {
+ // Ignore
+ }
+ },
+ setSetting: function (setting, value) {
switch (setting) {
case 'prefetching':
- this.rememberOriginalValue('network', 'prefetch-next');
- // https://bugzilla.mozilla.org/show_bug.cgi?id=814169
- // Sigh.
- // eMatrix: doesn't seem the case for Pale
- // Moon/Basilisk, but let's keep this anyway
- this.rememberOriginalValue('network.http', 'speculative-parallel-limit');
-
- // https://github.com/gorhill/uBlock/issues/292
- // "true" means "do not disable", i.e. leave entry alone
- if (value) {
+ this.rememberOriginalValue('network', 'prefetch-next');
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=814169
+ // Sigh.
+ // eMatrix: doesn't seem the case for Pale
+ // Moon/Basilisk, but let's keep this anyway
+ this.rememberOriginalValue('network.http', 'speculative-parallel-limit');
+
+ // https://github.com/gorhill/uBlock/issues/292
+ // "true" means "do not disable", i.e. leave entry alone
+ if (value) {
this.clear('network', 'prefetch-next');
this.clear('network.http', 'speculative-parallel-limit');
- } else {
+ } else {
this.setValue('network', 'prefetch-next', false);
this.setValue('network.http',
- 'speculative-parallel-limit', 0);
- }
- break;
+ 'speculative-parallel-limit', 0);
+ }
+ break;
case 'hyperlinkAuditing':
- this.rememberOriginalValue('browser', 'send_pings');
- this.rememberOriginalValue('beacon', 'enabled');
-
- // https://github.com/gorhill/uBlock/issues/292
- // "true" means "do not disable", i.e. leave entry alone
- if (value) {
+ this.rememberOriginalValue('browser', 'send_pings');
+ this.rememberOriginalValue('beacon', 'enabled');
+
+ // https://github.com/gorhill/uBlock/issues/292
+ // "true" means "do not disable", i.e. leave entry alone
+ if (value) {
this.clear('browser', 'send_pings');
this.clear('beacon', 'enabled');
- } else {
+ } else {
this.setValue('browser', 'send_pings', false);
this.setValue('beacon', 'enabled', false);
- }
- break;
+ }
+ break;
case 'webrtcIPAddress':
- let prefName;
- let prefVal;
-
- // https://github.com/gorhill/uBlock/issues/894
- // Do not disable completely WebRTC if it can be avoided. FF42+
- // has a `media.peerconnection.ice.default_address_only` pref which
- // purpose is to prevent local IP address leakage.
- if (this.getValue('media.peerconnection',
- 'ice.default_address_only') !== undefined) {
+ let prefName;
+ let prefVal;
+
+ // https://github.com/gorhill/uBlock/issues/894
+ // Do not disable completely WebRTC if it can be avoided. FF42+
+ // has a `media.peerconnection.ice.default_address_only` pref which
+ // purpose is to prevent local IP address leakage.
+ if (this.getValue('media.peerconnection',
+ 'ice.default_address_only') !== undefined) {
prefName = 'ice.default_address_only';
prefVal = true;
- } else {
+ } else {
prefName = 'enabled';
prefVal = false;
- }
+ }
- this.rememberOriginalValue('media.peerconnection', prefName);
- if (value) {
+ this.rememberOriginalValue('media.peerconnection', prefName);
+ if (value) {
this.clear('media.peerconnection', prefName);
- } else {
+ } else {
this.setValue('media.peerconnection', prefName, prefVal);
- }
- break;
+ }
+ break;
default:
- break;
+ break;
}
- },
- set: function (details) {
+ },
+ set: function (details) {
for (let setting in details) {
- if (details.hasOwnProperty(setting) === false) {
+ if (details.hasOwnProperty(setting) === false) {
continue;
- }
- this.setSetting(setting, !!details[setting]);
+ }
+ this.setSetting(setting, !!details[setting]);
}
- },
- restoreAll: function () {
+ },
+ restoreAll: function () {
let pos;
for (let key in this.originalValues) {
- if (this.originalValues.hasOwnProperty(key) === false) {
+ if (this.originalValues.hasOwnProperty(key) === false) {
continue;
- }
-
- pos = key.lastIndexOf('.');
- this.clear(key.slice(0, pos), key.slice(pos + 1));
+ }
+
+ pos = key.lastIndexOf('.');
+ this.clear(key.slice(0, pos), key.slice(pos + 1));
}
- },
+ },
};
vAPI.addCleanUpTask(vAPI.browser.settings
- .restoreAll.bind(vAPI.browser.settings));
+ .restoreAll.bind(vAPI.browser.settings));
vAPI.browser.data = {};
vAPI.browser.data.clearCache = function (callback) {
- // PURGE_DISK_DATA_ONLY:1
- // PURGE_DISK_ALL:2
- // PURGE_EVERYTHING:3
- // However I verified that no argument does clear the cache data.
- // There is no cache2 for older versions of Firefox.
- if (Services.cache2) {
+ // PURGE_DISK_DATA_ONLY:1
+ // PURGE_DISK_ALL:2
+ // PURGE_EVERYTHING:3
+ // However I verified that no argument does clear the cache data.
+ // There is no cache2 for older versions of Firefox.
+ if (Services.cache2) {
Services.cache2.clear();
- } else if (Services.cache) {
+ } else if (Services.cache) {
Services.cache.evictEntries(Services.cache.STORE_ON_DISK);
- }
-
- if (typeof callback === 'function') {
+ }
+
+ if (typeof callback === 'function') {
callback();
- }
+ }
};
vAPI.browser.data.clearOrigin = function(/* domain */) {
- // TODO
- // eMatrix: is this actually needed? I don't really know what
- // it's supposed to do anyway.
+ // TODO
+ // eMatrix: is this actually needed? I don't really know what
+ // it's supposed to do anyway.
};
})();
diff --git a/js/vapi-client.js b/js/vapi-client.js
index 81ddd51..4c48d03 100644
--- a/js/vapi-client.js
+++ b/js/vapi-client.js
@@ -27,155 +27,155 @@
(function (self) {
vAPI.sessionId = String.fromCharCode(Date.now() % 25 + 97) +
- Math.random().toString(36).slice(2);
+ Math.random().toString(36).slice(2);
vAPI.shutdown = (function () {
- let jobs = [];
+ let jobs = [];
- let add = function (job) {
+ let add = function (job) {
jobs.push(job);
- };
+ };
- let exec = function () {
+ let exec = function () {
//console.debug('Shutting down...');
let job;
while ((job = jobs.pop())) {
- job();
+ job();
}
- };
+ };
- return {
+ return {
add: add,
exec: exec
- };
+ };
})();
vAPI.messaging = {
- listeners: new Set(),
- pending: new Map(),
- requestId: 1,
- connected: false,
- messageListenerCallback: null,
- toggleListenerCallback: null,
-
- start: function () {
+ listeners: new Set(),
+ pending: new Map(),
+ requestId: 1,
+ connected: false,
+ messageListenerCallback: null,
+ toggleListenerCallback: null,
+
+ start: function () {
this.addListener(this.builtinListener);
if (this.toggleListenerCallback === null) {
- this.toggleListenerCallback = this.toggleListener.bind(this);
+ this.toggleListenerCallback = this.toggleListener.bind(this);
}
-
+
window.addEventListener('pagehide',
- this.toggleListenerCallback, true);
+ this.toggleListenerCallback, true);
window.addEventListener('pageshow',
- this.toggleListenerCallback, true);
- },
- shutdown: function () {
+ this.toggleListenerCallback, true);
+ },
+ shutdown: function () {
if (this.toggleListenerCallback !== null) {
- window.removeEventListener('pagehide',
- this.toggleListenerCallback, true);
- window.removeEventListener('pageshow',
- this.toggleListenerCallback, true);
+ window.removeEventListener('pagehide',
+ this.toggleListenerCallback, true);
+ window.removeEventListener('pageshow',
+ this.toggleListenerCallback, true);
}
this.removeAllListeners();
-
+
//service pending callbacks
var pending = this.pending;
this.pending.clear();
for (let callback of pending.values()) {
- if (typeof callback === 'function') {
+ if (typeof callback === 'function') {
callback(null);
- }
+ }
}
- },
- connect: function () {
+ },
+ connect: function () {
if (!this.connected) {
- if (this.messageListenerCallback === null) {
+ if (this.messageListenerCallback === null) {
this.messageListenerCallback =
- this.messageListener.bind(this);
- }
- addMessageListener(this.messageListenerCallback);
- this.connected = true;
+ this.messageListener.bind(this);
+ }
+ addMessageListener(this.messageListenerCallback);
+ this.connected = true;
}
- },
- disconnect: function () {
+ },
+ disconnect: function () {
if (this.connected) {
- removeMessageListener();
- this.connected = false;
+ removeMessageListener();
+ this.connected = false;
}
- },
- messageListener: function (msg) {
+ },
+ messageListener: function (msg) {
let details = JSON.parse(msg);
if (!details) {
- return;
+ return;
}
if (details.broadcast) {
- this.sendToListeners(details.msg);
- return;
+ this.sendToListeners(details.msg);
+ return;
}
if (details.requestId) {
- let listener = this.pending.get(details.requestId);
- if (listener !== undefined) {
+ let listener = this.pending.get(details.requestId);
+ if (listener !== undefined) {
this.pending.delete(details.requestId);
listener(details.msg);
return;
- }
+ }
}
- },
- builtinListener: function (msg) {
+ },
+ builtinListener: function (msg) {
if (typeof msg.cmd === 'string' && msg.cmd === 'injectScript') {
- let details = msg.details;
- if (!details.allFrames && window !== window.top) {
+ let details = msg.details;
+ if (!details.allFrames && window !== window.top) {
return;
- }
- self.injectScript(details.file);
+ }
+ self.injectScript(details.file);
}
- },
- send: function (channelName, message, callback) {
+ },
+ send: function (channelName, message, callback) {
this.connect()
message = {
- channelName: self._sandboxId_ + '|' + channelName,
- msg: message
+ channelName: self._sandboxId_ + '|' + channelName,
+ msg: message
};
if (callback) {
- message.requestId = this.requestId++;
- this.pending.set(message.requestId, callback);
+ message.requestId = this.requestId++;
+ this.pending.set(message.requestId, callback);
}
sendAsyncMessage('ematrix:background', message);
- },
- toggleListener: function ({type, persisted}) {
+ },
+ toggleListener: function ({type, persisted}) {
if (type === 'pagehide' && !persisted) {
- vAPI.shutdown.exec();
- this.shutdown();
- return;
+ vAPI.shutdown.exec();
+ this.shutdown();
+ return;
}
if (type === 'pagehide') {
- this.disconnect();
+ this.disconnect();
} else {
- this.connect();
+ this.connect();
}
- },
- sendToListeners: function (msg) {
+ },
+ sendToListeners: function (msg) {
for (let listener of this.listeners) {
- listener(msg);
+ listener(msg);
}
- },
- addListener: function (listener) {
+ },
+ addListener: function (listener) {
this.listeners.add(listener);
this.connect()
- },
- removeListener: function (listener) {
+ },
+ removeListener: function (listener) {
this.listeners.delete(listener);
- },
- removeAllListeners: function () {
+ },
+ removeAllListeners: function () {
this.disconnect();
this.listeners.clear();
- }
+ }
};
vAPI.messaging.start()
@@ -185,8 +185,8 @@
// be injected in top window).
// Needs more investigating
// if ( window !== window.top ) {
- // vAPI.shutdown.add(function() {
- // vAPI = null;
- // });
+ // vAPI.shutdown.add(function() {
+ // vAPI = null;
+ // });
// }
})(this);
diff --git a/js/vapi-cloud.js b/js/vapi-cloud.js
index cedcfcd..e3cb56b 100644
--- a/js/vapi-cloud.js
+++ b/js/vapi-cloud.js
@@ -27,130 +27,130 @@
(function () {
vAPI.cloud = (function () {
- let extensionBranchPath = 'extensions.' + location.host;
- let cloudBranchPath = extensionBranchPath + '.cloudStorage';
-
- // https://github.com/gorhill/uBlock/issues/80#issuecomment-132081658
- // We must use get/setComplexValue in order to properly handle strings
- // with unicode characters.
- let iss = Ci.nsISupportsString;
- let argstr = Components.classes['@mozilla.org/supports-string;1']
+ let extensionBranchPath = 'extensions.' + location.host;
+ let cloudBranchPath = extensionBranchPath + '.cloudStorage';
+
+ // https://github.com/gorhill/uBlock/issues/80#issuecomment-132081658
+ // We must use get/setComplexValue in order to properly handle strings
+ // with unicode characters.
+ let iss = Ci.nsISupportsString;
+ let argstr = Components.classes['@mozilla.org/supports-string;1']
.createInstance(iss);
- let options = {
+ let options = {
defaultDeviceName: '',
deviceName: ''
- };
+ };
- // User-supplied device name.
- try {
+ // User-supplied device name.
+ try {
options.deviceName = Services.prefs
.getBranch(extensionBranchPath + '.')
.getComplexValue('deviceName', iss)
.data;
- } catch(ex) {
- // Ignore
- }
+ } catch(ex) {
+ // Ignore
+ }
- var getDefaultDeviceName = function() {
+ var getDefaultDeviceName = function() {
var name = '';
try {
- name = Services.prefs
+ name = Services.prefs
.getBranch('services.sync.client.')
.getComplexValue('name', iss)
.data;
} catch(ex) {
- // Ignore
+ // Ignore
}
return name || window.navigator.platform || window.navigator.oscpu;
- };
+ };
- let start = function (dataKeys) {
+ let start = function (dataKeys) {
let extensionBranch =
- Services.prefs.getBranch(extensionBranchPath + '.');
+ Services.prefs.getBranch(extensionBranchPath + '.');
let syncBranch =
- Services.prefs.getBranch('services.sync.prefs.sync.');
+ Services.prefs.getBranch('services.sync.prefs.sync.');
// Mark config entries as syncable
argstr.data = '';
let dataKey;
for (let i=0; i<dataKeys.length; ++i) {
- dataKey = dataKeys[i];
- if (extensionBranch.prefHasUserValue('cloudStorage.' + dataKey)
- === false) {
+ dataKey = dataKeys[i];
+ if (extensionBranch.prefHasUserValue('cloudStorage.' + dataKey)
+ === false) {
extensionBranch.setComplexValue('cloudStorage.' + dataKey,
- iss, argstr);
- }
-
- syncBranch.setBoolPref(cloudBranchPath + '.' + dataKey, true);
+ iss, argstr);
+ }
+
+ syncBranch.setBoolPref(cloudBranchPath + '.' + dataKey, true);
}
- };
+ };
- let push = function (datakey, data, callback) {
+ let push = function (datakey, data, callback) {
let branch = Services.prefs.getBranch(cloudBranchPath + '.');
let bin = {
- 'source': options.deviceName || getDefaultDeviceName(),
- 'tstamp': Date.now(),
- 'data': data,
- 'size': 0
+ 'source': options.deviceName || getDefaultDeviceName(),
+ 'tstamp': Date.now(),
+ 'data': data,
+ 'size': 0
};
bin.size = JSON.stringify(bin).length;
argstr.data = JSON.stringify(bin);
branch.setComplexValue(datakey, iss, argstr);
-
+
if (typeof callback === 'function') {
- callback();
+ callback();
}
- };
+ };
- let pull = function (datakey, callback) {
+ let pull = function (datakey, callback) {
let result = null;
let branch = Services.prefs.getBranch(cloudBranchPath + '.');
-
+
try {
- let json = branch.getComplexValue(datakey, iss).data;
- if (typeof json === 'string') {
+ let json = branch.getComplexValue(datakey, iss).data;
+ if (typeof json === 'string') {
result = JSON.parse(json);
- }
+ }
} catch(ex) {
- // Ignore
+ // Ignore
}
-
+
callback(result);
- };
+ };
- let getOptions = function (callback) {
+ let getOptions = function (callback) {
if (typeof callback !== 'function') {
- return;
+ return;
}
-
+
options.defaultDeviceName = getDefaultDeviceName();
callback(options);
- };
+ };
- let setOptions = function (details, callback) {
+ let setOptions = function (details, callback) {
if (typeof details !== 'object' || details === null) {
- return;
+ return;
}
let branch = Services.prefs.getBranch(extensionBranchPath + '.');
if (typeof details.deviceName === 'string') {
- argstr.data = details.deviceName;
- branch.setComplexValue('deviceName', iss, argstr);
- options.deviceName = details.deviceName;
+ argstr.data = details.deviceName;
+ branch.setComplexValue('deviceName', iss, argstr);
+ options.deviceName = details.deviceName;
}
getOptions(callback);
- };
+ };
- return {
+ return {
start: start,
push: push,
pull: pull,
getOptions: getOptions,
setOptions: setOptions
- };
+ };
})();
})();
diff --git a/js/vapi-common.js b/js/vapi-common.js
index c95d01f..6a79096 100644
--- a/js/vapi-common.js
+++ b/js/vapi-common.js
@@ -32,82 +32,82 @@ Cu.import('resource://gre/modules/Services.jsm');
(function (self) {
vAPI.setTimeout = vAPI.setTimeout || function (callback, delay, extra) {
- return setTimeout(function (a) {
- callback(a);
- }, delay, extra);
+ return setTimeout(function (a) {
+ callback(a);
+ }, delay, extra);
};
// http://www.w3.org/International/questions/qa-scripts#directions
let setScriptDirection = function(language) {
- let dir =
- ['ar', 'he', 'fa', 'ps', 'ur'].indexOf(language) !== -1
- ? 'rtl'
- : 'ltr';
-
- document.body.setAttribute('dir', dir);
+ let dir =
+ ['ar', 'he', 'fa', 'ps', 'ur'].indexOf(language) !== -1
+ ? 'rtl'
+ : 'ltr';
+
+ document.body.setAttribute('dir', dir);
};
vAPI.download = function (details) {
- if (!details.url) {
+ if (!details.url) {
return;
- }
+ }
- let a = document.createElement('a');
- a.href = details.url;
- a.setAttribute('download', details.filename || '');
- a.dispatchEvent(new MouseEvent('click'));
+ let a = document.createElement('a');
+ a.href = details.url;
+ a.setAttribute('download', details.filename || '');
+ a.dispatchEvent(new MouseEvent('click'));
};
vAPI.insertHTML = (function () {
- const parser = Cc['@mozilla.org/parserutils;1']
+ const parser = Cc['@mozilla.org/parserutils;1']
.getService(Ci.nsIParserUtils);
- // https://github.com/gorhill/uBlock/issues/845
- // Apparently dashboard pages execute with `about:blank` principal.
+ // https://github.com/gorhill/uBlock/issues/845
+ // Apparently dashboard pages execute with `about:blank` principal.
- return function (node, html) {
+ return function (node, html) {
while (node.firstChild) {
- node.removeChild(node.firstChild);
+ node.removeChild(node.firstChild);
}
- let parsed =
- parser.parseFragment(html,
- parser.SanitizerAllowStyle,
- false,
- Services.io.newURI('about:blank',
- null, null),
- document.documentElement);
+ let parsed =
+ parser.parseFragment(html,
+ parser.SanitizerAllowStyle,
+ false,
+ Services.io.newURI('about:blank',
+ null, null),
+ document.documentElement);
node.appendChild(parsed);
- };
+ };
})();
vAPI.getURL = function (path) {
- return 'chrome://'
- + location.host
- + '/content/'
- + path.replace(/^\/+/, '');
+ return 'chrome://'
+ + location.host
+ + '/content/'
+ + path.replace(/^\/+/, '');
};
-
+
vAPI.i18n = (function () {
- let stringBundle =
- Services.strings.createBundle('chrome://'
- + location.host
- + '/locale/messages.properties');
+ let stringBundle =
+ Services.strings.createBundle('chrome://'
+ + location.host
+ + '/locale/messages.properties');
- return function (s) {
+ return function (s) {
try {
- return stringBundle.GetStringFromName(s);
+ return stringBundle.GetStringFromName(s);
} catch (ex) {
- return '';
+ return '';
}
- };
+ };
})();
setScriptDirection(navigator.language);
vAPI.closePopup = function() {
- sendAsyncMessage(location.host + ':closePopup');
+ sendAsyncMessage(location.host + ':closePopup');
};
// A localStorage-like object which should be accessible from the
@@ -115,50 +115,50 @@ Cu.import('resource://gre/modules/Services.jsm');
// This storage is optional, but it is nice to have, for a more polished user
// experience.
vAPI.localStorage = {
- pbName: '',
- pb: null,
- str: Cc['@mozilla.org/supports-string;1']
+ pbName: '',
+ pb: null,
+ str: Cc['@mozilla.org/supports-string;1']
.createInstance(Ci.nsISupportsString),
-
- init: function (pbName) {
+
+ init: function (pbName) {
this.pbName = pbName;
this.pb = Services.prefs.getBranch(pbName);
- },
- getItem: function (key) {
+ },
+ getItem: function (key) {
try {
- return this.pb
- .getComplexValue(key,
- Ci.nsISupportsString).data;
+ return this.pb
+ .getComplexValue(key,
+ Ci.nsISupportsString).data;
} catch (ex) {
- return null;
+ return null;
}
- },
- setItem: function (key, value) {
+ },
+ setItem: function (key, value) {
this.str.data = value;
this.pb.setComplexValue(key,
- Ci.nsISupportsString,
- this.str);
- },
- getBool: function (key) {
+ Ci.nsISupportsString,
+ this.str);
+ },
+ getBool: function (key) {
try {
- return this.pb.getBoolPref(key);
+ return this.pb.getBoolPref(key);
} catch (ex) {
- return null;
+ return null;
}
- },
- setBool: function (key, value) {
+ },
+ setBool: function (key, value) {
this.pb.setBoolPref(key, value);
- },
- setDefaultBool: function (key, defaultValue) {
+ },
+ setDefaultBool: function (key, defaultValue) {
Services.prefs.getDefaultBranch(this.pbName)
- .setBoolPref(key, defaultValue);
- },
- removeItem: function (key) {
+ .setBoolPref(key, defaultValue);
+ },
+ removeItem: function (key) {
this.pb.clearUserPref(key);
- },
- clear: function () {
+ },
+ clear: function () {
this.pb.deleteBranch('');
- }
+ }
};
vAPI.localStorage.init('extensions.' + location.host + '.');
diff --git a/js/vapi-contextmenu.js b/js/vapi-contextmenu.js
index f36d8ba..7cf8568 100644
--- a/js/vapi-contextmenu.js
+++ b/js/vapi-contextmenu.js
@@ -27,186 +27,186 @@
(function () {
vAPI.contextMenu = {
- contextMap: {
+ contextMap: {
frame: 'inFrame',
link: 'onLink',
image: 'onImage',
audio: 'onAudio',
video: 'onVideo',
editable: 'onEditableArea'
- }
+ }
};
vAPI.contextMenu.displayMenuItem = function ({target}) {
- let doc = target.ownerDocument;
- let gContextMenu = doc.defaultView.gContextMenu;
- if (!gContextMenu.browser) {
+ let doc = target.ownerDocument;
+ let gContextMenu = doc.defaultView.gContextMenu;
+ if (!gContextMenu.browser) {
return;
- }
+ }
- let menuitem = doc.getElementById(vAPI.contextMenu.menuItemId);
- let currentURI = gContextMenu.browser.currentURI;
+ let menuitem = doc.getElementById(vAPI.contextMenu.menuItemId);
+ let currentURI = gContextMenu.browser.currentURI;
- // https://github.com/chrisaljoudi/uBlock/issues/105
- // TODO: Should the element picker works on any kind of pages?
- if (!currentURI.schemeIs('http') && !currentURI.schemeIs('https')) {
+ // https://github.com/chrisaljoudi/uBlock/issues/105
+ // TODO: Should the element picker works on any kind of pages?
+ if (!currentURI.schemeIs('http') && !currentURI.schemeIs('https')) {
menuitem.setAttribute('hidden', true);
return;
- }
+ }
- let ctx = vAPI.contextMenu.contexts;
+ let ctx = vAPI.contextMenu.contexts;
- if (!ctx) {
+ if (!ctx) {
menuitem.setAttribute('hidden', false);
return;
- }
+ }
- let ctxMap = vAPI.contextMenu.contextMap;
+ let ctxMap = vAPI.contextMenu.contextMap;
- for (let context of ctx) {
+ for (let context of ctx) {
if (context === 'page'
- && !gContextMenu.onLink
- && !gContextMenu.onImage
- && !gContextMenu.onEditableArea
- && !gContextMenu.inFrame
- && !gContextMenu.onVideo
- && !gContextMenu.onAudio) {
- menuitem.setAttribute('hidden', false);
- return;
+ && !gContextMenu.onLink
+ && !gContextMenu.onImage
+ && !gContextMenu.onEditableArea
+ && !gContextMenu.inFrame
+ && !gContextMenu.onVideo
+ && !gContextMenu.onAudio) {
+ menuitem.setAttribute('hidden', false);
+ return;
}
if (ctxMap.hasOwnProperty(context)
- && gContextMenu[ctxMap[context]]) {
- menuitem.setAttribute('hidden', false);
- return;
+ && gContextMenu[ctxMap[context]]) {
+ menuitem.setAttribute('hidden', false);
+ return;
}
- }
+ }
- menuitem.setAttribute('hidden', true);
+ menuitem.setAttribute('hidden', true);
};
vAPI.contextMenu.register = (function () {
- let register = function (doc) {
+ let register = function (doc) {
if (!this.menuItemId) {
- return;
+ return;
}
// Already installed?
if (doc.getElementById(this.menuItemId) !== null) {
- return;
+ return;
}
let contextMenu = doc.getElementById('contentAreaContextMenu');
-
+
let menuitem = doc.createElement('menuitem');
menuitem.setAttribute('id', this.menuItemId);
menuitem.setAttribute('label', this.menuLabel);
menuitem.setAttribute('image', vAPI.getURL('img/browsericons/icon19-19.png'));
menuitem.setAttribute('class', 'menuitem-iconic');
menuitem.addEventListener('command', this.onCommand);
-
+
contextMenu.addEventListener('popupshowing', this.displayMenuItem);
contextMenu.insertBefore(menuitem, doc.getElementById('inspect-separator'));
- };
-
- let registerSafely = function (doc, tryCount) {
- // https://github.com/gorhill/uBlock/issues/906
- // Be sure document.readyState is 'complete': it could happen
- // at launch time that we are called by
- // vAPI.contextMenu.create() directly before the environment
- // is properly initialized.
+ };
+
+ let registerSafely = function (doc, tryCount) {
+ // https://github.com/gorhill/uBlock/issues/906
+ // Be sure document.readyState is 'complete': it could happen
+ // at launch time that we are called by
+ // vAPI.contextMenu.create() directly before the environment
+ // is properly initialized.
if (doc.readyState === 'complete') {
- register.call(this, doc);
- return;
+ register.call(this, doc);
+ return;
}
-
+
if (typeof tryCount !== 'number') {
- tryCount = 0;
+ tryCount = 0;
}
-
+
tryCount += 1;
if (tryCount < 8) {
- vAPI.setTimeout(registerSafely.bind(this, doc, tryCount), 200);
+ vAPI.setTimeout(registerSafely.bind(this, doc, tryCount), 200);
}
- };
+ };
- return registerSafely;
+ return registerSafely;
})();
vAPI.contextMenu.unregister = function (doc) {
- if (!this.menuItemId) {
+ if (!this.menuItemId) {
return;
- }
+ }
- let menuitem = doc.getElementById(this.menuItemId);
- if (menuitem === null) {
+ let menuitem = doc.getElementById(this.menuItemId);
+ if (menuitem === null) {
return;
- }
-
- let contextMenu = menuitem.parentNode;
- menuitem.removeEventListener('command', this.onCommand);
- contextMenu.removeEventListener('popupshowing', this.displayMenuItem);
- contextMenu.removeChild(menuitem);
+ }
+
+ let contextMenu = menuitem.parentNode;
+ menuitem.removeEventListener('command', this.onCommand);
+ contextMenu.removeEventListener('popupshowing', this.displayMenuItem);
+ contextMenu.removeChild(menuitem);
};
vAPI.contextMenu.create = function (details, callback) {
- this.menuItemId = details.id;
- this.menuLabel = details.title;
- this.contexts = details.contexts;
+ this.menuItemId = details.id;
+ this.menuLabel = details.title;
+ this.contexts = details.contexts;
- if (Array.isArray(this.contexts) && this.contexts.length) {
+ if (Array.isArray(this.contexts) && this.contexts.length) {
this.contexts = this.contexts.indexOf('all') === -1
- ? this.contexts
- : null;
- } else {
+ ? this.contexts
+ : null;
+ } else {
// default in Chrome
this.contexts = ['page'];
- }
+ }
- this.onCommand = function () {
+ this.onCommand = function () {
let gContextMenu = vAPI.browser.getOwnerWindow(this).gContextMenu;
let details = {
- menuItemId: this.id
+ menuItemId: this.id
};
if (gContextMenu.inFrame) {
- details.tagName = 'iframe';
- // Probably won't work with e10s
- // eMatrix: doesn't matter ;)
- details.frameUrl = gContextMenu.focusedWindow.location.href;
+ details.tagName = 'iframe';
+ // Probably won't work with e10s
+ // eMatrix: doesn't matter ;)
+ details.frameUrl = gContextMenu.focusedWindow.location.href;
} else if (gContextMenu.onImage) {
- details.tagName = 'img';
- details.srcUrl = gContextMenu.mediaURL;
+ details.tagName = 'img';
+ details.srcUrl = gContextMenu.mediaURL;
} else if (gContextMenu.onAudio) {
- details.tagName = 'audio';
- details.srcUrl = gContextMenu.mediaURL;
+ details.tagName = 'audio';
+ details.srcUrl = gContextMenu.mediaURL;
} else if (gContextMenu.onVideo) {
- details.tagName = 'video';
- details.srcUrl = gContextMenu.mediaURL;
+ details.tagName = 'video';
+ details.srcUrl = gContextMenu.mediaURL;
} else if (gContextMenu.onLink) {
- details.tagName = 'a';
- details.linkUrl = gContextMenu.linkURL;
+ details.tagName = 'a';
+ details.linkUrl = gContextMenu.linkURL;
}
callback(details, {
- id: vAPI.tabs.manager.tabIdFromTarget(gContextMenu.browser),
- url: gContextMenu.browser.currentURI.asciiSpec
+ id: vAPI.tabs.manager.tabIdFromTarget(gContextMenu.browser),
+ url: gContextMenu.browser.currentURI.asciiSpec
});
- };
+ };
- for (let win of vAPI.window.getWindows()) {
+ for (let win of vAPI.window.getWindows()) {
this.register(win.document);
- }
+ }
};
vAPI.contextMenu.remove = function () {
- for (let win of vAPI.window.getWindows()) {
+ for (let win of vAPI.window.getWindows()) {
this.unregister(win.document);
- }
+ }
- this.menuItemId = null;
- this.menuLabel = null;
- this.contexts = null;
- this.onCommand = null;
+ this.menuItemId = null;
+ this.menuLabel = null;
+ this.contexts = null;
+ this.onCommand = null;
};
})();
diff --git a/js/vapi-cookies.js b/js/vapi-cookies.js
index 569205c..b1e2fb3 100644
--- a/js/vapi-cookies.js
+++ b/js/vapi-cookies.js
@@ -29,90 +29,90 @@
vAPI.cookies = {};
vAPI.cookies.CookieEntry = function (ffCookie) {
- this.domain = ffCookie.host;
- this.name = ffCookie.name;
- this.path = ffCookie.path;
- this.secure = ffCookie.isSecure === true;
- this.session = ffCookie.expires === 0;
- this.value = ffCookie.value;
+ this.domain = ffCookie.host;
+ this.name = ffCookie.name;
+ this.path = ffCookie.path;
+ this.secure = ffCookie.isSecure === true;
+ this.session = ffCookie.expires === 0;
+ this.value = ffCookie.value;
};
vAPI.cookies.start = function () {
- Services.obs.addObserver(this, 'cookie-changed', false);
- Services.obs.addObserver(this, 'private-cookie-changed', false);
- vAPI.addCleanUpTask(this.stop.bind(this));
+ Services.obs.addObserver(this, 'cookie-changed', false);
+ Services.obs.addObserver(this, 'private-cookie-changed', false);
+ vAPI.addCleanUpTask(this.stop.bind(this));
};
vAPI.cookies.stop = function () {
- Services.obs.removeObserver(this, 'cookie-changed');
- Services.obs.removeObserver(this, 'private-cookie-changed');
+ Services.obs.removeObserver(this, 'cookie-changed');
+ Services.obs.removeObserver(this, 'private-cookie-changed');
};
vAPI.cookies.observe = function (subject, topic, reason) {
- //if ( topic !== 'cookie-changed' && topic !== 'private-cookie-changed' ) {
- // return;
- //}
- //
- if (reason === 'cleared' && typeof this.onAllRemoved === 'function') {
+ //if ( topic !== 'cookie-changed' && topic !== 'private-cookie-changed' ) {
+ // return;
+ //}
+ //
+ if (reason === 'cleared' && typeof this.onAllRemoved === 'function') {
this.onAllRemoved();
return;
- }
- if (subject === null) {
+ }
+ if (subject === null) {
return;
- }
- if (subject instanceof Ci.nsICookie2 === false) {
+ }
+ if (subject instanceof Ci.nsICookie2 === false) {
try {
- subject = subject.QueryInterface(Ci.nsICookie2);
+ subject = subject.QueryInterface(Ci.nsICookie2);
} catch (ex) {
- return;
+ return;
}
- }
- if (reason === 'deleted') {
+ }
+ if (reason === 'deleted') {
if (typeof this.onRemoved === 'function') {
- this.onRemoved(new this.CookieEntry(subject));
+ this.onRemoved(new this.CookieEntry(subject));
}
return;
- }
- if (typeof this.onChanged === 'function') {
+ }
+ if (typeof this.onChanged === 'function') {
this.onChanged(new this.CookieEntry(subject));
- }
+ }
};
vAPI.cookies.getAll = function(callback) {
- // Meant and expected to be asynchronous.
- if (typeof callback !== 'function') {
+ // Meant and expected to be asynchronous.
+ if (typeof callback !== 'function') {
return;
- }
-
- let onAsync = function () {
+ }
+
+ let onAsync = function () {
let out = [];
let enumerator = Services.cookies.enumerator;
let ffcookie;
while (enumerator.hasMoreElements()) {
- ffcookie = enumerator.getNext();
- if (ffcookie instanceof Ci.nsICookie) {
+ ffcookie = enumerator.getNext();
+ if (ffcookie instanceof Ci.nsICookie) {
out.push(new this.CookieEntry(ffcookie));
- }
+ }
}
-
+
callback(out);
- };
-
- vAPI.setTimeout(onAsync.bind(this), 0);
+ };
+
+ vAPI.setTimeout(onAsync.bind(this), 0);
};
vAPI.cookies.remove = function (details, callback) {
- let uri = Services.io.newURI(details.url, null, null);
- let cookies = Services.cookies;
- cookies.remove(uri.asciiHost, details.name, uri.path, false, {});
- cookies.remove( '.' + uri.asciiHost, details.name, uri.path, false, {});
-
- if (typeof callback === 'function') {
+ let uri = Services.io.newURI(details.url, null, null);
+ let cookies = Services.cookies;
+ cookies.remove(uri.asciiHost, details.name, uri.path, false, {});
+ cookies.remove( '.' + uri.asciiHost, details.name, uri.path, false, {});
+
+ if (typeof callback === 'function') {
callback({
- domain: uri.asciiHost,
- name: details.name,
- path: uri.path
+ domain: uri.asciiHost,
+ name: details.name,
+ path: uri.path
});
- }
+ }
};
})();
diff --git a/js/vapi-core.js b/js/vapi-core.js
index c2c508d..94ae599 100644
--- a/js/vapi-core.js
+++ b/js/vapi-core.js
@@ -27,26 +27,26 @@
(function (self) {
vAPI.modernFirefox =
- Services.appinfo.ID === '{ec8030f7-c20a-464f-9b0e-13a3a9e97384}'
- && Services.vc.compare(Services.appinfo.version, '44') > 0;
+ Services.appinfo.ID === '{ec8030f7-c20a-464f-9b0e-13a3a9e97384}'
+ && Services.vc.compare(Services.appinfo.version, '44') > 0;
vAPI.app = {
- name: 'eMatrix',
- version: location.hash.slice(1),
-
- start: function () {
- return;
- },
- stop: function () {
- return;
- },
- restart: function () {
- Cc['@mozilla.org/childprocessmessagemanager;1']
- .getService(Ci.nsIMessageSender)
- .sendAsyncMessage(location.host + '-restart');
- },
+ name: 'eMatrix',
+ version: location.hash.slice(1),
+
+ start: function () {
+ return;
+ },
+ stop: function () {
+ return;
+ },
+ restart: function () {
+ Cc['@mozilla.org/childprocessmessagemanager;1']
+ .getService(Ci.nsIMessageSender)
+ .sendAsyncMessage(location.host + '-restart');
+ },
};
-
+
// List of things that needs to be destroyed when disabling the extension
// Only functions should be added to it
// eMatrix: taken care by vAPI.addCleanUpTask --- use that function
@@ -57,75 +57,75 @@
let expectedNumberOfCleanups = 7;
vAPI.addCleanUpTask = function (task) {
- if (typeof task !== 'function') {
- return;
- }
+ if (typeof task !== 'function') {
+ return;
+ }
- cleanupTasks.push(task);
+ cleanupTasks.push(task);
};
vAPI.deferUntil = function (testFn, mainFn, details) {
- let dtls = (typeof details !== 'object') ? {} : details;
- let now = 0;
- let next = dtls.next || 200;
- let until = dtls.until || 2000;
+ let dtls = (typeof details !== 'object') ? {} : details;
+ let now = 0;
+ let next = dtls.next || 200;
+ let until = dtls.until || 2000;
- let check = function () {
+ let check = function () {
if (testFn() === true || now >= until) {
- mainFn();
- return;
+ mainFn();
+ return;
}
now += next;
vAPI.setTimeout(check, next);
- };
+ };
- if ('sync' in dtls && dtls.sync === true) {
+ if ('sync' in dtls && dtls.sync === true) {
check();
- } else {
+ } else {
vAPI.setTimeout(check, 1);
- }
+ }
};
window.addEventListener('unload', function () {
- // if (typeof vAPI.app.onShutdown === 'function') {
+ // if (typeof vAPI.app.onShutdown === 'function') {
// vAPI.app.onShutdown();
- // }
-
- // IMPORTANT: cleanup tasks must be executed using LIFO order.
- for (let i=cleanupTasks.length-1; i>=0; --i) {
- try {
- cleanupTasks[i]();
- } catch (e) {
- // Just in case a clean up task ends up throwing for
- // no reason
- console.error(e);
- }
- }
-
- // eMatrix: temporarily disabled
- // if (cleanupTasks.length < expectedNumberOfCleanups) {
+ // }
+
+ // IMPORTANT: cleanup tasks must be executed using LIFO order.
+ for (let i=cleanupTasks.length-1; i>=0; --i) {
+ try {
+ cleanupTasks[i]();
+ } catch (e) {
+ // Just in case a clean up task ends up throwing for
+ // no reason
+ console.error(e);
+ }
+ }
+
+ // eMatrix: temporarily disabled
+ // if (cleanupTasks.length < expectedNumberOfCleanups) {
// console.error
- // ('eMatrix> Cleanup tasks performed: %s (out of %s)',
+ // ('eMatrix> Cleanup tasks performed: %s (out of %s)',
// cleanupTasks.length,
// expectedNumberOfCleanups);
- // }
-
- // frameModule needs to be cleared too
- let frameModuleURL = vAPI.getURL('frameModule.js');
- let frameModule = {};
-
- Cu.import(frameModuleURL, frameModule);
- frameModule.contentObserver.unregister();
- Cu.unload(frameModuleURL);
+ // }
+
+ // frameModule needs to be cleared too
+ let frameModuleURL = vAPI.getURL('frameModule.js');
+ let frameModule = {};
+
+ Cu.import(frameModuleURL, frameModule);
+ frameModule.contentObserver.unregister();
+ Cu.unload(frameModuleURL);
});
vAPI.noTabId = '-1';
vAPI.isBehindTheSceneTabId = function (tabId) {
- return tabId.toString() === '-1';
+ return tabId.toString() === '-1';
};
vAPI.lastError = function () {
- return null;
+ return null;
};
})(this);
diff --git a/js/vapi-messaging.js b/js/vapi-messaging.js
index ef11094..b4f468f 100644
--- a/js/vapi-messaging.js
+++ b/js/vapi-messaging.js
@@ -27,111 +27,111 @@
(function () {
Cu.import('chrome://ematrix/content/CallbackWrapper.jsm');
-
+
vAPI.messaging = {
- get globalMessageManager() {
+ get globalMessageManager() {
return Cc['@mozilla.org/globalmessagemanager;1']
.getService(Ci.nsIMessageListenerManager);
- },
- frameScript: vAPI.getURL('frameScript.js'),
- listeners: {},
- defaultHandler: null,
- NOOPFUNC: function(){},
- UNHANDLED: 'vAPI.messaging.notHandled'
+ },
+ frameScript: vAPI.getURL('frameScript.js'),
+ listeners: {},
+ defaultHandler: null,
+ NOOPFUNC: function(){},
+ UNHANDLED: 'vAPI.messaging.notHandled'
};
vAPI.messaging.listen = function (listenerName, callback) {
- this.listeners[listenerName] = callback;
+ this.listeners[listenerName] = callback;
};
vAPI.messaging.onMessage = function ({target, data}) {
- let messageManager = target.messageManager;
+ let messageManager = target.messageManager;
- if (!messageManager) {
+ if (!messageManager) {
// Message came from a popup, and its message manager is
// not usable. So instead we broadcast to the parent
// window.
messageManager =
- vAPI.browser.
- getOwnerWindow(target.webNavigation
- .QueryInterface(Ci.nsIDocShell)
- .chromeEventHandler).messageManager;
- }
-
- let channelNameRaw = data.channelName;
- let pos = channelNameRaw.indexOf('|');
- let channelName = channelNameRaw.slice(pos + 1);
-
- let callback = vAPI.messaging.NOOPFUNC;
- if (data.requestId !== undefined) {
+ vAPI.browser.
+ getOwnerWindow(target.webNavigation
+ .QueryInterface(Ci.nsIDocShell)
+ .chromeEventHandler).messageManager;
+ }
+
+ let channelNameRaw = data.channelName;
+ let pos = channelNameRaw.indexOf('|');
+ let channelName = channelNameRaw.slice(pos + 1);
+
+ let callback = vAPI.messaging.NOOPFUNC;
+ if (data.requestId !== undefined) {
callback = CallbackWrapper.factory(messageManager,
- channelName,
- channelNameRaw.slice(0, pos),
- data.requestId).callback;
- }
+ channelName,
+ channelNameRaw.slice(0, pos),
+ data.requestId).callback;
+ }
- let sender = {
+ let sender = {
tab: {
- id: vAPI.tabs.manager.tabIdFromTarget(target)
+ id: vAPI.tabs.manager.tabIdFromTarget(target)
}
- };
+ };
+
+ // Specific handler
+ let r = vAPI.messaging.UNHANDLED;
+ let listener = vAPI.messaging.listeners[channelName];
- // Specific handler
- let r = vAPI.messaging.UNHANDLED;
- let listener = vAPI.messaging.listeners[channelName];
-
- if (typeof listener === 'function') {
+ if (typeof listener === 'function') {
r = listener(data.msg, sender, callback);
- }
- if (r !== vAPI.messaging.UNHANDLED) {
+ }
+ if (r !== vAPI.messaging.UNHANDLED) {
return;
- }
+ }
- // Default handler
- r = vAPI.messaging.defaultHandler(data.msg, sender, callback);
- if (r !== vAPI.messaging.UNHANDLED) {
+ // Default handler
+ r = vAPI.messaging.defaultHandler(data.msg, sender, callback);
+ if (r !== vAPI.messaging.UNHANDLED) {
return;
- }
+ }
- console.error('eMatrix> messaging > unknown request: %o', data);
+ console.error('eMatrix> messaging > unknown request: %o', data);
- // Unhandled: Need to callback anyways in case caller expected
- // an answer, or else there is a memory leak on caller's side
- callback();
+ // Unhandled: Need to callback anyways in case caller expected
+ // an answer, or else there is a memory leak on caller's side
+ callback();
};
vAPI.messaging.setup = function (defaultHandler) {
- // Already setup?
- if (this.defaultHandler !== null) {
+ // Already setup?
+ if (this.defaultHandler !== null) {
return;
- }
+ }
- if (typeof defaultHandler !== 'function') {
+ if (typeof defaultHandler !== 'function') {
defaultHandler = function () {
- return vAPI.messaging.UNHANDLED;
- };
- }
-
- this.defaultHandler = defaultHandler;
- this.globalMessageManager.addMessageListener(location.host
- + ':background',
- this.onMessage);
- this.globalMessageManager.loadFrameScript(this.frameScript, true);
-
- vAPI.addCleanUpTask(function () {
+ return vAPI.messaging.UNHANDLED;
+ };
+ }
+
+ this.defaultHandler = defaultHandler;
+ this.globalMessageManager.addMessageListener(location.host
+ + ':background',
+ this.onMessage);
+ this.globalMessageManager.loadFrameScript(this.frameScript, true);
+
+ vAPI.addCleanUpTask(function () {
let gmm = vAPI.messaging.globalMessageManager;
gmm.removeDelayedFrameScript(vAPI.messaging.frameScript);
gmm.removeMessageListener(location.host + ':background',
- vAPI.messaging.onMessage);
- });
+ vAPI.messaging.onMessage);
+ });
};
vAPI.messaging.broadcast = function (message) {
- this.globalMessageManager
- .broadcastAsyncMessage(location.host + ':broadcast',
- JSON.stringify({
- broadcast: true,
- msg: message}));
+ this.globalMessageManager
+ .broadcastAsyncMessage(location.host + ':broadcast',
+ JSON.stringify({
+ broadcast: true,
+ msg: message}));
};
})();
diff --git a/js/vapi-net.js b/js/vapi-net.js
index ec12d2a..f8d1052 100644
--- a/js/vapi-net.js
+++ b/js/vapi-net.js
@@ -29,41 +29,41 @@
vAPI.net = {};
vAPI.net.registerListeners = function () {
- this.onBeforeRequest.types = this.onBeforeRequest.types
- ? new Set(this.onBeforeRequest.types)
- : null;
-
- this.onBeforeSendHeaders.types = this.onBeforeSendHeaders.types
- ? new Set(this.onBeforeSendHeaders.types)
- : null;
+ this.onBeforeRequest.types = this.onBeforeRequest.types
+ ? new Set(this.onBeforeRequest.types)
+ : null;
- let shouldLoadListenerMessageName = location.host + ':shouldLoad';
- let shouldLoadListener = function (e) {
+ this.onBeforeSendHeaders.types = this.onBeforeSendHeaders.types
+ ? new Set(this.onBeforeSendHeaders.types)
+ : null;
+
+ let shouldLoadListenerMessageName = location.host + ':shouldLoad';
+ let shouldLoadListener = function (e) {
let details = e.data;
let pendingReq = vAPI.httpObserver.createPendingRequest(details.url);
pendingReq.rawType = details.rawType;
pendingReq.tabId = vAPI.tabs.manager.tabIdFromTarget(e.target);
- };
+ };
- // https://github.com/gorhill/uMatrix/issues/200
- // We need this only for Firefox 34 and less: the tab id is derived from
- // the origin of the message.
- if (!vAPI.modernFirefox) {
+ // https://github.com/gorhill/uMatrix/issues/200
+ // We need this only for Firefox 34 and less: the tab id is derived from
+ // the origin of the message.
+ if (!vAPI.modernFirefox) {
vAPI.messaging.globalMessageManager
- .addMessageListener(shouldLoadListenerMessageName,
- shouldLoadListener);
- }
+ .addMessageListener(shouldLoadListenerMessageName,
+ shouldLoadListener);
+ }
- vAPI.httpObserver.register();
+ vAPI.httpObserver.register();
- vAPI.addCleanUpTask(function () {
+ vAPI.addCleanUpTask(function () {
if (!vAPI.modernFirefox) {
- vAPI.messaging.globalMessageManager
- .removeMessageListener(shouldLoadListenerMessageName,
- shouldLoadListener);
+ vAPI.messaging.globalMessageManager
+ .removeMessageListener(shouldLoadListenerMessageName,
+ shouldLoadListener);
}
vAPI.httpObserver.unregister();
- });
+ });
};
})();
diff --git a/js/vapi-storage.js b/js/vapi-storage.js
index 8acb244..c94f595 100644
--- a/js/vapi-storage.js
+++ b/js/vapi-storage.js
@@ -29,60 +29,60 @@
// API matches that of chrome.storage.local:
// https://developer.chrome.com/extensions/storage
vAPI.storage = (function () {
- let db = null;
- let vacuumTimer = null;
+ let db = null;
+ let vacuumTimer = null;
- let close = function () {
+ let close = function () {
if (vacuumTimer !== null) {
- clearTimeout(vacuumTimer);
- vacuumTimer = null;
+ clearTimeout(vacuumTimer);
+ vacuumTimer = null;
}
-
+
if (db === null) {
- return;
+ return;
}
-
+
db.asyncClose();
db = null;
- };
+ };
- let open = function () {
+ let open = function () {
if (db !== null) {
- return db;
+ return db;
}
-
+
// Create path
let path = Services.dirsvc.get('ProfD', Ci.nsIFile);
path.append('ematrix-data');
if (!path.exists()) {
- path.create(Ci.nsIFile.DIRECTORY_TYPE, parseInt('0774', 8));
+ path.create(Ci.nsIFile.DIRECTORY_TYPE, parseInt('0774', 8));
}
if (!path.isDirectory()) {
- throw Error('Should be a directory...');
+ throw Error('Should be a directory...');
}
- let path2 = Services.dirsvc.get('ProfD', Ci.nsIFile);
- path2.append('extension-data');
- path2.append(location.host + '.sqlite');
- if (path2.exists()) {
- path2.moveTo(path, location.host+'.sqlite');
- }
+ let path2 = Services.dirsvc.get('ProfD', Ci.nsIFile);
+ path2.append('extension-data');
+ path2.append(location.host + '.sqlite');
+ if (path2.exists()) {
+ path2.moveTo(path, location.host+'.sqlite');
+ }
- path.append(location.host + '.sqlite');
+ path.append(location.host + '.sqlite');
// Open database
try {
- db = Services.storage.openDatabase(path);
- if (db.connectionReady === false) {
+ db = Services.storage.openDatabase(path);
+ if (db.connectionReady === false) {
db.asyncClose();
db = null;
- }
+ }
} catch (ex) {
- // Ignore
+ // Ignore
}
if (db === null) {
- return null;
+ return null;
}
// Database was opened, register cleanup task
@@ -90,240 +90,240 @@
// Setup database
db.createAsyncStatement('CREATE TABLE IF NOT EXISTS '
- +'"settings" ("name" '
- +'TEXT PRIMARY KEY NOT NULL, '
- +'"value" TEXT);')
- .executeAsync();
+ +'"settings" ("name" '
+ +'TEXT PRIMARY KEY NOT NULL, '
+ +'"value" TEXT);')
+ .executeAsync();
if (vacuum !== null) {
- vacuumTimer = vAPI.setTimeout(vacuum, 60000);
+ vacuumTimer = vAPI.setTimeout(vacuum, 60000);
}
return db;
- };
+ };
- // Vacuum only once, and only while idle
- let vacuum = function () {
+ // Vacuum only once, and only while idle
+ let vacuum = function () {
vacuumTimer = null;
if (db === null) {
- return;
+ return;
}
let idleSvc =
- Cc['@mozilla.org/widget/idleservice;1']
- .getService(Ci.nsIIdleService);
-
+ Cc['@mozilla.org/widget/idleservice;1']
+ .getService(Ci.nsIIdleService);
+
if (idleSvc.idleTime < 60000) {
- vacuumTimer = vAPI.setTimeout(vacuum, 60000);
- return;
+ vacuumTimer = vAPI.setTimeout(vacuum, 60000);
+ return;
}
-
+
db.createAsyncStatement('VACUUM').executeAsync();
vacuum = null;
- };
+ };
- // Execute a query
- let runStatement = function (stmt, callback) {
+ // Execute a query
+ let runStatement = function (stmt, callback) {
let result = {};
stmt.executeAsync({
- handleResult: function (rows) {
+ handleResult: function (rows) {
if (!rows || typeof callback !== 'function') {
- return;
+ return;
}
let row;
while ((row = rows.getNextRow())) {
- // we assume that there will be two columns, since we're
- // using it only for preferences
- // eMatrix: the above comment is obsolete
- // (it's not used just for preferences
- // anymore), but we still expect two columns.
- let res = row.getResultByIndex(0);
- result[res] = row.getResultByIndex(1);
+ // we assume that there will be two columns, since we're
+ // using it only for preferences
+ // eMatrix: the above comment is obsolete
+ // (it's not used just for preferences
+ // anymore), but we still expect two columns.
+ let res = row.getResultByIndex(0);
+ result[res] = row.getResultByIndex(1);
}
- },
- handleCompletion: function (reason) {
+ },
+ handleCompletion: function (reason) {
if (typeof callback === 'function' && reason === 0) {
- callback(result);
+ callback(result);
}
- },
- handleError: function (error) {
+ },
+ handleError: function (error) {
console.error('SQLite error ', error.result, error.message);
-
+
// Caller expects an answer regardless of failure.
if (typeof callback === 'function' ) {
- callback(null);
+ callback(null);
}
- },
+ },
});
- };
+ };
- let bindNames = function (stmt, names) {
+ let bindNames = function (stmt, names) {
if (Array.isArray(names) === false || names.length === 0) {
- return;
+ return;
}
-
+
let params = stmt.newBindingParamsArray();
-
- for (let i=names.length-1; i>=0; --i) {
- let bp = params.newBindingParams();
- bp.bindByName('name', names[i]);
- params.addParams(bp);
+
+ for (let i=names.length-1; i>=0; --i) {
+ let bp = params.newBindingParams();
+ bp.bindByName('name', names[i]);
+ params.addParams(bp);
}
-
+
stmt.bindParameters(params);
- };
+ };
- let clear = function (callback) {
+ let clear = function (callback) {
if (open() === null) {
- if (typeof callback === 'function') {
+ if (typeof callback === 'function') {
callback();
- }
- return;
+ }
+ return;
}
-
+
runStatement(db.createAsyncStatement('DELETE FROM "settings";'),
- callback);
- };
+ callback);
+ };
- let getBytesInUse = function (keys, callback) {
+ let getBytesInUse = function (keys, callback) {
if (typeof callback !== 'function') {
- return;
+ return;
}
if (open() === null) {
- callback(0);
- return;
+ callback(0);
+ return;
}
let stmt;
if (Array.isArray(keys)) {
- stmt = db.createAsyncStatement('SELECT "size" AS "size", '
- +'SUM(LENGTH("value")) '
- +'FROM "settings" WHERE '
- +'"name" = :name');
- bindNames(keys);
+ stmt = db.createAsyncStatement('SELECT "size" AS "size", '
+ +'SUM(LENGTH("value")) '
+ +'FROM "settings" WHERE '
+ +'"name" = :name');
+ bindNames(keys);
} else {
- stmt = db.createAsyncStatement('SELECT "size" AS "size", '
- +'SUM(LENGTH("value")) '
- +'FROM "settings"');
+ stmt = db.createAsyncStatement('SELECT "size" AS "size", '
+ +'SUM(LENGTH("value")) '
+ +'FROM "settings"');
}
runStatement(stmt, function (result) {
- callback(result.size);
+ callback(result.size);
});
- };
+ };
- let read = function (details, callback) {
+ let read = function (details, callback) {
if (typeof callback !== 'function') {
- return;
+ return;
}
let prepareResult = function (result) {
- for (let key in result) {
+ for (let key in result) {
if (result.hasOwnProperty(key) === false) {
- continue;
+ continue;
}
-
+
result[key] = JSON.parse(result[key]);
- }
-
- if (typeof details === 'object' && details !== null) {
+ }
+
+ if (typeof details === 'object' && details !== null) {
for (let key in details) {
- if (result.hasOwnProperty(key) === false) {
+ if (result.hasOwnProperty(key) === false) {
result[key] = details[key];
- }
+ }
}
- }
-
- callback(result);
+ }
+
+ callback(result);
};
if (open() === null) {
- prepareResult({});
- return;
+ prepareResult({});
+ return;
}
let names = [];
if (details !== null) {
- if (Array.isArray(details)) {
+ if (Array.isArray(details)) {
names = details;
- } else if (typeof details === 'object') {
+ } else if (typeof details === 'object') {
names = Object.keys(details);
- } else {
+ } else {
names = [details.toString()];
- }
+ }
}
let stmt;
if (names.length === 0) {
- stmt = db.createAsyncStatement('SELECT * FROM "settings"');
+ stmt = db.createAsyncStatement('SELECT * FROM "settings"');
} else {
- stmt = db.createAsyncStatement('SELECT * FROM "settings" '
- +'WHERE "name" = :name');
- bindNames(stmt, names);
+ stmt = db.createAsyncStatement('SELECT * FROM "settings" '
+ +'WHERE "name" = :name');
+ bindNames(stmt, names);
}
-
+
runStatement(stmt, prepareResult);
- };
+ };
- let remove = function (keys, callback) {
+ let remove = function (keys, callback) {
if (open() === null) {
- if (typeof callback === 'function') {
+ if (typeof callback === 'function') {
callback();
- }
- return;
+ }
+ return;
}
-
+
var stmt = db.createAsyncStatement('DELETE FROM "settings" '
- +'WHERE "name" = :name');
+ +'WHERE "name" = :name');
bindNames(stmt, typeof keys === 'string' ? [keys] : keys);
runStatement(stmt, callback);
- };
-
- let write = function (details, callback) {
+ };
+
+ let write = function (details, callback) {
if (open() === null) {
- if (typeof callback === 'function') {
+ if (typeof callback === 'function') {
callback();
- }
- return;
+ }
+ return;
}
-
+
let stmt = db.createAsyncStatement('INSERT OR REPLACE INTO '
- +'"settings" ("name", "value") '
- +'VALUES(:name, :value)');
+ +'"settings" ("name", "value") '
+ +'VALUES(:name, :value)');
let params = stmt.newBindingParamsArray();
-
+
for (let key in details) {
- if (details.hasOwnProperty(key) === false) {
+ if (details.hasOwnProperty(key) === false) {
continue;
- }
-
- let bp = params.newBindingParams();
- bp.bindByName('name', key);
- bp.bindByName('value', JSON.stringify(details[key]));
- params.addParams(bp);
+ }
+
+ let bp = params.newBindingParams();
+ bp.bindByName('name', key);
+ bp.bindByName('value', JSON.stringify(details[key]));
+ params.addParams(bp);
}
-
+
if (params.length === 0) {
- return;
+ return;
}
stmt.bindParameters(params);
runStatement(stmt, callback);
- };
+ };
- // Export API
- var api = {
+ // Export API
+ var api = {
QUOTA_BYTES: 100 * 1024 * 1024,
clear: clear,
get: read,
getBytesInUse: getBytesInUse,
remove: remove,
set: write
- };
-
- return api;
+ };
+
+ return api;
})();
vAPI.cacheStorage = vAPI.storage;
diff --git a/js/vapi-tabs.js b/js/vapi-tabs.js
index 9fbecd6..bf1aeea 100644
--- a/js/vapi-tabs.js
+++ b/js/vapi-tabs.js
@@ -29,548 +29,548 @@
vAPI.tabs = {};
vAPI.tabs.registerListeners = function() {
- vAPI.tabs.manager.start();
+ vAPI.tabs.manager.start();
};
vAPI.tabs.get = function (tabId, callback) {
- // eMatrix: the following might be obsoleted (though probably
- // still relevant at least for Pale Moon.)
- //
- // Firefox:
- //
- // browser -> ownerDocument -> defaultView -> gBrowser -> browsers --+
- // ^ |
- // | |
- // +--------------------------------------------------------------+
- //
- // browser (browser)
- // contentTitle
- // currentURI
- // ownerDocument (XULDocument)
- // defaultView (ChromeWindow)
- // gBrowser (tabbrowser OR browser)
- // browsers (browser)
- // selectedBrowser
- // selectedTab
- // tabs (tab.tabbrowser-tab)
- //
- // Fennec: (what I figured so far)
- //
- // tab -> browser windows -> window -> BrowserApp -> tabs --+
- // ^ window |
- // | |
- // +-----------------------------------------------------------+
- //
- // tab
- // browser
- // [manual search to go back to tab from list of windows]
- let browser;
-
- if (tabId === null) {
+ // eMatrix: the following might be obsoleted (though probably
+ // still relevant at least for Pale Moon.)
+ //
+ // Firefox:
+ //
+ // browser -> ownerDocument -> defaultView -> gBrowser -> browsers --+
+ // ^ |
+ // | |
+ // +--------------------------------------------------------------+
+ //
+ // browser (browser)
+ // contentTitle
+ // currentURI
+ // ownerDocument (XULDocument)
+ // defaultView (ChromeWindow)
+ // gBrowser (tabbrowser OR browser)
+ // browsers (browser)
+ // selectedBrowser
+ // selectedTab
+ // tabs (tab.tabbrowser-tab)
+ //
+ // Fennec: (what I figured so far)
+ //
+ // tab -> browser windows -> window -> BrowserApp -> tabs --+
+ // ^ window |
+ // | |
+ // +-----------------------------------------------------------+
+ //
+ // tab
+ // browser
+ // [manual search to go back to tab from list of windows]
+ let browser;
+
+ if (tabId === null) {
browser = vAPI.tabs.manager.currentBrowser();
tabId = vAPI.tabs.manager.tabIdFromTarget(browser);
- } else {
+ } else {
browser = vAPI.tabs.manager.browserFromTabId(tabId);
- }
+ }
- // For internal use
- if (typeof callback !== 'function') {
+ // For internal use
+ if (typeof callback !== 'function') {
return browser;
- }
+ }
- if (!browser || !browser.currentURI) {
+ if (!browser || !browser.currentURI) {
callback();
return;
- }
+ }
- let win = vAPI.browser.getOwnerWindow(browser);
- let tabBrowser = vAPI.browser.getTabBrowser(win);
+ let win = vAPI.browser.getOwnerWindow(browser);
+ let tabBrowser = vAPI.browser.getTabBrowser(win);
- callback({
+ callback({
id: tabId,
windowId: vAPI.window.idFromWindow(win),
active: tabBrowser !== null
- && browser === tabBrowser.selectedBrowser,
+ && browser === tabBrowser.selectedBrowser,
url: browser.currentURI.asciiSpec,
title: browser.contentTitle
- });
+ });
};
vAPI.tabs.getAllSync = function (window) {
- let win;
- let tabs = [];
+ let win;
+ let tabs = [];
- for (let win of vAPI.window.getWindows()) {
+ for (let win of vAPI.window.getWindows()) {
if (window && window !== win) {
- continue;
+ continue;
}
let tabBrowser = vAPI.browser.getTabBrowser(win);
if (tabBrowser === null) {
- continue;
+ continue;
}
// This can happens if a tab-less window is currently opened.
// Example of a tab-less window: one opened from clicking
// "View Page Source".
if (!tabBrowser.tabs) {
- continue;
+ continue;
}
for (let tab of tabBrowser.tabs) {
- tabs.push(tab);
+ tabs.push(tab);
}
- }
+ }
- return tabs;
+ return tabs;
};
vAPI.tabs.getAll = function (callback) {
- let tabs = [];
+ let tabs = [];
- for (let browser of vAPI.tabs.manager.browsers()) {
+ for (let browser of vAPI.tabs.manager.browsers()) {
let tab = vAPI.tabs.manager.tabFromBrowser(browser);
-
+
if (tab === null) {
- continue;
+ continue;
}
-
+
if (tab.hasAttribute('pending')) {
- continue;
+ continue;
}
-
+
tabs.push({
- id: vAPI.tabs.manager.tabIdFromTarget(browser),
- url: browser.currentURI.asciiSpec
+ id: vAPI.tabs.manager.tabIdFromTarget(browser),
+ url: browser.currentURI.asciiSpec
});
- }
+ }
- callback(tabs);
+ callback(tabs);
};
vAPI.tabs.open = function (details) {
- // properties of the details object:
- // + url - the address that will be opened
- // + tabId:- the tab is used if set, instead of creating a new one
- // + index: - undefined: end of the list, -1: following tab, or
- // after index
- // + active: - opens the tab in background - true and undefined:
- // foreground
- // + select: - if a tab is already opened with that url, then
- // select it instead of opening a new one
- if (!details.url) {
+ // properties of the details object:
+ // + url - the address that will be opened
+ // + tabId:- the tab is used if set, instead of creating a new one
+ // + index: - undefined: end of the list, -1: following tab, or
+ // after index
+ // + active: - opens the tab in background - true and undefined:
+ // foreground
+ // + select: - if a tab is already opened with that url, then
+ // select it instead of opening a new one
+ if (!details.url) {
return null;
- }
-
- // extension pages
- if (/^[\w-]{2,}:/.test(details.url) === false) {
+ }
+
+ // extension pages
+ if (/^[\w-]{2,}:/.test(details.url) === false) {
details.url = vAPI.getURL(details.url);
- }
+ }
- if (details.select) {
+ if (details.select) {
let URI = Services.io.newURI(details.url, null, null);
for (let tab of this.getAllSync()) {
- let browser = vAPI.tabs.manager.browserFromTarget(tab);
-
- // https://github.com/gorhill/uBlock/issues/2558
- if (browser === null) {
- continue;
- }
-
- // Or simply .equals if we care about the fragment
- if (URI.equalsExceptRef(browser.currentURI) === false) {
+ let browser = vAPI.tabs.manager.browserFromTarget(tab);
+
+ // https://github.com/gorhill/uBlock/issues/2558
+ if (browser === null) {
continue;
- }
+ }
- this.select(tab);
+ // Or simply .equals if we care about the fragment
+ if (URI.equalsExceptRef(browser.currentURI) === false) {
+ continue;
+ }
- // Update URL if fragment is different
- if (URI.equals(browser.currentURI) === false) {
+ this.select(tab);
+
+ // Update URL if fragment is different
+ if (URI.equals(browser.currentURI) === false) {
browser.loadURI(URI.asciiSpec);
- }
-
- return;
+ }
+
+ return;
}
- }
+ }
- if (details.active === undefined) {
+ if (details.active === undefined) {
details.active = true;
- }
+ }
- if (details.tabId) {
+ if (details.tabId) {
let tab = vAPI.tabs.manager.browserFromTabId(details.tabId);
-
+
if (tab) {
- vAPI.tabs.manager.browserFromTarget(tab).loadURI(details.url);
- return;
+ vAPI.tabs.manager.browserFromTarget(tab).loadURI(details.url);
+ return;
}
- }
+ }
- // Open in a standalone window
- if (details.popup === true) {
+ // Open in a standalone window
+ if (details.popup === true) {
Services.ww.openWindow(self,
- details.url,
- null,
- 'location=1,menubar=1,personalbar=1,'
- +'resizable=1,toolbar=1',
- null);
+ details.url,
+ null,
+ 'location=1,menubar=1,personalbar=1,'
+ +'resizable=1,toolbar=1',
+ null);
return;
- }
+ }
- let win = vAPI.window.getCurrentWindow();
- let tabBrowser = vAPI.browser.getTabBrowser(win);
-
- if (tabBrowser === null) {
+ let win = vAPI.window.getCurrentWindow();
+ let tabBrowser = vAPI.browser.getTabBrowser(win);
+
+ if (tabBrowser === null) {
return;
- }
+ }
- if (details.index === -1) {
+ if (details.index === -1) {
details.index =
- tabBrowser.browsers.indexOf(tabBrowser.selectedBrowser) + 1;
- }
+ tabBrowser.browsers.indexOf(tabBrowser.selectedBrowser) + 1;
+ }
- let tab = tabBrowser.loadOneTab(details.url, {
- inBackground: !details.active
- });
+ let tab = tabBrowser.loadOneTab(details.url, {
+ inBackground: !details.active
+ });
- if (details.index !== undefined) {
+ if (details.index !== undefined) {
tabBrowser.moveTabTo(tab, details.index);
- }
+ }
};
vAPI.tabs.replace = function (tabId, url) {
- // Replace the URL of a tab. Noop if the tab does not exist.
- let targetURL = url;
+ // Replace the URL of a tab. Noop if the tab does not exist.
+ let targetURL = url;
- // extension pages
- if (/^[\w-]{2,}:/.test(targetURL) !== true) {
+ // extension pages
+ if (/^[\w-]{2,}:/.test(targetURL) !== true) {
targetURL = vAPI.getURL(targetURL);
- }
+ }
- let browser = vAPI.tabs.manager.browserFromTabId(tabId);
- if (browser) {
+ let browser = vAPI.tabs.manager.browserFromTabId(tabId);
+ if (browser) {
browser.loadURI(targetURL);
- }
+ }
};
function removeInternal(tab, tabBrowser) {
- if (tabBrowser) {
- tabBrowser.removeTab(tab);
- }
+ if (tabBrowser) {
+ tabBrowser.removeTab(tab);
+ }
}
vAPI.tabs.remove = function (tabId) {
- let browser = vAPI.tabs.manager.browserFromTabId(tabId);
- if (!browser) {
+ let browser = vAPI.tabs.manager.browserFromTabId(tabId);
+ if (!browser) {
return;
- }
-
- let tab = vAPI.tabs.manager.tabFromBrowser(browser);
- if (!tab) {
+ }
+
+ let tab = vAPI.tabs.manager.tabFromBrowser(browser);
+ if (!tab) {
return;
- }
-
- removeInternal(tab,
- vAPI.browser.getTabBrowser
- (vAPI.browser.getOwnerWindow(browser)));
+ }
+
+ removeInternal(tab,
+ vAPI.browser.getTabBrowser
+ (vAPI.browser.getOwnerWindow(browser)));
};
vAPI.tabs.reload = function (tabId) {
- let browser = vAPI.tabs.manager.browserFromTabId(tabId);
- if (!browser) {
+ let browser = vAPI.tabs.manager.browserFromTabId(tabId);
+ if (!browser) {
return;
- }
+ }
- browser.webNavigation.reload(Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE);
+ browser.webNavigation.reload(Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE);
};
vAPI.tabs.select = function (tab) {
- if (typeof tab !== 'object') {
+ if (typeof tab !== 'object') {
tab = vAPI.tabs.manager
- .tabFromBrowser(vAPI.tabs.manager.browserFromTabId(tab));
- }
- if (!tab) {
+ .tabFromBrowser(vAPI.tabs.manager.browserFromTabId(tab));
+ }
+ if (!tab) {
return;
- }
+ }
- // https://github.com/gorhill/uBlock/issues/470
- let win = vAPI.browser.getOwnerWindow(tab);
- win.focus();
+ // https://github.com/gorhill/uBlock/issues/470
+ let win = vAPI.browser.getOwnerWindow(tab);
+ win.focus();
- let tabBrowser = vAPI.browser.getTabBrowser(win);
- if (tabBrowser) {
+ let tabBrowser = vAPI.browser.getTabBrowser(win);
+ if (tabBrowser) {
tabBrowser.selectedTab = tab;
- }
+ }
};
vAPI.tabs.injectScript = function (tabId, details, callback) {
- let browser = vAPI.tabs.manager.browserFromTabId(tabId);
- if (!browser) {
+ let browser = vAPI.tabs.manager.browserFromTabId(tabId);
+ if (!browser) {
return;
- }
+ }
- if (typeof details.file !== 'string') {
+ if (typeof details.file !== 'string') {
return;
- }
-
- details.file = vAPI.getURL(details.file);
- browser.messageManager.sendAsyncMessage(location.host + ':broadcast',
- JSON.stringify({
- broadcast: true,
- channelName: 'vAPI',
- msg: {
- cmd: 'injectScript',
- details: details
- }
- }));
-
- if (typeof callback === 'function') {
+ }
+
+ details.file = vAPI.getURL(details.file);
+ browser.messageManager.sendAsyncMessage(location.host + ':broadcast',
+ JSON.stringify({
+ broadcast: true,
+ channelName: 'vAPI',
+ msg: {
+ cmd: 'injectScript',
+ details: details
+ }
+ }));
+
+ if (typeof callback === 'function') {
vAPI.setTimeout(callback, 13);
- }
+ }
};
vAPI.tabs.manager = (function () {
- // TODO: find out whether we need a janitor to take care of stale entries.
+ // TODO: find out whether we need a janitor to take care of stale entries.
- // https://github.com/gorhill/uMatrix/issues/540
- // Use only weak references to hold onto browser references.
- let browserToTabIdMap = new WeakMap();
- let tabIdToBrowserMap = new Map();
- let tabIdGenerator = 1;
+ // https://github.com/gorhill/uMatrix/issues/540
+ // Use only weak references to hold onto browser references.
+ let browserToTabIdMap = new WeakMap();
+ let tabIdToBrowserMap = new Map();
+ let tabIdGenerator = 1;
- let indexFromBrowser = function (browser) {
+ let indexFromBrowser = function (browser) {
if (!browser) {
- return -1;
+ return -1;
}
- let win = vAPI.browser.getOwnerWindow(browser);
+ let win = vAPI.browser.getOwnerWindow(browser);
if (!win) {
- return -1;
+ return -1;
}
-
+
let tabBrowser = vAPI.browser.getTabBrowser(win);
if (tabBrowser === null) {
- return -1;
+ return -1;
}
-
+
// This can happen, for example, the `view-source:`
// window, there is no tabbrowser object, the browser
// object sits directly in the window.
if (tabBrowser === browser) {
- return 0;
+ return 0;
}
-
+
return tabBrowser.browsers.indexOf(browser);
- };
+ };
- let indexFromTarget = function (target) {
+ let indexFromTarget = function (target) {
return indexFromBrowser(browserFromTarget(target));
- };
+ };
- let tabFromBrowser = function (browser) {
+ let tabFromBrowser = function (browser) {
let i = indexFromBrowser(browser);
if (i === -1) {
- return null;
+ return null;
}
-
+
let win = vAPI.browser.getOwnerWindow(browser);
if (!win) {
- return null;
+ return null;
}
-
+
let tabBrowser = vAPI.browser.getTabBrowser(win);
if (tabBrowser === null) {
- return null;
+ return null;
}
-
+
if (!tabBrowser.tabs || i >= tabBrowser.tabs.length) {
- return null;
+ return null;
}
-
+
return tabBrowser.tabs[i];
- };
+ };
- let browserFromTarget = function (target) {
+ let browserFromTarget = function (target) {
if (!target) {
- return null;
+ return null;
}
-
+
if (target.linkedPanel) {
- // target is a tab
- target = target.linkedBrowser;
+ // target is a tab
+ target = target.linkedBrowser;
}
-
+
if (target.localName !== 'browser') {
- return null;
+ return null;
}
-
+
return target;
- };
+ };
- let tabIdFromTarget = function (target) {
+ let tabIdFromTarget = function (target) {
let browser = browserFromTarget(target);
if (browser === null) {
- return vAPI.noTabId;
+ return vAPI.noTabId;
}
-
+
let tabId = browserToTabIdMap.get(browser);
if (tabId === undefined) {
- tabId = '' + tabIdGenerator++;
- browserToTabIdMap.set(browser, tabId);
- tabIdToBrowserMap.set(tabId, Cu.getWeakReference(browser));
+ tabId = '' + tabIdGenerator++;
+ browserToTabIdMap.set(browser, tabId);
+ tabIdToBrowserMap.set(tabId, Cu.getWeakReference(browser));
}
-
+
return tabId;
- };
+ };
- let browserFromTabId = function (tabId) {
+ let browserFromTabId = function (tabId) {
let weakref = tabIdToBrowserMap.get(tabId);
let browser = weakref && weakref.get();
-
+
return browser || null;
- };
+ };
- let currentBrowser = function () {
+ let currentBrowser = function () {
let win = vAPI.window.getCurrentWindow();
-
+
// https://github.com/gorhill/uBlock/issues/399
// getTabBrowser() can return null at browser launch time.
let tabBrowser = vAPI.browser.getTabBrowser(win);
if (tabBrowser === null) {
- return null;
+ return null;
}
-
+
return browserFromTarget(tabBrowser.selectedTab);
- };
+ };
- let removeBrowserEntry = function (tabId, browser) {
+ let removeBrowserEntry = function (tabId, browser) {
if (tabId && tabId !== vAPI.noTabId) {
- vAPI.tabs.onClosed(tabId);
- delete vAPI.toolbarButton.tabs[tabId];
- tabIdToBrowserMap.delete(tabId);
+ vAPI.tabs.onClosed(tabId);
+ delete vAPI.toolbarButton.tabs[tabId];
+ tabIdToBrowserMap.delete(tabId);
}
-
+
if (browser) {
- browserToTabIdMap.delete(browser);
+ browserToTabIdMap.delete(browser);
}
- };
+ };
- let removeTarget = function (target) {
+ let removeTarget = function (target) {
onClose({
- target: target
- });
- };
+ target: target
+ });
+ };
- let getAllBrowsers = function () {
+ let getAllBrowsers = function () {
let browsers = [];
- for (let [tabId, weakref] of tabIdToBrowserMap) {
- let browser = weakref.get();
-
- // TODO: Maybe call removeBrowserEntry() if the
- // browser no longer exists?
- if (browser) {
+ for (let [tabId, weakref] of tabIdToBrowserMap) {
+ let browser = weakref.get();
+
+ // TODO: Maybe call removeBrowserEntry() if the
+ // browser no longer exists?
+ if (browser) {
browsers.push(browser);
- }
+ }
}
-
+
return browsers;
- };
-
- // var onOpen = function (target) {
- // var tabId = tabIdFromTarget(target);
- // var browser = browserFromTabId(tabId);
- // vAPI.tabs.onNavigation({
- // frameId: 0,
- // tabId: tabId,
- // url: browser.currentURI.asciiSpec,
- // });
- // };
-
- var onShow = function ({target}) {
+ };
+
+ // var onOpen = function (target) {
+ // var tabId = tabIdFromTarget(target);
+ // var browser = browserFromTabId(tabId);
+ // vAPI.tabs.onNavigation({
+ // frameId: 0,
+ // tabId: tabId,
+ // url: browser.currentURI.asciiSpec,
+ // });
+ // };
+
+ var onShow = function ({target}) {
tabIdFromTarget(target);
- };
+ };
- var onClose = function ({target}) {
+ var onClose = function ({target}) {
// target is tab in Firefox, browser in Fennec
let browser = browserFromTarget(target);
let tabId = browserToTabIdMap.get(browser);
removeBrowserEntry(tabId, browser);
- };
+ };
- var onSelect = function ({target}) {
- // This is an entry point: when creating a new tab, it is
- // not always reported through onLocationChanged...
- // Sigh. It is "reported" here however.
+ var onSelect = function ({target}) {
+ // This is an entry point: when creating a new tab, it is
+ // not always reported through onLocationChanged...
+ // Sigh. It is "reported" here however.
let browser = browserFromTarget(target);
let tabId = browserToTabIdMap.get(browser);
- if (tabId === undefined) {
- tabId = tabIdFromTarget(target);
- vAPI.tabs.onNavigation({
+ if (tabId === undefined) {
+ tabId = tabIdFromTarget(target);
+ vAPI.tabs.onNavigation({
frameId: 0,
tabId: tabId,
url: browser.currentURI.asciiSpec
- });
+ });
}
-
+
vAPI.setIcon(tabId, vAPI.browser.getOwnerWindow(target));
- };
+ };
- let locationChangedMessageName = location.host + ':locationChanged';
+ let locationChangedMessageName = location.host + ':locationChanged';
- let onLocationChanged = function (e) {
+ let onLocationChanged = function (e) {
let details = e.data;
// Ignore notifications related to our popup
if (details.url.lastIndexOf(vAPI.getURL('popup.html'), 0) === 0) {
- return;
+ return;
}
let browser = e.target;
let tabId = tabIdFromTarget(browser);
if (tabId === vAPI.noTabId) {
- return;
+ return;
}
// LOCATION_CHANGE_SAME_DOCUMENT = "did not load a new document"
if (details.flags
- & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT) {
- vAPI.tabs.onUpdated(tabId, {url: details.url}, {
+ & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT) {
+ vAPI.tabs.onUpdated(tabId, {url: details.url}, {
frameId: 0,
tabId: tabId,
url: browser.currentURI.asciiSpec
- });
- return;
+ });
+ return;
}
// https://github.com/chrisaljoudi/uBlock/issues/105
// Allow any kind of pages
vAPI.tabs.onNavigation({
- frameId: 0,
- tabId: tabId,
- url: details.url
+ frameId: 0,
+ tabId: tabId,
+ url: details.url
});
- };
+ };
- let attachToTabBrowser = function (window) {
+ let attachToTabBrowser = function (window) {
if (typeof vAPI.toolbarButton.attachToNewWindow === 'function') {
- vAPI.toolbarButton.attachToNewWindow(window);
+ vAPI.toolbarButton.attachToNewWindow(window);
}
let tabBrowser = vAPI.browser.getTabBrowser(window);
if (tabBrowser === null) {
- return;
+ return;
}
let tabContainer;
if (tabBrowser.deck) {
// Fennec
- tabContainer = tabBrowser.deck;
+ tabContainer = tabBrowser.deck;
} else if (tabBrowser.tabContainer) {
- // Firefox
- tabContainer = tabBrowser.tabContainer;
+ // Firefox
+ tabContainer = tabBrowser.tabContainer;
}
// https://github.com/gorhill/uBlock/issues/697
@@ -579,51 +579,51 @@
// of session restore -- it is set *after* the event is
// fired in such case.
if (tabContainer) {
- tabContainer.addEventListener('TabShow', onShow);
- tabContainer.addEventListener('TabClose', onClose);
- // when new window is opened TabSelect doesn't run on
- // the selected tab?
- tabContainer.addEventListener('TabSelect', onSelect);
- }
- };
-
- var canAttachToTabBrowser = function (window) {
- // https://github.com/gorhill/uBlock/issues/906
- // Ensure the environment is ready before trying to attaching.
+ tabContainer.addEventListener('TabShow', onShow);
+ tabContainer.addEventListener('TabClose', onClose);
+ // when new window is opened TabSelect doesn't run on
+ // the selected tab?
+ tabContainer.addEventListener('TabSelect', onSelect);
+ }
+ };
+
+ var canAttachToTabBrowser = function (window) {
+ // https://github.com/gorhill/uBlock/issues/906
+ // Ensure the environment is ready before trying to attaching.
let document = window && window.document;
if (!document || document.readyState !== 'complete') {
- return false;
+ return false;
}
// On some platforms, the tab browser isn't immediately
- // available, try waiting a bit if this
+ // available, try waiting a bit if this
// https://github.com/gorhill/uBlock/issues/763
// Not getting a tab browser should not prevent from
// attaching ourself to the window.
let tabBrowser = vAPI.browser.getTabBrowser(window);
if (tabBrowser === null) {
- return false;
+ return false;
}
return vAPI.window.toBrowserWindow(window) !== null;
- };
+ };
- let onWindowLoad = function (win) {
+ let onWindowLoad = function (win) {
vAPI.deferUntil(canAttachToTabBrowser.bind(null, win),
- attachToTabBrowser.bind(null, win));
- };
+ attachToTabBrowser.bind(null, win));
+ };
- let onWindowUnload = function (win) {
+ let onWindowUnload = function (win) {
let tabBrowser = vAPI.browser.getTabBrowser(win);
if (tabBrowser === null) {
- return;
+ return;
}
let tabContainer = tabBrowser.tabContainer;
if (tabContainer) {
- tabContainer.removeEventListener('TabShow', onShow);
- tabContainer.removeEventListener('TabClose', onClose);
- tabContainer.removeEventListener('TabSelect', onSelect);
+ tabContainer.removeEventListener('TabShow', onShow);
+ tabContainer.removeEventListener('TabClose', onClose);
+ tabContainer.removeEventListener('TabSelect', onSelect);
}
// https://github.com/gorhill/uBlock/issues/574
@@ -631,85 +631,85 @@
// sometimes the window IS the tab.
let tabs;
if (tabBrowser.tabs) {
- tabs = tabBrowser.tabs;
+ tabs = tabBrowser.tabs;
} else if (tabBrowser.localName === 'browser') {
- tabs = [tabBrowser];
+ tabs = [tabBrowser];
} else {
- tabs = [];
+ tabs = [];
}
let browser;
- let URI;
- let tabId;
- for (let i=tabs.length-1; i>=0; --i) {
- let tab = tabs[i];
- browser = browserFromTarget(tab);
- if (browser === null) {
+ let URI;
+ let tabId;
+ for (let i=tabs.length-1; i>=0; --i) {
+ let tab = tabs[i];
+ browser = browserFromTarget(tab);
+ if (browser === null) {
continue;
- }
-
- URI = browser.currentURI;
- // Close extension tabs
- if (URI.schemeIs('chrome') && URI.host === location.host) {
+ }
+
+ URI = browser.currentURI;
+ // Close extension tabs
+ if (URI.schemeIs('chrome') && URI.host === location.host) {
removeInternal(tab, vAPI.browser.getTabBrowser(win));
- }
-
- tabId = browserToTabIdMap.get(browser);
- if (tabId !== undefined) {
+ }
+
+ tabId = browserToTabIdMap.get(browser);
+ if (tabId !== undefined) {
removeBrowserEntry(tabId, browser);
tabIdToBrowserMap.delete(tabId);
- }
- browserToTabIdMap.delete(browser);
+ }
+ browserToTabIdMap.delete(browser);
}
- };
+ };
- var start = function () {
- // Initialize map with existing active tabs
+ var start = function () {
+ // Initialize map with existing active tabs
let tabBrowser;
- let tabs;
+ let tabs;
for (let win of vAPI.window.getWindows()) {
- onWindowLoad(win);
-
- tabBrowser = vAPI.browser.getTabBrowser(win);
- if (tabBrowser === null) {
+ onWindowLoad(win);
+
+ tabBrowser = vAPI.browser.getTabBrowser(win);
+ if (tabBrowser === null) {
continue;
- }
-
- for (let tab of tabBrowser.tabs) {
+ }
+
+ for (let tab of tabBrowser.tabs) {
if (!tab.hasAttribute('pending')) {
- tabIdFromTarget(tab);
+ tabIdFromTarget(tab);
}
- }
+ }
}
vAPI.window.onOpenWindow = onWindowLoad;
vAPI.window.onCloseWindow = onWindowUnload;
vAPI.messaging.globalMessageManager
- .addMessageListener(locationChangedMessageName,
- onLocationChanged);
- };
+ .addMessageListener(locationChangedMessageName,
+ onLocationChanged);
+ };
- let stop = function () {
+ let stop = function () {
vAPI.window.onOpenWindow = null;
vAPI.window.onCloseWindow = null;
vAPI.messaging.globalMessageManager
- .removeMessageListener(locationChangedMessageName,
- onLocationChanged);
+ .removeMessageListener(locationChangedMessageName,
+ onLocationChanged);
for (let win of vAPI.window.getWindows()) {
- onWindowUnload(win);
+ onWindowUnload(win);
}
browserToTabIdMap = new WeakMap();
tabIdToBrowserMap.clear();
- };
+ };
- vAPI.addCleanUpTask(stop);
+ vAPI.addCleanUpTask(stop);
- return {
+ return {
browsers: getAllBrowsers,
browserFromTabId: browserFromTabId,
browserFromTarget: browserFromTarget,
@@ -719,6 +719,6 @@
start: start,
tabFromBrowser: tabFromBrowser,
tabIdFromTarget: tabIdFromTarget
- };
+ };
})();
})();
diff --git a/js/vapi-window.js b/js/vapi-window.js
index 1619ead..c512135 100644
--- a/js/vapi-window.js
+++ b/js/vapi-window.js
@@ -27,157 +27,157 @@
(function () {
vAPI.window = (function () {
- let windowToIdMap = new Map();
- let windowIdGenerator = 1;
- let api = {
+ let windowToIdMap = new Map();
+ let windowIdGenerator = 1;
+ let api = {
onOpenWindow: null,
onCloseWindow: null
- };
-
- // https://github.com/gorhill/uMatrix/issues/586 This is
- // necessary hack because on SeaMonkey 2.40, for unknown
- // reasons private windows do not have the attribute
- // `windowtype` set to `navigator:browser`. As a fallback, the
- // code here will also test whether the id attribute is
- // `main-window`.
- api.toBrowserWindow = function (win) {
+ };
+
+ // https://github.com/gorhill/uMatrix/issues/586 This is
+ // necessary hack because on SeaMonkey 2.40, for unknown
+ // reasons private windows do not have the attribute
+ // `windowtype` set to `navigator:browser`. As a fallback, the
+ // code here will also test whether the id attribute is
+ // `main-window`.
+ api.toBrowserWindow = function (win) {
let docElement = win && win.document
- && win.document.documentElement;
-
+ && win.document.documentElement;
+
if (!docElement) {
- return null;
+ return null;
}
if (vAPI.thunderbird) {
- return docElement.getAttribute('windowtype') === 'mail:3pane'
- ? win
- : null;
+ return docElement.getAttribute('windowtype') === 'mail:3pane'
+ ? win
+ : null;
}
-
+
return docElement.getAttribute('windowtype') === 'navigator:browser'
- || docElement.getAttribute('id') === 'main-window'
- ? win
- : null;
- };
+ || docElement.getAttribute('id') === 'main-window'
+ ? win
+ : null;
+ };
- api.getWindows = function () {
+ api.getWindows = function () {
return windowToIdMap.keys();
- };
+ };
- api.idFromWindow = function (win) {
+ api.idFromWindow = function (win) {
return windowToIdMap.get(win) || 0;
- };
+ };
- api.getCurrentWindow = function () {
+ api.getCurrentWindow = function () {
return this.toBrowserWindow(Services.wm.getMostRecentWindow(null));
- };
+ };
- let addWindow = function (win) {
+ let addWindow = function (win) {
if (!win || windowToIdMap.has(win)) {
- return;
+ return;
}
-
+
windowToIdMap.set(win, windowIdGenerator++);
-
+
if (typeof api.onOpenWindow === 'function') {
- api.onOpenWindow(win);
+ api.onOpenWindow(win);
}
- };
+ };
- let removeWindow = function (win) {
+ let removeWindow = function (win) {
if (!win || windowToIdMap.delete(win) !== true) {
- return;
+ return;
}
-
+
if (typeof api.onCloseWindow === 'function') {
- api.onCloseWindow(win);
+ api.onCloseWindow(win);
}
- };
+ };
- // https://github.com/gorhill/uMatrix/issues/357
- // Use nsIWindowMediator for being notified of opened/closed windows.
- let listeners = {
+ // https://github.com/gorhill/uMatrix/issues/357
+ // Use nsIWindowMediator for being notified of opened/closed windows.
+ let listeners = {
onOpenWindow: function (aWindow) {
- let win;
- try {
+ let win;
+ try {
win = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindow);
- } catch (e) {
- // Ignore
- }
-
- addWindow(win);
+ } catch (e) {
+ // Ignore
+ }
+
+ addWindow(win);
},
onCloseWindow: function (aWindow) {
- let win;
- try {
+ let win;
+ try {
win = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindow);
- } catch (e) {
- // Ignore
- }
-
- removeWindow(win);
+ } catch (e) {
+ // Ignore
+ }
+
+ removeWindow(win);
},
observe: function (aSubject, topic) {
- let win;
- try {
+ let win;
+ try {
win = aSubject.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindow);
- } catch (e) {
- // Ignore
- }
-
- if (!win) {
- return;
- }
-
- switch (topic) {
- case 'domwindowopened':
- addWindow(win);
- break;
- case 'domwindowclosed':
- removeWindow(win);
- break;
- default:
- console.error('unknown observer topic');
- break;
- }
+ } catch (e) {
+ // Ignore
+ }
+
+ if (!win) {
+ return;
+ }
+
+ switch (topic) {
+ case 'domwindowopened':
+ addWindow(win);
+ break;
+ case 'domwindowclosed':
+ removeWindow(win);
+ break;
+ default:
+ console.error('unknown observer topic');
+ break;
+ }
}
- };
+ };
- (function() {
+ (function() {
let winumerator;
winumerator = Services.wm.getEnumerator(null);
while (winumerator.hasMoreElements()) {
- let win = winumerator.getNext();
-
- if (!win.closed) {
+ let win = winumerator.getNext();
+
+ if (!win.closed) {
windowToIdMap.set(win, windowIdGenerator++);
- }
+ }
}
winumerator = Services.ww.getWindowEnumerator();
while (winumerator.hasMoreElements()) {
- let win = winumerator.getNext()
+ let win = winumerator.getNext()
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindow);
-
- if (!win.closed) {
+
+ if (!win.closed) {
windowToIdMap.set(win, windowIdGenerator++);
- }
+ }
}
Services.wm.addListener(listeners);
Services.ww.registerNotification(listeners);
- })();
+ })();
- vAPI.addCleanUpTask(function() {
+ vAPI.addCleanUpTask(function() {
Services.wm.removeListener(listeners);
Services.ww.unregisterNotification(listeners);
windowToIdMap.clear();
- });
+ });
- return api;
+ return api;
})();
})();
diff --git a/js/xal.js b/js/xal.js
index ec37a6c..3f30ce2 100644
--- a/js/xal.js
+++ b/js/xal.js
@@ -29,47 +29,47 @@
ηMatrix.XAL = (function(){
-/******************************************************************************/
+ /******************************************************************************/
-var exports = {};
-var noopFunc = function(){};
+ var exports = {};
+ var noopFunc = function(){};
-/******************************************************************************/
+ /******************************************************************************/
-exports.keyvalSetOne = function(key, val, callback) {
- var bin = {};
- bin[key] = val;
- vAPI.storage.set(bin, callback || noopFunc);
-};
+ exports.keyvalSetOne = function(key, val, callback) {
+ var bin = {};
+ bin[key] = val;
+ vAPI.storage.set(bin, callback || noopFunc);
+ };
-/******************************************************************************/
+ /******************************************************************************/
-exports.keyvalGetOne = function(key, callback) {
- vAPI.storage.get(key, callback);
-};
+ exports.keyvalGetOne = function(key, callback) {
+ vAPI.storage.get(key, callback);
+ };
-/******************************************************************************/
+ /******************************************************************************/
-exports.keyvalSetMany = function(dict, callback) {
- vAPI.storage.set(dict, callback || noopFunc);
-};
+ exports.keyvalSetMany = function(dict, callback) {
+ vAPI.storage.set(dict, callback || noopFunc);
+ };
-/******************************************************************************/
+ /******************************************************************************/
-exports.keyvalRemoveOne = function(key, callback) {
- vAPI.storage.remove(key, callback || noopFunc);
-};
+ exports.keyvalRemoveOne = function(key, callback) {
+ vAPI.storage.remove(key, callback || noopFunc);
+ };
-/******************************************************************************/
+ /******************************************************************************/
-exports.keyvalRemoveAll = function(callback) {
- vAPI.storage.clear(callback || noopFunc);
-};
+ exports.keyvalRemoveAll = function(callback) {
+ vAPI.storage.clear(callback || noopFunc);
+ };
-/******************************************************************************/
+ /******************************************************************************/
-return exports;
+ return exports;
-/******************************************************************************/
+ /******************************************************************************/
})();