diff options
Diffstat (limited to 'js/tab.js')
-rw-r--r-- | js/tab.js | 1062 |
1 files changed, 531 insertions, 531 deletions
@@ -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); + })(); -/******************************************************************************/ + /******************************************************************************/ })(); |