aboutsummaryrefslogtreecommitdiffstats
path: root/js/tab.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/tab.js')
-rw-r--r--js/tab.js1062
1 files changed, 531 insertions, 531 deletions
diff --git a/js/tab.js b/js/tab.js
index d6998b1..008ea30 100644
--- a/js/tab.js
+++ b/js/tab.js
@@ -26,71 +26,71 @@
(function() {
-'use strict';
+ 'use strict';
-/******************************************************************************/
+ /******************************************************************************/
-var ηm = ηMatrix;
+ var ηm = ηMatrix;
-// https://github.com/gorhill/httpswitchboard/issues/303
-// Some kind of trick going on here:
-// Any scheme other than 'http' and 'https' is remapped into a fake
-// URL which trick the rest of ηMatrix into being able to process an
-// otherwise unmanageable scheme. ηMatrix needs web page to have a proper
-// hostname to work properly, so just like the 'behind-the-scene'
-// fake domain name, we map unknown schemes into a fake '{scheme}-scheme'
-// hostname. This way, for a specific scheme you can create scope with
-// rules which will apply only to that scheme.
+ // https://github.com/gorhill/httpswitchboard/issues/303
+ // Some kind of trick going on here:
+ // Any scheme other than 'http' and 'https' is remapped into a fake
+ // URL which trick the rest of ηMatrix into being able to process an
+ // otherwise unmanageable scheme. ηMatrix needs web page to have a proper
+ // hostname to work properly, so just like the 'behind-the-scene'
+ // fake domain name, we map unknown schemes into a fake '{scheme}-scheme'
+ // hostname. This way, for a specific scheme you can create scope with
+ // rules which will apply only to that scheme.
-/******************************************************************************/
-/******************************************************************************/
+ /******************************************************************************/
+ /******************************************************************************/
-ηm.normalizePageURL = function(tabId, pageURL) {
- if ( vAPI.isBehindTheSceneTabId(tabId) ) {
- return 'http://' + this.behindTheSceneScope + '/';
- }
-
- // https://github.com/gorhill/uMatrix/issues/992
- if (pageURL.startsWith('wyciwyg:')) {
- // Matches strings like 'wyciwyg://101/'
- let filter = /^wyciwyg:\/\/\d+\//.exec(pageURL);
- if (filter) {
- pageURL = pageURL.slice(filter[0].length);
- }
- }
-
- // If the URL is that of our "blocked page" document, return the URL of
- // the blocked page.
- if ( pageURL.lastIndexOf(vAPI.getURL('main-blocked.html'), 0) === 0 ) {
- var matches = /main-blocked\.html\?details=([^&]+)/.exec(pageURL);
- if ( matches && matches.length === 2 ) {
- try {
- var details = JSON.parse(atob(matches[1]));
- pageURL = details.url;
- } catch (e) {
+ ηm.normalizePageURL = function(tabId, pageURL) {
+ if ( vAPI.isBehindTheSceneTabId(tabId) ) {
+ return 'http://' + this.behindTheSceneScope + '/';
+ }
+
+ // https://github.com/gorhill/uMatrix/issues/992
+ if (pageURL.startsWith('wyciwyg:')) {
+ // Matches strings like 'wyciwyg://101/'
+ let filter = /^wyciwyg:\/\/\d+\//.exec(pageURL);
+ if (filter) {
+ pageURL = pageURL.slice(filter[0].length);
+ }
+ }
+
+ // If the URL is that of our "blocked page" document, return the URL of
+ // the blocked page.
+ if ( pageURL.lastIndexOf(vAPI.getURL('main-blocked.html'), 0) === 0 ) {
+ var matches = /main-blocked\.html\?details=([^&]+)/.exec(pageURL);
+ if ( matches && matches.length === 2 ) {
+ try {
+ var details = JSON.parse(atob(matches[1]));
+ pageURL = details.url;
+ } catch (e) {
+ }
}
}
- }
- var uri = this.URI.set(pageURL);
- var scheme = uri.scheme;
- if ( scheme === 'https' || scheme === 'http' ) {
- return uri.normalizedURI();
- }
+ var uri = this.URI.set(pageURL);
+ var scheme = uri.scheme;
+ if ( scheme === 'https' || scheme === 'http' ) {
+ return uri.normalizedURI();
+ }
- var fakeHostname = scheme + '-scheme';
+ var fakeHostname = scheme + '-scheme';
- if ( uri.hostname !== '' ) {
- fakeHostname = uri.hostname + '.' + fakeHostname;
- } else if ( scheme === 'about' ) {
- fakeHostname = uri.path + '.' + fakeHostname;
- }
+ if ( uri.hostname !== '' ) {
+ fakeHostname = uri.hostname + '.' + fakeHostname;
+ } else if ( scheme === 'about' ) {
+ fakeHostname = uri.path + '.' + fakeHostname;
+ }
- return 'http://' + fakeHostname + '/';
-};
+ return 'http://' + fakeHostname + '/';
+ };
-/******************************************************************************/
-/******************************************************************************
+ /******************************************************************************/
+ /******************************************************************************
To keep track from which context *exactly* network requests are made. This is
often tricky for various reasons, and the challenge is not specific to one
@@ -142,579 +142,579 @@ master switch and dynamic filtering rules can be evaluated now properly even
in the absence of a PageStore object, this was not the case before.
Also, the TabContext object will try its best to find a good candidate root
-document URL for when none exists. This takes care of
+document URL for when none exists. This takes care of
<https://github.com/chrisaljoudi/uBlock/issues/1001>.
The TabContext manager is self-contained, and it takes care to properly
housekeep itself.
-*/
+ */
-ηm.tabContextManager = (function() {
- var tabContexts = Object.create(null);
+ ηm.tabContextManager = (function() {
+ var tabContexts = Object.create(null);
- // https://github.com/chrisaljoudi/uBlock/issues/1001
- // This is to be used as last-resort fallback in case a tab is found to not
- // be bound while network requests are fired for the tab.
- var mostRecentRootDocURL = '';
- var mostRecentRootDocURLTimestamp = 0;
+ // https://github.com/chrisaljoudi/uBlock/issues/1001
+ // This is to be used as last-resort fallback in case a tab is found to not
+ // be bound while network requests are fired for the tab.
+ var mostRecentRootDocURL = '';
+ var mostRecentRootDocURLTimestamp = 0;
+
+ var gcPeriod = 31 * 60 * 1000; // every 31 minutes
+
+ // A pushed entry is removed from the stack unless it is committed with
+ // a set time.
+ var StackEntry = function(url, commit) {
+ this.url = url;
+ this.committed = commit;
+ this.tstamp = Date.now();
+ };
- var gcPeriod = 31 * 60 * 1000; // every 31 minutes
+ var TabContext = function(tabId) {
+ this.tabId = tabId;
+ this.stack = [];
+ this.rawURL =
+ this.normalURL =
+ this.scheme =
+ this.rootHostname =
+ this.rootDomain = '';
+ this.secure = false;
+ this.commitTimer = null;
+ this.gcTimer = null;
- // A pushed entry is removed from the stack unless it is committed with
- // a set time.
- var StackEntry = function(url, commit) {
- this.url = url;
- this.committed = commit;
- this.tstamp = Date.now();
- };
+ tabContexts[tabId] = this;
+ };
- var TabContext = function(tabId) {
- this.tabId = tabId;
- this.stack = [];
- this.rawURL =
- this.normalURL =
- this.scheme =
- this.rootHostname =
- this.rootDomain = '';
- this.secure = false;
- this.commitTimer = null;
- this.gcTimer = null;
-
- tabContexts[tabId] = this;
- };
+ TabContext.prototype.destroy = function() {
+ if ( vAPI.isBehindTheSceneTabId(this.tabId) ) {
+ return;
+ }
+ if ( this.gcTimer !== null ) {
+ clearTimeout(this.gcTimer);
+ this.gcTimer = null;
+ }
+ delete tabContexts[this.tabId];
+ };
- TabContext.prototype.destroy = function() {
- if ( vAPI.isBehindTheSceneTabId(this.tabId) ) {
- return;
- }
- if ( this.gcTimer !== null ) {
- clearTimeout(this.gcTimer);
+ TabContext.prototype.onTab = function(tab) {
+ if ( tab ) {
+ this.gcTimer = vAPI.setTimeout(this.onGC.bind(this), gcPeriod);
+ } else {
+ this.destroy();
+ }
+ };
+
+ TabContext.prototype.onGC = function() {
this.gcTimer = null;
- }
- delete tabContexts[this.tabId];
- };
+ if ( vAPI.isBehindTheSceneTabId(this.tabId) ) {
+ return;
+ }
+ vAPI.tabs.get(this.tabId, this.onTab.bind(this));
+ };
+
+ // https://github.com/gorhill/uBlock/issues/248
+ // Stack entries have to be committed to stick. Non-committed stack
+ // entries are removed after a set delay.
+ TabContext.prototype.onCommit = function() {
+ if ( vAPI.isBehindTheSceneTabId(this.tabId) ) {
+ return;
+ }
+ this.commitTimer = null;
+ // Remove uncommitted entries at the top of the stack.
+ var i = this.stack.length;
+ while ( i-- ) {
+ if ( this.stack[i].committed ) {
+ break;
+ }
+ }
+ // https://github.com/gorhill/uBlock/issues/300
+ // If no committed entry was found, fall back on the bottom-most one
+ // as being the committed one by default.
+ if ( i === -1 && this.stack.length !== 0 ) {
+ this.stack[0].committed = true;
+ i = 0;
+ }
+ i += 1;
+ if ( i < this.stack.length ) {
+ this.stack.length = i;
+ this.update();
+ ηm.bindTabToPageStats(this.tabId, 'newURL');
+ }
+ };
- TabContext.prototype.onTab = function(tab) {
- if ( tab ) {
+ // This takes care of orphanized tab contexts. Can't be started for all
+ // contexts, as the behind-the-scene context is permanent -- so we do not
+ // want to flush it.
+ TabContext.prototype.autodestroy = function() {
+ if ( vAPI.isBehindTheSceneTabId(this.tabId) ) {
+ return;
+ }
this.gcTimer = vAPI.setTimeout(this.onGC.bind(this), gcPeriod);
- } else {
- this.destroy();
- }
- };
+ };
- TabContext.prototype.onGC = function() {
- this.gcTimer = null;
- if ( vAPI.isBehindTheSceneTabId(this.tabId) ) {
- return;
- }
- vAPI.tabs.get(this.tabId, this.onTab.bind(this));
- };
+ // Update just force all properties to be updated to match the most recent
+ // root URL.
+ TabContext.prototype.update = function() {
+ if ( this.stack.length === 0 ) {
+ this.rawURL = this.normalURL = this.scheme =
+ this.rootHostname = this.rootDomain = '';
+ this.secure = false;
+ return;
+ }
+ this.rawURL = this.stack[this.stack.length - 1].url;
+ this.normalURL = ηm.normalizePageURL(this.tabId, this.rawURL);
+ this.scheme = ηm.URI.schemeFromURI(this.rawURL);
+ this.rootHostname = ηm.URI.hostnameFromURI(this.normalURL);
+ this.rootDomain = ηm.URI.domainFromHostname(this.rootHostname) || this.rootHostname;
+ this.secure = ηm.URI.isSecureScheme(this.scheme);
+ };
- // https://github.com/gorhill/uBlock/issues/248
- // Stack entries have to be committed to stick. Non-committed stack
- // entries are removed after a set delay.
- TabContext.prototype.onCommit = function() {
- if ( vAPI.isBehindTheSceneTabId(this.tabId) ) {
- return;
- }
- this.commitTimer = null;
- // Remove uncommitted entries at the top of the stack.
- var i = this.stack.length;
- while ( i-- ) {
- if ( this.stack[i].committed ) {
- break;
+ // Called whenever a candidate root URL is spotted for the tab.
+ TabContext.prototype.push = function(url, context) {
+ if ( vAPI.isBehindTheSceneTabId(this.tabId) ) { return; }
+ var committed = context !== undefined;
+ var count = this.stack.length;
+ var topEntry = this.stack[count - 1];
+ if ( topEntry && topEntry.url === url ) {
+ if ( committed ) {
+ topEntry.committed = true;
+ }
+ return;
+ }
+ if ( this.commitTimer !== null ) {
+ clearTimeout(this.commitTimer);
+ }
+ if ( committed ) {
+ this.stack = [new StackEntry(url, true)];
+ } else {
+ this.stack.push(new StackEntry(url));
+ this.commitTimer = vAPI.setTimeout(this.onCommit.bind(this), 1000);
}
- }
- // https://github.com/gorhill/uBlock/issues/300
- // If no committed entry was found, fall back on the bottom-most one
- // as being the committed one by default.
- if ( i === -1 && this.stack.length !== 0 ) {
- this.stack[0].committed = true;
- i = 0;
- }
- i += 1;
- if ( i < this.stack.length ) {
- this.stack.length = i;
this.update();
- ηm.bindTabToPageStats(this.tabId, 'newURL');
- }
- };
+ ηm.bindTabToPageStats(this.tabId, context);
+ };
- // This takes care of orphanized tab contexts. Can't be started for all
- // contexts, as the behind-the-scene context is permanent -- so we do not
- // want to flush it.
- TabContext.prototype.autodestroy = function() {
- if ( vAPI.isBehindTheSceneTabId(this.tabId) ) {
- return;
- }
- this.gcTimer = vAPI.setTimeout(this.onGC.bind(this), gcPeriod);
- };
+ // These are to be used for the API of the tab context manager.
- // Update just force all properties to be updated to match the most recent
- // root URL.
- TabContext.prototype.update = function() {
- if ( this.stack.length === 0 ) {
- this.rawURL = this.normalURL = this.scheme =
- this.rootHostname = this.rootDomain = '';
- this.secure = false;
- return;
- }
- this.rawURL = this.stack[this.stack.length - 1].url;
- this.normalURL = ηm.normalizePageURL(this.tabId, this.rawURL);
- this.scheme = ηm.URI.schemeFromURI(this.rawURL);
- this.rootHostname = ηm.URI.hostnameFromURI(this.normalURL);
- this.rootDomain = ηm.URI.domainFromHostname(this.rootHostname) || this.rootHostname;
- this.secure = ηm.URI.isSecureScheme(this.scheme);
- };
+ var push = function(tabId, url, context) {
+ var entry = tabContexts[tabId];
+ if ( entry === undefined ) {
+ entry = new TabContext(tabId);
+ entry.autodestroy();
+ }
+ entry.push(url, context);
+ mostRecentRootDocURL = url;
+ mostRecentRootDocURLTimestamp = Date.now();
+ return entry;
+ };
- // Called whenever a candidate root URL is spotted for the tab.
- TabContext.prototype.push = function(url, context) {
- if ( vAPI.isBehindTheSceneTabId(this.tabId) ) { return; }
- var committed = context !== undefined;
- var count = this.stack.length;
- var topEntry = this.stack[count - 1];
- if ( topEntry && topEntry.url === url ) {
- if ( committed ) {
- topEntry.committed = true;
+ // Find a tab context for a specific tab. If none is found, attempt to
+ // fix this. When all fail, the behind-the-scene context is returned.
+ var mustLookup = function(tabId, url) {
+ var entry;
+ if ( url !== undefined ) {
+ entry = push(tabId, url);
+ } else {
+ entry = tabContexts[tabId];
}
- return;
- }
- if ( this.commitTimer !== null ) {
- clearTimeout(this.commitTimer);
- }
- if ( committed ) {
- this.stack = [new StackEntry(url, true)];
- } else {
- this.stack.push(new StackEntry(url));
- this.commitTimer = vAPI.setTimeout(this.onCommit.bind(this), 1000);
- }
- this.update();
- ηm.bindTabToPageStats(this.tabId, context);
- };
+ if ( entry !== undefined ) {
+ return entry;
+ }
+ // https://github.com/chrisaljoudi/uBlock/issues/1025
+ // Google Hangout popup opens without a root frame. So for now we will
+ // just discard that best-guess root frame if it is too far in the
+ // future, at which point it ceases to be a "best guess".
+ if ( mostRecentRootDocURL !== '' && mostRecentRootDocURLTimestamp + 500 < Date.now() ) {
+ mostRecentRootDocURL = '';
+ }
+ // https://github.com/chrisaljoudi/uBlock/issues/1001
+ // Not a behind-the-scene request, yet no page store found for the
+ // tab id: we will thus bind the last-seen root document to the
+ // unbound tab. It's a guess, but better than ending up filtering
+ // nothing at all.
+ if ( mostRecentRootDocURL !== '' ) {
+ return push(tabId, mostRecentRootDocURL);
+ }
+ // If all else fail at finding a page store, re-categorize the
+ // request as behind-the-scene. At least this ensures that ultimately
+ // the user can still inspect/filter those net requests which were
+ // about to fall through the cracks.
+ // Example: Chromium + case #12 at
+ // http://raymondhill.net/ublock/popup.html
+ return tabContexts[vAPI.noTabId];
+ };
- // These are to be used for the API of the tab context manager.
+ var lookup = function(tabId) {
+ return tabContexts[tabId] || null;
+ };
- var push = function(tabId, url, context) {
- var entry = tabContexts[tabId];
- if ( entry === undefined ) {
- entry = new TabContext(tabId);
- entry.autodestroy();
- }
- entry.push(url, context);
- mostRecentRootDocURL = url;
- mostRecentRootDocURLTimestamp = Date.now();
- return entry;
- };
+ // Behind-the-scene tab context
+ (function() {
+ var entry = new TabContext(vAPI.noTabId);
+ entry.stack.push(new StackEntry('', true));
+ entry.rawURL = '';
+ entry.normalURL = ηm.normalizePageURL(entry.tabId);
+ entry.rootHostname = ηm.URI.hostnameFromURI(entry.normalURL);
+ entry.rootDomain = ηm.URI.domainFromHostname(entry.rootHostname) || entry.rootHostname;
+ })();
+
+ // https://github.com/gorhill/uMatrix/issues/513
+ // Force a badge update here, it could happen that all the subsequent
+ // network requests are already in the page store, which would cause
+ // the badge to no be updated for these network requests.
+
+ vAPI.tabs.onNavigation = function(details) {
+ var tabId = details.tabId;
+ if ( vAPI.isBehindTheSceneTabId(tabId) ) { return; }
+ push(tabId, details.url, 'newURL');
+ ηm.updateBadgeAsync(tabId);
+ };
- // Find a tab context for a specific tab. If none is found, attempt to
- // fix this. When all fail, the behind-the-scene context is returned.
- var mustLookup = function(tabId, url) {
- var entry;
- if ( url !== undefined ) {
- entry = push(tabId, url);
- } else {
- entry = tabContexts[tabId];
- }
- if ( entry !== undefined ) {
- return entry;
- }
- // https://github.com/chrisaljoudi/uBlock/issues/1025
- // Google Hangout popup opens without a root frame. So for now we will
- // just discard that best-guess root frame if it is too far in the
- // future, at which point it ceases to be a "best guess".
- if ( mostRecentRootDocURL !== '' && mostRecentRootDocURLTimestamp + 500 < Date.now() ) {
- mostRecentRootDocURL = '';
- }
- // https://github.com/chrisaljoudi/uBlock/issues/1001
- // Not a behind-the-scene request, yet no page store found for the
- // tab id: we will thus bind the last-seen root document to the
- // unbound tab. It's a guess, but better than ending up filtering
- // nothing at all.
- if ( mostRecentRootDocURL !== '' ) {
- return push(tabId, mostRecentRootDocURL);
- }
- // If all else fail at finding a page store, re-categorize the
- // request as behind-the-scene. At least this ensures that ultimately
- // the user can still inspect/filter those net requests which were
- // about to fall through the cracks.
- // Example: Chromium + case #12 at
- // http://raymondhill.net/ublock/popup.html
- return tabContexts[vAPI.noTabId];
- };
+ // https://github.com/gorhill/uMatrix/issues/872
+ // `changeInfo.url` may not always be available (Firefox).
- var lookup = function(tabId) {
- return tabContexts[tabId] || null;
- };
+ vAPI.tabs.onUpdated = function(tabId, changeInfo, tab) {
+ if ( vAPI.isBehindTheSceneTabId(tabId) ) { return; }
+ if ( typeof tab.url !== 'string' || tab.url === '' ) { return; }
+ var url = changeInfo.url || tab.url;
+ if ( url ) {
+ push(tabId, url, 'updateURL');
+ }
+ };
- // Behind-the-scene tab context
- (function() {
- var entry = new TabContext(vAPI.noTabId);
- entry.stack.push(new StackEntry('', true));
- entry.rawURL = '';
- entry.normalURL = ηm.normalizePageURL(entry.tabId);
- entry.rootHostname = ηm.URI.hostnameFromURI(entry.normalURL);
- entry.rootDomain = ηm.URI.domainFromHostname(entry.rootHostname) || entry.rootHostname;
+ vAPI.tabs.onClosed = function(tabId) {
+ ηm.unbindTabFromPageStats(tabId);
+ var entry = tabContexts[tabId];
+ if ( entry instanceof TabContext ) {
+ entry.destroy();
+ }
+ };
+
+ return {
+ push: push,
+ lookup: lookup,
+ mustLookup: mustLookup
+ };
})();
- // https://github.com/gorhill/uMatrix/issues/513
- // Force a badge update here, it could happen that all the subsequent
- // network requests are already in the page store, which would cause
- // the badge to no be updated for these network requests.
+ vAPI.tabs.registerListeners();
- vAPI.tabs.onNavigation = function(details) {
- var tabId = details.tabId;
- if ( vAPI.isBehindTheSceneTabId(tabId) ) { return; }
- push(tabId, details.url, 'newURL');
- ηm.updateBadgeAsync(tabId);
- };
+ /******************************************************************************/
+ /******************************************************************************/
- // https://github.com/gorhill/uMatrix/issues/872
- // `changeInfo.url` may not always be available (Firefox).
+ // Create an entry for the tab if it doesn't exist
- vAPI.tabs.onUpdated = function(tabId, changeInfo, tab) {
- if ( vAPI.isBehindTheSceneTabId(tabId) ) { return; }
- if ( typeof tab.url !== 'string' || tab.url === '' ) { return; }
- var url = changeInfo.url || tab.url;
- if ( url ) {
- push(tabId, url, 'updateURL');
+ ηm.bindTabToPageStats = function(tabId, context) {
+ this.updateBadgeAsync(tabId);
+
+ // Do not create a page store for URLs which are of no interests
+ // Example: dev console
+ var tabContext = this.tabContextManager.lookup(tabId);
+ if ( tabContext === null ) {
+ throw new Error('Unmanaged tab id: ' + tabId);
}
- };
- vAPI.tabs.onClosed = function(tabId) {
- ηm.unbindTabFromPageStats(tabId);
- var entry = tabContexts[tabId];
- if ( entry instanceof TabContext ) {
- entry.destroy();
+ // rhill 2013-11-24: Never ever rebind behind-the-scene
+ // virtual tab.
+ // https://github.com/gorhill/httpswitchboard/issues/67
+ if ( vAPI.isBehindTheSceneTabId(tabId) ) {
+ return this.pageStores[tabId];
}
- };
- return {
- push: push,
- lookup: lookup,
- mustLookup: mustLookup
- };
-})();
+ var normalURL = tabContext.normalURL;
+ var pageStore = this.pageStores[tabId] || null;
-vAPI.tabs.registerListeners();
+ // The previous page URL, if any, associated with the tab
+ if ( pageStore !== null ) {
+ // No change, do not rebind
+ if ( pageStore.pageUrl === normalURL ) {
+ return pageStore;
+ }
-/******************************************************************************/
-/******************************************************************************/
+ // https://github.com/gorhill/uMatrix/issues/37
+ // Just rebind whenever possible: the URL changed, but the document
+ // maybe is the same.
+ // Example: Google Maps, Github
+ // https://github.com/gorhill/uMatrix/issues/72
+ // Need to double-check that the new scope is same as old scope
+ if ( context === 'updateURL' && pageStore.pageHostname === tabContext.rootHostname ) {
+ pageStore.rawURL = tabContext.rawURL;
+ pageStore.normalURL = normalURL;
+ this.updateTitle(tabId);
+ this.pageStoresToken = Date.now();
+ return pageStore;
+ }
-// Create an entry for the tab if it doesn't exist
-
-ηm.bindTabToPageStats = function(tabId, context) {
- this.updateBadgeAsync(tabId);
-
- // Do not create a page store for URLs which are of no interests
- // Example: dev console
- var tabContext = this.tabContextManager.lookup(tabId);
- if ( tabContext === null ) {
- throw new Error('Unmanaged tab id: ' + tabId);
- }
-
- // rhill 2013-11-24: Never ever rebind behind-the-scene
- // virtual tab.
- // https://github.com/gorhill/httpswitchboard/issues/67
- if ( vAPI.isBehindTheSceneTabId(tabId) ) {
- return this.pageStores[tabId];
- }
-
- var normalURL = tabContext.normalURL;
- var pageStore = this.pageStores[tabId] || null;
-
- // The previous page URL, if any, associated with the tab
- if ( pageStore !== null ) {
- // No change, do not rebind
- if ( pageStore.pageUrl === normalURL ) {
- return pageStore;
+ // We won't be reusing this page store.
+ this.unbindTabFromPageStats(tabId);
}
- // https://github.com/gorhill/uMatrix/issues/37
- // Just rebind whenever possible: the URL changed, but the document
- // maybe is the same.
- // Example: Google Maps, Github
- // https://github.com/gorhill/uMatrix/issues/72
- // Need to double-check that the new scope is same as old scope
- if ( context === 'updateURL' && pageStore.pageHostname === tabContext.rootHostname ) {
- pageStore.rawURL = tabContext.rawURL;
- pageStore.normalURL = normalURL;
- this.updateTitle(tabId);
- this.pageStoresToken = Date.now();
- return pageStore;
+ // Try to resurrect first.
+ pageStore = this.resurrectPageStore(tabId, normalURL);
+ if ( pageStore === null ) {
+ pageStore = this.pageStoreFactory(tabContext);
}
+ this.pageStores[tabId] = pageStore;
+ this.updateTitle(tabId);
+ this.pageStoresToken = Date.now();
- // We won't be reusing this page store.
- this.unbindTabFromPageStats(tabId);
- }
+ // console.debug('tab.js > bindTabToPageStats(): dispatching traffic in tab id %d to page store "%s"', tabId, pageUrl);
- // Try to resurrect first.
- pageStore = this.resurrectPageStore(tabId, normalURL);
- if ( pageStore === null ) {
- pageStore = this.pageStoreFactory(tabContext);
- }
- this.pageStores[tabId] = pageStore;
- this.updateTitle(tabId);
- this.pageStoresToken = Date.now();
+ return pageStore;
+ };
- // console.debug('tab.js > bindTabToPageStats(): dispatching traffic in tab id %d to page store "%s"', tabId, pageUrl);
+ /******************************************************************************/
- return pageStore;
-};
+ ηm.unbindTabFromPageStats = function(tabId) {
+ // Never unbind behind-the-scene page store.
+ if ( vAPI.isBehindTheSceneTabId(tabId) ) {
+ return;
+ }
-/******************************************************************************/
+ var pageStore = this.pageStores[tabId] || null;
+ if ( pageStore === null ) {
+ return;
+ }
-ηm.unbindTabFromPageStats = function(tabId) {
- // Never unbind behind-the-scene page store.
- if ( vAPI.isBehindTheSceneTabId(tabId) ) {
- return;
- }
+ delete this.pageStores[tabId];
+ this.pageStoresToken = Date.now();
- var pageStore = this.pageStores[tabId] || null;
- if ( pageStore === null ) {
- return;
- }
+ if ( pageStore.incinerationTimer ) {
+ clearTimeout(pageStore.incinerationTimer);
+ pageStore.incinerationTimer = null;
+ }
- delete this.pageStores[tabId];
- this.pageStoresToken = Date.now();
+ if ( this.pageStoreCemetery.hasOwnProperty(tabId) === false ) {
+ this.pageStoreCemetery[tabId] = {};
+ }
+ var pageStoreCrypt = this.pageStoreCemetery[tabId];
- if ( pageStore.incinerationTimer ) {
- clearTimeout(pageStore.incinerationTimer);
- pageStore.incinerationTimer = null;
- }
+ var pageURL = pageStore.pageUrl;
+ pageStoreCrypt[pageURL] = pageStore;
- if ( this.pageStoreCemetery.hasOwnProperty(tabId) === false ) {
- this.pageStoreCemetery[tabId] = {};
- }
- var pageStoreCrypt = this.pageStoreCemetery[tabId];
+ pageStore.incinerationTimer = vAPI.setTimeout(
+ this.incineratePageStore.bind(this, tabId, pageURL),
+ 4 * 60 * 1000
+ );
+ };
- var pageURL = pageStore.pageUrl;
- pageStoreCrypt[pageURL] = pageStore;
+ /******************************************************************************/
- pageStore.incinerationTimer = vAPI.setTimeout(
- this.incineratePageStore.bind(this, tabId, pageURL),
- 4 * 60 * 1000
- );
-};
+ ηm.resurrectPageStore = function(tabId, pageURL) {
+ if ( this.pageStoreCemetery.hasOwnProperty(tabId) === false ) {
+ return null;
+ }
+ var pageStoreCrypt = this.pageStoreCemetery[tabId];
-/******************************************************************************/
+ if ( pageStoreCrypt.hasOwnProperty(pageURL) === false ) {
+ return null;
+ }
-ηm.resurrectPageStore = function(tabId, pageURL) {
- if ( this.pageStoreCemetery.hasOwnProperty(tabId) === false ) {
- return null;
- }
- var pageStoreCrypt = this.pageStoreCemetery[tabId];
+ var pageStore = pageStoreCrypt[pageURL];
- if ( pageStoreCrypt.hasOwnProperty(pageURL) === false ) {
- return null;
- }
+ if ( pageStore.incinerationTimer !== null ) {
+ clearTimeout(pageStore.incinerationTimer);
+ pageStore.incinerationTimer = null;
+ }
- var pageStore = pageStoreCrypt[pageURL];
+ delete pageStoreCrypt[pageURL];
+ if ( Object.keys(pageStoreCrypt).length === 0 ) {
+ delete this.pageStoreCemetery[tabId];
+ }
- if ( pageStore.incinerationTimer !== null ) {
- clearTimeout(pageStore.incinerationTimer);
- pageStore.incinerationTimer = null;
- }
+ return pageStore;
+ };
- delete pageStoreCrypt[pageURL];
- if ( Object.keys(pageStoreCrypt).length === 0 ) {
- delete this.pageStoreCemetery[tabId];
- }
+ /******************************************************************************/
- return pageStore;
-};
+ ηm.incineratePageStore = function(tabId, pageURL) {
+ if ( this.pageStoreCemetery.hasOwnProperty(tabId) === false ) {
+ return;
+ }
+ var pageStoreCrypt = this.pageStoreCemetery[tabId];
-/******************************************************************************/
+ if ( pageStoreCrypt.hasOwnProperty(pageURL) === false ) {
+ return;
+ }
-ηm.incineratePageStore = function(tabId, pageURL) {
- if ( this.pageStoreCemetery.hasOwnProperty(tabId) === false ) {
- return;
- }
- var pageStoreCrypt = this.pageStoreCemetery[tabId];
+ var pageStore = pageStoreCrypt[pageURL];
+ if ( pageStore.incinerationTimer !== null ) {
+ clearTimeout(pageStore.incinerationTimer);
+ pageStore.incinerationTimer = null;
+ }
- if ( pageStoreCrypt.hasOwnProperty(pageURL) === false ) {
- return;
- }
+ delete pageStoreCrypt[pageURL];
+ if ( Object.keys(pageStoreCrypt).length === 0 ) {
+ delete this.pageStoreCemetery[tabId];
+ }
- var pageStore = pageStoreCrypt[pageURL];
- if ( pageStore.incinerationTimer !== null ) {
- clearTimeout(pageStore.incinerationTimer);
- pageStore.incinerationTimer = null;
- }
+ pageStore.dispose();
+ };
- delete pageStoreCrypt[pageURL];
- if ( Object.keys(pageStoreCrypt).length === 0 ) {
- delete this.pageStoreCemetery[tabId];
- }
+ /******************************************************************************/
- pageStore.dispose();
-};
+ ηm.pageStoreFromTabId = function(tabId) {
+ return this.pageStores[tabId] || null;
+ };
-/******************************************************************************/
+ // Never return null
+ ηm.mustPageStoreFromTabId = function(tabId) {
+ return this.pageStores[tabId] || this.pageStores[vAPI.noTabId];
+ };
-ηm.pageStoreFromTabId = function(tabId) {
- return this.pageStores[tabId] || null;
-};
+ /******************************************************************************/
-// Never return null
-ηm.mustPageStoreFromTabId = function(tabId) {
- return this.pageStores[tabId] || this.pageStores[vAPI.noTabId];
-};
+ ηm.forceReload = function(tabId, bypassCache) {
+ vAPI.tabs.reload(tabId, bypassCache);
+ };
-/******************************************************************************/
+ /******************************************************************************/
-ηm.forceReload = function(tabId, bypassCache) {
- vAPI.tabs.reload(tabId, bypassCache);
-};
+ // Update badge
-/******************************************************************************/
+ // rhill 2013-11-09: well this sucks, I can't update icon/badge
+ // incrementally, as chromium overwrite the icon at some point without
+ // notifying me, and this causes internal cached state to be out of sync.
-// Update badge
+ ηm.updateBadgeAsync = (function() {
+ var tabIdToTimer = Object.create(null);
-// rhill 2013-11-09: well this sucks, I can't update icon/badge
-// incrementally, as chromium overwrite the icon at some point without
-// notifying me, and this causes internal cached state to be out of sync.
+ var updateBadge = function(tabId) {
+ delete tabIdToTimer[tabId];
-ηm.updateBadgeAsync = (function() {
- var tabIdToTimer = Object.create(null);
+ var iconId = null;
+ var badgeStr = '';
- var updateBadge = function(tabId) {
- delete tabIdToTimer[tabId];
+ var pageStore = this.pageStoreFromTabId(tabId);
+ if ( pageStore !== null ) {
+ var total = pageStore.perLoadAllowedRequestCount +
+ pageStore.perLoadBlockedRequestCount;
+ if ( total ) {
+ var squareSize = 19;
+ var greenSize = squareSize * Math.sqrt(pageStore.perLoadAllowedRequestCount / total);
+ iconId = greenSize < squareSize/2 ? Math.ceil(greenSize) : Math.floor(greenSize);
+ }
+ if ( this.userSettings.iconBadgeEnabled && pageStore.perLoadBlockedRequestCount !== 0) {
+ badgeStr = this.formatCount(pageStore.perLoadBlockedRequestCount);
+ }
+ }
- var iconId = null;
- var badgeStr = '';
+ vAPI.setIcon(tabId, iconId, badgeStr);
+ };
- var pageStore = this.pageStoreFromTabId(tabId);
- if ( pageStore !== null ) {
- var total = pageStore.perLoadAllowedRequestCount +
- pageStore.perLoadBlockedRequestCount;
- if ( total ) {
- var squareSize = 19;
- var greenSize = squareSize * Math.sqrt(pageStore.perLoadAllowedRequestCount / total);
- iconId = greenSize < squareSize/2 ? Math.ceil(greenSize) : Math.floor(greenSize);
+ return function(tabId) {
+ if ( tabIdToTimer[tabId] ) {
+ return;
}
- if ( this.userSettings.iconBadgeEnabled && pageStore.perLoadBlockedRequestCount !== 0) {
- badgeStr = this.formatCount(pageStore.perLoadBlockedRequestCount);
+ if ( vAPI.isBehindTheSceneTabId(tabId) ) {
+ return;
}
- }
-
- vAPI.setIcon(tabId, iconId, badgeStr);
- };
-
- return function(tabId) {
- if ( tabIdToTimer[tabId] ) {
- return;
- }
- if ( vAPI.isBehindTheSceneTabId(tabId) ) {
- return;
- }
- tabIdToTimer[tabId] = vAPI.setTimeout(updateBadge.bind(this, tabId), 750);
- };
-})();
-
-/******************************************************************************/
+ tabIdToTimer[tabId] = vAPI.setTimeout(updateBadge.bind(this, tabId), 750);
+ };
+ })();
-ηm.updateTitle = (function() {
- var tabIdToTimer = Object.create(null);
- var tabIdToTryCount = Object.create(null);
- var delay = 499;
+ /******************************************************************************/
- var tryNoMore = function(tabId) {
- delete tabIdToTryCount[tabId];
- };
+ ηm.updateTitle = (function() {
+ var tabIdToTimer = Object.create(null);
+ var tabIdToTryCount = Object.create(null);
+ var delay = 499;
- var tryAgain = function(tabId) {
- var count = tabIdToTryCount[tabId];
- if ( count === undefined ) {
- return false;
- }
- if ( count === 1 ) {
+ var tryNoMore = function(tabId) {
delete tabIdToTryCount[tabId];
- return false;
- }
- tabIdToTryCount[tabId] = count - 1;
- tabIdToTimer[tabId] = vAPI.setTimeout(updateTitle.bind(ηm, tabId), delay);
- return true;
- };
+ };
- var onTabReady = function(tabId, tab) {
- if ( !tab ) {
- return tryNoMore(tabId);
- }
- var pageStore = this.pageStoreFromTabId(tabId);
- if ( pageStore === null ) {
- return tryNoMore(tabId);
- }
- if ( !tab.title && tryAgain(tabId) ) {
- return;
- }
- // https://github.com/gorhill/uMatrix/issues/225
- // Sometimes title changes while page is loading.
- var settled = tab.title && tab.title === pageStore.title;
- pageStore.title = tab.title || tab.url || '';
- this.pageStoresToken = Date.now();
- if ( settled || !tryAgain(tabId) ) {
- tryNoMore(tabId);
- }
- };
+ var tryAgain = function(tabId) {
+ var count = tabIdToTryCount[tabId];
+ if ( count === undefined ) {
+ return false;
+ }
+ if ( count === 1 ) {
+ delete tabIdToTryCount[tabId];
+ return false;
+ }
+ tabIdToTryCount[tabId] = count - 1;
+ tabIdToTimer[tabId] = vAPI.setTimeout(updateTitle.bind(ηm, tabId), delay);
+ return true;
+ };
- var updateTitle = function(tabId) {
- delete tabIdToTimer[tabId];
- vAPI.tabs.get(tabId, onTabReady.bind(this, tabId));
- };
+ var onTabReady = function(tabId, tab) {
+ if ( !tab ) {
+ return tryNoMore(tabId);
+ }
+ var pageStore = this.pageStoreFromTabId(tabId);
+ if ( pageStore === null ) {
+ return tryNoMore(tabId);
+ }
+ if ( !tab.title && tryAgain(tabId) ) {
+ return;
+ }
+ // https://github.com/gorhill/uMatrix/issues/225
+ // Sometimes title changes while page is loading.
+ var settled = tab.title && tab.title === pageStore.title;
+ pageStore.title = tab.title || tab.url || '';
+ this.pageStoresToken = Date.now();
+ if ( settled || !tryAgain(tabId) ) {
+ tryNoMore(tabId);
+ }
+ };
- return function(tabId) {
- if ( vAPI.isBehindTheSceneTabId(tabId) ) {
- return;
- }
- if ( tabIdToTimer[tabId] ) {
- clearTimeout(tabIdToTimer[tabId]);
- }
- tabIdToTimer[tabId] = vAPI.setTimeout(updateTitle.bind(this, tabId), delay);
- tabIdToTryCount[tabId] = 5;
- };
-})();
+ var updateTitle = function(tabId) {
+ delete tabIdToTimer[tabId];
+ vAPI.tabs.get(tabId, onTabReady.bind(this, tabId));
+ };
-/******************************************************************************/
+ return function(tabId) {
+ if ( vAPI.isBehindTheSceneTabId(tabId) ) {
+ return;
+ }
+ if ( tabIdToTimer[tabId] ) {
+ clearTimeout(tabIdToTimer[tabId]);
+ }
+ tabIdToTimer[tabId] = vAPI.setTimeout(updateTitle.bind(this, tabId), delay);
+ tabIdToTryCount[tabId] = 5;
+ };
+ })();
-// Stale page store entries janitor
-// https://github.com/chrisaljoudi/uBlock/issues/455
+ /******************************************************************************/
-(function() {
- var cleanupPeriod = 7 * 60 * 1000;
- var cleanupSampleAt = 0;
- var cleanupSampleSize = 11;
-
- var cleanup = function() {
- var vapiTabs = vAPI.tabs;
- var tabIds = Object.keys(ηm.pageStores).sort();
- var checkTab = function(tabId) {
- vapiTabs.get(tabId, function(tab) {
- if ( !tab ) {
- ηm.unbindTabFromPageStats(tabId);
+ // Stale page store entries janitor
+ // https://github.com/chrisaljoudi/uBlock/issues/455
+
+ (function() {
+ var cleanupPeriod = 7 * 60 * 1000;
+ var cleanupSampleAt = 0;
+ var cleanupSampleSize = 11;
+
+ var cleanup = function() {
+ var vapiTabs = vAPI.tabs;
+ var tabIds = Object.keys(ηm.pageStores).sort();
+ var checkTab = function(tabId) {
+ vapiTabs.get(tabId, function(tab) {
+ if ( !tab ) {
+ ηm.unbindTabFromPageStats(tabId);
+ }
+ });
+ };
+ if ( cleanupSampleAt >= tabIds.length ) {
+ cleanupSampleAt = 0;
+ }
+ var tabId;
+ var n = Math.min(cleanupSampleAt + cleanupSampleSize, tabIds.length);
+ for ( var i = cleanupSampleAt; i < n; i++ ) {
+ tabId = tabIds[i];
+ if ( vAPI.isBehindTheSceneTabId(tabId) ) {
+ continue;
}
- });
- };
- if ( cleanupSampleAt >= tabIds.length ) {
- cleanupSampleAt = 0;
- }
- var tabId;
- var n = Math.min(cleanupSampleAt + cleanupSampleSize, tabIds.length);
- for ( var i = cleanupSampleAt; i < n; i++ ) {
- tabId = tabIds[i];
- if ( vAPI.isBehindTheSceneTabId(tabId) ) {
- continue;
+ checkTab(tabId);
}
- checkTab(tabId);
- }
- cleanupSampleAt = n;
+ cleanupSampleAt = n;
- vAPI.setTimeout(cleanup, cleanupPeriod);
- };
+ vAPI.setTimeout(cleanup, cleanupPeriod);
+ };
- vAPI.setTimeout(cleanup, cleanupPeriod);
-})();
+ vAPI.setTimeout(cleanup, cleanupPeriod);
+ })();
-/******************************************************************************/
+ /******************************************************************************/
})();