diff options
Diffstat (limited to 'js')
50 files changed, 6725 insertions, 6959 deletions
diff --git a/js/about.js b/js/about.js index a35b45c..9ada80e 100644 --- a/js/about.js +++ b/js/about.js @@ -2,7 +2,7 @@ ηMatrix - a browser extension to black/white list requests. Copyright (C) 2014-2019 Raymond Hill - Copyright (C) 2019-2020 Alessio Vanni + Copyright (C) 2019-2022 Alessio Vanni This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/js/asset-viewer.js b/js/asset-viewer.js index 8998892..12f0e3b 100644 --- a/js/asset-viewer.js +++ b/js/asset-viewer.js @@ -2,7 +2,7 @@ ηMatrix - a browser extension to black/white list requests. Copyright (C) 2014-2019 Raymond Hill - Copyright (C) 2019-2020 Alessio Vanni + Copyright (C) 2019-2022 Alessio Vanni This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see {http://www.gnu.org/licenses/}. - Home: https://libregit.spks.xyz/heckyel/ematrix + Home: https://gitlab.com/vannilla/ematrix uMatrix Home: https://github.com/gorhill/uMatrix */ @@ -36,7 +36,7 @@ } vAPI.messaging.send('asset-viewer.js', { - what : 'getAssetContent', - url: matches[1] + what : 'getAssetContent', + url: matches[1] }, onAssetContentReceived); })(); diff --git a/js/assets.js b/js/assets.js index 286cf0d..9bb2470 100644 --- a/js/assets.js +++ b/js/assets.js @@ -2,7 +2,7 @@ ηMatrix - a browser extension to black/white list requests. Copyright (C) 2013-2019 Raymond Hill - Copyright (C) 2019-2020 Alessio Vanni + Copyright (C) 2019-2022 Alessio Vanni This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,14 +17,12 @@ You should have received a copy of the GNU General Public License along with this program. If not, see {http://www.gnu.org/licenses/}. - Home: https://libregit.spks.xyz/heckyel/ematrix + Home: https://gitlab.com/vannilla/ematrix uMatrix Home: https://github.com/gorhill/uMatrix */ 'use strict'; -/******************************************************************************/ - ηMatrix.assets = (function() { let api = {}; @@ -33,21 +31,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 +55,257 @@ 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; + + let save = function () { + 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]; + + let onRead = function (bin) { + if (!bin || !bin['assetCacheRegistry']) { + cacheRegistry = {}; + } else { + cacheRegistry = bin['assetCacheRegistry']; + } + + cacheRegistryReady = true; + + let f; + while ((f = cacheRegistryCallbacks.shift())) { + f(cacheRegistry); + } + }; + + vAPI.cacheStorage.get('assetCacheRegistry', onRead); }; 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 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 cacheRemove = function (pattern, callback) { - let onReady = function (cache) { - let entries = []; + let fullkey = 'cache/' + key; + let cache = { placeholder: true }; - for (let key in cache) { - if (pattern instanceof RegExp && !pattern.test(key)) { - continue; - } + let report = function (content, error) { + let details = { + assetKey: key, + content: content, + }; - if (typeof pattern === 'string' && key !== pattern) { - continue; - } + if (error) { + details.error = error; + } - entries.push(key); + return callback(details); + }; - delete cache[key]; - } + let onRead = function (bin) { + if (!bin || !bin[fullkey]) { + return report('', 'E_NOTFOUND'); + } - if (entries.length !== 0) { - vAPI.cacheStorage.remove(content); + let entry = cache[key]; + if (entry === undefined) { + return report('', 'E_NOTFOUND'); + } - let bin = { - assetCacheRegistry: cache, - }; - vAPI.cacheStorage.set(bin); - } + entry.readTime = Date.now(); + saveCacheRegistry(true); - if (typeof callback === 'function') { - callback(); - } + report(bin[fullkey]); + }; - for (let i=0; i<entries.length; ++i) { - notifyObservers('after-asset-updated', { - assetKey: entries[i], - }); - } - }; + let onReady = function (registry) { + cache = registry; + vAPI.cacheStorage.get(fullkey, onRead); + }; - 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 writeCache = function (key, details, callback) { + let fullkey = 'cache/' + key; + let content = ''; + + if (typeof details === 'string') { + content = details; + } else if (details instanceof Object) { + content = details.content || ''; + } + + if (content === '') { + return removeCache(key, callback); + } + + let report = function (content) { + let details = { + assetKey: key, + content: content, + }; + + if (typeof callback === 'function') { + callback(details); + } + + notifyObservers('after-asset-updated', details); + }; + + let onReady = function (registry) { + let entry = registry[key]; + if (entry === undefined) { + entry = registry[key] = {}; + } + + entry.writeTime = entry.readTime = Date.now(); + if (details instanceof Object && typeof details.url === 'string') { + entry.remoteURL = details.url; + } + + let bin = { + assetCacheRegistry: cacheRegistry, + }; + + bin[fullkey] = content; + vAPI.cacheStorage.set(bin); + report(content); + }; + + getCacheRegistry(onReady); }; - let removeCache = function (key, callback) { - let onReady = function (cacheRegistry) { - let removedEntries = []; - let removedContent = []; + let removeCache = function (pattern, callback) { + let onReady = function (cache) { + let entries = []; + let contents = []; + + for (let key in cache) { + if (pattern instanceof RegExp && !pattern.test(key)) { + continue; + } - for (let k in cacheRegistry) { - if (key instanceof RegExp && !key.test(k)) { - continue; - } - if (typeof key === 'string' && k !== key) { - continue; - } + if (typeof pattern === 'string' && key !== pattern) { + continue; + } - removedEntries.push(k); - removedContent.push(k); + entries.push(key); + contents.push('cache/' + key); - delete cacheRegistry[k]; - } + delete cache[key]; + } - if (removedContent.length > 0) { - vAPI.cacheStorage.remove(removedContent); + if (contents.length !== 0) { + vAPI.cacheStorage.remove(contents); - let bin = { - assetCacheRegistry: cacheRegistry, - }; + let bin = { + assetCacheRegistry: cache, + }; + vAPI.cacheStorage.set(bin); + } - 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<removedEntries.length; ++i) { - notifyObservers('after-asset-updated', { - assetKey: removedEntries[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); }; // Source Registry @@ -367,248 +314,252 @@ 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; + + let save = function () { + 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; + } + + if (json instanceof Object === false) { + 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; - } else { - assetDetails.lastError = undefined; - } - 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 = details.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 urls = []; + + let report = function (content, error) { + let details = { + assetKey: key, + content: content, + }; + if (error) { + details.error = assetDetails.lastError = error; + } else { + assetDetails.lastError = undefined; + } + callback(details); + }; + + let tryLoad = function (tries) { + let tr = (tries === undefined) ? 10 : tries; + + if (tr <= 0) { + console.warn('ηMatrix could not load the asset ' + +assetDetails.title); + return; + } + + 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' + } + }); + return tryLoad(tries); + } + + writeCache(key, { + content: details.content, + url: contentUrl, + }); + + registerSource(key, {error: undefined}); + report(details.content); + }; + + let onRemoteContentError = function (details, tries) { + let text = details.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] || {}; + if (typeof assetDetails.contentURL === 'string') { + urls = [assetDetails.contentURL]; + } else if (Array.isArray(assetDetails.contentURL)) { + urls = assetDetails.contentURL.slice(0); + } + tryLoad(); + }; + + getSourceRegistry(onReady); }; // Updater @@ -620,443 +571,446 @@ 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 cacheReg = undefined; - let sourceReg = undefined; - - let gcOne = function (key) { - let entry = cacheReg[key]; - if (entry && entry.readTime < cacheRegistryStart) { - cacheRemove(key); - } - }; - - let findOne = function () { - let now = Date.now(); - let sourceEntry; - let cacheEntry; - - for (let key in sourceReg) { - sourceEntry = sourceReg[key]; - if (sourceEntry.hasRemoteURL !== true) { - continue; - } - if (updateFetch.has(key)) { - continue; - } - - cacheEntry = cacheReg[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 === 'assets.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) { - sourceReg = registry; - updateOne(); - }; - - let onCacheReady = function (registry) { - cacheReg = registry; - getSourceRegistry(onSourceReady); - }; - - getCacheRegistry(onCacheReady); + let cacheReg = undefined; + let sourceReg = undefined; + + let gcOne = function (key) { + let entry = cacheReg[key]; + if (entry && entry.readTime < cacheRegistryStart) { + removeCache(key); + } + }; + + let findOne = function () { + let now = Date.now(); + let sourceEntry; + let cacheEntry; + + for (let key in sourceReg) { + sourceEntry = sourceReg[key]; + if (sourceEntry.hasRemoteURL !== true) { + continue; + } + if (updateFetch.has(key)) { + continue; + } + + cacheEntry = cacheReg[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 === 'assets.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) { + sourceReg = registry; + updateOne(); + }; + + let onCacheReady = function (registry) { + cacheReg = 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); + while (pos !== -1) { + observers.splice(pos, 1); + pos = observers.indexOf(observer); + } }; 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) { + return onError.call(null, details, tr); + } + + if (isEmptyString(this.responseText) === true) { + return onError.call(null, details, tr); + } + + 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('>')) { + return onError.call(null, details, tr); + } + + details.content = t; + return 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 urls = []; + let contentUrl = ''; + + 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 tr = (tries === undefined) ? 10 : tries; + + if (tr <= 0) { + console.warn('ηMatrix couldn\'t download the asset ' + +assetDetails.title); + return; + } + + while ((contentUrl = urls.shift())) { + external = externalPathRegex.test(contentUrl); + if (external === true && assetDetails.loaded !== true) { + break; + } + if (external === false || assetDetails.hasLocalURL !== true) { + break; + } + } + + if (!contentUrl) { + return report('', 'E_NOTFOUND'); + } + + api.fetchText(contentUrl, + onContentLoaded, + onContentNotLoaded, + tr-1); + }; + + let onContentLoaded = function (details, tries) { + if (isEmptyString(details.content) === true) { + return onContentNotLoaded(undefined, tries); + } + + 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 !== '') { + return report(details.content); + } + + let onRead = function (details) { + console.debug(details); + report(details.content || 'missing contents'); + } + + let onReady = function (registry) { + assetDetails = registry[key] || {}; + if (typeof assetDetails.contentURL === 'string') { + urls = [assetDetails.contentURL]; + } else if (Array.isArray(assetDetails.contentURL)) { + urls = assetDetails.contentURL.slice(0); + } + if (true === assetDetails.loaded) { + readCache(key, onRead); + } else { + 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); + removeCache(pattern, callback); }; api.rmrf = function () { - cacheRemove(/./); + removeCache(/./); }; 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 !== undefined) { - if (newDelay < oldDelay) { - clearTimeout(updateTimer); - updateTimer = vAPI.setTimeout(updateNext, updateDelay); - } - return; - } + if (updateStatus !== 'stop') { + 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(); + } }; api.checkVersion = function () { - let cache; - - let onSourceReady = function (registry) { - let source = JSON.parse(JSON.stringify(registry)); - let version = ηMatrix.userSettings.assetVersion; - - if (!version) { - ηMatrix.userSettings.assetVersion = 1; - version = 1; - } - - if (!source["assets.json"].version - || version > source["assets.json"].version) { - for (let key in source) { - switch (key) { - case "hphosts": - case "malware-0": - case "malware-1": - delete source[key]; - api.remove(key, function () {}); - break; - default: - break; - } - - source["assets.json"].version = version; - } - - let createRegistry = function () { - api.fetchText - (ηMatrix.assetsBootstrapLocation || 'assets/assets.json', - function (details) { - updateSourceRegistry(details.content, true); - }); - }; - - createRegistry(); - } - }; - - let onCacheReady = function (registry) { - cache = JSON.parse(JSON.stringify(registry)); - - getSourceRegistry(onSourceReady); - }; - - getCacheRegistry(onCacheReady); + let cache; + + let onSourceReady = function (registry) { + let source = JSON.parse(JSON.stringify(registry)); + let version = ηMatrix.userSettings.assetVersion; + + if (!version) { + ηMatrix.userSettings.assetVersion = 1; + version = 1; + } + + if (!source["assets.json"].version + || version > source["assets.json"].version) { + for (let key in source) { + switch (key) { + case "hphosts": + case "malware-0": + case "malware-1": + delete source[key]; + api.remove(key, function () {}); + break; + default: + break; + } + + source["assets.json"].version = version; + } + + let createRegistry = function () { + api.fetchText + (ηMatrix.assetsBootstrapLocation || 'assets/assets.json', + function (details) { + updateSourceRegistry(details.content, true); + }); + }; + + createRegistry(); + } + }; + + let onCacheReady = function (registry) { + cache = JSON.parse(JSON.stringify(registry)); + + getSourceRegistry(onSourceReady); + }; + + getCacheRegistry(onCacheReady); }; return api; })(); - -/******************************************************************************/ diff --git a/js/background.js b/js/background.js index 889decd..b31bbed 100644 --- a/js/background.js +++ b/js/background.js @@ -2,7 +2,7 @@ ηMatrix - a browser extension to black/white list requests. Copyright (C) 2014-2019 Raymond Hill - Copyright (C) 2019-2020 Alessio Vanni + Copyright (C) 2019-2022 Alessio Vanni This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see {http://www.gnu.org/licenses/}. - Home: https://libregit.spks.xyz/heckyel/ematrix + Home: https://gitlab.com/vannilla/ematrix uMatrix Home: https://github.com/gorhill/uMatrix */ @@ -32,45 +32,45 @@ var ηMatrix = (function () { let oneDay = 24 * oneHour; let _RequestStats = function () { - this.reset(); + 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; + 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 (); + this.allowed = new _RequestStats (); + this.blocked = new _RequestStats (); }; RequestStats.prototype.reset = function () { - this.blocked.reset(); - this.allowed.reset(); + this.blocked.reset(); + this.allowed.reset(); }; RequestStats.prototype.record = function (type, blocked) { - // Remember: always test against **false** - if (blocked !== false) { + // Remember: always test against **false** + if (blocked !== false) { this.blocked[type] += 1; this.blocked.all += 1; - } else { + } else { this.allowed[type] += 1; this.allowed.all += 1; - } + } }; var requestStatsFactory = function () { - return new RequestStats(); + return new RequestStats(); }; /** @@ -93,8 +93,8 @@ var ηMatrix = (function () { */ var rawSettingsDefault = { - disableCSPReportInjection: false, - placeholderBackground: [ + disableCSPReportInjection: false, + placeholderBackground: [ 'url("data:image/png;base64,', 'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAK', 'CAAAAACoWZBhAAAABGdBTUEAALGPC/xh', @@ -108,13 +108,13 @@ var ηMatrix = (function () { 'KzAzOjAwa+9TNQAAAABJRU5ErkJggg==', '") ', 'repeat scroll #fff' - ].join(''), - placeholderBorder: '1px solid rgba(0, 0, 0, 0.1)', - imagePlaceholder: true, - imagePlaceholderBackground: 'default', - imagePlaceholderBorder: 'default', - framePlaceholder: true, - framePlaceholderDocument: [ + ].join(''), + 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>', @@ -145,13 +145,13 @@ var ηMatrix = (function () { '</a>{{url}}</span>', '</body></html>' ].join(''), - framePlaceholderBackground: 'default', + framePlaceholderBackground: 'default', }; return { - onBeforeStartQueue: [], + onBeforeStartQueue: [], - userSettings: { + userSettings: { alwaysDetachLogger: false, autoUpdate: false, clearBrowserCache: true, @@ -173,56 +173,56 @@ var ηMatrix = (function () { popupScopeLevel: 'domain', processHyperlinkAuditing: true, processReferer: false, - disableUpdateIcon: false, - resolveCname: false, - assetsVersion: 1, - }, - - 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: new LiquidDict(), - ubiquitousWhitelist: new LiquidDict(), - - // 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 () {}, + disableUpdateIcon: false, + resolveCname: false, + assetsVersion: 1, + }, + + 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: new LiquidDict(), + ubiquitousWhitelist: new LiquidDict(), + + // 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 () {}, }; })(); diff --git a/js/browsercache.js b/js/browsercache.js index 496d2d2..283820e 100644 --- a/js/browsercache.js +++ b/js/browsercache.js @@ -2,7 +2,7 @@ ηMatrix - a browser extension to black/white list requests. Copyright (C) 2015-2019 Raymond Hill - Copyright (C) 2019-2020 Alessio Vanni + Copyright (C) 2019-2022 Alessio Vanni This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see {http://www.gnu.org/licenses/}. - Home: https://libregit.spks.xyz/heckyel/ematrix + Home: https://gitlab.com/vannilla/ematrix uMatrix Home: https://github.com/gorhill/uMatrix */ @@ -27,29 +27,29 @@ // Browser data jobs let clearCache = function () { - vAPI.setTimeout(clearCache, 15 * 60 * 1000); + vAPI.setTimeout(clearCache, 15 * 60 * 1000); - var ηm = ηMatrix; - if (!ηm.userSettings.clearBrowserCache) { + var ηm = ηMatrix; + if (!ηm.userSettings.clearBrowserCache) { return; - } + } - ηm.clearBrowserCacheCycle -= 15; - if (ηm.clearBrowserCacheCycle > 0) { + η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); diff --git a/js/cloud-ui.js b/js/cloud-ui.js index 6d5e875..51919af 100644 --- a/js/cloud-ui.js +++ b/js/cloud-ui.js @@ -2,7 +2,7 @@ ηMatrix - a browser extension to black/white list requests. Copyright (C) 2015-2019 Raymond Hill - Copyright (C) 2019-2020 Alessio Vanni + Copyright (C) 2019-2022 Alessio Vanni This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see {http://www.gnu.org/licenses/}. - Home: https://libregit.spks.xyz/heckyel/ematrix + Home: https://gitlab.com/vannilla/ematrix uMatrix Home: https://github.com/gorhill/uBlock */ @@ -25,34 +25,34 @@ (function () { self.cloud = { - options: {}, - datakey: '', - data: undefined, - onPush: null, - onPull: null + options: {}, + datakey: '', + data: undefined, + onPush: null, + onPull: null }; let widget = uDom.nodeFromId('cloudWidget'); if (widget === null) { - return; + return; } self.cloud.datakey = widget.getAttribute('data-cloud-entry') || ''; if (self.cloud.datakey === '') { - return; + return; } let onCloudDataReceived = function (entry) { - if (typeof entry !== 'object' || entry === null) { + if (typeof entry !== 'object' || entry === null) { return; - } + } - self.cloud.data = entry.data; + self.cloud.data = entry.data; - uDom.nodeFromId('cloudPull').removeAttribute('disabled'); - uDom.nodeFromId('cloudPullAndMerge').removeAttribute('disabled'); + uDom.nodeFromId('cloudPull').removeAttribute('disabled'); + uDom.nodeFromId('cloudPullAndMerge').removeAttribute('disabled'); - let timeOptions = { + let timeOptions = { weekday: 'short', year: 'numeric', month: 'short', @@ -61,100 +61,100 @@ minute: 'numeric', second: 'numeric', timeZoneName: 'short', - }; + }; - let time = new Date(entry.tstamp); - widget.querySelector('span').textContent = entry.source - + '\n' - + time.toLocaleString('fullwide', timeOptions); + let time = new Date(entry.tstamp); + widget.querySelector('span').textContent = entry.source + + '\n' + + time.toLocaleString('fullwide', timeOptions); }; let fetchCloudData = function () { - vAPI.messaging.send('cloud-ui.js', { - what: 'cloudPull', - datakey: self.cloud.datakey + vAPI.messaging.send('cloud-ui.js', { + what: 'cloudPull', + datakey: self.cloud.datakey }, onCloudDataReceived); }; let pushData = function () { - if (typeof self.cloud.onPush !== 'function') { + if (typeof self.cloud.onPush !== 'function') { return; - } + } - vAPI.messaging.send('cloud-ui.js', { - what: 'cloudPush', - datakey: self.cloud.datakey, - data: self.cloud.onPush() + vAPI.messaging.send('cloud-ui.js', { + what: 'cloudPush', + datakey: self.cloud.datakey, + data: self.cloud.onPush() }, fetchCloudData); }; let pullData = function (ev) { - if (typeof self.cloud.onPull === 'function') { + if (typeof self.cloud.onPull === 'function') { self.cloud.onPull(self.cloud.data, ev.shiftKey); - } + } }; let pullAndMergeData = function () { - if (typeof self.cloud.onPull === 'function') { + if (typeof self.cloud.onPull === 'function') { self.cloud.onPull(self.cloud.data, true); - } + } }; let openOptions = function () { - let input = uDom.nodeFromId('cloudDeviceName'); - input.value = self.cloud.options.deviceName; - input.setAttribute('placeholder', self.cloud.options.defaultDeviceName); - uDom.nodeFromId('cloudOptions').classList.add('show'); + let input = uDom.nodeFromId('cloudDeviceName'); + input.value = self.cloud.options.deviceName; + input.setAttribute('placeholder', self.cloud.options.defaultDeviceName); + uDom.nodeFromId('cloudOptions').classList.add('show'); }; let closeOptions = function (ev) { - let root = uDom.nodeFromId('cloudOptions'); - if (ev.target !== root) { + let root = uDom.nodeFromId('cloudOptions'); + if (ev.target !== root) { return; - } - root.classList.remove('show'); + } + root.classList.remove('show'); }; let submitOptions = function () { - let onOptions = function (options) { + let onOptions = function (options) { if (typeof options !== 'object' || options === null) { - return; + return; } self.cloud.options = options; - }; + }; - vAPI.messaging.send('cloud-ui.js', { + vAPI.messaging.send('cloud-ui.js', { what: 'cloudSetOptions', options: { - deviceName: uDom.nodeFromId('cloudDeviceName').value + deviceName: uDom.nodeFromId('cloudDeviceName').value } - }, onOptions); - uDom.nodeFromId('cloudOptions').classList.remove('show'); + }, onOptions); + uDom.nodeFromId('cloudOptions').classList.remove('show'); }; let onInitialize = function (options) { - if (typeof options !== 'object' || options === null) { + if (typeof options !== 'object' || options === null) { return; - } + } - if (!options.enabled) { + if (!options.enabled) { return; - } - self.cloud.options = options; - - let xhr = new XMLHttpRequest(); - xhr.open('GET', 'cloud-ui.html', true); - xhr.overrideMimeType('text/html;charset=utf-8'); - xhr.responseType = 'text'; - xhr.onload = function () { + } + self.cloud.options = options; + + let 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; let parser = new DOMParser(); - let parsed = parser.parseFromString(this.responseText, 'text/html'); - let fromParent = parsed.body; + let parsed = parser.parseFromString(this.responseText, 'text/html'); + let fromParent = parsed.body; while (fromParent.firstElementChild !== null) { - widget.appendChild(document - .adoptNode(fromParent.firstElementChild)); + widget.appendChild(document + .adoptNode(fromParent.firstElementChild)); } vAPI.i18n.render(widget); @@ -168,13 +168,13 @@ uDom('#cloudOptionsSubmit').on('click', submitOptions); fetchCloudData(); - }; + }; - xhr.send(); + xhr.send(); }; vAPI.messaging.send('cloud-ui.js', { - what: 'cloudGetOptions' + what: 'cloudGetOptions' }, onInitialize); // https://www.youtube.com/watch?v=aQFp67VoiDA diff --git a/js/contentscript-start.js b/js/contentscript-start.js index 4680e8c..b7d74ba 100644 --- a/js/contentscript-start.js +++ b/js/contentscript-start.js @@ -2,7 +2,7 @@ ηMatrix - a browser extension to black/white list requests. Copyright (C) 2017-2019 Raymond Hill - Copyright (C) 2019-2020 Alessio Vanni + Copyright (C) 2019-2022 Alessio Vanni This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see {http://www.gnu.org/licenses/}. - Home: https://libregit.spks.xyz/heckyel/ematrix + Home: https://gitlab.com/vannilla/ematrix uMatrix Home: https://github.com/gorhill/uMatrix */ @@ -26,7 +26,7 @@ // Injected into content pages (function () { if (typeof vAPI !== 'object') { - return; + return; } vAPI.selfWorkerSrcReported = vAPI.selfWorkerSrcReported || false; @@ -35,15 +35,15 @@ 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; } // Firefox and Chromium differs in how they fill the // 'effectiveDirective' property. - // ηMatrix: what does Pale Moon/Basilisk do? + // ηMatrix: what does Pale Moon/Basilisk do? if (ev.effectiveDirective.startsWith('worker-src') === false - && ev.effectiveDirective.startsWith('frame-src') === false) { + && ev.effectiveDirective.startsWith('frame-src') === false) { return false; } @@ -62,8 +62,8 @@ // reports. if (ev.blockedURI.includes('://') === false) { if (vAPI.selfWorkerSrcReported) { - return true; - } + return true; + } vAPI.selfWorkerSrcReported = true; } @@ -80,8 +80,8 @@ document.addEventListener('securitypolicyviolation', function (ev) { if (!handler(ev)) { - return; - } + return; + } ev.stopPropagation(); ev.preventDefault(); }, true); diff --git a/js/contentscript.js b/js/contentscript.js index 21fbcf1..9fb15f8 100644 --- a/js/contentscript.js +++ b/js/contentscript.js @@ -2,7 +2,7 @@ ηMatrix - a browser extension to black/white list requests. Copyright (C) 2014-2019 Raymond Hill - Copyright (C) 2019-2020 Alessio Vanni + Copyright (C) 2019-2022 Alessio Vanni This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see {http://www.gnu.org/licenses/}. - Home: https://libregit.spks.xyz/heckyel/ematrix + Home: https://gitlab.com/vannilla/ematrix uMatrix Home: https://github.com/gorhill/uMatrix */ @@ -29,54 +29,54 @@ // https://github.com/chrisaljoudi/uBlock/issues/464 // https://github.com/gorhill/uMatrix/issues/621 if (document instanceof HTMLDocument === false - && document instanceof XMLDocument === false) { - return; + && document instanceof XMLDocument === false) { + return; } // This can also happen (for example if script injected into a // `data:` URI doc) if (!window.location) { - return; + return; } // This can happen if (typeof vAPI !== 'object') { - //console.debug('contentscript.js > vAPI not found'); - return; + //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; + //console.debug('contentscript.js > content script already injected'); + return; } vAPI.contentscriptEndInjected = true; // Executed only once. (function () { - let localStorageHandler = function (mustRemove) { + let localStorageHandler = function (mustRemove) { if (mustRemove) { - window.localStorage.clear(); - window.sessionStorage.clear(); + 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 { + // 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 { let hasLocalStorage = - window.localStorage && window.localStorage.length !== 0; + window.localStorage && window.localStorage.length !== 0; let hasSessionStorage = - window.sessionStorage && window.sessionStorage.length !== 0; + window.sessionStorage && window.sessionStorage.length !== 0; if (hasLocalStorage || hasSessionStorage) { - vAPI.messaging.send('contentscript.js', { + vAPI.messaging.send('contentscript.js', { what: 'contentScriptHasLocalStorage', originURL: window.location.origin - }, localStorageHandler); + }, localStorageHandler); } // TODO: indexedDB @@ -92,13 +92,13 @@ // "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) { - } + } catch (e) { + } })(); // https://github.com/gorhill/uMatrix/issues/45 let collapser = (function () { - let resquestIdGenerator = 1; + let resquestIdGenerator = 1; let processTimer; let toProcess = []; let toFilter = []; @@ -107,320 +107,320 @@ let cachedBlockedMapHash; let cachedBlockedMapTimer; let reURLPlaceholder = /\{\{url\}\}/g; - let src1stProps = { + let src1stProps = { embed: 'src', iframe: 'src', img: 'src', object: 'data', - }; - let src2ndProps = { + }; + let src2ndProps = { img: 'srcset', - }; - let tagToTypeMap = { + }; + let tagToTypeMap = { embed: 'media', iframe: 'frame', img: 'image', object: 'media', - }; - let cachedBlockedSetClear = function () { + }; + let cachedBlockedSetClear = function () { cachedBlockedMap = - cachedBlockedMapHash = - cachedBlockedMapTimer = undefined; - }; + cachedBlockedMapHash = + cachedBlockedMapTimer = undefined; + }; - // https://github.com/chrisaljoudi/uBlock/issues/174 - // Do not remove fragment from src URL - let onProcessed = function (response) { + // https://github.com/chrisaljoudi/uBlock/issues/174 + // Do not remove fragment from src URL + let onProcessed = function (response) { if (!response) { // This happens if uBO is disabled or restarted. - toCollapse.clear(); - return; + toCollapse.clear(); + return; } let targets = toCollapse.get(response.id); if (targets === undefined) { - return; - } + return; + } toCollapse.delete(response.id); if (cachedBlockedMapHash !== response.hash) { - cachedBlockedMap = new Map(response.blockedResources); - cachedBlockedMapHash = response.hash; - if (cachedBlockedMapTimer !== undefined) { + cachedBlockedMap = new Map(response.blockedResources); + cachedBlockedMapHash = response.hash; + if (cachedBlockedMapTimer !== undefined) { clearTimeout(cachedBlockedMapTimer); - } - cachedBlockedMapTimer = - vAPI.setTimeout(cachedBlockedSetClear, 30000); + } + cachedBlockedMapTimer = + vAPI.setTimeout(cachedBlockedSetClear, 30000); } if (cachedBlockedMap === undefined || cachedBlockedMap.size === 0) { - return; + return; } let placeholders = response.placeholders; for (let target of targets) { - let tag = target.localName; + let tag = target.localName; - let prop = src1stProps[tag]; - if (prop === undefined) { - continue; - } + let prop = src1stProps[tag]; + if (prop === undefined) { + continue; + } - let src = target[prop]; - if (typeof src !== 'string' || src.length === 0) { + let src = target[prop]; + if (typeof src !== 'string' || src.length === 0) { prop = src2ndProps[tag]; if (prop === undefined) { - continue; - } + continue; + } src = target[prop]; if (typeof src !== 'string' || src.length === 0) { - continue; - } - } - - let collapsed = cachedBlockedMap.get(tagToTypeMap[tag] - + ' ' - + src); - if (collapsed === undefined) { - continue; - } - - if (collapsed) { + continue; + } + } + + let 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': + switch (tag) { + case 'iframe': if (placeholders.frame !== true) { - break; - } + break; + } let docurl = 'data:text/html,' - + encodeURIComponent(placeholders - .frameDocument - .replace(reURLPlaceholder, src)); + + encodeURIComponent(placeholders + .frameDocument + .replace(reURLPlaceholder, src)); let replaced = false; // Using contentWindow.location prevent tainting browser // history -- i.e. breaking back button (seen on Chromium). if (target.contentWindow) { - try { + try { target.contentWindow.location.replace(docurl); replaced = true; - } catch(ex) { - } + } catch(ex) { + } } if (!replaced) { - target.setAttribute('src', docurl); + target.setAttribute('src', docurl); } break; - case 'img': + case 'img': if (placeholders.image !== true) { - break; - } + 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'); + 'important'); target.style.setProperty('background', - placeholders.imageBackground, - 'important'); + placeholders.imageBackground, + 'important'); break; - } + } } - }; + }; - let send = function () { + let send = function () { processTimer = undefined; toCollapse.set(resquestIdGenerator, toProcess); let msg = { - what: 'lookupBlockedCollapsibles', - id: resquestIdGenerator, - toFilter: toFilter, - hash: cachedBlockedMapHash + what: 'lookupBlockedCollapsibles', + id: resquestIdGenerator, + toFilter: toFilter, + hash: cachedBlockedMapHash }; vAPI.messaging.send('contentscript.js', msg, onProcessed); toProcess = []; toFilter = []; resquestIdGenerator += 1; - }; + }; - let process = function (delay) { + let process = function (delay) { if (toProcess.length === 0) { - return; - } + return; + } if (delay === 0) { - if (processTimer !== undefined) { + if (processTimer !== undefined) { clearTimeout(processTimer); - } - send(); + } + send(); } else if (processTimer === undefined) { - processTimer = vAPI.setTimeout(send, delay || 47); + processTimer = vAPI.setTimeout(send, delay || 47); } - }; + }; - let add = function (target) { + let add = function (target) { toProcess.push(target); - }; + }; - let addMany = function (targets) { - for (let i=targets.length-1; i>=0; --i) { - toProcess.push(targets[i]); + let addMany = function (targets) { + for (let i=targets.length-1; i>=0; --i) { + toProcess.push(targets[i]); } - }; + }; - let iframeSourceModified = function (mutations) { - for (let i=mutations.length-1; i>=0; --i) { - addIFrame(mutations[i].target, true); + let iframeSourceModified = function (mutations) { + for (let i=mutations.length-1; i>=0; --i) { + addIFrame(mutations[i].target, true); } process(); - }; + }; - let iframeSourceObserver; - let iframeSourceObserverOptions = { + let iframeSourceObserver; + let iframeSourceObserverOptions = { attributes: true, attributeFilter: [ 'src' ] - }; + }; - let addIFrame = function (iframe, dontObserve) { + let 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) { + if (iframeSourceObserver === undefined) { iframeSourceObserver = - new MutationObserver(iframeSourceModified); - } - iframeSourceObserver.observe(iframe, - iframeSourceObserverOptions); + new MutationObserver(iframeSourceModified); + } + iframeSourceObserver.observe(iframe, + iframeSourceObserverOptions); } let src = iframe.src; if (src === '' || typeof src !== 'string') { - return; - } + return; + } if (src.startsWith('http') === false) { - return; - } + return; + } toFilter.push({ - type: 'frame', - url: iframe.src, - }); + type: 'frame', + url: iframe.src, + }); add(iframe); - }; + }; - let addIFrames = function (iframes) { - for (let i=iframes.length-1; i>=0; --i) { - addIFrame(iframes[i]); + let addIFrames = function (iframes) { + for (let i=iframes.length-1; i>=0; --i) { + addIFrame(iframes[i]); } - }; - - let addNodeList = function (nodeList) { - for (let i=nodeList.length-1; i>=0; --i) { - let node = nodeList[i]; - if (node.nodeType !== 1) { - continue; - } - if (node.localName === 'iframe') { + }; + + let addNodeList = function (nodeList) { + for (let i=nodeList.length-1; i>=0; --i) { + let node = nodeList[i]; + if (node.nodeType !== 1) { + continue; + } + if (node.localName === 'iframe') { addIFrame(node); - } - if (node.childElementCount !== 0) { + } + if (node.childElementCount !== 0) { addIFrames(node.querySelectorAll('iframe')); - } + } } - }; + }; - let onResourceFailed = function (ev) { + let onResourceFailed = function (ev) { if (tagToTypeMap[ev.target.localName] !== undefined) { - add(ev.target); - process(); + add(ev.target); + process(); } - }; - document.addEventListener('error', onResourceFailed, true); + }; + document.addEventListener('error', onResourceFailed, true); - vAPI.shutdown.add(function () { + vAPI.shutdown.add(function () { document.removeEventListener('error', onResourceFailed, true); if (iframeSourceObserver !== undefined) { - iframeSourceObserver.disconnect(); - iframeSourceObserver = undefined; + iframeSourceObserver.disconnect(); + iframeSourceObserver = undefined; } if (processTimer !== undefined) { - clearTimeout(processTimer); - processTimer = undefined; + clearTimeout(processTimer); + processTimer = undefined; } - }); + }); - return { + return { addMany: addMany, addIFrames: addIFrames, addNodeList: addNodeList, process: process - }; + }; })(); // Observe changes in the DOM // Added node lists will be cumulated here before being processed (function () { - // This fixes http://acid3.acidtests.org/ - if (!document.body) { - return; - } + // This fixes http://acid3.acidtests.org/ + if (!document.body) { + return; + } - let addedNodeLists = []; - let addedNodeListsTimer; + let addedNodeLists = []; + let addedNodeListsTimer; - let treeMutationObservedHandler = function () { + let treeMutationObservedHandler = function () { addedNodeListsTimer = undefined; - for (let i=addedNodeLists.length-1; i>=0; --i) { - collapser.addNodeList(addedNodeLists[i]); + for (let i=addedNodeLists.length-1; i>=0; --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. - let treeMutationObservedHandlerAsync = function (mutations) { - for (let i=mutations.length-1; i>=0; --i) { - let nodeList = mutations[i].addedNodes; - if (nodeList.length !== 0) { + }; + + // https://github.com/gorhill/uBlock/issues/205 + // Do not handle added node directly from within mutation observer. + let treeMutationObservedHandlerAsync = function (mutations) { + for (let i=mutations.length-1; i>=0; --i) { + let nodeList = mutations[i].addedNodes; + if (nodeList.length !== 0) { addedNodeLists.push(nodeList); - } + } } if (addedNodeListsTimer === undefined) { - addedNodeListsTimer = - vAPI.setTimeout(treeMutationObservedHandler, 47); + addedNodeListsTimer = + vAPI.setTimeout(treeMutationObservedHandler, 47); } - }; + }; - // https://github.com/gorhill/httpswitchboard/issues/176 - let treeObserver = - new MutationObserver(treeMutationObservedHandlerAsync); - treeObserver.observe(document.body, { + // https://github.com/gorhill/httpswitchboard/issues/176 + let treeObserver = + new MutationObserver(treeMutationObservedHandlerAsync); + treeObserver.observe(document.body, { childList: true, subtree: true - }); + }); - vAPI.shutdown.add(function () { + vAPI.shutdown.add(function () { if (addedNodeListsTimer !== undefined) { - clearTimeout(addedNodeListsTimer); - addedNodeListsTimer = undefined; + clearTimeout(addedNodeListsTimer); + addedNodeListsTimer = undefined; } if (treeObserver !== null) { - treeObserver.disconnect(); - treeObserver = undefined; + treeObserver.disconnect(); + treeObserver = undefined; } addedNodeLists = []; - }); + }); })(); // Executed only once. @@ -437,58 +437,58 @@ // 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) { + 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 + what: 'securityPolicyViolation', + directive: 'script-src', + documentURI: window.location.href }); - } + } - if (document.querySelector('style,[style]') !== null) { + if (document.querySelector('style,[style]') !== null) { vAPI.messaging.send('contentscript.js', { - what: 'securityPolicyViolation', - directive: 'style-src', - documentURI: window.location.href + what: 'securityPolicyViolation', + directive: 'style-src', + documentURI: window.location.href }); - } + } - collapser.addMany(document.querySelectorAll('img')); - collapser.addIFrames(document.querySelectorAll('iframe')); - collapser.process(); + collapser.addMany(document.querySelectorAll('img')); + collapser.addIFrames(document.querySelectorAll('iframe')); + collapser.process(); })(); // Executed only once. @@ -496,88 +496,88 @@ // Force `display` property, Firefox is still affected by the issue. (function () { - let noscripts = document.querySelectorAll('noscript'); - if (noscripts.length === 0) { - return; - } + let noscripts = document.querySelectorAll('noscript'); + if (noscripts.length === 0) { + return; + } - let redirectTimer; + let redirectTimer; let reMetaContent = /^\s*(\d+)\s*;\s*url=(['"]?)([^'"]+)\2/i; let reSafeURL = /^https?:\/\//; - let autoRefresh = function (root) { + let autoRefresh = function (root) { let meta = - root.querySelector('meta[http-equiv="refresh"][content]'); + root.querySelector('meta[http-equiv="refresh"][content]'); if (meta === null) { - return; - } + return; + } let match = reMetaContent.exec(meta.getAttribute('content')); if (match === null || match[3].trim() === '') { - return; - } + return; + } let url = new URL(match[3], document.baseURI); if (reSafeURL.test(url.href) === false) { - return; - } + return; + } redirectTimer = setTimeout(function () { location.assign(url.href); - }, parseInt(match[1], 10) * 1000 + 1); + }, parseInt(match[1], 10) * 1000 + 1); meta.parentNode.removeChild(meta); - }; + }; - let morphNoscript = function (from) { + let morphNoscript = function (from) { if (/^application\/(?:xhtml\+)?xml/.test(document.contentType)) { - let to = document.createElement('span'); - while (from.firstChild !== null) { + let to = document.createElement('span'); + while (from.firstChild !== null) { to.appendChild(from.firstChild); - } - return to; + } + return to; } let parser = new DOMParser(); let doc = - parser.parseFromString('<span>' + from.textContent + '</span>', - 'text/html'); + parser.parseFromString('<span>' + from.textContent + '</span>', + 'text/html'); return document.adoptNode(doc.querySelector('span')); - }; + }; - let renderNoscriptTags = function (response) { + let renderNoscriptTags = function (response) { if (response !== true) { - return; - } + return; + } for (let noscript of noscripts) { - let parent = noscript.parentNode; - if (parent === null) { - continue; - } + let parent = noscript.parentNode; + if (parent === null) { + continue; + } - let span = morphNoscript(noscript); - span.style.setProperty('display', 'inline', 'important'); + let span = morphNoscript(noscript); + span.style.setProperty('display', 'inline', 'important'); - if (redirectTimer === undefined) { + if (redirectTimer === undefined) { autoRefresh(span); - } + } - parent.replaceChild(span, noscript); + parent.replaceChild(span, noscript); } - }; + }; - vAPI.messaging.send('contentscript.js', { - what: 'mustRenderNoscriptTags?' - }, renderNoscriptTags); + vAPI.messaging.send('contentscript.js', { + what: 'mustRenderNoscriptTags?' + }, renderNoscriptTags); })(); vAPI.messaging.send('contentscript.js', { - what: 'shutdown?' + what: 'shutdown?' }, function (response) { if (response === true) { - vAPI.shutdown.exec(); + vAPI.shutdown.exec(); } }); })(); diff --git a/js/cookies.js b/js/cookies.js index 217468b..b176fbb 100644 --- a/js/cookies.js +++ b/js/cookies.js @@ -2,7 +2,7 @@ ηMatrix - a browser extension to black/white list requests. Copyright (C) 2013-2019 Raymond Hill - Copyright (C) 2019-2020 Alessio Vanni + Copyright (C) 2019-2022 Alessio Vanni This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see {http://www.gnu.org/licenses/}. - Home: https://libregit.spks.xyz/heckyel/ematrix + Home: https://gitlab.com/vannilla/ematrix uMatrix Home: https://github.com/gorhill/uMatrix */ @@ -50,205 +50,205 @@ // Look for cookies to record for a specific web page let 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) { + // 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) { + } + recordPageCookiesQueue.set(pageStats.pageUrl, pageStats); + if (processPageRecordQueueTimer === null) { processPageRecordQueueTimer = - vAPI.setTimeout(processPageRecordQueue, 1000); - } + vAPI.setTimeout(processPageRecordQueue, 1000); + } }; let cookieLogEntryBuilder = [ - '', - '{', - '', - '-cookie:', - '', - '}' + '', + '{', + '', + '-cookie:', + '', + '}' ]; let recordPageCookie = function (pageStore, key) { - if (vAPI.isBehindTheSceneTabId(pageStore.tabId)) { + if (vAPI.isBehindTheSceneTabId(pageStore.tabId)) { + return; + } + + let entry = CookieCache.get(key); + let pageHostname = pageStore.pageHostname; + let block = ηm.mustBlock(pageHostname, entry.hostname, 'cookie'); + + cookieLogEntryBuilder[0] = CookieUtils.urlFromEntry(entry); + cookieLogEntryBuilder[2] = entry.session ? 'session' : 'persistent'; + cookieLogEntryBuilder[4] = encodeURIComponent(entry.name); + + let 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); + + entry.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; - } - - let entry = CookieCache.get(key); - let pageHostname = pageStore.pageHostname; - let block = ηm.mustBlock(pageHostname, entry.hostname, 'cookie'); - - cookieLogEntryBuilder[0] = CookieUtils.urlFromEntry(entry); - cookieLogEntryBuilder[2] = entry.session ? 'session' : 'persistent'; - cookieLogEntryBuilder[4] = encodeURIComponent(entry.name); - - let 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); - - entry.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) { + } + if (!ηm.userSettings.deleteCookies) { return; - } - removeCookieAsync(key); + } + removeCookieAsync(key); }; // Look for cookies to potentially remove for a specific web page let 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) { + // 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) { + } + removePageCookiesQueue.set(pageStats.pageUrl, pageStats); + if (processPageRemoveQueueTimer === null) { processPageRemoveQueueTimer = - vAPI.setTimeout(processPageRemoveQueue, 15 * 1000); - } + vAPI.setTimeout(processPageRemoveQueue, 15 * 1000); + } }; // Candidate for removal let removeCookieAsync = function (key) { - removeCookieQueue.add(key); + removeCookieQueue.add(key); }; let chromeCookieRemove = function (entry, name) { - let url = CookieUtils.urlFromEntry(entry); - if (url === '') { + let url = CookieUtils.urlFromEntry(entry); + if (url === '') { return; - } - let sessionKey = CookieUtils.keyFromURL(UriTools.set(url), - 'session', name); - let persistKey = CookieUtils.keyFromURL(UriTools.set(url), - 'persistent', name); + } + let sessionKey = CookieUtils.keyFromURL(UriTools.set(url), + 'session', name); + let persistKey = CookieUtils.keyFromURL(UriTools.set(url), + 'persistent', name); - let callback = function(details) { + let callback = function(details) { let success = !!details; let template = success ? - i18nCookieDeleteSuccess : - i18nCookieDeleteFailure; + i18nCookieDeleteSuccess : + i18nCookieDeleteFailure; if (CookieCache.remove(sessionKey)) { - if (success) { + if (success) { ηm.cookieRemovedCounter += 1; - } - ηm.logger.writeOne('', 'info', 'cookie', - template.replace('{{value}}', - sessionKey)); + } + ηm.logger.writeOne('', 'info', 'cookie', + template.replace('{{value}}', + sessionKey)); } if (CookieCache.remove(persistKey)) { - if (success) { + if (success) { ηm.cookieRemovedCounter += 1; - } - ηm.logger.writeOne('', 'info', 'cookie', - template.replace('{{value}}', - persistKey)); + } + ηm.logger.writeOne('', 'info', 'cookie', + template.replace('{{value}}', + persistKey)); } - }; + }; - vAPI.cookies.remove({ - url: url, name: name - }, callback); + vAPI.cookies.remove({ + url: url, name: name + }, callback); }; let i18nCookieDeleteSuccess = vAPI.i18n('loggerEntryCookieDeleted'); let i18nCookieDeleteFailure = vAPI.i18n('loggerEntryDeleteCookieError'); let processPageRecordQueue = function () { - processPageRecordQueueTimer = null; + processPageRecordQueueTimer = null; - for (let pageStore of recordPageCookiesQueue.values()) { + for (let pageStore of recordPageCookiesQueue.values()) { findAndRecordPageCookies(pageStore); - } - recordPageCookiesQueue.clear(); + } + recordPageCookiesQueue.clear(); }; let processPageRemoveQueue = function () { - processPageRemoveQueueTimer = null; + processPageRemoveQueueTimer = null; - for (let pageStore of removePageCookiesQueue.values()) { + for (let pageStore of removePageCookiesQueue.values()) { findAndRemovePageCookies(pageStore); - } - removePageCookiesQueue.clear(); + } + removePageCookiesQueue.clear(); }; // Effectively remove cookies. let processRemoveQueue = function () { - let userSettings = ηm.userSettings; - let deleteCookies = userSettings.deleteCookies; - - // Session cookies which timestamp is *after* tstampObsolete will - // be left untouched - // https://github.com/gorhill/httpswitchboard/issues/257 - let dusc = userSettings.deleteUnusedSessionCookies; - let dusca = userSettings.deleteUnusedSessionCookiesAfter; - let tstampObsolete = dusc ? - Date.now() - dusca * 60 * 1000 : + let userSettings = ηm.userSettings; + let deleteCookies = userSettings.deleteCookies; + + // Session cookies which timestamp is *after* tstampObsolete will + // be left untouched + // https://github.com/gorhill/httpswitchboard/issues/257 + let dusc = userSettings.deleteUnusedSessionCookies; + let dusca = userSettings.deleteUnusedSessionCookiesAfter; + let tstampObsolete = dusc ? + Date.now() - dusca * 60 * 1000 : 0; - let srcHostnames; - let entry; + let srcHostnames; + let entry; - for (let key of removeCookieQueue) { + for (let key 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?) entry = CookieCache.get(key); if (entry === undefined) { - continue; - } + continue; + } // Delete obsolete session cookies: enabled. if (tstampObsolete !== 0 && entry.session) { - if (entry.tstamp < tstampObsolete) { + if (entry.tstamp < tstampObsolete) { chromeCookieRemove(entry, entry.name); continue; - } + } } // Delete all blocked cookies: disabled. if (deleteCookies === false) { - continue; + continue; } // Query scopes only if we are going to use them if (srcHostnames === undefined) { - srcHostnames = ηm.tMatrix.extractAllSourceHostnames(); + 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(key, srcHostnames)) { - chromeCookieRemove(entry, entry.name); + chromeCookieRemove(entry, entry.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 @@ -258,145 +258,145 @@ // maybe a user has 1000s of cookies sitting in his browser... let processClean = function () { - let us = ηm.userSettings; + let us = ηm.userSettings; - if (us.deleteCookies || us.deleteUnusedSessionCookies) { + if (us.deleteCookies || us.deleteUnusedSessionCookies) { let keys = Array.from(CookieCache.keys()); - let len = keys.length; - let step, offset, n; + let len = keys.length; + let step, offset, n; if (len > 25) { - step = len / 25; - offset = Math.floor(Math.random() * len); - n = 25; + step = len / 25; + offset = Math.floor(Math.random() * len); + n = 25; } else { - step = 1; - offset = 0; - n = len; + step = 1; + offset = 0; + n = len; } let i = offset; while (n--) { - removeCookieAsync(keys[Math.floor(i % len)]); - i += step; + removeCookieAsync(keys[Math.floor(i % len)]); + i += step; } - } + } - vAPI.setTimeout(processClean, processCleanPeriod); + vAPI.setTimeout(processClean, processCleanPeriod); }; let findAndRecordPageCookies = function (pageStore) { - for (let key of CookieCache.keys()) { + for (let key of CookieCache.keys()) { if (CookieUtils.matchDomains(key, pageStore.allHostnamesString)) { - recordPageCookie(pageStore, key); + recordPageCookie(pageStore, key); } - } + } }; let findAndRemovePageCookies = function (pageStore) { - for (let key of CookieCache.keys()) { + for (let key of CookieCache.keys()) { if (CookieUtils.matchDomains(key, pageStore.allHostnamesString)) { - removeCookieAsync(key); + removeCookieAsync(key); } - } + } }; let canRemoveCookie = function (key, srcHostnames) { - let entry = CookieCache.get(key); - if (entry === undefined) { - return false; - } + let entry = CookieCache.get(key); + if (entry === undefined) { + return false; + } - let cookieHostname = entry.hostname; - let srcHostname; + let cookieHostname = entry.hostname; + let srcHostname; - for (srcHostname of entry.usedOn) { + for (srcHostname of entry.usedOn) { if (ηm.mustAllow(srcHostname, cookieHostname, 'cookie')) { - return false; + 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; + // 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; - let pos; + let pos; - for (;;) { + for (;;) { if (srcHostnames.has(srcHostname)) { - if (ηm.mustAllow(srcHostname, cookieHostname, 'cookie')) { + if (ηm.mustAllow(srcHostname, cookieHostname, 'cookie')) { return false; - } + } } if (srcHostname === entry.domain) { - break; + break; } pos = srcHostname.indexOf('.'); if (pos === -1) { - break; + break; } srcHostname = srcHostname.slice(pos + 1); - } - return true; + } + return true; }; // 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 - let key = CookieUtils.keyFromCookie(cookie); - let entry = CookieCache.get(key); + // rhill 2013-12-11: If cookie value didn't change, no need to record. + // https://github.com/gorhill/httpswitchboard/issues/79 + let key = CookieUtils.keyFromCookie(cookie); + let entry = CookieCache.get(key); - if (entry === undefined) { + if (entry === undefined) { entry = CookieCache.add(cookie); - } else { + } else { entry.tstamp = Date.now(); if (cookie.value === entry.value) { - return; - } + return; + } entry.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. - let pageStores = ηm.pageStores; - let pageStore; - for (let tabId in pageStores) { + // 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. + let pageStores = ηm.pageStores; + let pageStore; + for (let tabId in pageStores) { if (pageStores.hasOwnProperty(tabId) === false) { - continue; + continue; } pageStore = pageStores[tabId]; if (!CookieUtils.matchDomains(key, pageStore.allHostnamesString)) { - continue; + continue; } recordPageCookie(pageStore, key); - } + } }; // Listen to any change in cookieland, we will update page stats accordingly. vAPI.cookies.onRemoved = function (cookie) { - let key = CookieUtils.keyFromCookie(cookie); - if (CookieCache.remove(key)) { + let key = CookieUtils.keyFromCookie(cookie); + if (CookieCache.remove(key)) { ηm.logger.writeOne('', 'info', 'cookie', - i18nCookieDeleteSuccess.replace('{{value}}', - key)); - } + i18nCookieDeleteSuccess.replace('{{value}}', + key)); + } }; // Listen to any change in cookieland, we will update page stats accordingly. vAPI.cookies.onAllRemoved = function () { - for (let key of CookieCache.keys()) { + for (let key of CookieCache.keys()) { if (CookieCache.remove(key)) { - ηm.logger.writeOne('', 'info', 'cookie', - i18nCookieDeleteSuccess.replace('{{value}}', - key)); + ηm.logger.writeOne('', 'info', 'cookie', + i18nCookieDeleteSuccess.replace('{{value}}', + key)); } - } + } }; vAPI.cookies.getAll(CookieCache.addVector); @@ -408,7 +408,7 @@ // Expose only what is necessary return { - recordPageCookies: recordPageCookiesAsync, - removePageCookies: removePageCookiesAsync + recordPageCookies: recordPageCookiesAsync, + removePageCookies: removePageCookiesAsync }; })(); diff --git a/js/dashboard-common.js b/js/dashboard-common.js index 677a3f3..b0eb9cf 100644 --- a/js/dashboard-common.js +++ b/js/dashboard-common.js @@ -2,7 +2,7 @@ ηMatrix - a browser extension to black/white list requests. Copyright (C) 2014-2019 Raymond Hill - Copyright (C) 2019-2020 Alessio Vanni + Copyright (C) 2019-2022 Alessio Vanni This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see {http://www.gnu.org/licenses/}. - Home: https://libregit.spks.xyz/heckyel/ematrix + Home: https://gitlab.com/vannilla/ematrix uMatrix Home: https://github.com/gorhill/uMatrix */ @@ -28,8 +28,8 @@ uDom.onLoad(function () { uDom('a').attr('target', '_blank'); uDom('a[href*="dashboard.html"]').attr('target', '_parent'); uDom('.whatisthis').on('click', function () { - uDom(this) - .parent() + uDom(this) + .parent() .descendants('.whatisthis-expandable') .toggleClass('whatisthis-expanded'); }); diff --git a/js/dashboard.js b/js/dashboard.js index 8e565f6..6a466be 100644 --- a/js/dashboard.js +++ b/js/dashboard.js @@ -2,7 +2,7 @@ ηMatrix - a browser extension to black/white list requests. Copyright (C) 2014-2019 Raymond Hill - Copyright (C) 2019-2020 Alessio Vanni + Copyright (C) 2019-2022 Alessio Vanni This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see {http://www.gnu.org/licenses/}. - Home: https://libregit.spks.xyz/heckyel/ematrix + Home: https://gitlab.com/vannilla/ematrix uMatrix Home: https://github.com/gorhill/uMatrix */ @@ -30,7 +30,7 @@ uDom('iframe').attr('src', url); uDom('.tabButton').forEach(function (button) { button.toggleClass('selected', - button.attr('data-dashboard-panel-url') === url); + button.attr('data-dashboard-panel-url') === url); }); }; diff --git a/js/hosts-files.js b/js/hosts-files.js index b2f19f6..92a7efc 100644 --- a/js/hosts-files.js +++ b/js/hosts-files.js @@ -2,7 +2,7 @@ ηMatrix - a browser extension to black/white list requests. Copyright (C) 2014-2019 Raymond Hill - Copyright (C) 2019-2020 Alessio Vanni + Copyright (C) 2019-2022 Alessio Vanni This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see {http://www.gnu.org/licenses/}. - Home: https://libregit.spks.xyz/heckyel/ematrix + Home: https://gitlab.com/vannilla/ematrix uMatrix Home: https://github.com/gorhill/uMatrix */ @@ -30,104 +30,104 @@ let reValidExternalList = /[a-z-]+:\/\/\S*\/\S+/; vAPI.messaging.addListener(function (msg) { - switch (msg.what) { - case 'assetUpdated': + switch (msg.what) { + case 'assetUpdated': updateAssetStatus(msg); break; - case 'assetsUpdated': + case 'assetsUpdated': document.body.classList.remove('updating'); break; - case 'loadHostsFilesCompleted': + case 'loadHostsFilesCompleted': renderHostsFiles(); break; - default: + default: break; - } + } }); let renderNumber = function (value) { - return value.toLocaleString(); + return value.toLocaleString(); }; let renderHostsFiles = function (soft) { - let listEntryTemplate = uDom('#templates .listEntry'); + let listEntryTemplate = uDom('#templates .listEntry'); let listStatsTemplate = vAPI.i18n('hostsFilesPerFileStats'); let renderETTS = vAPI.i18n.renderElapsedTimeToString; let reExternalHostFile = /^https?:/; - // Assemble a pretty list name if possible - let listNameFromListKey = function (listKey) { + // Assemble a pretty list name if possible + let listNameFromListKey = function (listKey) { let list = - listDetails.current[listKey] || listDetails.available[listKey]; + listDetails.current[listKey] || listDetails.available[listKey]; let listTitle = list ? list.title : ''; if (listTitle === '') { - return listKey; - } + return listKey; + } return listTitle; - }; + }; - let liFromListEntry = function (listKey, li) { + let liFromListEntry = function (listKey, li) { let entry = listDetails.available[listKey]; - let elem; + let elem; if (!li) { - li = listEntryTemplate.clone().nodeAt(0); + li = listEntryTemplate.clone().nodeAt(0); } if (li.getAttribute('data-listkey') !== listKey) { - li.setAttribute('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); + 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'); + li.classList.remove('toRemove'); - if (entry.supportName) { + if (entry.supportName) { li.classList.add('support'); elem = li.querySelector('a.support'); elem.setAttribute('href', entry.supportURL); elem.setAttribute('title', entry.supportName); - } else { + } else { li.classList.remove('support'); - } + } - if (entry.external) { + if (entry.external) { li.classList.add('external'); - } else { + } else { li.classList.remove('external'); - } + } - if (entry.instructionURL) { + if (entry.instructionURL) { li.classList.add('mustread'); elem = li.querySelector('a.mustread'); elem.setAttribute('href', entry.instructionURL); - } else { + } 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; + elem = li.querySelector('input[type="checkbox"]'); + elem.checked = entry.off !== true; } elem = li.querySelector('span.counts'); let text = ''; if (!isNaN(+entry.entryUsedCount) && !isNaN(+entry.entryCount)) { - text = listStatsTemplate + text = listStatsTemplate .replace('{{used}}', - renderNumber(entry.off ? 0 : entry.entryUsedCount)) + renderNumber(entry.off ? 0 : entry.entryUsedCount)) .replace('{{total}}', - renderNumber(entry.entryCount)); + renderNumber(entry.entryCount)); } elem.textContent = text; @@ -137,26 +137,26 @@ let remoteURL = asset.remoteURL; li.classList.toggle('unsecure', - typeof remoteURL === 'string' - && remoteURL.lastIndexOf('http:', 0) === 0); + 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); + asset.cached === true && asset.writeTime > 0); if (asset.cached) { - li.querySelector('.status.cache') - .setAttribute('title', - lastUpdateTemplateString - .replace('{{ago}}', - renderETTS(asset.writeTime))); + li.querySelector('.status.cache') + .setAttribute('title', + lastUpdateTemplateString + .replace('{{ago}}', + renderETTS(asset.writeTime))); } li.classList.remove('discard'); return li; - }; + }; - let onListsReceived = function (details) { + let onListsReceived = function (details) { // Before all, set context vars listDetails = details; @@ -165,225 +165,227 @@ uDom('#lists .listEntry').addClass('discard'); let availableLists = details.available; - let listKeys = Object.keys(details.available); + let listKeys = Object.keys(details.available); // Sort works this way: // - Send /^https?:/ items at the end (custom hosts file URL) listKeys.sort(function (a, b) { - let ta = availableLists[a].title || a; + let ta = availableLists[a].title || a; let tb = availableLists[b].title || b; + let ca = reExternalHostFile.test(ta); + let cb = reExternalHostFile.test(tb); - if (reExternalHostFile.test(ta) === reExternalHostFile.test(tb)) { + if (ca === cb) { return ta.localeCompare(tb); - } + } - return reExternalHostFile.test(tb) ? -1 : 1; + return (cb) ? -1 : 1; }); let ulList = document.querySelector('#lists'); for (let i=0; i<listKeys.length; ++i) { - let liEntry = liFromListEntry(listKeys[i], ulList.children[i]); - if (liEntry.parentElement === null) { + let liEntry = liFromListEntry(listKeys[i], ulList.children[i]); + if (liEntry.parentElement === null) { ulList.appendChild(liEntry); - } + } } uDom('#lists .listEntry.discard').remove(); uDom('#listsOfBlockedHostsPrompt') - .text(vAPI.i18n('hostsFilesStats') - .replace('{{blockedHostnameCount}}', - renderNumber(details.blockedHostnameCount))); + .text(vAPI.i18n('hostsFilesStats') + .replace('{{blockedHostnameCount}}', + renderNumber(details.blockedHostnameCount))); uDom('#autoUpdate').prop('checked', - listDetails.autoUpdate === true); + listDetails.autoUpdate === true); if (!soft) { - hostsFilesSettingsHash = hashFromCurrentFromSettings(); + hostsFilesSettingsHash = hashFromCurrentFromSettings(); } renderWidgets(); - }; + }; - vAPI.messaging.send('hosts-files.js', { - what: 'getLists' - }, onListsReceived); + vAPI.messaging.send('hosts-files.js', { + what: 'getLists' + }, onListsReceived); }; let renderWidgets = function () { - let sel1 = - 'body:not(.updating) #lists .listEntry.obsolete ' - + '> input[type="checkbox"]:checked'; - let sel2 = '#lists .listEntry.cached'; - - uDom('#buttonUpdate') - .toggleClass('disabled', document.querySelector(sel1) === null); - uDom('#buttonPurgeAll') - .toggleClass('disabled', document.querySelector(sel2) === null); - uDom('#buttonApply') - .toggleClass('disabled', - hostsFilesSettingsHash === - hashFromCurrentFromSettings()); + let sel1 = + 'body:not(.updating) #lists .listEntry.obsolete ' + + '> input[type="checkbox"]:checked'; + let sel2 = '#lists .listEntry.cached'; + + uDom('#buttonUpdate') + .toggleClass('disabled', document.querySelector(sel1) === null); + uDom('#buttonPurgeAll') + .toggleClass('disabled', document.querySelector(sel2) === null); + uDom('#buttonApply') + .toggleClass('disabled', + hostsFilesSettingsHash === + hashFromCurrentFromSettings()); }; let updateAssetStatus = function (details) { - let 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) { - let str = vAPI.i18n.renderElapsedTimeToString(Date.now()); + let 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) { + let str = vAPI.i18n.renderElapsedTimeToString(Date.now()); li.querySelector('.status.cache') - .setAttribute('title', - lastUpdateTemplateString.replace('{{ago}}', str)); - } + .setAttribute('title', + lastUpdateTemplateString.replace('{{ago}}', str)); + } - renderWidgets(); + renderWidgets(); }; /** - Compute a hash from all the settings affecting how filter lists are loaded - in memory. + Compute a hash from all the settings affecting how filter lists are loaded + in memory. **/ let hashFromCurrentFromSettings = function () { - let hash = []; + let hash = []; let listHash = []; - let sel = '#lists .listEntry[data-listkey]:not(.toRemove)'; - let ext = 'externalHostsFiles'; + let sel = '#lists .listEntry[data-listkey]:not(.toRemove)'; + let ext = 'externalHostsFiles'; let listEntries = document.querySelectorAll(sel); for (let i=listEntries.length-1; i>=0; --i) { let li = listEntries[i]; if (li.querySelector('input[type="checkbox"]:checked') !== null) { - listHash.push(li.getAttribute('data-listkey')); + listHash.push(li.getAttribute('data-listkey')); } - } + } - hash.push(listHash.sort().join(), - reValidExternalList.test(document.getElementById(ext).value), - document.querySelector('#lists .listEntry.toRemove') !== null); + hash.push(listHash.sort().join(), + reValidExternalList.test(document.getElementById(ext).value), + document.querySelector('#lists .listEntry.toRemove') !== null); - return hash.join(); + return hash.join(); }; let onHostsFilesSettingsChanged = function () { - renderWidgets(); + renderWidgets(); }; let onRemoveExternalHostsFile = function (ev) { - let liEntry = uDom(this).ancestors('[data-listkey]'); + let liEntry = uDom(this).ancestors('[data-listkey]'); let listKey = liEntry.attr('data-listkey'); - if (listKey) { + if (listKey) { liEntry.toggleClass('toRemove'); renderWidgets(); - } + } - ev.preventDefault(); + ev.preventDefault(); }; let onPurgeClicked = function () { - let button = uDom(this); + let button = uDom(this); let liEntry = button.ancestors('[data-listkey]'); let listKey = liEntry.attr('data-listkey'); - if (!listKey) { - return; - } + if (!listKey) { + return; + } - vAPI.messaging.send('hosts-files.js', { - what: 'purgeCache', - assetKey: listKey - }); + vAPI.messaging.send('hosts-files.js', { + what: 'purgeCache', + assetKey: listKey + }); - liEntry.addClass('obsolete'); - liEntry.removeClass('cached'); + liEntry.addClass('obsolete'); + liEntry.removeClass('cached'); - if (liEntry.descendants('input').first().prop('checked')) { + if (liEntry.descendants('input').first().prop('checked')) { renderWidgets(); - } + } }; let selectHostsFiles = function (callback) { - // Hosts files to select - let toSelect = []; - let sel = '#lists .listEntry[data-listkey]:not(.toRemove)'; - let sel2 = '#lists .listEntry.toRemove[data-listkey]'; + // Hosts files to select + let toSelect = []; + let sel = '#lists .listEntry[data-listkey]:not(.toRemove)'; + let sel2 = '#lists .listEntry.toRemove[data-listkey]'; let liEntries = document.querySelectorAll(sel); for (let i=liEntries.length-1; i>=0; --i) { let li = liEntries[i]; if (li.querySelector('input[type="checkbox"]:checked') !== null) { - toSelect.push(li.getAttribute('data-listkey')); + toSelect.push(li.getAttribute('data-listkey')); } - } + } - // External hosts files to remove - let toRemove = []; - liEntries = document.querySelectorAll(sel2); + // External hosts files to remove + let toRemove = []; + liEntries = document.querySelectorAll(sel2); - for (let i=liEntries.length-1; i>=0; --i) { + for (let i=liEntries.length-1; i>=0; --i) { toRemove.push(liEntries[i].getAttribute('data-listkey')); - } + } - // External hosts files to import - let externalListsElem = document.getElementById('externalHostsFiles'); + // External hosts files to import + let externalListsElem = document.getElementById('externalHostsFiles'); let toImport = externalListsElem.value.trim(); - externalListsElem.value = ''; + externalListsElem.value = ''; - vAPI.messaging.send('hosts-files.js', { - what: 'selectHostsFiles', - toSelect: toSelect, - toImport: toImport, - toRemove: toRemove + vAPI.messaging.send('hosts-files.js', { + what: 'selectHostsFiles', + toSelect: toSelect, + toImport: toImport, + toRemove: toRemove }, callback); - hostsFilesSettingsHash = hashFromCurrentFromSettings(); + hostsFilesSettingsHash = hashFromCurrentFromSettings(); }; let buttonApplyHandler = function () { - uDom('#buttonApply').removeClass('enabled'); - selectHostsFiles(function () { + uDom('#buttonApply').removeClass('enabled'); + selectHostsFiles(function () { vAPI.messaging.send('hosts-files.js', { - what: 'reloadHostsFiles' - }); - }); + what: 'reloadHostsFiles' + }); + }); - renderWidgets(); + renderWidgets(); }; let buttonUpdateHandler = function () { - uDom('#buttonUpdate').removeClass('enabled'); - selectHostsFiles(function () { + uDom('#buttonUpdate').removeClass('enabled'); + selectHostsFiles(function () { document.body.classList.add('updating'); vAPI.messaging.send('hosts-files.js', { - what: 'forceUpdateAssets' - }); + what: 'forceUpdateAssets' + }); renderWidgets(); - }); - renderWidgets(); + }); + renderWidgets(); }; let buttonPurgeAllHandler = function () { - uDom('#buttonPurgeAll').removeClass('enabled'); - vAPI.messaging.send('hosts-files.js', { - what: 'purgeAllCaches' - }, function () { - renderHostsFiles(true); + uDom('#buttonPurgeAll').removeClass('enabled'); + vAPI.messaging.send('hosts-files.js', { + what: 'purgeAllCaches' + }, function () { + renderHostsFiles(true); }); }; let autoUpdateCheckboxChanged = function () { - vAPI.messaging.send('hosts-files.js', { - what: 'userSettings', - name: 'autoUpdate', - value: this.checked + vAPI.messaging.send('hosts-files.js', { + what: 'userSettings', + name: 'autoUpdate', + value: this.checked }); }; @@ -392,9 +394,9 @@ uDom('#buttonUpdate').on('click', buttonUpdateHandler); uDom('#buttonPurgeAll').on('click', buttonPurgeAllHandler); uDom('#lists').on('change', '.listEntry > input', - onHostsFilesSettingsChanged); + onHostsFilesSettingsChanged); uDom('#lists').on('click', '.listEntry > a.remove', - onRemoveExternalHostsFile); + onRemoveExternalHostsFile); uDom('#lists').on('click', 'span.cache', onPurgeClicked); uDom('#externalHostsFiles').on('input', onHostsFilesSettingsChanged); diff --git a/js/httpsb.js b/js/httpsb.js index be487d5..1a8362a 100644 --- a/js/httpsb.js +++ b/js/httpsb.js @@ -2,7 +2,7 @@ ηMatrix - a browser extension to black/white list requests. Copyright (C) 2014-2019 Raymond Hill - Copyright (C) 2019-2020 Alessio Vanni + Copyright (C) 2019-2022 Alessio Vanni This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see {http://www.gnu.org/licenses/}. - Home: https://libregit.spks.xyz/heckyel/ematrix + Home: https://gitlab.com/vannilla/ematrix uMatrix Home: https://github.com/gorhill/uMatrix */ @@ -2,7 +2,7 @@ ηMatrix - a browser extension to black/white list requests. Copyright (C) 2014-2019 Raymond Hill - Copyright (C) 2019-2020 Alessio Vanni + Copyright (C) 2019-2022 Alessio Vanni This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -44,187 +44,187 @@ // in case. let reSafeTags = - /^([\s\S]*?)<(b|blockquote|code|em|i|kbd|span|sup)>(.+?)<\/\2>([\s\S]*)$/; + /^([\s\S]*?)<(b|blockquote|code|em|i|kbd|span|sup)>(.+?)<\/\2>([\s\S]*)$/; let reSafeInput = /^([\s\S]*?)<(input type="[^"]+")>(.*?)([\s\S]*)$/; let reInput = /^input type=(['"])([a-z]+)\1$/; let reSafeLink = - /^([\s\S]*?)<(a href=['"]https?:\/\/[^'" <>]+['"])>(.+?)<\/a>([\s\S]*)$/; + /^([\s\S]*?)<(a href=['"]https?:\/\/[^'" <>]+['"])>(.+?)<\/a>([\s\S]*)$/; let reLink = /^a href=(['"])(https?:\/\/[^'"]+)\1$/; let safeTextToTagNode = function (text) { - let matches; - let node; + let matches; + let node; - if (text.lastIndexOf('a ', 0) === 0) { + if (text.lastIndexOf('a ', 0) === 0) { matches = reLink.exec(text); if (matches === null) { - return null; - } + return null; + } node = document.createElement('a'); node.setAttribute('href', matches[2]); return node; - } - if (text.lastIndexOf('input ', 0) === 0) { + } + if (text.lastIndexOf('input ', 0) === 0) { matches = reInput.exec(text); if (matches === null) { - return 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(). - // ηMatrix: is it important for us? - // ηMatrix (4.4.3 onwards): let's just use the variable and - // hope for the best, no need to have a redundant switch. - /* - switch (text) { - case 'b': + } + // Firefox extension validator warns if using a variable as + // argument for document.createElement(). + // ηMatrix: is it important for us? + // ηMatrix (4.4.3 onwards): let's just use the variable and + // hope for the best, no need to have a redundant switch. + /* + switch (text) { + case 'b': return document.createElement('b'); - case 'blockquote': + case 'blockquote': return document.createElement('blockquote'); - case 'code': + case 'code': return document.createElement('code'); - case 'em': + case 'em': return document.createElement('em'); - case 'i': + case 'i': return document.createElement('i'); - case 'kbd': + case 'kbd': return document.createElement('kbd'); - case 'span': + case 'span': return document.createElement('span'); - case 'sup': + case 'sup': return document.createElement('sup'); - default: + default: break; - } - */ - return document.createElement(text); + } + */ + return document.createElement(text); }; let safeTextToTextNode = function (text) { - if (text.indexOf('&') !== -1) { + if (text.indexOf('&') !== -1) { text = text - .replace(/“/g, '“') - .replace(/”/g, '”') - .replace(/‘/g, '‘') - .replace(/’/g, '’') - .replace(/</g, '<') - .replace(/>/g, '>') - .replace(/'/g, '\''); - } - return document.createTextNode(text); + .replace(/“/g, '“') + .replace(/”/g, '”') + .replace(/‘/g, '‘') + .replace(/’/g, '’') + .replace(/</g, '<') + .replace(/>/g, '>') + .replace(/'/g, '\''); + } + return document.createTextNode(text); }; let safeTextToDOM = function (text, parent) { - if (text === '') { - return; - } + if (text === '') { + return; + } - if (text.indexOf('<') === -1) { + if (text.indexOf('<') === -1) { return parent.appendChild(safeTextToTextNode(text)); - } + } - // Using the raw <p> element is not allowed for security reason, - // but it's good for formatting content, so here it's substituted - // for a safer equivalent (for the extension.) - text = text - .replace(/^<p>|<\/p>/g, '') + // Using the raw <p> element is not allowed for security reason, + // but it's good for formatting content, so here it's substituted + // for a safer equivalent (for the extension.) + text = text + .replace(/^<p>|<\/p>/g, '') .replace(/<p>/g, '\n\n'); - let matches; - let matches1 = reSafeTags.exec(text); - let matches2 = reSafeLink.exec(text); - if (matches1 !== null && matches2 !== null) { + let matches; + let matches1 = reSafeTags.exec(text); + let matches2 = reSafeLink.exec(text); + if (matches1 !== null && matches2 !== null) { matches = matches1.index < matches2.index ? matches1 : matches2; - } else if (matches1 !== null) { + } else if (matches1 !== null) { matches = matches1; - } else if (matches2 !== null) { + } else if (matches2 !== null) { matches = matches2; - } else { + } else { matches = reSafeInput.exec(text); - } - if (matches === null) { + } + if (matches === null) { parent.appendChild(safeTextToTextNode(text)); return; - } + } - safeTextToDOM(matches[1], parent); - let node = safeTextToTagNode(matches[2]) || parent; - safeTextToDOM(matches[3], node); - parent.appendChild(node); - safeTextToDOM(matches[4], parent); + safeTextToDOM(matches[1], parent); + let 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) { - let docu = document; + let docu = document; let root = context || docu; let i, elem, text; - let elems = root.querySelectorAll('[data-i18n]'); - let n = elems.length; - for (i=0; i<n; ++i) { + let elems = root.querySelectorAll('[data-i18n]'); + let n = elems.length; + for (i=0; i<n; ++i) { elem = elems[i]; text = vAPI.i18n(elem.getAttribute('data-i18n')); if (!text) { - continue; - } + continue; + } // TODO: remove once it's all replaced with <input type="..."> if (text.indexOf('{') !== -1) { - text = - text.replace(/\{\{input:([^}]+)\}\}/g, '<input type="$1">'); + text = + text.replace(/\{\{input:([^}]+)\}\}/g, '<input type="$1">'); } safeTextToDOM(text, elem); - } + } - uDom('[title]', context).forEach(function (elem) { + uDom('[title]', context).forEach(function (elem) { let title = vAPI.i18n(elem.attr('title')); if (title) { - elem.attr('title', title); + elem.attr('title', title); } - }); + }); - uDom('[placeholder]', context).forEach(function (elem) { + uDom('[placeholder]', context).forEach(function (elem) { elem.attr('placeholder', vAPI.i18n(elem.attr('placeholder'))); - }); + }); - uDom('[data-i18n-tip]', context).forEach(function (elem) { + uDom('[data-i18n-tip]', context).forEach(function (elem) { elem.attr('data-tip', - vAPI.i18n(elem.attr('data-i18n-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) { - let value = (Date.now() - tstamp) / 60000; - if (value < 2) { + let value = (Date.now() - tstamp) / 60000; + if (value < 2) { return vAPI.i18n('elapsedOneMinuteAgo'); - } - if (value < 60) { + } + if (value < 60) { return vAPI - .i18n('elapsedManyMinutesAgo') - .replace('{{value}}', Math.floor(value).toLocaleString()); - } - value /= 60; - if (value < 2) { + .i18n('elapsedManyMinutesAgo') + .replace('{{value}}', Math.floor(value).toLocaleString()); + } + value /= 60; + if (value < 2) { return vAPI.i18n('elapsedOneHourAgo'); - } - if (value < 24) { + } + if (value < 24) { return vAPI - .i18n('elapsedManyHoursAgo') - .replace('{{value}}', Math.floor(value).toLocaleString()); - } - value /= 24; - if (value < 2) { + .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()); + } + return vAPI + .i18n('elapsedManyDaysAgo') + .replace('{{value}}', Math.floor(value).toLocaleString()); }; })(); diff --git a/js/liquid-dict.js b/js/liquid-dict.js index 8a03d7a..085f67e 100644 --- a/js/liquid-dict.js +++ b/js/liquid-dict.js @@ -2,7 +2,7 @@ ηMatrix - a browser extension to black/white list requests. Copyright (C) 2014-2019 Raymond Hill - Copyright (C) 2019-2020 Alessio Vanni + Copyright (C) 2019-2022 Alessio Vanni This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see {http://www.gnu.org/licenses/}. - Home: https://libregit.spks.xyz/heckyel/ematrix + Home: https://gitlab.com/vannilla/ematrix uMatrix Home: https://github.com/gorhill/uMatrix */ @@ -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; - } - } - 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 LiquidDict = function() { + this.dict = {}; + this.count = 0; + this.duplicateCount = 0; + this.bucketCount = 0; + this.frozenBucketCount = 0; - 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; - }; + // Somewhat arbitrary: I need to come up with hard data to know at which + // point binary search is better than indexOf. + this.cutoff = 500; +}; - /******************************************************************************/ +/******************************************************************************/ - LiquidDict.prototype.add = function(word) { - var key = this.makeKey(word); - if ( key === undefined ) { - return false; +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 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 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); } - if ( bucket[word] === undefined ) { - bucket[word] = true; - this.count += 1; + } +}; + +/******************************************************************************/ + +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; } - this.duplicateCount += 1; + } + return false; +}; + +/******************************************************************************/ + +LiquidDict.prototype.add = function(word) { + var key = this.makeKey(word); + if ( key === undefined ) { 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); - } + } + 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.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 8f4ffbf..5f9aedb 100644 --- a/js/logger-ui.js +++ b/js/logger-ui.js @@ -2,7 +2,7 @@ ηMatrix - a browser extension to black/white list requests. Copyright (C) 2015-2019 Raymond Hill - Copyright (C) 2019-2020 Alessio Vanni + Copyright (C) 2019-2022 Alessio Vanni This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see {http://www.gnu.org/licenses/}. - Home: https://libregit.spks.xyz/heckyel/ematrix + Home: https://gitlab.com/vannilla/ematrix uMatrix Home: https://github.com/gorhill/sessbench */ @@ -39,249 +39,249 @@ let hiddenTemplate = document.querySelector('#hiddenTemplate > span'); let prettyRequestTypes = { - 'main_frame': 'doc', - 'stylesheet': 'css', - 'sub_frame': 'frame', - 'xmlhttprequest': 'xhr' + 'main_frame': 'doc', + 'stylesheet': 'css', + 'sub_frame': 'frame', + 'xmlhttprequest': 'xhr' }; let dontEmphasizeSet = new Set([ - 'COOKIE', - 'CSP', - 'REFERER' + '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'); + .getElementById('content') + .style + .setProperty('margin-top', + document.getElementById('toolbar').clientHeight + 'px'); let classNameFromTabId = function (tabId) { - if (tabId === noTabId) { + if (tabId === noTabId) { return 'tab_bts'; - } - if (tabId !== '') { + } + if (tabId !== '') { return 'tab_' + tabId; - } - return ''; + } + return ''; }; // Emphasize hostname and cookie name. let emphasizeCookie = function (s) { - let pnode = emphasizeHostname(s); - if (pnode.childNodes.length !== 3) { + let pnode = emphasizeHostname(s); + if (pnode.childNodes.length !== 3) { return pnode; - } + } - let prefix = '-cookie:'; - let text = pnode.childNodes[2].textContent; + let prefix = '-cookie:'; + let text = pnode.childNodes[2].textContent; - let beg = text.indexOf(prefix); - if (beg === -1) { + let beg = text.indexOf(prefix); + if (beg === -1) { return pnode; - } - beg += prefix.length; + } + beg += prefix.length; - let end = text.indexOf('}', beg); - if (end === -1) { + let end = text.indexOf('}', beg); + if (end === -1) { return pnode; - } + } - let 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]); + let 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; + return pnode; }; // Emphasize hostname in URL. let emphasizeHostname = function (url) { - let hnbeg = url.indexOf('://'); - if (hnbeg === -1) { + let hnbeg = url.indexOf('://'); + if (hnbeg === -1) { return document.createTextNode(url); - } - hnbeg += 3; + } + hnbeg += 3; - let hnend = url.indexOf('/', hnbeg); - if (hnend === -1) { + let hnend = url.indexOf('/', hnbeg); + if (hnend === -1) { hnend = url.slice(hnbeg).search(/\?#/); if (hnend !== -1) { - hnend += hnbeg; + hnend += hnbeg; } else { - hnend = url.length; + hnend = url.length; } - } + } - let 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); + let 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; + return node; }; let createCellAt = function (tr, index) { - let td = tr.cells[index]; - let mustAppend = !td; - if (mustAppend) { + let td = tr.cells[index]; + let mustAppend = !td; + if (mustAppend) { td = tdJunkyard.pop(); - } + } - if (td) { + if (td) { td.removeAttribute('colspan'); td.textContent = ''; - } else { + } else { td = document.createElement('td'); - } - if (mustAppend) { + } + if (mustAppend) { tr.appendChild(td); - } + } - return td; + return td; }; let createRow = function (layout) { - let tr = trJunkyard.pop(); - if (tr) { + let tr = trJunkyard.pop(); + if (tr) { tr.className = ''; - } else { + } else { tr = document.createElement('tr'); - } + } - let index; - for (index=0; index<firstVarDataCol; ++index) { + let index; + for (index=0; index<firstVarDataCol; ++index) { createCellAt(tr, index); - } + } - let i = 1, span = 1, td; - for (;;) { + let i = 1, span = 1, td; + for (;;) { td = createCellAt(tr, index); if (i === lastVarDataIndex) { - break; + break; } if (layout.charAt(i) !== '1') { - span += 1; + span += 1; } else { - if (span !== 1) { + if (span !== 1) { td.setAttribute('colspan', span); - } - index += 1; - span = 1; + } + index += 1; + span = 1; } i += 1; - } + } - if (span !== 1) { + if (span !== 1) { td.setAttribute('colspan', span); - } - index += 1; - while ((td = tr.cells[index])) { + } + index += 1; + while ((td = tr.cells[index])) { tdJunkyard.push(tr.removeChild(td)); - } + } - return tr; + return tr; }; let createHiddenTextNode = function (text) { - let node = hiddenTemplate.cloneNode(true); - node.textContent = text; - return node; + let node = hiddenTemplate.cloneNode(true); + node.textContent = text; + return node; }; let padTo2 = function (v) { - return v < 10 ? '0' + v : v; + return v < 10 ? '0' + v : v; }; let createGap = function (tabId, url) { - let 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); + let 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); }; let renderLogEntry = function (entry) { - let tr; - let fvdc = firstVarDataCol; + let tr; + let fvdc = firstVarDataCol; - switch (entry.cat) { - case 'error': - case 'info': + switch (entry.cat) { + case 'error': + case 'info': tr = createRow('1'); if (entry.d0 === 'cookie') { - tr.cells[fvdc].appendChild(emphasizeCookie(entry.d1)); + tr.cells[fvdc].appendChild(emphasizeCookie(entry.d1)); } else { - tr.cells[fvdc].textContent = entry.d0; + tr.cells[fvdc].textContent = entry.d0; } break; - case 'net': + 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); + createGap(entry.tab, entry.d1); } if (entry.d3) { - tr.classList.add('blocked'); - tr.cells[fvdc].textContent = '--'; + tr.classList.add('blocked'); + tr.cells[fvdc].textContent = '--'; } else { - tr.cells[fvdc].textContent = ''; + tr.cells[fvdc].textContent = ''; } tr.cells[fvdc+1].textContent = - (prettyRequestTypes[entry.d2] || entry.d2); + (prettyRequestTypes[entry.d2] || entry.d2); if (dontEmphasizeSet.has(entry.d2)) { - tr.cells[fvdc+2].textContent = entry.d1; + tr.cells[fvdc+2].textContent = entry.d1; } else if ( entry.d2 === 'cookie' ) { - tr.cells[fvdc+2].appendChild(emphasizeCookie(entry.d1)); + tr.cells[fvdc+2].appendChild(emphasizeCookie(entry.d1)); } else { - tr.cells[fvdc+2].appendChild(emphasizeHostname(entry.d1)); + tr.cells[fvdc+2].appendChild(emphasizeHostname(entry.d1)); } break; - default: + default: tr = createRow('1'); tr.cells[fvdc].textContent = entry.d0; break; - } - - // Fields common to all rows. - let time = logDate; - time.setTime(entry.tstamp - logDateTimezoneOffset); - tr.cells[0].textContent = padTo2(time.getUTCHours()) - + ':' - + padTo2(time.getUTCMinutes()) - + ':' - + padTo2(time.getSeconds()); - - if (entry.tab) { + } + + // Fields common to all rows. + let 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[1].appendChild(createHiddenTextNode('bts')); } - } - if (entry.cat !== '') { + } + if (entry.cat !== '') { tr.classList.add('cat_' + entry.cat); - } + } - rowFilterer.filterOne(tr, true); + rowFilterer.filterOne(tr, true); - tbody.insertBefore(tr, tbody.firstChild); + tbody.insertBefore(tr, tbody.firstChild); }; // Reuse date objects. @@ -289,180 +289,180 @@ let logDateTimezoneOffset = logDate.getTimezoneOffset() * 60000; let renderLogEntries = function (response) { - let entries = response.entries; - if (entries.length === 0) { + let entries = response.entries; + if (entries.length === 0) { return; - } + } - // Preserve scroll position - let height = tbody.offsetHeight; + // Preserve scroll position + let height = tbody.offsetHeight; - let tabIds = response.tabIds; - let n = entries.length; - let entry; - for (let i=0; i<n; ++i) { + let tabIds = response.tabIds; + let n = entries.length; + let entry; + for (let i=0; i<n; ++i) { entry = entries[i]; // Unlikely, but it may happen if (entry.tab && tabIds.hasOwnProperty(entry.tab) === false) { - continue; + 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); + // 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); - let yDelta = tbody.offsetHeight - height; - if (yDelta === 0) { + let yDelta = tbody.offsetHeight - height; + if (yDelta === 0) { return; - } + } - // Chromium: - // body.scrollTop = good value - // body.parentNode.scrollTop = 0 - // if (document.body.scrollTop !== 0) { + // 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 - let parentNode = document.body.parentNode; - if (parentNode && parentNode.scrollTop !== 0) { + // Firefox: + // body.scrollTop = 0 + // body.parentNode.scrollTop = good value + let parentNode = document.body.parentNode; + if (parentNode && parentNode.scrollTop !== 0) { parentNode.scrollTop += yDelta; - } + } }; let synchronizeTabIds = function (newTabIds) { - let oldTabIds = allTabIds; - let autoDeleteVoidRows = - !!vAPI.localStorage.getItem('loggerAutoDeleteVoidRows'); - let rowVoided = false; - let trs; + let oldTabIds = allTabIds; + let autoDeleteVoidRows = + !!vAPI.localStorage.getItem('loggerAutoDeleteVoidRows'); + let rowVoided = false; + let trs; - for (let tabId in oldTabIds) { + for (let tabId in oldTabIds) { if (oldTabIds.hasOwnProperty(tabId) === false) { - continue; + continue; } if (newTabIds.hasOwnProperty(tabId)) { - continue; + continue; } // Mark or remove voided rows trs = uDom('.tab_' + tabId); if (autoDeleteVoidRows) { - toJunkyard(trs); + toJunkyard(trs); } else { - trs.removeClass('canMtx'); - rowVoided = true; + trs.removeClass('canMtx'); + rowVoided = true; } // Remove popup if it is currently bound to a removed tab. if (tabId === popupManager.tabId) { - popupManager.toggleOff(); + popupManager.toggleOff(); } - } + } - let select = document.getElementById('pageSelector'); - let selectValue = select.value; - let tabIds = Object.keys(newTabIds).sort(function (a, b) { + let select = document.getElementById('pageSelector'); + let selectValue = select.value; + let tabIds = Object.keys(newTabIds).sort(function (a, b) { return newTabIds[a].localeCompare(newTabIds[b]); - }); + }); - let i, j; - for (i=0, j=2; i<tabIds.length; ++i) { + let i, j; + for (i=0, j=2; i<tabIds.length; ++i) { let tabId = tabIds[i]; if (tabId === noTabId) { - continue; + continue; } let option = select.options[j]; j += 1; if (!option) { - option = document.createElement('option'); - select.appendChild(option); + option = document.createElement('option'); + select.appendChild(option); } option.textContent = newTabIds[tabId]; option.value = classNameFromTabId(tabId); if (option.value === selectValue) { - option.setAttribute('selected', ''); + option.setAttribute('selected', ''); } else { - option.removeAttribute('selected'); + option.removeAttribute('selected'); } - } + } - while (j < select.options.length) { + while (j < select.options.length) { select.removeChild(select.options[j]); - } + } - if (select.value !== selectValue) { + if (select.value !== selectValue) { select.selectedIndex = 0; select.value = ''; select.options[0].setAttribute('selected', ''); pageSelectorChanged(); - } + } - allTabIds = newTabIds; + allTabIds = newTabIds; - return rowVoided; + return rowVoided; }; let truncateLog = function (size) { - if (size === 0) { + if (size === 0) { size = 5000; - } + } - let tbody = document.querySelector('#content tbody'); - size = Math.min(size, 10000); + let tbody = document.querySelector('#content tbody'); + size = Math.min(size, 10000); - while (tbody.childElementCount > size) { + while (tbody.childElementCount > size) { let tr = tbody.lastElementChild; trJunkyard.push(tbody.removeChild(tr)); - } + } }; let onLogBufferRead = function (response) { - if (!response || response.unavailable) { + if (!response || response.unavailable) { readLogBufferAsync(); return; - } + } - // This tells us the behind-the-scene tab id - noTabId = response.noTabId; + // This tells us the behind-the-scene tab id + noTabId = response.noTabId; - // This may have changed meanwhile - if (response.maxLoggedRequests !== maxEntries) { + // This may have changed meanwhile + if (response.maxLoggedRequests !== maxEntries) { maxEntries = response.maxLoggedRequests; uDom('#maxEntries').val(maxEntries || ''); - } + } - // Neuter rows for which a tab does not exist anymore - let rowVoided = false; - if (response.tabIdsToken !== allTabIdsToken) { + // Neuter rows for which a tab does not exist anymore + let rowVoided = false; + if (response.tabIdsToken !== allTabIdsToken) { rowVoided = synchronizeTabIds(response.tabIds); allTabIdsToken = response.tabIdsToken; - } + } - renderLogEntries(response); + renderLogEntries(response); - if (rowVoided) { + if (rowVoided) { uDom('#clean') - .toggleClass('disabled', - tbody - .querySelector('tr.tab:not(.canMtx)') === null); - } + .toggleClass('disabled', + tbody + .querySelector('tr.tab:not(.canMtx)') === null); + } - // Synchronize toolbar with content of log - uDom('#clear').toggleClass('disabled', - 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 @@ -471,83 +471,83 @@ // no multi time out events. let readLogBuffer = function () { - if (ownerId === undefined) { - return; - } - - vAPI.messaging.send('logger-ui.js', { - what: 'readMany', - ownerId: ownerId - }, onLogBufferRead); + if (ownerId === undefined) { + return; + } + + vAPI.messaging.send('logger-ui.js', { + what: 'readMany', + ownerId: ownerId + }, onLogBufferRead); }; let readLogBufferAsync = function () { - if (ownerId === undefined) { - return; - } - vAPI.setTimeout(readLogBuffer, 1200); + if (ownerId === undefined) { + return; + } + vAPI.setTimeout(readLogBuffer, 1200); }; let pageSelectorChanged = function () { - let style = document.getElementById('tabFilterer'); - let tabClass = document.getElementById('pageSelector').value; - let sheet = style.sheet; + let style = document.getElementById('tabFilterer'); + let tabClass = document.getElementById('pageSelector').value; + let sheet = style.sheet; - while (sheet.cssRules.length !== 0) { + while (sheet.cssRules.length !== 0) { sheet.deleteRule(0); - } + } - if (tabClass !== '') { + if (tabClass !== '') { sheet.insertRule('#content table tr:not(.' - + tabClass - + ') { display: none; }', 0); - } - uDom('#refresh').toggleClass('disabled', - tabClass === '' || tabClass === 'tab_bts'); + + tabClass + + ') { display: none; }', 0); + } + uDom('#refresh').toggleClass('disabled', + tabClass === '' || tabClass === 'tab_bts'); }; let refreshTab = function () { - let tabClass = document.getElementById('pageSelector').value; - let matches = tabClass.match(/^tab_(.+)$/); - if (matches === null) { + let tabClass = document.getElementById('pageSelector').value; + let matches = tabClass.match(/^tab_(.+)$/); + if (matches === null) { return; - } + } - if (matches[1] === 'bts') { + if (matches[1] === 'bts') { return; - } + } - vAPI.messaging.send('logger-ui.js', { - what: 'forceReloadTab', - tabId: matches[1] - }); + vAPI.messaging.send('logger-ui.js', { + what: 'forceReloadTab', + tabId: matches[1] + }); }; let onMaxEntriesChanged = function () { - let raw = uDom(this).val(); + let raw = uDom(this).val(); - try { + try { maxEntries = parseInt(raw, 10); if (isNaN(maxEntries)) { - maxEntries = 0; + maxEntries = 0; } - } catch (e) { + } catch (e) { maxEntries = 0; - } + } - vAPI.messaging.send('logger-ui.js', { + vAPI.messaging.send('logger-ui.js', { what: 'userSettings', name: 'maxLoggedRequests', value: maxEntries - }); + }); - truncateLog(maxEntries); + truncateLog(maxEntries); }; let rowFilterer = (function () { - let filters = []; + let filters = []; - let parseInput = function () { + let parseInput = function () { filters = []; let rawPart, hardBeg, hardEnd; @@ -557,69 +557,69 @@ let n = rawParts.length; for (let i=0; i<n; ++i) { - rawPart = rawParts[i]; - if (rawPart.charAt(0) === '!') { + rawPart = rawParts[i]; + if (rawPart.charAt(0) === '!') { if (reStrs.length === 0) { - not = true; + not = true; } rawPart = rawPart.slice(1); - } + } - hardBeg = rawPart.charAt(0) === '|'; - if (hardBeg) { + hardBeg = rawPart.charAt(0) === '|'; + if (hardBeg) { rawPart = rawPart.slice(1); - } + } - hardEnd = rawPart.slice(-1) === '|'; - if (hardEnd) { + hardEnd = rawPart.slice(-1) === '|'; + if (hardEnd) { rawPart = rawPart.slice(0, -1); - } + } - if ( rawPart === '' ) { + if ( rawPart === '' ) { continue; - } + } - reStr = rawPart.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - if (hardBeg) { + reStr = rawPart.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + if (hardBeg) { reStr = '(?:^|\\s)' + reStr; - } - if (hardEnd) { + } + if (hardEnd) { reStr += '(?:\\s|$)'; - } + } - reStrs.push(reStr); - if (i < (n - 1) && rawParts[i + 1] === '||') { + reStrs.push(reStr); + if (i < (n - 1) && rawParts[i + 1] === '||') { i += 1; continue; - } + } - reStr = reStrs.length === 1 ? reStrs[0] : reStrs.join('|'); - filters.push({ + reStr = reStrs.length === 1 ? reStrs[0] : reStrs.join('|'); + filters.push({ re: new RegExp(reStr, 'i'), r: !not - }); - reStrs = []; - not = false; + }); + reStrs = []; + not = false; } - }; + }; - let filterOne = function (tr, clean) { + let filterOne = function (tr, clean) { let ff = filters; let fcount = ff.length; if (fcount === 0 && clean === true) { - return; + return; } // do not filter out doc boundaries, they help separate // important section of log. let cl = tr.classList; if (cl.contains('doc')) { - return; + return; } if (fcount === 0) { - cl.remove('f'); - return; + cl.remove('f'); + return; } let cc = tr.cells; @@ -631,141 +631,141 @@ // positive filter expression = there must one hit on any field // negative filter expression = there must be no hit on all fields for (let i=0; i<fcount; ++i) { - f = ff[i]; - hit = !f.r; + f = ff[i]; + hit = !f.r; - for (j=0; j<ccount; ++j) { + for (j=0; j<ccount; ++j) { if (f.re.test(cc[j].textContent)) { - hit = f.r; - break; + hit = f.r; + break; } - } + } - if (!hit) { + if (!hit) { cl.add('f'); return; - } + } } cl.remove('f'); - }; + }; - let filterAll = function () { + let filterAll = function () { // Special case: no filter if (filters.length === 0) { - uDom('#content tr').removeClass('f'); - return; + uDom('#content tr').removeClass('f'); + return; } let tbody = document.querySelector('#content tbody'); let rows = tbody.rows; for (let i=rows.length-1; i>=0; --i) { - filterOne(rows[i]); + filterOne(rows[i]); } - }; + }; - let onFilterChangedAsync = (function () { + let onFilterChangedAsync = (function () { let timer = null; let commit = function () { - timer = null; - parseInput(); - filterAll(); + timer = null; + parseInput(); + filterAll(); }; return function () { - if (timer !== null) { + if (timer !== null) { clearTimeout(timer); - } - timer = vAPI.setTimeout(commit, 750); + } + timer = vAPI.setTimeout(commit, 750); }; - })(); + })(); - let onFilterButton = function () { + let onFilterButton = function () { let cl = document.body.classList; cl.toggle('f', cl.contains('f') === false); - }; + }; - uDom('#filterButton').on('click', onFilterButton); - uDom('#filterInput').on('input', onFilterChangedAsync); + uDom('#filterButton').on('click', onFilterButton); + uDom('#filterInput').on('input', onFilterChangedAsync); - return { + return { filterOne: filterOne, filterAll: filterAll, - }; + }; })(); let toJunkyard = function (trs) { - trs.remove(); - for (let i=trs.length-1; i>=0; --i) { + trs.remove(); + for (let i=trs.length-1; i>=0; --i) { trJunkyard.push(trs.nodeAt(i)); - } + } }; let clearBuffer = function () { - let tbody = document.querySelector('#content tbody'); - let tr; + let tbody = document.querySelector('#content tbody'); + let tr; - while (tbody.firstChild !== null) { + while (tbody.firstChild !== null) { tr = tbody.lastElementChild; trJunkyard.push(tbody.removeChild(tr)); - } + } - uDom('#clear').addClass('disabled'); - uDom('#clean').addClass('disabled'); + uDom('#clear').addClass('disabled'); + uDom('#clean').addClass('disabled'); }; let cleanBuffer = function () { - let rows = uDom('#content tr.tab:not(.canMtx)').remove(); - for (let i=rows.length-1; i>=0; --i) { + let rows = uDom('#content tr.tab:not(.canMtx)').remove(); + for (let i=rows.length-1; i>=0; --i) { trJunkyard.push(rows.nodeAt(i)); - } - uDom('#clean').addClass('disabled'); + } + uDom('#clean').addClass('disabled'); }; let toggleCompactView = function () { - document.body.classList.toggle('compactView'); - uDom('#content table .vExpanded').removeClass('vExpanded'); + document.body.classList.toggle('compactView'); + uDom('#content table .vExpanded').removeClass('vExpanded'); }; let toggleCompactRow = function (ev) { - ev.target.parentElement.classList.toggle('vExpanded'); + ev.target.parentElement.classList.toggle('vExpanded'); }; let popupManager = (function () { - let realTabId = null; - let localTabId = null; - let container = null; - let popup = null; - let popupObserver = null; - let style = null; - let styleTemplate = [ + let realTabId = null; + let localTabId = null; + let container = null; + let popup = null; + let popupObserver = null; + let style = null; + let styleTemplate = [ 'tr:not(.tab_{{tabId}}) {', 'cursor: not-allowed;', 'opacity: 0.2;', '}' - ].join('\n'); + ].join('\n'); - let resizePopup = function () { + let resizePopup = function () { if (popup === null) { - return; + return; } let popupBody = popup.contentWindow.document.body; if (popupBody.clientWidth !== 0 - && container.clientWidth !== popupBody.clientWidth) { - container.style.setProperty('width', popupBody.clientWidth + 'px'); + && 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'); + && popup.clientHeight !== popupBody.clientHeight) { + popup.style.setProperty('height', popupBody.clientHeight + 'px'); } let ph = document.documentElement.clientHeight; let crect = container.getBoundingClientRect(); if (crect.height > ph) { - popup.style.setProperty('height', 'calc(' + ph + 'px - 1.8em)'); + popup.style.setProperty('height', 'calc(' + ph + 'px - 1.8em)'); } // Adjust width for presence/absence of vertical scroll bar which may @@ -773,54 +773,54 @@ let cw = container.clientWidth; let dw = popup.contentWindow.document.documentElement.clientWidth; if (cw !== dw) { - container.style.setProperty('width', (2 * cw - dw) + 'px'); + container.style.setProperty('width', (2 * cw - dw) + 'px'); } - }; + }; - let toggleSize = function () { + let toggleSize = function () { container.classList.toggle('hide'); - }; + }; - let onResizeRequested = function () { + let onResizeRequested = function () { let popupBody = popup.contentWindow.document.body; if (popupBody.hasAttribute('data-resize-popup') === false) { - return; + return; } popupBody.removeAttribute('data-resize-popup'); resizePopup(); - }; + }; - let onLoad = function () { + let onLoad = function () { resizePopup(); let popupBody = popup.contentDocument.body; popupBody.removeAttribute('data-resize-popup'); popupObserver.observe(popupBody, { - attributes: true, - attributesFilter: [ 'data-resize-popup' ] + attributes: true, + attributesFilter: [ 'data-resize-popup' ] }); - }; + }; - let toggleOn = function (td) { + let toggleOn = function (td) { let tr = td.parentNode; let matches = tr.className.match(/(?:^| )tab_([^ ]+)/); if (matches === null) { - return; + return; } realTabId = localTabId = matches[1]; if (localTabId === 'bts') { - realTabId = noTabId; + realTabId = noTabId; } container = document.getElementById('popupContainer'); container - .querySelector('div > span:nth-of-type(1)') - .addEventListener('click', toggleSize); + .querySelector('div > span:nth-of-type(1)') + .addEventListener('click', toggleSize); container - .querySelector('div > span:nth-of-type(2)') - .addEventListener('click', toggleOff); + .querySelector('div > span:nth-of-type(2)') + .addEventListener('click', toggleOff); popup = document.createElement('iframe'); popup.addEventListener('load', onLoad); @@ -832,17 +832,17 @@ style.textContent = styleTemplate.replace('{{tabId}}', localTabId); document.body.classList.add('popupOn'); - }; + }; - let toggleOff = function () { + let toggleOff = function () { document.body.classList.remove('popupOn'); container - .querySelector('div > span:nth-of-type(1)') - .removeEventListener('click', toggleSize); + .querySelector('div > span:nth-of-type(1)') + .removeEventListener('click', toggleSize); container - .querySelector('div > span:nth-of-type(2)') - .removeEventListener('click', toggleOff); + .querySelector('div > span:nth-of-type(2)') + .removeEventListener('click', toggleOff); container.classList.remove('hide'); popup.removeEventListener('load', onLoad); @@ -857,48 +857,48 @@ container = null; realTabId = null; - }; + }; - let exports = { + let exports = { toggleOn: function (ev) { - if (realTabId === null) { + if (realTabId === null) { toggleOn(ev.target); - } + } }, toggleOff: function () { - if (realTabId !== null) { + if (realTabId !== null) { toggleOff(); - } + } } - }; + }; - Object.defineProperty(exports, 'tabId', { + Object.defineProperty(exports, 'tabId', { get: function () { - return realTabId || 0; - }, - }); + return realTabId || 0; + }, + }); - return exports; + return exports; })(); let grabView = function () { - if (ownerId === undefined) { + if (ownerId === undefined) { ownerId = Date.now(); - } - readLogBufferAsync(); + } + readLogBufferAsync(); }; let releaseView = function () { - if (ownerId === undefined) { - return; - } + if (ownerId === undefined) { + return; + } - vAPI.messaging.send('logger-ui.js', { - what: 'releaseView', - ownerId: ownerId - }); + vAPI.messaging.send('logger-ui.js', { + what: 'releaseView', + ownerId: ownerId + }); - ownerId = undefined; + ownerId = undefined; }; window.addEventListener('pagehide', releaseView); @@ -915,7 +915,7 @@ uDom('#clear').on('click', clearBuffer); uDom('#maxEntries').on('change', onMaxEntriesChanged); uDom('#content table').on('click', 'tr > td:nth-of-type(1)', - toggleCompactRow); + toggleCompactRow); uDom('#content table').on('click', 'tr.canMtx > td:nth-of-type(2)', - popupManager.toggleOn); + popupManager.toggleOn); })(); diff --git a/js/logger.js b/js/logger.js index a602d91..86f8c9c 100644 --- a/js/logger.js +++ b/js/logger.js @@ -2,7 +2,7 @@ ηMatrix - a browser extension to black/white list requests. Copyright (C) 2015-2019 Raymond Hill - Copyright (C) 2019-2020 Alessio Vanni + Copyright (C) 2019-2022 Alessio Vanni This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see {http://www.gnu.org/licenses/}. - Home: https://libregit.spks.xyz/heckyel/ematrix + Home: https://gitlab.com/vannilla/ematrix uMatrix Home: https://github.com/gorhill/uBlock */ @@ -48,7 +48,7 @@ var janitor = function () { if (buffer !== null - && lastReadTime < (Date.now() - logBufferObsoleteAfter)) { + && lastReadTime < (Date.now() - logBufferObsoleteAfter)) { buffer = null; writePtr = 0; api.ownerId = undefined; @@ -63,8 +63,8 @@ writeOne: function () { if (buffer === null) { - return; - } + return; + } if (writePtr === buffer.length) { buffer.push(new LogEntry(arguments)); diff --git a/js/main-blocked.js b/js/main-blocked.js index cea79ce..20703a7 100644 --- a/js/main-blocked.js +++ b/js/main-blocked.js @@ -2,7 +2,7 @@ ηMatrix - a browser extension to black/white list requests. Copyright (C) 2015-2019 Raymond Hill - Copyright (C) 2019-2020-2021 Alessio Vanni + Copyright (C) 2019-2022 Alessio Vanni This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/js/matrix.js b/js/matrix.js index fc0c90d..58276f6 100644 --- a/js/matrix.js +++ b/js/matrix.js @@ -2,7 +2,7 @@ ηMatrix - a browser extension to black/white list requests. Copyright (C) 2014-2019 Raymond Hill - Copyright (C) 2019-2020 Alessio Vanni + Copyright (C) 2019-2022 Alessio Vanni This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see {http://www.gnu.org/licenses/}. - Home: https://libregit.spks.xyz/heckyel/ematrix + Home: https://gitlab.com/vannilla/ematrix uMatrix Home: https://github.com/gorhill/uMatrix */ @@ -32,844 +32,844 @@ Cu.import('chrome://ematrix/content/lib/Punycode.jsm'); Cu.import('chrome://ematrix/content/lib/UriTools.jsm'); - /******************************************************************************/ +/******************************************************************************/ - 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; - }; +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; - - 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 - }; +/******************************************************************************/ - 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.Transparent = 0; +Matrix.Red = 1; +Matrix.Green = 2; +Matrix.Gray = 3; - /******************************************************************************/ +Matrix.Indirect = 0x00; +Matrix.Direct = 0x80; - Matrix.columnHeaderIndices = (function() { - var out = new Map(), - i = 0; - for ( var type of typeBitOffsets.keys() ) { - out.set(type, i++); - } - return out; - })(); +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; +/******************************************************************************/ - Matrix.switchNames = new Set(switchBitOffsets.keys()); +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 +}; - /******************************************************************************/ +/******************************************************************************/ - // For performance purpose, as simple tests as possible - var reHostnameVeryCoarse = /[g-z_-]/; - var reIPv4VeryCoarse = /\.\d+$/; +Matrix.columnHeaderIndices = (function() { + var out = new Map(), + i = 0; + for ( var type of typeBitOffsets.keys() ) { + out.set(type, i++); + } + return out; +})(); - // 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) === '['; - }; +Matrix.switchNames = new Set(switchBitOffsets.keys()); - /******************************************************************************/ +/******************************************************************************/ - 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); - }; +// For performance purpose, as simple tests as possible +var reHostnameVeryCoarse = /[g-z_-]/; +var reIPv4VeryCoarse = /\.\d+$/; - 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) : '*'; - }; +// 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 - Matrix.toBroaderHostname = toBroaderHostname; +var isIPAddress = function(hostname) { + if ( reHostnameVeryCoarse.test(hostname) ) { + return false; + } + if ( reIPv4VeryCoarse.test(hostname) ) { + return true; + } + return hostname.charAt(0) === '['; +}; - /******************************************************************************/ +/******************************************************************************/ - // 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 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; - var extractFirstPartyDesDomain = function(srcHostname, desHostname) { - if ( srcHostname === '*' || desHostname === '*' || desHostname === '1st-party' ) { - return ''; - } - var ηmuri = UriTools; - var srcDomain = ηmuri.domainFromHostname(srcHostname) || srcHostname; - var desDomain = ηmuri.domainFromHostname(desHostname) || desHostname; - return desDomain === srcDomain ? desDomain : ''; - }; +/******************************************************************************/ - /******************************************************************************/ +// 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. - Matrix.prototype.reset = function() { - this.switches = new Map(); - this.rules = new Map(); - this.rootValue = Matrix.RedIndirect; - this.modifiedTime = 0; - }; +var extractFirstPartyDesDomain = function(srcHostname, desHostname) { + if ( srcHostname === '*' || desHostname === '*' || desHostname === '1st-party' ) { + return ''; + } + var ηmuri = UriTools; + var srcDomain = ηmuri.domainFromHostname(srcHostname) || srcHostname; + var desDomain = ηmuri.domainFromHostname(desHostname) || desHostname; + return desDomain === srcDomain ? desDomain : ''; +}; - /******************************************************************************/ +/******************************************************************************/ - 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.reset = function() { + this.switches = new Map(); + this.rules = new Map(); + this.rootValue = Matrix.RedIndirect; + this.modifiedTime = 0; +}; - /******************************************************************************/ +/******************************************************************************/ - // Copy another matrix to self. Do this incrementally to minimize impact on - // a live matrix. +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.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]); - } - // 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 +// 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); + } + } + // 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 - // 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 ) { - return true; - } - this.setCell(srcHostname, desHostname, type, 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; +}; - /******************************************************************************/ +/******************************************************************************/ - 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); +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; +}; - /******************************************************************************/ +/******************************************************************************/ - 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); +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; +}; - 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); +/******************************************************************************/ - 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; - } +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; } } - // 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; - 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; +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 (;;) { - d = toBroaderHostname(d); - if ( d === '*' ) { break; } + 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 narrower rule + // Do not override a 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 + // 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; } - // 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; } + } + } - // Any-hostname any-type cell - this.specificityRegister = 6; - r = this.evaluateCellZ(srcHostname, '*', '*'); + // 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); if ( r === 1 ) { return Matrix.RedIndirect; } if ( r === 2 ) { return Matrix.GreenIndirect; } - return this.rootValue; - }; - - // https://www.youtube.com/watch?v=4C5ZkwrnVfM + // 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 - /******************************************************************************/ +/******************************************************************************/ - 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 ) { - return true; - } - this.switches.set(srcHostname, bits | ((newState ? 1 : 2) << bitOffset)); +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; +}; - /******************************************************************************/ +/******************************************************************************/ - // 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; + 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; if ( bits !== 0 ) { - bits = bits >> bitOffset & 3; - if ( bits !== 0 ) { - return bits === 1; - } + return bits === 1; } } - return false; - }; + } + return false; +}; - /******************************************************************************/ - - 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)); +/******************************************************************************/ + +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 out.sort().join('\n'); + return cachedResult; }; +})(); + +/******************************************************************************/ - /******************************************************************************/ - - 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); +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); if ( lineEnd < 0 ) { - lineEnd = text.indexOf('\r', lineBeg); - if ( lineEnd < 0 ) { - lineEnd = textEnd; - } + lineEnd = textEnd; } - line = text.slice(lineBeg, lineEnd).trim(); - lineBeg = lineEnd + 1; + } + line = text.slice(lineBeg, lineEnd).trim(); + lineBeg = lineEnd + 1; - pos = line.indexOf('# '); - if ( pos !== -1 ) { - line = line.slice(0, pos).trim(); - } - if ( line === '' ) { - continue; - } + pos = line.indexOf('# '); + if ( pos !== -1 ) { + line = line.slice(0, pos).trim(); + } + if ( line === '' ) { + continue; + } - fields = line.split(/\s+/); + fields = line.split(/\s+/); - // Less than 2 fields makes no sense - if ( fields.length < 2 ) { - continue; - } + // Less than 2 fields makes no sense + if ( fields.length < 2 ) { + continue; + } - fieldVal = fields[0]; + fieldVal = fields[0]; - // Special directives: + // Special directives: - // title - pos = fieldVal.indexOf('title:'); - if ( pos !== -1 ) { - // TODO - continue; - } + // title + pos = fieldVal.indexOf('title:'); + if ( pos !== -1 ) { + // TODO + continue; + } - // Name - pos = fieldVal.indexOf('name:'); - if ( pos !== -1 ) { - // TODO - continue; - } + // Name + pos = fieldVal.indexOf('name:'); + if ( pos !== -1 ) { + // TODO + continue; + } - // Switch on/off + // Switch on/off - // `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); + // `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]); + + // No state field: reject + fieldVal = fields[2]; + if ( fieldVal === null ) { + continue; } - 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]); + // Unknown state: reject + if ( nameToSwitchStateMap.hasOwnProperty(fieldVal) === false ) { continue; } - // Unknown directive - if ( fieldVal.endsWith(':') ) { - continue; + // Backward compatibility: + // `chromium-behind-the-scene` is now `behind-the-scene` + if ( srcHostname === 'chromium-behind-the-scene' ) { + srcHostname = 'behind-the-scene'; } - // Valid rule syntax: + matrix.setSwitch(switchName, srcHostname, nameToSwitchStateMap[fieldVal]); + continue; + } - // srcHostname desHostname [type [state]] - // type = a valid request type - // state = [`block`, `allow`, `inherit`] + // Unknown directive + if ( fieldVal.endsWith(':') ) { + continue; + } - // srcHostname desHostname type - // type = a valid request type - // state = `allow` + // Valid rule syntax: - // srcHostname desHostname - // type = `*` - // state = `allow` + // srcHostname desHostname [type [state]] + // type = a valid request type + // state = [`block`, `allow`, `inherit`] - // Lines with invalid syntax silently ignored + // srcHostname desHostname type + // type = a valid request type + // state = `allow` - srcHostname = Punycode.toASCII(fields[0]); - desHostname = Punycode.toASCII(fields[1]); + // srcHostname desHostname + // type = `*` + // state = `allow` - fieldVal = fields[2]; + // Lines with invalid syntax silently ignored - 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 = '*'; - } + srcHostname = Punycode.toASCII(fields[0]); + desHostname = Punycode.toASCII(fields[1]); - fieldVal = fields[3]; + fieldVal = fields[2]; - if ( fieldVal !== undefined ) { - // Unknown state: reject - if ( nameToStateMap.hasOwnProperty(fieldVal) === false ) { - continue; - } - state = nameToStateMap[fieldVal]; - } else { - state = 2; + if ( fieldVal !== undefined ) { + type = fieldVal; + // https://github.com/gorhill/uMatrix/issues/759 + // Backward compatibility. + if ( type === 'plugin' ) { + type = 'media'; } - - matrix.setCell(srcHostname, desHostname, type, state); + // Unknown type: reject + if ( typeBitOffsets.has(type) === false ) { + continue; + } + } else { + type = '*'; } - if ( !append ) { - this.assign(matrix); + fieldVal = fields[3]; + + if ( fieldVal !== undefined ) { + // Unknown state: reject + if ( nameToStateMap.hasOwnProperty(fieldVal) === false ) { + continue; + } + state = nameToStateMap[fieldVal]; + } else { + state = 2; } - this.modifiedTime = Date.now(); - }; + matrix.setCell(srcHostname, desHostname, type, state); + } - /******************************************************************************/ + if ( !append ) { + this.assign(matrix); + } - Matrix.prototype.toSelfie = function() { - return { - magicId: magicId, - switches: Array.from(this.switches), - rules: Array.from(this.rules) - }; - }; + this.modifiedTime = Date.now(); +}; - /******************************************************************************/ +/******************************************************************************/ - 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.toSelfie = function() { + return { + magicId: magicId, + switches: Array.from(this.switches), + rules: Array.from(this.rules) }; +}; - /******************************************************************************/ +/******************************************************************************/ - 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 - }); - } - } - srcHostname = toBroaderHostname(srcHostname); - if ( srcHostname === '' ) { - break; - } - } - return out; - }; +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.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; +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 + }); } - if ( switchBitOffsets.has(action.what) ) { - val = from.evaluateSwitch(action.what, action.src); - changed = this.setSwitch(action.what, action.src, val) || changed; - continue; + } + 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 + }); } } - return changed; - }; + srcHostname = toBroaderHostname(srcHostname); + if ( srcHostname === '' ) { + break; + } + } + 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; +}; - 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 6dd0979..86ac038 100644 --- a/js/messaging.js +++ b/js/messaging.js @@ -2,7 +2,7 @@ ηMatrix - a browser extension to black/white list requests. Copyright (C) 2014-2019 Raymond Hill - Copyright (C) 2019-2020 Alessio Vanni + Copyright (C) 2019-2022 Alessio Vanni This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see {http://www.gnu.org/licenses/}. - Home: https://notabug.org/heckyel/ematrix + Home: https://gitlab.com/vannilla/ematrix uMatrix Home: https://github.com/gorhill/uMatrix */ @@ -343,7 +343,7 @@ let sz = ηm.tMatrix.evaluateSwitchZ(request.switchName, request.srcHostname); ηm.tMatrix.setSwitchZ(request.switchName, - request.scrHostname, + request.srcHostname, sz === false); return undefined; }); @@ -676,7 +676,7 @@ addMethod('purgeCache', function (request, sender, cb) { ηm.assets.purge(request.assetKey); - ηm.assets.remove('compiled/' + request.assetKey); + ηm.assets.remove('cache/' + request.assetKey); return undefined; }); @@ -722,16 +722,16 @@ }; let onAllRemoved = function () { - vAPI.storage.set(userData.settings, onCountdown); + vAPI.storage.set(data.settings, onCountdown); vAPI.storage.set({ - userMatrix: userData.rules, + userMatrix: data.rules, }, onCountdown); vAPI.storage.set({ - liveHostsFiles: userData.hostsFiles, + liveHostsFiles: data.hostsFiles, }, onCountdown); - if (userData.rawSettings instanceof Object) { - ηMatrix.saveRawSettings(userData.rawSettings, onCountdown); + if (data.rawSettings instanceof Object) { + ηMatrix.saveRawSettings(data.rawSettings, onCountdown); } }; @@ -740,7 +740,7 @@ ηm.XAL.keyvalRemoveAll(onAllRemoved); }; - restoreUserData(request.userData); + restoreData(request.userData); return undefined; }); @@ -750,6 +750,8 @@ }; ηm.XAL.keyvalRemoveAll(onAllRemoved); + /* Let's also clear the database, just to really make it a reset */ + ηm.assets.rmrf(); return undefined; }); @@ -768,7 +770,7 @@ let tabIds = {}; for (let id in ηm.pageStores) { - let store = ηm.pageStoreFromTabId(tabId); + let store = ηm.pageStoreFromTabId(id); if (store === null) { continue; diff --git a/js/pagestats.js b/js/pagestats.js index b4c65c1..fd1757d 100644 --- a/js/pagestats.js +++ b/js/pagestats.js @@ -2,7 +2,7 @@ ηMatrix - a browser extension to black/white list requests. Copyright (C) 2013-2019 Raymond Hill - Copyright (C) 2019-2020 Alessio Vanni + Copyright (C) 2019-2022 Alessio Vanni This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -30,58 +30,58 @@ let ηm = ηMatrix; let BlockedCollapsibles = function () { - this.boundPruneAsyncCallback = this.pruneAsyncCallback.bind(this); - this.blocked = new Map(); - this.hash = 0; - this.timer = null; + this.boundPruneAsyncCallback = this.pruneAsyncCallback.bind(this); + this.blocked = new Map(); + this.hash = 0; + this.timer = null; }; BlockedCollapsibles.prototype = { - shelfLife: 10 * 1000, + shelfLife: 10 * 1000, - add: function (type, url, isSpecific) { + add: function (type, url, isSpecific) { if (this.blocked.size === 0) { - this.pruneAsync(); - } + this.pruneAsync(); + } let 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; + now |= 0x00000001; } else { - now &= 0xFFFFFFFE; + now &= 0xFFFFFFFE; } this.blocked.set(type + ' ' + url, now); this.hash = now; - }, - reset: function () { + }, + reset: function () { this.blocked.clear(); this.hash = 0; if (this.timer !== null) { - clearTimeout(this.timer); - this.timer = null; + clearTimeout(this.timer); + this.timer = null; } - }, - pruneAsync: function () { + }, + pruneAsync: function () { if (this.timer === null) { - this.timer = vAPI.setTimeout(this.boundPruneAsyncCallback, - this.shelfLife * 2); + this.timer = vAPI.setTimeout(this.boundPruneAsyncCallback, + this.shelfLife * 2); } - }, - pruneAsyncCallback: function () { + }, + pruneAsyncCallback: function () { this.timer = null; let obsolete = Date.now() - this.shelfLife; for (let entry of this.blocked) { - if (entry[1] <= obsolete) { + if (entry[1] <= obsolete) { this.blocked.delete(entry[0]); - } + } } if (this.blocked.size !== 0) { - this.pruneAsync(); - } - } + this.pruneAsync(); + } + } }; // Ref: Given a URL, returns a (somewhat) unique 32-bit value @@ -89,19 +89,19 @@ // http://www.isthe.com/chongo/tech/comp/fnv/index.html#FNV-reference-source // The rest is custom, suited for uMatrix. let 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); + 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: [], + collapsibleTypes: new Set([ 'image' ]), + pageStoreJunkyard: [], - init: function (tabContext) { + init: function (tabContext) { this.tabId = tabContext.tabId; this.rawUrl = tabContext.rawURL; this.pageUrl = tabContext.normalURL; @@ -124,8 +124,8 @@ this.mtxContentModifiedTime = 0; this.mtxCountModifiedTime = 0; return this; - }, - dispose: function () { + }, + dispose: function () { this.rawUrl = ''; this.pageUrl = ''; this.pageHostname = ''; @@ -136,72 +136,72 @@ this.allHostnamesString = ' '; this.blockedCollapsibles.reset(); if (this.incinerationTimer !== null) { - clearTimeout(this.incinerationTimer); - this.incinerationTimer = null; + clearTimeout(this.incinerationTimer); + this.incinerationTimer = null; } if (this.pageStoreJunkyard.length < 8) { - this.pageStoreJunkyard.push(this); + this.pageStoreJunkyard.push(this); } - }, - cacheBlockedCollapsible: function (type, url, specificity) { + }, + cacheBlockedCollapsible: function (type, url, specificity) { if (this.collapsibleTypes.has(type)) { - this.blockedCollapsibles.add(type, - url, - specificity !== 0 - && specificity < 5); + this.blockedCollapsibles.add(type, + url, + specificity !== 0 + && specificity < 5); } - }, - lookupBlockedCollapsibles: function (request, response) { + }, + lookupBlockedCollapsibles: function (request, response) { let tabContext = ηm.tabContextManager.lookup(this.tabId); if (tabContext === null) { - return; - } + return; + } let collapseBlacklisted = ηm.userSettings.collapseBlacklisted; - let collapseBlocked = ηm.userSettings.collapseBlocked; + let collapseBlocked = ηm.userSettings.collapseBlocked; let blockedResources = response.blockedResources; if (Array.isArray(request.toFilter) && request.toFilter.length !== 0) { - let roothn = tabContext.rootHostname; + let roothn = tabContext.rootHostname; let hnFromURI = UriTools.hostnameFromURI; let tMatrix = ηm.tMatrix; - for (let entry of request.toFilter) { + for (let entry of request.toFilter) { if (tMatrix.mustBlock(roothn, - hnFromURI(entry.url), - entry.type) === false) { - continue; + hnFromURI(entry.url), + entry.type) === false) { + continue; } blockedResources.push([ - entry.type + ' ' + entry.url, - collapseBlocked - || collapseBlacklisted - && tMatrix.specificityRegister !== 0 - && tMatrix.specificityRegister < 5 + entry.type + ' ' + entry.url, + collapseBlocked + || collapseBlacklisted + && tMatrix.specificityRegister !== 0 + && tMatrix.specificityRegister < 5 ]); - } + } } if (this.blockedCollapsibles.hash === response.hash) { - return; - } + return; + } response.hash = this.blockedCollapsibles.hash; for (let entry of this.blockedCollapsibles.blocked) { - blockedResources.push([ + blockedResources.push([ entry[0], collapseBlocked - || collapseBlacklisted - && (entry[1] & 1) !== 0 - ]); + || collapseBlacklisted + && (entry[1] & 1) !== 0 + ]); } - }, - recordRequest: function (type, url, block) { - if (block !== false) { - this.perLoadBlockedRequestCount++; + }, + recordRequest: function (type, url, block) { + if (block !== false) { + this.perLoadBlockedRequestCount++; } else { - this.perLoadAllowedRequestCount++; + this.perLoadAllowedRequestCount++; } // Store distinct network requests. This is used to: @@ -209,19 +209,19 @@ // - count the number of distinct URLs for any given // hostname-type pair let hostname = UriTools.hostnameFromURI(url); - let key = hostname + ' ' + type; - let uids = this.hostnameTypeCells.get(key); + let key = hostname + ' ' + type; + let uids = this.hostnameTypeCells.get(key); if (uids === undefined) { - this.hostnameTypeCells.set(key, (uids = new Set())); + this.hostnameTypeCells.set(key, (uids = new Set())); } else if (uids.size > 99) { - return; + return; } let uid = this.uidFromURL(url); if (uids.has(uid)) { - return; - } + return; + } uids.add(uid); // Count blocked/allowed requests @@ -236,28 +236,28 @@ this.mtxCountModifiedTime = Date.now(); if (this.domains.has(hostname) === false) { - this.domains.add(hostname); - this.allHostnamesString += hostname + ' '; - this.mtxContentModifiedTime = Date.now(); + this.domains.add(hostname); + this.allHostnamesString += hostname + ' '; + this.mtxContentModifiedTime = Date.now(); } - }, - uidFromURL: function (uri) { + }, + uidFromURL: function (uri) { var hint = 0x811c9dc5; - let i = uri.length; + let i = uri.length; while (i--) { - hint ^= uri.charCodeAt(i) | 0; - hint += (hint<<1) + (hint<<4) + (hint<<7) + (hint<<8) + (hint<<24) | 0; - hint >>>= 0; + hint ^= uri.charCodeAt(i) | 0; + hint += (hint<<1) + (hint<<4) + (hint<<7) + (hint<<8) + (hint<<24) | 0; + hint >>>= 0; } return hint; - } + } }; return function (tabContext) { - let entry = PageStore.prototype.pageStoreJunkyard.pop(); - if (entry) { + let entry = PageStore.prototype.pageStoreJunkyard.pop(); + if (entry) { return entry.init(tabContext); - } - return new PageStore(tabContext); + } + return new PageStore(tabContext); }; })(); diff --git a/js/polyfill.js b/js/polyfill.js index 27f2984..83f2fdb 100644 --- a/js/polyfill.js +++ b/js/polyfill.js @@ -2,7 +2,7 @@ ηMatrix - a browser extension to black/white list requests. Copyright (C) 2017-2019 Raymond Hill - Copyright (C) 2019-2020 Alessio Vanni + Copyright (C) 2019-2022 Alessio Vanni This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see {http://www.gnu.org/licenses/}. - Home: https://libregit.spks.xyz/heckyel/ematrix + Home: https://gitlab.com/vannilla/ematrix uMatrix Home: https://github.com/gorhill/uMatrix This file has been originally imported from: diff --git a/js/popup.js b/js/popup.js index d750997..073f57a 100644 --- a/js/popup.js +++ b/js/popup.js @@ -2,7 +2,7 @@ ηMatrix - a browser extension to black/white list requests. Copyright (C) 2014-2019 Raymond Hill - Copyright (C) 2019-2020 Alessio Vanni + Copyright (C) 2019-2022 Alessio Vanni This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see {http://www.gnu.org/licenses/}. - Home: https://libregit.spks.xyz/heckyel/ematrix + Home: https://gitlab.com/vannilla/ematrix uMatrix Home: https://github.com/gorhill/uMatrix */ @@ -29,67 +29,67 @@ // Stuff which is good to do very early so as to avoid visual glitches. (function () { - let paneContentPaddingTop = - vAPI.localStorage.getItem('paneContentPaddingTop'); + let paneContentPaddingTop = + vAPI.localStorage.getItem('paneContentPaddingTop'); let touchDevice = vAPI.localStorage.getItem('touchDevice'); - if (typeof paneContentPaddingTop === 'string') { + if (typeof paneContentPaddingTop === 'string') { document - .querySelector('.paneContent') - .style - .setProperty('padding-top', - paneContentPaddingTop); - } - - /* This is for CSS */ - if (touchDevice === 'true') { + .querySelector('.paneContent') + .style + .setProperty('padding-top', + paneContentPaddingTop); + } + + /* This is for CSS */ + if (touchDevice === 'true') { document.body.setAttribute('data-touch', 'true'); - } else { + } 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.removeEventListener(ev.type, onTouched); + document.body.setAttribute('data-touch', 'true'); + vAPI.localStorage.setItem('touchDevice', 'true'); + resizePopup(); }); - } + } })(); let popupWasResized = function () { - document.body.setAttribute('data-resize-popup', ''); + document.body.setAttribute('data-resize-popup', ''); }; let resizePopup = (function () { - let timer; + let timer; - let fix = function () { + let fix = function () { timer = undefined; let doc = document; // Manually adjust the position of the main matrix according to the // height of the toolbar/matrix header. let paddingTop = - (doc.querySelector('.paneHead').clientHeight + 2) + 'px'; + (doc.querySelector('.paneHead').clientHeight + 2) + 'px'; let paneContent = doc.querySelector('.paneContent'); if (paddingTop !== paneContent.style.paddingTop) { - paneContent.style.setProperty('padding-top', paddingTop); - vAPI.localStorage.setItem('paneContentPaddingTop', paddingTop); + paneContent.style.setProperty('padding-top', paddingTop); + vAPI.localStorage.setItem('paneContentPaddingTop', paddingTop); } document - .body - .classList - .toggle('hConstrained', - window.innerWidth < document.body.clientWidth); + .body + .classList + .toggle('hConstrained', + window.innerWidth < document.body.clientWidth); popupWasResized(); - }; + }; - return function () { + return function () { if (timer !== undefined) { - clearTimeout(timer); + clearTimeout(timer); } timer = vAPI.setTimeout(fix, 97); - }; + }; })(); // Must be consistent with definitions in matrix.js @@ -106,15 +106,15 @@ let matrixCellHotspots = null; let matrixHeaderPrettyNames = { - 'all': '', - 'cookie': '', - 'css': '', - 'image': '', - 'media': '', - 'script': '', - 'xhr': '', - 'frame': '', - 'other': '' + 'all': '', + 'cookie': '', + 'css': '', + 'image': '', + 'media': '', + 'script': '', + 'xhr': '', + 'frame': '', + 'other': '' }; let firstPartyLabel = ''; @@ -122,30 +122,30 @@ let expandosIdGenerator = 1; let nodeToExpandosMap = (function () { - if (typeof window.Map === 'function') { + if (typeof window.Map === 'function') { return new window.Map(); - } + } })(); let expandosFromNode = function (node) { - if (node instanceof HTMLElement === false - && typeof node.nodeAt === 'function') { + if (node instanceof HTMLElement === false + && typeof node.nodeAt === 'function') { node = node.nodeAt(0); - } - if (nodeToExpandosMap) { + } + if (nodeToExpandosMap) { let expandosId = node.getAttribute('data-expandos'); if (!expandosId) { - expandosId = '' + (expandosIdGenerator++); - node.setAttribute('data-expandos', expandosId); + expandosId = '' + (expandosIdGenerator++); + node.setAttribute('data-expandos', expandosId); } let expandos = nodeToExpandosMap.get(expandosId); if (expandos === undefined) { - expandos = Object.create(null); - nodeToExpandosMap.set(expandosId, expandos); + expandos = Object.create(null); + nodeToExpandosMap.set(expandosId, expandos); } return expandos; - } - return node; + } + return node; }; function getUserSetting(setting) { @@ -153,28 +153,28 @@ } function setUserSetting(setting, value) { - matrixSnapshot.userSettings[setting] = value; - vAPI.messaging.send('popup.js', { + matrixSnapshot.userSettings[setting] = value; + vAPI.messaging.send('popup.js', { what: 'userSettings', name: setting, value: value - }); + }); } function getUISetting(setting) { - let r = vAPI.localStorage.getItem(setting); - if (typeof r !== 'string') { + let 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)); + vAPI.localStorage.setItem(setting, JSON.stringify(value)); } function updateMatrixSnapshot() { - matrixSnapshotPoller.pollNow(); + matrixSnapshotPoller.pollNow(); } // For display purpose, create four distinct groups of rows: @@ -186,162 +186,162 @@ function getGroupStats() { - // Try to not reshuffle groups around while popup is opened if - // no new hostname added. - let latestDomainListSnapshot = - Object.keys(matrixSnapshot.rows).sort().join(); - if (latestDomainListSnapshot === allHostnamesSnapshot) { + // Try to not reshuffle groups around while popup is opened if + // no new hostname added. + let 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 - let pageDomain = matrixSnapshot.domain; - let rows = matrixSnapshot.rows; - let anyTypeOffset = matrixSnapshot.headerIndices.get('*'); - let hostname, domain; - let row, color, count; - let 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) { + } + allHostnamesSnapshot = latestDomainListSnapshot; + + // First, group according to whether at least one node in the domain + // hierarchy is white or blacklisted + let pageDomain = matrixSnapshot.domain; + let rows = matrixSnapshot.rows; + let anyTypeOffset = matrixSnapshot.headerIndices.get('*'); + let hostname, domain; + let row, color, count; + let 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; + continue; } if (hostname === '*' || hostname === '1st-party') { - continue; + continue; } domain = rows[hostname].domain; if (domain === pageDomain || hostname !== domain) { - continue; + continue; } row = rows[domain]; color = row.temporary[anyTypeOffset]; if (color === DarkGreen) { - domainToGroupMap[domain] = 2; - continue; + domainToGroupMap[domain] = 2; + continue; } if (color === DarkRed) { - domainToGroupMap[domain] = 4; - continue; + domainToGroupMap[domain] = 4; + continue; } count = row.counts[anyTypeOffset]; if (count !== 0) { - domainToGroupMap[domain] = 3; - continue; + domainToGroupMap[domain] = 3; + continue; } - } + } - // 2nd pass: green wins - for (hostname in rows) { + // 2nd pass: green wins + for (hostname in rows) { if (rows.hasOwnProperty(hostname) === false) { - continue; + continue; } row = rows[hostname]; domain = row.domain; if (domainToGroupMap.hasOwnProperty(domain)) { - continue; + continue; } color = row.temporary[anyTypeOffset]; if (color === DarkGreen) { - domainToGroupMap[domain] = 2; + domainToGroupMap[domain] = 2; } - } + } - // 3rd pass: gray with count wins - for (hostname in rows) { + // 3rd pass: gray with count wins + for (hostname in rows) { if (rows.hasOwnProperty(hostname) === false) { - continue; + continue; } row = rows[hostname]; domain = row.domain; if (domainToGroupMap.hasOwnProperty(domain)) { - continue; + continue; } color = row.temporary[anyTypeOffset]; count = row.counts[anyTypeOffset]; if ( color !== DarkRed && count !== 0 ) { - domainToGroupMap[domain] = 3; + domainToGroupMap[domain] = 3; } - } + } - // 4th pass: red wins whatever is left - for (hostname in rows) { + // 4th pass: red wins whatever is left + for (hostname in rows) { if (rows.hasOwnProperty(hostname) === false) { - continue; + continue; } row = rows[hostname]; domain = row.domain; if (domainToGroupMap.hasOwnProperty(domain)) { - continue; + continue; } color = row.temporary[anyTypeOffset]; if (color === DarkRed) { - domainToGroupMap[domain] = 4; + domainToGroupMap[domain] = 4; } - } + } - // 5th pass: gray wins whatever is left - for (hostname in rows) { + // 5th pass: gray wins whatever is left + for (hostname in rows) { if (rows.hasOwnProperty(hostname) === false) { - continue; + continue; } domain = rows[hostname].domain; if (domainToGroupMap.hasOwnProperty(domain)) { - continue; + continue; } domainToGroupMap[domain] = 3; - } + } - // Last pass: put each domain in a group - let groups = [ - {}, {}, {}, {}, {} - ]; + // Last pass: put each domain in a group + let groups = [ + {}, {}, {}, {}, {} + ]; - for (hostname in rows) { + for (hostname in rows) { if (rows.hasOwnProperty(hostname) === false) { - continue; + continue; } if ( hostname === '*' ) { - continue; + continue; } domain = rows[hostname].domain; let groupIndex = domainToGroupMap[domain]; let group = groups[groupIndex]; if (group.hasOwnProperty(domain) === false) { - group[domain] = {}; + group[domain] = {}; } group[domain][hostname] = true; - } + } - groupsSnapshot = groups; + groupsSnapshot = groups; - return groups; + return groups; } // helpers function getTemporaryColor(hostname, type) { - return matrixSnapshot - .rows[hostname] - .temporary[matrixSnapshot.headerIndices.get(type)]; + return matrixSnapshot + .rows[hostname] + .temporary[matrixSnapshot.headerIndices.get(type)]; } function getPermanentColor(hostname, type) { - return matrixSnapshot - .rows[hostname] - .permanent[matrixSnapshot.headerIndices.get(type)]; + return matrixSnapshot + .rows[hostname] + .permanent[matrixSnapshot.headerIndices.get(type)]; } function addCellClass(cell, hostname, type) { - let cl = cell.classList; - cl.add('matCell'); - cl.add('t' + getTemporaryColor(hostname, type).toString(16)); - cl.add('p' + getPermanentColor(hostname, type).toString(16)); + let cl = cell.classList; + cl.add('matCell'); + cl.add('t' + getTemporaryColor(hostname, type).toString(16)); + cl.add('p' + getPermanentColor(hostname, type).toString(16)); } // This is required for when we update the matrix while it is open: @@ -349,118 +349,118 @@ // want to lose all his hardwork. function getCollapseState(domain) { - let states = getUISetting('popupCollapseSpecificDomains'); - if (typeof states === 'object' && states[domain] !== undefined) { + let states = getUISetting('popupCollapseSpecificDomains'); + if (typeof states === 'object' && states[domain] !== undefined) { return states[domain]; - } - return matrixSnapshot.collapseAllDomains === true; + } + return matrixSnapshot.collapseAllDomains === true; } function toggleCollapseState(elem) { - if (elem.ancestors('#matHead.collapsible').length > 0) { + if (elem.ancestors('#matHead.collapsible').length > 0) { toggleMainCollapseState(elem); - } else { + } else { toggleSpecificCollapseState(elem); - } - popupWasResized(); + } + popupWasResized(); } function toggleMainCollapseState(uelem) { - let matHead = - uelem.ancestors('#matHead.collapsible').toggleClass('collapsed'); - let collapsed = - matrixSnapshot.collapseAllDomains = matHead.hasClass('collapsed'); - - uDom('#matList .matSection.collapsible') - .toggleClass('collapsed', collapsed); - setUserSetting('popupCollapseAllDomains', collapsed); - - let specificCollapseStates = - getUISetting('popupCollapseSpecificDomains') || {}; - let domains = Object.keys(specificCollapseStates); - for (let i=domains.length-1; i>=0; --i) { + let matHead = + uelem.ancestors('#matHead.collapsible').toggleClass('collapsed'); + let collapsed = + matrixSnapshot.collapseAllDomains = matHead.hasClass('collapsed'); + + uDom('#matList .matSection.collapsible') + .toggleClass('collapsed', collapsed); + setUserSetting('popupCollapseAllDomains', collapsed); + + let specificCollapseStates = + getUISetting('popupCollapseSpecificDomains') || {}; + let domains = Object.keys(specificCollapseStates); + for (let i=domains.length-1; i>=0; --i) { let domain = domains[i]; if (specificCollapseStates[domain] === collapsed) { - delete specificCollapseStates[domain]; + delete specificCollapseStates[domain]; } - } - setUISetting('popupCollapseSpecificDomains', specificCollapseStates); + } + setUISetting('popupCollapseSpecificDomains', specificCollapseStates); } function toggleSpecificCollapseState(uelem) { - // Remember collapse state forever, but only if it is different - // from main collapse switch. - let section = - uelem.ancestors('.matSection.collapsible').toggleClass('collapsed'); + // Remember collapse state forever, but only if it is different + // from main collapse switch. + let section = + uelem.ancestors('.matSection.collapsible').toggleClass('collapsed'); let domain = expandosFromNode(section).domain; let collapsed = section.hasClass('collapsed'); let mainCollapseState = matrixSnapshot.collapseAllDomains === true; let specificCollapseStates = - getUISetting('popupCollapseSpecificDomains') || {}; + getUISetting('popupCollapseSpecificDomains') || {}; - if (collapsed !== mainCollapseState) { + if (collapsed !== mainCollapseState) { specificCollapseStates[domain] = collapsed; setUISetting('popupCollapseSpecificDomains', - specificCollapseStates); - } else if (specificCollapseStates[domain] !== undefined) { + specificCollapseStates); + } else if (specificCollapseStates[domain] !== undefined) { delete specificCollapseStates[domain]; setUISetting('popupCollapseSpecificDomains', - specificCollapseStates); - } + specificCollapseStates); + } } // Update count value of matrix cells(s) function updateMatrixCounts() { - let matCells = uDom('.matrix .matRow.rw > .matCell'); + let matCells = uDom('.matrix .matRow.rw > .matCell'); let matRow, matCell, count, counts; let headerIndices = matrixSnapshot.headerIndices; let rows = matrixSnapshot.rows; let expandos; - for (let i=matCells.length-1; i>=0; --i) { + for (let i=matCells.length-1; i>=0; --i) { matCell = matCells.nodeAt(i); expandos = expandosFromNode(matCell); if (expandos.hostname === '*' || expandos.reqType === '*') { - continue; + continue; } matRow = matCell.parentNode; counts = matRow.classList.contains('meta') ? 'totals' : 'counts'; count = rows[expandos.hostname][counts][headerIndices - .get(expandos.reqType)]; + .get(expandos.reqType)]; if (count === expandos.count) { - continue; - } + continue; + } expandos.count = count; matCell.textContent = cellTextFromCount(count); - } + } } function cellTextFromCount(count) { - if (count === 0) { - return '\u00A0'; - } + if (count === 0) { + return '\u00A0'; + } - if (count < 100) { - return count; - } + if (count < 100) { + return count; + } - return '99+'; + return '99+'; } // Update color of matrix cells(s) // Color changes when rules change function updateMatrixColors() { - let cells = uDom('.matrix .matRow.rw > .matCell').removeClass(); + let cells = uDom('.matrix .matRow.rw > .matCell').removeClass(); let cell, expandos; - for (let i=cells.length-1; i>=0; --i) { + for (let i=cells.length-1; i>=0; --i) { cell = cells.nodeAt(i); expandos = expandosFromNode(cell); addCellClass(cell, expandos.hostname, expandos.reqType); - } + } - popupWasResized(); + popupWasResized(); } // Update behavior of matrix: @@ -470,78 +470,78 @@ // - It is not part of group 3 (blacklisted hostnames) function updateMatrixBehavior() { - matrixList = matrixList || uDom('#matList'); - let sections = matrixList.descendants('.matSection'); - let section, subdomainRows, subdomainRow; - for (let i=sections.length-1; i>=0; --i) { + matrixList = matrixList || uDom('#matList'); + let sections = matrixList.descendants('.matSection'); + let section, subdomainRows, subdomainRow; + for (let i=sections.length-1; i>=0; --i) { section = sections.at(i); subdomainRows = section.descendants('.l2:not(.g4)'); for (let j=subdomainRows.length-1; j>=0; --j) { - subdomainRow = subdomainRows.at(j); - subdomainRow.toggleClass('collapsible', - subdomainRow - .descendants('.t81,.t82') - .length === 0); + subdomainRow = subdomainRows.at(j); + subdomainRow.toggleClass('collapsible', + subdomainRow + .descendants('.t81,.t82') + .length === 0); } section.toggleClass('collapsible', - subdomainRows.filter('.collapsible').length > 0); - } + subdomainRows.filter('.collapsible').length > 0); + } } // handle user interaction with filters function getCellAction(hostname, type, leaning) { - let temporaryColor = getTemporaryColor(hostname, type); - let hue = temporaryColor & 0x03; + let temporaryColor = getTemporaryColor(hostname, type); + let hue = temporaryColor & 0x03; - // Special case: root toggle only between two states - if (type === '*' && hostname === '*') { + // Special case: root toggle only between two states + if (type === '*' && hostname === '*') { return hue === Green ? - 'blacklistMatrixCell' : - 'whitelistMatrixCell'; - } + 'blacklistMatrixCell' : + 'whitelistMatrixCell'; + } - // When explicitly blocked/allowed, can only graylist - let saturation = temporaryColor & 0x80; - if (saturation === Dark) { + // When explicitly blocked/allowed, can only graylist + let saturation = temporaryColor & 0x80; + if (saturation === Dark) { return 'graylistMatrixCell'; - } + } - return leaning === 'whitelisting' ? - 'whitelistMatrixCell' : - 'blacklistMatrixCell'; + return leaning === 'whitelisting' ? + 'whitelistMatrixCell' : + 'blacklistMatrixCell'; } function handleFilter(button, leaning) { - // our parent cell knows who we are - let cell = button.ancestors('div.matCell'); + // our parent cell knows who we are + let cell = button.ancestors('div.matCell'); let expandos = expandosFromNode(cell); let type = expandos.reqType; let desHostname = expandos.hostname; - // https://github.com/gorhill/uMatrix/issues/24 - // No hostname can happen -- like with blacklist meta row - if (desHostname === '') { + // https://github.com/gorhill/uMatrix/issues/24 + // No hostname can happen -- like with blacklist meta row + if (desHostname === '') { return; - } + } - let request = { + let request = { what: getCellAction(desHostname, type, leaning), srcHostname: matrixSnapshot.scope, desHostname: desHostname, type: type - }; + }; - vAPI.messaging.send('popup.js', request, updateMatrixSnapshot); + vAPI.messaging.send('popup.js', request, updateMatrixSnapshot); } function handleWhitelistFilter(button) { - handleFilter(button, 'whitelisting'); + handleFilter(button, 'whitelisting'); } function handleBlacklistFilter(button) { - handleFilter(button, 'blacklisting'); + handleFilter(button, 'blacklisting'); } let matrixRowPool = []; @@ -551,299 +551,299 @@ let matrixList = null; let startMatrixUpdate = function () { - matrixList = matrixList || uDom('#matList'); - matrixList.detach(); - let rows = matrixList.descendants('.matRow'); - rows.detach(); - matrixRowPool = matrixRowPool.concat(rows.toArray()); - let sections = matrixList.descendants('.matSection'); - sections.detach(); - matrixSectionPool = matrixSectionPool.concat(sections.toArray()); - let groups = matrixList.descendants('.matGroup'); - groups.detach(); - matrixGroupPool = matrixGroupPool.concat(groups.toArray()); + matrixList = matrixList || uDom('#matList'); + matrixList.detach(); + let rows = matrixList.descendants('.matRow'); + rows.detach(); + matrixRowPool = matrixRowPool.concat(rows.toArray()); + let sections = matrixList.descendants('.matSection'); + sections.detach(); + matrixSectionPool = matrixSectionPool.concat(sections.toArray()); + let groups = matrixList.descendants('.matGroup'); + groups.detach(); + matrixGroupPool = matrixGroupPool.concat(groups.toArray()); }; let 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) { + // 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'); + updateMatrixBehavior(); + matrixList.css('display', ''); + matrixList.appendTo('.paneContent'); }; let createMatrixGroup = function () { - let group = matrixGroupPool.pop(); - if (group) { + let group = matrixGroupPool.pop(); + if (group) { return uDom(group).removeClass().addClass('matGroup'); - } - return uDom(document.createElement('div')).addClass('matGroup'); + } + return uDom(document.createElement('div')).addClass('matGroup'); }; let createMatrixSection = function () { - let section = matrixSectionPool.pop(); - if (section) { + let section = matrixSectionPool.pop(); + if (section) { return uDom(section).removeClass().addClass('matSection'); - } - return uDom(document.createElement('div')).addClass('matSection'); + } + return uDom(document.createElement('div')).addClass('matSection'); }; let createMatrixRow = function () { - let row = matrixRowPool.pop(); - if (row) { + let 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) { + if (matrixRowTemplate === null) { matrixRowTemplate = uDom('#templates .matRow'); - } + } - return matrixRowTemplate.clone(); + return matrixRowTemplate.clone(); }; function renderMatrixHeaderRow() { - let matHead = uDom('#matHead.collapsible'); - matHead.toggleClass('collapsed', - matrixSnapshot.collapseAllDomains === true); - - let cells = matHead.descendants('.matCell') - let cell = cells.nodeAt(0); - let 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', ''); + let matHead = uDom('#matHead.collapsible'); + matHead.toggleClass('collapsed', + matrixSnapshot.collapseAllDomains === true); + + let cells = matHead.descendants('.matCell') + let cell = cells.nodeAt(0); + let 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 renderMatrixCellDomain(cell, domain) { - let expandos = expandosFromNode(cell); - expandos.hostname = domain; - expandos.reqType = '*'; - addCellClass(cell.nodeAt(0), domain, '*'); + let expandos = expandosFromNode(cell); + expandos.hostname = domain; + expandos.reqType = '*'; + addCellClass(cell.nodeAt(0), domain, '*'); - let contents = cell.contents(); - contents.nodeAt(0).textContent = domain === '1st-party' ? + let contents = cell.contents(); + contents.nodeAt(0).textContent = domain === '1st-party' ? firstPartyLabel : Punycode.toUnicode(domain); - contents.nodeAt(1).textContent = ' '; + contents.nodeAt(1).textContent = ' '; } function renderMatrixCellSubdomain(cell, domain, subomain) { - let expandos = expandosFromNode(cell); - expandos.hostname = subomain; - expandos.reqType = '*'; - addCellClass(cell.nodeAt(0), subomain, '*'); + let expandos = expandosFromNode(cell); + expandos.hostname = subomain; + expandos.reqType = '*'; + addCellClass(cell.nodeAt(0), subomain, '*'); - let contents = cell.contents(); - contents.nodeAt(0).textContent = - Punycode.toUnicode(subomain.slice(0, - subomain.lastIndexOf(domain)-1)) - + '.'; - contents.nodeAt(1).textContent = Punycode.toUnicode(domain); + let 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) { - let expandos = expandosFromNode(cell); - expandos.hostname = domain; - expandos.reqType = '*'; - addCellClass(cell.nodeAt(0), domain, '*'); + let expandos = expandosFromNode(cell); + expandos.hostname = domain; + expandos.reqType = '*'; + addCellClass(cell.nodeAt(0), domain, '*'); - let contents = cell.contents(); - contents.nodeAt(0).textContent = '\u2217.' + Punycode.toUnicode(domain); - contents.nodeAt(1).textContent = ' '; + let contents = cell.contents(); + contents.nodeAt(0).textContent = '\u2217.' + Punycode.toUnicode(domain); + contents.nodeAt(1).textContent = ' '; } function renderMatrixCellType(cell, hostname, type, count) { - let node = cell.nodeAt(0); + let node = cell.nodeAt(0); let expandos = expandosFromNode(node); - expandos.hostname = hostname; - expandos.reqType = type; - expandos.count = count; - addCellClass(node, hostname, type); - node.textContent = cellTextFromCount(count); + expandos.hostname = hostname; + expandos.reqType = type; + expandos.count = count; + addCellClass(node, hostname, type); + node.textContent = cellTextFromCount(count); } function renderMatrixCellTypes(cells, hostname, countName) { - let counts = matrixSnapshot.rows[hostname][countName]; - let 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')]); + let counts = matrixSnapshot.rows[hostname][countName]; + let 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 makeMatrixRowDomain(domain) { - let matrixRow = createMatrixRow().addClass('rw'); - let cells = matrixRow.descendants('.matCell'); - renderMatrixCellDomain(cells.at(0), domain); - renderMatrixCellTypes(cells, domain, 'counts'); - return matrixRow; + let matrixRow = createMatrixRow().addClass('rw'); + let cells = matrixRow.descendants('.matCell'); + renderMatrixCellDomain(cells.at(0), domain); + renderMatrixCellTypes(cells, domain, 'counts'); + return matrixRow; } function makeMatrixRowSubdomain(domain, subdomain) { - let matrixRow = createMatrixRow().addClass('rw'); - let cells = matrixRow.descendants('.matCell'); - renderMatrixCellSubdomain(cells.at(0), domain, subdomain); - renderMatrixCellTypes(cells, subdomain, 'counts'); - return matrixRow; + let matrixRow = createMatrixRow().addClass('rw'); + let cells = matrixRow.descendants('.matCell'); + renderMatrixCellSubdomain(cells.at(0), domain, subdomain); + renderMatrixCellTypes(cells, subdomain, 'counts'); + return matrixRow; } function makeMatrixMetaRowDomain(domain) { - let matrixRow = createMatrixRow().addClass('rw'); - let cells = matrixRow.descendants('.matCell'); - renderMatrixMetaCellDomain(cells.at(0), domain); - renderMatrixCellTypes(cells, domain, 'totals'); - return matrixRow; + let matrixRow = createMatrixRow().addClass('rw'); + let cells = matrixRow.descendants('.matCell'); + renderMatrixMetaCellDomain(cells.at(0), domain); + renderMatrixCellTypes(cells, domain, 'totals'); + return matrixRow; } function renderMatrixMetaCellType(cell, count) { - // https://github.com/gorhill/uMatrix/issues/24 - // Don't forget to reset cell properties - let node = cell.nodeAt(0); + // https://github.com/gorhill/uMatrix/issues/24 + // Don't forget to reset cell properties + let node = cell.nodeAt(0); let expandos = expandosFromNode(node); - expandos.hostname = ''; - expandos.reqType = ''; - expandos.count = count; - cell.addClass('t1'); - node.textContent = cellTextFromCount(count); + expandos.hostname = ''; + expandos.reqType = ''; + expandos.count = count; + cell.addClass('t1'); + node.textContent = cellTextFromCount(count); } function makeMatrixMetaRow(totals) { - let headerIndices = matrixSnapshot.headerIndices; + let headerIndices = matrixSnapshot.headerIndices; let matrixRow = createMatrixRow().at(0).addClass('ro'); let cells = matrixRow.descendants('.matCell'); let contents = cells.at(0).addClass('t81').contents(); let 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; + 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 computeMatrixGroupMetaStats(group) { - let headerIndices = matrixSnapshot.headerIndices; + let headerIndices = matrixSnapshot.headerIndices; let anyTypeIndex = headerIndices.get('*'); let totals = new Array(headerIndices.size); for (let i=headerIndices.size-1; i>=0; --i) { totals[i] = 0; - } + } - let rows = matrixSnapshot.rows; - let row; - for (let hostname in rows) { + let rows = matrixSnapshot.rows; + let row; + for (let hostname in rows) { if (rows.hasOwnProperty(hostname) === false) { - continue; + continue; } row = rows[hostname]; if (group.hasOwnProperty(row.domain) === false) { - continue; + continue; } if (row.counts[anyTypeIndex] === 0) { - continue; + continue; } totals[0] += 1; for (let i=1; i<headerIndices.size; ++i) { - totals[i] += row.counts[i]; + totals[i] += row.counts[i]; } - } + } - return totals; + return totals; } // Compare hostname helper, to order hostname in a logical manner: @@ -851,667 +851,667 @@ // named hostname function hostnameCompare(a, b) { - // Normalize: most significant parts first - if (!a.match(/^\d+(\.\d+){1,3}$/)) { + // Normalize: most significant parts first + if (!a.match(/^\d+(\.\d+){1,3}$/)) { let aa = a.split('.'); a = aa.slice(-2).concat(aa.slice(0,-2).reverse()).join('.'); - } + } - if (!b.match(/^\d+(\.\d+){1,3}$/)) { + if (!b.match(/^\d+(\.\d+){1,3}$/)) { let bb = b.split('.'); b = bb.slice(-2).concat(bb.slice(0,-2).reverse()).join('.'); - } + } - return a.localeCompare(b); + return a.localeCompare(b); } function makeMatrixGroup0SectionDomain() { - return makeMatrixRowDomain('1st-party').addClass('g0 l1'); + return makeMatrixRowDomain('1st-party').addClass('g0 l1'); } function makeMatrixGroup0Section() { - let domainDiv = createMatrixSection(); - expandosFromNode(domainDiv).domain = '1st-party'; - makeMatrixGroup0SectionDomain().appendTo(domainDiv); - return domainDiv; + let 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) { + // Show literal "1st-party" row only if there is + // at least one 1st-party hostname + if (Object.keys(groupsSnapshot[1]).length === 0) { return; - } - let groupDiv = createMatrixGroup().addClass('g0'); - makeMatrixGroup0Section().appendTo(groupDiv); - groupDiv.appendTo(matrixList); + } + let groupDiv = createMatrixGroup().addClass('g0'); + makeMatrixGroup0Section().appendTo(groupDiv); + groupDiv.appendTo(matrixList); } function makeMatrixGroup1SectionDomain(domain) { - return makeMatrixRowDomain(domain).addClass('g1 l1'); + return makeMatrixRowDomain(domain).addClass('g1 l1'); } function makeMatrixGroup1SectionSubomain(domain, subdomain) { - return makeMatrixRowSubdomain(domain, subdomain).addClass('g1 l2'); + return makeMatrixRowSubdomain(domain, subdomain).addClass('g1 l2'); } function makeMatrixGroup1SectionMetaDomain(domain) { - return makeMatrixMetaRowDomain(domain).addClass('g1 l1 meta'); + return makeMatrixMetaRowDomain(domain).addClass('g1 l1 meta'); } function makeMatrixGroup1Section(hostnames) { - let domain = hostnames[0]; - let domainDiv = - createMatrixSection().toggleClass('collapsed', - getCollapseState(domain)); - expandosFromNode(domainDiv).domain = domain; - if (hostnames.length > 1) { + let domain = hostnames[0]; + let domainDiv = + createMatrixSection().toggleClass('collapsed', + getCollapseState(domain)); + expandosFromNode(domainDiv).domain = domain; + if (hostnames.length > 1) { makeMatrixGroup1SectionMetaDomain(domain).appendTo(domainDiv); - } - makeMatrixGroup1SectionDomain(domain).appendTo(domainDiv); - for (let i=1; i<hostnames.length; ++i) { + } + makeMatrixGroup1SectionDomain(domain).appendTo(domainDiv); + for (let i=1; i<hostnames.length; ++i) { makeMatrixGroup1SectionSubomain(domain, hostnames[i]) - .appendTo(domainDiv); - } - return domainDiv; + .appendTo(domainDiv); + } + return domainDiv; } function makeMatrixGroup1(group) { - let domains = Object.keys(group).sort(hostnameCompare); - if (domains.length) { + let domains = Object.keys(group).sort(hostnameCompare); + if (domains.length) { let groupDiv = createMatrixGroup().addClass('g1'); makeMatrixGroup1Section(Object.keys(group[domains[0]]) - .sort(hostnameCompare)) - .appendTo(groupDiv); + .sort(hostnameCompare)) + .appendTo(groupDiv); for (let i=1; i<domains.length; ++i) { - makeMatrixGroup1Section(Object.keys(group[domains[i]]) - .sort(hostnameCompare)) + makeMatrixGroup1Section(Object.keys(group[domains[i]]) + .sort(hostnameCompare)) .appendTo(groupDiv); } groupDiv.appendTo(matrixList); - } + } } function makeMatrixGroup2SectionDomain(domain) { - return makeMatrixRowDomain(domain).addClass('g2 l1'); + return makeMatrixRowDomain(domain).addClass('g2 l1'); } function makeMatrixGroup2SectionSubomain(domain, subdomain) { - return makeMatrixRowSubdomain(domain, subdomain).addClass('g2 l2'); + return makeMatrixRowSubdomain(domain, subdomain).addClass('g2 l2'); } function makeMatrixGroup2SectionMetaDomain(domain) { - return makeMatrixMetaRowDomain(domain).addClass('g2 l1 meta'); + return makeMatrixMetaRowDomain(domain).addClass('g2 l1 meta'); } function makeMatrixGroup2Section(hostnames) { - let domain = hostnames[0]; - let domainDiv = - createMatrixSection().toggleClass('collapsed', - getCollapseState(domain)); - expandosFromNode(domainDiv).domain = domain; - if (hostnames.length > 1) { + let domain = hostnames[0]; + let domainDiv = + createMatrixSection().toggleClass('collapsed', + getCollapseState(domain)); + expandosFromNode(domainDiv).domain = domain; + if (hostnames.length > 1) { makeMatrixGroup2SectionMetaDomain(domain).appendTo(domainDiv); - } - makeMatrixGroup2SectionDomain(domain).appendTo(domainDiv); - for (let i=1; i<hostnames.length; ++i) { + } + makeMatrixGroup2SectionDomain(domain).appendTo(domainDiv); + for (let i=1; i<hostnames.length; ++i) { makeMatrixGroup2SectionSubomain(domain, hostnames[i]) - .appendTo(domainDiv); - } - return domainDiv; + .appendTo(domainDiv); + } + return domainDiv; } function makeMatrixGroup2(group) { - let domains = Object.keys(group).sort(hostnameCompare); - if (domains.length) { + let domains = Object.keys(group).sort(hostnameCompare); + if (domains.length) { let groupDiv = createMatrixGroup().addClass('g2'); makeMatrixGroup2Section(Object.keys(group[domains[0]]) - .sort(hostnameCompare)) - .appendTo(groupDiv); + .sort(hostnameCompare)) + .appendTo(groupDiv); for (let i=1; i<domains.length; ++i) { - makeMatrixGroup2Section(Object.keys(group[domains[i]]) - .sort(hostnameCompare)) + makeMatrixGroup2Section(Object.keys(group[domains[i]]) + .sort(hostnameCompare)) .appendTo(groupDiv); } groupDiv.appendTo(matrixList); - } + } } function makeMatrixGroup3SectionDomain(domain) { - return makeMatrixRowDomain(domain).addClass('g3 l1'); + return makeMatrixRowDomain(domain).addClass('g3 l1'); } function makeMatrixGroup3SectionSubomain(domain, subdomain) { - return makeMatrixRowSubdomain(domain, subdomain).addClass('g3 l2'); + return makeMatrixRowSubdomain(domain, subdomain).addClass('g3 l2'); } function makeMatrixGroup3SectionMetaDomain(domain) { - return makeMatrixMetaRowDomain(domain).addClass('g3 l1 meta'); + return makeMatrixMetaRowDomain(domain).addClass('g3 l1 meta'); } function makeMatrixGroup3Section(hostnames) { - let domain = hostnames[0]; - let domainDiv = createMatrixSection().toggleClass('collapsed', - getCollapseState(domain)); - expandosFromNode(domainDiv).domain = domain; - if (hostnames.length > 1) { + let domain = hostnames[0]; + let domainDiv = createMatrixSection().toggleClass('collapsed', + getCollapseState(domain)); + expandosFromNode(domainDiv).domain = domain; + if (hostnames.length > 1) { makeMatrixGroup3SectionMetaDomain(domain).appendTo(domainDiv); - } - makeMatrixGroup3SectionDomain(domain).appendTo(domainDiv); - for (let i=1; i<hostnames.length; ++i) { + } + makeMatrixGroup3SectionDomain(domain).appendTo(domainDiv); + for (let i=1; i<hostnames.length; ++i) { makeMatrixGroup3SectionSubomain(domain, hostnames[i]) - .appendTo(domainDiv); - } - return domainDiv; + .appendTo(domainDiv); + } + return domainDiv; } function makeMatrixGroup3(group) { - let domains = Object.keys(group).sort(hostnameCompare); - if (domains.length) { + let domains = Object.keys(group).sort(hostnameCompare); + if (domains.length) { let groupDiv = createMatrixGroup().addClass('g3'); makeMatrixGroup3Section(Object.keys(group[domains[0]]) - .sort(hostnameCompare)) - .appendTo(groupDiv); + .sort(hostnameCompare)) + .appendTo(groupDiv); for (let i=1; i<domains.length; ++i) { - makeMatrixGroup3Section(Object.keys(group[domains[i]]) - .sort(hostnameCompare)) + makeMatrixGroup3Section(Object.keys(group[domains[i]]) + .sort(hostnameCompare)) .appendTo(groupDiv); } groupDiv.appendTo(matrixList); - } + } } function makeMatrixGroup4SectionDomain(domain) { - return makeMatrixRowDomain(domain).addClass('g4 l1'); + return makeMatrixRowDomain(domain).addClass('g4 l1'); } function makeMatrixGroup4SectionSubomain(domain, subdomain) { - return makeMatrixRowSubdomain(domain, subdomain).addClass('g4 l2'); + return makeMatrixRowSubdomain(domain, subdomain).addClass('g4 l2'); } function makeMatrixGroup4Section(hostnames) { - let domain = hostnames[0]; - let domainDiv = createMatrixSection(); - expandosFromNode(domainDiv).domain = domain; - makeMatrixGroup4SectionDomain(domain).appendTo(domainDiv); - for (let i=1; i<hostnames.length; ++i) { + let domain = hostnames[0]; + let domainDiv = createMatrixSection(); + expandosFromNode(domainDiv).domain = domain; + makeMatrixGroup4SectionDomain(domain).appendTo(domainDiv); + for (let i=1; i<hostnames.length; ++i) { makeMatrixGroup4SectionSubomain(domain, hostnames[i]) - .appendTo(domainDiv); - } - return domainDiv; + .appendTo(domainDiv); + } + return domainDiv; } function makeMatrixGroup4(group) { - let domains = Object.keys(group).sort(hostnameCompare); - if (domains.length === 0) { + let domains = Object.keys(group).sort(hostnameCompare); + if (domains.length === 0) { return; - } - let groupDiv = createMatrixGroup().addClass('g4'); - createMatrixSection() + } + let groupDiv = createMatrixGroup().addClass('g4'); + createMatrixSection() .addClass('g4Meta') .toggleClass('g4Collapsed', - !!matrixSnapshot.collapseBlacklistedDomains) + !!matrixSnapshot.collapseBlacklistedDomains) .appendTo(groupDiv); - makeMatrixMetaRow(computeMatrixGroupMetaStats(group), 'g4') + makeMatrixMetaRow(computeMatrixGroupMetaStats(group), 'g4') .appendTo(groupDiv); - makeMatrixGroup4Section(Object.keys(group[domains[0]]) - .sort(hostnameCompare)) + makeMatrixGroup4Section(Object.keys(group[domains[0]]) + .sort(hostnameCompare)) .appendTo(groupDiv); - for (let i=1; i<domains.length; ++i) { + for (let i=1; i<domains.length; ++i) { makeMatrixGroup4Section(Object.keys(group[domains[i]]) - .sort(hostnameCompare)) - .appendTo(groupDiv); - } - groupDiv.appendTo(matrixList); + .sort(hostnameCompare)) + .appendTo(groupDiv); + } + groupDiv.appendTo(matrixList); } let makeMenu = function () { - let groupStats = getGroupStats(); + let groupStats = getGroupStats(); - if (Object.keys(groupStats).length === 0) { - return; - } + if (Object.keys(groupStats).length === 0) { + return; + } - // https://github.com/gorhill/httpswitchboard/issues/31 - if (matrixCellHotspots) { + // https://github.com/gorhill/httpswitchboard/issues/31 + if (matrixCellHotspots) { matrixCellHotspots.detach(); - } + } - renderMatrixHeaderRow(); + renderMatrixHeaderRow(); - startMatrixUpdate(); - makeMatrixGroup0(groupStats[0]); - makeMatrixGroup1(groupStats[1]); - makeMatrixGroup2(groupStats[2]); - makeMatrixGroup3(groupStats[3]); - makeMatrixGroup4(groupStats[4]); - endMatrixUpdate(); + startMatrixUpdate(); + makeMatrixGroup0(groupStats[0]); + makeMatrixGroup1(groupStats[1]); + makeMatrixGroup2(groupStats[2]); + makeMatrixGroup3(groupStats[3]); + makeMatrixGroup4(groupStats[4]); + endMatrixUpdate(); - initScopeCell(); - updateMatrixButtons(); - resizePopup(); + 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 || ''; - - let prettyNames = matrixHeaderPrettyNames; - let keys = Object.keys(prettyNames); - for (let i=keys.length-1; i>=0; --i) { + document.body.style.setProperty('font-size', + getUserSetting('displayTextSize')); + document.body.classList.toggle('colorblind', + getUserSetting('colorBlindFriendly')); + uDom.nodeFromId('version').textContent = + matrixSnapshot.appVersion || ''; + + let prettyNames = matrixHeaderPrettyNames; + let keys = Object.keys(prettyNames); + for (let i=keys.length-1; i>=0; --i) { let key = keys[i]; let cell = uDom('#matHead .matCell[data-req-type="'+ key +'"]'); let text = vAPI.i18n(key + 'PrettyName'); cell.text(text); prettyNames[key] = text; - } + } - firstPartyLabel = uDom('[data-i18n="matrix1stPartyLabel"]').text(); - blacklistedHostnamesLabel = - uDom('[data-i18n="matrixBlacklistedHostnames"]').text(); + firstPartyLabel = uDom('[data-i18n="matrix1stPartyLabel"]').text(); + blacklistedHostnamesLabel = + uDom('[data-i18n="matrixBlacklistedHostnames"]').text(); } // 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(); + if (matrixSnapshot.scope === '*') { + return; + } + matrixSnapshot.scope = '*'; + document.body.classList.add('globalScope'); + matrixSnapshot.tMatrixModifiedTime = undefined; + updateMatrixSnapshot(); + dropDownMenuHide(); } function selectSpecificScope(ev) { - let 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(); + let 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 ηMatrix. - if (matrixSnapshot.url === '') { - return; - } - let specificScope = uDom.nodeFromId('specificScope'); - - while (specificScope.firstChild !== null) { + // It's possible there is no page URL at this point: some pages cannot + // be filtered by ηMatrix. + if (matrixSnapshot.url === '') { + return; + } + let specificScope = uDom.nodeFromId('specificScope'); + + while (specificScope.firstChild !== null) { specificScope.removeChild(specificScope.firstChild); - } + } - // Fill in the scope menu entries - let pos = matrixSnapshot.domain.indexOf('.'); - let tld, labels; - if (pos === -1) { + // Fill in the scope menu entries + let pos = matrixSnapshot.domain.indexOf('.'); + let tld, labels; + if (pos === -1) { tld = ''; labels = matrixSnapshot.hostname; - } else { + } else { tld = matrixSnapshot.domain.slice(pos + 1); labels = matrixSnapshot.hostname.slice(0, -tld.length); - } - let beg = 0; - let span, label; - while (beg < labels.length) { + } + let beg = 0; + let span, label; + while (beg < labels.length) { pos = labels.indexOf('.', beg); if (pos === -1) { - pos = labels.length; + pos = labels.length; } else { - pos += 1; + pos += 1; } label = document.createElement('span'); label.appendChild(document - .createTextNode(Punycode - .toUnicode(labels.slice(beg, - pos)))); + .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 !== '') { + } + 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(); + } + updateScopeCell(); } function updateScopeCell() { - let specificScope = uDom.nodeFromId('specificScope'); + let specificScope = uDom.nodeFromId('specificScope'); let isGlobal = matrixSnapshot.scope === '*'; - document.body.classList.toggle('globalScope', isGlobal); - specificScope.classList.toggle('on', !isGlobal); - uDom.nodeFromId('globalScope').classList.toggle('on', isGlobal); + document.body.classList.toggle('globalScope', isGlobal); + specificScope.classList.toggle('on', !isGlobal); + uDom.nodeFromId('globalScope').classList.toggle('on', isGlobal); - for (let node of specificScope.children) { + for (let node of specificScope.children) { node.classList.toggle('on', - !isGlobal - && matrixSnapshot - .scope - .endsWith(node.getAttribute('data-scope'))); - } + !isGlobal + && matrixSnapshot + .scope + .endsWith(node.getAttribute('data-scope'))); + } } function updateMatrixSwitches() { - let count = 0; + let count = 0; let enabled; let switches = matrixSnapshot.tSwitches; - for (let switchName in switches) { + for (let switchName in switches) { if (switches.hasOwnProperty(switchName) === false) { - continue; - } + continue; + } enabled = switches[switchName]; if (enabled && switchName !== 'matrix-off') { - count += 1; + 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 = + } + 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 = + uDom.nodeFromSelector('#mtxSwitch_matrix-off span.badge').textContent = matrixSnapshot.blockedCount.toLocaleString(); - document.body.classList.toggle('powerOff', switches['matrix-off']); + document.body.classList.toggle('powerOff', switches['matrix-off']); } function toggleMatrixSwitch(ev) { - if (ev.target.localName === 'a') { - return; - } - - let elem = ev.currentTarget; - let pos = elem.id.indexOf('_'); - if (pos === -1) { - return; - } - - let switchName = elem.id.slice(pos + 1); - let request = { + if (ev.target.localName === 'a') { + return; + } + + let elem = ev.currentTarget; + let pos = elem.id.indexOf('_'); + if (pos === -1) { + return; + } + + let switchName = elem.id.slice(pos + 1); + let request = { what: 'toggleMatrixSwitch', switchName: switchName, srcHostname: matrixSnapshot.scope - }; - vAPI.messaging.send('popup.js', request, updateMatrixSnapshot); + }; + vAPI.messaging.send('popup.js', request, updateMatrixSnapshot); } function updatePersistButton() { - let diffCount = matrixSnapshot.diff.length; - let button = uDom('#buttonPersist'); + let diffCount = matrixSnapshot.diff.length; + let button = uDom('#buttonPersist'); - button.contents() + button.contents() .filter(function () { - return this.nodeType===3; - }) - .first() + return this.nodeType===3; + }) + .first() .text(diffCount > 0 ? '\uf13e' : '\uf023'); - button.descendants('span.badge').text(diffCount > 0 ? diffCount : ''); + button.descendants('span.badge').text(diffCount > 0 ? diffCount : ''); - let disabled = diffCount === 0; + let disabled = diffCount === 0; - button.toggleClass('disabled', disabled); - uDom('#buttonRevertScope').toggleClass('disabled', disabled); + button.toggleClass('disabled', disabled); + uDom('#buttonRevertScope').toggleClass('disabled', disabled); } function persistMatrix() { - let request = { + let request = { what: 'applyDiffToPermanentMatrix', diff: matrixSnapshot.diff - }; - vAPI.messaging.send('popup.js', request, updateMatrixSnapshot); + }; + vAPI.messaging.send('popup.js', request, updateMatrixSnapshot); } // rhill 2014-03-12: revert completely ALL changes related to the // current page, including scopes. function revertMatrix() { - let request = { + let request = { what: 'applyDiffToTemporaryMatrix', diff: matrixSnapshot.diff - }; - vAPI.messaging.send('popup.js', request, updateMatrixSnapshot); + }; + vAPI.messaging.send('popup.js', request, updateMatrixSnapshot); } // Buttons which are affected by any changes in the matrix function updateMatrixButtons() { - updateScopeCell(); - updateMatrixSwitches(); - updatePersistButton(); + updateScopeCell(); + updateMatrixSwitches(); + updatePersistButton(); } function revertAll() { - let request = { + let request = { what: 'revertTemporaryMatrix' - }; - vAPI.messaging.send('popup.js', request, updateMatrixSnapshot); - dropDownMenuHide(); + }; + vAPI.messaging.send('popup.js', request, updateMatrixSnapshot); + dropDownMenuHide(); } function buttonReloadHandler(ev) { - vAPI.messaging.send('popup.js', { + vAPI.messaging.send('popup.js', { what: 'forceReloadTab', tabId: matrixSnapshot.tabId, bypassCache: ev.ctrlKey || ev.metaKey || ev.shiftKey - }); + }); } function mouseenterMatrixCellHandler(ev) { - matrixCellHotspots.appendTo(ev.target); + matrixCellHotspots.appendTo(ev.target); } function mouseleaveMatrixCellHandler() { - matrixCellHotspots.detach(); + matrixCellHotspots.detach(); } function gotoExtensionURL(ev) { - let url = uDom(ev.currentTarget).attr('data-extension-url'); - if (url) { + let url = uDom(ev.currentTarget).attr('data-extension-url'); + if (url) { vAPI.messaging.send('popup.js', { - what: 'gotoExtensionURL', - url: url, - shiftKey: ev.shiftKey + what: 'gotoExtensionURL', + url: url, + shiftKey: ev.shiftKey }); - } - dropDownMenuHide(); - vAPI.closePopup(); + } + dropDownMenuHide(); + vAPI.closePopup(); } function dropDownMenuShow(ev) { - let button = ev.target; - let menuOverlay = document.getElementById(button.getAttribute('data-dropdown-menu')); - let butnRect = button.getBoundingClientRect(); - let viewRect = document.body.getBoundingClientRect(); - let butnNormalLeft = butnRect.left / (viewRect.width - butnRect.width); - menuOverlay.classList.add('show'); - let menu = menuOverlay.querySelector('.dropdown-menu'); - let menuRect = menu.getBoundingClientRect(); - let menuLeft = butnNormalLeft * (viewRect.width - menuRect.width); - menu.style.left = menuLeft.toFixed(0) + 'px'; - menu.style.top = butnRect.bottom + 'px'; + let button = ev.target; + let menuOverlay = document.getElementById(button.getAttribute('data-dropdown-menu')); + let butnRect = button.getBoundingClientRect(); + let viewRect = document.body.getBoundingClientRect(); + let butnNormalLeft = butnRect.left / (viewRect.width - butnRect.width); + menuOverlay.classList.add('show'); + let menu = menuOverlay.querySelector('.dropdown-menu'); + let menuRect = menu.getBoundingClientRect(); + let 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'); + uDom('.dropdown-menu-capture').removeClass('show'); } let onMatrixSnapshotReady = function (response) { - if (response === 'ENOTFOUND') { + if (response === 'ENOTFOUND') { uDom.nodeFromId('noTabFound').textContent = - vAPI.i18n('matrixNoTabFound'); + vAPI.i18n('matrixNoTabFound'); document.body.classList.add('noTabFound'); return; - } + } - // Now that tabId and pageURL are set, we can build our menu - initMenuEnvironment(); - makeMenu(); + // 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 === '') { + // 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', ''); - } - - // Create a hash to find out whether the reload button needs to be - // highlighted. - // TODO: - // ηMatrix: not sure what the purpose of highlighting is... - // Maybe telling the user that the page needs refreshing? But - // that's hardly useful (and by now people have gotten used to - // the lack of such a feature.) - // Not really going to do it, but let's leave the comment. + } + + // Create a hash to find out whether the reload button needs to be + // highlighted. + // TODO: + // ηMatrix: not sure what the purpose of highlighting is... + // Maybe telling the user that the page needs refreshing? But + // that's hardly useful (and by now people have gotten used to + // the lack of such a feature.) + // Not really going to do it, but let's leave the comment. }; let matrixSnapshotPoller = (function () { - let timer = null; + let timer = null; - let preprocessMatrixSnapshot = function (snapshot) { + let preprocessMatrixSnapshot = function (snapshot) { if (Array.isArray(snapshot.headerIndices)) { - snapshot.headerIndices = new Map(snapshot.headerIndices); + snapshot.headerIndices = new Map(snapshot.headerIndices); } return snapshot; - }; + }; - let processPollResult = function (response) { + let processPollResult = function (response) { if (typeof response !== 'object') { - return; + return; } if (response.mtxContentModified === false - && response.mtxCountModified === false - && response.pMatrixModified === false - && response.tMatrixModified === false) { - return; + && response.mtxCountModified === false + && response.pMatrixModified === false + && response.tMatrixModified === false) { + return; } matrixSnapshot = preprocessMatrixSnapshot(response); if (response.mtxContentModified) { - makeMenu(); - return; + makeMenu(); + return; } if (response.mtxCountModified) { - updateMatrixCounts(); + updateMatrixCounts(); } if (response.pMatrixModified - || response.tMatrixModified - || response.scopeModified) { - updateMatrixColors(); - updateMatrixBehavior(); - updateMatrixButtons(); + || response.tMatrixModified + || response.scopeModified) { + updateMatrixColors(); + updateMatrixBehavior(); + updateMatrixButtons(); } - }; + }; - let onPolled = function (response) { + let onPolled = function (response) { processPollResult(response); pollAsync(); - }; + }; - let pollNow = function () { + let 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, + 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); - }; + }; - let poll = function () { + let poll = function () { timer = null; pollNow(); - }; + }; - let pollAsync = function () { + let pollAsync = function () { if (timer !== null) { - return; + return; } if (document.defaultView === null) { - return; + return; } timer = vAPI.setTimeout(poll, 1414); - }; + }; - let unpollAsync = function () { + let unpollAsync = function () { if (timer !== null) { - clearTimeout(timer); - timer = null; + clearTimeout(timer); + timer = null; } - }; + }; - (function () { + (function () { let tabId = matrixSnapshot.tabId; // If no tab id yet, see if there is one specified in our URL if (tabId === undefined) { - let matches = window - .location - .search - .match(/(?:\?|&)tabId=([^&]+)/); + let matches = window + .location + .search + .match(/(?:\?|&)tabId=([^&]+)/); - if (matches !== null) { + if (matches !== null) { tabId = matches[1]; // No need for logger button when embedded in logger uDom('[data-extension-url="logger-ui.html"]').remove(); - } + } } let snapshotFetched = function (response) { - if (typeof response === 'object') { + if (typeof response === 'object') { matrixSnapshot = preprocessMatrixSnapshot(response); - } - onMatrixSnapshotReady(response); - pollAsync(); + } + onMatrixSnapshotReady(response); + pollAsync(); }; vAPI.messaging.send('popup.js', { - what: 'matrixSnapshot', - tabId: tabId + what: 'matrixSnapshot', + tabId: tabId }, snapshotFetched); - })(); + })(); - return { + return { pollNow: pollNow - }; + }; })(); // Below is UI stuff which is not key to make the menu, so this can @@ -1536,8 +1536,8 @@ matrixCellHotspots = uDom('#cellHotspots').detach(); uDom('body') - .on('mouseenter', '.matCell', mouseenterMatrixCellHandler) - .on('mouseleave', '.matCell', mouseleaveMatrixCellHandler); + .on('mouseenter', '.matCell', mouseenterMatrixCellHandler) + .on('mouseleave', '.matCell', mouseleaveMatrixCellHandler); uDom('#specificScope').on('click', selectSpecificScope); uDom('#globalScope').on('click', selectGlobalScope); @@ -1553,9 +1553,9 @@ uDom('body').on('click', '.dropdown-menu-capture', dropDownMenuHide); uDom('#matList').on('click', '.g4Meta', function (ev) { - matrixSnapshot.collapseBlacklistedDomains = + matrixSnapshot.collapseBlacklistedDomains = ev.target.classList.toggle('g4Collapsed'); - setUserSetting('popupCollapseBlacklistedDomains', - matrixSnapshot.collapseBlacklistedDomains); + setUserSetting('popupCollapseBlacklistedDomains', + matrixSnapshot.collapseBlacklistedDomains); }); })(); diff --git a/js/profiler.js b/js/profiler.js index 7406e92..49fe7d9 100644 --- a/js/profiler.js +++ b/js/profiler.js @@ -2,7 +2,7 @@ ηMatrix - a browser extension to black/white list requests. Copyright (C) 2014-2019 Raymond Hill - Copyright (C) 2019-2020 Alessio Vanni + Copyright (C) 2019-2022 Alessio Vanni This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see {http://www.gnu.org/licenses/}. - Home: https://libregit.spks.xyz/heckyel/ematrix + Home: https://gitlab.com/vannilla/ematrix uMatrix Home: https://github.com/gorhill/uMatrix */ diff --git a/js/raw-settings.js b/js/raw-settings.js index 5940188..54c5b3c 100644 --- a/js/raw-settings.js +++ b/js/raw-settings.js @@ -2,7 +2,7 @@ ηMatrix - a browser extension to black/white list requests. Copyright (C) 2018-2019 Raymond Hill - Copyright (C) 2019-2020 Alessio Vanni + Copyright (C) 2019-2022 Alessio Vanni This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see {http://www.gnu.org/licenses/}. - Home: https://libregit.spks.xyz/heckyel/ematrix + Home: https://gitlab.com/vannilla/ematrix uMatrix Home: https://github.com/gorhill/uBlock */ @@ -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; - } - } - for ( i = 0; i < n; i++ ) { - pos = lines[i].indexOf(' '); - pretty.push(whitespaces.slice(0, max - pos) + lines[i]); + 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; } - 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 - ); + } + 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 + ); +}; - /******************************************************************************/ +/******************************************************************************/ - // 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 5694b83..a9e11b4 100644 --- a/js/settings.js +++ b/js/settings.js @@ -2,7 +2,7 @@ ηMatrix - a browser extension to black/white list requests. Copyright (C) 2014-2019 Raymond Hill - Copyright (C) 2019-2020 Alessio Vanni + Copyright (C) 2019-2022 Alessio Vanni This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see {http://www.gnu.org/licenses/}. - Home: https://libregit.spks.xyz/heckyel/ematrix + Home: https://gitlab.com/vannilla/ematrix uMatrix Home: https://github.com/gorhill/uMatrix */ @@ -27,100 +27,100 @@ let cachedSettings = {}; function changeUserSettings(name, value) { - vAPI.messaging.send('settings.js', { + vAPI.messaging.send('settings.js', { what: 'userSettings', name: name, value: value - }); + }); } function changeMatrixSwitch(name, state) { - vAPI.messaging.send('settings.js', { + vAPI.messaging.send('settings.js', { what: 'setMatrixSwitch', switchName: name, state: state - }); + }); } function onChangeValueHandler(elem, setting, min, max) { - let oldVal = cachedSettings.userSettings[setting]; - let newVal = Math.round(parseFloat(elem.value)); - if (typeof newVal !== 'number') { + let oldVal = cachedSettings.userSettings[setting]; + let newVal = Math.round(parseFloat(elem.value)); + if (typeof newVal !== 'number') { newVal = oldVal; - } else { + } else { newVal = Math.max(newVal, min); newVal = Math.min(newVal, max); - } - elem.value = newVal; - if (newVal !== oldVal) { + } + elem.value = newVal; + if (newVal !== oldVal) { changeUserSettings(setting, newVal); - } + } } function prepareToDie() { - onChangeValueHandler(uDom.nodeFromId('deleteUnusedSessionCookiesAfter'), - 'deleteUnusedSessionCookiesAfter', - 15, 1440); - onChangeValueHandler(uDom.nodeFromId('clearBrowserCacheAfter'), - 'clearBrowserCacheAfter', - 15, 1440); + onChangeValueHandler(uDom.nodeFromId('deleteUnusedSessionCookiesAfter'), + 'deleteUnusedSessionCookiesAfter', + 15, 1440); + onChangeValueHandler(uDom.nodeFromId('clearBrowserCacheAfter'), + 'clearBrowserCacheAfter', + 15, 1440); } function onInputChanged(ev) { - let target = ev.target; + let target = ev.target; - switch (target.id) { - case 'displayTextSize': + switch (target.id) { + case 'displayTextSize': changeUserSettings('displayTextSize', target.value + 'px'); break; - case 'clearBrowserCache': - case 'cloudStorageEnabled': - case 'collapseBlacklisted': - case 'colorBlindFriendly': - case 'deleteCookies': - case 'deleteLocalStorage': - case 'deleteUnusedSessionCookies': - case 'iconBadgeEnabled': - case 'processHyperlinkAuditing': - case 'disableUpdateIcon': - case 'resolveCname': - changeUserSettings(target.id, target.checked); - break; - case 'collapseBlocked': + case 'clearBrowserCache': + case 'cloudStorageEnabled': + case 'collapseBlacklisted': + case 'colorBlindFriendly': + case 'deleteCookies': + case 'deleteLocalStorage': + case 'deleteUnusedSessionCookies': + case 'iconBadgeEnabled': + case 'processHyperlinkAuditing': + case 'disableUpdateIcon': + case 'resolveCname': changeUserSettings(target.id, target.checked); - synchronizeWidgets(); break; - case 'noMixedContent': - case 'noscriptTagsSpoofed': - case 'processReferer': + case 'collapseBlocked': + changeUserSettings(target.id, target.checked); + synchronizeWidgets(); + break; + case 'noMixedContent': + case 'noscriptTagsSpoofed': + case 'processReferer': changeMatrixSwitch(target.getAttribute('data-matrix-switch'), - target.checked); + target.checked); break; - case 'deleteUnusedSessionCookiesAfter': + case 'deleteUnusedSessionCookiesAfter': onChangeValueHandler(target, 'deleteUnusedSessionCookiesAfter', - 15, 1440); + 15, 1440); break; - case 'clearBrowserCacheAfter': + case 'clearBrowserCacheAfter': onChangeValueHandler(target, 'clearBrowserCacheAfter', 15, 1440); break; - case 'popupScopeLevel': + case 'popupScopeLevel': changeUserSettings('popupScopeLevel', target.value); break; - default: + default: break; - } + } } function synchronizeWidgets() { - let e1, e2; + let e1, e2; - e1 = uDom.nodeFromId('collapseBlocked'); - e2 = uDom.nodeFromId('collapseBlacklisted'); - if (e1.checked) { + e1 = uDom.nodeFromId('collapseBlocked'); + e2 = uDom.nodeFromId('collapseBlacklisted'); + if (e1.checked) { e2.setAttribute('disabled', ''); - } else { + } else { e2.removeAttribute('disabled'); - } + } } let onSettingsReceived = function (settings) { @@ -131,24 +131,24 @@ let matrixSwitches = settings.matrixSwitches; uDom('[data-setting-bool]').forEach(function (elem) { - elem.prop('checked', userSettings[elem.prop('id')] === true); + elem.prop('checked', userSettings[elem.prop('id')] === true); }); uDom('[data-matrix-switch]').forEach(function (elem) { - let switchName = elem.attr('data-matrix-switch'); - if (typeof switchName === 'string' && switchName !== '') { + let 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; + parseInt(userSettings.displayTextSize, 10) || 14; uDom.nodeFromId('popupScopeLevel').value = userSettings.popupScopeLevel; uDom.nodeFromId('deleteUnusedSessionCookiesAfter').value = - userSettings.deleteUnusedSessionCookiesAfter; + userSettings.deleteUnusedSessionCookiesAfter; uDom.nodeFromId('clearBrowserCacheAfter').value = - userSettings.clearBrowserCacheAfter; + userSettings.clearBrowserCacheAfter; synchronizeWidgets(); @@ -159,6 +159,6 @@ } vAPI.messaging.send('settings.js', { - what: 'getUserSettings' + what: 'getUserSettings' }, onSettingsReceived); })(); diff --git a/js/start.js b/js/start.js index b0f3970..f7867ee 100644 --- a/js/start.js +++ b/js/start.js @@ -2,7 +2,7 @@ ηMatrix - a browser extension to black/white list requests. Copyright (C) 2014-2019 Raymond Hill - Copyright (C) 2019-2020 Alessio Vanni + Copyright (C) 2019-2022 Alessio Vanni This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see {http://www.gnu.org/licenses/}. - Home: https://libregit.spks.xyz/heckyel/ematrix + Home: https://gitlab.com/vannilla/ematrix uMatrix Home: https://github.com/gorhill/uMatrix */ @@ -31,58 +31,58 @@ let ηm = ηMatrix; let processCallbackQueue = function (queue, callback) { - while (queue.length > 0) { - let fn = queue.pop(); - fn(); - } - - if (typeof callback === 'function') { - callback(); - } + while (queue.length > 0) { + let fn = queue.pop(); + fn(); + } + + if (typeof callback === 'function') { + callback(); + } }; let onAllDone = function () { - ηm.webRequest.start(); + ηm.webRequest.start(); - ηm.assets.checkVersion(); - ηm.assets.addObserver(ηm.assetObserver.bind(ηm)); - ηm.scheduleAssetUpdater(ηm.userSettings.autoUpdate ? 7 * 60 * 1000 : 0); + ηm.assets.checkVersion(); + ηm.assets.addObserver(ηm.assetObserver.bind(ηm)); + ηm.scheduleAssetUpdater(ηm.userSettings.autoUpdate ? 7 * 60 * 1000 : 0); - vAPI.cloud.start([ 'myRulesPane' ]); + vAPI.cloud.start([ 'myRulesPane' ]); }; let onTabsReady = function (tabs) { - for (let i=tabs.length-1; i>=0; --i) { - // console.debug('start.js > binding %d tabs', i); + for (let i=tabs.length-1; i>=0; --i) { + // console.debug('start.js > binding %d tabs', i); let tab = tabs[i]; ηm.tabContextManager.push(tab.id, tab.url, 'newURL'); - } + } - onAllDone(); + onAllDone(); }; let onUserSettingsLoaded = function () { - ηm.loadHostsFiles(); + ηm.loadHostsFiles(); }; let 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'); - - vAPI.tabs.getAll(onTabsReady); + η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'); + + vAPI.tabs.getAll(onTabsReady); }; processCallbackQueue(ηm.onBeforeStartQueue, function () { - ηm.loadPublicSuffixList(onPSLReady); + ηm.loadPublicSuffixList(onPSLReady); }); })(); diff --git a/js/storage.js b/js/storage.js index f5afca8..24249ab 100644 --- a/js/storage.js +++ b/js/storage.js @@ -2,7 +2,7 @@ ηMatrix - a browser extension to black/white list requests. Copyright (C) 2014-2019 Raymond Hill - Copyright (C) 2019-2020 Alessio Vanni + Copyright (C) 2019-2022 Alessio Vanni This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -552,7 +552,7 @@ Components.utils.import('chrome://ematrix/content/lib/Tools.jsm'); }; ηMatrix.scheduleAssetUpdater = (function () { - let timer; + let timer = undefined; let next = 0; return function (updateDelay) { @@ -618,11 +618,11 @@ Components.utils.import('chrome://ematrix/content/lib/Tools.jsm'); if (details.assetKeys.length !== 0) { this.loadHostsFiles(); } - if (this.userSettings.autoUpdate) { - this.scheduleAssetUpdater(25200000); - } else { - this.scheduleAssetUpdater(0); - } + + ηm.scheduleAssetUpdater(ηm.userSettings.autoUpdate ? + 7 * 60 * 100000 : + 0); + vAPI.messaging.broadcast({ what: 'assetsUpdated', assetKeys: details.assetKeys @@ -2,7 +2,7 @@ ηMatrix - a browser extension to black/white list requests. Copyright (C) 2014-2019 Raymond Hill - Copyright (C) 2019-2020 Alessio Vanni + Copyright (C) 2019-2022 Alessio Vanni This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see {http://www.gnu.org/licenses/}. - Home: https://libregit.spks.xyz/heckyel/ematrix + Home: https://gitlab.com/vannilla/ematrix uMatrix Home: https://github.com/gorhill/uMatrix */ @@ -40,47 +40,47 @@ // apply only to that scheme. ηm.normalizePageURL = function (tabId, pageURL) { - if (vAPI.isBehindTheSceneTabId(tabId)) { + 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) { + } + + // 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) { let matches = /main-blocked\.html\?details=([^&]+)/.exec(pageURL); if (matches && matches.length === 2) { - try { + try { let details = JSON.parse(atob(matches[1])); pageURL = details.url; - } catch (e) { - } + } catch (e) { + } } - } + } - let uri = UriTools.set(pageURL); - let scheme = uri.scheme; - if (scheme === 'https' || scheme === 'http') { + let uri = UriTools.set(pageURL); + let scheme = uri.scheme; + if (scheme === 'https' || scheme === 'http') { return UriTools.normalizedURI(); - } + } - let fakeHostname = scheme + '-scheme'; + let fakeHostname = scheme + '-scheme'; - if (uri.hostname !== '') { + if (uri.hostname !== '') { fakeHostname = uri.hostname + '.' + fakeHostname; - } else if (scheme === 'about') { + } else if (scheme === 'about') { fakeHostname = uri.path + '.' + fakeHostname; - } + } - return 'http://' + fakeHostname + '/'; + return 'http://' + fakeHostname + '/'; }; /* @@ -149,119 +149,119 @@ */ ηm.tabContextManager = (function () { - let tabContexts = Object.create(null); + let 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. - let mostRecentRootDocURL = ''; - let 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. + let mostRecentRootDocURL = ''; + let mostRecentRootDocURLTimestamp = 0; - let gcPeriod = 31 * 60 * 1000; // every 31 minutes + let gcPeriod = 31 * 60 * 1000; // every 31 minutes - // A pushed entry is removed from the stack unless it is - // committed with a set time. - let StackEntry = function (url, commit) { + // A pushed entry is removed from the stack unless it is + // committed with a set time. + let StackEntry = function (url, commit) { this.url = url; this.committed = commit; this.tstamp = Date.now(); - }; + }; - let TabContext = function (tabId) { + let TabContext = function (tabId) { this.tabId = tabId; this.stack = []; this.rawURL = - this.normalURL = - this.scheme = - this.rootHostname = - this.rootDomain = ''; + this.normalURL = + this.scheme = + this.rootHostname = + this.rootDomain = ''; this.secure = false; this.commitTimer = null; this.gcTimer = null; tabContexts[tabId] = this; - }; + }; - TabContext.prototype.destroy = function () { + TabContext.prototype.destroy = function () { if (vAPI.isBehindTheSceneTabId(this.tabId)) { - return; + return; } if (this.gcTimer !== null) { - clearTimeout(this.gcTimer); - this.gcTimer = null; + clearTimeout(this.gcTimer); + this.gcTimer = null; } delete tabContexts[this.tabId]; - }; + }; - TabContext.prototype.onTab = function (tab) { + TabContext.prototype.onTab = function (tab) { if (tab) { - this.gcTimer = vAPI.setTimeout(this.onGC.bind(this), gcPeriod); + this.gcTimer = vAPI.setTimeout(this.onGC.bind(this), gcPeriod); } else { - this.destroy(); + this.destroy(); } - }; + }; - TabContext.prototype.onGC = function () { + TabContext.prototype.onGC = function () { this.gcTimer = null; if (vAPI.isBehindTheSceneTabId(this.tabId)) { - return; + 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 () { + // 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; + return; } this.commitTimer = null; // Remove uncommitted entries at the top of the stack. let i = this.stack.length; while (i--) { - if (this.stack[i].committed) { + 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; + this.stack[0].committed = true; + i = 0; } ++i; if (i < this.stack.length) { - this.stack.length = i; - this.update(); - ηm.bindTabToPageStats(this.tabId, 'newURL'); + this.stack.length = i; + this.update(); + ηm.bindTabToPageStats(this.tabId, 'newURL'); } - }; + }; - // 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 () { + // 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; + return; } this.gcTimer = vAPI.setTimeout(this.onGC.bind(this), gcPeriod); - }; + }; - // Update just force all properties to be updated to match the - // most recent root URL. - TabContext.prototype.update = function () { + // 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.normalURL = + this.scheme = + this.rootHostname = + this.rootDomain = ''; + this.secure = false; + return; } this.rawURL = this.stack[this.stack.length - 1].url; @@ -269,71 +269,71 @@ this.scheme = UriTools.schemeFromURI(this.rawURL); this.rootHostname = UriTools.hostnameFromURI(this.normalURL); this.rootDomain = UriTools.domainFromHostname(this.rootHostname) - || this.rootHostname; + || this.rootHostname; this.secure = UriTools.isSecureScheme(this.scheme); - }; + }; - // Called whenever a candidate root URL is spotted for the tab. - TabContext.prototype.push = function (url, context) { + // Called whenever a candidate root URL is spotted for the tab. + TabContext.prototype.push = function (url, context) { if (vAPI.isBehindTheSceneTabId(this.tabId)) { - return; - } + return; + } let committed = context !== undefined; let count = this.stack.length; let topEntry = this.stack[count - 1]; if (topEntry && topEntry.url === url) { - if (committed) { + if (committed) { topEntry.committed = true; - } - return; + } + return; } if (this.commitTimer !== null) { - clearTimeout(this.commitTimer); + clearTimeout(this.commitTimer); } if (committed) { - this.stack = [new StackEntry(url, true)]; + this.stack = [new StackEntry(url, true)]; } else { - this.stack.push(new StackEntry(url)); - this.commitTimer = - vAPI.setTimeout(this.onCommit.bind(this), 1000); + this.stack.push(new StackEntry(url)); + this.commitTimer = + vAPI.setTimeout(this.onCommit.bind(this), 1000); } this.update(); ηm.bindTabToPageStats(this.tabId, context); - }; + }; - // These are to be used for the API of the tab context manager. + // These are to be used for the API of the tab context manager. - let push = function (tabId, url, context) { + let push = function (tabId, url, context) { let entry = tabContexts[tabId]; if (entry === undefined) { - entry = new TabContext(tabId); - entry.autodestroy(); + entry = new TabContext(tabId); + entry.autodestroy(); } entry.push(url, context); mostRecentRootDocURL = url; mostRecentRootDocURLTimestamp = Date.now(); return entry; - }; + }; - // 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. - let mustLookup = function (tabId, url) { + // 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. + let mustLookup = function (tabId, url) { let entry; if (url !== undefined) { - entry = push(tabId, url); + entry = push(tabId, url); } else { - entry = tabContexts[tabId]; + entry = tabContexts[tabId]; } if (entry !== undefined) { - return entry; + return entry; } // https://github.com/chrisaljoudi/uBlock/issues/1025 @@ -342,8 +342,8 @@ // it is too far in the future, at which point it ceases // to be a "best guess". if (mostRecentRootDocURL - !== '' && mostRecentRootDocURLTimestamp + 500 < Date.now()) { - mostRecentRootDocURL = ''; + !== '' && mostRecentRootDocURLTimestamp + 500 < Date.now()) { + mostRecentRootDocURL = ''; } // https://github.com/chrisaljoudi/uBlock/issues/1001 @@ -352,7 +352,7 @@ // document to the unbound tab. It's a guess, but better // than ending up filtering nothing at all. if (mostRecentRootDocURL !== '') { - return push(tabId, mostRecentRootDocURL); + return push(tabId, mostRecentRootDocURL); } // If all else fail at finding a page store, re-categorize @@ -363,69 +363,69 @@ // Example: Chromium + case #12 at // http://raymondhill.net/ublock/popup.html return tabContexts[vAPI.noTabId]; - }; + }; - let lookup = function (tabId) { + let lookup = function (tabId) { return tabContexts[tabId] || null; - }; + }; - // Behind-the-scene tab context - (function () { + // Behind-the-scene tab context + (function () { let entry = new TabContext(vAPI.noTabId); entry.stack.push(new StackEntry('', true)); entry.rawURL = ''; entry.normalURL = ηm.normalizePageURL(entry.tabId); entry.rootHostname = UriTools.hostnameFromURI(entry.normalURL); entry.rootDomain = UriTools.domainFromHostname(entry.rootHostname) - || 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. + // 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) { + vAPI.tabs.onNavigation = function (details) { let tabId = details.tabId; if (vAPI.isBehindTheSceneTabId(tabId)) { - return; - } + return; + } push(tabId, details.url, 'newURL'); ηm.updateBadgeAsync(tabId); - }; + }; - // https://github.com/gorhill/uMatrix/issues/872 - // `changeInfo.url` may not always be available (Firefox). + // https://github.com/gorhill/uMatrix/issues/872 + // `changeInfo.url` may not always be available (Firefox). - vAPI.tabs.onUpdated = function (tabId, changeInfo, tab) { + vAPI.tabs.onUpdated = function (tabId, changeInfo, tab) { if (vAPI.isBehindTheSceneTabId(tabId)) { - return; - } + return; + } if (typeof tab.url !== 'string' || tab.url === '') { - return; - } + return; + } let url = changeInfo.url || tab.url; if (url) { - push(tabId, url, 'updateURL'); + push(tabId, url, 'updateURL'); } - }; + }; - vAPI.tabs.onClosed = function (tabId) { + vAPI.tabs.onClosed = function (tabId) { ηm.unbindTabFromPageStats(tabId); let entry = tabContexts[tabId]; if (entry instanceof TabContext) { - entry.destroy(); + entry.destroy(); } - }; + }; - return { + return { push: push, lookup: lookup, mustLookup: mustLookup - }; + }; })(); vAPI.tabs.registerListeners(); @@ -433,30 +433,30 @@ // Create an entry for the tab if it doesn't exist ηm.bindTabToPageStats = function (tabId, context) { - this.updateBadgeAsync(tabId); + this.updateBadgeAsync(tabId); - // Do not create a page store for URLs which are of no - // interests Example: dev console - let tabContext = this.tabContextManager.lookup(tabId); - if (tabContext === null) { + // Do not create a page store for URLs which are of no + // interests Example: dev console + let 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)) { + // 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]; - } + } - let normalURL = tabContext.normalURL; - let pageStore = this.pageStores[tabId] || null; + let normalURL = tabContext.normalURL; + let pageStore = this.pageStores[tabId] || null; - // The previous page URL, if any, associated with the tab - if (pageStore !== 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; + return pageStore; } // https://github.com/gorhill/uMatrix/issues/37 @@ -466,124 +466,124 @@ // 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; + && pageStore.pageHostname === tabContext.rootHostname) { + pageStore.rawURL = tabContext.rawURL; + pageStore.normalURL = normalURL; + this.updateTitle(tabId); + this.pageStoresToken = Date.now(); + return pageStore; } // We won't be reusing this page store. this.unbindTabFromPageStats(tabId); - } + } - // Try to resurrect first. - pageStore = this.resurrectPageStore(tabId, normalURL); - if (pageStore === null) { + // 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(); + } + this.pageStores[tabId] = pageStore; + this.updateTitle(tabId); + this.pageStoresToken = Date.now(); - return pageStore; + return pageStore; }; ηm.unbindTabFromPageStats = function (tabId) { - if (vAPI.isBehindTheSceneTabId(tabId)) { + if (vAPI.isBehindTheSceneTabId(tabId)) { return; - } + } - let pageStore = this.pageStores[tabId] || null; - if (pageStore === null) { + let pageStore = this.pageStores[tabId] || null; + if (pageStore === null) { return; - } + } - delete this.pageStores[tabId]; - this.pageStoresToken = Date.now(); + delete this.pageStores[tabId]; + this.pageStoresToken = Date.now(); - if (pageStore.incinerationTimer) { + if (pageStore.incinerationTimer) { clearTimeout(pageStore.incinerationTimer); pageStore.incinerationTimer = null; - } + } - if (this.pageStoreCemetery.hasOwnProperty(tabId) === false) { + if (this.pageStoreCemetery.hasOwnProperty(tabId) === false) { this.pageStoreCemetery[tabId] = {}; - } - let pageStoreCrypt = this.pageStoreCemetery[tabId]; + } + let pageStoreCrypt = this.pageStoreCemetery[tabId]; - let pageURL = pageStore.pageUrl; - pageStoreCrypt[pageURL] = pageStore; + let pageURL = pageStore.pageUrl; + pageStoreCrypt[pageURL] = pageStore; - pageStore.incinerationTimer = - vAPI.setTimeout(this.incineratePageStore.bind(this, tabId, pageURL), - 4 * 60 * 1000); + pageStore.incinerationTimer = + vAPI.setTimeout(this.incineratePageStore.bind(this, tabId, pageURL), + 4 * 60 * 1000); }; ηm.resurrectPageStore = function (tabId, pageURL) { - if (this.pageStoreCemetery.hasOwnProperty(tabId) === false) { + if (this.pageStoreCemetery.hasOwnProperty(tabId) === false) { return null; - } + } - let pageStoreCrypt = this.pageStoreCemetery[tabId]; + let pageStoreCrypt = this.pageStoreCemetery[tabId]; - if (pageStoreCrypt.hasOwnProperty(pageURL) === false) { + if (pageStoreCrypt.hasOwnProperty(pageURL) === false) { return null; - } + } - let pageStore = pageStoreCrypt[pageURL]; + let pageStore = pageStoreCrypt[pageURL]; - if (pageStore.incinerationTimer !== null) { + if (pageStore.incinerationTimer !== null) { clearTimeout(pageStore.incinerationTimer); pageStore.incinerationTimer = null; - } + } - delete pageStoreCrypt[pageURL]; - if (Object.keys(pageStoreCrypt).length === 0) { + delete pageStoreCrypt[pageURL]; + if (Object.keys(pageStoreCrypt).length === 0) { delete this.pageStoreCemetery[tabId]; - } + } - return pageStore; + return pageStore; }; ηm.incineratePageStore = function (tabId, pageURL) { - if (this.pageStoreCemetery.hasOwnProperty(tabId) === false) { + if (this.pageStoreCemetery.hasOwnProperty(tabId) === false) { return; - } + } - let pageStoreCrypt = this.pageStoreCemetery[tabId]; + let pageStoreCrypt = this.pageStoreCemetery[tabId]; - if (pageStoreCrypt.hasOwnProperty(pageURL) === false) { + if (pageStoreCrypt.hasOwnProperty(pageURL) === false) { return; - } + } - let pageStore = pageStoreCrypt[pageURL]; - if (pageStore.incinerationTimer !== null) { + let pageStore = pageStoreCrypt[pageURL]; + if (pageStore.incinerationTimer !== null) { clearTimeout(pageStore.incinerationTimer); pageStore.incinerationTimer = null; - } + } - delete pageStoreCrypt[pageURL]; + delete pageStoreCrypt[pageURL]; - if (Object.keys(pageStoreCrypt).length === 0) { + if (Object.keys(pageStoreCrypt).length === 0) { delete this.pageStoreCemetery[tabId]; - } + } - pageStore.dispose(); + pageStore.dispose(); }; ηm.pageStoreFromTabId = function (tabId) { - return this.pageStores[tabId] || null; + return this.pageStores[tabId] || null; }; // Never return null ηm.mustPageStoreFromTabId = function (tabId) { - return this.pageStores[tabId] || this.pageStores[vAPI.noTabId]; + return this.pageStores[tabId] || this.pageStores[vAPI.noTabId]; }; ηm.forceReload = function (tabId, bypassCache) { - vAPI.tabs.reload(tabId, bypassCache); + vAPI.tabs.reload(tabId, bypassCache); }; // Update badge @@ -595,9 +595,9 @@ // ηMatrix: does it matter to us? ηm.updateBadgeAsync = (function () { - let tabIdToTimer = Object.create(null); + let tabIdToTimer = Object.create(null); - let updateBadge = function (tabId) { + let updateBadge = function (tabId) { delete tabIdToTimer[tabId]; let iconId = null; @@ -605,81 +605,81 @@ let pageStore = this.pageStoreFromTabId(tabId); if (pageStore !== null) { - let total = pageStore.perLoadAllowedRequestCount + + let total = pageStore.perLoadAllowedRequestCount + pageStore.perLoadBlockedRequestCount; - if (total) { + if (total) { let squareSize = 19; let greenSize = squareSize * - Math.sqrt(pageStore.perLoadAllowedRequestCount / total); + Math.sqrt(pageStore.perLoadAllowedRequestCount / total); iconId = greenSize < squareSize/2 ? - Math.ceil(greenSize) : - Math.floor(greenSize); - } + Math.ceil(greenSize) : + Math.floor(greenSize); + } - if (this.userSettings.iconBadgeEnabled - && pageStore.perLoadBlockedRequestCount !== 0) { + if (this.userSettings.iconBadgeEnabled + && pageStore.perLoadBlockedRequestCount !== 0) { badgeStr = - this.formatCount(pageStore.perLoadBlockedRequestCount); - } + this.formatCount(pageStore.perLoadBlockedRequestCount); + } } vAPI.setIcon(tabId, iconId, badgeStr); - }; + }; - return function (tabId) { + return function (tabId) { if (tabIdToTimer[tabId]) { - return; + return; } if (vAPI.isBehindTheSceneTabId(tabId)) { - return; + return; } tabIdToTimer[tabId] = - vAPI.setTimeout(updateBadge.bind(this, tabId), 750); - }; + vAPI.setTimeout(updateBadge.bind(this, tabId), 750); + }; })(); ηm.updateTitle = (function () { - let tabIdToTimer = Object.create(null); - let tabIdToTryCount = Object.create(null); - let delay = 499; + let tabIdToTimer = Object.create(null); + let tabIdToTryCount = Object.create(null); + let delay = 499; - let tryNoMore = function (tabId) { + let tryNoMore = function (tabId) { delete tabIdToTryCount[tabId]; - }; + }; - let tryAgain = function (tabId) { + let tryAgain = function (tabId) { let count = tabIdToTryCount[tabId]; if (count === undefined) { - return false; + return false; } if (count === 1) { - delete tabIdToTryCount[tabId]; - return false; + delete tabIdToTryCount[tabId]; + return false; } tabIdToTryCount[tabId] = count - 1; tabIdToTimer[tabId] = - vAPI.setTimeout(updateTitle.bind(ηm, tabId), delay); + vAPI.setTimeout(updateTitle.bind(ηm, tabId), delay); return true; - }; + }; - let onTabReady = function (tabId, tab) { + let onTabReady = function (tabId, tab) { if (!tab) { - return tryNoMore(tabId); + return tryNoMore(tabId); } let pageStore = this.pageStoreFromTabId(tabId); if (pageStore === null) { - return tryNoMore(tabId); + return tryNoMore(tabId); } if (!tab.title && tryAgain(tabId)) { - return; + return; } // https://github.com/gorhill/uMatrix/issues/225 @@ -688,67 +688,67 @@ pageStore.title = tab.title || tab.url || ''; this.pageStoresToken = Date.now(); if (settled || !tryAgain(tabId)) { - tryNoMore(tabId); + tryNoMore(tabId); } - }; + }; - let updateTitle = function (tabId) { + let updateTitle = function (tabId) { delete tabIdToTimer[tabId]; vAPI.tabs.get(tabId, onTabReady.bind(this, tabId)); - }; + }; - return function (tabId) { + return function (tabId) { if (vAPI.isBehindTheSceneTabId(tabId)) { - return; + return; } if (tabIdToTimer[tabId]) { - clearTimeout(tabIdToTimer[tabId]); + clearTimeout(tabIdToTimer[tabId]); } tabIdToTimer[tabId] = - vAPI.setTimeout(updateTitle.bind(this, tabId), delay); + vAPI.setTimeout(updateTitle.bind(this, tabId), delay); tabIdToTryCount[tabId] = 5; - }; + }; })(); // Stale page store entries janitor // https://github.com/chrisaljoudi/uBlock/issues/455 (function () { - let cleanupPeriod = 7 * 60 * 1000; - let cleanupSampleAt = 0; - let cleanupSampleSize = 11; + let cleanupPeriod = 7 * 60 * 1000; + let cleanupSampleAt = 0; + let cleanupSampleSize = 11; - let cleanup = function () { + let cleanup = function () { let tabIds = Object.keys(ηm.pageStores).sort(); let checkTab = function(tabId) { - vAPI.tabs.get(tabId, function (tab) { + vAPI.tabs.get(tabId, function (tab) { if (!tab) { - ηm.unbindTabFromPageStats(tabId); + ηm.unbindTabFromPageStats(tabId); } - }); + }); }; if (cleanupSampleAt >= tabIds.length) { - cleanupSampleAt = 0; + cleanupSampleAt = 0; } let tabId; let n = - Math.min(cleanupSampleAt + cleanupSampleSize, tabIds.length); + Math.min(cleanupSampleAt + cleanupSampleSize, tabIds.length); for (let i=cleanupSampleAt; i<n; i++) { - tabId = tabIds[i]; - if (vAPI.isBehindTheSceneTabId(tabId)) { + tabId = tabIds[i]; + if (vAPI.isBehindTheSceneTabId(tabId)) { continue; - } - checkTab(tabId); + } + checkTab(tabId); } cleanupSampleAt = n; vAPI.setTimeout(cleanup, cleanupPeriod); - }; + }; - vAPI.setTimeout(cleanup, cleanupPeriod); + vAPI.setTimeout(cleanup, cleanupPeriod); })(); })(); diff --git a/js/traffic.js b/js/traffic.js index aae4070..e2d6a3e 100644 --- a/js/traffic.js +++ b/js/traffic.js @@ -2,7 +2,7 @@ ηMatrix - a browser extension to black/white list requests. Copyright (C) 2014-2019 Raymond Hill - Copyright (C) 2019-2020 Alessio Vanni + Copyright (C) 2019-2022 Alessio Vanni This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see {http://www.gnu.org/licenses/}. - Home: https://notabug.org/heckyel/ematrix + Home: https://gitlab.com/vannilla/ematrix uMatrix Home: https://github.com/gorhill/uMatrix */ @@ -2,7 +2,7 @@ ηMatrix - a browser extension to black/white list requests. Copyright (C) 2014-2019 Raymond Hill - Copyright (C) 2019-2020 Alessio Vanni + Copyright (C) 2019-2022 Alessio Vanni This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -26,8 +26,6 @@ 'use strict'; -/******************************************************************************/ - // It's just a silly, minimalist DOM framework: this allows me to not rely // on jQuery. jQuery contains way too much stuff than I need, and as per // Opera rules, I am not allowed to use a cut-down version of jQuery. So @@ -288,7 +286,8 @@ var uDom = (function () { let i = this.nodes.length; while (i--) { let cn = this.nodes[i]; - if ((p = cn.parentNode)) { + let p = cn.parentNode; + if (p) { p.removeChild(cn); } } diff --git a/js/uritools.js b/js/uritools.js index 770d54a..9ec928f 100644 --- a/js/uritools.js +++ b/js/uritools.js @@ -2,7 +2,7 @@ ηMatrix - a browser extension to black/white list requests. Copyright (C) 2014-2019 Raymond Hill - Copyright (C) 2019-2020 Alessio Vanni + Copyright (C) 2019-2022 Alessio Vanni This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see {http://www.gnu.org/licenses/}. - Home: https://libregit.spks.xyz/heckyel/ematrix + Home: https://gitlab.com/vannilla/ematrix uMatrix Home: https://github.com/gorhill/uMatrix */ @@ -37,500 +37,500 @@ 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); - }; +/******************************************************************************/ - URI.reNetworkScheme = /^(?:https?|wss?|ftps?)\b/; +// 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.isSecureScheme = function(scheme) { - return this.reSecureScheme.test(scheme); - }; +// 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.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. +/******************************************************************************/ - // 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.isNetworkScheme = function(scheme) { + return this.reNetworkScheme.test(scheme); +}; - 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.reNetworkScheme = /^(?:https?|wss?|ftps?)\b/; - /******************************************************************************/ +/******************************************************************************/ - 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.isSecureScheme = function(scheme) { + return this.reSecureScheme.test(scheme); +}; - URI.domain = function() { - return this.domainFromHostname(this.hostname); - }; +URI.reSecureScheme = /^(?:https|wss|ftps)\b/; - // 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. +/******************************************************************************/ - var domainCache = new Map(); - var domainCacheCountLowWaterMark = 75; - var domainCacheCountHighWaterMark = 100; - var domainCacheEntryJunkyard = []; - var domainCacheEntryJunkyardMax = domainCacheCountHighWaterMark - domainCacheCountLowWaterMark; +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 DomainCacheEntry = function(domain) { - this.init(domain); - }; +/******************************************************************************/ - DomainCacheEntry.prototype.init = function(domain) { - this.domain = domain; - this.tstamp = Date.now(); - return this; - }; +URI.pathFromURI = function(uri) { + var matches = rePathFromURI.exec(uri); + return matches !== null ? matches[1] : ''; +}; - 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); - } - }; + // 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); - var domainCacheReset = function() { - domainCache.clear(); - }; +/******************************************************************************/ - psl.onChanged.addListener(domainCacheReset); +URI.domainFromURI = function(uri) { + if ( !uri ) { + return ''; + } + return this.domainFromHostname(this.hostnameFromURI(uri)); +}; - /******************************************************************************/ +/******************************************************************************/ - URI.domainFromURI = function(uri) { - if ( !uri ) { - return ''; - } - return this.domainFromHostname(this.hostnameFromURI(uri)); - }; +// Normalize the way ηMatrix expects it - /******************************************************************************/ +URI.normalizedURI = function() { + // Will be removed: + // - port + // - user id/password + // - fragment + return this.assemble(this.normalizeBits); +}; - // Normalize the way ηMatrix expects it +/******************************************************************************/ - URI.normalizedURI = function() { - // Will be removed: - // - port - // - user id/password - // - fragment - return this.assemble(this.normalizeBits); - }; +URI.rootURL = function() { + if ( !this.hostname ) { + return ''; + } + return this.assemble(this.schemeBit | this.hostnameBit); +}; - /******************************************************************************/ +/******************************************************************************/ - URI.rootURL = function() { - if ( !this.hostname ) { - return ''; - } - return this.assemble(this.schemeBit | this.hostnameBit); - }; +URI.isValidHostname = function(hostname) { + var r; + try { + r = reValidHostname.test(hostname); + } + catch (e) { + return false; + } + return r; +}; - /******************************************************************************/ +/******************************************************************************/ - 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; - } +// Return the parent domain. For IP address, there is no parent domain. - // Parent is hostname minus first label - return hostname.slice(hostname.indexOf('.') + 1); - }; +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; + } - // Return all possible parent hostnames which can be derived from `hostname`, - // ordered from direct parent up to domain inclusively. + // Parent is hostname minus first label + return hostname.slice(hostname.indexOf('.') + 1); +}; - 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 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; +}; - /******************************************************************************/ +/******************************************************************************/ - // 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 ccd15c2..b8933b0 100644 --- a/js/user-rules.js +++ b/js/user-rules.js @@ -2,7 +2,7 @@ ηMatrix - a browser extension to black/white list requests. Copyright (C) 2014-2019 Raymond Hill - Copyright (C) 2019-2020 Alessio Vanni + Copyright (C) 2019-2022 Alessio Vanni This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see {http://www.gnu.org/licenses/}. - Home: https://libregit.spks.xyz/heckyel/ematrix + Home: https://gitlab.com/vannilla/ematrix uMatrix Home: https://github.com/gorhill/uMatrix */ @@ -27,303 +27,303 @@ // Switches before, rules after let directiveSort = function (a, b) { - let aIsSwitch = a.indexOf(':') !== -1; - let bIsSwitch = b.indexOf(':') !== -1; + let aIsSwitch = a.indexOf(':') !== -1; + let bIsSwitch = b.indexOf(':') !== -1; - if (aIsSwitch === bIsSwitch) { + if (aIsSwitch === bIsSwitch) { return a.localeCompare(b); - } + } - return aIsSwitch ? -1 : 1; + return aIsSwitch ? -1 : 1; }; let processUserRules = function (response) { - let allRules = {}; - let permanentRules = {}; - let temporaryRules = {}; + let allRules = {}; + let permanentRules = {}; + let temporaryRules = {}; - let rules = response.permanentRules.split(/\n+/); - for (let i=rules.length-1; i>=0; --i) { + let rules = response.permanentRules.split(/\n+/); + for (let i=rules.length-1; i>=0; --i) { let rule = rules[i].trim(); if (rule.length !== 0) { - permanentRules[rule] = allRules[rule] = true; + permanentRules[rule] = allRules[rule] = true; } - } + } - rules = response.temporaryRules.split(/\n+/); - for (let i=rules.length-1; i>=0; --i) { + rules = response.temporaryRules.split(/\n+/); + for (let i=rules.length-1; i>=0; --i) { let rule = rules[i].trim(); if (rule.length !== 0) { - temporaryRules[rule] = allRules[rule] = true; + temporaryRules[rule] = allRules[rule] = true; } - } + } - let permanentList = document.createDocumentFragment(); + let permanentList = document.createDocumentFragment(); let temporaryList = document.createDocumentFragment(); let li; - rules = Object.keys(allRules).sort(directiveSort); - for (let i=0; i<rules.length; ++i) { + rules = Object.keys(allRules).sort(directiveSort); + for (let i=0; i<rules.length; ++i) { let rule = rules[i]; let onLeft = permanentRules.hasOwnProperty(rule); let onRight = temporaryRules.hasOwnProperty(rule); - li = document.createElement('li'); + li = document.createElement('li'); if (onLeft && onRight) { - li.textContent = rule; - permanentList.appendChild(li); - li = document.createElement('li'); - li.textContent = rule; - temporaryList.appendChild(li); + li.textContent = rule; + permanentList.appendChild(li); + li = document.createElement('li'); + li.textContent = rule; + temporaryList.appendChild(li); } else if (onLeft) { - li.textContent = rule; - permanentList.appendChild(li); - li = document.createElement('li'); - li.textContent = rule; - li.className = 'notRight toRemove'; - temporaryList.appendChild(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.textContent = '\xA0'; - permanentList.appendChild(li); - li = document.createElement('li'); - li.textContent = rule; - li.className = 'notLeft'; - temporaryList.appendChild(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 let fromRequestPolicy = function (content) { - let matches = /\[origins-to-destinations\]([^\[]+)/.exec(content); - if (matches === null || matches.length !== 2) { + let matches = /\[origins-to-destinations\]([^\[]+)/.exec(content); + if (matches === null || matches.length !== 2) { return; - } - return matches[1].trim() + } + return matches[1].trim() .replace(/\|/g, ' ') .replace(/\n/g, ' * allow\n'); }; // https://github.com/gorhill/uMatrix/issues/270 let fromNoScript = function (content) { - let noscript = null; + let noscript = null; - try { + 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') { + } 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; - } + } - let out = new Set(); - let reBad = /[a-z]+:\w*$/; - let reURL = /[a-z]+:\/\/([0-9a-z.-]+)/; - let directives = noscript.whitelist.split(/\s+/); + let out = new Set(); + let reBad = /[a-z]+:\w*$/; + let reURL = /[a-z]+:\/\/([0-9a-z.-]+)/; + let directives = noscript.whitelist.split(/\s+/); - for (let i=directives.length-1; i>=0; --i) { + for (let i=directives.length-1; i>=0; --i) { let directive = directives[i].trim(); if (directive === '') { - continue; + continue; } if (reBad.test(directive)) { - continue; + continue; } let matches = reURL.exec(directive); if (matches !== null) { - directive = matches[1]; + directive = matches[1]; } 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'); }; let handleImportFilePicker = function () { - let fileReaderOnLoadHandler = function () { + let fileReaderOnLoadHandler = function () { if (typeof this.result !== 'string' || this.result === '') { - return; + return; } let result = fromRequestPolicy(this.result); if (result === undefined) { - result = fromNoScript(this.result); - if (result === undefined) { + result = fromNoScript(this.result); + if (result === undefined) { result = this.result; - } + } } if (this.result === '') { - return; - } + return; + } let request = { - 'what': 'setUserRules', - 'temporaryRules': rulesFromHTML('#diff .right li') - + '\n' + result, + 'what': 'setUserRules', + 'temporaryRules': rulesFromHTML('#diff .right li') + + '\n' + result, }; vAPI.messaging.send('user-rules.js', request, processUserRules); - }; + }; - var file = this.files[0]; - if (file === undefined || file.name === '') { + var file = this.files[0]; + if (file === undefined || file.name === '') { return; - } + } - if (file.type.indexOf('text') !== 0 - && file.type !== 'application/json') { + if (file.type.indexOf('text') !== 0 + && file.type !== 'application/json') { return; - } + } - let fr = new FileReader(); - fr.onload = fileReaderOnLoadHandler; - fr.readAsText(file); + let fr = new FileReader(); + fr.onload = fileReaderOnLoadHandler; + fr.readAsText(file); }; let startImportFilePicker = function () { - let 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(); + let 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({ + vAPI.download({ 'url': 'data:text/plain,' - + encodeURIComponent(rulesFromHTML('#diff .left li') + '\n'), + + encodeURIComponent(rulesFromHTML('#diff .left li') + '\n'), 'filename': uDom('[data-i18n="userRulesDefaultFileName"]').text(), - }); + }); } var rulesFromHTML = function(selector) { - let rules = []; - let lis = uDom(selector); + let rules = []; + let lis = uDom(selector); - for (let i=0; i<lis.length; ++i) { + for (let i=0; i<lis.length; ++i) { let li = lis.at(i); if (li.hasClassName('toRemove')) { - rules.push(''); + rules.push(''); } else { - rules.push(li.text()); + rules.push(li.text()); } - } + } - return rules.join('\n'); + return rules.join('\n'); }; let revertHandler = function () { - let request = { + let request = { 'what': 'setUserRules', 'temporaryRules': rulesFromHTML('#diff .left li'), - }; + }; - vAPI.messaging.send('user-rules.js', request, processUserRules); + vAPI.messaging.send('user-rules.js', request, processUserRules); }; let commitHandler = function () { - var request = { + 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); }; let editStartHandler = function () { - uDom('#diff .right textarea').val(rulesFromHTML('#diff .right li')); - let parent = uDom(this).ancestors('#diff'); - parent.toggleClass('edit', true); + uDom('#diff .right textarea').val(rulesFromHTML('#diff .right li')); + let parent = uDom(this).ancestors('#diff'); + parent.toggleClass('edit', true); }; let editStopHandler = function () { - let parent = uDom(this).ancestors('#diff'); - parent.toggleClass('edit', false); + let parent = uDom(this).ancestors('#diff'); + parent.toggleClass('edit', false); - let request = { + let 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); }; let editCancelHandler = function () { - let parent = uDom(this).ancestors('#diff'); - parent.toggleClass('edit', false); + let parent = uDom(this).ancestors('#diff'); + parent.toggleClass('edit', false); }; let temporaryRulesToggler = function() { - var li = uDom(this); - li.toggleClass('toRemove'); + var li = uDom(this); + li.toggleClass('toRemove'); - let request = { + let 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'); + return rulesFromHTML('#diff .left li'); }; self.cloud.onPull = function (data, append) { - if (typeof data !== 'string') { - return; - } + if (typeof data !== 'string') { + return; + } - if (append) { + if (append) { data = rulesFromHTML('#diff .right li') + '\n' + data; - } + } - let request = { + let request = { 'what': 'setUserRules', 'temporaryRules': data, - }; + }; - vAPI.messaging.send('user-rules.js', request, processUserRules); + 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); + // 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 2dffa5e..0125e10 100644 --- a/js/usersettings.js +++ b/js/usersettings.js @@ -2,7 +2,7 @@ ηMatrix - a browser extension to black/white list requests. Copyright (C) 2014-2019 Raymond Hill - Copyright (C) 2019-2020 Alessio Vanni + Copyright (C) 2019-2022 Alessio Vanni This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see {http://www.gnu.org/licenses/}. - Home: https://libregit.spks.xyz/heckyel/ematrix + Home: https://gitlab.com/vannilla/ematrix uMatrix Home: https://github.com/gorhill/uMatrix */ diff --git a/js/utils.js b/js/utils.js index be6ed9c..d545e28 100644 --- a/js/utils.js +++ b/js/utils.js @@ -2,7 +2,7 @@ ηMatrix - a browser extension to black/white list requests. Copyright (C) 2014-2019 Raymond Hill - Copyright (C) 2019-2020 Alessio Vanni + Copyright (C) 2019-2022 Alessio Vanni This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see {http://www.gnu.org/licenses/}. - Home: https://libregit.spks.xyz/heckyel/ematrix + Home: https://gitlab.com/vannilla/ematrix uMatrix Home: https://github.com/gorhill/uMatrix */ diff --git a/js/vapi-background.js b/js/vapi-background.js index ff64178..2e79839 100644 --- a/js/vapi-background.js +++ b/js/vapi-background.js @@ -2,7 +2,7 @@ ηMatrix - a browser extension to black/white list requests. Copyright (C) 2014-2019 The uMatrix/uBlock Origin authors - Copyright (C) 2019-2020 Alessio Vanni + Copyright (C) 2019-2022 Alessio Vanni This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see {http://www.gnu.org/licenses/}. - Home: https://libregit.spks.xyz/heckyel/ematrix + Home: https://gitlab.com/vannilla/ematrix uMatrix Home: https://github.com/gorhill/uMatrix */ @@ -38,53 +38,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', @@ -106,434 +106,434 @@ 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 () { 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); - }, - handleRequest: function (channel, URI, tabId, rawType) { + this.contractID, + false); + }, + 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 - } + .getInterface(Ci.nsILoadContext); + } catch(ex) { + // Ignore + } - if (!lc) { + 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; - }, - operate: function (channel, URI, topic) { - let channelData = this.channelDataFromChannel(channel); + }, + operate: function (channel, URI, topic) { + let channelData = this.channelDataFromChannel(channel); - if (topic.lastIndexOf('http-on-examine-', 0) === 0) { - if (channelData === null) { + if (topic.lastIndexOf('http-on-examine-', 0) === 0) { + 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 + // 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. let tabId; let pendingRequest = - PendingRequestBuffer.lookupRequest(URI.asciiSpec); + PendingRequestBuffer.lookupRequest(URI.asciiSpec); let rawType = 1; let loadInfo = channel.loadInfo; // 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]); - }, - 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 (ηMatrix.userSettings.resolveCname === true) { - let key = URI.scheme + '://' + URI.host; - if (HostMap.get(key)) { - this.operate(channel, HostMap.get(key), topic); - return; - } - - let CC = Components.classes; - let CI = Components.interfaces; - - let tab = this.tabIdFromChannel(channel); - - let dns = CC['@mozilla.org/network/dns-service;1'] - .createInstance(CI.nsIDNSService); - - let listener = { - onLookupComplete: function (req, rec, stat) { - if (!Components.isSuccessCode(stat)) { - console.error("can't resolve canonical name"); - return; - } - let addr = rec.canonicalName; - - ηMatrix.logger.writeOne(tab, 'info', - URI.host + ' => ' + addr); - - let ios = CC['@mozilla.org/network/io-service;1'] - .createInstance(CI.nsIIOService); - - let uri = ios.newURI(URI.scheme+'://'+addr, null, null); - - HostMap.put(key, uri); - - vAPI.httpObserver.operate(channel, uri, topic); - }, - }; - - dns.asyncResolve(URI.host, - CI.nsIDNSService.RESOLVE_CANONICAL_NAME, - listener, - null); - } else { - this.operate(channel, URI, topic); - } - }, - asyncOnChannelRedirect: function (oldChannel, newChannel, - flags, callback) { - // contentPolicy.shouldLoad doesn't detect redirects, this - // needs to be used + if (ηMatrix.userSettings.resolveCname === true) { + let key = URI.scheme + '://' + URI.host; + if (HostMap.get(key)) { + this.operate(channel, HostMap.get(key), topic); + return; + } + + let CC = Components.classes; + let CI = Components.interfaces; + + let tab = this.tabIdFromChannel(channel); + + let dns = CC['@mozilla.org/network/dns-service;1'] + .createInstance(CI.nsIDNSService); + + let listener = { + onLookupComplete: function (req, rec, stat) { + if (!Components.isSuccessCode(stat)) { + console.error("can't resolve canonical name"); + return; + } + let addr = rec.canonicalName; + + ηMatrix.logger.writeOne(tab, 'info', + URI.host + ' => ' + addr); + + let ios = CC['@mozilla.org/network/io-service;1'] + .createInstance(CI.nsIIOService); + + let uri = ios.newURI(URI.scheme+'://'+addr, null, null); + + HostMap.put(key, uri); + + vAPI.httpObserver.operate(channel, uri, topic); + }, + }; + + dns.asyncResolve(URI.host, + CI.nsIDNSService.RESOLVE_CANONICAL_NAME, + listener, + null); + } else { + this.operate(channel, URI, topic); + } + }, + 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: '' }; (function () { - 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]; @@ -541,19 +541,19 @@ button.classList.toggle('off', !icon || !icon.img); let iconId = (ηMatrix.userSettings.disableUpdateIcon) ? - icon && icon.img ? '19' : 'off' : - icon && icon.img ? icon.img : 'off'; + icon && icon.img ? '19' : 'off' : + icon && icon.img ? icon.img : 'off'; icon = 'url(' - + vAPI.getURL('img/browsericons/icon19-' - + iconId - + '.png') - + ')'; + + vAPI.getURL('img/browsericons/icon19-' + + iconId + + '.png') + + ')'; - button.style.listStyleImage = icon; - }; + button.style.listStyleImage = icon; + }; - tbb.populatePanel = function (doc, panel) { + tbb.populatePanel = function (doc, panel) { panel.setAttribute('id', this.viewId); let iframe = doc.createElement('iframe'); @@ -562,135 +562,130 @@ 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 body = win.document.body; + body.removeAttribute('data-resize-popup'); - let mutationObserver = - new win.MutationObserver(onResizeRequested); + let mutationObserver = + new win.MutationObserver(onResizeRequested); - mutationObserver.observe(body, { + 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}") { + let tbb = vAPI.toolbarButton; + if (tbb.init !== null) { return; - } + } - let tbb = vAPI.toolbarButton; - if (tbb.init !== null) { - return; - } + tbb.codePath = 'legacy'; + tbb.viewId = tbb.id + '-panel'; - tbb.codePath = 'legacy'; - tbb.viewId = tbb.id + '-panel'; + let styleSheetUri = null; - let styleSheetUri = null; - - let createToolbarButton = function (window) { + let createToolbarButton = function (window) { let document = window.document; let toolbarButton = document.createElement('toolbarbutton'); @@ -699,40 +694,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; @@ -740,21 +735,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. @@ -762,75 +757,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://gitlab.com/vannilla/ematrix/issues/5 - // https://gitlab.com/vannilla/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://gitlab.com/vannilla/ematrix/issues/5 + // https://gitlab.com/vannilla/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 @@ -839,386 +834,200 @@ // 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)); - }; - - tbb.init = function () { - vAPI.messaging.globalMessageManager - .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) { - 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) { - return null; - } - tbb.codePath = 'australis'; - tbb.CustomizableUI = CustomizableUI; - tbb.defaultArea = CustomizableUI.AREA_NAVBAR; - - let CUIEvents = {}; - - let badgeCSSRules = 'background: #000;color: #fff'; - - let updateBadgeStyle = function () { - for (let win of vAPI.window.getWindows()) { - let button = win.document.getElementById(tbb.id); - if (button === null) { - continue; - } - let badge = button.ownerDocument - .getAnonymousElementByAttribute(button, - 'class', - 'toolbarbutton-badge'); - if (!badge) { - continue; - } - - badge.style.cssText = badgeCSSRules; - } - }; - - let updateBadge = function () { - let wId = tbb.id; - let buttonInPanel = - CustomizableUI.getWidget(wId).areaType - === CustomizableUI.TYPE_MENU_PANEL; - - for (let win of vAPI.window.getWindows()) { - let button = win.document.getElementById(wId); - if (button === null) { - continue; - } - - if (buttonInPanel) { - button.classList.remove('badged-button'); - continue; - } - - button.classList.add('badged-button'); - } - - if (buttonInPanel) { - return; - } - - // Anonymous elements need some time to be reachable - vAPI.setTimeout(updateBadgeStyle, 250); - }.bind(CUIEvents); - - CUIEvents.onCustomizeEnd = updateBadge; - CUIEvents.onWidgetAdded = updateBadge; - CUIEvents.onWidgetUnderflow = updateBadge; - - let onPopupCloseRequested = function ({target}) { - if (typeof tbb.closePopup === 'function') { - tbb.closePopup(target); - } - }; - - let shutdown = function () { - for (let win of vAPI.window.getWindows()) { - let panel = win.document.getElementById(tbb.viewId); - if (panel !== null && panel.parentNode !== null) { - panel.parentNode.removeChild(panel); - } - - win.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils) - .removeSheet(styleURI, 1); - } - - CustomizableUI.removeListener(CUIEvents); - CustomizableUI.destroyWidget(tbb.id); - - vAPI.messaging.globalMessageManager - .removeMessageListener(location.host + ':closePopup', - onPopupCloseRequested); - }; - - let styleURI = null; - - tbb.onBeforeCreated = function (doc) { - let panel = doc.createElement('panelview'); - - this.populatePanel(doc, panel); - - doc.getElementById('PanelUI-multiView').appendChild(panel); - - doc.defaultView.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils) - .loadSheet(styleURI, 1); - }; - - tbb.onCreated = function (button) { - button.setAttribute('badge', ''); - vAPI.setTimeout(updateBadge, 250); - }; - - 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); - } catch (ex) { - // Ignore - } - }; - - tbb.closePopup = function (tabBrowser) { - CustomizableUI.hidePanelForNode(tabBrowser - .ownerDocument - .getElementById(tbb.viewId)); - }; + addLegacyToolbarButton.bind(null, win)); + }; - tbb.init = function () { + tbb.init = function () { vAPI.messaging.globalMessageManager - .addMessageListener(location.host + ':closePopup', - onPopupCloseRequested); - - CustomizableUI.addListener(CUIEvents); - - var style = [ - '#' + this.id + '.off {', - 'list-style-image: url(', - vAPI.getURL('img/browsericons/icon19-off.png'), - ');', - '}', - '#' + this.id + ' {', - 'list-style-image: url(', - vAPI.getURL('img/browsericons/icon19-19.png'), - ');', - '}', - '#' + 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); - - CustomizableUI.createWidget(this); + .addMessageListener(location.host + ':closePopup', + onPopupCloseRequested); 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, @@ -1227,16 +1036,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 48474f9..2d92498 100644 --- a/js/vapi-browser.js +++ b/js/vapi-browser.js @@ -2,7 +2,7 @@ ηMatrix - a browser extension to black/white list requests. Copyright (C) 2014-2019 The uMatrix/uBlock Origin authors - Copyright (C) 2019-2020 Alessio Vanni + Copyright (C) 2019-2022 Alessio Vanni This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see {http://www.gnu.org/licenses/}. - Home: https://libregit.spks.xyz/heckyel/ematrix + Home: https://gitlab.com/vannilla/ematrix uMatrix Home: https://github.com/gorhill/uMatrix */ @@ -29,48 +29,48 @@ 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]; @@ -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'); + 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) { + // 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 40598b8..4635853 100644 --- a/js/vapi-client.js +++ b/js/vapi-client.js @@ -2,7 +2,7 @@ ηMatrix - a browser extension to black/white list requests. Copyright (C) 2014-2019 The uMatrix/uBlock Origin authors - Copyright (C) 2019-2020 Alessio Vanni + Copyright (C) 2019-2022 Alessio Vanni This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see {http://www.gnu.org/licenses/}. - Home: https://libregit.spks.xyz/heckyel/ematrix + Home: https://gitlab.com/vannilla/ematrix uMatrix Home: https://github.com/gorhill/uMatrix */ @@ -27,66 +27,66 @@ (function (self) { if (self.vAPI === undefined) { - self.vAPI = {}; + self.vAPI = {}; } let vAPI = self.vAPI; vAPI.setTimeout = vAPI.setTimeout || function (callback, delay, extra) { - return setTimeout(function (a) { - callback(a); - }, delay, extra); + return setTimeout(function (a) { + callback(a); + }, delay, extra); }; 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(); @@ -94,100 +94,100 @@ 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() @@ -197,8 +197,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 70d7907..c86f351 100644 --- a/js/vapi-cloud.js +++ b/js/vapi-cloud.js @@ -2,7 +2,7 @@ ηMatrix - a browser extension to black/white list requests. Copyright (C) 2014-2019 The uMatrix/uBlock Origin authors - Copyright (C) 2019-2020 Alessio Vanni + Copyright (C) 2019-2022 Alessio Vanni This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see {http://www.gnu.org/licenses/}. - Home: https://libregit.spks.xyz/heckyel/ematrix + Home: https://gitlab.com/vannilla/ematrix uMatrix Home: https://github.com/gorhill/uMatrix */ @@ -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); - } + iss, argstr); + } - syncBranch.setBoolPref(cloudBranchPath + '.' + dataKey, true); + 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 db48206..d541070 100644 --- a/js/vapi-common.js +++ b/js/vapi-common.js @@ -2,7 +2,7 @@ ηMatrix - a browser extension to black/white list requests. Copyright (C) 2014-2019 The uMatrix/uBlock Origin authors - Copyright (C) 2019-2020 Alessio Vanni + Copyright (C) 2019-2022 Alessio Vanni This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see {http://www.gnu.org/licenses/}. - Home: https://libregit.spks.xyz/heckyel/ematrix + Home: https://gitlab.com/vannilla/ematrix uMatrix Home: https://github.com/gorhill/uMatrix */ @@ -32,86 +32,86 @@ Cu.import('resource://gre/modules/Services.jsm'); (function (self) { if (self.vAPI === undefined) { - self.vAPI = vAPI; + self.vAPI = vAPI; } 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'; + let dir = + ['ar', 'he', 'fa', 'ps', 'ur'].indexOf(language) !== -1 + ? 'rtl' + : 'ltr'; - document.body.setAttribute('dir', dir); + 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 @@ -119,50 +119,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 271e30b..9e20b23 100644 --- a/js/vapi-contextmenu.js +++ b/js/vapi-contextmenu.js @@ -2,7 +2,7 @@ ηMatrix - a browser extension to black/white list requests. Copyright (C) 2014-2019 The uMatrix/uBlock Origin authors - Copyright (C) 2019-2020 Alessio Vanni + Copyright (C) 2019-2022 Alessio Vanni This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see {http://www.gnu.org/licenses/}. - Home: https://libregit.spks.xyz/heckyel/ematrix + Home: https://gitlab.com/vannilla/ematrix uMatrix Home: https://github.com/gorhill/uMatrix */ @@ -27,73 +27,73 @@ (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'); @@ -107,106 +107,106 @@ 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 c694b8d..b6635a5 100644 --- a/js/vapi-cookies.js +++ b/js/vapi-cookies.js @@ -2,7 +2,7 @@ ηMatrix - a browser extension to black/white list requests. Copyright (C) 2014-2019 The uMatrix/uBlock Origin authors - Copyright (C) 2019-2020 Alessio Vanni + Copyright (C) 2019-2022 Alessio Vanni This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see {http://www.gnu.org/licenses/}. - Home: https://libregit.spks.xyz/heckyel/ematrix + Home: https://gitlab.com/vannilla/ematrix uMatrix Home: https://github.com/gorhill/uMatrix */ @@ -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, {}); + 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') { + 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 5d33a60..0a9cc0b 100644 --- a/js/vapi-core.js +++ b/js/vapi-core.js @@ -2,7 +2,7 @@ ηMatrix - a browser extension to black/white list requests. Copyright (C) 2014-2019 The uMatrix/uBlock Origin authors - Copyright (C) 2019-2020 Alessio Vanni + Copyright (C) 2019-2022 Alessio Vanni This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see {http://www.gnu.org/licenses/}. - Home: https://libregit.spks.xyz/heckyel/ematrix + Home: https://gitlab.com/vannilla/ematrix uMatrix Home: https://github.com/gorhill/uMatrix */ @@ -27,24 +27,24 @@ (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 @@ -57,72 +57,72 @@ 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 - Cu.import('chrome://ematrix/content/lib/FrameModule.jsm'); - contentObserver.unregister(); - Cu.unload('chrome://ematrix/content/lib/FrameModule.jsm'); + // frameModule needs to be cleared too + Cu.import('chrome://ematrix/content/lib/FrameModule.jsm'); + contentObserver.unregister(); + Cu.unload('chrome://ematrix/content/lib/FrameModule.jsm'); }); 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 afc18a1..5fb4b59 100644 --- a/js/vapi-messaging.js +++ b/js/vapi-messaging.js @@ -2,7 +2,7 @@ ηMatrix - a browser extension to black/white list requests. Copyright (C) 2014-2019 The uMatrix/uBlock Origin authors - Copyright (C) 2019-2020 Alessio Vanni + Copyright (C) 2019-2022 Alessio Vanni This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see {http://www.gnu.org/licenses/}. - Home: https://libregit.spks.xyz/heckyel/ematrix + Home: https://gitlab.com/vannilla/ematrix uMatrix Home: https://github.com/gorhill/uMatrix */ @@ -29,109 +29,109 @@ Cu.import('chrome://ematrix/content/lib/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; - }; - } + return vAPI.messaging.UNHANDLED; + }; + } - this.defaultHandler = defaultHandler; - this.globalMessageManager.addMessageListener(location.host - + ':background', - this.onMessage); - this.globalMessageManager.loadFrameScript(this.frameScript, true); + this.defaultHandler = defaultHandler; + this.globalMessageManager.addMessageListener(location.host + + ':background', + this.onMessage); + this.globalMessageManager.loadFrameScript(this.frameScript, true); - vAPI.addCleanUpTask(function () { + 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 41cbc4f..9212c7b 100644 --- a/js/vapi-net.js +++ b/js/vapi-net.js @@ -2,7 +2,7 @@ ηMatrix - a browser extension to black/white list requests. Copyright (C) 2014-2019 The uMatrix/uBlock Origin authors - Copyright (C) 2019-2020 Alessio Vanni + Copyright (C) 2019-2022 Alessio Vanni This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see {http://www.gnu.org/licenses/}. - Home: https://libregit.spks.xyz/heckyel/ematrix + Home: https://gitlab.com/vannilla/ematrix uMatrix Home: https://github.com/gorhill/uMatrix */ @@ -31,42 +31,42 @@ vAPI.net = {}; vAPI.net.registerListeners = function () { - this.onBeforeRequest.types = this.onBeforeRequest.types - ? new Set(this.onBeforeRequest.types) - : null; + 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.onBeforeSendHeaders.types = this.onBeforeSendHeaders.types + ? new Set(this.onBeforeSendHeaders.types) + : null; - let shouldLoadListenerMessageName = location.host + ':shouldLoad'; - let shouldLoadListener = function (e) { + let shouldLoadListenerMessageName = location.host + ':shouldLoad'; + let shouldLoadListener = function (e) { let details = e.data; let pendingReq = - PendingRequestBuffer.createRequest(details.url); + PendingRequestBuffer.createRequest(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-popup.js b/js/vapi-popup.js index 0859e81..12280e7 100644 --- a/js/vapi-popup.js +++ b/js/vapi-popup.js @@ -2,7 +2,7 @@ ηMatrix - a browser extension to black/white list requests. Copyright (C) 2014-2019 The uMatrix/uBlock Origin authors - Copyright (C) 2019-2020 Alessio Vanni + Copyright (C) 2019-2022 Alessio Vanni This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see {http://www.gnu.org/licenses/}. - Home: https://libregit.spks.xyz/heckyel/ematrix + Home: https://gitlab.com/vannilla/ematrix uMatrix Home: https://github.com/gorhill/uMatrix */ diff --git a/js/vapi-storage.js b/js/vapi-storage.js index 8e46b90..bcc3a9a 100644 --- a/js/vapi-storage.js +++ b/js/vapi-storage.js @@ -2,7 +2,7 @@ ηMatrix - a browser extension to black/white list requests. Copyright (C) 2014-2019 The uMatrix/uBlock Origin authors - Copyright (C) 2019-2020 Alessio Vanni + Copyright (C) 2019-2022 Alessio Vanni This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see {http://www.gnu.org/licenses/}. - Home: https://libregit.spks.xyz/heckyel/ematrix + Home: https://gitlab.com/vannilla/ematrix uMatrix Home: https://github.com/gorhill/uMatrix */ @@ -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 02c3644..e5eab6b 100644 --- a/js/vapi-tabs.js +++ b/js/vapi-tabs.js @@ -2,7 +2,7 @@ ηMatrix - a browser extension to black/white list requests. Copyright (C) 2014-2019 The uMatrix/uBlock Origin authors - Copyright (C) 2019-2020 Alessio Vanni + Copyright (C) 2019-2022 Alessio Vanni This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see {http://www.gnu.org/licenses/}. - Home: https://libregit.spks.xyz/heckyel/ematrix + Home: https://gitlab.com/vannilla/ematrix uMatrix Home: https://github.com/gorhill/uMatrix */ @@ -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); + let browser = vAPI.tabs.manager.browserFromTarget(tab); - // https://github.com/gorhill/uBlock/issues/2558 - if (browser === null) { - continue; - } + // 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) { + // Or simply .equals if we care about the fragment + if (URI.equalsExceptRef(browser.currentURI) === false) { continue; - } + } - this.select(tab); + this.select(tab); - // Update URL if fragment is different - if (URI.equals(browser.currentURI) === false) { + // 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); + let win = vAPI.window.getCurrentWindow(); + let tabBrowser = vAPI.browser.getTabBrowser(win); - if (tabBrowser === null) { + 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(); + for (let [tabId, weakref] of tabIdToBrowserMap) { + let browser = weakref.get(); - // TODO: Maybe call removeBrowserEntry() if the - // browser no longer exists? - if (browser) { + // 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,20 +579,20 @@ // 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); + 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. + 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 @@ -602,28 +602,28 @@ // 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); + onWindowLoad(win); - tabBrowser = vAPI.browser.getTabBrowser(win); - if (tabBrowser === null) { + 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 9e35db8..c7baede 100644 --- a/js/vapi-window.js +++ b/js/vapi-window.js @@ -2,7 +2,7 @@ ηMatrix - a browser extension to black/white list requests. Copyright (C) 2014-2019 The uMatrix/uBlock Origin authors - Copyright (C) 2019-2020 Alessio Vanni + Copyright (C) 2019-2022 Alessio Vanni This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see {http://www.gnu.org/licenses/}. - Home: https://libregit.spks.xyz/heckyel/ematrix + Home: https://gitlab.com/vannilla/ematrix uMatrix Home: https://github.com/gorhill/uMatrix */ @@ -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 - } + } catch (e) { + // Ignore + } - addWindow(win); + addWindow(win); }, onCloseWindow: function (aWindow) { - let win; - try { + let win; + try { win = aWindow.QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIDOMWindow); - } catch (e) { - // Ignore - } + } catch (e) { + // Ignore + } - removeWindow(win); + 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(); + let win = winumerator.getNext(); - if (!win.closed) { + 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; })(); })(); @@ -2,7 +2,7 @@ ηMatrix - a browser extension to black/white list requests. Copyright (C) 2014-2019 Raymond Hill - Copyright (C) 2019-2020 Alessio Vanni + Copyright (C) 2019-2022 Alessio Vanni This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see {http://www.gnu.org/licenses/}. - Home: https://libregit.spks.xyz/heckyel/ematrix + Home: https://gitlab.com/vannilla/ematrix uMatrix Home: https://github.com/gorhill/uMatrix */ @@ -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; - /******************************************************************************/ +/******************************************************************************/ })(); |