diff --git a/js/contentscript-end.js b/js/contentscript-end.js index 62ca7be6e..000f505c7 100644 --- a/js/contentscript-end.js +++ b/js/contentscript-end.js @@ -247,30 +247,39 @@ var uBlockMessaging = (function(name){ processHighMediumGenerics(highGenerics.hideMedium, hideSelectors); } if ( highGenerics.hideHighCount ) { - processHighHighGenerics(highGenerics.hideHigh, hideSelectors); + processHighHighGenericsAsync(); } } if ( hideSelectors.length ) { - hideElements(hideSelectors); - var style = document.createElement('style'); - style.setAttribute('class', 'ublock-postload-1ae7a5f130fc79b4fdb8a4272d9426b5'); - // The linefeed before the style block is very important: do no remove! - style.appendChild(document.createTextNode(hideSelectors.join(',\n') + '\n{display:none !important;}')); - var parent = document.body || document.documentElement; - if ( parent ) { - parent.appendChild(style); - } - messaging.tell({ - what: 'injectedSelectors', - type: 'cosmetic', - hostname: window.location.hostname, - selectors: hideSelectors - }); - //console.debug('µBlock> generic cosmetic filters: injecting %d CSS rules:', hideSelectors.length, text); + addStyleTag(hideSelectors); } contextNodes.length = 0; }; + // Ensure elements matching a set of selectors are visually removed + // from the page, by: + // - Modifying the style property on the elements themselves + // - Injecting a style tag + + var addStyleTag = function(selectors) { + hideElements(selectors); + var style = document.createElement('style'); + style.setAttribute('class', 'ublock-postload-1ae7a5f130fc79b4fdb8a4272d9426b5'); + // The linefeed before the style block is very important: do no remove! + style.appendChild(document.createTextNode(selectors.join(',\n') + '\n{display:none !important;}')); + var parent = document.body || document.documentElement; + if ( parent ) { + parent.appendChild(style); + } + messaging.tell({ + what: 'injectedSelectors', + type: 'cosmetic', + hostname: window.location.hostname, + selectors: selectors + }); + //console.debug('µBlock> generic cosmetic filters: injecting %d CSS rules:', selectors.length, text); + }; + var hideElements = function(selectors) { // https://github.com/gorhill/uBlock/issues/207 // Do not call querySelectorAll() using invalid CSS selectors @@ -289,6 +298,8 @@ var uBlockMessaging = (function(name){ } }; + // Extract and return the staged nodes which (may) match the selectors. + var selectNodes = function(selector) { var targetNodes = []; var i = contextNodes.length; @@ -309,6 +320,10 @@ var uBlockMessaging = (function(name){ return targetNodes; }; + // Low generics: + // - [id] + // - [class] + var processLowGenerics = function(generics, out) { var i = generics.length; var selector; @@ -322,6 +337,10 @@ var uBlockMessaging = (function(name){ } }; + // High-low generics: + // - [alt="..."] + // - [title="..."] + var processHighLowGenerics = function(generics, out) { var attrs = ['title', 'alt']; var attr, attrValue, nodeList, iNode, node; @@ -351,6 +370,9 @@ var uBlockMessaging = (function(name){ } }; + // High-medium generics: + // - [href^="http"] + var processHighMediumGenerics = function(generics, out) { var nodeList = selectNodes('a[href^="http"]'); var iNode = nodeList.length; @@ -376,22 +398,45 @@ var uBlockMessaging = (function(name){ } }; - var processHighHighGenerics = function(generics, out) { + // High-high generics are *very costly* to process, so we will coalesce + // requests to process high-high generics into as few requests as possible. + // The gain is *significant* on bloated pages. + + var processHighHighGenericsTimer = null; + + var processHighHighGenerics = function() { + processHighHighGenericsTimer = null; if ( injectedSelectors['{{highHighGenerics}}'] !== undefined ) { return; } - if ( document.querySelector(generics) === null ) { return; } + if ( document.querySelector(highGenerics.hideHigh) === null ) { return; } injectedSelectors['{{highHighGenerics}}'] = true; - var selectors = generics.split(',\n'); - var iSelector = selectors.length; + // We need to filter out possible exception cosmetic filters from + // high-high generics selectors. + var selectors = highGenerics.hideHigh.split(',\n'); + var i = selectors.length; var selector; - while ( iSelector-- ) { - selector = selectors[iSelector]; - if ( injectedSelectors[selector] === undefined ) { + while ( i-- ) { + selector = selectors[i]; + if ( injectedSelectors.hasOwnProperty(selector) ) { + selectors.splice(i, 1); + } else { injectedSelectors[selector] = true; - out.push(selector); } } + if ( selectors.length !== 0 ) { + addStyleTag(selectors); + } }; + var processHighHighGenericsAsync = function() { + if ( processHighHighGenericsTimer !== null ) { + clearTimeout(processHighHighGenericsTimer); + } + processHighHighGenericsTimer = setTimeout(processHighHighGenerics, 300); + }; + + // Extract all ids: these will be passed to the cosmetic filtering + // engine, and in return we will obtain only the relevant CSS selectors. + var idsFromNodeList = function(nodes) { if ( !nodes || !nodes.length ) { return; @@ -418,6 +463,9 @@ var uBlockMessaging = (function(name){ } }; + // Extract all classes: these will be passed to the cosmetic filtering + // engine, and in return we will obtain only the relevant CSS selectors. + var classesFromNodeList = function(nodes) { if ( !nodes || !nodes.length ) { return; @@ -460,8 +508,14 @@ var uBlockMessaging = (function(name){ } }; + // Start cosmetic filtering. + domLoaded(); + // Below this point is the code which takes care to observe changes in + // the page and to add if needed relevant CSS rules as a result of the + // changes. + // Observe changes in the DOM only if... // - there is a document.body // - there is at least one `script` tag diff --git a/js/pagestore.js b/js/pagestore.js index eba4c27df..9a0f17d78 100644 --- a/js/pagestore.js +++ b/js/pagestore.js @@ -385,9 +385,10 @@ PageStore.prototype.getNetFilteringSwitch = function() { PageStore.prototype.filterRequest = function(context, requestType, requestURL) { var result = this.netFilteringCache.lookup(requestURL); if ( result !== undefined ) { + //console.debug(' cache HIT: PageStore.filterRequest("%s")', requestURL); return result.slice(result.indexOf('\t') + 1); } - //console.debug('µBlock> PageStore.filterRequest(): "%s" not in cache', requestURL); + //console.debug('cache MISS: PageStore.filterRequest("%s")', requestURL); result = µb.netFilteringEngine.matchString(context, requestURL, requestType); if ( collapsibleRequestTypes.indexOf(requestType) !== -1 || µb.userSettings.logRequests ) { this.netFilteringCache.add(requestURL, requestType + '\t' + result);