From b82742d126bb49b9edd7c2a75c31871711875f88 Mon Sep 17 00:00:00 2001 From: gorhill Date: Sun, 20 Jul 2014 15:00:26 -0400 Subject: [PATCH] this is for #84, #70, #53, #36 --- assets/ublock/filters.txt | 2 +- assets/ublock/thirdparty-lists.txt | 131 ++++++++++++++++++++++ js/3p-filters.js | 2 +- js/abp-filters.js | 79 ++++++-------- js/abp-hide-filters.js | 8 ++ js/assets.js | 106 +++++++++++++++--- js/background.js | 2 +- js/contentscript-end.js | 170 +++++++++++------------------ 8 files changed, 333 insertions(+), 167 deletions(-) create mode 100644 assets/ublock/thirdparty-lists.txt diff --git a/assets/ublock/filters.txt b/assets/ublock/filters.txt index b8c06a77e..3d0ead71c 100644 --- a/assets/ublock/filters.txt +++ b/assets/ublock/filters.txt @@ -47,7 +47,7 @@ google.*##.mw > #rcnt > #center_col > #taw > #tvcap > .c # https://github.com/gorhill/uBlock/issues/70 # Workaround for issue 70. -deviantart.com##.dp-ad-target.atf-right-300x250 +#deviantart.com##.dp-ad-target.atf-right-300x250 # Seen on mediafire.com ||torconpro.com^ diff --git a/assets/ublock/thirdparty-lists.txt b/assets/ublock/thirdparty-lists.txt new file mode 100644 index 000000000..7745e9485 --- /dev/null +++ b/assets/ublock/thirdparty-lists.txt @@ -0,0 +1,131 @@ +mirror1.malwaredomains.com/files/immortal_domains.txt +http://mirror1.malwaredomains.com/files/immortal_domains.txt + +mirror1.malwaredomains.com/files/justdomains +http://mirror1.malwaredomains.com/files/justdomains + +pgl.yoyo.org/as/serverlist +http://pgl.yoyo.org/adservers/serverlist.php?hostformat=hosts&showintro=1&mimetype=plaintext + +www.malwaredomainlist.com/hostslist/hosts.txt +http://www.malwaredomainlist.com/hostslist/hosts.txt + +hosts-file.net/ad-servers +http://hosts-file.net/.%5Cad_servers.txt + +someonewhocares.org/hosts/hosts +http://someonewhocares.org/hosts/hosts + +winhelp2002.mvps.org/hosts.txt +http://winhelp2002.mvps.org/hosts.txt + +spam404bl.com/spam404scamlist.txt +http://spam404bl.com/spam404scamlist.txt + +easylist-downloads.adblockplus.org/easylist.txt +https://easylist-downloads.adblockplus.org/easylist.txt + +easylist-downloads.adblockplus.org/easylist_noelemhide.txt +https://easylist-downloads.adblockplus.org/easylist_noelemhide.txt + +easylist-downloads.adblockplus.org/easyprivacy.txt +https://easylist-downloads.adblockplus.org/easyprivacy.txt + +easylist-downloads.adblockplus.org/fanboy-annoyance.txt +https://easylist-downloads.adblockplus.org/fanboy-annoyance.txt + +easylist-downloads.adblockplus.org/fanboy-social.txt +https://easylist-downloads.adblockplus.org/fanboy-social.txt + +www.fanboy.co.nz/r/fanboy-ultimate.txt +https://www.fanboy.co.nz/r/fanboy-ultimate.txt + +www.fanboy.co.nz/enhancedstats.txt +https://www.fanboy.co.nz/enhancedstats.txt + +www.fanboy.co.nz/fanboy-antifacebook.txt +https://www.fanboy.co.nz/fanboy-antifacebook.txt + +raw.githubusercontent.com/reek/anti-adblock-killer/master/anti-adblock-killer-filters.txt +https://raw.githubusercontent.com/reek/anti-adblock-killer/master/anti-adblock-killer-filters.txt + +easylist-downloads.adblockplus.org/easylistgermany.txt +https://easylist-downloads.adblockplus.org/easylistgermany.txt + +www.hufilter.hu/hufilter.txt +http://www.hufilter.hu/hufilter.txt + +easylist-downloads.adblockplus.org/easylistitaly.txt +https://easylist-downloads.adblockplus.org/easylistitaly.txt + +dl.dropboxusercontent.com/u/1289327/abpxfiles/filtri.txt +https://dl.dropboxusercontent.com/u/1289327/abpxfiles/filtri.txt + +easylist-downloads.adblockplus.org/easylistdutch.txt +https://easylist-downloads.adblockplus.org/easylistdutch.txt + +easylist-downloads.adblockplus.org/liste_fr.txt +https://easylist-downloads.adblockplus.org/liste_fr.txt + +easylist-downloads.adblockplus.org/advblock.txt +https://easylist-downloads.adblockplus.org/advblock.txt + +easylist-downloads.adblockplus.org/bitblock.txt +https://easylist-downloads.adblockplus.org/bitblock.txt + +easylist-downloads.adblockplus.org/easylistchina.txt +https://easylist-downloads.adblockplus.org/easylistchina.txt + +cjxlist1.googlecode.com/svn/cjxlist.txt +https://cjxlist1.googlecode.com/svn/cjxlist.txt + +adblock-chinalist.googlecode.com/svn/trunk/adblock.txt +http://adblock-chinalist.googlecode.com/svn/trunk/adblock.txt + +adblock-plus-japanese-filter.googlecode.com/hg/abp_jp.txt +https://adblock-plus-japanese-filter.googlecode.com/hg/abp_jp.txt + +margevicius.lt/AdBlockPlusLithuania.txt +http://margevicius.lt/AdBlockPlusLithuania.txt + +stanev.org/abp/adblock_bg.txt +http://stanev.org/abp/adblock_bg.txt + +indonesianadblockrules.googlecode.com/hg/subscriptions/abpindo.txt +https://indonesianadblockrules.googlecode.com/hg/subscriptions/abpindo.txt + +liste-ar-adblock.googlecode.com/hg/Liste_AR.txt +https://liste-ar-adblock.googlecode.com/hg/Liste_AR.txt + +raw.githubusercontent.com/tomasko126/easylistczechandslovak/master/filters.txt +https://raw.githubusercontent.com/tomasko126/easylistczechandslovak/master/filters.txt + +raw.githubusercontent.com/adblockpolska/Adblock_PL_List/master/adblock_polska.txt +https://raw.githubusercontent.com/adblockpolska/Adblock_PL_List/master/adblock_polska.txt + +raw.githubusercontent.com/AdBlockPlusIsrael/EasyListHebrew/master/EasyListHebrew.txt +https://raw.githubusercontent.com/AdBlockPlusIsrael/EasyListHebrew/master/EasyListHebrew.txt + +raw.githubusercontent.com/wiltteri/wiltteri.txt/master/wiltteri.txt +http://raw.githubusercontent.com/wiltteri/wiltteri.txt/master/wiltteri.txt + +home.fredfiber.no/langsholt/adblock.txt +http://home.fredfiber.no/langsholt/adblock.txt + +www.fanboy.co.nz/fanboy-swedish.txt +https://www.fanboy.co.nz/fanboy-swedish.txt + +adblock.schack.dk/block.txt +http://adblock.schack.dk/block.txt + +adblock.gardar.net/is.abp.txt +http://adblock.gardar.net/is.abp.txt + +www.void.gr/kargig/void-gr-filters.txt +https://www.void.gr/kargig/void-gr-filters.txt + +abp.mozilla-hispano.org/nauscopio/filtros.txt +http://abp.mozilla-hispano.org/nauscopio/filtros.txt + +gitorious.org/adblock-latvian/adblock-latvian/raw/master_lists/latvian-list.txt +https://gitorious.org/adblock-latvian/adblock-latvian/raw/master%3Alists/latvian-list.txt \ No newline at end of file diff --git a/js/3p-filters.js b/js/3p-filters.js index 11ba4d3e2..31cbacb8c 100644 --- a/js/3p-filters.js +++ b/js/3p-filters.js @@ -122,7 +122,7 @@ var renderBlacklists = function() { '
  • ', '', ' ', - '', + '', '{{name}}', '', ': ', diff --git a/js/abp-filters.js b/js/abp-filters.js index 9effda05b..92c6e1b4d 100644 --- a/js/abp-filters.js +++ b/js/abp-filters.js @@ -1008,25 +1008,41 @@ FilterBucket.prototype.match = function(url, tokenBeg) { /******************************************************************************/ var FilterContainer = function() { - this.categories = {}; - this.duplicates = {}; - this.url = ''; - this.tokenBeg = 0; - this.tokenEnd = 0; + this.reAnyToken = /[%0-9a-z]+/g; + this.buckets = new Array(8); + this.blockedAnyPartyHostnames = new µBlock.LiquidDict(); + this.blocked3rdPartyHostnames = new µBlock.LiquidDict(); this.filterParser = new FilterParser(); + this.reset(); +}; + +/******************************************************************************/ + +// Reset all, thus reducing to a minimum memory footprint of the context. + +FilterContainer.prototype.reset = function() { + this.frozen = false; this.processedFilterCount = 0; this.acceptedCount = 0; this.allowFilterCount = 0; this.blockFilterCount = 0; this.duplicateCount = 0; + this.categories = {}; + this.duplicates = {}; + this.blockedAnyPartyHostnames.reset(); + this.blocked3rdPartyHostnames.reset(); + this.filterParser.reset(); +}; - // This is for hostname-based-any-request filters - this.blockedAnyPartyHostnames = new µBlock.LiquidDict(); - this.blocked3rdPartyHostnames = new µBlock.LiquidDict(); +/******************************************************************************/ - // Used during URL matching - this.reAnyToken = /[%0-9a-z]+/g; - this.buckets = new Array(8); +FilterContainer.prototype.freeze = function() { + //histogram('allFilters', this.categories); + this.blockedAnyPartyHostnames.freeze(); + this.blocked3rdPartyHostnames.freeze(); + this.duplicates = {}; + this.filterParser.reset(); + this.frozen = true; }; /******************************************************************************/ @@ -1233,35 +1249,7 @@ FilterContainer.prototype.addToCategory = function(category, tokenKey, filter) { /******************************************************************************/ -// Reset all, thus reducing to a minimum memory footprint of the context. - -FilterContainer.prototype.reset = function() { - this.processedFilterCount = 0; - this.acceptedCount = 0; - this.allowFilterCount = 0; - this.blockFilterCount = 0; - this.duplicateCount = 0; - this.categories = {}; - this.blockedAnyPartyHostnames.reset(); - this.blocked3rdPartyHostnames.reset(); - this.duplicates = {}; - this.filterParser.reset(); -}; - -/******************************************************************************/ - -FilterContainer.prototype.freeze = function() { - //histogram('allFilters', this.categories); - this.blockedAnyPartyHostnames.freeze(); - this.blocked3rdPartyHostnames.freeze(); - this.duplicates = {}; - this.filterParser.reset(); -}; - -/******************************************************************************/ - -FilterContainer.prototype.matchTokens = function() { - var url = this.url; +FilterContainer.prototype.matchTokens = function(url) { var re = this.reAnyToken; var matches, beg, token; var buckets = this.buckets; @@ -1383,14 +1371,17 @@ FilterContainer.prototype.match3rdPartyHostname = function(requestHostname) { /******************************************************************************/ -FilterContainer.prototype.matchString = function(pageDetails, url, requestType, requestHostname) { +FilterContainer.prototype.matchString = function(pageDetails, requestURL, requestType, requestHostname) { // adbProfiler.countUrl(); + if ( this.frozen !== true ) { + return false; + } // https://github.com/gorhill/httpswitchboard/issues/239 // Convert url to lower case: // `match-case` option not supported, but then, I saw only one // occurrence of it in all the supported lists (bulgaria list). - this.url = url.toLowerCase(); + var url = requestURL.toLowerCase(); // The logic here is simple: // @@ -1442,7 +1433,7 @@ FilterContainer.prototype.matchString = function(pageDetails, url, requestType, buckets[5] = categories[this.makeCategoryKey(BlockAction | type | party)]; buckets[6] = categories[this.makeCategoryKey(BlockOneParty | type | domainParty)]; buckets[7] = categories[this.makeCategoryKey(BlockOtherParties | type)]; - br = this.matchTokens(); + br = this.matchTokens(url); } // If there is no block filter, no need to test against allow filters @@ -1459,7 +1450,7 @@ FilterContainer.prototype.matchString = function(pageDetails, url, requestType, buckets[5] = categories[this.makeCategoryKey(AllowAction | type | party)]; buckets[6] = categories[this.makeCategoryKey(AllowOneParty | type | domainParty)]; buckets[7] = categories[this.makeCategoryKey(AllowOtherParties | type | domainParty)]; - var ar = this.matchTokens(); + var ar = this.matchTokens(url); if ( ar !== false ) { return '@@' + ar; } diff --git a/js/abp-hide-filters.js b/js/abp-hide-filters.js index 4dfd82762..6a15042bb 100644 --- a/js/abp-hide-filters.js +++ b/js/abp-hide-filters.js @@ -272,6 +272,7 @@ var FilterContainer = function() { FilterContainer.prototype.reset = function() { this.filterParser.reset(); + this.frozen = false; this.acceptedCount = 0; this.processedCount = 0; this.genericFilters = {}; @@ -399,6 +400,7 @@ FilterContainer.prototype.freeze = function() { // console.debug('Number of duplicate cosmetic filters skipped:', this.duplicateCount); this.duplicates = {}; + this.frozen = true; //console.log('µBlock> adp-hide-filters.js: %d filters accepted', this.acceptedCount); //console.log('µBlock> adp-hide-filters.js: %d filters processed', this.processedCount); @@ -611,6 +613,9 @@ FilterContainer.prototype.addFilterEntry = function(filterDict, hash, f) { /******************************************************************************/ FilterContainer.prototype.retrieveGenericSelectors = function(tabHostname, request) { + if ( this.frozen !== true ) { + return; + } if ( !tabHostname || µb.getCosmeticFilteringSwitch(tabHostname) !== true ) { return; } @@ -669,6 +674,9 @@ FilterContainer.prototype.retrieveGenericSelectors = function(tabHostname, reque /******************************************************************************/ FilterContainer.prototype.retrieveDomainSelectors = function(tabHostname, request) { + if ( this.frozen !== true ) { + return; + } if ( !tabHostname || µb.getCosmeticFilteringSwitch(tabHostname) !== true ) { return; } diff --git a/js/assets.js b/js/assets.js index 9d48c13ad..70e02d688 100644 --- a/js/assets.js +++ b/js/assets.js @@ -62,8 +62,9 @@ File system structure: var fileSystem; var fileSystemQuota = 40 * 1024 * 1024; -var remoteRoot = µBlock.projectServerRoot; +var repositoryRoot = µBlock.projectServerRoot; var nullFunc = function() { }; +var thirdpartyHomeURLs = null; /******************************************************************************/ @@ -71,6 +72,7 @@ var getTextFileFromURL = function(url, onLoad, onError) { // console.log('µBlock> getTextFileFromURL("%s"):', url); var xhr = new XMLHttpRequest(); xhr.responseType = 'text'; + xhr.timeout = 15000; xhr.onload = onLoad; xhr.onerror = onError; xhr.ontimeout = onError; @@ -268,7 +270,7 @@ var readRemoteFile = function(path, callback) { // 'ublock=...' is to skip browser cache getTextFileFromURL( - remoteRoot + path + '?ublock=' + Date.now(), + repositoryRoot + path + '?ublock=' + Date.now(), onRemoteFileLoaded, onRemoteFileError ); @@ -346,8 +348,10 @@ var writeLocalFile = function(path, content, callback) { var updateFromRemote = function(details, callback) { // 'ublock=...' is to skip browser cache - var remoteURL = remoteRoot + details.path + '?ublock=' + Date.now(); var targetPath = details.path; + // https://github.com/gorhill/uBlock/issues/84 + var homeURL = ''; + var repositoryURL = repositoryRoot + targetPath + '?ublock=' + Date.now(); var targetMd5 = details.md5 || ''; var reportBackError = function() { @@ -360,30 +364,106 @@ var updateFromRemote = function(details, callback) { var onRemoteFileLoaded = function() { this.onload = this.onerror = null; if ( typeof this.responseText !== 'string' ) { - console.error('µBlock> updateFromRemote("%s") / onRemoteFileLoaded(): no response', remoteURL); + console.error('µBlock> updateFromRemote("%s") / onRemoteFileLoaded(): no response', repositoryURL); reportBackError(); return; } - if ( YaMD5.hashStr(this.responseText) !== targetMd5 ) { - console.error('µBlock> updateFromRemote("%s") / onRemoteFileLoaded(): bad md5 checksum', remoteURL); + if ( targetMd5 !== '' && YaMD5.hashStr(this.responseText) !== targetMd5 ) { + console.error('µBlock> updateFromRemote("%s") / onRemoteFileLoaded(): bad md5 checksum', repositoryURL); reportBackError(); return; } - // console.debug('µBlock> updateFromRemote("%s") / onRemoteFileLoaded()', remoteURL); + // console.debug('µBlock> updateFromRemote("%s") / onRemoteFileLoaded()', repositoryURL); writeLocalFile(targetPath, this.responseText, callback); }; var onRemoteFileError = function(ev) { this.onload = this.onerror = null; - console.error('µBlock> updateFromRemote() / onRemoteFileError("%s"):', remoteURL, this.statusText); + console.error('µBlock> updateFromRemote() / onRemoteFileError("%s"):', repositoryURL, this.statusText); reportBackError(); }; - getTextFileFromURL( - remoteURL, - onRemoteFileLoaded, - onRemoteFileError - ); + var onHomeFileLoaded = function() { + this.onload = this.onerror = null; + if ( typeof this.responseText !== 'string' ) { + console.error('µBlock> updateFromRemote("%s") / onHomeFileLoaded(): no response', homeURL); + getTextFileFromURL(repositoryURL, onRemoteFileLoaded, onRemoteFileError); + return; + } + if ( targetMd5 !== '' && YaMD5.hashStr(this.responseText) !== targetMd5 ) { + console.error('µBlock> updateFromRemote("%s") / onHomeFileLoaded(): bad md5 checksum', homeURL); + getTextFileFromURL(repositoryURL, onRemoteFileLoaded, onRemoteFileError); + return; + } + // console.debug('µBlock> updateFromRemote("%s") / onHomeFileLoaded()', homeURL); + writeLocalFile(targetPath, this.responseText, callback); + }; + + var onHomeFileError = function(ev) { + this.onload = this.onerror = null; + console.error('µBlock> updateFromRemote() / onHomeFileError("%s"):', homeURL, this.statusText); + getTextFileFromURL(repositoryURL, onRemoteFileLoaded, onRemoteFileError); + }; + + // https://github.com/gorhill/uBlock/issues/84 + // Create a URL from where the asset needs to be downloaded. It can be: + // - a home server URL + // - a github repo URL + var getThirdpartyHomeURL = function() { + // If it is a 3rd-party, look-up home server URL, if any. + if ( targetPath.indexOf('assets/thirdparties/') === 0 ) { + homeURL = targetPath.replace('assets/thirdparties/', ''); + if ( thirdpartyHomeURLs && thirdpartyHomeURLs[homeURL] ) { + homeURL = thirdpartyHomeURLs[homeURL]; + } else { + homeURL = 'http://' + homeURL; + } + } + // If there is a home server, disregard checksum: the file is assumed + // most up to date at the home server. + if ( homeURL !== '' ) { + targetMd5 = ''; + getTextFileFromURL(homeURL, onHomeFileLoaded, onHomeFileError); + return; + } + // The resource will be pulled from Github repo. It's reserved for + // more important assets, so we keep and use the checksum. + getTextFileFromURL(repositoryURL, onRemoteFileLoaded, onRemoteFileError); + }; + + // https://github.com/gorhill/uBlock/issues/84 + // First try to load from the actual home server of a third-party. + var onThirdpartyHomeURLsLoaded = function(details) { + thirdpartyHomeURLs = {}; + if ( details.error ) { + getThirdpartyHomeURL(); + return; + } + var urlPairs = details.content.split(/\n\n+/); + var i = urlPairs.length; + var pair, pos, k, v; + while ( i-- ) { + pair = urlPairs[i]; + pos = pair.indexOf('\n'); + if ( pos === -1 ) { + continue; + } + k = pair.slice(0, pos).trim(); + v = pair.slice(pos).trim(); + if ( k === '' || v === '' ) { + continue; + } + thirdpartyHomeURLs[k] = v; + } + getThirdpartyHomeURL(); + }; + + // Get home servers if not done yet. + if ( thirdpartyHomeURLs === null ) { + readLocalFile('assets/ublock/thirdparty-lists.txt', onThirdpartyHomeURLsLoaded); + } else { + getThirdpartyHomeURL(); + } }; /******************************************************************************/ diff --git a/js/background.js b/js/background.js index 392c3d7fa..e073a74ad 100644 --- a/js/background.js +++ b/js/background.js @@ -43,7 +43,7 @@ return { allowedRequestCount: 0 }, - updateAssetsEvery: 4 * 24 * 60 * 60 * 1000, + updateAssetsEvery: 2 * 24 * 60 * 60 * 1000, projectServerRoot: 'https://raw2.github.com/gorhill/ublock/master/', userFiltersPath: 'assets/user/filters.txt', diff --git a/js/contentscript-end.js b/js/contentscript-end.js index e120fcb2b..9aff7ecb9 100644 --- a/js/contentscript-end.js +++ b/js/contentscript-end.js @@ -146,9 +146,8 @@ var cosmeticFiltering = (function() { injectedSelectors[exceptions[i]] = true; } } - - // TODO: evaluate merging into a single loop - selectorsFromNodeList(document.querySelectorAll('*[class],*[id]')); + idsFromNodeList(document.querySelectorAll('[id]')); + classesFromNodeList(document.querySelectorAll('[class]')); retrieveGenericSelectors(); }; @@ -302,42 +301,47 @@ var cosmeticFiltering = (function() { } }; - // TODO: split back into to specialized functions as it was before. Too - // many code paths creeped back, I thought I could get rid of most. - var selectorsFromNodeList = function(nodes) { + var idsFromNodeList = function(nodes) { if ( !nodes || !nodes.length ) { return; } if ( idSelectors === null ) { idSelectors = []; } + var qq = queriedSelectors; + var ii = idSelectors; + var node, v; + var i = nodes.length; + while ( i-- ) { + node = nodes[i]; + if ( node.nodeType !== 1 ) { continue; } + // id + v = nodes[i].id; + // quite unlikely, so no need to be fancy + if ( typeof v !== 'string' ) { continue; } + v = v.trim(); + if ( v === '' ) { continue; } + v = '#' + v; + if ( qq[v] ) { continue; } + ii.push(v); + qq[v] = true; + } + }; + + var classesFromNodeList = function(nodes) { + if ( !nodes || !nodes.length ) { + return; + } if ( classSelectors === null ) { classSelectors = {}; } var qq = queriedSelectors; var cc = classSelectors; - var ii = idSelectors; - var node, v, classNames, j; + var node, v, vv, j; var i = nodes.length; while ( i-- ) { node = nodes[i]; - if ( node.nodeType !== 1 ) { - continue; - } - // id - v = nodes[i].id; - // quite unlikely, so no need to be fancy - if ( typeof v !== 'string' ) { - v = ''; - } - v = v.trim(); - if ( v !== '' ) { - v = '#' + v; - if ( !qq[v] ) { - ii.push(v); - qq[v] = true; - } - } + if ( node.nodeType !== 1 ) { continue; } // class v = nodes[i].className; // it could be an SVGAnimatedString... @@ -353,10 +357,10 @@ var cosmeticFiltering = (function() { continue; } // many classes - classNames = v.trim().split(/\s+/); - j = classNames.length; + vv = v.trim().split(' '); + j = vv.length; while ( j-- ) { - v = classNames[j]; + v = vv[j].trim(); if ( v === '' ) { continue; } v = '.' + v; if ( qq[v] ) { continue; } @@ -371,12 +375,14 @@ var cosmeticFiltering = (function() { var nodeList, j, node; while ( i-- ) { nodeList = nodeLists[i]; - selectorsFromNodeList(nodeList); + idsFromNodeList(nodeList); + classesFromNodeList(nodeList); j = nodeList.length; while ( j-- ) { node = nodeList[j]; - if ( node.querySelectorAll ) { - selectorsFromNodeList(node.querySelectorAll('*[id],*[class]')); + if ( typeof node.querySelectorAll === 'function' ) { + idsFromNodeList(node.querySelectorAll('[id]')); + classesFromNodeList(node.querySelectorAll('[class]')); } } } @@ -394,7 +400,7 @@ var cosmeticFiltering = (function() { // https://github.com/gorhill/uBlock/issues/7 -var blockedElementHider = (function() { +(function() { var hideOne = function(elem, collapse) { // If `!important` is not there, going back using history will likely // cause the hidden element to re-appear. @@ -404,21 +410,9 @@ var blockedElementHider = (function() { } }; - var observeOne = function(elem) { - var onComplete = function() { - var elem = this; - var onAnswerReceived = function(details) { - if ( details.blocked ) { - hideOne(elem, details.collapse); - } - }; - messaging.ask({ what: 'blockedRequest', url: this.src }, onAnswerReceived); - this.removeEventListener('load', onComplete); - }; - elem.addEventListener('load', onComplete); - }; - - var hideMany = function(elems, details) { + // First pass + messaging.ask({ what: 'blockedRequests' }, function(details) { + var elems = document.querySelectorAll('img,iframe'); var blockedRequests = details.blockedRequests; var collapse = details.collapse; var i = elems.length; @@ -426,78 +420,41 @@ var blockedElementHider = (function() { while ( i-- ) { elem = elems[i]; src = elem.src; - if ( typeof src !== 'string' ) { + if ( typeof src !== 'string' || src === '' ) { continue; } - if ( src === '' ) { - observeOne(elem); - } else if ( blockedRequests[src] ) { + if ( blockedRequests[src] ) { hideOne(elem, collapse); } } - }; + }); - var processElements = function(elems) { - var blockedRequestsReceived = function(details) { - hideMany(elems, details); - var i = elems.length; - while ( i-- ) { - hideMany(elems[i].querySelectorAll('img,iframe'), details); + // Listeners to mop up whatever is otherwise missed: + // - Future requests not blocked yet + // - Elements dynamically added to the page + // - Elements which resource URL changes + var onResourceLoaded = function(ev) { + var target = ev.target; + if ( !target || !target.src ) { + return; + } + var tag = target.tagName.toLowerCase(); + if ( tag !== 'img' && tag !== 'iframe' ) { + return; + } + var onAnswerReceived = function(details) { + if ( details.blocked ) { + hideOne(target, details.collapse); } }; - messaging.ask({ what: 'blockedRequests' }, blockedRequestsReceived); - }; - - // rhill 2014-07-01: Avoid useless work: only nodes which are element are - // of interest at this point -- because it is common that a lot of plain - // text nodes get added. - var addNodeLists = function(nodeLists) { - var elems = []; - var i = nodeLists.length; - var nodeList, j, node; - while ( i-- ) { - nodeList = nodeLists[i]; - j = nodeList.length; - while ( j-- ) { - node = nodeList[j]; - if ( node.querySelectorAll ) { - elems.push(node); - } - } - } - if ( elems.length ) { - processElements(elems); - } - }; - - var onBlockedRequestsReceived = function(details) { - hideMany(document.querySelectorAll('img,iframe'), details); - }; - messaging.ask({ what: 'blockedRequests' }, onBlockedRequestsReceived); - - return { - addNodeLists: addNodeLists + messaging.ask({ what: 'blockedRequest', url: target.src }, onAnswerReceived); }; + document.addEventListener('load', onResourceLoaded, true); + document.addEventListener('error', onResourceLoaded, true); })(); /******************************************************************************/ -// rhill 2013-11-09: Weird... This code is executed from µBlock -// context first time extension is launched. Avoid this. -// TODO: Investigate if this was a fluke or if it can really happen. -// I suspect this could only happen when I was using chrome.tabs.executeScript(), -// because now a delarative content script is used, along with "http{s}" URL -// pattern matching. - -// console.debug('µBlock> window.location.href = "%s"', window.location.href); - -if ( /^https?:\/\/./.test(window.location.href) === false ) { - console.debug("Huh?"); - return; -} - -/******************************************************************************/ - // Observe changes in the DOM var mutationObservedHandler = function(mutations) { @@ -511,7 +468,6 @@ var mutationObservedHandler = function(mutations) { } if ( nodeLists.length ) { cosmeticFiltering.processNodeLists(nodeLists); - blockedElementHider.addNodeLists(nodeLists); } };