diff options
Diffstat (limited to 'js')
-rw-r--r-- | js/asset-viewer.js | 21 | ||||
-rw-r--r-- | js/background.js | 94 | ||||
-rw-r--r-- | js/browsercache.js | 28 | ||||
-rw-r--r-- | js/cloud-ui.js | 140 | ||||
-rw-r--r-- | js/contentscript-start.js | 72 | ||||
-rw-r--r-- | js/contentscript.js | 517 | ||||
-rw-r--r-- | js/cookies.js | 464 | ||||
-rw-r--r-- | js/dashboard-common.js | 16 | ||||
-rw-r--r-- | js/dashboard.js | 24 | ||||
-rw-r--r-- | js/hosts-files.js | 407 | ||||
-rw-r--r-- | js/httpsb.js | 8 | ||||
-rw-r--r-- | js/i18n.js | 92 | ||||
-rw-r--r-- | js/logger-ui.js | 706 | ||||
-rw-r--r-- | js/logger.js | 40 | ||||
-rw-r--r-- | js/matrix.js | 16 | ||||
-rw-r--r-- | js/messaging.js | 17 | ||||
-rw-r--r-- | js/pagestats.js | 6 | ||||
-rw-r--r-- | js/popup.js | 1134 | ||||
-rw-r--r-- | js/settings.js | 139 | ||||
-rw-r--r-- | js/start.js | 64 | ||||
-rw-r--r-- | js/storage.js | 8 | ||||
-rw-r--r-- | js/tab.js | 658 | ||||
-rw-r--r-- | js/traffic.js | 10 | ||||
-rw-r--r-- | js/user-rules.js | 280 | ||||
-rw-r--r-- | js/vapi-background.js | 112 | ||||
-rw-r--r-- | js/vapi-core.js | 9 | ||||
-rw-r--r-- | js/vapi-messaging.js | 2 | ||||
-rw-r--r-- | js/vapi-net.js | 5 |
28 files changed, 2425 insertions, 2664 deletions
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; } @@ -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(/“/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(); }; @@ -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); }; |