diff options
author | Jesús <heckyel@hyperbola.info> | 2019-12-30 15:55:13 -0500 |
---|---|---|
committer | Jesús <heckyel@hyperbola.info> | 2019-12-30 15:55:13 -0500 |
commit | 288df6a7bf8b933e2dc499e38f4915fcf974c14b (patch) | |
tree | 77bba994f260c064d3ee7f76c427ddfaa4f91710 /js/tab.js | |
parent | a2c9deaa145b780722e93b3899600f287c8094a4 (diff) | |
download | ematrix-288df6a7bf8b933e2dc499e38f4915fcf974c14b.tar.lz ematrix-288df6a7bf8b933e2dc499e38f4915fcf974c14b.tar.xz ematrix-288df6a7bf8b933e2dc499e38f4915fcf974c14b.zip |
backport
- Flush caches on upgrade
- Properly handle FrameModule's unloading
- Use the new module and remove the old implementation
Diffstat (limited to 'js/tab.js')
-rw-r--r-- | js/tab.js | 658 |
1 files changed, 346 insertions, 312 deletions
@@ -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); })(); - - /******************************************************************************/ - })(); |