aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesús <heckyel@hyperbola.info>2019-12-30 15:55:13 -0500
committerJesús <heckyel@hyperbola.info>2019-12-30 15:55:13 -0500
commit288df6a7bf8b933e2dc499e38f4915fcf974c14b (patch)
tree77bba994f260c064d3ee7f76c427ddfaa4f91710
parenta2c9deaa145b780722e93b3899600f287c8094a4 (diff)
downloadematrix-288df6a7bf8b933e2dc499e38f4915fcf974c14b.tar.lz
ematrix-288df6a7bf8b933e2dc499e38f4915fcf974c14b.tar.xz
ematrix-288df6a7bf8b933e2dc499e38f4915fcf974c14b.zip
backport
- Flush caches on upgrade - Properly handle FrameModule's unloading - Use the new module and remove the old implementation
-rw-r--r--Makefile31
-rw-r--r--background.html10
-rw-r--r--bootstrap.js127
-rw-r--r--chrome.manifest1
-rw-r--r--dashboard.html16
-rw-r--r--frameScript.js43
-rw-r--r--hosts-files.html1
-rw-r--r--install.rdf12
-rw-r--r--js/asset-viewer.js21
-rw-r--r--js/background.js94
-rw-r--r--js/browsercache.js28
-rw-r--r--js/cloud-ui.js140
-rw-r--r--js/contentscript-start.js72
-rw-r--r--js/contentscript.js517
-rw-r--r--js/cookies.js464
-rw-r--r--js/dashboard-common.js16
-rw-r--r--js/dashboard.js24
-rw-r--r--js/hosts-files.js407
-rw-r--r--js/httpsb.js8
-rw-r--r--js/i18n.js92
-rw-r--r--js/logger-ui.js706
-rw-r--r--js/logger.js40
-rw-r--r--js/matrix.js16
-rw-r--r--js/messaging.js17
-rw-r--r--js/pagestats.js6
-rw-r--r--js/popup.js1134
-rw-r--r--js/settings.js139
-rw-r--r--js/start.js64
-rw-r--r--js/storage.js8
-rw-r--r--js/tab.js658
-rw-r--r--js/traffic.js10
-rw-r--r--js/user-rules.js280
-rw-r--r--js/vapi-background.js112
-rw-r--r--js/vapi-core.js9
-rw-r--r--js/vapi-messaging.js2
-rw-r--r--js/vapi-net.js5
-rw-r--r--lib/CallbackWrapper.jsm (renamed from CallbackWrapper.jsm)4
-rw-r--r--lib/CookieCache.jsm171
-rw-r--r--lib/FrameModule.jsm (renamed from frameModule.js)194
-rw-r--r--lib/HttpRequestHeaders.jsm (renamed from HttpRequestHeaders.jsm)4
-rw-r--r--lib/LiquidDict.jsm175
-rw-r--r--lib/PendingRequests.jsm97
-rw-r--r--lib/PublicSuffixList.jsm308
-rw-r--r--lib/Punycode.jsm305
-rw-r--r--lib/UriTools.jsm405
-rw-r--r--locale/ar/messages.properties182
-rw-r--r--logger-ui.html2
-rw-r--r--main-blocked.html2
-rw-r--r--popup.html2
-rw-r--r--raw-settings.html1
-rw-r--r--settings.html2
-rw-r--r--user-rules.html2
52 files changed, 4098 insertions, 3088 deletions
diff --git a/Makefile b/Makefile
index e232c82..df9e481 100644
--- a/Makefile
+++ b/Makefile
@@ -26,7 +26,7 @@
# 2) It gives some flexibility in that files can be created or deleted
# freely as long as they are not part of any of these lists
-VERSION := 3.0.8
+VERSION := 3.0.9
help:
@echo 'Makefile for generate eMatrix '
@@ -46,7 +46,7 @@ ROOTHTML := about.html asset-viewer.html background.html \
logger-ui.html main-blocked.html popup.html \
raw-settings.html settings.html user-rules.html
-ROOTOTHER := frameScript.js frameModule.js
+ROOTOTHER := frameScript.js
ROOT := $(ROOTMETA) $(ROOTHTML) $(ROOTOTHER)
@@ -97,14 +97,17 @@ IMGOTHER := img/cloud.png img/icon_16.png img/icon_64.png \
IMG := $(IMGICON) $(IMGOTHER)
-LIB := lib/publicsuffixlist.js lib/punycode.js lib/yamd5.js
+LIB := lib/PublicSuffixList.jsm lib/Punycode.jsm lib/FrameModule.jsm \
+ lib/CallbackWrapper.jsm lib/HttpRequestHeaders.jsm \
+ lib/PendingRequests.jsm lib/CookieCache.jsm lib/UriTools.jsm \
+ lib/LiquidDict.jsm
# The locale folder is split into parts because it's a long list
# and it's somewhat easier to read this way
-LOC1 := locale/en/messages.properties locale/ar/messages.properties \
- locale/bg/messages.properties locale/bn/messages.properties \
- locale/cs/messages.properties locale/da/messages.properties \
- locale/de/messages.properties locale/el/messages.properties
+LOC1 := locale/en/messages.properties locale/bg/messages.properties \
+ locale/bn/messages.properties locale/cs/messages.properties \
+ locale/da/messages.properties locale/de/messages.properties \
+ locale/el/messages.properties
LOC2 := locale/eo/messages.properties locale/es/messages.properties \
locale/et/messages.properties locale/fa/messages.properties \
@@ -138,15 +141,13 @@ JS1 := js/about.js js/assets.js js/asset-viewer.js js/background.js \
js/contentscript-start.js js/cookies.js js/dashboard-common.js \
js/dashboard.js js/hosts-files.js
-JS2 := js/httpsb.js js/i18n.js js/liquid-dict.js js/logger.js \
- js/logger-ui.js js/main-blocked.js js/matrix.js \
- js/messaging.js js/pagestats.js js/polyfill.js js/popup.js \
- js/profiler.js js/raw-settings.js js/settings.js js/start.js \
- js/storage.js
+JS2 := js/httpsb.js js/i18n.js js/logger.js js/logger-ui.js \
+ js/main-blocked.js js/matrix.js js/messaging.js \
+ js/pagestats.js js/popup.js js/profiler.js js/raw-settings.js \
+ js/settings.js js/start.js js/storage.js
-JS3 := js/tab.js js/traffic.js js/udom.js js/uritools.js \
- js/user-rules.js js/usersettings.js js/utils.js js/xal.js \
- CallbackWrapper.jsm HttpRequestHeaders.jsm
+JS3 := js/tab.js js/traffic.js js/udom.js js/user-rules.js \
+ js/usersettings.js js/utils.js js/xal.js
JS4 := js/vapi-client.js js/vapi-common.js js/vapi-background.js \
js/vapi-tabs.js js/vapi-window.js js/vapi-core.js \
diff --git a/background.html b/background.html
index 9ad8fc6..5759071 100644
--- a/background.html
+++ b/background.html
@@ -2,6 +2,7 @@
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- ***************************************************************************
ηMatrix - a browser extension to black/white list requests.
@@ -23,14 +24,11 @@
Home: https://libregit.org/heckyel/ematrix
uMatrix Home: https://github.com/gorhill/uMatrix
-->
- <title>eMatrix</title>
+ <title>ηMatrix</title>
</head>
<body>
<!-- Each script (or anyway the majority of them) depends on the
- scripts above it, so keep these ordered like this -->
- <script src="js/polyfill.js"></script>
- <script src="lib/punycode.js"></script>
- <script src="lib/publicsuffixlist.js"></script>
+ scripts above it, so keep these ordered like this -->
<script src="js/vapi-common.js"></script>
<script src="js/vapi-core.js"></script>
<script src="js/vapi-window.js"></script>
@@ -45,12 +43,10 @@
<script src="js/background.js"></script>
<script src="js/xal.js"></script>
<script src="js/usersettings.js"></script>
- <script src="js/liquid-dict.js"></script>
<script src="js/matrix.js"></script>
<script src="js/utils.js"></script>
<script src="js/assets.js"></script>
<script src="js/httpsb.js"></script>
- <script src="js/uritools.js"></script>
<script src="js/cookies.js"></script>
<script src="js/logger.js"></script>
<script src="js/messaging.js"></script>
diff --git a/bootstrap.js b/bootstrap.js
index b0cdc91..3134dbe 100644
--- a/bootstrap.js
+++ b/bootstrap.js
@@ -21,13 +21,8 @@
uMatrix Home: https://github.com/gorhill/uMatrix
*/
-/* global ADDON_UNINSTALL, APP_SHUTDOWN */
-/* exported startup, shutdown, install, uninstall */
-
'use strict';
-/******************************************************************************/
-
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
// Accessing the context of the background page:
@@ -38,10 +33,11 @@ let windowlessBrowserPL = null;
let bgProcess = null;
let version;
const hostName = 'ematrix';
+
const restartListener = {
get messageManager() {
return Cc['@mozilla.org/parentprocessmessagemanager;1']
- .getService(Ci.nsIMessageListenerManager);
+ .getService(Ci.nsIMessageListenerManager);
},
receiveMessage: function() {
@@ -50,19 +46,17 @@ const restartListener = {
}
};
-/******************************************************************************/
-
// https://github.com/gorhill/uBlock/issues/2493
// Fix by https://github.com/gijsk
// imported from https://github.com/gorhill/uBlock/pull/2497
-function startup(data/*, reason*/) {
- if ( data !== undefined ) {
+function startup(data, reason) {
+ if (data !== undefined) {
version = data.version;
}
// Already started?
- if ( bgProcess !== null ) {
+ if (bgProcess !== null) {
return;
}
@@ -70,45 +64,58 @@ function startup(data/*, reason*/) {
}
function createBgProcess(parentDocument) {
- bgProcess = parentDocument.documentElement.appendChild(
- parentDocument.createElementNS('http://www.w3.org/1999/xhtml', 'iframe')
- );
- bgProcess.setAttribute(
- 'src',
- 'chrome://' + hostName + '/content/background.html#' + version
- );
+ bgProcess = parentDocument
+ .documentElement
+ .appendChild(parentDocument
+ .createElementNS('http://www.w3.org/1999/xhtml',
+ 'iframe'));
+
+ bgProcess.setAttribute('src',
+ 'chrome://' + hostName
+ + '/content/background.html#' + version);
// https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIMessageListenerManager#addMessageListener%28%29
// "If the same listener registers twice for the same message, the
// "second registration is ignored."
- restartListener.messageManager.addMessageListener(
- hostName + '-restart',
- restartListener
- );
+ restartListener
+ .messageManager
+ .addMessageListener(hostName + '-restart', restartListener);
}
function getWindowlessBrowserFrame(appShell) {
windowlessBrowser = appShell.createWindowlessBrowser(true);
windowlessBrowser.QueryInterface(Ci.nsIInterfaceRequestor);
+
let webProgress = windowlessBrowser.getInterface(Ci.nsIWebProgress);
- let XPCOMUtils = Cu.import('resource://gre/modules/XPCOMUtils.jsm', null).XPCOMUtils;
+ Cu.import('resource://gre/modules/XPCOMUtils.jsm');
+
windowlessBrowserPL = {
QueryInterface: XPCOMUtils.generateQI([
Ci.nsIWebProgressListener,
Ci.nsIWebProgressListener2,
Ci.nsISupportsWeakReference
]),
+
onStateChange: function(wbp, request, stateFlags, status) {
- if ( !request ) { return; }
- if ( stateFlags & Ci.nsIWebProgressListener.STATE_STOP ) {
+ if (!request) {
+ return;
+ }
+
+ if (stateFlags & Ci.nsIWebProgressListener.STATE_STOP) {
webProgress.removeProgressListener(windowlessBrowserPL);
windowlessBrowserPL = null;
createBgProcess(windowlessBrowser.document);
}
}
};
- webProgress.addProgressListener(windowlessBrowserPL, Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT);
- windowlessBrowser.document.location = "data:application/vnd.mozilla.xul+xml;charset=utf-8,<window%20id='" + hostName + "-win'/>";
+
+ webProgress.addProgressListener(windowlessBrowserPL,
+ Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT);
+
+ windowlessBrowser.document.location =
+ "data:application/vnd.mozilla.xul+xml;charset=utf-8,<window%20id='"
+ + hostName
+ + "-win'/>";
}
function waitForHiddenWindow() {
@@ -120,13 +127,13 @@ function waitForHiddenWindow() {
try {
hiddenDoc = appShell.hiddenDOMWindow &&
- appShell.hiddenDOMWindow.document;
+ appShell.hiddenDOMWindow.document;
} catch (ex) {
}
// Do not test against `loading`: it does appear `readyState` could be
// undefined if looked up too early.
- if ( !hiddenDoc || hiddenDoc.readyState !== 'complete' ) {
+ if (!hiddenDoc || hiddenDoc.readyState !== 'complete') {
return false;
}
@@ -139,11 +146,11 @@ function waitForHiddenWindow() {
// also what the webextension implementation in Firefox uses for
// background pages).
getWindowlessBrowserFrame(appShell);
-
+
return true;
};
- if ( isReady() ) {
+ if (isReady()) {
return;
}
@@ -153,21 +160,24 @@ function waitForHiddenWindow() {
let tryDelay = 5;
let trySum = 0;
+
// https://trac.torproject.org/projects/tor/ticket/19438
// Try for a longer period.
+ // ηMatrix: I doubt this applies to us...
let tryMax = 600011;
+
let timer = Cc['@mozilla.org/timer;1']
.createInstance(Ci.nsITimer);
let checkLater = function() {
trySum += tryDelay;
- if ( trySum >= tryMax ) {
+ if (trySum >= tryMax) {
timer = null;
return;
}
timer.init(timerObserver, tryDelay, timer.TYPE_ONE_SHOT);
tryDelay *= 2;
- if ( tryDelay > 503 ) {
+ if (tryDelay > 503) {
tryDelay = 503;
}
};
@@ -175,7 +185,8 @@ function waitForHiddenWindow() {
var timerObserver = {
observe: function() {
timer.cancel();
- if ( isReady() ) {
+
+ if (isReady()) {
timer = null;
} else {
checkLater();
@@ -186,58 +197,60 @@ function waitForHiddenWindow() {
checkLater();
}
-/******************************************************************************/
-
function shutdown(data, reason) {
- if ( reason === APP_SHUTDOWN ) {
+ if (reason === APP_SHUTDOWN) {
return;
}
- if ( bgProcess !== null ) {
+ if (bgProcess !== null) {
bgProcess.parentNode.removeChild(bgProcess);
bgProcess = null;
}
- if ( windowlessBrowser !== null ) {
+ if (windowlessBrowser !== null) {
// close() does not exist for older versions of Firefox.
- if ( typeof windowlessBrowser.close === 'function' ) {
+ // ηMatrix: how old? But keeping it doesn't really hurt that much.
+ if (typeof windowlessBrowser.close === 'function') {
windowlessBrowser.close();
}
windowlessBrowser = null;
windowlessBrowserPL = null;
}
- if ( data === undefined ) {
+ if (data === undefined) {
return;
}
// Remove the restartObserver only when the extension is being disabled
- restartListener.messageManager.removeMessageListener(
- hostName + '-restart',
- restartListener
- );
+ restartListener
+ .messageManager
+ .removeMessageListener(hostName + '-restart',
+ restartListener);
}
-/******************************************************************************/
-
-function install() {
+function install(data, reason) {
// https://bugzil.la/719376
Cc['@mozilla.org/intl/stringbundle;1']
- .getService(Ci.nsIStringBundleService)
- .flushBundles();
-}
+ .getService(Ci.nsIStringBundleService)
+ .flushBundles();
+
+ if (reason === ADDON_UPGRADE) {
+ let Services =
+ Cu.import('resource://gre/modules/Services.jsm', null).Services
-/******************************************************************************/
+ Services.obs.notifyObservers(null, 'chrome-flush-caches', null);
+ Services.obs.notifyObservers(null, 'message-manager-flush-caches', null);
+ }
+}
function uninstall(data, aReason) {
- if ( aReason !== ADDON_UNINSTALL ) {
+ if (aReason !== ADDON_UNINSTALL) {
return;
}
+
// To cleanup vAPI.localStorage in vapi-common.js, aka
// "extensions.ematrix.*" in `about:config`.
Cu.import('resource://gre/modules/Services.jsm', null)
- .Services.prefs.getBranch('extensions.' + hostName + '.')
- .deleteBranch('');
+ .Services.prefs.getBranch('extensions.' + hostName + '.')
+ .deleteBranch('');
}
-
-/******************************************************************************/
diff --git a/chrome.manifest b/chrome.manifest
index 919bf02..7463ee8 100644
--- a/chrome.manifest
+++ b/chrome.manifest
@@ -22,7 +22,6 @@
content ematrix ./
locale ematrix en ./locale/en/
-locale ematrix ar ./locale/ar/
locale ematrix bg ./locale/bg/
locale ematrix bn ./locale/bn/
locale ematrix cs ./locale/cs/
diff --git a/dashboard.html b/dashboard.html
index 209875b..4d19944 100644
--- a/dashboard.html
+++ b/dashboard.html
@@ -23,21 +23,21 @@
Home: https://libregit.org/heckyel/ematrix
uMatrix Home: https://github.com/gorhill/uMatrix
-->
- <meta name="viewport" content="width=device-width, initial-scale=1">
- <link rel="shortcut icon" type="image/png" href="img/icon_16.png">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title data-i18n="dashboardPageName"></title>
+ <link rel="shortcut icon" type="image/png" href="img/icon_16.png">
<link href="css/dashboard.css" rel="stylesheet" type="text/css">
<link href="css/common.css" rel="stylesheet" type="text/css">
</head>
<body>
<div id="dashboard-nav">
<div id="dashboard-nav-widgets">
- <span data-i18n="extName"></span>
- <a class="tabButton" id="settings" href="#settings" data-dashboard-panel-url="settings.html" data-i18n="settingsPageName"></a>
- <a class="tabButton" id="user-rules" href="#user-rules" data-dashboard-panel-url="user-rules.html" data-i18n="userRulesPageName"></a>
- <a class="tabButton" id="hosts-files" href="#hosts-files" data-dashboard-panel-url="hosts-files.html" data-i18n="ubiquitousRulesPageName"></a>
- <a class="tabButton" id="raw-settings" href="#raw-settings" data-dashboard-panel-url="raw-settings.html" data-i18n="rawSettingsPageName"></a>
- <a class="tabButton" id="about" href="#about" data-dashboard-panel-url="about.html" data-i18n="aboutPageName"></a>
+ <span data-i18n="extName"></span>
+ <a class="tabButton" id="settings" href="#settings" data-dashboard-panel-url="settings.html" data-i18n="settingsPageName"></a>
+ <a class="tabButton" id="user-rules" href="#user-rules" data-dashboard-panel-url="user-rules.html" data-i18n="userRulesPageName"></a>
+ <a class="tabButton" id="hosts-files" href="#hosts-files" data-dashboard-panel-url="hosts-files.html" data-i18n="ubiquitousRulesPageName"></a>
+ <a class="tabButton" id="raw-settings" href="#raw-settings" data-dashboard-panel-url="raw-settings.html" data-i18n="rawSettingsPageName"></a>
+ <a class="tabButton" id="about" href="#about" data-dashboard-panel-url="about.html" data-i18n="aboutPageName"></a>
</div>
</div>
<iframe src=""></iframe>
diff --git a/frameScript.js b/frameScript.js
index e4b2dbe..cc1a564 100644
--- a/frameScript.js
+++ b/frameScript.js
@@ -21,54 +21,43 @@
uMatrix Home: https://github.com/gorhill/uMatrix
*/
-/******************************************************************************/
+'use strict';
-var locationChangeListener; // Keep alive while frameScript is alive
-
-(function() {
-
- 'use strict';
+Components.utils.import('chrome://ematrix/content/lib/FrameModule.jsm');
- /******************************************************************************/
-
- let {contentObserver, LocationChangeListener} = Components.utils.import(
- Components.stack.filename.replace('Script', 'Module'),
- null
- );
+var locationChangeListener; // Keep alive while frameScript is alive
- let injectContentScripts = function(win) {
- if ( !win || !win.document ) {
+(function () {
+ let injectContentScripts = function (win) {
+ if (!win || !win.document) {
return;
}
contentObserver.observe(win.document);
- if ( win.frames && win.frames.length ) {
- let i = win.frames.length;
- while ( i-- ) {
+ if (win.frames && win.frames.length) {
+ for (let i = win.frames.length; i>0; --i) {
injectContentScripts(win.frames[i]);
}
}
};
- let onLoadCompleted = function() {
+ let onLoadCompleted = function () {
removeMessageListener('ematrix-load-completed', onLoadCompleted);
injectContentScripts(content);
};
addMessageListener('ematrix-load-completed', onLoadCompleted);
- if ( docShell ) {
+ if (docShell) {
let Ci = Components.interfaces;
- let wp = docShell.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebProgress);
+ let wp = docShell
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebProgress);
let dw = wp.DOMWindow;
- if ( dw === dw.top ) {
+
+ if (dw === dw.top) {
locationChangeListener = new LocationChangeListener(docShell);
}
- };
-
- /******************************************************************************/
-
+ }
})();
-
-/******************************************************************************/
diff --git a/hosts-files.html b/hosts-files.html
index b0cc6d6..da3854e 100644
--- a/hosts-files.html
+++ b/hosts-files.html
@@ -68,6 +68,5 @@
<script src="js/i18n.js"></script>
<script src="js/dashboard-common.js"></script>
<script src="js/hosts-files.js"></script>
-
</body>
</html>
diff --git a/install.rdf b/install.rdf
index e9f86da..76a85ac 100644
--- a/install.rdf
+++ b/install.rdf
@@ -22,7 +22,7 @@
<r:RDF xmlns:r="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.mozilla.org/2004/em-rdf#">
<r:Description about="urn:mozilla:install-manifest">
<id>eMatrix@vannilla.org</id>
- <version>3.0.8</version>
+ <version>3.0.9</version>
<name>ηMatrix</name>
<description>Point &amp; click to forbid/allow any class of requests made by your browser. Use it to block scripts, iframes, ads, facebook, etc.</description>
<!-- uMatrix homepageURL -->
@@ -363,16 +363,6 @@
</r:Description>
</targetApplication>
- <!-- SeaMonkey -->
- <!-- Untested for ηMatrix -->
- <targetApplication>
- <r:Description>
- <id>{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}</id>
- <minVersion>2.24</minVersion>
- <maxVersion>2.*</maxVersion>
- </r:Description>
- </targetApplication>
-
<!-- Pale Moon -->
<targetApplication>
<r:Description>
diff --git a/js/asset-viewer.js b/js/asset-viewer.js
index 1d88459..0185ff1 100644
--- a/js/asset-viewer.js
+++ b/js/asset-viewer.js
@@ -23,25 +23,20 @@
'use strict';
-/******************************************************************************/
-
(function() {
-
- var onAssetContentReceived = function(details) {
+ let onAssetContentReceived = function (details) {
document.getElementById('content').textContent =
details && (details.content || '');
};
- var q = window.location.search;
- var matches = q.match(/^\?url=([^&]+)/);
- if ( !matches || matches.length !== 2 ) {
+ let q = window.location.search;
+ let matches = q.match(/^\?url=([^&]+)/);
+ if (!matches || matches.length !== 2) {
return;
}
- vAPI.messaging.send(
- 'asset-viewer.js',
- { what : 'getAssetContent', url: matches[1] },
- onAssetContentReceived
- );
-
+ vAPI.messaging.send('asset-viewer.js', {
+ what : 'getAssetContent',
+ url: matches[1]
+ }, onAssetContentReceived);
})();
diff --git a/js/background.js b/js/background.js
index 07065b7..7ac9ef2 100644
--- a/js/background.js
+++ b/js/background.js
@@ -23,25 +23,19 @@
'use strict';
-/******************************************************************************/
+var ηMatrix = (function () {
+ Cu.import('chrome://ematrix/content/lib/LiquidDict.jsm');
-var ηMatrix = (function() { // jshint ignore:line
+ let oneSecond = 1000;
+ let oneMinute = 60 * oneSecond;
+ let oneHour = 60 * oneMinute;
+ let oneDay = 24 * oneHour;
- /******************************************************************************/
-
- var oneSecond = 1000;
- var oneMinute = 60 * oneSecond;
- var oneHour = 60 * oneMinute;
- var oneDay = 24 * oneHour;
-
- /******************************************************************************/
- /******************************************************************************/
-
- var _RequestStats = function() {
+ let _RequestStats = function () {
this.reset();
};
- _RequestStats.prototype.reset = function() {
+ _RequestStats.prototype.reset = function () {
this.all =
this.doc =
this.frame =
@@ -54,21 +48,19 @@ var ηMatrix = (function() { // jshint ignore:line
this.cookie = 0;
};
- /******************************************************************************/
-
- var RequestStats = function() {
- this.allowed = new _RequestStats();
- this.blocked = new _RequestStats();
+ var RequestStats = function () {
+ this.allowed = new _RequestStats ();
+ this.blocked = new _RequestStats ();
};
- RequestStats.prototype.reset = function() {
+ RequestStats.prototype.reset = function () {
this.blocked.reset();
this.allowed.reset();
};
- RequestStats.prototype.record = function(type, blocked) {
+ RequestStats.prototype.record = function (type, blocked) {
// Remember: always test against **false**
- if ( blocked !== false ) {
+ if (blocked !== false) {
this.blocked[type] += 1;
this.blocked.all += 1;
} else {
@@ -77,35 +69,32 @@ var ηMatrix = (function() { // jshint ignore:line
}
};
- var requestStatsFactory = function() {
+ var requestStatsFactory = function () {
return new RequestStats();
};
- /*******************************************************************************
-
- SVG-based icons below were extracted from
- fontawesome-webfont.svg v4.7. Excerpt of copyright notice at
- the top of the file:
-
- > Created by FontForge 20120731 at Mon Oct 24 17:37:40 2016
- > By ,,,
- > Copyright Dave Gandy 2016. All rights reserved.
+ /**
+ SVG-based icons below were extracted from
+ fontawesome-webfont.svg v4.7. Excerpt of copyright notice at
+ the top of the file:
- Excerpt of the license information in the fontawesome CSS
- file bundled with the package:
+ > Created by FontForge 20120731 at Mon Oct 24 17:37:40 2016
+ > By ,,,
+ > Copyright Dave Gandy 2016. All rights reserved.
- > Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome
- > License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
+ Excerpt of the license information in the fontawesome CSS
+ file bundled with the package:
- Font icons:
- - glyph-name: "external_link"
+ > Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome
+ > License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
+ Font icons:
+ - glyph-name: "external_link"
*/
var rawSettingsDefault = {
disableCSPReportInjection: false,
- placeholderBackground:
- [
+ placeholderBackground: [
'url("data:image/png;base64,',
'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAK',
'CAAAAACoWZBhAAAABGdBTUEAALGPC/xh',
@@ -125,8 +114,7 @@ var ηMatrix = (function() { // jshint ignore:line
imagePlaceholderBackground: 'default',
imagePlaceholderBorder: 'default',
framePlaceholder: true,
- framePlaceholderDocument:
- [
+ framePlaceholderDocument: [
'<html><head>',
'<meta charset="utf-8">',
'<style>',
@@ -160,8 +148,6 @@ var ηMatrix = (function() { // jshint ignore:line
framePlaceholderBackground: 'default',
};
- /******************************************************************************/
-
return {
onBeforeStartQueue: [],
@@ -186,7 +172,8 @@ var ηMatrix = (function() { // jshint ignore:line
popupCollapseBlacklistedDomains: false,
popupScopeLevel: 'domain',
processHyperlinkAuditing: true,
- processReferer: false
+ processReferer: false,
+ disableUpdateIcon: false,
},
rawSettingsDefault: rawSettingsDefault,
@@ -207,8 +194,8 @@ var ηMatrix = (function() { // jshint ignore:line
liveHostsFiles: {
},
- // urls stats are kept on the back burner while waiting to be reactivated
- // in a tab or another.
+ // urls stats are kept on the back burner while waiting to be
+ // reactivated in a tab or another.
pageStores: {},
pageStoresToken: 0,
pageStoreCemetery: {},
@@ -217,7 +204,8 @@ var ηMatrix = (function() { // jshint ignore:line
tMatrix: null,
pMatrix: null,
- ubiquitousBlacklist: null,
+ ubiquitousBlacklist: new LiquidDict(),
+ ubiquitousWhitelist: new LiquidDict(),
// various stats
requestStatsFactory: requestStatsFactory,
@@ -233,14 +221,6 @@ var ηMatrix = (function() { // jshint ignore:line
// record what the browser is doing behind the scene
behindTheSceneScope: 'behind-the-scene',
- noopFunc: function(){},
-
- // so that I don't have to care for last comma
- dummy: 0
+ noopFunc: function () {},
};
-
- /******************************************************************************/
-
})();
-
-/******************************************************************************/
diff --git a/js/browsercache.js b/js/browsercache.js
index 1360f92..bc2fdd2 100644
--- a/js/browsercache.js
+++ b/js/browsercache.js
@@ -21,46 +21,36 @@
uMatrix Home: https://github.com/gorhill/uMatrix
*/
-/* global ηMatrix */
-
'use strict';
-/******************************************************************************/
-
-(function() {
-
- /******************************************************************************/
-
+(function () {
// Browser data jobs
- var clearCache = function() {
+ let clearCache = function () {
vAPI.setTimeout(clearCache, 15 * 60 * 1000);
var ηm = ηMatrix;
- if ( !ηm.userSettings.clearBrowserCache ) {
+ if (!ηm.userSettings.clearBrowserCache) {
return;
}
ηm.clearBrowserCacheCycle -= 15;
- if ( ηm.clearBrowserCacheCycle > 0 ) {
+ if (ηm.clearBrowserCacheCycle > 0) {
return;
}
vAPI.browser.data.clearCache();
ηm.clearBrowserCacheCycle = ηm.userSettings.clearBrowserCacheAfter;
- ηm.browserCacheClearedCounter++;
+ ++ηm.browserCacheClearedCounter;
// TODO: i18n
- ηm.logger.writeOne('', 'info', vAPI.i18n('loggerEntryBrowserCacheCleared'));
+ η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 0f81833..fed262a 100644
--- a/js/cloud-ui.js
+++ b/js/cloud-ui.js
@@ -21,16 +21,9 @@
uMatrix Home: https://github.com/gorhill/uBlock
*/
-/* global uDom */
-
'use strict';
-/******************************************************************************/
-
-(function() {
-
- /******************************************************************************/
-
+(function () {
self.cloud = {
options: {},
datakey: '',
@@ -39,22 +32,18 @@
onPull: null
};
- /******************************************************************************/
-
- var widget = uDom.nodeFromId('cloudWidget');
- if ( widget === null ) {
+ let widget = uDom.nodeFromId('cloudWidget');
+ if (widget === null) {
return;
}
self.cloud.datakey = widget.getAttribute('data-cloud-entry') || '';
- if ( self.cloud.datakey === '' ) {
+ if (self.cloud.datakey === '') {
return;
}
- /******************************************************************************/
-
- var onCloudDataReceived = function(entry) {
- if ( typeof entry !== 'object' || entry === null ) {
+ let onCloudDataReceived = function (entry) {
+ if (typeof entry !== 'object' || entry === null) {
return;
}
@@ -63,7 +52,7 @@
uDom.nodeFromId('cloudPull').removeAttribute('disabled');
uDom.nodeFromId('cloudPullAndMerge').removeAttribute('disabled');
- var timeOptions = {
+ let timeOptions = {
weekday: 'short',
year: 'numeric',
month: 'short',
@@ -71,85 +60,64 @@
hour: 'numeric',
minute: 'numeric',
second: 'numeric',
- timeZoneName: 'short'
+ timeZoneName: 'short',
};
- var 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);
};
- /******************************************************************************/
-
- var fetchCloudData = function() {
- vAPI.messaging.send(
- 'cloud-ui.js',
- {
- what: 'cloudPull',
- datakey: self.cloud.datakey
- },
- onCloudDataReceived
- );
+ let fetchCloudData = function () {
+ vAPI.messaging.send('cloud-ui.js', {
+ what: 'cloudPull',
+ datakey: self.cloud.datakey
+ }, onCloudDataReceived);
};
- /******************************************************************************/
-
- var pushData = function() {
- if ( typeof self.cloud.onPush !== 'function' ) {
+ let pushData = function () {
+ if (typeof self.cloud.onPush !== 'function') {
return;
}
- vAPI.messaging.send(
- 'cloud-ui.js',
- {
- what: 'cloudPush',
- datakey: self.cloud.datakey,
- data: self.cloud.onPush()
- },
- fetchCloudData
- );
- };
- /******************************************************************************/
+ vAPI.messaging.send('cloud-ui.js', {
+ what: 'cloudPush',
+ datakey: self.cloud.datakey,
+ data: self.cloud.onPush()
+ }, fetchCloudData);
+ };
- var pullData = function(ev) {
- if ( typeof self.cloud.onPull === 'function' ) {
+ let pullData = function (ev) {
+ if (typeof self.cloud.onPull === 'function') {
self.cloud.onPull(self.cloud.data, ev.shiftKey);
}
};
- /******************************************************************************/
-
- var pullAndMergeData = function() {
- if ( typeof self.cloud.onPull === 'function' ) {
+ let pullAndMergeData = function () {
+ if (typeof self.cloud.onPull === 'function') {
self.cloud.onPull(self.cloud.data, true);
}
};
- /******************************************************************************/
-
- var openOptions = function() {
- var input = uDom.nodeFromId('cloudDeviceName');
+ 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');
};
- /******************************************************************************/
-
- var closeOptions = function(ev) {
- var root = uDom.nodeFromId('cloudOptions');
- if ( ev.target !== root ) {
+ let closeOptions = function (ev) {
+ let root = uDom.nodeFromId('cloudOptions');
+ if (ev.target !== root) {
return;
}
root.classList.remove('show');
};
- /******************************************************************************/
-
- var submitOptions = function() {
- var onOptions = function(options) {
- if ( typeof options !== 'object' || options === null ) {
+ let submitOptions = function () {
+ let onOptions = function (options) {
+ if (typeof options !== 'object' || options === null) {
return;
}
self.cloud.options = options;
@@ -164,31 +132,29 @@
uDom.nodeFromId('cloudOptions').classList.remove('show');
};
- /******************************************************************************/
-
- var onInitialize = function(options) {
- if ( typeof options !== 'object' || options === null ) {
+ let onInitialize = function (options) {
+ if (typeof options !== 'object' || options === null) {
return;
}
- if ( !options.enabled ) {
+ if (!options.enabled) {
return;
}
self.cloud.options = options;
- var xhr = new XMLHttpRequest();
+ let xhr = new XMLHttpRequest();
xhr.open('GET', 'cloud-ui.html', true);
xhr.overrideMimeType('text/html;charset=utf-8');
xhr.responseType = 'text';
- xhr.onload = function() {
+ xhr.onload = function () {
this.onload = null;
- var parser = new DOMParser(),
- parsed = parser.parseFromString(this.responseText, 'text/html'),
- fromParent = parsed.body;
- while ( fromParent.firstElementChild !== null ) {
- widget.appendChild(
- document.adoptNode(fromParent.firstElementChild)
- );
+ let parser = new DOMParser();
+ let parsed = parser.parseFromString(this.responseText, 'text/html');
+ let fromParent = parsed.body;
+
+ while (fromParent.firstElementChild !== null) {
+ widget.appendChild(document
+ .adoptNode(fromParent.firstElementChild));
}
vAPI.i18n.render(widget);
@@ -203,13 +169,13 @@
fetchCloudData();
};
+
xhr.send();
};
- vAPI.messaging.send('cloud-ui.js', { what: 'cloudGetOptions' }, onInitialize);
-
- /******************************************************************************/
+ vAPI.messaging.send('cloud-ui.js', {
+ what: 'cloudGetOptions'
+ }, onInitialize);
// https://www.youtube.com/watch?v=aQFp67VoiDA
-
})();
diff --git a/js/contentscript-start.js b/js/contentscript-start.js
index 3096023..4c320f8 100644
--- a/js/contentscript-start.js
+++ b/js/contentscript-start.js
@@ -23,40 +23,34 @@
'use strict';
-/******************************************************************************/
-/******************************************************************************/
-
// Injected into content pages
-
-(function() {
-
- if ( typeof vAPI !== 'object' ) { return; }
+(function () {
+ if (typeof vAPI !== 'object') {
+ return;
+ }
vAPI.selfWorkerSrcReported = vAPI.selfWorkerSrcReported || false;
var reGoodWorkerSrc = /(?:frame|worker)-src[^;,]+?'none'/;
var handler = function(ev) {
- if (
- ev.isTrusted !== true ||
- ev.originalPolicy.includes('report-uri about:blank') === false
- ) {
+ if (ev.isTrusted !== true
+ || ev.originalPolicy.includes('report-uri about:blank') === false) {
return false;
}
// Firefox and Chromium differs in how they fill the
// 'effectiveDirective' property.
- if (
- ev.effectiveDirective.startsWith('worker-src') === false &&
- ev.effectiveDirective.startsWith('frame-src') === false
- ) {
+ // ηMatrix: what does Pale Moon/Basilisk do?
+ if (ev.effectiveDirective.startsWith('worker-src') === false
+ && ev.effectiveDirective.startsWith('frame-src') === false) {
return false;
}
- // Further validate that the policy violation is relevant to uMatrix:
+ // Further validate that the policy violation is relevant to ηMatrix:
// the event still could have been fired as a result of a CSP header
- // not injected by uMatrix.
- if ( reGoodWorkerSrc.test(ev.originalPolicy) === false ) {
+ // not injected by ηMatrix.
+ if (reGoodWorkerSrc.test(ev.originalPolicy) === false) {
return false;
}
@@ -64,35 +58,31 @@
// However, we do want to report external resources each time.
// TODO: this could eventually lead to duplicated reports for external
// resources if another extension uses the same approach as
- // uMatrix. Think about what could be done to avoid duplicate
+ // ηMatrix. Think about what could be done to avoid duplicate
// reports.
- if ( ev.blockedURI.includes('://') === false ) {
- if ( vAPI.selfWorkerSrcReported ) { return true; }
+ if (ev.blockedURI.includes('://') === false) {
+ if (vAPI.selfWorkerSrcReported) {
+ return true;
+ }
vAPI.selfWorkerSrcReported = true;
}
- vAPI.messaging.send(
- 'contentscript.js',
- {
- what: 'securityPolicyViolation',
- directive: 'worker-src',
- blockedURI: ev.blockedURI,
- documentURI: ev.documentURI,
- blocked: ev.disposition === 'enforce'
- }
- );
+ vAPI.messaging.send('contentscript.js', {
+ what: 'securityPolicyViolation',
+ directive: 'worker-src',
+ blockedURI: ev.blockedURI,
+ documentURI: ev.documentURI,
+ blocked: ev.disposition === 'enforce'
+ });
return true;
};
- document.addEventListener(
- 'securitypolicyviolation',
- function(ev) {
- if ( !handler(ev) ) { return; }
- ev.stopPropagation();
- ev.preventDefault();
- },
- true
- );
-
+ document.addEventListener('securitypolicyviolation', function (ev) {
+ if (!handler(ev)) {
+ return;
+ }
+ ev.stopPropagation();
+ ev.preventDefault();
+ }, true);
})();
diff --git a/js/contentscript.js b/js/contentscript.js
index 650d1be..c304fdf 100644
--- a/js/contentscript.js
+++ b/js/contentscript.js
@@ -21,70 +21,58 @@
uMatrix Home: https://github.com/gorhill/uMatrix
*/
-/* global HTMLDocument, XMLDocument */
-
'use strict';
-/******************************************************************************/
-/******************************************************************************/
-
// Injected into content pages
-(function() {
-
- /******************************************************************************/
-
+(function () {
// https://github.com/chrisaljoudi/uBlock/issues/464
// https://github.com/gorhill/uMatrix/issues/621
- if (
- document instanceof HTMLDocument === false &&
- document instanceof XMLDocument === false
- ) {
+ if (document instanceof HTMLDocument === false
+ && document instanceof XMLDocument === false) {
return;
}
- // This can also happen (for example if script injected into a `data:` URI doc)
- if ( !window.location ) {
+ // This can also happen (for example if script injected into a
+ // `data:` URI doc)
+ if (!window.location) {
return;
}
// This can happen
- if ( typeof vAPI !== 'object' ) {
+ if (typeof vAPI !== 'object') {
//console.debug('contentscript.js > vAPI not found');
return;
}
// https://github.com/chrisaljoudi/uBlock/issues/456
// Already injected?
- if ( vAPI.contentscriptEndInjected ) {
+ if (vAPI.contentscriptEndInjected) {
//console.debug('contentscript.js > content script already injected');
return;
}
vAPI.contentscriptEndInjected = true;
- /******************************************************************************/
- /******************************************************************************/
-
// Executed only once.
-
- (function() {
- var localStorageHandler = function(mustRemove) {
- if ( mustRemove ) {
+ (function () {
+ let localStorageHandler = function (mustRemove) {
+ if (mustRemove) {
window.localStorage.clear();
window.sessionStorage.clear();
}
};
// Check with extension whether local storage must be emptied
- // rhill 2014-03-28: we need an exception handler in case 3rd-party access
- // to site data is disabled.
+ // rhill 2014-03-28: we need an exception handler in case
+ // 3rd-party access to site data is disabled.
// https://github.com/gorhill/httpswitchboard/issues/215
try {
- var hasLocalStorage =
+ let hasLocalStorage =
window.localStorage && window.localStorage.length !== 0;
- var hasSessionStorage =
+ let hasSessionStorage =
window.sessionStorage && window.sessionStorage.length !== 0;
- if ( hasLocalStorage || hasSessionStorage ) {
+
+ if (hasLocalStorage || hasSessionStorage) {
vAPI.messaging.send('contentscript.js', {
what: 'contentScriptHasLocalStorage',
originURL: window.location.origin
@@ -104,140 +92,154 @@
// "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
-
- var collapser = (function() {
- var resquestIdGenerator = 1,
- processTimer,
- toProcess = [],
- toFilter = [],
- toCollapse = new Map(),
- cachedBlockedMap,
- cachedBlockedMapHash,
- cachedBlockedMapTimer,
- reURLPlaceholder = /\{\{url\}\}/g;
- var src1stProps = {
- 'embed': 'src',
- 'iframe': 'src',
- 'img': 'src',
- 'object': 'data'
+ let collapser = (function () {
+ let resquestIdGenerator = 1;
+ let processTimer;
+ let toProcess = [];
+ let toFilter = [];
+ let toCollapse = new Map();
+ let cachedBlockedMap;
+ let cachedBlockedMapHash;
+ let cachedBlockedMapTimer;
+ let reURLPlaceholder = /\{\{url\}\}/g;
+ let src1stProps = {
+ embed: 'src',
+ iframe: 'src',
+ img: 'src',
+ object: 'data',
};
- var src2ndProps = {
- 'img': 'srcset'
+ let src2ndProps = {
+ img: 'srcset',
};
- var tagToTypeMap = {
+ let tagToTypeMap = {
embed: 'media',
iframe: 'frame',
img: 'image',
- object: 'media'
+ object: 'media',
};
- var cachedBlockedSetClear = function() {
+ let cachedBlockedSetClear = function () {
cachedBlockedMap =
cachedBlockedMapHash =
cachedBlockedMapTimer = undefined;
};
// https://github.com/chrisaljoudi/uBlock/issues/174
- // Do not remove fragment from src URL
- var onProcessed = function(response) {
- if ( !response ) { // This happens if uBO is disabled or restarted.
+ // Do not remove fragment from src URL
+ let onProcessed = function (response) {
+ if (!response) { // This happens if uBO is disabled or restarted.
toCollapse.clear();
return;
}
- var targets = toCollapse.get(response.id);
- if ( targets === undefined ) { return; }
+ let targets = toCollapse.get(response.id);
+ if (targets === undefined) {
+ return;
+ }
+
toCollapse.delete(response.id);
- if ( cachedBlockedMapHash !== response.hash ) {
+
+ if (cachedBlockedMapHash !== response.hash) {
cachedBlockedMap = new Map(response.blockedResources);
cachedBlockedMapHash = response.hash;
- if ( cachedBlockedMapTimer !== undefined ) {
+ if (cachedBlockedMapTimer !== undefined) {
clearTimeout(cachedBlockedMapTimer);
}
- cachedBlockedMapTimer = vAPI.setTimeout(cachedBlockedSetClear, 30000);
+ cachedBlockedMapTimer =
+ vAPI.setTimeout(cachedBlockedSetClear, 30000);
}
- if ( cachedBlockedMap === undefined || cachedBlockedMap.size === 0 ) {
+
+ if (cachedBlockedMap === undefined || cachedBlockedMap.size === 0) {
return;
}
- var placeholders = response.placeholders,
- tag, prop, src, collapsed, docurl, replaced;
+ let placeholders = response.placeholders;
- for ( var target of targets ) {
- tag = target.localName;
- prop = src1stProps[tag];
- if ( prop === undefined ) { continue; }
- src = target[prop];
- if ( typeof src !== 'string' || src.length === 0 ) {
+ for (let target of targets) {
+ let tag = target.localName;
+
+ let prop = src1stProps[tag];
+ if (prop === undefined) {
+ continue;
+ }
+
+ let src = target[prop];
+ if (typeof src !== 'string' || src.length === 0) {
prop = src2ndProps[tag];
- if ( prop === undefined ) { continue; }
+ if (prop === undefined) {
+ continue;
+ }
src = target[prop];
- if ( typeof src !== 'string' || src.length === 0 ) { continue; }
+ if (typeof src !== 'string' || src.length === 0) {
+ continue;
+ }
}
- collapsed = cachedBlockedMap.get(tagToTypeMap[tag] + ' ' + src);
- if ( collapsed === undefined ) { continue; }
- if ( collapsed ) {
+
+ 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 ) {
+
+ switch (tag) {
case 'iframe':
- if ( placeholders.frame !== true ) { break; }
- docurl =
- 'data:text/html,' +
- encodeURIComponent(
- placeholders.frameDocument.replace(
- reURLPlaceholder,
- src
- )
- );
+ if (placeholders.frame !== true) {
+ break;
+ }
+
+ docurl = 'data:text/html,'
+ + encodeURIComponent(placeholders
+ .frameDocument
+ .replace(reURLPlaceholder, src));
replaced = false;
+
// Using contentWindow.location prevent tainting browser
// history -- i.e. breaking back button (seen on Chromium).
- if ( target.contentWindow ) {
+ if (target.contentWindow) {
try {
target.contentWindow.location.replace(docurl);
replaced = true;
} catch(ex) {
}
}
- if ( !replaced ) {
+
+ if (!replaced) {
target.setAttribute('src', docurl);
}
break;
case 'img':
- if ( placeholders.image !== true ) { break; }
+ if (placeholders.image !== true) {
+ break;
+ }
+
target.style.setProperty('display', 'inline-block');
target.style.setProperty('min-width', '20px', 'important');
target.style.setProperty('min-height', '20px', 'important');
- target.style.setProperty(
- 'border',
- placeholders.imageBorder,
- 'important'
- );
- target.style.setProperty(
- 'background',
- placeholders.imageBackground,
- 'important'
- );
+ target.style.setProperty('border', placeholders.imageBorder,
+ 'important');
+ target.style.setProperty('background',
+ placeholders.imageBackground,
+ 'important');
break;
}
}
};
- var send = function() {
+ let send = function () {
processTimer = undefined;
toCollapse.set(resquestIdGenerator, toProcess);
- var msg = {
+ let msg = {
what: 'lookupBlockedCollapsibles',
id: resquestIdGenerator,
toFilter: toFilter,
@@ -249,95 +251,108 @@
resquestIdGenerator += 1;
};
- var process = function(delay) {
- if ( toProcess.length === 0 ) { return; }
- if ( delay === 0 ) {
- if ( processTimer !== undefined ) {
+ let process = function (delay) {
+ if (toProcess.length === 0) {
+ return;
+ }
+
+ if (delay === 0) {
+ if (processTimer !== undefined) {
clearTimeout(processTimer);
}
send();
- } else if ( processTimer === undefined ) {
+ } else if (processTimer === undefined) {
processTimer = vAPI.setTimeout(send, delay || 47);
}
};
- var add = function(target) {
+ let add = function (target) {
toProcess.push(target);
};
- var addMany = function(targets) {
- var i = targets.length;
- while ( i-- ) {
+ let addMany = function (targets) {
+ for (let i=targets.length-1; i>=0; --i) {
toProcess.push(targets[i]);
}
};
- var iframeSourceModified = function(mutations) {
- var i = mutations.length;
- while ( i-- ) {
+ let iframeSourceModified = function (mutations) {
+ for (let i=mutations.length-1; i>=0; --i) {
addIFrame(mutations[i].target, true);
}
process();
};
- var iframeSourceObserver;
- var iframeSourceObserverOptions = {
+
+ let iframeSourceObserver;
+ let iframeSourceObserverOptions = {
attributes: true,
attributeFilter: [ 'src' ]
};
- var 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 ) {
- iframeSourceObserver = new MutationObserver(iframeSourceModified);
+ if (dontObserve !== true) {
+ if (iframeSourceObserver === undefined) {
+ iframeSourceObserver =
+ new MutationObserver(iframeSourceModified);
}
- iframeSourceObserver.observe(iframe, iframeSourceObserverOptions);
+ iframeSourceObserver.observe(iframe,
+ iframeSourceObserverOptions);
+ }
+
+ let src = iframe.src;
+ if (src === '' || typeof src !== 'string') {
+ return;
+ }
+ if (src.startsWith('http') === false) {
+ return;
}
- var src = iframe.src;
- if ( src === '' || typeof src !== 'string' ) { return; }
- if ( src.startsWith('http') === false ) { return; }
- toFilter.push({ type: 'frame', url: iframe.src });
+
+ toFilter.push({
+ type: 'frame',
+ url: iframe.src,
+ });
+
add(iframe);
};
- var addIFrames = function(iframes) {
- var i = iframes.length;
- while ( i-- ) {
+ let addIFrames = function (iframes) {
+ for (let i=iframes.length-1; i>=0; --i) {
addIFrame(iframes[i]);
}
};
- var addNodeList = function(nodeList) {
- var node,
- i = nodeList.length;
- while ( i-- ) {
- node = nodeList[i];
- if ( node.nodeType !== 1 ) { continue; }
- if ( node.localName === 'iframe' ) {
+ 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'));
}
}
};
- var onResourceFailed = function(ev) {
- if ( tagToTypeMap[ev.target.localName] !== undefined ) {
+ let onResourceFailed = function (ev) {
+ if (tagToTypeMap[ev.target.localName] !== undefined) {
add(ev.target);
process();
}
};
document.addEventListener('error', onResourceFailed, true);
- vAPI.shutdown.add(function() {
+ vAPI.shutdown.add(function () {
document.removeEventListener('error', onResourceFailed, true);
- if ( iframeSourceObserver !== undefined ) {
+ if (iframeSourceObserver !== undefined) {
iframeSourceObserver.disconnect();
iframeSourceObserver = undefined;
}
- if ( processTimer !== undefined ) {
+ if (processTimer !== undefined) {
clearTimeout(processTimer);
processTimer = undefined;
}
@@ -351,24 +366,21 @@
};
})();
- /******************************************************************************/
- /******************************************************************************/
-
// Observe changes in the DOM
-
// Added node lists will be cumulated here before being processed
- (function() {
+ (function () {
// This fixes http://acid3.acidtests.org/
- if ( !document.body ) { return; }
+ if (!document.body) {
+ return;
+ }
- var addedNodeLists = [];
- var addedNodeListsTimer;
+ let addedNodeLists = [];
+ let addedNodeListsTimer;
- var treeMutationObservedHandler = function() {
+ let treeMutationObservedHandler = function () {
addedNodeListsTimer = undefined;
- var i = addedNodeLists.length;
- while ( i-- ) {
+ for (let i=addedNodeLists.length-1; i>=0; --i) {
collapser.addNodeList(addedNodeLists[i]);
}
collapser.process();
@@ -377,33 +389,33 @@
// https://github.com/gorhill/uBlock/issues/205
// Do not handle added node directly from within mutation observer.
- var treeMutationObservedHandlerAsync = function(mutations) {
- var iMutation = mutations.length,
- nodeList;
- while ( iMutation-- ) {
- nodeList = mutations[iMutation].addedNodes;
- if ( nodeList.length !== 0 ) {
+ 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);
+ if (addedNodeListsTimer === undefined) {
+ addedNodeListsTimer =
+ vAPI.setTimeout(treeMutationObservedHandler, 47);
}
};
// https://github.com/gorhill/httpswitchboard/issues/176
- var treeObserver = new MutationObserver(treeMutationObservedHandlerAsync);
+ let treeObserver =
+ new MutationObserver(treeMutationObservedHandlerAsync);
treeObserver.observe(document.body, {
childList: true,
subtree: true
});
- vAPI.shutdown.add(function() {
- if ( addedNodeListsTimer !== undefined ) {
+ vAPI.shutdown.add(function () {
+ if (addedNodeListsTimer !== undefined) {
clearTimeout(addedNodeListsTimer);
addedNodeListsTimer = undefined;
}
- if ( treeObserver !== null ) {
+ if (treeObserver !== null) {
treeObserver.disconnect();
treeObserver = undefined;
}
@@ -411,9 +423,6 @@
});
})();
- /******************************************************************************/
- /******************************************************************************/
-
// Executed only once.
//
// https://github.com/gorhill/httpswitchboard/issues/25
@@ -427,12 +436,41 @@
// https://github.com/gorhill/uMatrix/issues/924
// Report inline styles.
- (function() {
- if (
- document.querySelector('script:not([src])') !== null ||
- document.querySelector('a[href^="javascript:"]') !== null ||
- document.querySelector('[onabort],[onblur],[oncancel],[oncanplay],[oncanplaythrough],[onchange],[onclick],[onclose],[oncontextmenu],[oncuechange],[ondblclick],[ondrag],[ondragend],[ondragenter],[ondragexit],[ondragleave],[ondragover],[ondragstart],[ondrop],[ondurationchange],[onemptied],[onended],[onerror],[onfocus],[oninput],[oninvalid],[onkeydown],[onkeypress],[onkeyup],[onload],[onloadeddata],[onloadedmetadata],[onloadstart],[onmousedown],[onmouseenter],[onmouseleave],[onmousemove],[onmouseout],[onmouseover],[onmouseup],[onwheel],[onpause],[onplay],[onplaying],[onprogress],[onratechange],[onreset],[onresize],[onscroll],[onseeked],[onseeking],[onselect],[onshow],[onstalled],[onsubmit],[onsuspend],[ontimeupdate],[ontoggle],[onvolumechange],[onwaiting],[onafterprint],[onbeforeprint],[onbeforeunload],[onhashchange],[onlanguagechange],[onmessage],[onoffline],[ononline],[onpagehide],[onpageshow],[onrejectionhandled],[onpopstate],[onstorage],[onunhandledrejection],[onunload],[oncopy],[oncut],[onpaste]') !== null
- ) {
+ (function () {
+ if (document.querySelector('script:not([src])') !== null
+ || document.querySelector('a[href^="javascript:"]') !== null
+ || document.querySelector('[onabort],[onblur],[oncancel],'
+ + '[oncanplay],[oncanplaythrough],'
+ + '[onchange],[onclick],[onclose],'
+ + '[oncontextmenu],[oncuechange],'
+ + '[ondblclick],[ondrag],[ondragend],'
+ + '[ondragenter],[ondragexit],'
+ + '[ondragleave],[ondragover],'
+ + '[ondragstart],[ondrop],'
+ + '[ondurationchange],[onemptied],'
+ + '[onended],[onerror],[onfocus],'
+ + '[oninput],[oninvalid],[onkeydown],'
+ + '[onkeypress],[onkeyup],[onload],'
+ + '[onloadeddata],[onloadedmetadata],'
+ + '[onloadstart],[onmousedown],'
+ + '[onmouseenter],[onmouseleave],'
+ + '[onmousemove],[onmouseout],'
+ + '[onmouseover],[onmouseup],[onwheel],'
+ + '[onpause],[onplay],[onplaying],'
+ + '[onprogress],[onratechange],[onreset],'
+ + '[onresize],[onscroll],[onseeked],'
+ + '[onseeking],[onselect],[onshow],'
+ + '[onstalled],[onsubmit],[onsuspend],'
+ + '[ontimeupdate],[ontoggle],'
+ + '[onvolumechange],[onwaiting],'
+ + '[onafterprint],[onbeforeprint],'
+ + '[onbeforeunload],[onhashchange],'
+ + '[onlanguagechange],[onmessage],'
+ + '[onoffline],[ononline],[onpagehide],'
+ + '[onpageshow],[onrejectionhandled],'
+ + '[onpopstate],[onstorage],'
+ + '[onunhandledrejection],[onunload],'
+ + '[oncopy],[oncut],[onpaste]') !== null) {
vAPI.messaging.send('contentscript.js', {
what: 'securityPolicyViolation',
directive: 'script-src',
@@ -440,7 +478,7 @@
});
}
- if ( document.querySelector('style,[style]') !== null ) {
+ if (document.querySelector('style,[style]') !== null) {
vAPI.messaging.send('contentscript.js', {
what: 'securityPolicyViolation',
directive: 'style-src',
@@ -453,90 +491,93 @@
collapser.process();
})();
- /******************************************************************************/
- /******************************************************************************/
-
// Executed only once.
-
// https://github.com/gorhill/uMatrix/issues/232
- // Force `display` property, Firefox is still affected by the issue.
-
- (function() {
- var noscripts = document.querySelectorAll('noscript');
- if ( noscripts.length === 0 ) { return; }
-
- var redirectTimer,
- reMetaContent = /^\s*(\d+)\s*;\s*url=(['"]?)([^'"]+)\2/i,
- reSafeURL = /^https?:\/\//;
-
- var autoRefresh = function(root) {
- var meta = root.querySelector('meta[http-equiv="refresh"][content]');
- if ( meta === null ) { return; }
- var match = reMetaContent.exec(meta.getAttribute('content'));
- if ( match === null || match[3].trim() === '' ) { return; }
- var url = new URL(match[3], document.baseURI);
- if ( reSafeURL.test(url.href) === false ) { return; }
- redirectTimer = setTimeout(
- function() {
- location.assign(url.href);
- },
- parseInt(match[1], 10) * 1000 + 1
- );
+ // Force `display` property, Firefox is still affected by the issue.
+
+ (function () {
+ let noscripts = document.querySelectorAll('noscript');
+ if (noscripts.length === 0) {
+ return;
+ }
+
+ let redirectTimer;
+ let reMetaContent = /^\s*(\d+)\s*;\s*url=(['"]?)([^'"]+)\2/i;
+ let reSafeURL = /^https?:\/\//;
+
+ let autoRefresh = function (root) {
+ let meta =
+ root.querySelector('meta[http-equiv="refresh"][content]');
+ if (meta === null) {
+ return;
+ }
+
+ let match = reMetaContent.exec(meta.getAttribute('content'));
+ if (match === null || match[3].trim() === '') {
+ return;
+ }
+
+ let url = new URL(match[3], document.baseURI);
+ if (reSafeURL.test(url.href) === false) {
+ return;
+ }
+
+ redirectTimer = setTimeout(function () {
+ location.assign(url.href);
+ }, parseInt(match[1], 10) * 1000 + 1);
+
meta.parentNode.removeChild(meta);
};
- var morphNoscript = function(from) {
- if ( /^application\/(?:xhtml\+)?xml/.test(document.contentType) ) {
- var to = document.createElement('span');
- while ( from.firstChild !== null ) {
+ let morphNoscript = function (from) {
+ if (/^application\/(?:xhtml\+)?xml/.test(document.contentType)) {
+ let to = document.createElement('span');
+ while (from.firstChild !== null) {
to.appendChild(from.firstChild);
}
return to;
}
- var parser = new DOMParser();
- var doc = parser.parseFromString(
- '<span>' + from.textContent + '</span>',
- 'text/html'
- );
+
+ let parser = new DOMParser();
+ let doc =
+ parser.parseFromString('<span>' + from.textContent + '</span>',
+ 'text/html');
+
return document.adoptNode(doc.querySelector('span'));
};
- var renderNoscriptTags = function(response) {
- if ( response !== true ) { return; }
- var parent, span;
- for ( var noscript of noscripts ) {
- parent = noscript.parentNode;
- if ( parent === null ) { continue; }
- span = morphNoscript(noscript);
+ let renderNoscriptTags = function (response) {
+ if (response !== true) {
+ return;
+ }
+
+ for (let noscript of noscripts) {
+ let parent = noscript.parentNode;
+ if (parent === null) {
+ continue;
+ }
+
+ let span = morphNoscript(noscript);
span.style.setProperty('display', 'inline', 'important');
- if ( redirectTimer === undefined ) {
+
+ if (redirectTimer === undefined) {
autoRefresh(span);
}
+
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?' },
- function(response) {
- if ( response === true ) {
- vAPI.shutdown.exec();
- }
+ vAPI.messaging.send('contentscript.js', {
+ what: 'shutdown?'
+ }, function (response) {
+ if (response === true) {
+ vAPI.shutdown.exec();
}
- );
-
- /******************************************************************************/
- /******************************************************************************/
-
+ });
})();
diff --git a/js/cookies.js b/js/cookies.js
index ee271ca..2e15e03 100644
--- a/js/cookies.js
+++ b/js/cookies.js
@@ -28,181 +28,43 @@
"use strict";
-/******************************************************************************/
-
// Isolate from global namespace
// Use cached-context approach rather than object-based approach, as details
// of the implementation do not need to be visible
-ηMatrix.cookieHunter = (function() {
-
- /******************************************************************************/
-
- var ηm = ηMatrix;
+ηMatrix.cookieHunter = (function () {
+ Cu.import('chrome://ematrix/content/lib/UriTools.jsm');
+ Cu.import('chrome://ematrix/content/lib/CookieCache.jsm');
- var recordPageCookiesQueue = new Map();
- var removePageCookiesQueue = new Map();
- var removeCookieQueue = new Set();
- var cookieDict = new Map();
- var cookieEntryJunkyard = [];
- var processRemoveQueuePeriod = 2 * 60 * 1000;
- var processCleanPeriod = 10 * 60 * 1000;
- var processPageRecordQueueTimer = null;
- var processPageRemoveQueueTimer = null;
-
- /******************************************************************************/
-
- var CookieEntry = function(cookie) {
- this.usedOn = new Set();
- this.init(cookie);
- };
-
- CookieEntry.prototype.init = function(cookie) {
- this.secure = cookie.secure;
- this.session = cookie.session;
- this.anySubdomain = cookie.domain.charAt(0) === '.';
- this.hostname = this.anySubdomain ? cookie.domain.slice(1) : cookie.domain;
- this.domain = ηm.URI.domainFromHostname(this.hostname) || this.hostname;
- this.path = cookie.path;
- this.name = cookie.name;
- this.value = cookie.value;
- this.tstamp = Date.now();
- this.usedOn.clear();
- return this;
- };
-
- // Release anything which may consume too much memory
-
- CookieEntry.prototype.dispose = function() {
- this.hostname = '';
- this.domain = '';
- this.path = '';
- this.name = '';
- this.value = '';
- this.usedOn.clear();
- return this;
- };
-
- /******************************************************************************/
-
- var addCookieToDict = function(cookie) {
- var cookieKey = cookieKeyFromCookie(cookie),
- cookieEntry = cookieDict.get(cookieKey);
- if ( cookieEntry === undefined ) {
- cookieEntry = cookieEntryJunkyard.pop();
- if ( cookieEntry ) {
- cookieEntry.init(cookie);
- } else {
- cookieEntry = new CookieEntry(cookie);
- }
- cookieDict.set(cookieKey, cookieEntry);
- }
- return cookieEntry;
- };
-
- /******************************************************************************/
-
- var addCookiesToDict = function(cookies) {
- var i = cookies.length;
- while ( i-- ) {
- addCookieToDict(cookies[i]);
- }
- };
+ let ηm = ηMatrix;
- /******************************************************************************/
-
- var removeCookieFromDict = function(cookieKey) {
- var cookieEntry = cookieDict.get(cookieKey);
- if ( cookieEntry === undefined ) { return false; }
- cookieDict.delete(cookieKey);
- if ( cookieEntryJunkyard.length < 25 ) {
- cookieEntryJunkyard.push(cookieEntry.dispose());
- }
- return true;
- };
-
- /******************************************************************************/
-
- var cookieKeyBuilder = [
- '', // 0 = scheme
- '://',
- '', // 2 = domain
- '', // 3 = path
- '{',
- '', // 5 = persistent or session
- '-cookie:',
- '', // 7 = name
- '}'
- ];
-
- var cookieKeyFromCookie = function(cookie) {
- var cb = cookieKeyBuilder;
- cb[0] = cookie.secure ? 'https' : 'http';
- cb[2] = cookie.domain.charAt(0) === '.' ? cookie.domain.slice(1) : cookie.domain;
- cb[3] = cookie.path;
- cb[5] = cookie.session ? 'session' : 'persistent';
- cb[7] = cookie.name;
- return cb.join('');
- };
-
- var cookieKeyFromCookieURL = function(url, type, name) {
- var ηmuri = ηm.URI.set(url);
- var cb = cookieKeyBuilder;
- cb[0] = ηmuri.scheme;
- cb[2] = ηmuri.hostname;
- cb[3] = ηmuri.path;
- cb[5] = type;
- cb[7] = name;
- return cb.join('');
- };
-
- /******************************************************************************/
-
- var cookieURLFromCookieEntry = function(entry) {
- if ( !entry ) {
- return '';
- }
- return (entry.secure ? 'https://' : 'http://') + entry.hostname + entry.path;
- };
-
- /******************************************************************************/
-
- var cookieMatchDomains = function(cookieKey, allHostnamesString) {
- var cookieEntry = cookieDict.get(cookieKey);
- if ( cookieEntry === undefined ) { return false; }
- if ( allHostnamesString.indexOf(' ' + cookieEntry.hostname + ' ') < 0 ) {
- if ( !cookieEntry.anySubdomain ) {
- return false;
- }
- if ( allHostnamesString.indexOf('.' + cookieEntry.hostname + ' ') < 0 ) {
- return false;
- }
- }
- return true;
- };
-
- /******************************************************************************/
+ let recordPageCookiesQueue = new Map();
+ let removePageCookiesQueue = new Map();
+ let removeCookieQueue = new Set();
+ let processRemoveQueuePeriod = 2 * 60 * 1000;
+ let processCleanPeriod = 10 * 60 * 1000;
+ let processPageRecordQueueTimer = null;
+ let processPageRemoveQueueTimer = null;
// Look for cookies to record for a specific web page
- var recordPageCookiesAsync = function(pageStats) {
+ 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 ) {
+ if (!pageStats) {
return;
}
recordPageCookiesQueue.set(pageStats.pageUrl, pageStats);
- if ( processPageRecordQueueTimer === null ) {
- processPageRecordQueueTimer = vAPI.setTimeout(processPageRecordQueue, 1000);
+ if (processPageRecordQueueTimer === null) {
+ processPageRecordQueueTimer =
+ vAPI.setTimeout(processPageRecordQueue, 1000);
}
};
- /******************************************************************************/
-
- var cookieLogEntryBuilder = [
+ let cookieLogEntryBuilder = [
'',
'{',
'',
@@ -211,168 +73,176 @@
'}'
];
- var recordPageCookie = function(pageStore, cookieKey) {
- if ( vAPI.isBehindTheSceneTabId(pageStore.tabId) ) { return; }
+ let recordPageCookie = function (pageStore, key) {
+ if (vAPI.isBehindTheSceneTabId(pageStore.tabId)) {
+ return;
+ }
- var cookieEntry = cookieDict.get(cookieKey);
- var pageHostname = pageStore.pageHostname;
- var block = ηm.mustBlock(pageHostname, cookieEntry.hostname, 'cookie');
+ let entry = CookieCache.get(key);
+ let pageHostname = pageStore.pageHostname;
+ let block = ηm.mustBlock(pageHostname, entry.hostname, 'cookie');
- cookieLogEntryBuilder[0] = cookieURLFromCookieEntry(cookieEntry);
- cookieLogEntryBuilder[2] = cookieEntry.session ? 'session' : 'persistent';
- cookieLogEntryBuilder[4] = encodeURIComponent(cookieEntry.name);
+ cookieLogEntryBuilder[0] = CookieUtils.urlFromEntry(entry);
+ cookieLogEntryBuilder[2] = entry.session ? 'session' : 'persistent';
+ cookieLogEntryBuilder[4] = encodeURIComponent(entry.name);
- var cookieURL = cookieLogEntryBuilder.join('');
+ 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);
+ ηm.logger.writeOne(pageStore.tabId, 'net',
+ pageHostname, cookieURL, 'cookie', block);
- cookieEntry.usedOn.add(pageHostname);
+ 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 ) {
+ if (!block) {
return;
}
- if ( !ηm.userSettings.deleteCookies ) {
+ if (!ηm.userSettings.deleteCookies) {
return;
}
- removeCookieAsync(cookieKey);
+ removeCookieAsync(key);
};
- /******************************************************************************/
-
// Look for cookies to potentially remove for a specific web page
- var removePageCookiesAsync = function(pageStats) {
+ 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 ) {
+ if (!pageStats) {
return;
}
removePageCookiesQueue.set(pageStats.pageUrl, pageStats);
- if ( processPageRemoveQueueTimer === null ) {
- processPageRemoveQueueTimer = vAPI.setTimeout(processPageRemoveQueue, 15 * 1000);
+ if (processPageRemoveQueueTimer === null) {
+ processPageRemoveQueueTimer =
+ vAPI.setTimeout(processPageRemoveQueue, 15 * 1000);
}
};
- /******************************************************************************/
-
// Candidate for removal
- var removeCookieAsync = function(cookieKey) {
- removeCookieQueue.add(cookieKey);
+ let removeCookieAsync = function (key) {
+ removeCookieQueue.add(key);
};
- /******************************************************************************/
-
- var chromeCookieRemove = function(cookieEntry, name) {
- var url = cookieURLFromCookieEntry(cookieEntry);
- if ( url === '' ) {
+ let chromeCookieRemove = function (entry, name) {
+ let url = CookieUtils.urlFromEntry(entry);
+ if (url === '') {
return;
}
- var sessionCookieKey = cookieKeyFromCookieURL(url, 'session', name);
- var persistCookieKey = cookieKeyFromCookieURL(url, 'persistent', name);
- var callback = function(details) {
- var success = !!details;
- var template = success ? i18nCookieDeleteSuccess : i18nCookieDeleteFailure;
- if ( removeCookieFromDict(sessionCookieKey) ) {
- if ( success ) {
+ let sessionKey = CookieUtils.keyFromURL(UriTools.set(url),
+ 'session', name);
+ let persistKey = CookieUtils.keyFromURL(UriTools.set(url),
+ 'persistent', name);
+
+ let callback = function(details) {
+ let success = !!details;
+ let template = success ?
+ i18nCookieDeleteSuccess :
+ i18nCookieDeleteFailure;
+
+ if (CookieCache.remove(sessionKey)) {
+ if (success) {
ηm.cookieRemovedCounter += 1;
}
- ηm.logger.writeOne('', 'info', 'cookie', template.replace('{{value}}', sessionCookieKey));
+ ηm.logger.writeOne('', 'info', 'cookie',
+ template.replace('{{value}}',
+ sessionKey));
}
- if ( removeCookieFromDict(persistCookieKey) ) {
- if ( success ) {
+ if (CookieCache.remove(persistKey)) {
+ if (success) {
ηm.cookieRemovedCounter += 1;
}
- ηm.logger.writeOne('', 'info', 'cookie', template.replace('{{value}}', persistCookieKey));
+ η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);
};
- var i18nCookieDeleteSuccess = vAPI.i18n('loggerEntryCookieDeleted');
- var i18nCookieDeleteFailure = vAPI.i18n('loggerEntryDeleteCookieError');
-
- /******************************************************************************/
+ let i18nCookieDeleteSuccess = vAPI.i18n('loggerEntryCookieDeleted');
+ let i18nCookieDeleteFailure = vAPI.i18n('loggerEntryDeleteCookieError');
- var processPageRecordQueue = function() {
+ let processPageRecordQueue = function () {
processPageRecordQueueTimer = null;
- for ( var pageStore of recordPageCookiesQueue.values() ) {
+ for (let pageStore of recordPageCookiesQueue.values()) {
findAndRecordPageCookies(pageStore);
}
recordPageCookiesQueue.clear();
};
- /******************************************************************************/
-
- var processPageRemoveQueue = function() {
+ let processPageRemoveQueue = function () {
processPageRemoveQueueTimer = null;
- for ( var pageStore of removePageCookiesQueue.values() ) {
+ for (let pageStore of removePageCookiesQueue.values()) {
findAndRemovePageCookies(pageStore);
}
removePageCookiesQueue.clear();
};
- /******************************************************************************/
-
// Effectively remove cookies.
- var processRemoveQueue = function() {
- var userSettings = ηm.userSettings;
- var deleteCookies = userSettings.deleteCookies;
+ 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
- var tstampObsolete = userSettings.deleteUnusedSessionCookies ?
- Date.now() - userSettings.deleteUnusedSessionCookiesAfter * 60 * 1000 :
+ let dusc = userSettings.deleteUnusedSessionCookies;
+ let dusca = userSettings.deleteUnusedSessionCookiesAfter;
+ let tstampObsolete = dusc ?
+ Date.now() - dusca * 60 * 1000 :
0;
- var srcHostnames;
- var cookieEntry;
+ let srcHostnames;
+ let entry;
- for ( var cookieKey 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?)
- cookieEntry = cookieDict.get(cookieKey);
- if ( cookieEntry === undefined ) { continue; }
+ entry = CookieCache.get(key);
+ if (entry === undefined) {
+ continue;
+ }
// Delete obsolete session cookies: enabled.
- if ( tstampObsolete !== 0 && cookieEntry.session ) {
- if ( cookieEntry.tstamp < tstampObsolete ) {
- chromeCookieRemove(cookieEntry, cookieEntry.name);
+ if (tstampObsolete !== 0 && entry.session) {
+ if (entry.tstamp < tstampObsolete) {
+ chromeCookieRemove(entry, entry.name);
continue;
}
}
// Delete all blocked cookies: disabled.
- if ( deleteCookies === false ) {
+ if (deleteCookies === false) {
continue;
}
// Query scopes only if we are going to use them
- if ( srcHostnames === undefined ) {
+ if (srcHostnames === undefined) {
srcHostnames = ηm.tMatrix.extractAllSourceHostnames();
}
// Ensure cookie is not allowed on ALL current web pages: It can
// happen that a cookie is blacklisted on one web page while
// being whitelisted on another (because of per-page permissions).
- if ( canRemoveCookie(cookieKey, srcHostnames) ) {
- chromeCookieRemove(cookieEntry, cookieEntry.name);
+ if (canRemoveCookie(key, srcHostnames)) {
+ chromeCookieRemove(entry, entry.name);
}
}
@@ -381,21 +251,21 @@
vAPI.setTimeout(processRemoveQueue, processRemoveQueuePeriod);
};
- /******************************************************************************/
-
// Once in a while, we go ahead and clean everything that might have been
// left behind.
// Remove only some of the cookies which are candidate for removal: who knows,
// maybe a user has 1000s of cookies sitting in his browser...
- var processClean = function() {
- var us = ηm.userSettings;
- if ( us.deleteCookies || us.deleteUnusedSessionCookies ) {
- var cookieKeys = Array.from(cookieDict.keys()),
- len = cookieKeys.length,
- step, offset, n;
- if ( len > 25 ) {
+ let processClean = function () {
+ let us = ηm.userSettings;
+
+ if (us.deleteCookies || us.deleteUnusedSessionCookies) {
+ let keys = Array.from(CookieCache.keys());
+ let len = keys.length;
+ let step, offset, n;
+
+ if (len > 25) {
step = len / 25;
offset = Math.floor(Math.random() * len);
n = 25;
@@ -404,9 +274,10 @@
offset = 0;
n = len;
}
- var i = offset;
- while ( n-- ) {
- removeCookieAsync(cookieKeys[Math.floor(i % len)]);
+
+ let i = offset;
+ while (n--) {
+ removeCookieAsync(keys[Math.floor(i % len)]);
i += step;
}
}
@@ -414,57 +285,56 @@
vAPI.setTimeout(processClean, processCleanPeriod);
};
- /******************************************************************************/
-
- var findAndRecordPageCookies = function(pageStore) {
- for ( var cookieKey of cookieDict.keys() ) {
- if ( cookieMatchDomains(cookieKey, pageStore.allHostnamesString) ) {
- recordPageCookie(pageStore, cookieKey);
+ let findAndRecordPageCookies = function (pageStore) {
+ for (let key of CookieCache.keys()) {
+ if (CookieUtils.matchDomains(key, pageStore.allHostnamesString)) {
+ recordPageCookie(pageStore, key);
}
}
};
- /******************************************************************************/
-
- var findAndRemovePageCookies = function(pageStore) {
- for ( var cookieKey of cookieDict.keys() ) {
- if ( cookieMatchDomains(cookieKey, pageStore.allHostnamesString) ) {
- removeCookieAsync(cookieKey);
+ let findAndRemovePageCookies = function (pageStore) {
+ for (let key of CookieCache.keys()) {
+ if (CookieUtils.matchDomains(key, pageStore.allHostnamesString)) {
+ removeCookieAsync(key);
}
}
};
- /******************************************************************************/
-
- var canRemoveCookie = function(cookieKey, srcHostnames) {
- var cookieEntry = cookieDict.get(cookieKey);
- if ( cookieEntry === undefined ) { return false; }
+ let canRemoveCookie = function (key, srcHostnames) {
+ let entry = CookieCache.get(key);
+ if (entry === undefined) {
+ return false;
+ }
- var cookieHostname = cookieEntry.hostname;
- var srcHostname;
+ let cookieHostname = entry.hostname;
+ let srcHostname;
- for ( srcHostname of cookieEntry.usedOn ) {
- if ( ηm.mustAllow(srcHostname, cookieHostname, 'cookie') ) {
+ for (srcHostname of entry.usedOn) {
+ if (ηm.mustAllow(srcHostname, cookieHostname, 'cookie')) {
return false;
}
}
+
// Maybe there is a scope in which the cookie is 1st-party-allowed.
// For example, if I am logged in into `github.com`, I do not want to be
// logged out just because I did not yet open a `github.com` page after
// re-starting the browser.
srcHostname = cookieHostname;
- var pos;
+
+ let pos;
+
for (;;) {
- if ( srcHostnames.has(srcHostname) ) {
- if ( ηm.mustAllow(srcHostname, cookieHostname, 'cookie') ) {
+ if (srcHostnames.has(srcHostname)) {
+ if (ηm.mustAllow(srcHostname, cookieHostname, 'cookie')) {
return false;
}
}
- if ( srcHostname === cookieEntry.domain ) {
+ if (srcHostname === entry.domain) {
break;
}
pos = srcHostname.indexOf('.');
- if ( pos === -1 ) {
+ if (pos === -1) {
break;
}
srcHostname = srcHostname.slice(pos + 1);
@@ -472,81 +342,73 @@
return true;
};
- /******************************************************************************/
-
// Listen to any change in cookieland, we will update page stats accordingly.
- vAPI.cookies.onChanged = function(cookie) {
+ vAPI.cookies.onChanged = function (cookie) {
// rhill 2013-12-11: If cookie value didn't change, no need to record.
// https://github.com/gorhill/httpswitchboard/issues/79
- var cookieKey = cookieKeyFromCookie(cookie);
- var cookieEntry = cookieDict.get(cookieKey);
- if ( cookieEntry === undefined ) {
- cookieEntry = addCookieToDict(cookie);
+ let key = CookieUtils.keyFromCookie(cookie);
+ let entry = CookieCache.get(key);
+
+ if (entry === undefined) {
+ entry = CookieCache.add(cookie);
} else {
- cookieEntry.tstamp = Date.now();
- if ( cookie.value === cookieEntry.value ) { return; }
- cookieEntry.value = cookie.value;
+ entry.tstamp = Date.now();
+ if (cookie.value === entry.value) {
+ 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.
- var pageStores = ηm.pageStores;
- var pageStore;
- for ( var tabId in pageStores ) {
- if ( pageStores.hasOwnProperty(tabId) === false ) {
+ let pageStores = ηm.pageStores;
+ let pageStore;
+ for (let tabId in pageStores) {
+ if (pageStores.hasOwnProperty(tabId) === false) {
continue;
}
pageStore = pageStores[tabId];
- if ( !cookieMatchDomains(cookieKey, pageStore.allHostnamesString) ) {
+ if (!CookieUtils.matchDomains(key, pageStore.allHostnamesString)) {
continue;
}
- recordPageCookie(pageStore, cookieKey);
+ recordPageCookie(pageStore, key);
}
};
- /******************************************************************************/
-
// Listen to any change in cookieland, we will update page stats accordingly.
- vAPI.cookies.onRemoved = function(cookie) {
- var cookieKey = cookieKeyFromCookie(cookie);
- if ( removeCookieFromDict(cookieKey) ) {
- ηm.logger.writeOne('', 'info', 'cookie', i18nCookieDeleteSuccess.replace('{{value}}', cookieKey));
+ vAPI.cookies.onRemoved = function (cookie) {
+ let key = CookieUtils.keyFromCookie(cookie);
+ if (CookieCache.remove(key)) {
+ ηm.logger.writeOne('', 'info', 'cookie',
+ i18nCookieDeleteSuccess.replace('{{value}}',
+ key));
}
};
- /******************************************************************************/
-
// Listen to any change in cookieland, we will update page stats accordingly.
- vAPI.cookies.onAllRemoved = function() {
- for ( var cookieKey of cookieDict.keys() ) {
- if ( removeCookieFromDict(cookieKey) ) {
- ηm.logger.writeOne('', 'info', 'cookie', i18nCookieDeleteSuccess.replace('{{value}}', cookieKey));
+ vAPI.cookies.onAllRemoved = function () {
+ for (let key of CookieCache.keys()) {
+ if (CookieCache.remove(key)) {
+ ηm.logger.writeOne('', 'info', 'cookie',
+ i18nCookieDeleteSuccess.replace('{{value}}',
+ key));
}
}
};
- /******************************************************************************/
-
- vAPI.cookies.getAll(addCookiesToDict);
+ vAPI.cookies.getAll(CookieCache.addVector);
vAPI.cookies.start();
vAPI.setTimeout(processRemoveQueue, processRemoveQueuePeriod);
vAPI.setTimeout(processClean, processCleanPeriod);
- /******************************************************************************/
-
// Expose only what is necessary
return {
recordPageCookies: recordPageCookiesAsync,
removePageCookies: removePageCookiesAsync
};
-
- /******************************************************************************/
-
})();
-
-/******************************************************************************/
diff --git a/js/dashboard-common.js b/js/dashboard-common.js
index 68ba7d5..9eba87c 100644
--- a/js/dashboard-common.js
+++ b/js/dashboard-common.js
@@ -23,22 +23,14 @@
'use strict';
-/******************************************************************************/
-
-uDom.onLoad(function() {
-
- /******************************************************************************/
-
+uDom.onLoad(function () {
// Open links in the proper window
uDom('a').attr('target', '_blank');
uDom('a[href*="dashboard.html"]').attr('target', '_parent');
- uDom('.whatisthis').on('click', function() {
- uDom(this).parent()
+ uDom('.whatisthis').on('click', function () {
+ uDom(this)
+ .parent()
.descendants('.whatisthis-expandable')
.toggleClass('whatisthis-expanded');
});
-
-
- /******************************************************************************/
-
});
diff --git a/js/dashboard.js b/js/dashboard.js
index 5818d5c..196d900 100644
--- a/js/dashboard.js
+++ b/js/dashboard.js
@@ -21,37 +21,29 @@
uMatrix Home: https://github.com/gorhill/uMatrix
*/
-/* global uDom */
-
'use strict';
-/******************************************************************************/
-
(function() {
-
- var loadDashboardPanel = function(hash) {
+ let loadDashboardPanel = function (hash) {
var button = uDom(hash);
var url = button.attr('data-dashboard-panel-url');
uDom('iframe').attr('src', url);
- uDom('.tabButton').forEach(function(button){
- button.toggleClass(
- 'selected',
- button.attr('data-dashboard-panel-url') === url
- );
+ uDom('.tabButton').forEach(function (button) {
+ button.toggleClass('selected',
+ button.attr('data-dashboard-panel-url') === url);
});
};
- var onTabClickHandler = function() {
+ let onTabClickHandler = function () {
loadDashboardPanel(window.location.hash);
};
- uDom.onLoad(function() {
+ uDom.onLoad(function () {
window.addEventListener('hashchange', onTabClickHandler);
- var hash = window.location.hash;
- if ( hash.length < 2 ) {
+ let hash = window.location.hash;
+ if (hash.length < 2) {
hash = '#settings';
}
loadDashboardPanel(hash);
});
-
})();
diff --git a/js/hosts-files.js b/js/hosts-files.js
index 8c65648..ba57bde 100644
--- a/js/hosts-files.js
+++ b/js/hosts-files.js
@@ -21,25 +21,16 @@
uMatrix Home: https://github.com/gorhill/uMatrix
*/
-/* global uDom */
-
'use strict';
-/******************************************************************************/
-
-(function() {
-
- /******************************************************************************/
-
- var listDetails = {},
- lastUpdateTemplateString = vAPI.i18n('hostsFilesLastUpdate'),
- hostsFilesSettingsHash,
- reValidExternalList = /[a-z-]+:\/\/\S*\/\S+/;
+(function () {
+ let listDetails = {};
+ let lastUpdateTemplateString = vAPI.i18n('hostsFilesLastUpdate');
+ let hostsFilesSettingsHash;
+ let reValidExternalList = /[a-z-]+:\/\/\S*\/\S+/;
- /******************************************************************************/
-
- vAPI.messaging.addListener(function onMessage(msg) {
- switch ( msg.what ) {
+ vAPI.messaging.addListener(function (msg) {
+ switch (msg.what) {
case 'assetUpdated':
updateAssetStatus(msg);
break;
@@ -54,44 +45,51 @@
}
});
- /******************************************************************************/
-
- var renderNumber = function(value) {
+ let renderNumber = function (value) {
return value.toLocaleString();
};
- /******************************************************************************/
-
- var renderHostsFiles = function(soft) {
- var listEntryTemplate = uDom('#templates .listEntry'),
- listStatsTemplate = vAPI.i18n('hostsFilesPerFileStats'),
- renderElapsedTimeToString = vAPI.i18n.renderElapsedTimeToString,
- reExternalHostFile = /^https?:/;
+ let renderHostsFiles = function (soft) {
+ 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
- var listNameFromListKey = function(listKey) {
- var list = listDetails.current[listKey] || listDetails.available[listKey];
- var listTitle = list ? list.title : '';
- if ( listTitle === '' ) { return listKey; }
+ let listNameFromListKey = function (listKey) {
+ let list =
+ listDetails.current[listKey] || listDetails.available[listKey];
+ let listTitle = list ? list.title : '';
+
+ if (listTitle === '') {
+ return listKey;
+ }
+
return listTitle;
};
- var liFromListEntry = function(listKey, li) {
- var entry = listDetails.available[listKey],
- elem;
- if ( !li ) {
+ let liFromListEntry = function (listKey, li) {
+ let entry = listDetails.available[listKey];
+ let elem;
+
+ if (!li) {
li = listEntryTemplate.clone().nodeAt(0);
}
- if ( li.getAttribute('data-listkey') !== listKey ) {
+
+ if (li.getAttribute('data-listkey') !== listKey) {
li.setAttribute('data-listkey', listKey);
+
elem = li.querySelector('input[type="checkbox"]');
- elem.checked = entry.off !== true;
+ elem.checked = (entry.off !== true);
elem = li.querySelector('a:nth-of-type(1)');
- elem.setAttribute('href', 'asset-viewer.html?url=' + encodeURI(listKey));
+ elem.setAttribute('href',
+ 'asset-viewer.html?url=' + encodeURI(listKey));
elem.setAttribute('type', 'text/html');
elem.textContent = listNameFromListKey(listKey);
+
li.classList.remove('toRemove');
- if ( entry.supportName ) {
+
+ if (entry.supportName) {
li.classList.add('support');
elem = li.querySelector('a.support');
elem.setAttribute('href', entry.supportURL);
@@ -99,12 +97,14 @@
} else {
li.classList.remove('support');
}
- if ( entry.external ) {
+
+ if (entry.external) {
li.classList.add('external');
} 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);
@@ -112,40 +112,51 @@
li.classList.remove('mustread');
}
}
+
// https://github.com/gorhill/uBlock/issues/1429
- if ( !soft ) {
+ if (!soft) {
elem = li.querySelector('input[type="checkbox"]');
elem.checked = entry.off !== true;
}
+
elem = li.querySelector('span.counts');
- var text = '';
- if ( !isNaN(+entry.entryUsedCount) && !isNaN(+entry.entryCount) ) {
+
+ let text = '';
+ if (!isNaN(+entry.entryUsedCount) && !isNaN(+entry.entryCount)) {
text = listStatsTemplate
- .replace('{{used}}', renderNumber(entry.off ? 0 : entry.entryUsedCount))
- .replace('{{total}}', renderNumber(entry.entryCount));
+ .replace('{{used}}',
+ renderNumber(entry.off ? 0 : entry.entryUsedCount))
+ .replace('{{total}}',
+ renderNumber(entry.entryCount));
}
+
elem.textContent = text;
+
// https://github.com/chrisaljoudi/uBlock/issues/104
- var asset = listDetails.cache[listKey] || {};
- var remoteURL = asset.remoteURL;
- li.classList.toggle(
- 'unsecure',
- typeof remoteURL === 'string' && remoteURL.lastIndexOf('http:', 0) === 0
- );
+ let asset = listDetails.cache[listKey] || {};
+ let remoteURL = asset.remoteURL;
+
+ li.classList.toggle('unsecure',
+ typeof remoteURL === 'string'
+ && remoteURL.lastIndexOf('http:', 0) === 0);
li.classList.toggle('failed', asset.error !== undefined);
li.classList.toggle('obsolete', asset.obsolete === true);
- li.classList.toggle('cached', asset.cached === true && asset.writeTime > 0);
- if ( asset.cached ) {
- li.querySelector('.status.cache').setAttribute(
- 'title',
- lastUpdateTemplateString.replace('{{ago}}', renderElapsedTimeToString(asset.writeTime))
- );
+ li.classList.toggle('cached',
+ asset.cached === true && asset.writeTime > 0);
+
+ if (asset.cached) {
+ li.querySelector('.status.cache')
+ .setAttribute('title',
+ lastUpdateTemplateString
+ .replace('{{ago}}',
+ renderETTS(asset.writeTime)));
}
+
li.classList.remove('discard');
return li;
};
- var onListsReceived = function(details) {
+ let onListsReceived = function (details) {
// Before all, set context vars
listDetails = details;
@@ -153,239 +164,239 @@
// DOM list entries.
uDom('#lists .listEntry').addClass('discard');
- var availableLists = details.available,
- listKeys = Object.keys(details.available);
+ let availableLists = 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) {
- var ta = availableLists[a].title || a,
- tb = availableLists[b].title || b;
- if ( reExternalHostFile.test(ta) === reExternalHostFile.test(tb) ) {
+ listKeys.sort(function (a, b) {
+ let ta = availableLists[a].title || a;
+ let tb = availableLists[b].title || b;
+
+ if (reExternalHostFile.test(ta) === reExternalHostFile.test(tb)) {
return ta.localeCompare(tb);
}
+
return reExternalHostFile.test(tb) ? -1 : 1;
});
- var ulList = document.querySelector('#lists');
- for ( var i = 0; i < listKeys.length; i++ ) {
- var liEntry = liFromListEntry(listKeys[i], ulList.children[i]);
- if ( liEntry.parentElement === null ) {
+ 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) {
ulList.appendChild(liEntry);
}
}
uDom('#lists .listEntry.discard').remove();
- uDom('#listsOfBlockedHostsPrompt').text(
- vAPI.i18n('hostsFilesStats').replace(
- '{{blockedHostnameCount}}',
- renderNumber(details.blockedHostnameCount)
- )
- );
- uDom('#autoUpdate').prop('checked', listDetails.autoUpdate === true);
-
- if ( !soft ) {
+ uDom('#listsOfBlockedHostsPrompt')
+ .text(vAPI.i18n('hostsFilesStats')
+ .replace('{{blockedHostnameCount}}',
+ renderNumber(details.blockedHostnameCount)));
+ uDom('#autoUpdate').prop('checked',
+ listDetails.autoUpdate === true);
+
+ if (!soft) {
hostsFilesSettingsHash = hashFromCurrentFromSettings();
}
+
renderWidgets();
};
- vAPI.messaging.send('hosts-files.js', { what: 'getLists' }, onListsReceived);
+ vAPI.messaging.send('hosts-files.js', {
+ what: 'getLists'
+ }, onListsReceived);
};
- /******************************************************************************/
-
- var renderWidgets = function() {
- uDom('#buttonUpdate').toggleClass('disabled', document.querySelector('body:not(.updating) #lists .listEntry.obsolete > input[type="checkbox"]:checked') === null);
- uDom('#buttonPurgeAll').toggleClass('disabled', document.querySelector('#lists .listEntry.cached') === null);
- uDom('#buttonApply').toggleClass('disabled', hostsFilesSettingsHash === hashFromCurrentFromSettings());
+ 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 updateAssetStatus = function (details) {
+ let li = document
+ .querySelector('#lists .listEntry[data-listkey="'+details.key+'"]');
+ if (li === null) {
+ return;
+ }
- var updateAssetStatus = function(details) {
- var li = document.querySelector('#lists .listEntry[data-listkey="' + details.key + '"]');
- if ( li === null ) { return; }
li.classList.toggle('failed', !!details.failed);
li.classList.toggle('obsolete', !details.cached);
li.classList.toggle('cached', !!details.cached);
- if ( details.cached ) {
- li.querySelector('.status.cache').setAttribute(
- 'title',
- lastUpdateTemplateString.replace(
- '{{ago}}',
- vAPI.i18n.renderElapsedTimeToString(Date.now())
- )
- );
+
+ if (details.cached) {
+ let str = vAPI.i18n.renderElapsedTimeToString(Date.now());
+ li.querySelector('.status.cache')
+ .setAttribute('title',
+ lastUpdateTemplateString.replace('{{ago}}', str));
}
+
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.
**/
-
- var hashFromCurrentFromSettings = function() {
- var hash = [],
- listHash = [],
- listEntries = document.querySelectorAll('#lists .listEntry[data-listkey]:not(.toRemove)'),
- liEntry,
- i = listEntries.length;
- while ( i-- ) {
- liEntry = listEntries[i];
- if ( liEntry.querySelector('input[type="checkbox"]:checked') !== null ) {
- listHash.push(liEntry.getAttribute('data-listkey'));
+ let hashFromCurrentFromSettings = function () {
+ let hash = [];
+ let listHash = [];
+ 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'));
}
}
- hash.push(
- listHash.sort().join(),
- reValidExternalList.test(document.getElementById('externalHostsFiles').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();
};
- /******************************************************************************/
-
- var onHostsFilesSettingsChanged = function() {
+ let onHostsFilesSettingsChanged = function () {
renderWidgets();
};
- /******************************************************************************/
+ let onRemoveExternalHostsFile = function (ev) {
+ let liEntry = uDom(this).ancestors('[data-listkey]');
+ let listKey = liEntry.attr('data-listkey');
- var onRemoveExternalHostsFile = function(ev) {
- var liEntry = uDom(this).ancestors('[data-listkey]'),
- listKey = liEntry.attr('data-listkey');
- if ( listKey ) {
+ if (listKey) {
liEntry.toggleClass('toRemove');
renderWidgets();
}
+
ev.preventDefault();
};
- /******************************************************************************/
+ let onPurgeClicked = function () {
+ let button = uDom(this);
+ let liEntry = button.ancestors('[data-listkey]');
+ let listKey = liEntry.attr('data-listkey');
- var onPurgeClicked = function() {
- var button = uDom(this),
- liEntry = button.ancestors('[data-listkey]'),
- 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');
- if ( liEntry.descendants('input').first().prop('checked') ) {
+ if (liEntry.descendants('input').first().prop('checked')) {
renderWidgets();
}
};
- /******************************************************************************/
-
- var selectHostsFiles = function(callback) {
+ let selectHostsFiles = function (callback) {
// Hosts files to select
- var toSelect = [],
- liEntries = document.querySelectorAll('#lists .listEntry[data-listkey]:not(.toRemove)'),
- i = liEntries.length,
- liEntry;
- while ( i-- ) {
- liEntry = liEntries[i];
- if ( liEntry.querySelector('input[type="checkbox"]:checked') !== null ) {
- toSelect.push(liEntry.getAttribute('data-listkey'));
+ 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'));
}
}
// External hosts files to remove
- var toRemove = [];
- liEntries = document.querySelectorAll('#lists .listEntry.toRemove[data-listkey]');
- i = liEntries.length;
- while ( i-- ) {
+ let toRemove = [];
+ liEntries = document.querySelectorAll(sel2);
+
+ for (let i=liEntries.length-1; i>=0; --i) {
toRemove.push(liEntries[i].getAttribute('data-listkey'));
}
// External hosts files to import
- var externalListsElem = document.getElementById('externalHostsFiles'),
- toImport = externalListsElem.value.trim();
+ let externalListsElem = document.getElementById('externalHostsFiles');
+ let toImport = externalListsElem.value.trim();
+
externalListsElem.value = '';
- vAPI.messaging.send(
- 'hosts-files.js',
- {
- what: 'selectHostsFiles',
- toSelect: toSelect,
- toImport: toImport,
- toRemove: toRemove
- },
- callback
- );
+ vAPI.messaging.send('hosts-files.js', {
+ what: 'selectHostsFiles',
+ toSelect: toSelect,
+ toImport: toImport,
+ toRemove: toRemove
+ }, callback);
hostsFilesSettingsHash = hashFromCurrentFromSettings();
};
- /******************************************************************************/
-
- var buttonApplyHandler = function() {
+ let buttonApplyHandler = function () {
uDom('#buttonApply').removeClass('enabled');
- selectHostsFiles(function() {
- vAPI.messaging.send('hosts-files.js', { what: 'reloadHostsFiles' });
+ selectHostsFiles(function () {
+ vAPI.messaging.send('hosts-files.js', {
+ what: 'reloadHostsFiles'
+ });
});
+
renderWidgets();
};
- /******************************************************************************/
-
- var buttonUpdateHandler = function() {
+ let buttonUpdateHandler = function () {
uDom('#buttonUpdate').removeClass('enabled');
- selectHostsFiles(function() {
+ selectHostsFiles(function () {
document.body.classList.add('updating');
- vAPI.messaging.send('hosts-files.js', { what: 'forceUpdateAssets' });
+ vAPI.messaging.send('hosts-files.js', {
+ what: 'forceUpdateAssets'
+ });
renderWidgets();
});
renderWidgets();
};
- /******************************************************************************/
-
- var buttonPurgeAllHandler = function() {
+ let buttonPurgeAllHandler = function () {
uDom('#buttonPurgeAll').removeClass('enabled');
- vAPI.messaging.send(
- 'hosts-files.js',
- { what: 'purgeAllCaches' },
- function() {
- renderHostsFiles(true);
- }
- );
+ vAPI.messaging.send('hosts-files.js', {
+ what: 'purgeAllCaches'
+ }, function () {
+ renderHostsFiles(true);
+ });
};
- /******************************************************************************/
-
- var autoUpdateCheckboxChanged = function() {
- vAPI.messaging.send(
- 'hosts-files.js',
- {
- what: 'userSettings',
- name: 'autoUpdate',
- value: this.checked
- }
- );
+ let autoUpdateCheckboxChanged = function () {
+ vAPI.messaging.send('hosts-files.js', {
+ what: 'userSettings',
+ name: 'autoUpdate',
+ value: this.checked
+ });
};
- /******************************************************************************/
-
uDom('#autoUpdate').on('change', autoUpdateCheckboxChanged);
uDom('#buttonApply').on('click', buttonApplyHandler);
uDom('#buttonUpdate').on('click', buttonUpdateHandler);
uDom('#buttonPurgeAll').on('click', buttonPurgeAllHandler);
- uDom('#lists').on('change', '.listEntry > input', onHostsFilesSettingsChanged);
- uDom('#lists').on('click', '.listEntry > a.remove', onRemoveExternalHostsFile);
+ uDom('#lists').on('change', '.listEntry > input',
+ onHostsFilesSettingsChanged);
+ uDom('#lists').on('click', '.listEntry > a.remove',
+ onRemoveExternalHostsFile);
uDom('#lists').on('click', 'span.cache', onPurgeClicked);
uDom('#externalHostsFiles').on('input', onHostsFilesSettingsChanged);
renderHostsFiles();
-
- /******************************************************************************/
-
})();
diff --git a/js/httpsb.js b/js/httpsb.js
index b235f70..824a302 100644
--- a/js/httpsb.js
+++ b/js/httpsb.js
@@ -28,6 +28,8 @@
/******************************************************************************/
(function() {
+ Cu.import('chrome://ematrix/content/lib/UriTools.jsm');
+
var ηm = ηMatrix;
ηm.pMatrix = new ηm.Matrix();
ηm.pMatrix.setSwitch('matrix-off', 'about-scheme', 1);
@@ -59,7 +61,7 @@
/******************************************************************************/
ηMatrix.hostnameFromURL = function(url) {
- var hn = this.URI.hostnameFromURI(url);
+ var hn = UriTools.hostnameFromURI(url);
return hn === '' ? '*' : hn;
};
@@ -68,7 +70,7 @@
/******************************************************************************/
ηMatrix.evaluateURL = function(srcURL, desHostname, type) {
- var srcHostname = this.URI.hostnameFromURI(srcURL);
+ var srcHostname = UriTools.hostnameFromURI(srcURL);
return this.tMatrix.evaluateCellZXY(srcHostname, desHostname, type);
};
@@ -93,7 +95,7 @@
// done only from within a scope.
ηMatrix.autoWhitelistAllTemporarily = function(pageURL) {
- var srcHostname = this.URI.hostnameFromURI(pageURL);
+ var srcHostname = UriTools.hostnameFromURI(pageURL);
if ( this.mustBlock(srcHostname, '*', '*') === false ) {
return false;
}
diff --git a/js/i18n.js b/js/i18n.js
index 3a6b0e2..7c377bb 100644
--- a/js/i18n.js
+++ b/js/i18n.js
@@ -43,13 +43,15 @@
// used to check the source text. The above comment is kept just
// in case.
- let reSafeTags = /^([\s\S]*?)<(b|blockquote|code|em|i|kbd|span|sup)>(.+?)<\/\2>([\s\S]*)$/;
+ let reSafeTags =
+ /^([\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]*)$/;
+ let reSafeLink =
+ /^([\s\S]*?)<(a href=['"]https?:\/\/[^'" <>]+['"])>(.+?)<\/a>([\s\S]*)$/;
let reLink = /^a href=(['"])(https?:\/\/[^'"]+)\1$/;
- var safeTextToTagNode = function(text) {
+ let safeTextToTagNode = function (text) {
let matches;
let node;
@@ -96,7 +98,7 @@
}
};
- var safeTextToTextNode = function(text) {
+ let safeTextToTextNode = function (text) {
if (text.indexOf('&') !== -1) {
text = text
.replace(/&ldquo;/g, '“')
@@ -110,7 +112,7 @@
return document.createTextNode(text);
};
- var safeTextToDOM = function(text, parent) {
+ let safeTextToDOM = function (text, parent) {
if (text === '') {
return;
}
@@ -150,76 +152,74 @@
safeTextToDOM(matches[4], parent);
};
- /******************************************************************************/
-
// Helper to deal with the i18n'ing of HTML files.
- vAPI.i18n.render = function(context) {
- var docu = document,
- root = context || docu,
- elems, n, i, elem, text;
-
- elems = root.querySelectorAll('[data-i18n]');
- n = elems.length;
- for ( i = 0; i < n; i++ ) {
+ vAPI.i18n.render = function (context) {
+ 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) {
elem = elems[i];
text = vAPI.i18n(elem.getAttribute('data-i18n'));
- if ( !text ) { continue; }
+ if (!text) {
+ continue;
+ }
// TODO: remove once it's all replaced with <input type="...">
- if ( text.indexOf('{') !== -1 ) {
- text = text.replace(/\{\{input:([^}]+)\}\}/g, '<input type="$1">');
+ if (text.indexOf('{') !== -1) {
+ text =
+ text.replace(/\{\{input:([^}]+)\}\}/g, '<input type="$1">');
}
safeTextToDOM(text, elem);
}
- uDom('[title]', context).forEach(function(elem) {
- var title = vAPI.i18n(elem.attr('title'));
- if ( title ) {
+ uDom('[title]', context).forEach(function (elem) {
+ let title = vAPI.i18n(elem.attr('title'));
+ if (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) {
- elem.attr(
- 'data-tip',
- vAPI.i18n(elem.attr('data-i18n-tip'))
- .replace(/<br>/g, '\n')
- .replace(/\n{3,}/g, '\n\n')
- );
+ uDom('[data-i18n-tip]', context).forEach(function (elem) {
+ elem.attr('data-tip',
+ vAPI.i18n(elem.attr('data-i18n-tip'))
+ .replace(/<br>/g, '\n')
+ .replace(/\n{3,}/g, '\n\n'));
});
};
vAPI.i18n.render();
- /******************************************************************************/
-
- vAPI.i18n.renderElapsedTimeToString = function(tstamp) {
- var value = (Date.now() - tstamp) / 60000;
- if ( value < 2 ) {
+ vAPI.i18n.renderElapsedTimeToString = function (tstamp) {
+ let value = (Date.now() - tstamp) / 60000;
+ if (value < 2) {
return vAPI.i18n('elapsedOneMinuteAgo');
}
- if ( value < 60 ) {
- return vAPI.i18n('elapsedManyMinutesAgo').replace('{{value}}', Math.floor(value).toLocaleString());
+ if (value < 60) {
+ return vAPI
+ .i18n('elapsedManyMinutesAgo')
+ .replace('{{value}}', Math.floor(value).toLocaleString());
}
value /= 60;
- if ( value < 2 ) {
+ if (value < 2) {
return vAPI.i18n('elapsedOneHourAgo');
}
- if ( value < 24 ) {
- return vAPI.i18n('elapsedManyHoursAgo').replace('{{value}}', Math.floor(value).toLocaleString());
+ if (value < 24) {
+ return vAPI
+ .i18n('elapsedManyHoursAgo')
+ .replace('{{value}}', Math.floor(value).toLocaleString());
}
value /= 24;
- if ( value < 2 ) {
+ 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/logger-ui.js b/js/logger-ui.js
index 590e67b..aefb733 100644
--- a/js/logger-ui.js
+++ b/js/logger-ui.js
@@ -21,164 +21,157 @@
uMatrix Home: https://github.com/gorhill/sessbench
*/
-/* global uDom */
-
'use strict';
-/******************************************************************************/
-
-(function() {
-
- /******************************************************************************/
-
- var tbody = document.querySelector('#content tbody');
- var trJunkyard = [];
- var tdJunkyard = [];
- var firstVarDataCol = 2; // currently, column 2 (0-based index)
- var lastVarDataIndex = 3; // currently, d0-d3
- var maxEntries = 0;
- var noTabId = '';
- var allTabIds = {};
- var allTabIdsToken;
- var ownerId = Date.now();
-
- var emphasizeTemplate = document.querySelector('#emphasizeTemplate > span');
- var hiddenTemplate = document.querySelector('#hiddenTemplate > span');
-
- var prettyRequestTypes = {
+(function () {
+ let tbody = document.querySelector('#content tbody');
+ let trJunkyard = [];
+ let tdJunkyard = [];
+ let firstVarDataCol = 2; // currently, column 2 (0-based index)
+ let lastVarDataIndex = 3; // currently, d0-d3
+ let maxEntries = 0;
+ let noTabId = '';
+ let allTabIds = {};
+ let allTabIdsToken;
+ let ownerId = Date.now();
+
+ let emphasizeTemplate = document.querySelector('#emphasizeTemplate > span');
+ let hiddenTemplate = document.querySelector('#hiddenTemplate > span');
+
+ let prettyRequestTypes = {
'main_frame': 'doc',
'stylesheet': 'css',
'sub_frame': 'frame',
'xmlhttprequest': 'xhr'
};
- var dontEmphasizeSet = new Set([
+ let dontEmphasizeSet = new Set([
'COOKIE',
'CSP',
'REFERER'
]);
- /******************************************************************************/
-
// Adjust top padding of content table, to match that of toolbar height.
- document.getElementById('content').style.setProperty(
- 'margin-top',
- document.getElementById('toolbar').clientHeight + 'px'
- );
+ document
+ .getElementById('content')
+ .style
+ .setProperty('margin-top',
+ document.getElementById('toolbar').clientHeight + 'px');
- /******************************************************************************/
-
- var classNameFromTabId = function(tabId) {
- if ( tabId === noTabId ) {
+ let classNameFromTabId = function (tabId) {
+ if (tabId === noTabId) {
return 'tab_bts';
}
- if ( tabId !== '' ) {
+ if (tabId !== '') {
return 'tab_' + tabId;
}
return '';
};
- /******************************************************************************/
-
// Emphasize hostname and cookie name.
- var emphasizeCookie = function(s) {
- var pnode = emphasizeHostname(s);
- if ( pnode.childNodes.length !== 3 ) {
+ let emphasizeCookie = function (s) {
+ let pnode = emphasizeHostname(s);
+ if (pnode.childNodes.length !== 3) {
return pnode;
}
- var prefix = '-cookie:';
- var text = pnode.childNodes[2].textContent;
- var beg = text.indexOf(prefix);
- if ( beg === -1 ) {
+
+ let prefix = '-cookie:';
+ let text = pnode.childNodes[2].textContent;
+
+ let beg = text.indexOf(prefix);
+ if (beg === -1) {
return pnode;
}
beg += prefix.length;
- var end = text.indexOf('}', beg);
- if ( end === -1 ) {
+
+ let end = text.indexOf('}', beg);
+ if (end === -1) {
return pnode;
}
- var cnode = emphasizeTemplate.cloneNode(true);
+
+ 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;
};
- /******************************************************************************/
-
// Emphasize hostname in URL.
- var emphasizeHostname = function(url) {
- var hnbeg = url.indexOf('://');
- if ( hnbeg === -1 ) {
+ let emphasizeHostname = function (url) {
+ let hnbeg = url.indexOf('://');
+ if (hnbeg === -1) {
return document.createTextNode(url);
}
hnbeg += 3;
- var hnend = url.indexOf('/', hnbeg);
- if ( hnend === -1 ) {
+ let hnend = url.indexOf('/', hnbeg);
+ if (hnend === -1) {
hnend = url.slice(hnbeg).search(/\?#/);
- if ( hnend !== -1 ) {
+ if (hnend !== -1) {
hnend += hnbeg;
} else {
hnend = url.length;
}
}
- var node = emphasizeTemplate.cloneNode(true);
+ 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;
};
- /******************************************************************************/
-
- var createCellAt = function(tr, index) {
- var td = tr.cells[index];
- var mustAppend = !td;
- if ( mustAppend ) {
+ let createCellAt = function (tr, index) {
+ let td = tr.cells[index];
+ let mustAppend = !td;
+ if (mustAppend) {
td = tdJunkyard.pop();
}
- if ( td ) {
+
+ if (td) {
td.removeAttribute('colspan');
td.textContent = '';
} else {
td = document.createElement('td');
}
- if ( mustAppend ) {
+ if (mustAppend) {
tr.appendChild(td);
}
+
return td;
};
- /******************************************************************************/
-
- var createRow = function(layout) {
- var tr = trJunkyard.pop();
- if ( tr ) {
+ let createRow = function (layout) {
+ let tr = trJunkyard.pop();
+ if (tr) {
tr.className = '';
} else {
tr = document.createElement('tr');
}
- for ( var index = 0; index < firstVarDataCol; index++ ) {
+
+ let index;
+ for (index=0; index<firstVarDataCol; ++index) {
createCellAt(tr, index);
}
- var i = 1, span = 1, td;
+
+ let i = 1, span = 1, td;
for (;;) {
td = createCellAt(tr, index);
- if ( i === lastVarDataIndex ) {
+ if (i === lastVarDataIndex) {
break;
}
- if ( layout.charAt(i) !== '1' ) {
+ if (layout.charAt(i) !== '1') {
span += 1;
} else {
- if ( span !== 1 ) {
+ if (span !== 1) {
td.setAttribute('colspan', span);
}
index += 1;
@@ -186,34 +179,30 @@
}
i += 1;
}
- if ( span !== 1 ) {
+
+ if (span !== 1) {
td.setAttribute('colspan', span);
}
index += 1;
- while ( (td = tr.cells[index]) ) {
+ while ((td = tr.cells[index])) {
tdJunkyard.push(tr.removeChild(td));
}
+
return tr;
};
- /******************************************************************************/
-
- var createHiddenTextNode = function(text) {
- var node = hiddenTemplate.cloneNode(true);
+ let createHiddenTextNode = function (text) {
+ let node = hiddenTemplate.cloneNode(true);
node.textContent = text;
return node;
};
- /******************************************************************************/
-
- var padTo2 = function(v) {
+ let padTo2 = function (v) {
return v < 10 ? '0' + v : v;
};
- /******************************************************************************/
-
- var createGap = function(tabId, url) {
- var tr = createRow('1');
+ let createGap = function (tabId, url) {
+ let tr = createRow('1');
tr.classList.add('doc');
tr.classList.add('tab');
tr.classList.add('canMtx');
@@ -222,39 +211,41 @@
tbody.insertBefore(tr, tbody.firstChild);
};
- /******************************************************************************/
-
- var renderLogEntry = function(entry) {
- var tr;
- var fvdc = firstVarDataCol;
+ let renderLogEntry = function (entry) {
+ let tr;
+ let fvdc = firstVarDataCol;
- switch ( entry.cat ) {
+ switch (entry.cat) {
case 'error':
case 'info':
tr = createRow('1');
- if ( entry.d0 === 'cookie' ) {
+ if (entry.d0 === 'cookie') {
tr.cells[fvdc].appendChild(emphasizeCookie(entry.d1));
} else {
tr.cells[fvdc].textContent = entry.d0;
}
break;
-
case 'net':
tr = createRow('111');
tr.classList.add('canMtx');
+
// If the request is that of a root frame, insert a gap in the table
// in order to visually separate entries for different documents.
- if ( entry.d2 === 'doc' && entry.tab !== noTabId ) {
+ if (entry.d2 === 'doc' && entry.tab !== noTabId) {
createGap(entry.tab, entry.d1);
}
- if ( entry.d3 ) {
+
+ if (entry.d3) {
tr.classList.add('blocked');
tr.cells[fvdc].textContent = '--';
} else {
tr.cells[fvdc].textContent = '';
}
- tr.cells[fvdc+1].textContent = (prettyRequestTypes[entry.d2] || entry.d2);
- if ( dontEmphasizeSet.has(entry.d2) ) {
+
+ tr.cells[fvdc+1].textContent =
+ (prettyRequestTypes[entry.d2] || entry.d2);
+
+ if (dontEmphasizeSet.has(entry.d2)) {
tr.cells[fvdc+2].textContent = entry.d1;
} else if ( entry.d2 === 'cookie' ) {
tr.cells[fvdc+2].appendChild(emphasizeCookie(entry.d1));
@@ -262,7 +253,6 @@
tr.cells[fvdc+2].appendChild(emphasizeHostname(entry.d1));
}
break;
-
default:
tr = createRow('1');
tr.cells[fvdc].textContent = entry.d0;
@@ -270,20 +260,22 @@
}
// Fields common to all rows.
- var time = logDate;
+ let time = logDate;
time.setTime(entry.tstamp - logDateTimezoneOffset);
- tr.cells[0].textContent = padTo2(time.getUTCHours()) + ':' +
- padTo2(time.getUTCMinutes()) + ':' +
- padTo2(time.getSeconds());
+ tr.cells[0].textContent = padTo2(time.getUTCHours())
+ + ':'
+ + padTo2(time.getUTCMinutes())
+ + ':'
+ + padTo2(time.getSeconds());
- if ( entry.tab ) {
+ if (entry.tab) {
tr.classList.add('tab');
tr.classList.add(classNameFromTabId(entry.tab));
- if ( entry.tab === noTabId ) {
+ if (entry.tab === noTabId) {
tr.cells[1].appendChild(createHiddenTextNode('bts'));
}
}
- if ( entry.cat !== '' ) {
+ if (entry.cat !== '') {
tr.classList.add('cat_' + entry.cat);
}
@@ -293,27 +285,25 @@
};
// Reuse date objects.
- var logDate = new Date(),
- logDateTimezoneOffset = logDate.getTimezoneOffset() * 60000;
-
- /******************************************************************************/
+ let logDate = new Date();
+ let logDateTimezoneOffset = logDate.getTimezoneOffset() * 60000;
- var renderLogEntries = function(response) {
- var entries = response.entries;
- if ( entries.length === 0 ) {
+ let renderLogEntries = function (response) {
+ let entries = response.entries;
+ if (entries.length === 0) {
return;
}
// Preserve scroll position
- var height = tbody.offsetHeight;
+ let height = tbody.offsetHeight;
- var tabIds = response.tabIds;
- var n = entries.length;
- var entry;
- for ( var 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 ) {
+ if (entry.tab && tabIds.hasOwnProperty(entry.tab) === false) {
continue;
}
renderLogEntry(entries[i]);
@@ -324,85 +314,94 @@
// dynamically refreshed pages.
truncateLog(maxEntries);
- var 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 ) {
- document.body.scrollTop += yDelta;
- return;
- }
+ // if (document.body.scrollTop !== 0) {
+ // document.body.scrollTop += yDelta;
+ // return;
+ // }
// Firefox:
// body.scrollTop = 0
// body.parentNode.scrollTop = good value
- var parentNode = document.body.parentNode;
- if ( parentNode && parentNode.scrollTop !== 0 ) {
+ 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;
- var synchronizeTabIds = function(newTabIds) {
- var oldTabIds = allTabIds;
- var autoDeleteVoidRows = !!vAPI.localStorage.getItem('loggerAutoDeleteVoidRows');
- var rowVoided = false;
- var trs;
- for ( var tabId in oldTabIds ) {
- if ( oldTabIds.hasOwnProperty(tabId) === false ) {
+ for (let tabId in oldTabIds) {
+ if (oldTabIds.hasOwnProperty(tabId) === false) {
continue;
}
- if ( newTabIds.hasOwnProperty(tabId) ) {
+ if (newTabIds.hasOwnProperty(tabId)) {
continue;
}
// Mark or remove voided rows
trs = uDom('.tab_' + tabId);
- if ( autoDeleteVoidRows ) {
+
+ if (autoDeleteVoidRows) {
toJunkyard(trs);
} else {
trs.removeClass('canMtx');
rowVoided = true;
}
+
// Remove popup if it is currently bound to a removed tab.
- if ( tabId === popupManager.tabId ) {
+ if (tabId === popupManager.tabId) {
popupManager.toggleOff();
}
}
- var select = document.getElementById('pageSelector');
- var selectValue = select.value;
- var 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]);
});
- var option;
- for ( var i = 0, j = 2; i < tabIds.length; i++ ) {
- tabId = tabIds[i];
- if ( tabId === noTabId ) {
+
+ let i, j;
+ for (i=0, j=2; i<tabIds.length; ++i) {
+ let tabId = tabIds[i];
+ if (tabId === noTabId) {
continue;
}
- option = select.options[j];
+
+ let option = select.options[j];
j += 1;
- if ( !option ) {
+
+ if (!option) {
option = document.createElement('option');
select.appendChild(option);
}
+
option.textContent = newTabIds[tabId];
option.value = classNameFromTabId(tabId);
- if ( option.value === selectValue ) {
+
+ if (option.value === selectValue) {
option.setAttribute('selected', '');
} else {
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', '');
@@ -414,25 +413,22 @@
return rowVoided;
};
- /******************************************************************************/
-
- var truncateLog = function(size) {
- if ( size === 0 ) {
+ let truncateLog = function (size) {
+ if (size === 0) {
size = 5000;
}
- var tbody = document.querySelector('#content tbody');
+
+ let tbody = document.querySelector('#content tbody');
size = Math.min(size, 10000);
- var tr;
- while ( tbody.childElementCount > size ) {
- tr = tbody.lastElementChild;
+
+ while (tbody.childElementCount > size) {
+ let tr = tbody.lastElementChild;
trJunkyard.push(tbody.removeChild(tr));
}
};
- /******************************************************************************/
-
- var onLogBufferRead = function(response) {
- if ( !response || response.unavailable ) {
+ let onLogBufferRead = function (response) {
+ if (!response || response.unavailable) {
readLogBufferAsync();
return;
}
@@ -441,101 +437,98 @@
noTabId = response.noTabId;
// This may have changed meanwhile
- if ( response.maxLoggedRequests !== maxEntries ) {
+ if (response.maxLoggedRequests !== maxEntries) {
maxEntries = response.maxLoggedRequests;
uDom('#maxEntries').val(maxEntries || '');
}
// Neuter rows for which a tab does not exist anymore
- var rowVoided = false;
- if ( response.tabIdsToken !== allTabIdsToken ) {
+ let rowVoided = false;
+ if (response.tabIdsToken !== allTabIdsToken) {
rowVoided = synchronizeTabIds(response.tabIds);
allTabIdsToken = response.tabIdsToken;
}
renderLogEntries(response);
- if ( rowVoided ) {
- uDom('#clean').toggleClass(
- 'disabled',
- tbody.querySelector('tr.tab:not(.canMtx)') === null
- );
+ if (rowVoided) {
+ uDom('#clean')
+ .toggleClass('disabled',
+ tbody
+ .querySelector('tr.tab:not(.canMtx)') === null);
}
// Synchronize toolbar with content of log
- uDom('#clear').toggleClass(
- 'disabled',
- tbody.querySelector('tr') === null
- );
+ uDom('#clear').toggleClass('disabled',
+ tbody.querySelector('tr') === null);
readLogBufferAsync();
};
- /******************************************************************************/
+ // This can be called only once, at init time. After that, this
+ // will be called automatically. If called after init time, this
+ // will be messy, and this would require a bit more code to ensure
+ // no multi time out events.
- // This can be called only once, at init time. After that, this will be called
- // automatically. If called after init time, this will be messy, and this would
- // require a bit more code to ensure no multi time out events.
+ let readLogBuffer = function () {
+ if (ownerId === undefined) {
+ return;
+ }
- var readLogBuffer = function() {
- if ( ownerId === undefined ) { return; }
- vAPI.messaging.send(
- 'logger-ui.js',
- { what: 'readMany', ownerId: ownerId },
- onLogBufferRead
- );
+ vAPI.messaging.send('logger-ui.js', {
+ what: 'readMany',
+ ownerId: ownerId
+ }, onLogBufferRead);
};
- var readLogBufferAsync = function() {
- if ( ownerId === undefined ) { return; }
+ let readLogBufferAsync = function () {
+ 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;
- var pageSelectorChanged = function() {
- var style = document.getElementById('tabFilterer');
- var tabClass = document.getElementById('pageSelector').value;
- var sheet = style.sheet;
- while ( sheet.cssRules.length !== 0 ) {
+ while (sheet.cssRules.length !== 0) {
sheet.deleteRule(0);
}
- if ( tabClass !== '' ) {
- sheet.insertRule(
- '#content table tr:not(.' + tabClass + ') { display: none; }',
- 0
- );
+
+ if (tabClass !== '') {
+ sheet.insertRule('#content table tr:not(.'
+ + tabClass
+ + ') { display: none; }', 0);
}
- uDom('#refresh').toggleClass(
- 'disabled',
- tabClass === '' || tabClass === 'tab_bts'
- );
+ uDom('#refresh').toggleClass('disabled',
+ tabClass === '' || tabClass === 'tab_bts');
};
- /******************************************************************************/
-
- var refreshTab = function() {
- var tabClass = document.getElementById('pageSelector').value;
- var matches = tabClass.match(/^tab_(.+)$/);
- if ( matches === null ) {
+ let refreshTab = function () {
+ 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();
- var onMaxEntriesChanged = function() {
- var raw = uDom(this).val();
try {
maxEntries = parseInt(raw, 10);
- if ( isNaN(maxEntries) ) {
+ if (isNaN(maxEntries)) {
maxEntries = 0;
}
} catch (e) {
@@ -551,51 +544,55 @@
truncateLog(maxEntries);
};
- /******************************************************************************/
-
- var rowFilterer = (function() {
- var filters = [];
+ let rowFilterer = (function () {
+ let filters = [];
- var parseInput = function() {
+ let parseInput = function () {
filters = [];
- var rawPart, hardBeg, hardEnd;
- var raw = uDom('#filterInput').val().trim();
- var rawParts = raw.split(/\s+/);
- var reStr, reStrs = [], not = false;
- var n = rawParts.length;
- for ( var i = 0; i < n; i++ ) {
+ let rawPart, hardBeg, hardEnd;
+ let raw = uDom('#filterInput').val().trim();
+ let rawParts = raw.split(/\s+/);
+ let reStr, reStrs = [], not = false;
+ let n = rawParts.length;
+
+ for (let i=0; i<n; ++i) {
rawPart = rawParts[i];
- if ( rawPart.charAt(0) === '!' ) {
- if ( reStrs.length === 0 ) {
+ if (rawPart.charAt(0) === '!') {
+ if (reStrs.length === 0) {
not = true;
}
rawPart = rawPart.slice(1);
}
+
hardBeg = rawPart.charAt(0) === '|';
- if ( hardBeg ) {
+ if (hardBeg) {
rawPart = rawPart.slice(1);
}
+
hardEnd = rawPart.slice(-1) === '|';
- if ( hardEnd ) {
+ if (hardEnd) {
rawPart = rawPart.slice(0, -1);
}
+
if ( rawPart === '' ) {
continue;
}
- // https://developer.mozilla.org/en/docs/Web/JavaScript/Guide/Regular_Expressions
+
reStr = rawPart.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
- if ( hardBeg ) {
+ if (hardBeg) {
reStr = '(?:^|\\s)' + reStr;
}
- if ( hardEnd ) {
+ if (hardEnd) {
reStr += '(?:\\s|$)';
}
+
reStrs.push(reStr);
- if ( i < (n - 1) && rawParts[i + 1] === '||' ) {
+ if (i < (n - 1) && rawParts[i + 1] === '||') {
i += 1;
continue;
}
+
reStr = reStrs.length === 1 ? reStrs[0] : reStrs.join('|');
filters.push({
re: new RegExp(reStr, 'i'),
@@ -606,77 +603,85 @@
}
};
- var filterOne = function(tr, clean) {
- var ff = filters;
- var fcount = ff.length;
- if ( fcount === 0 && clean === true ) {
+ let filterOne = function (tr, clean) {
+ let ff = filters;
+ let fcount = ff.length;
+ if (fcount === 0 && clean === true) {
return;
}
- // do not filter out doc boundaries, they help separate important
- // section of log.
- var cl = tr.classList;
- if ( cl.contains('doc') ) {
+
+ // do not filter out doc boundaries, they help separate
+ // important section of log.
+ let cl = tr.classList;
+ if (cl.contains('doc')) {
return;
}
- if ( fcount === 0 ) {
+
+ if (fcount === 0) {
cl.remove('f');
return;
}
- var cc = tr.cells;
- var ccount = cc.length;
- var hit, j, f;
+
+ let cc = tr.cells;
+ let ccount = cc.length;
+ let hit, j, f;
+
// each filter expression must hit (implicit and-op)
// if...
// positive filter expression = there must one hit on any field
// negative filter expression = there must be no hit on all fields
- for ( var i = 0; i < fcount; i++ ) {
+ for (let i=0; i<fcount; ++i) {
f = ff[i];
hit = !f.r;
- for ( j = 0; j < ccount; j++ ) {
- if ( f.re.test(cc[j].textContent) ) {
+
+ for (j=0; j<ccount; ++j) {
+ if (f.re.test(cc[j].textContent)) {
hit = f.r;
break;
}
}
- if ( !hit ) {
+
+ if (!hit) {
cl.add('f');
return;
}
}
+
cl.remove('f');
};
- var filterAll = function() {
+ let filterAll = function () {
// Special case: no filter
- if ( filters.length === 0 ) {
+ if (filters.length === 0) {
uDom('#content tr').removeClass('f');
return;
}
- var tbody = document.querySelector('#content tbody');
- var rows = tbody.rows;
- var i = rows.length;
- while ( i-- ) {
+
+ let tbody = document.querySelector('#content tbody');
+ let rows = tbody.rows;
+ for (let i=rows.length-1; i>=0; --i) {
filterOne(rows[i]);
}
};
- var onFilterChangedAsync = (function() {
- var timer = null;
- var commit = function() {
+ let onFilterChangedAsync = (function () {
+ let timer = null;
+ let commit = function () {
timer = null;
parseInput();
filterAll();
};
- return function() {
- if ( timer !== null ) {
+
+ return function () {
+ if (timer !== null) {
clearTimeout(timer);
}
timer = vAPI.setTimeout(commit, 750);
};
})();
- var onFilterButton = function() {
- var cl = document.body.classList;
+ let onFilterButton = function () {
+ let cl = document.body.classList;
cl.toggle('f', cl.contains('f') === false);
};
@@ -685,113 +690,110 @@
return {
filterOne: filterOne,
- filterAll: filterAll
+ filterAll: filterAll,
};
})();
- /******************************************************************************/
-
- var toJunkyard = function(trs) {
+ let toJunkyard = function (trs) {
trs.remove();
- var i = trs.length;
- while ( i-- ) {
+ 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;
- var clearBuffer = function() {
- var tbody = document.querySelector('#content tbody');
- var 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');
};
- /******************************************************************************/
-
- var cleanBuffer = function() {
- var rows = uDom('#content tr.tab:not(.canMtx)').remove();
- var i = rows.length;
- while ( i-- ) {
+ let cleanBuffer = function () {
+ 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');
};
- /******************************************************************************/
-
- var toggleCompactView = function() {
+ let toggleCompactView = function () {
document.body.classList.toggle('compactView');
uDom('#content table .vExpanded').removeClass('vExpanded');
};
- var toggleCompactRow = function(ev) {
+ let toggleCompactRow = function (ev) {
ev.target.parentElement.classList.toggle('vExpanded');
};
- /******************************************************************************/
-
- var popupManager = (function() {
- var realTabId = null;
- var localTabId = null;
- var container = null;
- var popup = null;
- var popupObserver = null;
- var style = null;
- var styleTemplate = [
+ let popupManager = (function () {
+ 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');
- var resizePopup = function() {
- if ( popup === null ) {
+ let resizePopup = function () {
+ if (popup === null) {
return;
}
- var popupBody = popup.contentWindow.document.body;
- if ( popupBody.clientWidth !== 0 && container.clientWidth !== popupBody.clientWidth ) {
+
+ let popupBody = popup.contentWindow.document.body;
+ if (popupBody.clientWidth !== 0
+ && container.clientWidth !== popupBody.clientWidth) {
container.style.setProperty('width', popupBody.clientWidth + 'px');
}
+
popup.style.removeProperty('height');
- if ( popupBody.clientHeight !== 0 && popup.clientHeight !== popupBody.clientHeight ) {
+ if (popupBody.clientHeight !== 0
+ && popup.clientHeight !== popupBody.clientHeight) {
popup.style.setProperty('height', popupBody.clientHeight + 'px');
}
- var ph = document.documentElement.clientHeight;
- var crect = container.getBoundingClientRect();
- if ( crect.height > ph ) {
+
+ let ph = document.documentElement.clientHeight;
+ let crect = container.getBoundingClientRect();
+ if (crect.height > ph) {
popup.style.setProperty('height', 'calc(' + ph + 'px - 1.8em)');
}
+
// Adjust width for presence/absence of vertical scroll bar which may
// have appeared as a result of last operation.
- var cw = container.clientWidth;
- var dw = popup.contentWindow.document.documentElement.clientWidth;
- if ( cw !== dw ) {
+ let cw = container.clientWidth;
+ let dw = popup.contentWindow.document.documentElement.clientWidth;
+ if (cw !== dw) {
container.style.setProperty('width', (2 * cw - dw) + 'px');
}
};
- var toggleSize = function() {
+ let toggleSize = function () {
container.classList.toggle('hide');
};
- var onResizeRequested = function() {
- var popupBody = popup.contentWindow.document.body;
- if ( popupBody.hasAttribute('data-resize-popup') === false ) {
+ let onResizeRequested = function () {
+ let popupBody = popup.contentWindow.document.body;
+ if (popupBody.hasAttribute('data-resize-popup') === false) {
return;
}
+
popupBody.removeAttribute('data-resize-popup');
resizePopup();
};
- var onLoad = function() {
+ let onLoad = function () {
resizePopup();
- var popupBody = popup.contentDocument.body;
+ let popupBody = popup.contentDocument.body;
popupBody.removeAttribute('data-resize-popup');
popupObserver.observe(popupBody, {
attributes: true,
@@ -799,21 +801,26 @@
});
};
- var toggleOn = function(td) {
- var tr = td.parentNode;
- var matches = tr.className.match(/(?:^| )tab_([^ ]+)/);
- if ( matches === null ) {
+ let toggleOn = function (td) {
+ let tr = td.parentNode;
+ let matches = tr.className.match(/(?:^| )tab_([^ ]+)/);
+ if (matches === null) {
return;
}
+
realTabId = localTabId = matches[1];
- if ( localTabId === 'bts' ) {
+ if (localTabId === 'bts') {
realTabId = noTabId;
}
container = document.getElementById('popupContainer');
- container.querySelector('div > span:nth-of-type(1)').addEventListener('click', toggleSize);
- container.querySelector('div > span:nth-of-type(2)').addEventListener('click', toggleOff);
+ container
+ .querySelector('div > span:nth-of-type(1)')
+ .addEventListener('click', toggleSize);
+ container
+ .querySelector('div > span:nth-of-type(2)')
+ .addEventListener('click', toggleOff);
popup = document.createElement('iframe');
popup.addEventListener('load', onLoad);
@@ -827,11 +834,15 @@
document.body.classList.add('popupOn');
};
- var toggleOff = function() {
+ let toggleOff = function () {
document.body.classList.remove('popupOn');
- container.querySelector('div > span:nth-of-type(1)').removeEventListener('click', toggleSize);
- container.querySelector('div > span:nth-of-type(2)').removeEventListener('click', toggleOff);
+ container
+ .querySelector('div > span:nth-of-type(1)')
+ .removeEventListener('click', toggleSize);
+ container
+ .querySelector('div > span:nth-of-type(2)')
+ .removeEventListener('click', toggleOff);
container.classList.remove('hide');
popup.removeEventListener('load', onLoad);
@@ -848,41 +859,45 @@
realTabId = null;
};
- var exports = {
- toggleOn: function(ev) {
- if ( realTabId === null ) {
+ let exports = {
+ toggleOn: function (ev) {
+ if (realTabId === null) {
toggleOn(ev.target);
}
},
- toggleOff: function() {
- if ( realTabId !== null ) {
+ toggleOff: function () {
+ if (realTabId !== null) {
toggleOff();
}
}
};
Object.defineProperty(exports, 'tabId', {
- get: function() { return realTabId || 0; }
+ get: function () {
+ return realTabId || 0;
+ },
});
return exports;
})();
- /******************************************************************************/
-
- var grabView = function() {
- if ( ownerId === undefined ) {
+ let grabView = function () {
+ if (ownerId === undefined) {
ownerId = Date.now();
}
readLogBufferAsync();
};
- var releaseView = function() {
- if ( ownerId === undefined ) { return; }
- vAPI.messaging.send(
- 'logger-ui.js',
- { what: 'releaseView', ownerId: ownerId }
- );
+ let releaseView = function () {
+ if (ownerId === undefined) {
+ return;
+ }
+
+ vAPI.messaging.send('logger-ui.js', {
+ what: 'releaseView',
+ ownerId: ownerId
+ });
+
ownerId = undefined;
};
@@ -891,8 +906,6 @@
// https://bugzilla.mozilla.org/show_bug.cgi?id=1398625
window.addEventListener('beforeunload', releaseView);
- /******************************************************************************/
-
readLogBuffer();
uDom('#pageSelector').on('change', pageSelectorChanged);
@@ -901,9 +914,8 @@
uDom('#clean').on('click', cleanBuffer);
uDom('#clear').on('click', clearBuffer);
uDom('#maxEntries').on('change', onMaxEntriesChanged);
- uDom('#content table').on('click', 'tr > td:nth-of-type(1)', toggleCompactRow);
- uDom('#content table').on('click', 'tr.canMtx > td:nth-of-type(2)', popupManager.toggleOn);
-
- /******************************************************************************/
-
+ uDom('#content table').on('click', 'tr > td:nth-of-type(1)',
+ toggleCompactRow);
+ uDom('#content table').on('click', 'tr.canMtx > td:nth-of-type(2)',
+ popupManager.toggleOn);
})();
diff --git a/js/logger.js b/js/logger.js
index 6f742b3..56be185 100644
--- a/js/logger.js
+++ b/js/logger.js
@@ -23,16 +23,12 @@
'use strict';
-/******************************************************************************/
-/******************************************************************************/
-
-ηMatrix.logger = (function() {
-
- var LogEntry = function(args) {
+ηMatrix.logger = (function () {
+ let LogEntry = function (args) {
this.init(args);
};
- LogEntry.prototype.init = function(args) {
+ LogEntry.prototype.init = function (args) {
this.tstamp = Date.now();
this.tab = args[0] || '';
this.cat = args[1] || '';
@@ -50,45 +46,49 @@
// unused, and thus removed from memory.
var logBufferObsoleteAfter = 30 * 1000;
- var janitor = function() {
- if (
- buffer !== null &&
- lastReadTime < (Date.now() - logBufferObsoleteAfter)
- ) {
+ var janitor = function () {
+ if (buffer !== null
+ && lastReadTime < (Date.now() - logBufferObsoleteAfter)) {
buffer = null;
writePtr = 0;
api.ownerId = undefined;
}
- if ( buffer !== null ) {
+ if (buffer !== null) {
vAPI.setTimeout(janitor, logBufferObsoleteAfter);
}
};
var api = {
ownerId: undefined,
- writeOne: function() {
- if ( buffer === null ) { return; }
- if ( writePtr === buffer.length ) {
+
+ writeOne: function () {
+ if (buffer === null) {
+ return;
+ }
+
+ if (writePtr === buffer.length) {
buffer.push(new LogEntry(arguments));
} else {
buffer[writePtr].init(arguments);
}
+
writePtr += 1;
},
- readAll: function(ownerId) {
+ readAll: function (ownerId) {
this.ownerId = ownerId;
- if ( buffer === null ) {
+
+ if (buffer === null) {
buffer = [];
vAPI.setTimeout(janitor, logBufferObsoleteAfter);
}
+
var out = buffer.slice(0, writePtr);
writePtr = 0;
lastReadTime = Date.now();
+
return out;
}
};
return api;
})();
-
-/******************************************************************************/
diff --git a/js/matrix.js b/js/matrix.js
index 6af5ab1..9a74cc7 100644
--- a/js/matrix.js
+++ b/js/matrix.js
@@ -21,7 +21,6 @@
uMatrix Home: https://github.com/gorhill/uMatrix
*/
-/* global punycode */
/* jshint bitwise: false */
'use strict';
@@ -30,6 +29,9 @@
ηMatrix.Matrix = (function() {
+ Cu.import('chrome://ematrix/content/lib/Punycode.jsm');
+ Cu.import('chrome://ematrix/content/lib/UriTools.jsm');
+
/******************************************************************************/
var ηm = ηMatrix;
@@ -179,7 +181,7 @@
if ( srcHostname === '*' || desHostname === '*' || desHostname === '1st-party' ) {
return '';
}
- var ηmuri = ηm.URI;
+ var ηmuri = UriTools;
var srcDomain = ηmuri.domainFromHostname(srcHostname) || srcHostname;
var desDomain = ηmuri.domainFromHostname(desHostname) || desHostname;
return desDomain === srcDomain ? desDomain : '';
@@ -607,8 +609,8 @@
val = this.evaluateCell(srcHostname, desHostname, type);
if ( val === 0 ) { continue; }
out.push(
- punycode.toUnicode(srcHostname) + ' ' +
- punycode.toUnicode(desHostname) + ' ' +
+ Punycode.toUnicode(srcHostname) + ' ' +
+ Punycode.toUnicode(desHostname) + ' ' +
type + ' ' +
stateToNameMap.get(val)
);
@@ -694,7 +696,7 @@
switchName = fieldVal.slice(0, pos);
}
if ( switchBitOffsets.has(switchName) ) {
- srcHostname = punycode.toASCII(fields[1]);
+ srcHostname = Punycode.toASCII(fields[1]);
// No state field: reject
fieldVal = fields[2];
@@ -737,8 +739,8 @@
// Lines with invalid syntax silently ignored
- srcHostname = punycode.toASCII(fields[0]);
- desHostname = punycode.toASCII(fields[1]);
+ srcHostname = Punycode.toASCII(fields[0]);
+ desHostname = Punycode.toASCII(fields[1]);
fieldVal = fields[2];
diff --git a/js/messaging.js b/js/messaging.js
index 3252872..4864e90 100644
--- a/js/messaging.js
+++ b/js/messaging.js
@@ -30,6 +30,8 @@
(function() {
+ Cu.import('chrome://ematrix/content/lib/UriTools.jsm');
+
var ηm = ηMatrix;
/******************************************************************************/
@@ -220,7 +222,6 @@
r.rows['1st-party'] = new RowSnapshot(r.scope, '1st-party', '1st-party');
r.rowCount += 1;
- var ηmuri = ηm.URI;
var reqType, reqHostname, reqDomain;
var desHostname;
var row, typeIndex;
@@ -236,7 +237,7 @@
if ( reqHostname === '' ) {
reqHostname = pageStore.pageHostname;
}
- reqDomain = ηmuri.domainFromHostname(reqHostname) || reqHostname;
+ reqDomain = UriTools.domainFromHostname(reqHostname) || reqHostname;
// We want rows of self and ancestors
desHostname = reqHostname;
@@ -404,9 +405,9 @@
var foundInlineCode = function(tabId, pageStore, details, type) {
if ( pageStore === null ) { return; }
- var pageHostname = pageStore.pageHostname,
- ηmuri = ηm.URI.set(details.documentURI),
- frameURL = ηmuri.normalizedURI();
+ let pageHostname = pageStore.pageHostname;
+ let ηmuri = UriTools.set(details.documentURI);
+ let frameURL = UriTools.normalizedURI();
var blocked = details.blocked;
if ( blocked === undefined ) {
@@ -433,7 +434,7 @@
var blocked = ηm.mustBlock(
tabContext.rootHostname,
- ηm.URI.hostnameFromURI(originURL),
+ UriTools.hostnameFromURI(originURL),
'cookie'
);
@@ -553,7 +554,7 @@
case 'securityPolicyViolation':
if ( request.directive === 'worker-src' ) {
- var url = ηm.URI.hostnameFromURI(request.blockedURI) !== '' ?
+ var url = UriTools.hostnameFromURI(request.blockedURI) !== '' ?
request.blockedURI :
request.documentURI;
if ( pageStore !== null ) {
@@ -716,7 +717,7 @@
/******************************************************************************/
var prepEntries = function(entries) {
- var ηmuri = ηm.URI;
+ var ηmuri = UriTools;
var entry;
for ( var k in entries ) {
if ( entries.hasOwnProperty(k) === false ) {
diff --git a/js/pagestats.js b/js/pagestats.js
index d5c2cdd..3490071 100644
--- a/js/pagestats.js
+++ b/js/pagestats.js
@@ -27,6 +27,8 @@
ηMatrix.pageStoreFactory = (function() {
+ Cu.import('chrome://ematrix/content/lib/UriTools.jsm');
+
/******************************************************************************/
var ηm = ηMatrix;
@@ -179,7 +181,7 @@
request.toFilter.length !== 0
) {
var roothn = tabContext.rootHostname,
- hnFromURI = ηm.URI.hostnameFromURI,
+ hnFromURI = UriTools.hostnameFromURI,
tMatrix = ηm.tMatrix;
for ( entry of request.toFilter ) {
if ( tMatrix.mustBlock(roothn, hnFromURI(entry.url), entry.type) === false ) {
@@ -216,7 +218,7 @@
// - remember which hostname/type were seen
// - count the number of distinct URLs for any given
// hostname-type pair
- var hostname = ηm.URI.hostnameFromURI(url),
+ var hostname = UriTools.hostnameFromURI(url),
key = hostname + ' ' + type,
uids = this.hostnameTypeCells.get(key);
if ( uids === undefined ) {
diff --git a/js/popup.js b/js/popup.js
index 2197c5c..a5a48d7 100644
--- a/js/popup.js
+++ b/js/popup.js
@@ -21,32 +21,28 @@
uMatrix Home: https://github.com/gorhill/uMatrix
*/
-/* global punycode, uDom */
-/* jshint esnext: true, bitwise: false */
-
'use strict';
-/******************************************************************************/
-/******************************************************************************/
-
-(function() {
+(function () {
- /******************************************************************************/
- /******************************************************************************/
+ Cu.import('chrome://ematrix/content/lib/Punycode.jsm');
// Stuff which is good to do very early so as to avoid visual glitches.
-
- (function() {
- var paneContentPaddingTop = vAPI.localStorage.getItem('paneContentPaddingTop'),
- touchDevice = vAPI.localStorage.getItem('touchDevice');
-
- if ( typeof paneContentPaddingTop === 'string' ) {
- document.querySelector('.paneContent').style.setProperty(
- 'padding-top',
- paneContentPaddingTop
- );
+ (function () {
+ let paneContentPaddingTop =
+ vAPI.localStorage.getItem('paneContentPaddingTop');
+ let touchDevice = vAPI.localStorage.getItem('touchDevice');
+
+ if (typeof paneContentPaddingTop === 'string') {
+ document
+ .querySelector('.paneContent')
+ .style
+ .setProperty('padding-top',
+ paneContentPaddingTop);
}
- if ( touchDevice === 'true' ) {
+
+ /* This is for CSS */
+ if (touchDevice === 'true') {
document.body.setAttribute('data-touch', 'true');
} else {
document.addEventListener('touchstart', function onTouched(ev) {
@@ -58,54 +54,58 @@
}
})();
- var popupWasResized = function() {
+ let popupWasResized = function () {
document.body.setAttribute('data-resize-popup', '');
};
- var resizePopup = (function() {
- var timer;
- var fix = function() {
+ let resizePopup = (function () {
+ let timer;
+
+ let fix = function () {
timer = undefined;
- var doc = document;
+ let doc = document;
// Manually adjust the position of the main matrix according to the
// height of the toolbar/matrix header.
- var paddingTop = (doc.querySelector('.paneHead').clientHeight + 2) + 'px',
- paneContent = doc.querySelector('.paneContent');
- if ( paddingTop !== paneContent.style.paddingTop ) {
+ let paddingTop =
+ (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);
}
- document.body.classList.toggle(
- 'hConstrained',
- window.innerWidth < document.body.clientWidth
- );
+
+ document
+ .body
+ .classList
+ .toggle('hConstrained',
+ window.innerWidth < document.body.clientWidth);
+
popupWasResized();
};
- return function() {
- if ( timer !== undefined ) {
+
+ return function () {
+ if (timer !== undefined) {
clearTimeout(timer);
}
timer = vAPI.setTimeout(fix, 97);
};
})();
- /******************************************************************************/
- /******************************************************************************/
-
// Must be consistent with definitions in matrix.js
- var Dark = 0x80;
- var Red = 1;
- var Green = 2;
- var DarkRed = Dark | Red;
- var DarkGreen = Dark | Green;
+ let Dark = 0x80;
+ let Red = 1;
+ let Green = 2;
+ let DarkRed = Dark | Red;
+ let DarkGreen = Dark | Green;
- var matrixSnapshot = {};
- var groupsSnapshot = [];
- var allHostnamesSnapshot = 'do not leave this initial string empty';
+ let matrixSnapshot = {};
+ let groupsSnapshot = [];
+ let allHostnamesSnapshot = 'do not leave this initial string empty';
- var matrixCellHotspots = null;
+ let matrixCellHotspots = null;
- var matrixHeaderPrettyNames = {
+ let matrixHeaderPrettyNames = {
'all': '',
'cookie': '',
'css': '',
@@ -117,41 +117,37 @@
'other': ''
};
- var firstPartyLabel = '';
- var blacklistedHostnamesLabel = '';
+ let firstPartyLabel = '';
+ let blacklistedHostnamesLabel = '';
- var expandosIdGenerator = 1;
- var nodeToExpandosMap = (function() {
- if ( typeof window.Map === 'function' ) {
+ let expandosIdGenerator = 1;
+ let nodeToExpandosMap = (function () {
+ if (typeof window.Map === 'function') {
return new window.Map();
}
})();
- var expandosFromNode = function(node) {
- if (
- node instanceof HTMLElement === false &&
- typeof node.nodeAt === 'function'
- ) {
+ let expandosFromNode = function (node) {
+ if (node instanceof HTMLElement === false
+ && typeof node.nodeAt === 'function') {
node = node.nodeAt(0);
}
- if ( nodeToExpandosMap ) {
- var expandosId = node.getAttribute('data-expandos');
- if ( !expandosId ) {
+ if (nodeToExpandosMap) {
+ let expandosId = node.getAttribute('data-expandos');
+ if (!expandosId) {
expandosId = '' + (expandosIdGenerator++);
node.setAttribute('data-expandos', expandosId);
}
- var expandos = nodeToExpandosMap.get(expandosId);
- if ( expandos === undefined ) {
- nodeToExpandosMap.set(expandosId, (expandos = Object.create(null)));
+ let expandos = nodeToExpandosMap.get(expandosId);
+ if (expandos === undefined) {
+ expandos = Object.create(null);
+ nodeToExpandosMap.set(expandosId, expandos);
}
return expandos;
}
return node;
};
- /******************************************************************************/
- /******************************************************************************/
-
function getUserSetting(setting) {
return matrixSnapshot.userSettings[setting];
}
@@ -165,31 +161,22 @@
});
}
- /******************************************************************************/
-
function getUISetting(setting) {
- var 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);
}
function setUISetting(setting, value) {
- vAPI.localStorage.setItem(
- setting,
- JSON.stringify(value)
- );
+ vAPI.localStorage.setItem(setting, JSON.stringify(value));
}
- /******************************************************************************/
-
function updateMatrixSnapshot() {
matrixSnapshotPoller.pollNow();
}
- /******************************************************************************/
-
// For display purpose, create four distinct groups of rows:
// 0th: literal "1st-party" row
// 1st: page domain's related
@@ -201,76 +188,79 @@
// Try to not reshuffle groups around while popup is opened if
// no new hostname added.
- var latestDomainListSnapshot = Object.keys(matrixSnapshot.rows).sort().join();
- if ( latestDomainListSnapshot === allHostnamesSnapshot ) {
+ 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
- var pageDomain = matrixSnapshot.domain;
- var rows = matrixSnapshot.rows;
- var anyTypeOffset = matrixSnapshot.headerIndices.get('*');
- var hostname, domain;
- var row, color, count, groupIndex;
- var domainToGroupMap = {};
+ 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 ) {
+ for (hostname in rows) {
+ if (rows.hasOwnProperty(hostname) === false) {
continue;
}
- if ( hostname === '*' || hostname === '1st-party' ) {
+ if (hostname === '*' || hostname === '1st-party') {
continue;
}
domain = rows[hostname].domain;
- if ( domain === pageDomain || hostname !== domain ) {
+ if (domain === pageDomain || hostname !== domain) {
continue;
}
row = rows[domain];
color = row.temporary[anyTypeOffset];
- if ( color === DarkGreen ) {
+ if (color === DarkGreen) {
domainToGroupMap[domain] = 2;
continue;
}
- if ( color === DarkRed ) {
+ if (color === DarkRed) {
domainToGroupMap[domain] = 4;
continue;
}
count = row.counts[anyTypeOffset];
- if ( count !== 0 ) {
+ if (count !== 0) {
domainToGroupMap[domain] = 3;
continue;
}
}
+
// 2nd pass: green wins
- for ( hostname in rows ) {
- if ( rows.hasOwnProperty(hostname) === false ) {
+ for (hostname in rows) {
+ if (rows.hasOwnProperty(hostname) === false) {
continue;
}
row = rows[hostname];
domain = row.domain;
- if ( domainToGroupMap.hasOwnProperty(domain) ) {
+ if (domainToGroupMap.hasOwnProperty(domain)) {
continue;
}
color = row.temporary[anyTypeOffset];
- if ( color === DarkGreen ) {
+ if (color === DarkGreen) {
domainToGroupMap[domain] = 2;
}
}
+
// 3rd pass: gray with count wins
- for ( hostname in rows ) {
- if ( rows.hasOwnProperty(hostname) === false ) {
+ for (hostname in rows) {
+ if (rows.hasOwnProperty(hostname) === false) {
continue;
}
row = rows[hostname];
domain = row.domain;
- if ( domainToGroupMap.hasOwnProperty(domain) ) {
+ if (domainToGroupMap.hasOwnProperty(domain)) {
continue;
}
color = row.temporary[anyTypeOffset];
@@ -279,47 +269,51 @@
domainToGroupMap[domain] = 3;
}
}
+
// 4th pass: red wins whatever is left
- for ( hostname in rows ) {
- if ( rows.hasOwnProperty(hostname) === false ) {
+ for (hostname in rows) {
+ if (rows.hasOwnProperty(hostname) === false) {
continue;
}
row = rows[hostname];
domain = row.domain;
- if ( domainToGroupMap.hasOwnProperty(domain) ) {
+ if (domainToGroupMap.hasOwnProperty(domain)) {
continue;
}
color = row.temporary[anyTypeOffset];
- if ( color === DarkRed ) {
+ if (color === DarkRed) {
domainToGroupMap[domain] = 4;
}
}
+
// 5th pass: gray wins whatever is left
- for ( hostname in rows ) {
- if ( rows.hasOwnProperty(hostname) === false ) {
+ for (hostname in rows) {
+ if (rows.hasOwnProperty(hostname) === false) {
continue;
}
domain = rows[hostname].domain;
- if ( domainToGroupMap.hasOwnProperty(domain) ) {
+ if (domainToGroupMap.hasOwnProperty(domain)) {
continue;
}
domainToGroupMap[domain] = 3;
}
// Last pass: put each domain in a group
- var groups = [ {}, {}, {}, {}, {} ];
- var group;
- for ( hostname in rows ) {
- if ( rows.hasOwnProperty(hostname) === false ) {
+ let groups = [
+ {}, {}, {}, {}, {}
+ ];
+
+ for (hostname in rows) {
+ if (rows.hasOwnProperty(hostname) === false) {
continue;
}
if ( hostname === '*' ) {
continue;
}
domain = rows[hostname].domain;
- groupIndex = domainToGroupMap[domain];
- group = groups[groupIndex];
- if ( group.hasOwnProperty(domain) === false ) {
+ let groupIndex = domainToGroupMap[domain];
+ let group = groups[groupIndex];
+ if (group.hasOwnProperty(domain) === false) {
group[domain] = {};
}
group[domain][hostname] = true;
@@ -330,41 +324,40 @@
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) {
- var cl = cell.classList;
+ 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:
// the user might have collapsed/expanded one or more domains, and we don't
// want to lose all his hardwork.
function getCollapseState(domain) {
- var states = getUISetting('popupCollapseSpecificDomains');
- if ( typeof states === 'object' && states[domain] !== undefined ) {
+ let states = getUISetting('popupCollapseSpecificDomains');
+ if (typeof states === 'object' && states[domain] !== undefined) {
return states[domain];
}
return matrixSnapshot.collapseAllDomains === true;
}
function toggleCollapseState(elem) {
- if ( elem.ancestors('#matHead.collapsible').length > 0 ) {
+ if (elem.ancestors('#matHead.collapsible').length > 0) {
toggleMainCollapseState(elem);
} else {
toggleSpecificCollapseState(elem);
@@ -373,18 +366,21 @@
}
function toggleMainCollapseState(uelem) {
- var matHead = uelem.ancestors('#matHead.collapsible').toggleClass('collapsed');
- var collapsed = matrixSnapshot.collapseAllDomains = matHead.hasClass('collapsed');
- uDom('#matList .matSection.collapsible').toggleClass('collapsed', collapsed);
+ 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);
- var specificCollapseStates = getUISetting('popupCollapseSpecificDomains') || {};
- var domains = Object.keys(specificCollapseStates);
- var i = domains.length;
- var domain;
- while ( i-- ) {
- domain = domains[i];
- if ( specificCollapseStates[domain] === 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];
}
}
@@ -394,71 +390,79 @@
function toggleSpecificCollapseState(uelem) {
// Remember collapse state forever, but only if it is different
// from main collapse switch.
- var section = uelem.ancestors('.matSection.collapsible').toggleClass('collapsed'),
- domain = expandosFromNode(section).domain,
- collapsed = section.hasClass('collapsed'),
- mainCollapseState = matrixSnapshot.collapseAllDomains === true,
- specificCollapseStates = getUISetting('popupCollapseSpecificDomains') || {};
- if ( collapsed !== mainCollapseState ) {
+ 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') || {};
+
+ if (collapsed !== mainCollapseState) {
specificCollapseStates[domain] = collapsed;
- setUISetting('popupCollapseSpecificDomains', specificCollapseStates);
- } else if ( specificCollapseStates[domain] !== undefined ) {
+ setUISetting('popupCollapseSpecificDomains',
+ specificCollapseStates);
+ } else if (specificCollapseStates[domain] !== undefined) {
delete specificCollapseStates[domain];
- setUISetting('popupCollapseSpecificDomains', specificCollapseStates);
+ setUISetting('popupCollapseSpecificDomains',
+ specificCollapseStates);
}
}
- /******************************************************************************/
-
// Update count value of matrix cells(s)
function updateMatrixCounts() {
- var matCells = uDom('.matrix .matRow.rw > .matCell'),
- i = matCells.length,
- matRow, matCell, count, counts,
- headerIndices = matrixSnapshot.headerIndices,
- rows = matrixSnapshot.rows,
- expandos;
- while ( i-- ) {
+ 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) {
matCell = matCells.nodeAt(i);
expandos = expandosFromNode(matCell);
- if ( expandos.hostname === '*' || expandos.reqType === '*' ) {
+ if (expandos.hostname === '*' || expandos.reqType === '*') {
continue;
}
matRow = matCell.parentNode;
counts = matRow.classList.contains('meta') ? 'totals' : 'counts';
- count = rows[expandos.hostname][counts][headerIndices.get(expandos.reqType)];
- if ( count === expandos.count ) { continue; }
+ count = rows[expandos.hostname][counts][headerIndices
+ .get(expandos.reqType)];
+ if (count === expandos.count) {
+ continue;
+ }
expandos.count = count;
matCell.textContent = cellTextFromCount(count);
}
}
function cellTextFromCount(count) {
- if ( count === 0 ) { return '\u00A0'; }
- if ( count < 100 ) { return count; }
+ if (count === 0) {
+ return '\u00A0';
+ }
+
+ if (count < 100) {
+ return count;
+ }
+
return '99+';
}
- /******************************************************************************/
-
// Update color of matrix cells(s)
// Color changes when rules change
function updateMatrixColors() {
- var cells = uDom('.matrix .matRow.rw > .matCell').removeClass(),
- i = cells.length,
- cell, expandos;
- while ( i-- ) {
+ let cells = uDom('.matrix .matRow.rw > .matCell').removeClass();
+ let cell, expandos;
+ for (let i=cells.length-1; i>=0; --i) {
cell = cells.nodeAt(i);
expandos = expandosFromNode(cell);
addCellClass(cell, expandos.hostname, expandos.reqType);
}
+
popupWasResized();
}
- /******************************************************************************/
-
// Update behavior of matrix:
// - Whether a section is collapsible or not. It is collapsible if:
// - It has at least one subdomain AND
@@ -467,57 +471,68 @@
function updateMatrixBehavior() {
matrixList = matrixList || uDom('#matList');
- var sections = matrixList.descendants('.matSection');
- var i = sections.length;
- var section, subdomainRows, j, subdomainRow;
- while ( i-- ) {
+ 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)');
- j = subdomainRows.length;
- while ( j-- ) {
+ for (let j=subdomainRows.length-1; j>=0; --j) {
subdomainRow = subdomainRows.at(j);
- subdomainRow.toggleClass('collapsible', subdomainRow.descendants('.t81,.t82').length === 0);
+ subdomainRow.toggleClass('collapsible',
+ subdomainRow
+ .descendants('.t81,.t82')
+ .length === 0);
}
- section.toggleClass('collapsible', subdomainRows.filter('.collapsible').length > 0);
+
+ section.toggleClass('collapsible',
+ subdomainRows.filter('.collapsible').length > 0);
}
}
- /******************************************************************************/
-
// handle user interaction with filters
function getCellAction(hostname, type, leaning) {
- var temporaryColor = getTemporaryColor(hostname, type);
- var hue = temporaryColor & 0x03;
+ let temporaryColor = getTemporaryColor(hostname, type);
+ let hue = temporaryColor & 0x03;
+
// Special case: root toggle only between two states
- if ( type === '*' && hostname === '*' ) {
- return hue === Green ? 'blacklistMatrixCell' : 'whitelistMatrixCell';
+ if (type === '*' && hostname === '*') {
+ return hue === Green ?
+ 'blacklistMatrixCell' :
+ 'whitelistMatrixCell';
}
+
// When explicitly blocked/allowed, can only graylist
- var saturation = temporaryColor & 0x80;
- if ( saturation === Dark ) {
+ 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
- var cell = button.ancestors('div.matCell'),
- expandos = expandosFromNode(cell),
- type = expandos.reqType,
- desHostname = expandos.hostname;
+ 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 === '' ) {
+ if (desHostname === '') {
return;
}
- var request = {
+
+ let request = {
what: getCellAction(desHostname, type, leaning),
srcHostname: matrixSnapshot.scope,
desHostname: desHostname,
type: type
};
+
vAPI.messaging.send('popup.js', request, updateMatrixSnapshot);
}
@@ -529,164 +544,180 @@
handleFilter(button, 'blacklisting');
}
- /******************************************************************************/
-
- var matrixRowPool = [];
- var matrixSectionPool = [];
- var matrixGroupPool = [];
- var matrixRowTemplate = null;
- var matrixList = null;
+ let matrixRowPool = [];
+ let matrixSectionPool = [];
+ let matrixGroupPool = [];
+ let matrixRowTemplate = null;
+ let matrixList = null;
- var startMatrixUpdate = function() {
+ let startMatrixUpdate = function () {
matrixList = matrixList || uDom('#matList');
matrixList.detach();
- var rows = matrixList.descendants('.matRow');
+ let rows = matrixList.descendants('.matRow');
rows.detach();
matrixRowPool = matrixRowPool.concat(rows.toArray());
- var sections = matrixList.descendants('.matSection');
+ let sections = matrixList.descendants('.matSection');
sections.detach();
matrixSectionPool = matrixSectionPool.concat(sections.toArray());
- var groups = matrixList.descendants('.matGroup');
+ let groups = matrixList.descendants('.matGroup');
groups.detach();
matrixGroupPool = matrixGroupPool.concat(groups.toArray());
};
- var endMatrixUpdate = function() {
- // https://github.com/gorhill/httpswitchboard/issues/246
- // If the matrix has no rows, we need to insert a dummy one, invisible,
- // to ensure the extension pop-up is properly sized. This is needed because
- // the header pane's `position` property is `fixed`, which means it doesn't
- // affect layout size, hence the matrix header row will be truncated.
- if ( matrixSnapshot.rowCount <= 1 ) {
+ 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) {
matrixList.append(createMatrixRow().css('visibility', 'hidden'));
}
+
updateMatrixBehavior();
matrixList.css('display', '');
matrixList.appendTo('.paneContent');
};
- var createMatrixGroup = function() {
- var group = matrixGroupPool.pop();
- if ( group ) {
+ let createMatrixGroup = function () {
+ let group = matrixGroupPool.pop();
+ if (group) {
return uDom(group).removeClass().addClass('matGroup');
}
return uDom(document.createElement('div')).addClass('matGroup');
};
- var createMatrixSection = function() {
- var section = matrixSectionPool.pop();
- if ( section ) {
+ let createMatrixSection = function () {
+ let section = matrixSectionPool.pop();
+ if (section) {
return uDom(section).removeClass().addClass('matSection');
}
return uDom(document.createElement('div')).addClass('matSection');
};
- var createMatrixRow = function() {
- var row = matrixRowPool.pop();
- if ( row ) {
+ let createMatrixRow = function () {
+ 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();
};
- /******************************************************************************/
-
function renderMatrixHeaderRow() {
- var matHead = uDom('#matHead.collapsible');
- matHead.toggleClass('collapsed', matrixSnapshot.collapseAllDomains === true);
- var cells = matHead.descendants('.matCell'), cell, expandos;
- cell = cells.nodeAt(0);
- expandos = expandosFromNode(cell);
+ 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) {
- var expandos = expandosFromNode(cell);
+ let expandos = expandosFromNode(cell);
expandos.hostname = domain;
expandos.reqType = '*';
addCellClass(cell.nodeAt(0), domain, '*');
- var contents = cell.contents();
+
+ let contents = cell.contents();
contents.nodeAt(0).textContent = domain === '1st-party' ?
firstPartyLabel :
- punycode.toUnicode(domain);
+ Punycode.toUnicode(domain);
+
contents.nodeAt(1).textContent = ' ';
}
function renderMatrixCellSubdomain(cell, domain, subomain) {
- var expandos = expandosFromNode(cell);
+ let expandos = expandosFromNode(cell);
expandos.hostname = subomain;
expandos.reqType = '*';
addCellClass(cell.nodeAt(0), subomain, '*');
- var contents = cell.contents();
- contents.nodeAt(0).textContent = punycode.toUnicode(subomain.slice(0, subomain.lastIndexOf(domain)-1)) + '.';
- contents.nodeAt(1).textContent = punycode.toUnicode(domain);
+
+ 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) {
- var expandos = expandosFromNode(cell);
+ let expandos = expandosFromNode(cell);
expandos.hostname = domain;
expandos.reqType = '*';
addCellClass(cell.nodeAt(0), domain, '*');
- var contents = cell.contents();
- contents.nodeAt(0).textContent = '\u2217.' + punycode.toUnicode(domain);
+
+ let contents = cell.contents();
+ contents.nodeAt(0).textContent = '\u2217.' + Punycode.toUnicode(domain);
contents.nodeAt(1).textContent = ' ';
}
function renderMatrixCellType(cell, hostname, type, count) {
- var node = cell.nodeAt(0),
- expandos = expandosFromNode(node);
+ let node = cell.nodeAt(0);
+ let expandos = expandosFromNode(node);
expandos.hostname = hostname;
expandos.reqType = type;
expandos.count = count;
@@ -695,51 +726,55 @@
}
function renderMatrixCellTypes(cells, hostname, countName) {
- var counts = matrixSnapshot.rows[hostname][countName];
- var headerIndices = matrixSnapshot.headerIndices;
- renderMatrixCellType(cells.at(1), hostname, 'cookie', counts[headerIndices.get('cookie')]);
- renderMatrixCellType(cells.at(2), hostname, 'css', counts[headerIndices.get('css')]);
- renderMatrixCellType(cells.at(3), hostname, 'image', counts[headerIndices.get('image')]);
- renderMatrixCellType(cells.at(4), hostname, 'media', counts[headerIndices.get('media')]);
- renderMatrixCellType(cells.at(5), hostname, 'script', counts[headerIndices.get('script')]);
- renderMatrixCellType(cells.at(6), hostname, 'xhr', counts[headerIndices.get('xhr')]);
- renderMatrixCellType(cells.at(7), hostname, 'frame', counts[headerIndices.get('frame')]);
- renderMatrixCellType(cells.at(8), hostname, 'other', counts[headerIndices.get('other')]);
+ 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) {
- var matrixRow = createMatrixRow().addClass('rw');
- var cells = matrixRow.descendants('.matCell');
+ 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) {
- var matrixRow = createMatrixRow().addClass('rw');
- var cells = matrixRow.descendants('.matCell');
+ 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) {
- var matrixRow = createMatrixRow().addClass('rw');
- var cells = matrixRow.descendants('.matCell');
+ 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
- var node = cell.nodeAt(0),
- expandos = expandosFromNode(node);
+ let node = cell.nodeAt(0);
+ let expandos = expandosFromNode(node);
expandos.hostname = '';
expandos.reqType = '';
expandos.count = count;
@@ -748,87 +783,94 @@
}
function makeMatrixMetaRow(totals) {
- var headerIndices = matrixSnapshot.headerIndices,
- matrixRow = createMatrixRow().at(0).addClass('ro'),
- cells = matrixRow.descendants('.matCell'),
- contents = cells.at(0).addClass('t81').contents(),
- expandos = expandosFromNode(cells.nodeAt(0));
+ 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')]);
+ 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) {
- var headerIndices = matrixSnapshot.headerIndices,
- anyTypeIndex = headerIndices.get('*'),
- n = headerIndices.size,
- totals = new Array(n),
- i = n;
- while ( i-- ) {
+ 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;
}
- var rows = matrixSnapshot.rows, row;
- for ( var hostname in rows ) {
- if ( rows.hasOwnProperty(hostname) === false ) {
+
+ let rows = matrixSnapshot.rows;
+ let row;
+ for (let hostname in rows) {
+ if (rows.hasOwnProperty(hostname) === false) {
continue;
}
row = rows[hostname];
- if ( group.hasOwnProperty(row.domain) === false ) {
+ if (group.hasOwnProperty(row.domain) === false) {
continue;
}
- if ( row.counts[anyTypeIndex] === 0 ) {
+ if (row.counts[anyTypeIndex] === 0) {
continue;
}
totals[0] += 1;
- for ( i = 1; i < n; i++ ) {
+ for (let i=1; i<headerIndices.size; ++i) {
totals[i] += row.counts[i];
}
}
+
return totals;
}
- /******************************************************************************/
-
// Compare hostname helper, to order hostname in a logical manner:
// top-most < bottom-most, take into account whether IP address or
// named hostname
- function hostnameCompare(a,b) {
+ function hostnameCompare(a, b) {
// Normalize: most significant parts first
- if ( !a.match(/^\d+(\.\d+){1,3}$/) ) {
- var aa = a.split('.');
+ 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}$/) ) {
- var bb = b.split('.');
+
+ 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);
}
- /******************************************************************************/
-
function makeMatrixGroup0SectionDomain() {
return makeMatrixRowDomain('1st-party').addClass('g0 l1');
}
function makeMatrixGroup0Section() {
- var domainDiv = createMatrixSection();
+ let domainDiv = createMatrixSection();
expandosFromNode(domainDiv).domain = '1st-party';
makeMatrixGroup0SectionDomain().appendTo(domainDiv);
return domainDiv;
@@ -837,24 +879,20 @@
function makeMatrixGroup0() {
// Show literal "1st-party" row only if there is
// at least one 1st-party hostname
- if ( Object.keys(groupsSnapshot[1]).length === 0 ) {
+ if (Object.keys(groupsSnapshot[1]).length === 0) {
return;
}
- var groupDiv = createMatrixGroup().addClass('g0');
+ 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) {
@@ -862,17 +900,16 @@
}
function makeMatrixGroup1Section(hostnames) {
- var domain = hostnames[0];
- var domainDiv = createMatrixSection()
- .toggleClass('collapsed', getCollapseState(domain));
+ let domain = hostnames[0];
+ let domainDiv =
+ createMatrixSection().toggleClass('collapsed',
+ getCollapseState(domain));
expandosFromNode(domainDiv).domain = domain;
- if ( hostnames.length > 1 ) {
- makeMatrixGroup1SectionMetaDomain(domain)
- .appendTo(domainDiv);
+ if (hostnames.length > 1) {
+ makeMatrixGroup1SectionMetaDomain(domain).appendTo(domainDiv);
}
- makeMatrixGroup1SectionDomain(domain)
- .appendTo(domainDiv);
- for ( var i = 1; i < hostnames.length; i++ ) {
+ makeMatrixGroup1SectionDomain(domain).appendTo(domainDiv);
+ for (let i=1; i<hostnames.length; ++i) {
makeMatrixGroup1SectionSubomain(domain, hostnames[i])
.appendTo(domainDiv);
}
@@ -880,29 +917,27 @@
}
function makeMatrixGroup1(group) {
- var domains = Object.keys(group).sort(hostnameCompare);
- if ( domains.length ) {
- var groupDiv = createMatrixGroup().addClass('g1');
- makeMatrixGroup1Section(Object.keys(group[domains[0]]).sort(hostnameCompare))
+ 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);
- for ( var i = 1; i < domains.length; i++ ) {
- makeMatrixGroup1Section(Object.keys(group[domains[i]]).sort(hostnameCompare))
+ for (let i=1; i<domains.length; ++i) {
+ 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) {
@@ -910,16 +945,16 @@
}
function makeMatrixGroup2Section(hostnames) {
- var domain = hostnames[0];
- var domainDiv = createMatrixSection()
- .toggleClass('collapsed', getCollapseState(domain));
+ let domain = hostnames[0];
+ let domainDiv =
+ createMatrixSection().toggleClass('collapsed',
+ getCollapseState(domain));
expandosFromNode(domainDiv).domain = domain;
- if ( hostnames.length > 1 ) {
+ if (hostnames.length > 1) {
makeMatrixGroup2SectionMetaDomain(domain).appendTo(domainDiv);
}
- makeMatrixGroup2SectionDomain(domain)
- .appendTo(domainDiv);
- for ( var i = 1; i < hostnames.length; i++ ) {
+ makeMatrixGroup2SectionDomain(domain).appendTo(domainDiv);
+ for (let i=1; i<hostnames.length; ++i) {
makeMatrixGroup2SectionSubomain(domain, hostnames[i])
.appendTo(domainDiv);
}
@@ -927,30 +962,27 @@
}
function makeMatrixGroup2(group) {
- var domains = Object.keys(group).sort(hostnameCompare);
- if ( domains.length) {
- var groupDiv = createMatrixGroup()
- .addClass('g2');
- makeMatrixGroup2Section(Object.keys(group[domains[0]]).sort(hostnameCompare))
+ 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);
- for ( var i = 1; i < domains.length; i++ ) {
- makeMatrixGroup2Section(Object.keys(group[domains[i]]).sort(hostnameCompare))
+ for (let i=1; i<domains.length; ++i) {
+ 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) {
@@ -958,16 +990,15 @@
}
function makeMatrixGroup3Section(hostnames) {
- var domain = hostnames[0];
- var domainDiv = createMatrixSection()
- .toggleClass('collapsed', getCollapseState(domain));
+ let domain = hostnames[0];
+ let domainDiv = createMatrixSection().toggleClass('collapsed',
+ getCollapseState(domain));
expandosFromNode(domainDiv).domain = domain;
- if ( hostnames.length > 1 ) {
+ if (hostnames.length > 1) {
makeMatrixGroup3SectionMetaDomain(domain).appendTo(domainDiv);
}
- makeMatrixGroup3SectionDomain(domain)
- .appendTo(domainDiv);
- for ( var i = 1; i < hostnames.length; i++ ) {
+ makeMatrixGroup3SectionDomain(domain).appendTo(domainDiv);
+ for (let i=1; i<hostnames.length; ++i) {
makeMatrixGroup3SectionSubomain(domain, hostnames[i])
.appendTo(domainDiv);
}
@@ -975,39 +1006,35 @@
}
function makeMatrixGroup3(group) {
- var domains = Object.keys(group).sort(hostnameCompare);
- if ( domains.length) {
- var groupDiv = createMatrixGroup()
- .addClass('g3');
- makeMatrixGroup3Section(Object.keys(group[domains[0]]).sort(hostnameCompare))
+ 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);
- for ( var i = 1; i < domains.length; i++ ) {
- makeMatrixGroup3Section(Object.keys(group[domains[i]]).sort(hostnameCompare))
+ for (let i=1; i<domains.length; ++i) {
+ 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) {
- var domain = hostnames[0];
- var domainDiv = createMatrixSection();
+ let domain = hostnames[0];
+ let domainDiv = createMatrixSection();
expandosFromNode(domainDiv).domain = domain;
- makeMatrixGroup4SectionDomain(domain)
- .appendTo(domainDiv);
- for ( var i = 1; i < hostnames.length; i++ ) {
+ makeMatrixGroup4SectionDomain(domain).appendTo(domainDiv);
+ for (let i=1; i<hostnames.length; ++i) {
makeMatrixGroup4SectionSubomain(domain, hostnames[i])
.appendTo(domainDiv);
}
@@ -1015,35 +1042,38 @@
}
function makeMatrixGroup4(group) {
- var domains = Object.keys(group).sort(hostnameCompare);
- if ( domains.length === 0 ) {
+ let domains = Object.keys(group).sort(hostnameCompare);
+ if (domains.length === 0) {
return;
}
- var groupDiv = createMatrixGroup().addClass('g4');
+ let groupDiv = createMatrixGroup().addClass('g4');
createMatrixSection()
.addClass('g4Meta')
- .toggleClass('g4Collapsed', !!matrixSnapshot.collapseBlacklistedDomains)
+ .toggleClass('g4Collapsed',
+ !!matrixSnapshot.collapseBlacklistedDomains)
.appendTo(groupDiv);
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 ( var i = 1; i < domains.length; i++ ) {
- makeMatrixGroup4Section(Object.keys(group[domains[i]]).sort(hostnameCompare))
+ for (let i=1; i<domains.length; ++i) {
+ makeMatrixGroup4Section(Object.keys(group[domains[i]])
+ .sort(hostnameCompare))
.appendTo(groupDiv);
}
groupDiv.appendTo(matrixList);
}
- /******************************************************************************/
-
- var makeMenu = function() {
- var groupStats = getGroupStats();
+ let makeMenu = function () {
+ 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 ) {
+ if (matrixCellHotspots) {
matrixCellHotspots.detach();
}
@@ -1062,43 +1092,37 @@
resizePopup();
};
- /******************************************************************************/
-
// Do all the stuff that needs to be done before building menu et al.
function initMenuEnvironment() {
- document.body.style.setProperty(
- 'font-size',
- getUserSetting('displayTextSize')
- );
- document.body.classList.toggle(
- 'colorblind',
- getUserSetting('colorBlindFriendly')
- );
- uDom.nodeFromId('version').textContent = matrixSnapshot.appVersion || '';
-
- var prettyNames = matrixHeaderPrettyNames;
- var keys = Object.keys(prettyNames);
- var i = keys.length;
- var cell, key, text;
- while ( i-- ) {
- key = keys[i];
- cell = uDom('#matHead .matCell[data-req-type="'+ key +'"]');
- text = vAPI.i18n(key + 'PrettyName');
+ 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();
+ blacklistedHostnamesLabel =
+ uDom('[data-i18n="matrixBlacklistedHostnames"]').text();
}
- /******************************************************************************/
-
// Create page scopes for the web page
function selectGlobalScope() {
- if ( matrixSnapshot.scope === '*' ) { return; }
+ if (matrixSnapshot.scope === '*') {
+ return;
+ }
matrixSnapshot.scope = '*';
document.body.classList.add('globalScope');
matrixSnapshot.tMatrixModifiedTime = undefined;
@@ -1107,8 +1131,10 @@
}
function selectSpecificScope(ev) {
- var newScope = ev.target.getAttribute('data-scope');
- if ( !newScope || matrixSnapshot.scope === newScope ) { return; }
+ let newScope = ev.target.getAttribute('data-scope');
+ if (!newScope || matrixSnapshot.scope === newScope) {
+ return;
+ }
document.body.classList.remove('globalScope');
matrixSnapshot.scope = newScope;
matrixSnapshot.tMatrixModifiedTime = undefined;
@@ -1118,45 +1144,49 @@
function initScopeCell() {
// It's possible there is no page URL at this point: some pages cannot
- // be filtered by uMatrix.
- if ( matrixSnapshot.url === '' ) { return; }
- var specificScope = uDom.nodeFromId('specificScope');
+ // be filtered by ηMatrix.
+ if (matrixSnapshot.url === '') {
+ return;
+ }
+ let specificScope = uDom.nodeFromId('specificScope');
- while ( specificScope.firstChild !== null ) {
+ while (specificScope.firstChild !== null) {
specificScope.removeChild(specificScope.firstChild);
}
// Fill in the scope menu entries
- var pos = matrixSnapshot.domain.indexOf('.');
- var tld, labels;
- if ( pos === -1 ) {
+ let pos = matrixSnapshot.domain.indexOf('.');
+ let tld, labels;
+ if (pos === -1) {
tld = '';
labels = matrixSnapshot.hostname;
} else {
tld = matrixSnapshot.domain.slice(pos + 1);
labels = matrixSnapshot.hostname.slice(0, -tld.length);
}
- var beg = 0, span, label;
- while ( beg < labels.length ) {
+ let beg = 0;
+ let span, label;
+ while (beg < labels.length) {
pos = labels.indexOf('.', beg);
- if ( pos === -1 ) {
+ if (pos === -1) {
pos = labels.length;
} else {
pos += 1;
}
label = document.createElement('span');
- label.appendChild(
- document.createTextNode(punycode.toUnicode(labels.slice(beg, pos)))
- );
+ label.appendChild(document
+ .createTextNode(Punycode
+ .toUnicode(labels.slice(beg,
+ pos))));
span = document.createElement('span');
span.setAttribute('data-scope', labels.slice(beg) + tld);
span.appendChild(label);
specificScope.appendChild(span);
beg = pos;
}
- if ( tld !== '' ) {
+ if (tld !== '') {
label = document.createElement('span');
- label.appendChild(document.createTextNode(punycode.toUnicode(tld)));
+ label.appendChild(document.createTextNode(Punycode.toUnicode(tld)));
span = document.createElement('span');
span.setAttribute('data-scope', tld);
span.appendChild(label);
@@ -1166,50 +1196,48 @@
}
function updateScopeCell() {
- var specificScope = uDom.nodeFromId('specificScope'),
- isGlobal = matrixSnapshot.scope === '*';
+ 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);
- for ( var node of specificScope.children ) {
- node.classList.toggle(
- 'on',
- !isGlobal &&
- matrixSnapshot.scope.endsWith(node.getAttribute('data-scope'))
- );
+
+ for (let node of specificScope.children) {
+ node.classList.toggle('on',
+ !isGlobal
+ && matrixSnapshot
+ .scope
+ .endsWith(node.getAttribute('data-scope')));
}
}
- /******************************************************************************/
-
function updateMatrixSwitches() {
- var count = 0,
- enabled,
- switches = matrixSnapshot.tSwitches;
- for ( var switchName in switches ) {
- if ( switches.hasOwnProperty(switchName) === false ) { continue; }
+ let count = 0;
+ let enabled;
+ let switches = matrixSnapshot.tSwitches;
+
+ for (let switchName in switches) {
+ if (switches.hasOwnProperty(switchName) === false) {
+ continue;
+ }
enabled = switches[switchName];
- if ( enabled && switchName !== 'matrix-off' ) {
+ if (enabled && switchName !== 'matrix-off') {
count += 1;
}
uDom('#mtxSwitch_' + switchName).toggleClass('switchTrue', enabled);
}
- uDom.nodeFromId('mtxSwitch_https-strict').classList.toggle(
- 'relevant',
- matrixSnapshot.hasMixedContent
- );
- uDom.nodeFromId('mtxSwitch_no-workers').classList.toggle(
- 'relevant',
- matrixSnapshot.hasWebWorkers
- );
- uDom.nodeFromId('mtxSwitch_referrer-spoof').classList.toggle(
- 'relevant',
- matrixSnapshot.has3pReferrer
- );
- uDom.nodeFromId('mtxSwitch_noscript-spoof').classList.toggle(
- 'relevant',
- matrixSnapshot.hasNoscriptTags
- );
+ uDom.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 =
@@ -1218,12 +1246,18 @@
}
function toggleMatrixSwitch(ev) {
- if ( ev.target.localName === 'a' ) { return; }
- var elem = ev.currentTarget;
- var pos = elem.id.indexOf('_');
- if ( pos === -1 ) { return; }
- var switchName = elem.id.slice(pos + 1);
- var request = {
+ 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
@@ -1231,46 +1265,44 @@
vAPI.messaging.send('popup.js', request, updateMatrixSnapshot);
}
- /******************************************************************************/
-
function updatePersistButton() {
- var diffCount = matrixSnapshot.diff.length;
- var button = uDom('#buttonPersist');
+ let diffCount = matrixSnapshot.diff.length;
+ let button = uDom('#buttonPersist');
+
button.contents()
- .filter(function(){return this.nodeType===3;})
+ .filter(function () {
+ return this.nodeType===3;
+ })
.first()
.text(diffCount > 0 ? '\uf13e' : '\uf023');
+
button.descendants('span.badge').text(diffCount > 0 ? diffCount : '');
- var disabled = diffCount === 0;
+
+ let disabled = diffCount === 0;
+
button.toggleClass('disabled', disabled);
uDom('#buttonRevertScope').toggleClass('disabled', disabled);
}
- /******************************************************************************/
-
function persistMatrix() {
- var request = {
+ let request = {
what: 'applyDiffToPermanentMatrix',
diff: matrixSnapshot.diff
};
vAPI.messaging.send('popup.js', request, updateMatrixSnapshot);
}
- /******************************************************************************/
-
// rhill 2014-03-12: revert completely ALL changes related to the
// current page, including scopes.
function revertMatrix() {
- var request = {
+ let request = {
what: 'applyDiffToTemporaryMatrix',
diff: matrixSnapshot.diff
};
vAPI.messaging.send('popup.js', request, updateMatrixSnapshot);
}
- /******************************************************************************/
-
// Buttons which are affected by any changes in the matrix
function updateMatrixButtons() {
@@ -1279,18 +1311,14 @@
updatePersistButton();
}
- /******************************************************************************/
-
function revertAll() {
- var request = {
+ let request = {
what: 'revertTemporaryMatrix'
};
vAPI.messaging.send('popup.js', request, updateMatrixSnapshot);
dropDownMenuHide();
}
- /******************************************************************************/
-
function buttonReloadHandler(ev) {
vAPI.messaging.send('popup.js', {
what: 'forceReloadTab',
@@ -1299,8 +1327,6 @@
});
}
- /******************************************************************************/
-
function mouseenterMatrixCellHandler(ev) {
matrixCellHotspots.appendTo(ev.target);
}
@@ -1309,11 +1335,9 @@
matrixCellHotspots.detach();
}
- /******************************************************************************/
-
function gotoExtensionURL(ev) {
- var 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,
@@ -1324,18 +1348,16 @@
vAPI.closePopup();
}
- /******************************************************************************/
-
function dropDownMenuShow(ev) {
- var button = ev.target;
- var menuOverlay = document.getElementById(button.getAttribute('data-dropdown-menu'));
- var butnRect = button.getBoundingClientRect();
- var viewRect = document.body.getBoundingClientRect();
- var butnNormalLeft = butnRect.left / (viewRect.width - butnRect.width);
+ 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');
- var menu = menuOverlay.querySelector('.dropdown-menu');
- var menuRect = menu.getBoundingClientRect();
- var menuLeft = butnNormalLeft * (viewRect.width - menuRect.width);
+ 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';
}
@@ -1344,10 +1366,8 @@
uDom('.dropdown-menu-capture').removeClass('show');
}
- /******************************************************************************/
-
- var onMatrixSnapshotReady = function(response) {
- if ( response === 'ENOTFOUND' ) {
+ let onMatrixSnapshotReady = function (response) {
+ if (response === 'ENOTFOUND') {
uDom.nodeFromId('noTabFound').textContent =
vAPI.i18n('matrixNoTabFound');
document.body.classList.add('noTabFound');
@@ -1359,7 +1379,7 @@
makeMenu();
// After popup menu is built, check whether there is a non-empty matrix
- if ( matrixSnapshot.url === '' ) {
+ if (matrixSnapshot.url === '') {
uDom('#matHead').remove();
uDom('#toolbarContainer').remove();
@@ -1371,58 +1391,58 @@
// 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;
- var matrixSnapshotPoller = (function() {
- var timer = null;
-
- var preprocessMatrixSnapshot = function(snapshot) {
- if ( Array.isArray(snapshot.headerIndices) ) {
+ let preprocessMatrixSnapshot = function (snapshot) {
+ if (Array.isArray(snapshot.headerIndices)) {
snapshot.headerIndices = new Map(snapshot.headerIndices);
}
return snapshot;
};
- var processPollResult = function(response) {
- if ( typeof response !== 'object' ) {
+ let processPollResult = function (response) {
+ if (typeof response !== 'object') {
return;
}
- if (
- response.mtxContentModified === false &&
- response.mtxCountModified === false &&
- response.pMatrixModified === false &&
- response.tMatrixModified === false
- ) {
+
+ if (response.mtxContentModified === false
+ && response.mtxCountModified === false
+ && response.pMatrixModified === false
+ && response.tMatrixModified === false) {
return;
}
matrixSnapshot = preprocessMatrixSnapshot(response);
- if ( response.mtxContentModified ) {
+ if (response.mtxContentModified) {
makeMenu();
return;
}
- if ( response.mtxCountModified ) {
+ if (response.mtxCountModified) {
updateMatrixCounts();
}
- if (
- response.pMatrixModified ||
- response.tMatrixModified ||
- response.scopeModified
- ) {
+ if (response.pMatrixModified
+ || response.tMatrixModified
+ || response.scopeModified) {
updateMatrixColors();
updateMatrixBehavior();
updateMatrixButtons();
}
};
- var onPolled = function(response) {
+ let onPolled = function (response) {
processPollResult(response);
pollAsync();
};
- var pollNow = function() {
+ let pollNow = function () {
unpollAsync();
vAPI.messaging.send('popup.js', {
what: 'matrixSnapshot',
@@ -1436,43 +1456,47 @@
}, onPolled);
};
- var poll = function() {
+ let poll = function () {
timer = null;
pollNow();
};
- var pollAsync = function() {
- if ( timer !== null ) {
+ let pollAsync = function () {
+ if (timer !== null) {
return;
}
- if ( document.defaultView === null ) {
+ if (document.defaultView === null) {
return;
}
timer = vAPI.setTimeout(poll, 1414);
};
- var unpollAsync = function() {
- if ( timer !== null ) {
+ let unpollAsync = function () {
+ if (timer !== null) {
clearTimeout(timer);
timer = null;
}
};
- (function() {
- var tabId = matrixSnapshot.tabId;
+ (function () {
+ let tabId = matrixSnapshot.tabId;
// If no tab id yet, see if there is one specified in our URL
- if ( tabId === undefined ) {
- var matches = window.location.search.match(/(?:\?|&)tabId=([^&]+)/);
- if ( matches !== null ) {
+ if (tabId === undefined) {
+ let matches = window
+ .location
+ .search
+ .match(/(?:\?|&)tabId=([^&]+)/);
+
+ if (matches !== null) {
tabId = matches[1];
// No need for logger button when embedded in logger
uDom('[data-extension-url="logger-ui.html"]').remove();
}
}
- var snapshotFetched = function(response) {
- if ( typeof response === 'object' ) {
+ let snapshotFetched = function (response) {
+ if (typeof response === 'object') {
matrixSnapshot = preprocessMatrixSnapshot(response);
}
onMatrixSnapshotReady(response);
@@ -1490,28 +1514,31 @@
};
})();
- /******************************************************************************/
-
// Below is UI stuff which is not key to make the menu, so this can
// be done without having to wait for a tab to be bound to the menu.
// We reuse for all cells the one and only cell hotspots.
- uDom('#whitelist').on('click', function() {
+ uDom('#whitelist').on('click', function () {
handleWhitelistFilter(uDom(this));
return false;
});
- uDom('#blacklist').on('click', function() {
+
+ uDom('#blacklist').on('click', function () {
handleBlacklistFilter(uDom(this));
return false;
});
- uDom('#domainOnly').on('click', function() {
+
+ uDom('#domainOnly').on('click', function () {
toggleCollapseState(uDom(this));
return false;
});
+
matrixCellHotspots = uDom('#cellHotspots').detach();
+
uDom('body')
.on('mouseenter', '.matCell', mouseenterMatrixCellHandler)
.on('mouseleave', '.matCell', mouseleaveMatrixCellHandler);
+
uDom('#specificScope').on('click', selectSpecificScope);
uDom('#globalScope').on('click', selectGlobalScope);
uDom('[id^="mtxSwitch_"]').on('click', toggleMatrixSwitch);
@@ -1525,15 +1552,10 @@
uDom('body').on('click', '[data-dropdown-menu]', dropDownMenuShow);
uDom('body').on('click', '.dropdown-menu-capture', dropDownMenuHide);
- uDom('#matList').on('click', '.g4Meta', function(ev) {
+ uDom('#matList').on('click', '.g4Meta', function (ev) {
matrixSnapshot.collapseBlacklistedDomains =
ev.target.classList.toggle('g4Collapsed');
- setUserSetting(
- 'popupCollapseBlacklistedDomains',
- matrixSnapshot.collapseBlacklistedDomains
- );
+ setUserSetting('popupCollapseBlacklistedDomains',
+ matrixSnapshot.collapseBlacklistedDomains);
});
-
- /******************************************************************************/
-
})();
diff --git a/js/settings.js b/js/settings.js
index da8b6df..8a8d25a 100644
--- a/js/settings.js
+++ b/js/settings.js
@@ -21,19 +21,10 @@
uMatrix Home: https://github.com/gorhill/uMatrix
*/
-/* global uDom */
-
'use strict';
-/******************************************************************************/
-
(function() {
-
- /******************************************************************************/
-
- var cachedSettings = {};
-
- /******************************************************************************/
+ let cachedSettings = {};
function changeUserSettings(name, value) {
vAPI.messaging.send('settings.js', {
@@ -43,8 +34,6 @@
});
}
- /******************************************************************************/
-
function changeMatrixSwitch(name, state) {
vAPI.messaging.send('settings.js', {
what: 'setMatrixSwitch',
@@ -53,69 +42,62 @@
});
}
- /******************************************************************************/
-
function onChangeValueHandler(elem, setting, min, max) {
- var oldVal = cachedSettings.userSettings[setting];
- var 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 {
newVal = Math.max(newVal, min);
newVal = Math.min(newVal, max);
}
elem.value = newVal;
- if ( newVal !== oldVal ) {
+ 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) {
- var target = ev.target;
+ let target = ev.target;
- switch ( target.id ) {
+ switch (target.id) {
case 'displayTextSize':
changeUserSettings('displayTextSize', target.value + 'px');
break;
case 'clearBrowserCache':
case 'cloudStorageEnabled':
case 'collapseBlacklisted':
- case 'collapseBlocked':
case 'colorBlindFriendly':
case 'deleteCookies':
case 'deleteLocalStorage':
case 'deleteUnusedSessionCookies':
case 'iconBadgeEnabled':
case 'processHyperlinkAuditing':
+ case 'disableUpdateIcon':
changeUserSettings(target.id, target.checked);
break;
+ case 'collapseBlocked':
+ changeUserSettings(target.id, target.checked);
+ synchronizeWidgets();
+ break;
case 'noMixedContent':
case 'noscriptTagsSpoofed':
case 'processReferer':
- changeMatrixSwitch(
- target.getAttribute('data-matrix-switch'),
- target.checked
- );
+ changeMatrixSwitch(target.getAttribute('data-matrix-switch'),
+ target.checked);
break;
case 'deleteUnusedSessionCookiesAfter':
- onChangeValueHandler(target, 'deleteUnusedSessionCookiesAfter', 15, 1440);
+ onChangeValueHandler(target, 'deleteUnusedSessionCookiesAfter',
+ 15, 1440);
break;
case 'clearBrowserCacheAfter':
onChangeValueHandler(target, 'clearBrowserCacheAfter', 15, 1440);
@@ -126,71 +108,56 @@
default:
break;
}
-
- switch ( target.id ) {
- case 'collapseBlocked':
- synchronizeWidgets();
- break;
- default:
- break;
- }
}
- /******************************************************************************/
-
function synchronizeWidgets() {
- var e1, e2;
+ let e1, e2;
e1 = uDom.nodeFromId('collapseBlocked');
e2 = uDom.nodeFromId('collapseBlacklisted');
- if ( e1.checked ) {
+ if (e1.checked) {
e2.setAttribute('disabled', '');
} else {
e2.removeAttribute('disabled');
}
}
- /******************************************************************************/
+ let onSettingsReceived = function (settings) {
+ // Cache copy
+ cachedSettings = settings;
- vAPI.messaging.send(
- 'settings.js',
- { what: 'getUserSettings' },
- function onSettingsReceived(settings) {
- // Cache copy
- cachedSettings = settings;
+ let userSettings = settings.userSettings;
+ let matrixSwitches = settings.matrixSwitches;
- var userSettings = settings.userSettings;
- var matrixSwitches = settings.matrixSwitches;
-
- uDom('[data-setting-bool]').forEach(function(elem){
- elem.prop('checked', userSettings[elem.prop('id')] === true);
- });
+ uDom('[data-setting-bool]').forEach(function (elem) {
+ elem.prop('checked', userSettings[elem.prop('id')] === true);
+ });
- uDom('[data-matrix-switch]').forEach(function(elem){
- var switchName = elem.attr('data-matrix-switch');
- if ( typeof switchName === 'string' && switchName !== '' ) {
- elem.prop('checked', matrixSwitches[switchName] === true);
- }
- });
+ uDom('[data-matrix-switch]').forEach(function (elem) {
+ 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;
+ uDom.nodeFromId('displayTextSize').value =
+ parseInt(userSettings.displayTextSize, 10) || 14;
- uDom.nodeFromId('popupScopeLevel').value = userSettings.popupScopeLevel;
- uDom.nodeFromId('deleteUnusedSessionCookiesAfter').value =
- userSettings.deleteUnusedSessionCookiesAfter;
- uDom.nodeFromId('clearBrowserCacheAfter').value =
- userSettings.clearBrowserCacheAfter;
+ uDom.nodeFromId('popupScopeLevel').value = userSettings.popupScopeLevel;
+ uDom.nodeFromId('deleteUnusedSessionCookiesAfter').value =
+ userSettings.deleteUnusedSessionCookiesAfter;
+ uDom.nodeFromId('clearBrowserCacheAfter').value =
+ userSettings.clearBrowserCacheAfter;
- synchronizeWidgets();
+ synchronizeWidgets();
- document.addEventListener('change', onInputChanged);
+ document.addEventListener('change', onInputChanged);
- // https://github.com/gorhill/httpswitchboard/issues/197
- uDom(window).on('beforeunload', prepareToDie);
- }
- );
-
- /******************************************************************************/
+ // https://github.com/gorhill/httpswitchboard/issues/197
+ uDom(window).on('beforeunload', prepareToDie);
+ }
+ vAPI.messaging.send('settings.js', {
+ what: 'getUserSettings'
+ }, onSettingsReceived);
})();
diff --git a/js/start.js b/js/start.js
index 31a89d8..3a09551 100644
--- a/js/start.js
+++ b/js/start.js
@@ -25,33 +25,25 @@
// ORDER IS IMPORTANT
-/******************************************************************************/
-
// Load everything
(function() {
+ let ηm = ηMatrix;
- /******************************************************************************/
-
- var ηm = ηMatrix;
-
- /******************************************************************************/
-
- var processCallbackQueue = function(queue, callback) {
- var processOne = function() {
- var fn = queue.pop();
- if ( fn ) {
+ let processCallbackQueue = function (queue, callback) {
+ let processOne = function () {
+ let fn = queue.pop();
+ if (fn) {
fn(processOne);
- } else if ( typeof callback === 'function' ) {
+ } else if (typeof callback === 'function') {
callback();
}
};
+
processOne();
};
- /******************************************************************************/
-
- var onAllDone = function() {
+ let onAllDone = function () {
ηm.webRequest.start();
ηm.assets.addObserver(ηm.assetObserver.bind(ηm));
@@ -60,50 +52,38 @@
vAPI.cloud.start([ 'myRulesPane' ]);
};
- /******************************************************************************/
-
- var onTabsReady = function(tabs) {
- var tab;
- var i = tabs.length;
- // console.debug('start.js > binding %d tabs', i);
- while ( i-- ) {
- tab = tabs[i];
+ let onTabsReady = function (tabs) {
+ 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();
};
- /******************************************************************************/
-
- var onUserSettingsLoaded = function() {
+ let onUserSettingsLoaded = function () {
ηm.loadHostsFiles();
};
- /******************************************************************************/
-
- var onPSLReady = function() {
+ 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.
+ // 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');
+ η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() {
+ processCallbackQueue(ηm.onBeforeStartQueue, function () {
ηm.loadPublicSuffixList(onPSLReady);
});
-
- /******************************************************************************/
-
})();
-
-/******************************************************************************/
diff --git a/js/storage.js b/js/storage.js
index b3a5e7b..012d2f2 100644
--- a/js/storage.js
+++ b/js/storage.js
@@ -21,10 +21,12 @@
uMatrix Home: https://github.com/gorhill/uMatrix
*/
-/* global objectAssign, punycode, publicSuffixList */
+/* global objectAssign, publicSuffixList */
'use strict';
+Components.utils.import('chrome://ematrix/content/lib/PublicSuffixList.jsm');
+
/******************************************************************************/
ηMatrix.getBytesInUse = function() {
@@ -264,7 +266,7 @@
if ( entries.hasOwnProperty(assetKey) === false ) { continue; }
entry = entries[assetKey];
if ( entry.content !== 'filters' ) { continue; }
- availableHostsFiles[assetKey] = objectAssign({}, entry);
+ availableHostsFiles[assetKey] = Object.assign({}, entry);
}
// Now get user's selection of lists
@@ -517,7 +519,7 @@
var applyPublicSuffixList = function(details) {
if ( !details.error ) {
- publicSuffixList.parse(details.content, punycode.toASCII);
+ publicSuffixList.parse(details.content, Punycode.toASCII);
}
callback();
};
diff --git a/js/tab.js b/js/tab.js
index abaaf07..b34d923 100644
--- a/js/tab.js
+++ b/js/tab.js
@@ -21,32 +21,26 @@
uMatrix Home: https://github.com/gorhill/uMatrix
*/
-/******************************************************************************/
-/******************************************************************************/
-
-(function() {
-
+(function () {
'use strict';
- /******************************************************************************/
+ Cu.import('chrome://ematrix/content/lib/UriTools.jsm');
var ηm = ηMatrix;
// https://github.com/gorhill/httpswitchboard/issues/303
// Some kind of trick going on here:
- // Any scheme other than 'http' and 'https' is remapped into a fake
- // URL which trick the rest of ηMatrix into being able to process an
- // otherwise unmanageable scheme. ηMatrix needs web page to have a proper
- // hostname to work properly, so just like the 'behind-the-scene'
- // fake domain name, we map unknown schemes into a fake '{scheme}-scheme'
- // hostname. This way, for a specific scheme you can create scope with
- // rules which will apply only to that scheme.
-
- /******************************************************************************/
- /******************************************************************************/
-
- ηm.normalizePageURL = function(tabId, pageURL) {
- if ( vAPI.isBehindTheSceneTabId(tabId) ) {
+ // Any scheme other than 'http' and 'https' is remapped into a
+ // fake URL which trick the rest of ηMatrix into being able to
+ // process an otherwise unmanageable scheme. ηMatrix needs web
+ // page to have a proper hostname to work properly, so just like
+ // the 'behind-the-scene' fake domain name, we map unknown
+ // schemes into a fake '{scheme}-scheme' hostname. This way, for
+ // a specific scheme you can create scope with rules which will
+ // apply only to that scheme.
+
+ ηm.normalizePageURL = function (tabId, pageURL) {
+ if (vAPI.isBehindTheSceneTabId(tabId)) {
return 'http://' + this.behindTheSceneScope + '/';
}
@@ -59,117 +53,122 @@
}
}
- // If the URL is that of our "blocked page" document, return the URL of
- // the blocked page.
- if ( pageURL.lastIndexOf(vAPI.getURL('main-blocked.html'), 0) === 0 ) {
- var matches = /main-blocked\.html\?details=([^&]+)/.exec(pageURL);
- if ( matches && matches.length === 2 ) {
+ // 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 {
- var details = JSON.parse(atob(matches[1]));
+ let details = JSON.parse(atob(matches[1]));
pageURL = details.url;
} catch (e) {
}
}
}
- var uri = this.URI.set(pageURL);
- var scheme = uri.scheme;
- if ( scheme === 'https' || scheme === 'http' ) {
- return uri.normalizedURI();
+ let uri = UriTools.set(pageURL);
+ let scheme = uri.scheme;
+ if (scheme === 'https' || scheme === 'http') {
+ return UriTools.normalizedURI();
}
- var 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 + '/';
};
- /******************************************************************************/
- /******************************************************************************
-
-To keep track from which context *exactly* network requests are made. This is
-often tricky for various reasons, and the challenge is not specific to one
-browser.
-
-The time at which a URL is assigned to a tab and the time when a network
-request for a root document is made must be assumed to be unrelated: it's all
-asynchronous. There is no guaranteed order in which the two events are fired.
-
-Also, other "anomalies" can occur:
-
-- a network request for a root document is fired without the corresponding
-tab being really assigned a new URL
-<https://github.com/chrisaljoudi/uBlock/issues/516>
-
-- a network request for a secondary resource is labeled with a tab id for
-which no root document was pulled for that tab.
-<https://github.com/chrisaljoudi/uBlock/issues/1001>
-
-- a network request for a secondary resource is made without the root
-document to which it belongs being formally bound yet to the proper tab id,
-causing a bad scope to be used for filtering purpose.
-<https://github.com/chrisaljoudi/uBlock/issues/1205>
-<https://github.com/chrisaljoudi/uBlock/issues/1140>
-
-So the solution here is to keep a lightweight data structure which only
-purpose is to keep track as accurately as possible of which root document
-belongs to which tab. That's the only purpose, and because of this, there are
-no restrictions for when the URL of a root document can be associated to a tab.
-
-Before, the PageStore object was trying to deal with this, but it had to
-enforce some restrictions so as to not descend into one of the above issues, or
-other issues. The PageStore object can only be associated with a tab for which
-a definitive navigation event occurred, because it collects information about
-what occurred in the tab (for example, the number of requests blocked for a
-page).
-
-The TabContext objects do not suffer this restriction, and as a result they
-offer the most reliable picture of which root document URL is really associated
-to which tab. Moreover, the TabObject can undo an association from a root
-document, and automatically re-associate with the next most recent. This takes
-care of <https://github.com/chrisaljoudi/uBlock/issues/516>.
-
-The PageStore object no longer cache the various information about which
-root document it is currently bound. When it needs to find out, it will always
-defer to the TabContext object, which will provide the real answer. This takes
-case of <https://github.com/chrisaljoudi/uBlock/issues/1205>. In effect, the
-master switch and dynamic filtering rules can be evaluated now properly even
-in the absence of a PageStore object, this was not the case before.
-
-Also, the TabContext object will try its best to find a good candidate root
-document URL for when none exists. This takes care of
-<https://github.com/chrisaljoudi/uBlock/issues/1001>.
-
-The TabContext manager is self-contained, and it takes care to properly
-housekeep itself.
-
+ /*
+ To keep track from which context *exactly* network requests are
+ made. This is often tricky for various reasons, and the
+ challenge is not specific to one browser.
+
+ The time at which a URL is assigned to a tab and the time when a
+ network request for a root document is made must be assumed to
+ be unrelated: it's all asynchronous. There is no guaranteed
+ order in which the two events are fired.
+
+ Also, other "anomalies" can occur:
+
+ - a network request for a root document is fired without the
+ corresponding tab being really assigned a new URL.
+ <https://github.com/chrisaljoudi/uBlock/issues/516>
+
+ - a network request for a secondary resource is labeled with a
+ tab id for which no root document was pulled for that tab.
+ <https://github.com/chrisaljoudi/uBlock/issues/1001>
+
+ - a network request for a secondary resource is made without the
+ root document to which it belongs being formally bound yet to
+ the proper tab id, causing a bad scope to be used for filtering
+ purpose.
+ <https://github.com/chrisaljoudi/uBlock/issues/1205>
+ <https://github.com/chrisaljoudi/uBlock/issues/1140>
+
+ So the solution here is to keep a lightweight data structure
+ which only purpose is to keep track as accurately as possible of
+ which root document belongs to which tab. That's the only
+ purpose, and because of this, there are no restrictions for when
+ the URL of a root document can be associated to a tab.
+
+ Before, the PageStore object was trying to deal with this, but
+ it had to enforce some restrictions so as to not descend into
+ one of the above issues, or other issues. The PageStore object
+ can only be associated with a tab for which a definitive
+ navigation event occurred, because it collects information about
+ what occurred in the tab (for example, the number of requests
+ blocked for a page).
+
+ The TabContext objects do not suffer this restriction, and as a
+ result they offer the most reliable picture of which root
+ document URL is really associated to which tab. Moreover, the
+ TabObject can undo an association from a root document, and
+ automatically re-associate with the next most recent. This takes
+ care of <https://github.com/chrisaljoudi/uBlock/issues/516>.
+
+ The PageStore object no longer cache the various information
+ about which root document it is currently bound. When it needs
+ to find out, it will always defer to the TabContext object,
+ which will provide the real answer. This takes case of
+ <https://github.com/chrisaljoudi/uBlock/issues/1205>. In effect,
+ the master switch and dynamic filtering rules can be evaluated
+ now properly even in the absence of a PageStore object, this was
+ not the case before.
+
+ Also, the TabContext object will try its best to find a good
+ candidate root document URL for when none exists. This takes
+ care of <https://github.com/chrisaljoudi/uBlock/issues/1001>.
+
+ The TabContext manager is self-contained, and it takes care to
+ properly housekeep itself.
*/
- ηm.tabContextManager = (function() {
- var tabContexts = Object.create(null);
+ ηm.tabContextManager = (function () {
+ 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.
- var mostRecentRootDocURL = '';
- var mostRecentRootDocURLTimestamp = 0;
+ // 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;
- var 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.
- var 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();
};
- var TabContext = function(tabId) {
+ let TabContext = function (tabId) {
this.tabId = tabId;
this.stack = [];
this.rawURL =
@@ -184,120 +183,134 @@ housekeep itself.
tabContexts[tabId] = this;
};
- TabContext.prototype.destroy = function() {
- if ( vAPI.isBehindTheSceneTabId(this.tabId) ) {
+ TabContext.prototype.destroy = function () {
+ if (vAPI.isBehindTheSceneTabId(this.tabId)) {
return;
}
- if ( this.gcTimer !== null ) {
+ if (this.gcTimer !== null) {
clearTimeout(this.gcTimer);
this.gcTimer = null;
}
delete tabContexts[this.tabId];
};
- TabContext.prototype.onTab = function(tab) {
- if ( tab ) {
+ TabContext.prototype.onTab = function (tab) {
+ if (tab) {
this.gcTimer = vAPI.setTimeout(this.onGC.bind(this), gcPeriod);
} else {
this.destroy();
}
};
- TabContext.prototype.onGC = function() {
+ TabContext.prototype.onGC = function () {
this.gcTimer = null;
- if ( vAPI.isBehindTheSceneTabId(this.tabId) ) {
+ if (vAPI.isBehindTheSceneTabId(this.tabId)) {
return;
}
vAPI.tabs.get(this.tabId, this.onTab.bind(this));
};
// https://github.com/gorhill/uBlock/issues/248
- // Stack entries have to be committed to stick. Non-committed stack
- // entries are removed after a set delay.
- TabContext.prototype.onCommit = function() {
- if ( vAPI.isBehindTheSceneTabId(this.tabId) ) {
+ // Stack entries have to be committed to stick. Non-committed
+ // stack entries are removed after a set delay.
+ TabContext.prototype.onCommit = function () {
+ if (vAPI.isBehindTheSceneTabId(this.tabId)) {
return;
}
this.commitTimer = null;
// Remove uncommitted entries at the top of the stack.
- var i = this.stack.length;
- while ( i-- ) {
- if ( this.stack[i].committed ) {
+ let i = this.stack.length;
+ while (i--) {
+ if (this.stack[i].committed) {
break;
}
}
// https://github.com/gorhill/uBlock/issues/300
// If no committed entry was found, fall back on the bottom-most one
// as being the committed one by default.
- if ( i === -1 && this.stack.length !== 0 ) {
+ if (i === -1 && this.stack.length !== 0) {
this.stack[0].committed = true;
i = 0;
}
- i += 1;
- if ( i < this.stack.length ) {
+
+ ++i;
+ if (i < this.stack.length) {
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() {
- if ( vAPI.isBehindTheSceneTabId(this.tabId) ) {
+ // This takes care of orphanized tab contexts. Can't be
+ // started for all contexts, as the behind-the-scene context
+ // is permanent -- so we do not want to flush it.
+ TabContext.prototype.autodestroy = function () {
+ if (vAPI.isBehindTheSceneTabId(this.tabId)) {
return;
}
this.gcTimer = vAPI.setTimeout(this.onGC.bind(this), gcPeriod);
};
- // 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 = '';
+ // Update just force all properties to be updated to match the
+ // most recent root URL.
+ TabContext.prototype.update = function () {
+ if (this.stack.length === 0) {
+ this.rawURL =
+ this.normalURL =
+ this.scheme =
+ this.rootHostname =
+ this.rootDomain = '';
this.secure = false;
return;
}
+
this.rawURL = this.stack[this.stack.length - 1].url;
this.normalURL = ηm.normalizePageURL(this.tabId, this.rawURL);
- this.scheme = ηm.URI.schemeFromURI(this.rawURL);
- this.rootHostname = ηm.URI.hostnameFromURI(this.normalURL);
- this.rootDomain = ηm.URI.domainFromHostname(this.rootHostname) || this.rootHostname;
- this.secure = ηm.URI.isSecureScheme(this.scheme);
+ this.scheme = UriTools.schemeFromURI(this.rawURL);
+ this.rootHostname = UriTools.hostnameFromURI(this.normalURL);
+ this.rootDomain = UriTools.domainFromHostname(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) {
- if ( vAPI.isBehindTheSceneTabId(this.tabId) ) { return; }
- var committed = context !== undefined;
- var count = this.stack.length;
- var topEntry = this.stack[count - 1];
- if ( topEntry && topEntry.url === url ) {
- if ( committed ) {
+ TabContext.prototype.push = function (url, context) {
+ if (vAPI.isBehindTheSceneTabId(this.tabId)) {
+ return;
+ }
+
+ let committed = context !== undefined;
+ let count = this.stack.length;
+ let topEntry = this.stack[count - 1];
+
+ if (topEntry && topEntry.url === url) {
+ if (committed) {
topEntry.committed = true;
}
return;
}
- if ( this.commitTimer !== null ) {
+
+ if (this.commitTimer !== null) {
clearTimeout(this.commitTimer);
}
- if ( committed ) {
+
+ if (committed) {
this.stack = [new StackEntry(url, true)];
} else {
this.stack.push(new StackEntry(url));
- this.commitTimer = vAPI.setTimeout(this.onCommit.bind(this), 1000);
+ this.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.
- var push = function(tabId, url, context) {
- var entry = tabContexts[tabId];
- if ( entry === undefined ) {
+ let push = function (tabId, url, context) {
+ let entry = tabContexts[tabId];
+ if (entry === undefined) {
entry = new TabContext(tabId);
entry.autodestroy();
}
@@ -307,64 +320,77 @@ housekeep itself.
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.
- var mustLookup = function(tabId, url) {
- var entry;
- if ( url !== undefined ) {
+ // 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);
} else {
entry = tabContexts[tabId];
}
- if ( entry !== undefined ) {
+
+ if (entry !== undefined) {
return entry;
}
+
// https://github.com/chrisaljoudi/uBlock/issues/1025
- // Google Hangout popup opens without a root frame. So for now we will
- // just discard that best-guess root frame if it is too far in the
- // future, at which point it ceases to be a "best guess".
- if ( mostRecentRootDocURL !== '' && mostRecentRootDocURLTimestamp + 500 < Date.now() ) {
+ // Google Hangout popup opens without a root frame. So for
+ // now we will just discard that best-guess root frame if
+ // it is too far in the future, at which point it ceases
+ // to be a "best guess".
+ if (mostRecentRootDocURL
+ !== '' && mostRecentRootDocURLTimestamp + 500 < Date.now()) {
mostRecentRootDocURL = '';
}
+
// https://github.com/chrisaljoudi/uBlock/issues/1001
- // Not a behind-the-scene request, yet no page store found for the
- // tab id: we will thus bind the last-seen root document to the
- // unbound tab. It's a guess, but better than ending up filtering
- // nothing at all.
- if ( mostRecentRootDocURL !== '' ) {
+ // Not a behind-the-scene request, yet no page store found
+ // for the tab id: we will thus bind the last-seen root
+ // document to the unbound tab. It's a guess, but better
+ // than ending up filtering nothing at all.
+ if (mostRecentRootDocURL !== '') {
return push(tabId, mostRecentRootDocURL);
}
- // If all else fail at finding a page store, re-categorize the
- // request as behind-the-scene. At least this ensures that ultimately
- // the user can still inspect/filter those net requests which were
- // about to fall through the cracks.
+
+ // If all else fail at finding a page store, re-categorize
+ // the request as behind-the-scene. At least this ensures
+ // that ultimately the user can still inspect/filter those
+ // net requests which were about to fall through the
+ // cracks.
// Example: Chromium + case #12 at
// http://raymondhill.net/ublock/popup.html
return tabContexts[vAPI.noTabId];
};
- var lookup = function(tabId) {
+ let lookup = function (tabId) {
return tabContexts[tabId] || null;
};
// Behind-the-scene tab context
- (function() {
- var entry = new TabContext(vAPI.noTabId);
+ (function () {
+ let entry = new TabContext(vAPI.noTabId);
entry.stack.push(new StackEntry('', true));
entry.rawURL = '';
entry.normalURL = ηm.normalizePageURL(entry.tabId);
- entry.rootHostname = ηm.URI.hostnameFromURI(entry.normalURL);
- entry.rootDomain = ηm.URI.domainFromHostname(entry.rootHostname) || entry.rootHostname;
+ entry.rootHostname = UriTools.hostnameFromURI(entry.normalURL);
+ entry.rootDomain = UriTools.domainFromHostname(entry.rootHostname)
+ || entry.rootHostname;
})();
// https://github.com/gorhill/uMatrix/issues/513
- // Force a badge update here, it could happen that all the subsequent
- // network requests are already in the page store, which would cause
- // the badge to no be updated for these network requests.
-
- vAPI.tabs.onNavigation = function(details) {
- var tabId = details.tabId;
- if ( vAPI.isBehindTheSceneTabId(tabId) ) { return; }
+ // 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) {
+ let tabId = details.tabId;
+ if (vAPI.isBehindTheSceneTabId(tabId)) {
+ return;
+ }
push(tabId, details.url, 'newURL');
ηm.updateBadgeAsync(tabId);
};
@@ -372,19 +398,25 @@ housekeep itself.
// https://github.com/gorhill/uMatrix/issues/872
// `changeInfo.url` may not always be available (Firefox).
- vAPI.tabs.onUpdated = function(tabId, changeInfo, tab) {
- if ( vAPI.isBehindTheSceneTabId(tabId) ) { return; }
- if ( typeof tab.url !== 'string' || tab.url === '' ) { return; }
- var url = changeInfo.url || tab.url;
- if ( url ) {
+ vAPI.tabs.onUpdated = function (tabId, changeInfo, tab) {
+ if (vAPI.isBehindTheSceneTabId(tabId)) {
+ return;
+ }
+
+ if (typeof tab.url !== 'string' || tab.url === '') {
+ return;
+ }
+
+ let url = changeInfo.url || tab.url;
+ if (url) {
push(tabId, url, 'updateURL');
}
};
- vAPI.tabs.onClosed = function(tabId) {
+ vAPI.tabs.onClosed = function (tabId) {
ηm.unbindTabFromPageStats(tabId);
- var entry = tabContexts[tabId];
- if ( entry instanceof TabContext ) {
+ let entry = tabContexts[tabId];
+ if (entry instanceof TabContext) {
entry.destroy();
}
};
@@ -398,45 +430,43 @@ housekeep itself.
vAPI.tabs.registerListeners();
- /******************************************************************************/
- /******************************************************************************/
-
// Create an entry for the tab if it doesn't exist
- ηm.bindTabToPageStats = function(tabId, context) {
+ ηm.bindTabToPageStats = function (tabId, context) {
this.updateBadgeAsync(tabId);
- // Do not create a page store for URLs which are of no interests
- // Example: dev console
- var tabContext = this.tabContextManager.lookup(tabId);
- if ( tabContext === null ) {
+ // 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) ) {
+ if (vAPI.isBehindTheSceneTabId(tabId)) {
return this.pageStores[tabId];
}
- var normalURL = tabContext.normalURL;
- var 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 ) {
+ if (pageStore !== null) {
// No change, do not rebind
- if ( pageStore.pageUrl === normalURL ) {
+ if (pageStore.pageUrl === normalURL) {
return pageStore;
}
// https://github.com/gorhill/uMatrix/issues/37
- // Just rebind whenever possible: the URL changed, but the document
- // maybe is the same.
+ // Just rebind whenever possible: the URL changed, but the
+ // document maybe is the same.
// Example: Google Maps, Github
// https://github.com/gorhill/uMatrix/issues/72
// Need to double-check that the new scope is same as old scope
- if ( context === 'updateURL' && pageStore.pageHostname === tabContext.rootHostname ) {
+ if (context === 'updateURL'
+ && pageStore.pageHostname === tabContext.rootHostname) {
pageStore.rawURL = tabContext.rawURL;
pageStore.normalURL = normalURL;
this.updateTitle(tabId);
@@ -450,259 +480,266 @@ housekeep itself.
// Try to resurrect first.
pageStore = this.resurrectPageStore(tabId, normalURL);
- if ( pageStore === null ) {
+ if (pageStore === null) {
pageStore = this.pageStoreFactory(tabContext);
}
this.pageStores[tabId] = pageStore;
this.updateTitle(tabId);
this.pageStoresToken = Date.now();
- // console.debug('tab.js > bindTabToPageStats(): dispatching traffic in tab id %d to page store "%s"', tabId, pageUrl);
-
return pageStore;
};
- /******************************************************************************/
-
- ηm.unbindTabFromPageStats = function(tabId) {
- // Never unbind behind-the-scene page store.
- if ( vAPI.isBehindTheSceneTabId(tabId) ) {
+ ηm.unbindTabFromPageStats = function (tabId) {
+ if (vAPI.isBehindTheSceneTabId(tabId)) {
return;
}
- var 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();
- 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] = {};
}
- var pageStoreCrypt = this.pageStoreCemetery[tabId];
+ let pageStoreCrypt = this.pageStoreCemetery[tabId];
- var pageURL = pageStore.pageUrl;
+ 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 ) {
+ ηm.resurrectPageStore = function (tabId, pageURL) {
+ if (this.pageStoreCemetery.hasOwnProperty(tabId) === false) {
return null;
}
- var pageStoreCrypt = this.pageStoreCemetery[tabId];
- if ( pageStoreCrypt.hasOwnProperty(pageURL) === false ) {
+ let pageStoreCrypt = this.pageStoreCemetery[tabId];
+
+ if (pageStoreCrypt.hasOwnProperty(pageURL) === false) {
return null;
}
- var 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 ) {
+ if (Object.keys(pageStoreCrypt).length === 0) {
delete this.pageStoreCemetery[tabId];
}
return pageStore;
};
- /******************************************************************************/
-
- ηm.incineratePageStore = function(tabId, pageURL) {
- if ( this.pageStoreCemetery.hasOwnProperty(tabId) === false ) {
+ ηm.incineratePageStore = function (tabId, pageURL) {
+ if (this.pageStoreCemetery.hasOwnProperty(tabId) === false) {
return;
}
- var pageStoreCrypt = this.pageStoreCemetery[tabId];
- if ( pageStoreCrypt.hasOwnProperty(pageURL) === false ) {
+ let pageStoreCrypt = this.pageStoreCemetery[tabId];
+
+ if (pageStoreCrypt.hasOwnProperty(pageURL) === false) {
return;
}
- var pageStore = pageStoreCrypt[pageURL];
- if ( pageStore.incinerationTimer !== null ) {
+ let pageStore = pageStoreCrypt[pageURL];
+ if (pageStore.incinerationTimer !== null) {
clearTimeout(pageStore.incinerationTimer);
pageStore.incinerationTimer = null;
}
delete pageStoreCrypt[pageURL];
- if ( Object.keys(pageStoreCrypt).length === 0 ) {
+
+ if (Object.keys(pageStoreCrypt).length === 0) {
delete this.pageStoreCemetery[tabId];
}
pageStore.dispose();
};
- /******************************************************************************/
-
- ηm.pageStoreFromTabId = function(tabId) {
+ ηm.pageStoreFromTabId = function (tabId) {
return this.pageStores[tabId] || null;
};
// Never return null
- ηm.mustPageStoreFromTabId = function(tabId) {
+ ηm.mustPageStoreFromTabId = function (tabId) {
return this.pageStores[tabId] || this.pageStores[vAPI.noTabId];
};
- /******************************************************************************/
-
- ηm.forceReload = function(tabId, bypassCache) {
+ ηm.forceReload = function (tabId, bypassCache) {
vAPI.tabs.reload(tabId, bypassCache);
};
- /******************************************************************************/
-
// Update badge
// rhill 2013-11-09: well this sucks, I can't update icon/badge
- // incrementally, as chromium overwrite the icon at some point without
- // notifying me, and this causes internal cached state to be out of sync.
+ // incrementally, as chromium overwrite the icon at some point
+ // without notifying me, and this causes internal cached state to
+ // be out of sync.
+ // ηMatrix: does it matter to us?
- ηm.updateBadgeAsync = (function() {
- var tabIdToTimer = Object.create(null);
+ ηm.updateBadgeAsync = (function () {
+ let tabIdToTimer = Object.create(null);
- var updateBadge = function(tabId) {
+ let updateBadge = function (tabId) {
delete tabIdToTimer[tabId];
- var iconId = null;
- var badgeStr = '';
+ let iconId = null;
+ let badgeStr = '';
- var pageStore = this.pageStoreFromTabId(tabId);
- if ( pageStore !== null ) {
- var total = pageStore.perLoadAllowedRequestCount +
+ let pageStore = this.pageStoreFromTabId(tabId);
+ if (pageStore !== null) {
+ let total = pageStore.perLoadAllowedRequestCount +
pageStore.perLoadBlockedRequestCount;
- if ( total ) {
- var squareSize = 19;
- var greenSize = squareSize * Math.sqrt(pageStore.perLoadAllowedRequestCount / total);
- iconId = greenSize < squareSize/2 ? Math.ceil(greenSize) : Math.floor(greenSize);
+
+ if (total) {
+ let squareSize = 19;
+ let greenSize = squareSize *
+ Math.sqrt(pageStore.perLoadAllowedRequestCount / total);
+
+ iconId = greenSize < squareSize/2 ?
+ Math.ceil(greenSize) :
+ Math.floor(greenSize);
}
- if ( this.userSettings.iconBadgeEnabled && pageStore.perLoadBlockedRequestCount !== 0) {
- badgeStr = this.formatCount(pageStore.perLoadBlockedRequestCount);
+
+ if (this.userSettings.iconBadgeEnabled
+ && pageStore.perLoadBlockedRequestCount !== 0) {
+ badgeStr =
+ this.formatCount(pageStore.perLoadBlockedRequestCount);
}
}
vAPI.setIcon(tabId, iconId, badgeStr);
};
- return function(tabId) {
- if ( tabIdToTimer[tabId] ) {
+ return function (tabId) {
+ if (tabIdToTimer[tabId]) {
return;
}
- if ( vAPI.isBehindTheSceneTabId(tabId) ) {
+
+ if (vAPI.isBehindTheSceneTabId(tabId)) {
return;
}
- tabIdToTimer[tabId] = vAPI.setTimeout(updateBadge.bind(this, tabId), 750);
+
+ tabIdToTimer[tabId] =
+ vAPI.setTimeout(updateBadge.bind(this, tabId), 750);
};
})();
- /******************************************************************************/
+ ηm.updateTitle = (function () {
+ let tabIdToTimer = Object.create(null);
+ let tabIdToTryCount = Object.create(null);
+ let delay = 499;
- ηm.updateTitle = (function() {
- var tabIdToTimer = Object.create(null);
- var tabIdToTryCount = Object.create(null);
- var delay = 499;
-
- var tryNoMore = function(tabId) {
+ let tryNoMore = function (tabId) {
delete tabIdToTryCount[tabId];
};
- var tryAgain = function(tabId) {
- var count = tabIdToTryCount[tabId];
- if ( count === undefined ) {
+ let tryAgain = function (tabId) {
+ let count = tabIdToTryCount[tabId];
+ if (count === undefined) {
return false;
}
- if ( count === 1 ) {
+
+ if (count === 1) {
delete tabIdToTryCount[tabId];
return false;
}
+
tabIdToTryCount[tabId] = count - 1;
- tabIdToTimer[tabId] = vAPI.setTimeout(updateTitle.bind(ηm, tabId), delay);
+ tabIdToTimer[tabId] =
+ vAPI.setTimeout(updateTitle.bind(ηm, tabId), delay);
return true;
};
- var onTabReady = function(tabId, tab) {
- if ( !tab ) {
+ let onTabReady = function (tabId, tab) {
+ if (!tab) {
return tryNoMore(tabId);
}
- var pageStore = this.pageStoreFromTabId(tabId);
- if ( pageStore === null ) {
+
+ let pageStore = this.pageStoreFromTabId(tabId);
+ if (pageStore === null) {
return tryNoMore(tabId);
}
- if ( !tab.title && tryAgain(tabId) ) {
+
+ if (!tab.title && tryAgain(tabId)) {
return;
}
+
// https://github.com/gorhill/uMatrix/issues/225
// Sometimes title changes while page is loading.
- var settled = tab.title && tab.title === pageStore.title;
+ let settled = tab.title && tab.title === pageStore.title;
pageStore.title = tab.title || tab.url || '';
this.pageStoresToken = Date.now();
- if ( settled || !tryAgain(tabId) ) {
+ if (settled || !tryAgain(tabId)) {
tryNoMore(tabId);
}
};
- var updateTitle = function(tabId) {
+ let updateTitle = function (tabId) {
delete tabIdToTimer[tabId];
vAPI.tabs.get(tabId, onTabReady.bind(this, tabId));
};
- return function(tabId) {
- if ( vAPI.isBehindTheSceneTabId(tabId) ) {
+ return function (tabId) {
+ if (vAPI.isBehindTheSceneTabId(tabId)) {
return;
}
- if ( tabIdToTimer[tabId] ) {
+
+ if (tabIdToTimer[tabId]) {
clearTimeout(tabIdToTimer[tabId]);
}
- tabIdToTimer[tabId] = vAPI.setTimeout(updateTitle.bind(this, tabId), delay);
+
+ tabIdToTimer[tabId] =
+ vAPI.setTimeout(updateTitle.bind(this, tabId), delay);
tabIdToTryCount[tabId] = 5;
};
})();
- /******************************************************************************/
-
// Stale page store entries janitor
// https://github.com/chrisaljoudi/uBlock/issues/455
- (function() {
- var cleanupPeriod = 7 * 60 * 1000;
- var cleanupSampleAt = 0;
- var cleanupSampleSize = 11;
-
- var cleanup = function() {
- var vapiTabs = vAPI.tabs;
- var tabIds = Object.keys(ηm.pageStores).sort();
- var checkTab = function(tabId) {
- vapiTabs.get(tabId, function(tab) {
- if ( !tab ) {
+ (function () {
+ let cleanupPeriod = 7 * 60 * 1000;
+ let cleanupSampleAt = 0;
+ let cleanupSampleSize = 11;
+
+ let cleanup = function () {
+ let tabIds = Object.keys(ηm.pageStores).sort();
+ let checkTab = function(tabId) {
+ vAPI.tabs.get(tabId, function (tab) {
+ if (!tab) {
ηm.unbindTabFromPageStats(tabId);
}
});
};
- if ( cleanupSampleAt >= tabIds.length ) {
+ if (cleanupSampleAt >= tabIds.length) {
cleanupSampleAt = 0;
}
- var tabId;
- var n = Math.min(cleanupSampleAt + cleanupSampleSize, tabIds.length);
- for ( var i = cleanupSampleAt; i < n; i++ ) {
+
+ let tabId;
+ let n =
+ Math.min(cleanupSampleAt + cleanupSampleSize, tabIds.length);
+
+ for (let i=cleanupSampleAt; i<n; i++) {
tabId = tabIds[i];
- if ( vAPI.isBehindTheSceneTabId(tabId) ) {
+ if (vAPI.isBehindTheSceneTabId(tabId)) {
continue;
}
checkTab(tabId);
@@ -714,7 +751,4 @@ housekeep itself.
vAPI.setTimeout(cleanup, cleanupPeriod);
})();
-
- /******************************************************************************/
-
})();
diff --git a/js/traffic.js b/js/traffic.js
index 6a6f700..8e408cf 100644
--- a/js/traffic.js
+++ b/js/traffic.js
@@ -29,6 +29,8 @@
ηMatrix.webRequest = (function() {
+ Cu.import('chrome://ematrix/content/lib/UriTools.jsm');
+
/******************************************************************************/
// Intercept and filter web requests according to white and black lists.
@@ -36,7 +38,7 @@
var onBeforeRootFrameRequestHandler = function(details) {
var ηm = ηMatrix;
var requestURL = details.url;
- var requestHostname = ηm.URI.hostnameFromURI(requestURL);
+ var requestHostname = UriTools.hostnameFromURI(requestURL);
var tabId = details.tabId;
ηm.tabContextManager.push(tabId, requestURL);
@@ -76,7 +78,7 @@
var onBeforeRequestHandler = function(details) {
var ηm = ηMatrix,
- ηmuri = ηm.URI,
+ ηmuri = UriTools,
requestURL = details.url,
requestScheme = ηmuri.schemeFromURI(requestURL);
@@ -144,7 +146,7 @@
var onBeforeSendHeadersHandler = function(details) {
var ηm = ηMatrix,
- ηmuri = ηm.URI,
+ ηmuri = UriTools,
requestURL = details.url,
requestScheme = ηmuri.schemeFromURI(requestURL);
@@ -304,7 +306,7 @@
var csp = [],
cspReport = [],
rootHostname = tabContext.rootHostname,
- requestHostname = ηm.URI.hostnameFromURI(requestURL);
+ requestHostname = UriTools.hostnameFromURI(requestURL);
// Inline script tags.
if ( ηm.mustAllow(rootHostname, requestHostname, 'script' ) !== true ) {
diff --git a/js/user-rules.js b/js/user-rules.js
index 02ff787..4ab850b 100644
--- a/js/user-rules.js
+++ b/js/user-rules.js
@@ -21,79 +21,69 @@
uMatrix Home: https://github.com/gorhill/uMatrix
*/
-/* global uDom */
-
'use strict';
-/******************************************************************************/
-
-(function() {
-
- /******************************************************************************/
-
+(function () {
// Switches before, rules after
- var directiveSort = function(a, b) {
- var aIsSwitch = a.indexOf(':') !== -1;
- var bIsSwitch = b.indexOf(':') !== -1;
- if ( aIsSwitch === bIsSwitch ) {
+ let directiveSort = function (a, b) {
+ let aIsSwitch = a.indexOf(':') !== -1;
+ let bIsSwitch = b.indexOf(':') !== -1;
+
+ if (aIsSwitch === bIsSwitch) {
return a.localeCompare(b);
}
+
return aIsSwitch ? -1 : 1;
};
- /******************************************************************************/
+ let processUserRules = function (response) {
+ let allRules = {};
+ let permanentRules = {};
+ let temporaryRules = {};
- var processUserRules = function(response) {
- var rules, rule, i;
- var allRules = {};
- var permanentRules = {};
- var temporaryRules = {};
- var onLeft, onRight;
-
- rules = response.permanentRules.split(/\n+/);
- i = rules.length;
- while ( i-- ) {
- rule = rules[i].trim();
- if ( rule.length !== 0 ) {
+ 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;
}
}
+
rules = response.temporaryRules.split(/\n+/);
- i = rules.length;
- while ( i-- ) {
- rule = rules[i].trim();
- if ( rule.length !== 0 ) {
+ for (let i=rules.length-1; i>=0; --i) {
+ let rule = rules[i].trim();
+ if (rule.length !== 0) {
temporaryRules[rule] = allRules[rule] = true;
}
}
- var permanentList = document.createDocumentFragment(),
- temporaryList = document.createDocumentFragment(),
- li;
+ let permanentList = document.createDocumentFragment();
+ let temporaryList = document.createDocumentFragment();
+ let li;
rules = Object.keys(allRules).sort(directiveSort);
- for ( i = 0; i < rules.length; i++ ) {
- rule = rules[i];
- onLeft = permanentRules.hasOwnProperty(rule);
- onRight = temporaryRules.hasOwnProperty(rule);
- if ( onLeft && onRight ) {
- li = document.createElement('li');
+ 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');
+
+ if (onLeft && onRight) {
li.textContent = rule;
permanentList.appendChild(li);
li = document.createElement('li');
li.textContent = rule;
temporaryList.appendChild(li);
- } else if ( onLeft ) {
- li = document.createElement('li');
+ } else if (onLeft) {
li.textContent = rule;
permanentList.appendChild(li);
li = document.createElement('li');
li.textContent = rule;
li.className = 'notRight toRemove';
temporaryList.appendChild(li);
- } else if ( onRight ) {
- li = document.createElement('li');
+ } else if (onRight) {
li.textContent = '\xA0';
permanentList.appendChild(li);
li = document.createElement('li');
@@ -109,17 +99,16 @@
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')
+ .toggleClass('dirty',
+ response.temporaryRules !== response.permanentRules);
};
- /******************************************************************************/
-
// https://github.com/chrisaljoudi/uBlock/issues/757
// Support RequestPolicy rule syntax
-
- var fromRequestPolicy = function(content) {
- var matches = /\[origins-to-destinations\]([^\[]+)/.exec(content);
- if ( matches === null || matches.length !== 2 ) {
+ let fromRequestPolicy = function (content) {
+ let matches = /\[origins-to-destinations\]([^\[]+)/.exec(content);
+ if (matches === null || matches.length !== 2) {
return;
}
return matches[1].trim()
@@ -127,88 +116,95 @@
.replace(/\n/g, ' * allow\n');
};
- /******************************************************************************/
-
// https://github.com/gorhill/uMatrix/issues/270
+ let fromNoScript = function (content) {
+ let noscript = null;
- var fromNoScript = function(content) {
- var noscript = null;
try {
noscript = JSON.parse(content);
} catch (e) {
}
- if (
- noscript === null ||
- typeof noscript !== 'object' ||
- typeof noscript.prefs !== 'object' ||
- typeof noscript.prefs.clearClick === 'undefined' ||
- typeof noscript.whitelist !== 'string' ||
- typeof noscript.V !== 'string'
- ) {
+
+ if (noscript === null
+ || typeof noscript !== 'object'
+ || typeof noscript.prefs !== 'object'
+ || typeof noscript.prefs.clearClick === 'undefined'
+ || typeof noscript.whitelist !== 'string'
+ || typeof noscript.V !== 'string') {
return;
}
- var out = new Set();
- var reBad = /[a-z]+:\w*$/;
- var reURL = /[a-z]+:\/\/([0-9a-z.-]+)/;
- var directives = noscript.whitelist.split(/\s+/);
- var i = directives.length;
- var directive, matches;
- while ( i-- ) {
- directive = directives[i].trim();
- if ( directive === '' ) {
+
+ 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) {
+ let directive = directives[i].trim();
+ if (directive === '') {
continue;
}
- if ( reBad.test(directive) ) {
+ if (reBad.test(directive)) {
continue;
}
- matches = reURL.exec(directive);
- if ( matches !== null ) {
+
+ let matches = reURL.exec(directive);
+ if (matches !== null) {
directive = matches[1];
}
+
out.add('* ' + directive + ' * allow');
out.add('* ' + directive + ' script allow');
out.add('* ' + directive + ' frame allow');
}
+
return Array.from(out).join('\n');
};
- /******************************************************************************/
-
- var handleImportFilePicker = function() {
- var fileReaderOnLoadHandler = function() {
- if ( typeof this.result !== 'string' || this.result === '' ) {
+ let handleImportFilePicker = function () {
+ let fileReaderOnLoadHandler = function () {
+ if (typeof this.result !== 'string' || this.result === '') {
return;
}
- var result = fromRequestPolicy(this.result);
- if ( result === undefined ) {
+
+ let result = fromRequestPolicy(this.result);
+ if (result === undefined) {
result = fromNoScript(this.result);
- if ( result === undefined ) {
+ if (result === undefined) {
result = this.result;
}
}
- if ( this.result === '' ) { return; }
- var request = {
+
+ if (this.result === '') {
+ return;
+ }
+
+ let request = {
'what': 'setUserRules',
- 'temporaryRules': rulesFromHTML('#diff .right li') + '\n' + result
+ '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 === '' ) {
+ 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;
}
- var fr = new FileReader();
+
+ let fr = new FileReader();
fr.onload = fileReaderOnLoadHandler;
fr.readAsText(file);
};
- /******************************************************************************/
-
- var startImportFilePicker = function() {
- var input = document.getElementById('importFilePicker');
+ 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.
@@ -216,112 +212,105 @@
input.click();
};
- /******************************************************************************/
-
function exportUserRulesToFile() {
vAPI.download({
- 'url': 'data:text/plain,' + encodeURIComponent(rulesFromHTML('#diff .left li') + '\n'),
- 'filename': uDom('[data-i18n="userRulesDefaultFileName"]').text()
+ 'url': 'data:text/plain,'
+ + encodeURIComponent(rulesFromHTML('#diff .left li') + '\n'),
+ 'filename': uDom('[data-i18n="userRulesDefaultFileName"]').text(),
});
}
- /******************************************************************************/
-
var rulesFromHTML = function(selector) {
- var rules = [];
- var lis = uDom(selector);
- var li;
- for ( var i = 0; i < lis.length; i++ ) {
- li = lis.at(i);
- if ( li.hasClassName('toRemove') ) {
+ let rules = [];
+ let lis = uDom(selector);
+
+ for (let i=0; i<lis.length; ++i) {
+ let li = lis.at(i);
+ if (li.hasClassName('toRemove')) {
rules.push('');
} else {
rules.push(li.text());
}
}
+
return rules.join('\n');
};
- /******************************************************************************/
-
- var revertHandler = function() {
- var request = {
+ let revertHandler = function () {
+ let request = {
'what': 'setUserRules',
- 'temporaryRules': rulesFromHTML('#diff .left li')
+ 'temporaryRules': rulesFromHTML('#diff .left li'),
};
+
vAPI.messaging.send('user-rules.js', request, processUserRules);
};
- /******************************************************************************/
-
- var commitHandler = function() {
+ let commitHandler = function () {
var request = {
'what': 'setUserRules',
- 'permanentRules': rulesFromHTML('#diff .right li')
+ 'permanentRules': rulesFromHTML('#diff .right li'),
};
+
vAPI.messaging.send('user-rules.js', request, processUserRules);
};
- /******************************************************************************/
-
- var editStartHandler = function() {
+ let editStartHandler = function () {
uDom('#diff .right textarea').val(rulesFromHTML('#diff .right li'));
- var parent = uDom(this).ancestors('#diff');
+ let parent = uDom(this).ancestors('#diff');
parent.toggleClass('edit', true);
};
- /******************************************************************************/
-
- var editStopHandler = function() {
- var parent = uDom(this).ancestors('#diff');
+ let editStopHandler = function () {
+ let parent = uDom(this).ancestors('#diff');
parent.toggleClass('edit', false);
- var request = {
+
+ let request = {
'what': 'setUserRules',
- 'temporaryRules': uDom('#diff .right textarea').val()
+ 'temporaryRules': uDom('#diff .right textarea').val(),
};
+
vAPI.messaging.send('user-rules.js', request, processUserRules);
};
- /******************************************************************************/
-
- var editCancelHandler = function() {
- var parent = uDom(this).ancestors('#diff');
+ let editCancelHandler = function () {
+ let parent = uDom(this).ancestors('#diff');
parent.toggleClass('edit', false);
};
- /******************************************************************************/
-
- var temporaryRulesToggler = function() {
+ let temporaryRulesToggler = function() {
var li = uDom(this);
li.toggleClass('toRemove');
- var request = {
+
+ let request = {
'what': 'setUserRules',
- 'temporaryRules': rulesFromHTML('#diff .right li')
+ 'temporaryRules': rulesFromHTML('#diff .right li'),
};
+
vAPI.messaging.send('user-rules.js', request, processUserRules);
};
- /******************************************************************************/
-
- self.cloud.onPush = function() {
+ self.cloud.onPush = function () {
return rulesFromHTML('#diff .left li');
};
- self.cloud.onPull = function(data, append) {
- if ( typeof data !== 'string' ) { return; }
- if ( append ) {
+ self.cloud.onPull = function (data, append) {
+ if (typeof data !== 'string') {
+ return;
+ }
+
+ if (append) {
data = rulesFromHTML('#diff .right li') + '\n' + data;
}
- var request = {
+
+ let request = {
'what': 'setUserRules',
- 'temporaryRules': data
+ 'temporaryRules': data,
};
+
vAPI.messaging.send('user-rules.js', request, processUserRules);
};
- /******************************************************************************/
-
- uDom.onLoad(function() {
+ uDom.onLoad(function () {
// Handle user interaction
uDom('#importButton').on('click', startImportFilePicker);
uDom('#importFilePicker').on('change', handleImportFilePicker);
@@ -333,9 +322,8 @@
uDom('#editCancelButton').on('click', editCancelHandler);
uDom('#diff > .right > ul').on('click', 'li', temporaryRulesToggler);
- vAPI.messaging.send('user-rules.js', { what: 'getUserRules' }, processUserRules);
+ vAPI.messaging.send('user-rules.js', {
+ what: 'getUserRules',
+ }, processUserRules);
});
-
- /******************************************************************************/
-
})();
diff --git a/js/vapi-background.js b/js/vapi-background.js
index 7686bc9..e5e6998 100644
--- a/js/vapi-background.js
+++ b/js/vapi-background.js
@@ -22,7 +22,7 @@
*/
/* jshint bitwise: false, esnext: true */
-/* global self, Components, punycode */
+/* global self, Components */
// For background page
@@ -31,7 +31,9 @@
/******************************************************************************/
(function () {
- Cu.import('chrome://ematrix/content/HttpRequestHeaders.jsm');
+ Cu.import('chrome://ematrix/content/lib/HttpRequestHeaders.jsm');
+ Cu.import('chrome://ematrix/content/lib/PendingRequests.jsm');
+ Cu.import('chrome://ematrix/content/lib/Punycode.jsm');
// Icon-related stuff
vAPI.setIcon = function (tabId, iconId, badge) {
@@ -134,8 +136,6 @@
return this.QueryInterface(iid);
},
register: function () {
- this.pendingRingBufferInit();
-
Services.obs.addObserver(this, 'http-on-modify-request', true);
Services.obs.addObserver(this, 'http-on-examine-response', true);
Services.obs.addObserver(this, 'http-on-examine-cached-response', true);
@@ -174,82 +174,6 @@
this.contractID,
false);
},
- PendingRequest: function () {
- this.rawType = 0;
- this.tabId = 0;
- this._key = ''; // key is url, from URI.spec
- },
- // If all work fine, this map should not grow indefinitely. It
- // can have stale items in it, but these will be taken care of
- // when entries in the ring buffer are overwritten.
- pendingURLToIndex: new Map(),
- pendingWritePointer: 0,
- pendingRingBuffer: new Array(256),
- pendingRingBufferInit: function () {
- // Use and reuse pre-allocated PendingRequest objects =
- // less memory churning.
- for (let i=this.pendingRingBuffer.length-1; i>=0; --i) {
- this.pendingRingBuffer[i] = new this.PendingRequest();
- }
- },
- createPendingRequest: function (url) {
- // Pending request ring buffer:
- // +-------+-------+-------+-------+-------+-------+-------
- // |0 |1 |2 |3 |4 |5 |...
- // +-------+-------+-------+-------+-------+-------+-------
- //
- // URL to ring buffer index map:
- // { k = URL, s = ring buffer indices }
- //
- // s is a string which character codes map to ring buffer
- // indices -- for when the same URL is received multiple times
- // by shouldLoadListener() before the existing one is serviced
- // by the network request observer. I believe the use of a
- // string in lieu of an array reduces memory churning.
- let bucket;
- let i = this.pendingWritePointer;
- this.pendingWritePointer = i + 1 & 255;
-
- let preq = this.pendingRingBuffer[i];
- let si = String.fromCharCode(i);
-
- // Cleanup unserviced pending request
- if (preq._key !== '') {
- bucket = this.pendingURLToIndex.get(preq._key);
- if (bucket.length === 1) {
- this.pendingURLToIndex.delete(preq._key);
- } else {
- let pos = bucket.indexOf(si);
- this.pendingURLToIndex.set(preq._key,
- bucket.slice(0, pos)
- + bucket.slice(pos + 1));
- }
- }
-
- bucket = this.pendingURLToIndex.get(url);
- this.pendingURLToIndex.set(url, bucket === undefined
- ? si
- : bucket + si);
- preq._key = url;
- return preq;
- },
- lookupPendingRequest: function (url) {
- let bucket = this.pendingURLToIndex.get(url);
- if (bucket === undefined) {
- return null;
- }
-
- let i = bucket.charCodeAt(0);
- if (bucket.length === 1) {
- this.pendingURLToIndex.delete(url);
- } else {
- this.pendingURLToIndex.set(url, bucket.slice(1));
- }
-
- let preq = this.pendingRingBuffer[i];
- preq._key = ''; // mark as "serviced"
- return preq;
- },
handleRequest: function (channel, URI, tabId, rawType) {
let type = this.typeMap[rawType] || 'other';
@@ -460,7 +384,8 @@
// The channel was never serviced.
let tabId;
- let pendingRequest = this.lookupPendingRequest(URI.asciiSpec);
+ let pendingRequest =
+ PendingRequestBuffer.lookupRequest(URI.asciiSpec);
let rawType = 1;
let loadInfo = channel.loadInfo;
@@ -540,12 +465,7 @@
codePath: ''
};
- // Non-Fennec: common code paths.
(function () {
- if (vAPI.fennec) {
- return;
- }
-
let tbb = vAPI.toolbarButton;
let popupCommittedWidth = 0;
let popupCommittedHeight = 0;
@@ -571,8 +491,16 @@
button.setAttribute('badge', icon && icon.badge || '');
button.classList.toggle('off', !icon || !icon.img);
- let iconId = icon && icon.img ? icon.img : 'off';
- icon = 'url(' + vAPI.getURL('img/browsericons/icon19-' + iconId + '.png') + ')';
+ let iconId = (ηMatrix.userSettings.disableUpdateIcon) ?
+ icon && icon.img ? '19' : 'off' :
+ icon && icon.img ? icon.img : 'off';
+
+ icon = 'url('
+ + vAPI.getURL('img/browsericons/icon19-'
+ + iconId
+ + '.png')
+ + ')';
+
button.style.listStyleImage = icon;
};
@@ -814,12 +742,12 @@
// Found our button on this toolbar - but where on it?
let before = null;
- for (let i = index+1; i<currentset.length; ++i) {
+ for (let i=index+1; i<currentset.length; ++i) {
// The [id=...] notation doesn't work on
// space elements as they get a random ID each session
// (or something like that)
- // https://libregit.org/heckyel/ematrix/issues/5
- // https://libregit.org/heckyel/ematrix/issues/6
+ // 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
@@ -1246,7 +1174,7 @@
// Likelihood is that we do not have to punycode: given punycode overhead,
// it's faster to check and skip than do it unconditionally all the time.
- var punycodeHostname = punycode.toASCII;
+ var punycodeHostname = Punycode.toASCII;
var isNotASCII = /[^\x21-\x7F]/;
vAPI.punycodeHostname = function (hostname) {
diff --git a/js/vapi-core.js b/js/vapi-core.js
index 94ae599..fe80d7c 100644
--- a/js/vapi-core.js
+++ b/js/vapi-core.js
@@ -111,12 +111,9 @@
// }
// frameModule needs to be cleared too
- let frameModuleURL = vAPI.getURL('frameModule.js');
- let frameModule = {};
-
- Cu.import(frameModuleURL, frameModule);
- frameModule.contentObserver.unregister();
- Cu.unload(frameModuleURL);
+ Cu.import('chrome://ematrix/content/lib/FrameModule.jsm');
+ contentObserver.unregister();
+ Cu.unload('chrome://ematrix/content/lib/FrameModule.jsm');
});
vAPI.noTabId = '-1';
diff --git a/js/vapi-messaging.js b/js/vapi-messaging.js
index b4f468f..3c8ab02 100644
--- a/js/vapi-messaging.js
+++ b/js/vapi-messaging.js
@@ -26,7 +26,7 @@
/******************************************************************************/
(function () {
- Cu.import('chrome://ematrix/content/CallbackWrapper.jsm');
+ Cu.import('chrome://ematrix/content/lib/CallbackWrapper.jsm');
vAPI.messaging = {
get globalMessageManager() {
diff --git a/js/vapi-net.js b/js/vapi-net.js
index f8d1052..086e596 100644
--- a/js/vapi-net.js
+++ b/js/vapi-net.js
@@ -26,6 +26,8 @@
/******************************************************************************/
(function () {
+ Cu.import('chrome://ematrix/content/lib/PendingRequests.jsm');
+
vAPI.net = {};
vAPI.net.registerListeners = function () {
@@ -40,7 +42,8 @@
let shouldLoadListenerMessageName = location.host + ':shouldLoad';
let shouldLoadListener = function (e) {
let details = e.data;
- let pendingReq = vAPI.httpObserver.createPendingRequest(details.url);
+ let pendingReq =
+ PendingRequestBuffer.createRequest(details.url);
pendingReq.rawType = details.rawType;
pendingReq.tabId = vAPI.tabs.manager.tabIdFromTarget(e.target);
};
diff --git a/CallbackWrapper.jsm b/lib/CallbackWrapper.jsm
index 8611383..1d3664e 100644
--- a/CallbackWrapper.jsm
+++ b/lib/CallbackWrapper.jsm
@@ -17,10 +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.org/heckyel/ematrix
+ Home: https://gitlab.com/vannilla/ematrix
uMatrix Home: https://github.com/gorhill/uMatrix
*/
+'use strict';
+
var EXPORTED_SYMBOLS = ['CallbackWrapper'];
var junkyard = [];
diff --git a/lib/CookieCache.jsm b/lib/CookieCache.jsm
new file mode 100644
index 0000000..7b6fe6f
--- /dev/null
+++ b/lib/CookieCache.jsm
@@ -0,0 +1,171 @@
+/*******************************************************************************
+
+ ηMatrix - a browser extension to black/white list requests.
+ Copyright (C) 2014-2019 The uMatrix/uBlock Origin authors
+ Copyright (C) 2019 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
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see {http://www.gnu.org/licenses/}.
+
+ Home: https://gitlab.com/vannilla/ematrix
+ uMatrix Home: https://github.com/gorhill/uMatrix
+*/
+
+'use strict';
+
+Components.utils.import('chrome://ematrix/content/lib/UriTools.jsm');
+
+var EXPORTED_SYMBOLS = ['CookieCache', 'CookieUtils'];
+
+var junkyard = [];
+var dict = new Map();
+
+var CookieEntry = function (cookie) {
+ this.usedOn = new Set();
+ this.init(cookie);
+};
+
+CookieEntry.prototype.init = function (cookie) {
+ this.secure = cookie.secure;
+ this.session = cookie.session;
+ this.anySubdomain = cookie.domain.charAt(0) === '.';
+ this.hostname = this.anySubdomain ? cookie.domain.slice(1) : cookie.domain;
+ this.domain = UriTools.domainFromHostname(this.hostname) || this.hostname;
+ this.path = cookie.path;
+ this.name = cookie.name;
+ this.value = cookie.value;
+ this.tstamp = Date.now();
+ this.usedOn.clear();
+ return this;
+};
+
+CookieEntry.prototype.dispose = function () {
+ this.hostname = '';
+ this.domain = '';
+ this.path = '';
+ this.name = '';
+ this.value = '';
+ this.usedOn.clear();
+ return this;
+};
+
+var CookieUtils = {
+ keyFromCookie: function (cookie) {
+ let cb = [];
+
+ cb[0] = cookie.secure ? 'https' : 'http';
+ cb[1] = '://';
+ cb[2] = cookie.domain.charAt(0) === '.' ?
+ cookie.domain.slice(1) :
+ cookie.domain;
+ cb[3] = cookie.path;
+ cb[4] = '{';
+ cb[5] = cookie.session ? 'session' : 'persistent';
+ cb[6] = '-cookie:';
+ cb[7] = cookie.name;
+ cb[8] = '}';
+
+ return cb.join('');
+ },
+ keyFromURL: function (url, type, name) {
+ if (typeof url !== 'object') {
+ throw new Error('Invalid URL parameter');
+ }
+
+ let cb = [];
+
+ cb[0] = url.scheme;
+ cb[1] = '://';
+ cb[2] = url.hostname;
+ cb[3] = url.path;
+ cb[4] = '{';
+ cb[5] = type;
+ cb[6] = '-cookie:';
+ cb[7] = name;
+ cb[8] = '}';
+
+ return cb.join('');
+ },
+ urlFromEntry: function (entry) {
+ if (!entry) {
+ return '';
+ }
+
+ return (entry.secure ? 'https://' : 'http://')
+ + entry.hostname
+ + entry.path;
+ },
+ matchDomains: function (key, allHosts) {
+ let entry = CookieCache.get(key);
+
+ if (entry === undefined) {
+ return false;
+ }
+
+ if (allHosts.indexOf(' '+entry.hostname+' ') < 0) {
+ if (!entry.anySubdomain) {
+ return false;
+ }
+
+ if (allHosts.indexOf('.'+entry.hostname+' ') < 0) {
+ return false;
+ }
+ }
+
+ return true;
+ },
+};
+
+var CookieCache = {
+ add: function (cookie) {
+ let key = CookieUtils.keyFromCookie(cookie);
+ let value = dict.get(key);
+
+ if (value === undefined) {
+ value = junkyard.pop();
+ if (value) {
+ value.init(cookie);
+ } else {
+ value = new CookieEntry(cookie);
+ }
+ dict.set(key, value);
+ }
+
+ return value;
+ },
+ addVector: function (vector) {
+ for (let i=vector.length-1; i>=0; --i) {
+ CookieCache.add(vector[i]);
+ }
+ },
+ remove: function (cookie) {
+ let value = dict.get(cookie);
+ if (cookie === undefined) {
+ return false;
+ }
+
+ dict.delete(cookie);
+
+ if (junkyard.length < 25) {
+ junkyard.push(value.dispose());
+ }
+
+ return true;
+ },
+ get: function (key) {
+ return dict.get(key);
+ },
+ keys: function () {
+ return dict.keys();
+ }
+};
diff --git a/frameModule.js b/lib/FrameModule.jsm
index ed07a8b..ef149ae 100644
--- a/frameModule.js
+++ b/lib/FrameModule.jsm
@@ -17,30 +17,24 @@
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.org/heckyel/ematrix
+ Home: https://gitlab.com/vannilla/ematrix
uMatrix Home: https://github.com/gorhill/uMatrix
*/
-/* global Components */
-
'use strict';
-/******************************************************************************/
-
// https://github.com/gorhill/uBlock/issues/800#issuecomment-146580443
-this.EXPORTED_SYMBOLS = ['contentObserver', 'LocationChangeListener'];
+var EXPORTED_SYMBOLS = ['contentObserver', 'LocationChangeListener'];
const {interfaces: Ci, utils: Cu} = Components;
-const {Services} = Cu.import('resource://gre/modules/Services.jsm', null);
-const {XPCOMUtils} = Cu.import('resource://gre/modules/XPCOMUtils.jsm', null);
+Cu.import('resource://gre/modules/Services.jsm');
+Cu.import('resource://gre/modules/XPCOMUtils.jsm');
-const hostName = Services.io.newURI(Components.stack.filename, null, null).host;
+var hostName = Services.io.newURI(Components.stack.filename, null, null).host;
// Cu.import('resource://gre/modules/Console.jsm');
-/******************************************************************************/
-
-const getMessageManager = function(win) {
+const getMessageManager = function (win) {
let iface = win
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDocShell)
@@ -64,8 +58,6 @@ const getMessageManager = function(win) {
return null;
};
-/******************************************************************************/
-
var contentObserver = {
classDescription: 'content-policy for ' + hostName,
classID: Components.ID('{c84283d4-9975-41b7-b1a4-f106af56b51d}'),
@@ -86,93 +78,89 @@ var contentObserver = {
get categoryManager() {
return Components.classes['@mozilla.org/categorymanager;1']
- .getService(Ci.nsICategoryManager);
+ .getService(Ci.nsICategoryManager);
},
QueryInterface: XPCOMUtils.generateQI([
- Ci.nsIFactory,
- Ci.nsIObserver,
- Ci.nsIContentPolicy,
- Ci.nsISupportsWeakReference
+ Ci.nsIFactory,
+ Ci.nsIObserver,
+ Ci.nsIContentPolicy,
+ Ci.nsISupportsWeakReference
]),
- createInstance: function(outer, iid) {
- if ( outer ) {
+ createInstance: function (outer, iid) {
+ if (outer) {
throw Components.results.NS_ERROR_NO_AGGREGATION;
}
return this.QueryInterface(iid);
},
- register: function() {
+ register: function () {
Services.obs.addObserver(this, 'document-element-inserted', true);
- if ( !this.modernFirefox ) {
- this.componentRegistrar.registerFactory(
- this.classID,
- this.classDescription,
- this.contractID,
- this
- );
- this.categoryManager.addCategoryEntry(
- 'content-policy',
- this.contractID,
- this.contractID,
- false,
- true
- );
+ if (!this.modernFirefox) {
+ this.componentRegistrar
+ .registerFactory(this.classID,
+ this.classDescription,
+ this.contractID,
+ this);
+ this.categoryManager
+ .addCategoryEntry('content-policy',
+ this.contractID,
+ this.contractID,
+ false,
+ true);
}
},
- unregister: function() {
+ unregister: function () {
Services.obs.removeObserver(this, 'document-element-inserted');
- if ( !this.modernFirefox ) {
- this.componentRegistrar.unregisterFactory(
- this.classID,
- this
- );
- this.categoryManager.deleteCategoryEntry(
- 'content-policy',
- this.contractID,
- false
- );
+ if (!this.modernFirefox) {
+ this.componentRegistrar
+ .unregisterFactory(this.classID,
+ this);
+ this.categoryManager
+ .deleteCategoryEntry('content-policy',
+ this.contractID,
+ false);
}
},
// https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIContentPolicy
// https://bugzil.la/612921
- shouldLoad: function(type, location, origin, context) {
- if ( Services === undefined || !context ) {
+ shouldLoad: function (type, location, origin, context) {
+ if (!context) {
return this.ACCEPT;
}
- if ( !location.schemeIs('http') && !location.schemeIs('https') ) {
+ if (!location.schemeIs('http') && !location.schemeIs('https')) {
return this.ACCEPT;
}
- var contextWindow;
- if ( type === this.MAIN_FRAME ) {
+ let contextWindow;
+ if (type === this.MAIN_FRAME) {
contextWindow = context.contentWindow || context;
- } else if ( type === this.SUB_FRAME ) {
+ } else if (type === this.SUB_FRAME) {
contextWindow = context.contentWindow;
} else {
contextWindow = (context.ownerDocument || context).defaultView;
}
// https://github.com/gorhill/uMatrix/issues/706
- if ( !contextWindow ) {
+ if (!contextWindow) {
return this.ACCEPT;
}
// The context for the toolbar popup is an iframe element here,
// so check context.top instead of context
- if ( !contextWindow.top || !contextWindow.location ) {
+ if (!contextWindow.top || !contextWindow.location) {
return this.ACCEPT;
}
let messageManager = getMessageManager(contextWindow);
- if ( messageManager === null ) {
+ if (messageManager === null) {
return this.ACCEPT;
}
@@ -181,7 +169,7 @@ var contentObserver = {
url: location.asciiSpec
};
- if ( typeof messageManager.sendRpcMessage === 'function' ) {
+ if (typeof messageManager.sendRpcMessage === 'function') {
// https://bugzil.la/1092216
messageManager.sendRpcMessage(this.cpMessageName, details);
} else {
@@ -192,11 +180,11 @@ var contentObserver = {
return this.ACCEPT;
},
- initContentScripts: function(win, sandbox) {
+ initContentScripts: function (win, sandbox) {
let messager = getMessageManager(win);
let sandboxId = hostName + ':sb:' + this.uniqueSandboxId++;
- if ( sandbox ) {
+ if (sandbox) {
let sandboxName = [
win.location.href.slice(0, 100),
win.document.title.slice(0, 100)
@@ -212,7 +200,7 @@ var contentObserver = {
wantXHRConstructor: false
});
- sandbox.injectScript = function(script) {
+ sandbox.injectScript = function (script) {
Services.scriptloader.loadSubScript(script, sandbox);
};
}
@@ -223,35 +211,27 @@ var contentObserver = {
sandbox._sandboxId_ = sandboxId;
sandbox.sendAsyncMessage = messager.sendAsyncMessage;
- sandbox.addMessageListener = function(callback) {
- if ( sandbox._messageListener_ ) {
+ sandbox.addMessageListener = function (callback) {
+ if (sandbox._messageListener_) {
sandbox.removeMessageListener();
}
- sandbox._messageListener_ = function(message) {
+ sandbox._messageListener_ = function (message) {
callback(message.data);
};
- messager.addMessageListener(
- sandbox._sandboxId_,
- sandbox._messageListener_
- );
- messager.addMessageListener(
- hostName + ':broadcast',
- sandbox._messageListener_
- );
+ messager.addMessageListener(sandbox._sandboxId_,
+ sandbox._messageListener_);
+ messager.addMessageListener(hostName + ':broadcast',
+ sandbox._messageListener_);
};
- sandbox.removeMessageListener = function() {
+ sandbox.removeMessageListener = function () {
try {
- messager.removeMessageListener(
- sandbox._sandboxId_,
- sandbox._messageListener_
- );
- messager.removeMessageListener(
- hostName + ':broadcast',
- sandbox._messageListener_
- );
+ messager.removeMessageListener(sandbox._sandboxId_,
+ sandbox._messageListener_);
+ messager.removeMessageListener(hostName + ':broadcast',
+ sandbox._messageListener_);
} catch (ex) {
// It throws sometimes, mostly when the popup closes
}
@@ -262,26 +242,28 @@ var contentObserver = {
return sandbox;
},
- observe: function(doc) {
+ observe: function (doc) {
let win = doc.defaultView;
- if ( !win ) {
+ if (!win) {
return;
}
let loc = win.location;
- if ( !loc ) {
+ if (!loc) {
return;
}
// https://github.com/gorhill/uBlock/issues/260
// TODO: We may have to skip more types, for now let's be
// conservative, i.e. let's not test against `text/html`.
- if ( doc.contentType.lastIndexOf('image/', 0) === 0 ) {
+ if (doc.contentType.lastIndexOf('image/', 0) === 0) {
return;
}
- if ( loc.protocol !== 'http:' && loc.protocol !== 'https:' && loc.protocol !== 'file:' ) {
- if ( loc.protocol === 'chrome:' && loc.host === hostName ) {
+ if (loc.protocol !== 'http:'
+ && loc.protocol !== 'https:'
+ && loc.protocol !== 'file:') {
+ if (loc.protocol === 'chrome:' && loc.host === hostName) {
this.initContentScripts(win);
}
@@ -307,20 +289,21 @@ var contentObserver = {
lss(this.contentBaseURI + 'contentscript.js', sandbox);
};
- if ( doc.readyState === 'loading') {
+ if (doc.readyState === 'loading') {
doc.addEventListener('DOMContentLoaded', docReady, true);
} else {
- docReady({ target: doc, type: 'DOMContentLoaded' });
+ docReady({
+ target: doc,
+ type: 'DOMContentLoaded',
+ });
}
}
};
-/******************************************************************************/
-
-const locationChangedMessageName = hostName + ':locationChanged';
+var locationChangedMessageName = hostName + ':locationChanged';
-var LocationChangeListener = function(docShell) {
- if ( !docShell ) {
+var LocationChangeListener = function (docShell) {
+ if (!docShell) {
return;
}
@@ -328,7 +311,7 @@ var LocationChangeListener = function(docShell) {
var ds = requestor.getInterface(Ci.nsIWebProgress);
var mm = requestor.getInterface(Ci.nsIContentFrameMessageManager);
- if ( ds && mm && typeof mm.sendAsyncMessage === 'function' ) {
+ if (ds && mm && typeof mm.sendAsyncMessage === 'function') {
this.docShell = ds;
this.messageManager = mm;
ds.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_LOCATION);
@@ -340,18 +323,15 @@ LocationChangeListener.prototype.QueryInterface = XPCOMUtils.generateQI([
'nsISupportsWeakReference'
]);
-LocationChangeListener.prototype.onLocationChange = function(webProgress, request, location, flags) {
- if ( !webProgress.isTopLevel ) {
- return;
- }
- this.messageManager.sendAsyncMessage(locationChangedMessageName, {
- url: location.asciiSpec,
- flags: flags,
- });
-};
-
-/******************************************************************************/
+LocationChangeListener.prototype.onLocationChange =
+ function (webProgress, request, location, flags) {
+ if (!webProgress.isTopLevel) {
+ return;
+ }
+ this.messageManager.sendAsyncMessage(locationChangedMessageName, {
+ url: location.asciiSpec,
+ flags: flags,
+ });
+ };
contentObserver.register();
-
-/******************************************************************************/
diff --git a/HttpRequestHeaders.jsm b/lib/HttpRequestHeaders.jsm
index 217b5a3..4c125eb 100644
--- a/HttpRequestHeaders.jsm
+++ b/lib/HttpRequestHeaders.jsm
@@ -17,10 +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.org/heckyel/ematrix
+ Home: https://gitlab.com/vannilla/ematrix
uMatrix Home: https://github.com/gorhill/uMatrix
*/
+'use strict';
+
var EXPORTED_SYMBOLS = ['HTTPRequestHeaders'];
var junkyard = [];
diff --git a/lib/LiquidDict.jsm b/lib/LiquidDict.jsm
new file mode 100644
index 0000000..65de9c5
--- /dev/null
+++ b/lib/LiquidDict.jsm
@@ -0,0 +1,175 @@
+/*******************************************************************************
+
+ ηMatrix - a browser extension to black/white list requests.
+ Copyright (C) 2014-2019 Raymond Hill
+ Copyright (C) 2019 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
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see {http://www.gnu.org/licenses/}.
+
+ Home: https://gitlab.com/vannilla/ematrix
+ uMatrix Home: https://github.com/gorhill/uMatrix
+*/
+
+'use strict';
+
+var EXPORTED_SYMBOLS = ['LiquidDict'];
+
+var LiquidDict = function () {
+ this.dict = {};
+ this.count = 0;
+ this.duplicateCount = 0;
+ this.bucketCount = 0;
+ this.frozenCount = 0;
+ this.cutoff = 500;
+};
+
+function meltBucket(dict, len, bucket) {
+ let map = {};
+
+ --dict.frozenCount;
+
+ if (bucket.charAt(0) === ' ') {
+ bucket.trim().split(' ').map(function (e) {
+ map[e] = true;
+ });
+ } else {
+ for (let i=0; i<bucket.length; i+=len) {
+ map[bucket.substring(i, len)] = true;
+ }
+ }
+
+ return map;
+}
+
+function melt(dict) {
+ let buckets = dict.dict;
+ for (let key in buckets) {
+ let bucket = buckets[key];
+ if (typeof bucket === 'string') {
+ buckets[key] = meltBucket(dict, key.charCodeAt(0) & 0xFF, bucket);
+ }
+ }
+}
+
+function freezeBucket(dict, bucket) {
+ let words = Object.keys(bucket);
+ let wlen = words[0].length;
+
+ ++dict.frozenCount;
+
+ if (wlen * words.length < dict.cutoff) {
+ return ' ' + words.join(' ') + ' ';
+ }
+
+ return words.sort().join('');
+}
+
+LiquidDict.prototype.makeKey = function (word) {
+ let len = word.length;
+ if (len > 255) {
+ len = 255;
+ }
+
+ let 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) {
+ let key = this.makeKey(word);
+ let 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;
+ }
+
+ let len = word.length;
+ let left = 0;
+ let right = ~~(bucket.length / len + 0.5);
+
+ while (left < right) {
+ let i = left + right >> 1;
+ let needle = bucket.substr(len * i, len);
+
+ if (word < needle) {
+ right = i;
+ } else if (word > needle) {
+ left = i + 1;
+ } else {
+ return true;
+ }
+ }
+
+ return false;
+};
+
+LiquidDict.prototype.add = function (word) {
+ let key = this.makeKey(word);
+ if (key === undefined) {
+ return false;
+ }
+
+ let bucket = this.dict[key];
+ if (bucket === undefined) {
+ this.dict[key] = bucket = {};
+ ++this.bucketCount;
+ bucket[word] = true;
+ ++this.count;
+
+ 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;
+
+ return true;
+ }
+
+ ++this.duplicateCount;
+
+ return false;
+};
+
+LiquidDict.prototype.freeze = function () {
+ let buckets = this.dict;
+
+ for (let key in buckets) {
+ let 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;
+}
diff --git a/lib/PendingRequests.jsm b/lib/PendingRequests.jsm
new file mode 100644
index 0000000..1214c3c
--- /dev/null
+++ b/lib/PendingRequests.jsm
@@ -0,0 +1,97 @@
+/*******************************************************************************
+
+ ηMatrix - a browser extension to black/white list requests.
+ Copyright (C) 2014-2019 The uMatrix/uBlock Origin authors
+ Copyright (C) 2019 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
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see {http://www.gnu.org/licenses/}.
+
+ Home: https://gitlab.com/vannilla/ematrix
+ uMatrix Home: https://github.com/gorhill/uMatrix
+*/
+
+'use strict';
+
+var EXPORTED_SYMBOLS = ['PendingRequestBuffer'];
+
+function PendingRequest() {
+ this.rawType = 0;
+ this.tabId = 0;
+ this._key = '';
+}
+
+var urlToIndex = new Map();
+var writePointer = 0;
+var ringBuffer = new Array(256);
+
+var PendingRequestBuffer = (function () {
+ for (let i=ringBuffer.length-1; i>=0; --i) {
+ ringBuffer[i] = new PendingRequest();
+ }
+
+ return {
+ createRequest: function (url) {
+ // URL to ring buffer index map:
+ // { k = URL, s = ring buffer indices }
+ //
+ // s is a string which character codes map to ring buffer
+ // indices -- for when the same URL is received multiple times
+ // by shouldLoadListener() before the existing one is serviced
+ // by the network request observer. I believe the use of a
+ // string in lieu of an array reduces memory churning.
+ let bucket;
+ let i = writePointer;
+ writePointer = i + 1 & 255;
+
+ let req = ringBuffer[i];
+ let str = String.fromCharCode(i);
+
+ if (req._key !== '') {
+ bucket = urlToIndex.get(req._key);
+ if (bucket.lenght === 1) {
+ urlToIndex.delete(req._key);
+ } else {
+ let pos = bucket.indexOf(str);
+ urlToIndex.set(req._key,
+ bucket.slice(0, pos)+bucket.slice(pos+1));
+ }
+ }
+
+ bucket = urlToIndex.get(url);
+ urlToIndex.set(url,
+ (bucket === undefined) ? str : bucket + str);
+ req._key = url;
+
+ return req;
+ },
+ lookupRequest: function (url) {
+ let bucket = urlToIndex.get(url);
+ if (bucket === undefined) {
+ return null;
+ }
+
+ let i = bucket.charCodeAt(0);
+ if (bucket.length === 1) {
+ urlToIndex.delete(url);
+ } else {
+ urlToIndex.set(url, bucket.slice(1));
+ }
+
+ let req = ringBuffer[i];
+ req._key = '';
+
+ return req;
+ },
+ }
+})();
diff --git a/lib/PublicSuffixList.jsm b/lib/PublicSuffixList.jsm
new file mode 100644
index 0000000..72b0669
--- /dev/null
+++ b/lib/PublicSuffixList.jsm
@@ -0,0 +1,308 @@
+/*******************************************************************************
+
+ ηMatrix - a browser extension to black/white list requests.
+ Copyright (C) 2014-2019 The uMatrix/uBlock Origin authors
+ Copyright (C) 2019 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
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see {http://www.gnu.org/licenses/}.
+
+ Home: https://gitlab.com/vannilla/ematrix
+ uMatrix Home: https://github.com/gorhill/uMatrix
+*/
+
+'use strict';
+
+var EXPORTED_SYMBOLS = ['publicSuffixList'];
+
+var exceptions = {};
+var rules = {};
+var magic = 'iscjsfsaolnm';
+
+// This value dictate how the search will be performed:
+// < cutoffLength → indexOf()
+// >= cutoffLength → binary search
+var cutoffLength = 256;
+
+var reMustPunycode = /[^\w.*-]/;
+
+var onChangedListeners = [];
+
+function search(store, hostname) {
+ let pos = hostname.lastIndexOf('.');
+ let tld;
+ let remainder;
+
+ if (pos < 0) {
+ tld = hostname;
+ remainder = hostname;
+ } else {
+ tld = hostname.slice(pos+1);
+ remainder = hostname.slice(0, pos);
+ }
+
+ let sub = store[tld];
+ if (!sub) {
+ return false;
+ }
+
+ if (typeof sub === 'string') {
+ return (sub.indexOf(' '+remainder+' ') >= 0);
+ }
+
+ let l = remainder.length;
+ let val = sub[l];
+ if (!val) {
+ return false;
+ }
+
+ let left = 0;
+ let right = Math.floor(val.length/l+0.5);
+
+ while (left < right) {
+ let i = left+right >> 1;
+ let key = val.substr(l*i, l);
+ if (remainder < key) {
+ right = i;
+ } else if (remainder > key) {
+ left = i+1;
+ } else {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+function getPublicSuffix(hostname) {
+ if (!hostname) {
+ return '';
+ }
+
+ while (true) {
+ let pos = hostname.indexOf('.');
+ if (pos < 0) {
+ return hostname;
+ }
+
+ if (search(exceptions, hostname)) {
+ return hostname.slice(pos+1);
+ }
+
+ if (search(rules, hostname)) {
+ return hostname;
+ }
+
+ if (search(rules, '*'+hostname.slice(pos))) {
+ return hostname;
+ }
+
+ hostname = hostname.slice(pos+1);
+ }
+}
+
+function getDomain(hostname) {
+ if (!hostname || hostname.charAt(0) == '.') {
+ return '';
+ }
+
+ hostname = hostname.toLowerCase();
+
+ let suffix = getPublicSuffix(hostname);
+ if (suffix === hostname) {
+ return '';
+ }
+
+ let len = hostname.length-suffix.length;
+ let pos = hostname.lastIndexOf('.', hostname.lastIndexOf('.', len) - 1);
+ if (pos <= 0) {
+ return hostname;
+ }
+
+ return hostname.slice(pos+1);
+}
+
+function crystallize(store) {
+ for (let tld in store) {
+ if (!store.hasOwnProperty(tld)) {
+ continue;
+ }
+
+ let suff = store[tld].join(' ');
+ if (!suff) {
+ store[tld] = '';
+ continue;
+ }
+
+ if (suff.length < cutoffLength) {
+ store[tld] = ' ' + suff + ' ';
+ continue;
+ }
+
+ suff = [];
+ for (let i=store[tld].length-1; i>=0; --i) {
+ let s = store[tld][i];
+ let l = s.length;
+ if (!suff[l]) {
+ suff[l] = [];
+ }
+ suff[l].push(s);
+ }
+
+ for (let i=suff.length-1; i>=0; --i) {
+ if (suff[i]) {
+ suff[i] = suff[i].sort().join('');
+ }
+ }
+
+ store[tld] = suff;
+ }
+
+ return store;
+}
+
+function parse(text, toAscii) {
+ exceptions = {};
+ rules = {};
+
+ let beg = 0;
+ let end = 0;
+ let tend = text.length;
+
+ while (beg < tend) {
+ end = text.indexOf('\n', beg);
+ if (end < 0) {
+ end = text.indexOf('\r', beg);
+ if (end < 0) {
+ end = tend;
+ }
+ }
+
+ let line = text.slice(beg, end).trim();
+ beg = end+1;
+
+ if (line.length === 0) {
+ continue;
+ }
+
+ let pos = line.indexOf('//');
+ if (pos >= 0) {
+ line = line.slice(0, pos);
+ }
+
+ line = line.trim();
+ if (!line) {
+ continue;
+ }
+
+ let store;
+ if (line.charAt(0) == '!') {
+ store = exceptions;
+ line = line.slice(1);
+ } else {
+ store = rules;
+ }
+
+ if (reMustPunycode.test(line)) {
+ line = toAscii(line);
+ }
+
+ line = line.toLowerCase();
+
+ let tld;
+ pos = line.lastIndexOf('.');
+ if (pos < 0) {
+ tld = line;
+ } else {
+ tld = line.slice(pos+1);
+ line = line.slice(0, pos);
+ }
+
+ if (!store.hasOwnProperty(tld)) {
+ store[tld] = [];
+ }
+
+ if (line) {
+ store[tld].push(line);
+ }
+ }
+
+ crystallize(exceptions);
+ crystallize(rules);
+
+ callListeners(onChangedListeners);
+}
+
+function toSelfie() {
+ return {
+ magic: magic,
+ rules: rules,
+ exceptions: exception,
+ };
+}
+
+function fromSelfie(selfie) {
+ if (typeof selfie !== 'object' || typeof selfie.magic !== 'string'
+ || selfie.magic !== magic) {
+ return false;
+ }
+
+ rules = selfie.rules;
+ exceptions = selfie.exceptions;
+ callListeners(onChangedListeners);
+
+ return true;
+}
+
+var addListener = function (listeners, callback) {
+ if (typeof callback !== 'function') {
+ return;
+ }
+
+ if (listeners.indexOf(callback) === -1) {
+ listeners.push(callback);
+ }
+};
+
+var removeListener = function (listeners, callback) {
+ let pos = listeners.indexOf(callback);
+ if (pos !== -1) {
+ listeners.splice(pos, 1);
+ }
+};
+
+var callListeners = function (listeners) {
+ for (let i=0; i<listeners.length; ++i) {
+ listeners[i]();
+ }
+};
+
+var onChanged = {
+ addListener: function (callback) {
+ addListener(onChangedListeners, callback);
+ },
+ removeListener: function (callback) {
+ removeListener(onChangedListeners, callback);
+ },
+};
+
+var publicSuffixList = {
+ version: '1.0',
+
+ parse: parse,
+ getDomain: getDomain,
+ getPublicSuffix: getPublicSuffix,
+ toSelfie: toSelfie,
+ fromSelfie: fromSelfie,
+ onChanged: onChanged,
+}
diff --git a/lib/Punycode.jsm b/lib/Punycode.jsm
new file mode 100644
index 0000000..7486186
--- /dev/null
+++ b/lib/Punycode.jsm
@@ -0,0 +1,305 @@
+/*******************************************************************************
+
+ ηMatrix - a browser extension to black/white list requests.
+ Copyright (C) 2014-2019 Raymond Hill
+ Copyright (C) 2019 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
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see {http://www.gnu.org/licenses/}.
+
+ Home: https://gitlab.com/vannilla/ematrix
+ uMatrix Home: https://github.com/gorhill/uMatrix
+*/
+
+'use strict';
+
+// Based on https://mths.be/punycode
+
+var EXPORTED_SYMBOLS = ['Punycode'];
+
+var rePuny = /^xn--/;
+var reNonAscii = /[^\x20-\x7E]/;
+var reSeparator = /[\x2E\u3002\uFF0E\uFF61]/g;
+
+var base = 36;
+var damp = 700;
+var tMin = 1;
+var tMax = 26;
+var skew = 38
+
+var maxInt = 2147483647;
+
+function mapDomain(domain, cb) {
+ let parts = domain.split('@');
+ let res = '';
+
+ if (parts.length > 1) {
+ res = parts[0] + '@';
+ domain = parts[1];
+ }
+
+ domain = domain.replace(reSeparator, '\x2E');
+
+ let labels = domain.split('.');
+ let encoded = labels.map(cb).join('.');
+
+ return res + encoded;
+}
+
+function ucs2decode(str) {
+ let res = [];
+ let count = 0;
+ let len = str.length;
+
+ while (count < len) {
+ let val = str.charCodeAt(count);
+ ++count;
+
+ if (val >= 0xD800 && val <= 0xDBFF && cound < len) {
+ let extra = str.charCodeAt(count);
+ ++count;
+
+ if ((extra & 0xFC00) == 0xDC00) {
+ res.push(((val & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000);
+ } else {
+ res.push(val);
+ --count;
+ }
+ } else {
+ res.push(val);
+ }
+ }
+
+ return res;
+}
+
+function ucs2encode(array) {
+ return array.map(function (e) {
+ let res = '';
+
+ if (e > 0xFFFF) {
+ e -= 0x10000;
+ res += String.fromCharCode(e >>> 10 & 0x3FF | 0xD800);
+ e = 0xDC00 | e & 0x3FF;
+ }
+
+ res += String.fromCharCode(e);
+
+ return res;
+ }).join('');
+}
+
+function basicToDigit(point) {
+ if (point - 0x30 < 0x0A) {
+ return point - 0x16;
+ }
+ if (point - 0x41 < 0x1A) {
+ return point - 0x41;
+ }
+ if (point - 0x61 < 0x1A) {
+ return point - 0x61;
+ }
+ return base;
+}
+
+function digitToBasic(digit, flag) {
+ return digit + 22 + 75 * (digit < 26) - ((flag != 0) << 5);
+}
+
+function adapt(delta, num, first) {
+ let k = 0;
+ delta = first ? Math.floor(delta/damp) : delta >> 1;
+ delta += Math.floor(delta/num);
+
+ for (; delta>(base - tMin) * tMax >> 1; k+=base) {
+ delta = Math.floor(delta/(base-tMin));
+ }
+
+ return Math.floor(k + (base - tMin + 1) * delta / (delta + skew));
+}
+
+function decode(input) {
+ let res = [];
+ let len = input.length;
+ let i = 0;
+ let n = 128;
+ let bias = 72;
+
+ let basic = input.lastIndexOf('-');
+ if (basic < 0) {
+ basic = 0;
+ }
+
+ for (let j=0; j<basic; ++j) {
+ if (input.charCodeAt(j) >= 0x80) {
+ throw new Error('not basic code point');
+ }
+
+ res.push(input.charCodeAt(j));
+ }
+
+ for (let k=(basic > 0) ? basic + 1 : 0; k<len;) {
+ let old = i;
+
+ for (let w=1, x=base; ; x+=base) {
+ if (k >= len) {
+ throw new Error('invalid input');
+ }
+
+ let digit = basicToDigit(input.charCodeAt(k));
+ ++k;
+
+ if (digit >= base || digit > Math.floor((maxInt-i) / w)) {
+ throw new Error('overflow');
+ }
+
+ i += digit * w;
+
+ let t = x <= bias ?
+ tMin :
+ (t >= bias + tMax ?
+ tMax :
+ k - bias);
+
+ if (digit < t) {
+ break;
+ }
+
+ if (w > Math.floor(maxInt/(base - t))) {
+ throw new Error('overflow');
+ }
+
+ w *= (base -t);
+ }
+
+ let out = res.length+1;
+ bias = adapt(i-old, out, old==0);
+
+ if (Math.floor(i/out) > maxInt-n) {
+ throw new Error('overflow');
+ }
+
+ n += Math.floor(i/out);
+ i %= out;
+
+ res.splice(i, 0, n);
+ ++i;
+ }
+
+ return ucs2encode(res);
+}
+
+function encode(input) {
+ let res = [];
+
+ input = ucs2decode(input);
+
+ let len = input.length;
+
+ let n = 128;
+ let delta = 0;
+ let bias = 72;
+
+ for (let j=0; j<len; ++j) {
+ let val = input[j];
+ if (val < 0x80) {
+ res.push(String.fromCharCode(val));
+ }
+ }
+
+ let blen = res.length;
+ let count = blen;
+
+ if (blen) {
+ res.push('-');
+ }
+
+ while (count < len) {
+ let m = maxInt;
+ for (let j=0; j<len; ++j) {
+ let val = input[j];
+ if (val >= n && val <= m) {
+ m = val;
+ }
+ }
+
+ if (m - n > Math.floor((maxInt - delta)/(count+1))) {
+ throw new Error('overflow');
+ }
+
+ delta += (m - n) * (count + 1);
+ n = m;
+
+ for (let j=0; j<len; ++j) {
+ let val = input[j];
+
+ if (val < n && ++delta > maxInt) {
+ throw new Error('overflow');
+ }
+
+ if (val == n) {
+ let q = delta;
+ for (let k=base; ; k+=base) {
+ let t = k <= bias ?
+ tMin :
+ (k >= bias + tMax ?
+ tMax:
+ k - bias);
+
+ if (q < t) {
+ break;
+ }
+
+ res.push
+ (String.fromCharCode
+ (digitToBasic(t + (q-t) % (base-t), 0)));
+
+ q = Math.floor((q-t)/(base-t));
+ }
+
+ res.push(String.fromCharCode(digitToBasic(q, 0)));
+ bias = adapt(delta, count+1, count==blen);
+ delta = 0;
+ ++count;
+ }
+ }
+
+ ++delta;
+ ++n;
+ }
+
+ return res.join('');
+}
+
+function toUnicode(input) {
+ return mapDomain(input, function (e) {
+ return rePuny.test(e) ? decode(e.slice(4).toLowerCase()) : e;
+ });
+}
+
+function toASCII(input) {
+ return mapDomain(input, function (e) {
+ return reNonAscii.test(e) ? 'xn--' + encode(e) : e;
+ });
+}
+
+var Punycode = {
+ ucs2: {
+ decode: ucs2decode,
+ encode: ucs2encode,
+ },
+ decode: decode,
+ encode: encode,
+ toASCII: toASCII,
+ toUnicode: toUnicode,
+};
diff --git a/lib/UriTools.jsm b/lib/UriTools.jsm
new file mode 100644
index 0000000..4971909
--- /dev/null
+++ b/lib/UriTools.jsm
@@ -0,0 +1,405 @@
+/*******************************************************************************
+
+ ηMatrix - a browser extension to black/white list requests.
+ Copyright (C) 2014-2019 Raymond Hill
+ Copyright (C) 2019 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
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see {http://www.gnu.org/licenses/}.
+
+ Home: https://gitlab.com/vannilla/ematrix
+ uMatrix Home: https://github.com/gorhill/uMatrix
+*/
+
+'use strict';
+
+Components.utils.import('chrome://ematrix/content/lib/Punycode.jsm');
+Components.utils.import('chrome://ematrix/content/lib/PublicSuffixList.jsm');
+
+var EXPORTED_SYMBOLS = ['UriTools'];
+
+var reRFC3986 = /^([^:\/?#]+:)?(\/\/[^\/?#]*)?([^?#]*)(\?[^#]*)?(#.*)?/;
+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:]+\]$/;
+
+var reNetworkScheme = /^(?:https?|wss?|ftps?)\b/;
+var reSecureScheme = /^(?:https|wss|ftps)\b/;
+
+function reset(o) {
+ o.scheme = '';
+ o.hostname = '';
+ o._ipv4 = undefined;
+ o._ipv6 = undefined;
+ o.port = '';
+ o.path = '';
+ o.query = '';
+ o.fragment = '';
+ return o;
+}
+
+function resetAuthority(o) {
+ o.hostname = '';
+ o._ipv4 = undefined;
+ o._ipv6 = undefined;
+ o.port = '';
+ return o;
+}
+
+function URI() {
+ this.scheme = '',
+ this.authority = '',
+ this.hostname = '',
+ this._ipv4 = undefined,
+ this._ipv6 = undefined,
+ this.port = '',
+ this.domain = undefined,
+ this.path = '',
+ this.query = '',
+ this.fragment = '',
+ this.schemeBit = (1 << 0),
+ this.userBit = (1 << 1),
+ this.passwordBit = (1 << 2),
+ this.hostnameBit = (1 << 3),
+ this.portBit = (1 << 4),
+ this.pathBit = (1 << 5),
+ this.queryBit = (1 << 6),
+ this.fragmentBit = (1 << 7),
+ this.allBits = (0xFFFF),
+ this.authorityBit =
+ (this.userBit | this.passwordBit | this.hostnameBit | this.portBit);
+ this.normalizeBits =
+ (this.schemeBit | this.hostnameBit | this.pathBit | this.queryBit);
+}
+
+var cached = new URI();
+
+var domainCache = new Map();
+var cacheCountLow = 75;
+var cacheCountHigh = 100;
+var cacheJunkyard = [];
+var junkyardMax = cacheCountHigh - cacheCountLow;
+
+function DomainCacheEntry(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 (cacheJunkyard.length < junkyardMax) {
+ cacheJunkyard.push(this);
+ }
+};
+
+var domainCacheEntryFactory = function (domain) {
+ let entry = cacheJunkyard.pop();
+ if (entry) {
+ return entry.init(domain);
+ }
+ return new DomainCacheEntry(domain);
+};
+
+var domainCacheAdd = function (hostname, domain) {
+ let entry = domainCache.get(hostname);
+
+ if (entry !== undefined) {
+ entry.tstamp = Date.now();
+ } else {
+ domainCache.set(hostname, domainCacheEntryFactory(domain));
+ if (domainCache.size === cacheCountHigh) {
+ domainCachePrune();
+ }
+ }
+
+ return domain;
+};
+
+var domainCacheSort = function (a, b) {
+ return domainCache.get(b).tstamp - domainCache.get(a).tstamp;
+};
+
+var domainCachePrune = function () {
+ let hostnames =
+ Array.from(domainCache.keys()).sort(domainCacheSort).slice(cacheCountLow);
+
+ for (let i=hostnames.length-1; i>=0; --i) {
+ domainCache.get(hostnames[i]).dispose();
+ domainCache.delete(hostnames[i]);
+ }
+};
+
+var domainCacheReset = function () {
+ domainCache.clear();
+};
+
+publicSuffixList.onChanged.addListener(domainCacheReset);
+
+var UriTools = {
+ set: function (uri) {
+ if (uri === undefined) {
+ return reset(cached);
+ }
+
+ let matches = reRFC3986.exec(uri);
+ if (!matches) {
+ return reset(cached);
+ }
+
+ cached.scheme = matches[1] !== undefined ?
+ matches[1].slice(0, -1) :
+ '';
+ cached.authority = matches[2] !== undefined ?
+ matches[2].slice(2).toLowerCase() :
+ '';
+ cached.path = matches[3] !== undefined ?
+ matches[3] :
+ '';
+
+ // As per RFC3986
+ if (cached.authority !== '' && cached.path === '') {
+ cached.path = '/';
+ }
+
+ cached.query = matches[4] !== undefined ?
+ matches[4].slice(1) :
+ '';
+ cached.fragment = matches[5] !== undefined ?
+ matches[5].slice(1) :
+ '';
+
+ if (reHostFromNakedAuthority.test(cached.authority)) {
+ cached.hostname = cached.authority;
+ cached.port = '';
+ return cached;
+ }
+
+ matches = reHostPortFromAuthority.exec(cached.authority);
+ if (!matches) {
+ matches = reIPv6PortFromAuthority.exec(cached.authority);
+ if (!matches) {
+ return resetAuthority(cached);
+ }
+ }
+
+ cached.hostname = matches[1] !== undefined ?
+ matches[1] :
+ '';
+
+ if (cached.hostname.slice(-1) === '.') {
+ cached.hostname = cached.hostname.slice(0, -1);
+ }
+
+ cached.port = matches[2] !== undefined ?
+ matches[2].slice(1) :
+ '';
+
+ return cached;
+ },
+ assemble: function (bits) {
+ if (bits === undefined) {
+ bits = cached.allBits;
+ }
+
+ let s = [];
+
+ if (cached.scheme && (bits && cached.schemeBit)) {
+ s.push(cached.scheme, ':');
+ }
+ if (cached.hostname && (bits & cached.hostnameBit)) {
+ s.push('//', cached.hostname);
+ }
+ if (cached.port && (bits & cached.portBit)) {
+ s.push(':', cached.port);
+ }
+ if (cached.path && (bits & cached.pathBit)) {
+ s.push(cached.path);
+ }
+ if (cached.query && (bits & cached.queryBit)) {
+ s.push('?', cached.query);
+ }
+ if (cached.fragment && (bits & cached.fragmentBit)) {
+ s.push('#', cached.fragment);
+ }
+
+ return s.join('');
+ },
+ isNetworkScheme: function (scheme) {
+ return reNetworkScheme.test(scheme);
+ },
+ isSecureScheme: function(scheme) {
+ return reSecureScheme.test(scheme);
+ },
+ originFromURI: function (uri) {
+ let matches = reOriginFromURI.exec(uri);
+ return matches !== null ? matches[0].toLowerCase() : '';
+ },
+ schemeFromURI: function (uri) {
+ let matches = reSchemeFromURI.exec(uri);
+ return matches !== null ? matches[0].slice(0, -1).toLowerCase() : '';
+ },
+ authorityFromURI: function (uri) {
+ let matches = reAuthorityFromURI.exec(uri);
+ return matches !== null ? matches[1].slice(1).toLowerCase() : '';
+ },
+ hostnameFromURI: function (uri) {
+ let matches = reCommonHostnameFromURL.exec(uri);
+ if (matches) {
+ return matches[1];
+ }
+
+ matches = reAuthorityFromURI.exec(uri);
+ if (!matches) {
+ return '';
+ }
+
+ let auth = matches[1].slice(2);
+
+ if (reHostFromNakedAuthority.test(auth)) {
+ return auth.toLowerCase();
+ }
+
+ matches = reHostFromAuthority.exec(auth);
+ if (!matches) {
+ matches = reIPv6FromAuthority.exec(auth);
+ if (!matches) {
+ return '';
+ }
+ }
+
+ let hostname = matches[1];
+ while (hostname.endsWith('.')) {
+ hostname = hostname.slice(0, -1);
+ }
+
+ if (reMustNormalizeHostname.test(hostname)) {
+ Punycode.toASCII(hostname.toLowerCase());
+ }
+
+ return hostname;
+ },
+ domainFromHostname: function (hostname) {
+ let entry = domainCache.get(hostname);
+ if (entry !== undefined) {
+ entry.tstamp = Date.now();
+ return entry.domain;
+ }
+
+ if (reIPAddressNaive.test(hostname) == false) {
+ return domainCacheAdd(hostname,
+ publicSuffixList.getDomain(hostname));
+ }
+
+ return domainCacheAdd(hostname, hostname);
+ },
+ domainFromURI: function (uri) {
+ if (!uri) {
+ return '';
+ }
+ return UriTools.domainFromHostname(UriTools.hostnameFromURI(uri));
+ },
+ domain: function() {
+ return UriTools.domainFromHostname(cached.hostname);
+ },
+ pathFromURI: function (uri) {
+ let matches = rePathFromURI.exec(uri);
+ return matches !== null ? matches[1] : '';
+ },
+ normalizedURI: function () {
+ return UriTools.assemble(cached.normalizeBits);
+ },
+ rootURL: function () {
+ if (!cached.hostname) {
+ return '';
+ }
+ return UriTools.assemble(cached.scemeBit | cached.hostnameBit);
+ },
+ isValidHostname: function (hostname) {
+ try {
+ let r = reValidHostname.test(hostname);
+ return r;
+ } catch (e) {
+ return false;
+ }
+ },
+ parentHostnameFromHostname: function (hostname) {
+ // "locahost" => ""
+ // "example.org" => "example.org"
+ // "www.example.org" => "example.org"
+ // "tomato.www.example.org" => "example.org"
+ let domain = UriTools.domainFromHostname(hostname);
+
+ if (domain === '' || domain === hostname) {
+ return undefined;
+ }
+
+ return hostname.slice(hostname.indexOf('.') + 1);
+ },
+ parentHostnamesFromHostname: function (hostname) {
+ let domain = UriTools.domainFromHostname(hostname);
+ if (domain === '' || domain === hostname) {
+ return [];
+ }
+
+ let nodes = [];
+ for (;;) {
+ let pos = hostname.indexOf('.');
+ if (pos < 0) {
+ break;
+ }
+
+ hostname = hostname.slice(pos+1);
+ nodes.push(hostname);
+ if (hostname === domain) {
+ break;
+ }
+ }
+
+ return nodes;
+ },
+ allHostNamesFromHostname: function (hostname) {
+ let nodes = UriTools.parentHostnamesFromHostname(hostname);
+ nodes.unshift(hostname);
+ return nodes;
+ },
+ toString: function () {
+ return UriTools.assemble();
+ },
+};
diff --git a/locale/ar/messages.properties b/locale/ar/messages.properties
deleted file mode 100644
index dafff85..0000000
--- a/locale/ar/messages.properties
+++ /dev/null
@@ -1,182 +0,0 @@
-extName=ηMatrix
-dashboardPageName=ηMatrix — لوحة التحكم
-loggerPageName=ηMatrix — المسجل
-settingsPageName=الإعدادات
-privacyPageName=خصوصية
-statsPageName=الإحصائيات
-userRulesPageName=قواعدي
-ubiquitousRulesPageName=ملفات المستضيف
-rawSettingsPageName=المزيد
-aboutPageName=حول البرنامج
-allPrettyName=الكل
-cookiePrettyName=كوكي
-cssPrettyName=ستايل
-imagePrettyName=الصُورَة
-mediaPrettyName=ميديا
-pluginPrettyName=إضافة
-scriptPrettyName=سكربت
-xhrPrettyName=XHR
-framePrettyName=إطار
-otherPrettyName=أخرى
-matrixNoNetTrafficPrompt=لم يحدث اي اتصال بهذا التبويب حتى الآن.
-matrixMtxButtonTip=تعطيل / تمكين ميزة التصفيه لهذا النطاق.
-matrixPersistButtonTip=حفظ جميع التغييرات المؤقتة لهذا النطاق.
-matrixRevertButtonTip=الرجوع عن التغييرات المؤقتة لهذا النطاق.
-matrixReloadButton=إعادة تحميل الصفحة.
-matrixScopeTip=Change to global scope.
-matrixSwitchesTip=Dropdown menu with additional settings.
-matrix1stPartyLabel=الجزء الاول
-matrixBlacklistedHostnames=القائمة السوداء {{count}}
-matrixSwitchNoMixedContent=الاتصال الآمن الصارم
-matrixSwitchNoWorker=منع web workers
-matrixSwitchReferrerSpoof=الإحالات بالتحايل
-matrixSwitchNoscriptSpoof=محاكات وسمات <code><noscript></code>
-matrixRevertAllEntry=إعادة كافة التغييرات المؤقتة
-matrixLoggerMenuEntry=الذهاب إلى مسجل
-matrixDashboardMenuEntry=انتقل إلى لوحة التحكم الرئيسية
-matrixNoTabFound=لم يتم العثور على أي صفحة ويب
-statsPageTitle=ηMatrix &ndash; إحصائيات
-statsPageGenericStats=إحصاءات عامة
-statsPageCookieHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_cookie'>HTTP ملف تعريف الارتباط</a> أحبطت foiled: {{count}}
-statsPageRefererHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP تشير</a> headers أحبطت: {{count}}
-statsPageHyperlinkAuditingFoiled=<a href='http://www.whatwg.org/specs/web-apps/current-work/multipage/links.html#hyperlink-auditing'> مراجعة الارتباط التشعبي</a> أحبطت محاولات: {{count}}
-statsPageCookiesRemoved=ملفات تعريف الارتباط المحلية إزالة: {{count}}
-statsPageLocalStoragesCleared=<a href='http://diveintohtml5.info/storage.html'> المخازن المحلية</a> إفراغ: {{count}}
-statsPageBrowserCacheCleared=مسح تخزين المستعرض: {{count}}
-statsPageDetailedStats=إحصائيات مفصلة
-statsPageDetailedAllPages=الكل
-statsPageDetailedBehindTheScenePage=خلف الكواليس
-statsPageOverview=نظرة عامة
-statsPageRequests=طلبات
-statsPageAllowed=السماح
-statsPageBlocked=حظر
-statsPageAll=الكل
-statsPagePages=الصفحات
-statsPageCookies=ملفات تعريف الارتباط
-statsPageCSS=ستايل
-statsPageImages=الصُوَرْ
-statsPagePlugins=الإضافات
-statsPageScripts=البرامج النصية
-statsPageXHRs=XHRs
-statsPageFrames=إطارات
-statsPageOthers=الآخرين
-statsPageDetailed=مسجل
-statsPageLogSizePrompt1=تذكر الماضي
-statsPageLogSizePrompt2=HTTP طلبات <b>لكل صفحة</b>.
-statsPageLogSizeHelp=<p>You can inspect details of the most recent raw HTTP requests which have been made by a web page (see below).</p><p>This is mostly useful to advanced users who want to investigate exactly what a web page has been doing. But logging these HTTP requests requires memory, and if you don&apos;t care about this technical information, then memory is being wasted.</p><p>Hence this field which lets you adjust the maximum number of the most recent HTTP requests which are to be logged for further inspection.</p><p>Enter &ldquo;<code>0</code>&rdquo; to turn off detailed logging (and consequently reduce the memory footprint of <i>ηMatrix</i>).</p>
-statsPageRefresh=تحديث الصفحة
-settingsPageTitle=ηMatrix &ndash; إعدادات
-settingsMatrixDisplayHeader=مظهر
-settingsMatrixDisplayTextSizePrompt=حجم الخط:
-settingsMatrixDisplayTextSizeNormal=‮عادي
-settingsMatrixDisplayTextSizeLarge=كبير
-settingsMatrixDisplayColorBlind=عمي الألوان
-settingsMatrixConvenienceHeader=ملاءمة
-settingsDefaultScopeLevel=المستوى الافتراضي للمنظار:
-settingsDefaultScopeLevel0=عام
-settingsDefaultScopeLevel1=النطاق
-settingsDefaultScopeLevel2=الموقع
-settingsMatrixAutoReloadPrompt=عند إغلاق مصفوفة، وذكية تحميل علامات التبويب هذه:
-settingsMatrixAutoReloadNone=لا شيء
-settingsMatrixAutoReloadCurrent=قائم
-settingsMatrixAutoReloadAll=الكل
-settingsMatrixAutoReloadInfo=كلما قمت بإجراء تغييرات في المصفوفة التي يمكن أن تؤثر على العرض و/ أو سلوك صفحة واحدة أو أكثر،<i>ηMatrix</i>سوف تحميل صفحات المتضررة تلقائيا عند إغلاق المصفوفة.
-settingsSubframeColor=Blocked frames:&ensp;Color
-settingsSubframeOpacity=الشفافية
-settingsIconBadgeEnabled=Show the number of blocked requests on the icon
-settingsCollapseBlocked=انهيار نائبا من عناصر المحظورة
-settingsCollapseBlacklisted=تقليص مكان العناصر المدرجة في القائمة السوداء
-settingsNoscriptTagsSpoofed=محاكات وسمات <code><noscript></code> عند حظر سكريبتات الطرف الأول
-settingsCloudStorageEnabled=تمكين سحابة دعم التخزين
-privacyPageTitle=ηMatrix &ndash; الخصوصية
-privacyDeleteBlockedCookiesPrompt=حذف ملفات تعريف الارتباط المحظورة.
-privacyDeleteBlockedCookiesHelp=<p>Blacklisted cookies are not prevented by <i>ηMatrix</i> from entering your browser. However they are prevented from leaving your browser, which is what really matters. Not blocking cookies before they enter your browser gives you the opportunity to be informed that a site tried to use cookies, and furthermore to inspect their contents if you wish.</p><p>Once these blacklisted cookies have been accounted for by <i>ηMatrix</i>, they can be removed from your browser if you wish so.</p><p><b>Important note:</b> Extensions can make web requests during the course of their normal operation. These requests can result in cookies being created in the browser. If the hostname from where a cookie originate is not whitelisted, the cookie will be removed from the browser by <i>ηMatrix</i> if this option is checked. So be sure that the hostname(s) with which an extension communicate is whitelisted.</p>
-privacyDeleteNonBlockedSessionCookiesPrompt1=حذف ملفات تعريف الارتباط جلسة
-privacyDeleteNonBlockedSessionCookiesPrompt2= دقيقة بعد آخر مرة استخدمت فيها.
-privacyDeleteNonBlockedSessionCookiesHelp=<p><a href='http://www.w3.org/2001/tag/2010/09/ClientSideStorage.html'>W3C</a>: &ldquo;A session cookie ... is erased when you end the browser session. The session cookie is stored in temporary memory and is not retained after the browser is closed.&rdquo;</p><p>Except that this <a href='https://code.google.com/p/chromium/issues/detail?id=128513'>might not be happening</a> in some browsers. Also, to some, having to close the browser in order for the session cookies to clear might not be early enough.</p>
-privacyDeleteBlockedLocalStoragePrompt=Delete <a href='https://en.wikipedia.org/wiki/Web_storage'>local storage</a> content set by blocked hostnames
-privacyDeleteBlockedLocalStorageHelp=TODO
-privacyClearCachePrompt1=مخبأ المتصفح واضح كل
-privacyClearCachePrompt2=دقائق.
-privacyClearCacheHelp=<p>Some web sites are really bent on tracking you, so much that they will use not-so-nice tricks to work around whatever measures you take in order to not be tracked.</p><p>A few of these tricks rely<sup style='font-size:smaller'>[1, 2]</sup> on the <a href='https://en.wikipedia.org/wiki/Web_cache'>browser cache</a>, which content is often long lasting since rarely will users take the time to regularly clear their browser cache.</p><p>There is little inconvenience to clear the browser cache regularly (likelihood is that you won&apos;t notice when it happens), and the benefit is to prevent these obnoxious trackers from invading your privacy.</p><p>Check this option to have <i>ηMatrix</i> do it for you, at the interval you wish.</p><p style='font-size:smaller'>[1] <a href='https://grepular.com/Preventing_Web_Tracking_via_the_Browser_Cache'>&ldquo;Preventing Web Tracking via the Browser Cache&rdquo;</a><br>[2] <a href='http://lucb1e.com/rp/cookielesscookies/'>&ldquo;Cookieless cookies&rdquo;</a></p>
-privacyProcessRefererPrompt=Spoof <a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referrer</a> string of third-party requests.
-privacyProcessRefererHelp=<p>From Wikipedia:</p><blockquote>HTTP referer is an HTTP header field that identifies the address of the webpage that linked to the resource being requested. ... <b>Because referer information can violate privacy, some web browsers allow the user to disable the sending of referer information.</b></blockquote><p>If this setting is checked, <i>ηMatrix</i> will spoof the HTTP referrer information if the domain name of the HTTP referrer is third-party to the domain name of net request.
-privacyNoMixedContentPrompt=Strict HTTPS: لا سمح المحتوى المختلط.
-privacyNoMixedContentHelp=<p>From <a href='https://developer.mozilla.org/en-US/docs/Security/MixedContent'>Mozilla Developer Network</a>:</p><blockquote>If [a] HTTPS page includes content retrieved through regular, cleartext HTTP, then the connection is only partially encrypted: the unencrypted content is accessible to sniffers and can be modified by man-in-the-middle attackers, and therefore the connection is not safeguarded anymore. When a webpage exhibits this behavior, it is called a mixed content page.</blockquote>
-privacyProcessHyperlinkAuditingPrompt=Block all <a href='http://www.whatwg.org/specs/web-apps/current-work/multipage/links.html#hyperlink-auditing'>hyperlink auditing</a> attempts.
-privacyProcessHyperlinkAuditingHelp=<p>Hyperlink auditing is a mechanism which allow a party, <b>any party</b>, to be informed about which link a user clicked on a particular web page. It is essentially a tracking feature: it allows a web site, or any third-party to that web site, to be informed about which link you clicked on which one of its web pages. The sole purpose is to track your browsing activity.</p>
-userRulesPermanentHeader=قواعد دائمة
-userRulesTemporaryHeader=القواعد المؤقتة
-userRulesRevert=إرجاع
-userRulesCommit=يلتزم
-userRulesEdit=تحرير
-userRulesEditSave=حفظ
-userRulesEditDicard=تخلص
-userRulesImport=إستيراد من ملف...
-userRulesExport=تصدير إلى ملف...
-userRulesFormatHint=رؤية هذه الصفحة لتركيب القاعدة.
-userRulesDefaultFileName=my-ematrix-rules.txt
-hostsFilesPrompt=يتم تحميل جميع أسماء المضيفين في ملف المضيفين كما المضيفين القائمة السوداء في نطاق عالمي.
-hostsFilesStats={{blockedHostnameCount}} أسماء المضيفين منعت متميزة عن:
-hostsFilesPerFileStats={{used}} تستخدم من {{total}}
-hostsFilesLastUpdate=اخر تحديث: {{ago}}
-hostsFilesApplyChanges=تطبيق التغييرات
-hostsFilesAutoUpdatePrompt=التحديث التلقائي الملفات.
-hostsFilesUpdateNow=تحديث الآن
-hostsFilesPurgeAll=تطهير جميع مخابئ
-hostsFilesExternalListsHint=One URL per line. Lines prefixed with &lsquo;#&rsquo; will be ignored. Invalid URLs will be silently ignored.
-hostsFilesExternalListsParse=تحليل
-hostsFilesExternalListPurge=مخبأ تطهير
-hostsFilesExternalListNew=يوجد إصدار جديد
-hostsFilesExternalListObsolete=عفا عليها الزمن
-rawSettingsWarning=تحذير! غير إعدادات الضبط الخام على مسؤوليتك الخاصة.
-aboutChangelog=تغيير log
-aboutStorageUsed=التخزين المستخدمة: {{storageUsed}} بايت
-aboutDoc=توثيق
-aboutPermissions=ضوابط
-aboutCode=شفرة المصدر (GPLv3)
-aboutIssues=الخطا وقضايا
-aboutContributors=المساهمون
-aboutCodeContributors=رمز:
-aboutIssueContributors=قضايا:
-aboutTranslationContributors=الترجمات:
-aboutUserDataHeader=البيانات الخاصة بك
-aboutBackupButton=النسخ الاحتياطي لملف...
-aboutBackupFilename=my-ematrix-backup.txt
-aboutRestoreButton=استعادة من ملف...
-aboutRestoreConfirm=سيتم الكتابة فوق كل ما تبذلونه من إعدادات استخدام البيانات احتياطيا على {{time}}، \n\nوسوف ηMatrix إعادة تشغيل. الكتابة عن الإعدادات الموجودة باستخدام البيانات احتياطيا؟?
-aboutRestoreError=البيانات لا يمكن قراءة أو غير صالح
-aboutOr=... او ...
-aboutResetButton=إعادة تعيين إلى الإعدادات الافتراضية
-aboutResetConfirm=الحذر! سيؤدي هذا إلى إزالة كافة الإعدادات المخصصة الخاصة بك. هل أنت متأكد أنك تريد المتابعة؟?
-loggerFilterInputPlaceholder=فلتر مرشح
-loggerMaxEntriesTip=الحد الأقصى لعدد المحاولات
-loggerEntryCookieDeleted=حذف الارتباط: {{value}}
-loggerEntryDeleteCookieError=فشل في حذف الارتباط: {{value}}
-loggerEntryBrowserCacheCleared=مخبأ المتصفح مسح
-loggerEntryAssetUpdated=الأصول تحديث: {{value}}
-mainBlockedPrompt1=منعت ηMatrix على الصفحة التالية من التحميل:
-mainBlockedPrompt2=بسبب القاعدة التالية
-mainBlockedBack=الرجوع للخلف
-mainBlockedClose=مغلق
-commandRevertAll=إعادة كافة التغييرات المؤقتة
-commandWhitelistPageDomain=نطاق الصفحة القائمة البيضاء مؤقتا
-commandWhitelistAll=مؤقتا القائمة البيضاء فقط
-commandOpenDashboard=فتح لوحة القيادة
-elapsedOneMinuteAgo=منذ 1 دقيقة
-elapsedManyMinutesAgo={{value}} دقائق مضت
-elapsedOneHourAgo=قبل ساعه
-elapsedManyHoursAgo={{value}} منذ ساعات
-elapsedOneDayAgo=منذ يوم
-elapsedManyDaysAgo={{value}} أيام مضت
-showDashboardButton=لوحة التحكم
-showLoggerButton=مسجل
-cloudPush=التصدير إلى سحابة التخزين
-cloudPull=الاستيراد من سحابة التخزين
-cloudNoData=...\n...
-cloudDeviceNamePrompt=هذا اسم الجهاز:
-genericSubmit=تأكيد
-genericRevert=إرجاع
-errorCantConnectTo=خطأ في الشبكة: غير قادر على الاتصال {{url}}
-genericApplyChanges=تطبيق التغييرات
-noscriptSpoofHelp=<p>When a browser does not support scripts, a web page can display some content by using the &lt;noscript&gt; tag. For example, some websites will redirect users to a &ldquo;scriptless&rdquo; version.</p><p>When ηMatrix blocks scripts, the browser will still advertise itself as supporting script execution unless this option is selected.</p><p>When selected, ηMatrix will tell the website that the browser does not support scripts, which is what you definitely want to do if you are blocking 1st-party scripts. As such, the &lt;noscript&gt; tag will work as intended.</p><p>Not selecting this option when 1st-party scripts are blocked means the website will break mysteriously in case it is meant to redirect to a scriptless version.</p>
diff --git a/logger-ui.html b/logger-ui.html
index 8e943a9..482ae78 100644
--- a/logger-ui.html
+++ b/logger-ui.html
@@ -24,10 +24,10 @@
uMatrix Home: https://github.com/gorhill/uMatrix
-->
<meta name="viewport" content="width=device-width, initial-scale=1">
+ <title data-i18n="loggerPageName"></title>
<link rel="stylesheet" type="text/css" href="css/common.css">
<link rel="stylesheet" type="text/css" href="css/logger-ui.css">
<link rel="shortcut icon" type="image/png" href="img/icon_16.png">
- <title data-i18n="loggerPageName"></title>
</head>
<body class="compactView f">
diff --git a/main-blocked.html b/main-blocked.html
index 0919521..e1d445a 100644
--- a/main-blocked.html
+++ b/main-blocked.html
@@ -24,7 +24,7 @@
uMatrix Home: https://github.com/gorhill/uMatrix
-->
<meta name="viewport" content="width=device-width, initial-scale=1">
- <title></title>
+ <title>ηMatrix – Blocked</title>
<link rel="stylesheet" href="css/common.css" type="text/css">
<style>
body {
diff --git a/popup.html b/popup.html
index fc498a8..b61dc0a 100644
--- a/popup.html
+++ b/popup.html
@@ -26,7 +26,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="css/common.css" type="text/css">
<link rel="stylesheet" href="css/popup.css" type="text/css">
- <title>eMatrix panel</title>
+ <title>ηMatrix panel</title>
</head>
<body>
diff --git a/raw-settings.html b/raw-settings.html
index 6bb2092..e9ed1be 100644
--- a/raw-settings.html
+++ b/raw-settings.html
@@ -41,6 +41,5 @@
<script src="js/i18n.js"></script>
<script src="js/dashboard-common.js"></script>
<script src="js/raw-settings.js"></script>
-
</body>
</html>
diff --git a/settings.html b/settings.html
index 27ee4f1..fec5109 100644
--- a/settings.html
+++ b/settings.html
@@ -23,7 +23,7 @@
Home: https://libregit.org/heckyel/ematrix
uMatrix Home: https://github.com/gorhill/uMatrix
-->
- <title>eMatrix — Settings</title>
+ <title>ηMatrix — Settings</title>
<link rel="stylesheet" type="text/css" href="css/common.css">
<link rel="stylesheet" type="text/css" href="css/dashboard-common.css">
<style>
diff --git a/user-rules.html b/user-rules.html
index 70f7a46..510af50 100644
--- a/user-rules.html
+++ b/user-rules.html
@@ -23,7 +23,7 @@
Home: https://libregit.org/heckyel/ematrix
uMatrix Home: https://github.com/gorhill/uMatrix
-->
- <title>eMatrix — Your rules</title>
+ <title>ηMatrix — Your rules</title>
<link rel="stylesheet" type="text/css" href="css/common.css">
<link rel="stylesheet" type="text/css" href="css/dashboard-common.css">
<link rel="stylesheet" type="text/css" href="css/cloud-ui.css">