diff --git a/src/js/contentscript.js b/src/js/contentscript.js index e11d868cb..7547b6e9e 100644 --- a/src/js/contentscript.js +++ b/src/js/contentscript.js @@ -233,9 +233,9 @@ var platformHideNode = vAPI.hideNode, observer, changedNodes = [], observerOptions = { - attributes: true, - attributeFilter: [ 'style' ] - }; + attributes: true, + attributeFilter: [ 'style' ] + }; // https://jsperf.com/clientheight-and-clientwidth-vs-getcomputedstyle // Avoid getComputedStyle(), detecting whether a node is visible can be @@ -489,9 +489,9 @@ var domFilterer = { hiddenNodeEnforcer: false, loggerEnabled: undefined, - newHideSelectorBuffer: [], // Hide style filter buffer - newStyleRuleBuffer: [], // Non-hide style filter buffer - simpleHideSelectors: { // Hiding filters: simple selectors + newHideSelectorBuffer: [], // Hide style filter buffer + newStyleRuleBuffer: [], // Non-hide style filter buffer + simpleHideSelectors: { // Hiding filters: simple selectors entries: [], matchesProp: vAPI.matchesProp, selector: undefined, @@ -513,7 +513,7 @@ var domFilterer = { } } }, - complexHideSelectors: { // Hiding filters: complex selectors + complexHideSelectors: { // Hiding filters: complex selectors entries: [], selector: undefined, add: function(selector) { @@ -531,13 +531,8 @@ var domFilterer = { } } }, - styleSelectors: { // Style filters - entries: [], - add: function(o) { - this.entries.push(o); - } - }, - proceduralSelectors: { // Hiding filters: procedural + nqsSelectors: [], // Non-querySelector-able filters + proceduralSelectors: { // Hiding filters: procedural entries: [], add: function(o) { this.entries.push(new PSelector(o)); @@ -578,7 +573,12 @@ var domFilterer = { var o = JSON.parse(selector); if ( o.style ) { this.newStyleRuleBuffer.push(o.style.join(' ')); - this.styleSelectors.add(o); + this.nqsSelectors.push(o.raw); + return; + } + if ( o.pseudoclass ) { + this.newHideSelectorBuffer.push(o.raw); + this.nqsSelectors.push(o.raw); return; } if ( o.tasks ) { diff --git a/src/js/cosmetic-filtering.js b/src/js/cosmetic-filtering.js index 32b13ef95..44cb62421 100644 --- a/src/js/cosmetic-filtering.js +++ b/src/js/cosmetic-filtering.js @@ -1,7 +1,7 @@ /******************************************************************************* uBlock Origin - a browser extension to block requests. - Copyright (C) 2014-2016 Raymond Hill + Copyright (C) 2014-2017 Raymond Hill This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -732,7 +732,8 @@ FilterContainer.prototype.freeze = function() { // https://github.com/gorhill/uBlock/issues/1752 FilterContainer.prototype.compileSelector = (function() { - var reStyleSelector = /^(.+?):style\((.+?)\)$/, + var reAfterBeforeSelector = /^(.+?)(::?after|::?before)$/, + reStyleSelector = /^(.+?):style\((.+?)\)$/, reStyleBad = /url\([^)]+\)/, reScriptSelector = /^script:(contains|inject)\((.+)\)$/, div = document.createElement('div'); @@ -751,17 +752,43 @@ FilterContainer.prototype.compileSelector = (function() { } // We rarely reach this point. - var matches; + var matches, + selector = raw, + pseudoclass, + style; // `:style` selector? - if ( - (matches = reStyleSelector.exec(raw)) !== null && - isValidCSSSelector(matches[1]) && - isValidStyleProperty(matches[2]) - ) { + if ( (matches = reStyleSelector.exec(selector)) !== null ) { + selector = matches[1]; + style = matches[2]; + } + + // https://github.com/gorhill/uBlock/issues/2448 + // :after- or :before-based selector? + if ( (matches = reAfterBeforeSelector.exec(selector)) ) { + selector = matches[1]; + pseudoclass = matches[2]; + } + + if ( style !== undefined || pseudoclass !== undefined ) { + if ( isValidCSSSelector(selector) === false ) { + return; + } + if ( pseudoclass !== undefined ) { + selector += pseudoclass; + } + if ( style !== undefined ) { + if ( isValidStyleProperty(style) === false ) { + return; + } + return JSON.stringify({ + raw: raw, + style: [ selector, '{' + style + '}' ] + }); + } return JSON.stringify({ raw: raw, - style: [ matches[1], '{' + matches[2] + '}' ] + pseudoclass: true }); } diff --git a/src/js/scriptlets/cosmetic-logger.js b/src/js/scriptlets/cosmetic-logger.js index efcf61105..bbbd637f7 100644 --- a/src/js/scriptlets/cosmetic-logger.js +++ b/src/js/scriptlets/cosmetic-logger.js @@ -1,7 +1,7 @@ /******************************************************************************* uBlock Origin - a browser extension to block requests. - Copyright (C) 2015-2016 Raymond Hill + Copyright (C) 2015-2017 Raymond Hill This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -51,14 +51,11 @@ vAPI.domFilterer.simpleHideSelectors.entries.forEach(evaluateSelector); // Complex CSS selector-based cosmetic filters. vAPI.domFilterer.complexHideSelectors.entries.forEach(evaluateSelector); -// Style cosmetic filters. -vAPI.domFilterer.styleSelectors.entries.forEach(function(filter) { - if ( - loggedSelectors.hasOwnProperty(filter.raw) === false && - document.querySelector(filter.style[0]) !== null - ) { - loggedSelectors[filter.raw] = true; - matchedSelectors.push(filter.raw); +// Non-querySelector-able filters. +vAPI.domFilterer.nqsSelectors.forEach(function(filter) { + if ( loggedSelectors.hasOwnProperty(filter) === false ) { + loggedSelectors[filter] = true; + matchedSelectors.push(filter); } });