diff --git a/assets/checksums.txt b/assets/checksums.txt index 84dff7353..919afb8c9 100644 --- a/assets/checksums.txt +++ b/assets/checksums.txt @@ -1,6 +1,6 @@ 6572a7bce5f1747fee597cb170bd3b98 assets/ublock/privacy.txt 8ebba513a75ed3f7ed964688c2c6071c assets/ublock/filters.txt -4d9e52dd0f2fa7b4ff3c80043096e542 assets/ublock/mirror-candidates.txt +dcf3e05bae803343c9d632f0baf8bedd assets/ublock/mirror-candidates.txt b3ec069f792cec05484e6c45b3591466 assets/ublock/filter-lists.json 132b3ecc9da8a68c3faf740c00af734b assets/thirdparties/adblock-plus-japanese-filter.googlecode.com/hg/abp_jp.txt 94c0a3eab74c42783855f07b22a429cf assets/thirdparties/home.fredfiber.no/langsholt/adblock.txt diff --git a/assets/ublock/mirror-candidates.txt b/assets/ublock/mirror-candidates.txt index 674831478..184ca17f9 100644 --- a/assets/ublock/mirror-candidates.txt +++ b/assets/ublock/mirror-candidates.txt @@ -19,7 +19,7 @@ code.jquery.com # within a data: URI ^code\.jquery\.com\/(?!.*\/themes\/.+\.css) maxcdn.bootstrapcdn.com - ^maxcdn\.bootstrapcdn\.com\/font-awesome\/ + ^maxcdn\.bootstrapcdn\.com\/bootstrap\/.+\/.+\.js netdna.bootstrapcdn.com ^netdna\.bootstrapcdn\.com\/bootstrap\/ ^netdna\.bootstrapcdn\.com\/font-awesome\/ diff --git a/platform/chromium/vapi-background.js b/platform/chromium/vapi-background.js index 66223a843..6d4ff0217 100644 --- a/platform/chromium/vapi-background.js +++ b/platform/chromium/vapi-background.js @@ -86,18 +86,22 @@ vAPI.tabs.get = function(tabId, callback) { var onTabReady = function(tab) { // https://code.google.com/p/chromium/issues/detail?id=410868#c8 if ( chrome.runtime.lastError ) { - return; + ; } + // Caller must be prepared to deal with nil tab value callback(tab); }; if ( tabId !== null ) { + if ( typeof tabId === 'string' ) { + tabId = parseInt(tabId, 10); + } chrome.tabs.get(tabId, onTabReady); return; } var onTabReceived = function(tabs) { // https://code.google.com/p/chromium/issues/detail?id=410868#c8 if ( chrome.runtime.lastError ) { - return; + ; } callback(tabs[0]); }; diff --git a/platform/chromium/vapi-client.js b/platform/chromium/vapi-client.js index 47edde9a9..e4099a20f 100644 --- a/platform/chromium/vapi-client.js +++ b/platform/chromium/vapi-client.js @@ -36,6 +36,13 @@ self.vAPI = self.vAPI || {}; var chrome = self.chrome; var vAPI = self.vAPI; +// https://github.com/gorhill/uBlock/issues/456 +// Already injected? +if ( vAPI.vapiClientInjected ) { + return; +} +vAPI.vapiClientInjected = true; + vAPI.chrome = true; /******************************************************************************/ diff --git a/src/js/contentscript-end.js b/src/js/contentscript-end.js index 518f83e0f..6d229130d 100644 --- a/src/js/contentscript-end.js +++ b/src/js/contentscript-end.js @@ -21,18 +21,27 @@ /* global vAPI */ -'use strict'; - /******************************************************************************/ // Injected into content pages +(function() { + +'use strict'; + /******************************************************************************/ if ( vAPI.canExecuteContentScript() !== true ) { throw "uBlock> contentscript-end.js > Skipping " + location.protocol; } +// https://github.com/gorhill/uBlock/issues/456 +// Already injected? +if ( vAPI.contentscriptEndInjected ) { + return; +} +vAPI.contentscriptEndInjected = true; + /******************************************************************************/ var messager = vAPI.messaging.channel('contentscript-end.js'); @@ -259,15 +268,20 @@ var messager = vAPI.messaging.channel('contentscript-end.js'); node = nodeList[iNode]; attrValue = node.getAttribute(attr); if ( !attrValue ) { continue; } + // Candidate 1 = generic form + // If generic form is injected, no need to process the specific + // form, as the generic will affect all related specific forms selector = '[' + attr + '="' + attrValue + '"]'; - if ( generics[selector] ) { + if ( generics.hasOwnProperty(selector) ) { if ( injectedSelectors.hasOwnProperty(selector) === false ) { injectedSelectors[selector] = true; out.push(selector); + continue; } } + // Candidate 2 = specific form selector = node.tagName.toLowerCase() + selector; - if ( generics[selector] ) { + if ( generics.hasOwnProperty(selector) ) { if ( injectedSelectors.hasOwnProperty(selector) === false ) { injectedSelectors[selector] = true; out.push(selector); @@ -672,3 +686,8 @@ var messager = vAPI.messaging.channel('contentscript-end.js'); })(); /******************************************************************************/ +/******************************************************************************/ + +})(); + +/******************************************************************************/ diff --git a/src/js/contentscript-start.js b/src/js/contentscript-start.js index 6106c875d..2432562c2 100644 --- a/src/js/contentscript-start.js +++ b/src/js/contentscript-start.js @@ -44,6 +44,13 @@ if ( vAPI.canExecuteContentScript() !== true ) { return; } +// https://github.com/gorhill/uBlock/issues/456 +// Already injected? +if ( vAPI.contentscriptStartInjected ) { + return; +} +vAPI.contentscriptStartInjected = true; + /******************************************************************************/ var localMessager = vAPI.messaging.channel('contentscript-start.js'); @@ -55,14 +62,9 @@ var localMessager = vAPI.messaging.channel('contentscript-start.js'); // These can be inserted before the DOM is loaded. var cosmeticFilters = function(details) { - // Maybe uBlock's style tag was already injected? - var style = document.getElementById('ublock-preload-1ae7a5f130fc79b4fdb8a4272d9426b5'); - if ( style !== null ) { - return; - } var donthideCosmeticFilters = {}; var hideCosmeticFilters = {}; - style = document.createElement('style'); + var style = document.createElement('style'); style.setAttribute('id', 'ublock-preload-1ae7a5f130fc79b4fdb8a4272d9426b5'); var donthide = details.cosmeticDonthide; var hide = details.cosmeticHide; diff --git a/src/js/cosmetic-filtering.js b/src/js/cosmetic-filtering.js index e2a8f704a..0a95b0fc5 100644 --- a/src/js/cosmetic-filtering.js +++ b/src/js/cosmetic-filtering.js @@ -778,7 +778,9 @@ FilterContainer.prototype.freezeHighGenerics = function(what) { } var highHighGenericCount = 0; - var reHighLow = /^[a-z]*(\[(?:alt|title)="[^"]+"\])$/; + // https://github.com/gorhill/uBlock/issues/456 + // Include tag name, it's part of the filter + var reHighLow = /^[a-z]*\[(?:alt|title)="[^"]+"\]$/; var reHighMedium = /^\[href\^="https?:\/\/([^"]{8})[^"]*"\]$/; var matches, hash; @@ -788,8 +790,8 @@ FilterContainer.prototype.freezeHighGenerics = function(what) { } // ["title"] and ["alt"] will go in high-low generic bin. matches = reHighLow.exec(selector); - if ( matches && matches.length === 2 ) { - highLowGeneric[matches[1]] = true; + if ( matches && matches.length === 1 ) { + highLowGeneric[matches[0]] = true; highLowGenericCount += 1; continue; } diff --git a/src/js/tab.js b/src/js/tab.js index 048548b90..9584a1e70 100644 --- a/src/js/tab.js +++ b/src/js/tab.js @@ -19,18 +19,29 @@ Home: https://github.com/gorhill/uBlock */ -/* global µBlock */ +/* global vAPI, µBlock */ + +/******************************************************************************/ +/******************************************************************************/ + +(function() { + 'use strict'; /******************************************************************************/ +var µb = µBlock; + +/******************************************************************************/ +/******************************************************************************/ + // When the DOM content of root frame is loaded, this means the tab // content has changed. vAPI.tabs.onNavigation = function(details) { if ( details.frameId !== 0 ) { return; } - µBlock.bindTabToPageStats(details.tabId, details.url); + µb.bindTabToPageStats(details.tabId, details.url); }; // It may happen the URL in the tab changes, while the page's document @@ -43,21 +54,21 @@ vAPI.tabs.onUpdated = function(tabId, changeInfo, tab) { if ( !changeInfo.url ) { return; } - µBlock.bindTabToPageStats(tabId, changeInfo.url, 'tabUpdated'); + µb.bindTabToPageStats(tabId, changeInfo.url, 'tabUpdated'); }; vAPI.tabs.onClosed = function(tabId) { if ( tabId < 0 ) { return; } - µBlock.unbindTabFromPageStats(tabId); + µb.unbindTabFromPageStats(tabId); }; // https://github.com/gorhill/uBlock/issues/297 vAPI.tabs.onPopup = function(details) { //console.debug('vAPI.tabs.onPopup: url="%s"', details.url); - var pageStore = µBlock.pageStoreFromTabId(details.sourceTabId); + var pageStore = µb.pageStoreFromTabId(details.sourceTabId); if ( !pageStore ) { return; } @@ -66,8 +77,8 @@ vAPI.tabs.onPopup = function(details) { // https://github.com/gorhill/uBlock/issues/323 // If popup URL is whitelisted, do not block it - if ( µBlock.getNetFilteringSwitch(requestURL) ) { - result = µBlock.staticNetFilteringEngine.matchStringExactType(pageStore, requestURL, 'popup'); + if ( µb.getNetFilteringSwitch(requestURL) ) { + result = µb.staticNetFilteringEngine.matchStringExactType(pageStore, requestURL, 'popup'); } // https://github.com/gorhill/uBlock/issues/91 @@ -83,7 +94,7 @@ vAPI.tabs.onPopup = function(details) { // Blocked // It is a popup, block and remove the tab. - µBlock.unbindTabFromPageStats(details.tabId); + µb.unbindTabFromPageStats(details.tabId); vAPI.tabs.remove(details.tabId); // for Safari @@ -92,7 +103,6 @@ vAPI.tabs.onPopup = function(details) { vAPI.tabs.registerListeners(); - /******************************************************************************/ /******************************************************************************/ @@ -106,7 +116,7 @@ vAPI.tabs.registerListeners(); // hostname. This way, for a specific scheme you can create scope with // rules which will apply only to that scheme. -µBlock.normalizePageURL = function(pageURL) { +µb.normalizePageURL = function(pageURL) { var uri = this.URI.set(pageURL); if ( uri.scheme === 'https' || uri.scheme === 'http' ) { return uri.normalizedURI(); @@ -118,7 +128,7 @@ vAPI.tabs.registerListeners(); // Create an entry for the tab if it doesn't exist. -µBlock.bindTabToPageStats = function(tabId, pageURL, context) { +µb.bindTabToPageStats = function(tabId, pageURL, context) { this.updateBadgeAsync(tabId); // https://github.com/gorhill/httpswitchboard/issues/303 @@ -146,7 +156,7 @@ vAPI.tabs.registerListeners(); return pageStore; }; -µBlock.unbindTabFromPageStats = function(tabId) { +µb.unbindTabFromPageStats = function(tabId) { //console.debug('µBlock> unbindTabFromPageStats(%d)', tabId); var pageStore = this.pageStores[tabId]; if ( pageStore !== undefined ) { @@ -157,27 +167,60 @@ vAPI.tabs.registerListeners(); /******************************************************************************/ -µBlock.pageUrlFromTabId = function(tabId) { +µb.pageUrlFromTabId = function(tabId) { var pageStore = this.pageStores[tabId]; return pageStore ? pageStore.pageURL : ''; }; -µBlock.pageUrlFromPageStats = function(pageStats) { +µb.pageUrlFromPageStats = function(pageStats) { if ( pageStats ) { return pageStats.pageURL; } return ''; }; -µBlock.pageStoreFromTabId = function(tabId) { +µb.pageStoreFromTabId = function(tabId) { return this.pageStores[tabId]; }; +/******************************************************************************/ /******************************************************************************/ -// µBlock.forceReload = function(pageURL) { -// var tabId = this.tabIdFromPageUrl(pageURL); -// if ( tabId ) { -// chrome.tabs.reload(tabId, { bypassCache: true }); -// } -// }; +// Stale page store entries janitor +// https://github.com/gorhill/uBlock/issues/455 + +var pageStoreJanitorPeriod = 15 * 60 * 1000; +var pageStoreJanitorSampleAt = 0; +var pageStoreJanitorSampleSize = 10; + +var pageStoreJanitor = function() { + var vapiTabs = vAPI.tabs; + var tabIds = Object.keys(µb.pageStores).sort(); + var checkTab = function(tabId) { + vapiTabs.get(tabId, function(tab) { + if ( !tab ) { + //console.error('tab.js> pageStoreJanitor(): stale page store found:', µb.pageUrlFromTabId(tabId)); + µb.unbindTabFromPageStats(tabId); + } + }); + }; + if ( pageStoreJanitorSampleAt >= tabIds.length ) { + pageStoreJanitorSampleAt = 0; + } + var n = Math.min(pageStoreJanitorSampleAt + pageStoreJanitorSampleSize, tabIds.length); + for ( var i = pageStoreJanitorSampleAt; i < n; i++ ) { + checkTab(tabIds[i]); + } + pageStoreJanitorSampleAt = n; + + setTimeout(pageStoreJanitor, pageStoreJanitorPeriod); +}; + +setTimeout(pageStoreJanitor, pageStoreJanitorPeriod); + +/******************************************************************************/ +/******************************************************************************/ + +})(); + +/******************************************************************************/