diff --git a/platform/chromium/vapi-usercss.js b/platform/chromium/vapi-usercss.js index 56072eba6..e1da31e54 100644 --- a/platform/chromium/vapi-usercss.js +++ b/platform/chromium/vapi-usercss.js @@ -32,11 +32,66 @@ if ( typeof vAPI === 'object' ) { // >>>>>>>> start of HUGE-IF-BLOCK /******************************************************************************/ /******************************************************************************/ +vAPI.userStylesheet = { + style: null, + css: new Map(), + disabled: false, + apply: function() { + }, + add: function(cssText) { + if ( cssText === '' || this.css.has(cssText) ) { return; } + if ( this.style === null ) { + this.style = document.createElement('style'); + this.style.disabled = this.disabled; + var parent = document.head || document.documentElement; + if ( parent !== null ) { + parent.appendChild(this.style); + } + } + var sheet = this.style.sheet, + i = sheet.cssRules.length; + if ( !sheet ) { return; } + sheet.insertRule(cssText, i); + this.css.set(cssText, sheet.cssRules[i]); + }, + remove: function(cssText) { + if ( cssText === '' ) { return; } + var cssRule = this.css.get(cssText); + if ( cssRule === undefined ) { return; } + this.css.delete(cssText); + if ( this.style === null ) { return; } + var rules = this.style.sheet.cssRules, + i = rules.length; + while ( i-- ) { + if ( rules[i] !== cssRule ) { continue; } + this.style.sheet.deleteRule(i); + break; + } + if ( rules.length === 0 ) { + var parent = this.style.parentNode; + if ( parent !== null ) { + parent.removeChild(this.style); + } + this.style = null; + } + }, + toggle: function(state) { + if ( state === undefined ) { state = this.disabled; } + if ( state !== this.disabled ) { return; } + this.disabled = !state; + if ( this.style !== null ) { + this.style.disabled = this.disabled; + } + } +}; + +/******************************************************************************/ + vAPI.DOMFilterer = function() { this.commitTimer = new vAPI.SafeAnimationFrame(this.commitNow.bind(this)); this.domIsReady = document.readyState !== 'loading'; this.listeners = []; - this.hideNodeId = vAPI.randomToken(); + this.hideNodeAttr = vAPI.randomToken(); this.hideNodeStylesheet = false; this.excludedNodeSet = new WeakSet(); this.addedNodes = new Set(); @@ -48,73 +103,10 @@ vAPI.DOMFilterer = function() { this.specificComplexHide = new Set(); this.specificComplexHideAggregated = undefined; this.addedSpecificComplexHide = []; - + this.specificOthers = []; this.genericSimpleHide = new Set(); this.genericComplexHide = new Set(); - this.userStylesheet = { - style: null, - css: new Map(), - disabled: false, - add: function(cssText) { - if ( cssText === '' || this.css.has(cssText) ) { return; } - if ( this.style === null ) { - this.style = document.createElement('style'); - this.style.disabled = this.disabled; - var parent = document.head || document.documentElement; - if ( parent !== null ) { - parent.appendChild(this.style); - } - } - var sheet = this.style.sheet, - i = sheet.cssRules.length; - if ( !sheet ) { return; } - sheet.insertRule(cssText, i); - this.css.set(cssText, sheet.cssRules[i]); - }, - remove: function(cssText) { - if ( cssText === '' ) { return; } - var cssRule = this.css.get(cssText); - if ( cssRule === undefined ) { return; } - this.css.delete(cssText); - if ( this.style === null ) { return; } - var rules = this.style.sheet.cssRules, - i = rules.length; - while ( i-- ) { - if ( rules[i] !== cssRule ) { continue; } - this.style.sheet.deleteRule(i); - break; - } - if ( rules.length === 0 ) { - var parent = this.style.parentNode; - if ( parent !== null ) { - parent.removeChild(this.style); - } - this.style = null; - } - }, - toggle: function(state) { - if ( state === undefined ) { state = this.disabled; } - if ( state !== this.disabled ) { return; } - this.disabled = !state; - if ( this.style !== null ) { - this.style.disabled = this.disabled; - } - }, - getAllSelectors: function() { - var out = []; - var rules = this.style && - this.style.sheet && - this.style.sheet.cssRules; - if ( rules instanceof Object === false ) { return out; } - var i = rules.length; - while ( i-- ) { - out.push(rules.item(i).selectorText); - } - return out; - } - }; - this.hideNodeExpando = undefined; this.hideNodeBatchProcessTimer = undefined; this.hiddenNodeObserver = undefined; @@ -135,7 +127,7 @@ vAPI.DOMFilterer.prototype = { commitNow: function() { this.commitTimer.clear(); - if ( this.domIsReady !== true || this.userStylesheet.disabled ) { + if ( this.domIsReady !== true || vAPI.userStylesheet.disabled ) { return; } @@ -227,28 +219,31 @@ vAPI.DOMFilterer.prototype = { if ( details === undefined ) { details = {}; } - var isGeneric= details.lazy === true, - isSimple = details.type === 'simple', - isComplex = details.type === 'complex', - selector; - var selectorsStr = Array.isArray(selectors) ? selectors.join(',\n') : selectors; if ( selectorsStr.length === 0 ) { return; } - this.userStylesheet.add( - selectorsStr + - '\n{ ' + declarations + ' }' - ); + vAPI.userStylesheet.add(selectorsStr + '\n{ ' + declarations + ' }'); this.commit(); - this.triggerListeners('declarative', selectorsStr); if ( this.reHideStyle.test(declarations) === false ) { + this.specificOthers.push({ + selectors: selectorsStr, + declarations: declarations + }); return; } + // Do not strongly enforce internal CSS rules. + if ( details.internal ) { return; } + + var isGeneric= details.lazy === true, + isSimple = details.type === 'simple', + isComplex = details.type === 'complex', + selector; + if ( isGeneric ) { if ( isSimple ) { this.genericSimpleHide.add(selectorsStr); @@ -292,31 +287,6 @@ vAPI.DOMFilterer.prototype = { } }, - removeCSSRule: function(selectors, declarations) { - var selectorsStr = Array.isArray(selectors) - ? selectors.join(',\n') - : selectors; - if ( selectorsStr.length === 0 ) { return; } - this.userStylesheet.remove( - selectorsStr + - '\n{ ' + declarations + ' }' - ); - if ( this.reHideStyle.test(declarations) === false ) { return; } - var selectorsArr = Array.isArray(selectors) ? - selectors : - selectors.split(',\n'); - for ( var selector of selectorsArr ) { - if ( this.reCSSCombinators.test(selector) ) { - this.specificComplexHide.remove(selector); - this.genericComplexHide.remove(selector); - } else { - this.specificSimpleHide.remove(selector); - this.genericSimpleHide.remove(selector); - } - } - this.commit(); - }, - onDOMCreated: function() { this.domIsReady = true; this.addedNodes.clear(); @@ -391,7 +361,7 @@ vAPI.DOMFilterer.prototype = { }, hideNodeObserverHandler: function(mutations) { - if ( this.userStylesheet.disabled ) { return; } + if ( vAPI.userStylesheet.disabled ) { return; } var i = mutations.length, stagedNodes = this.hiddenNodesetToProcess; while ( i-- ) { @@ -413,8 +383,8 @@ vAPI.DOMFilterer.prototype = { new MutationObserver(this.hideNodeObserverHandler.bind(this)); if ( this.hideNodeStylesheet === false ) { this.hideNodeStylesheet = true; - this.userStylesheet.add( - '[' + this.hideNodeId + ']\n{ display: none !important; }' + vAPI.userStylesheet.add( + '[' + this.hideNodeAttr + ']\n{ display: none !important; }' ); } }, @@ -424,12 +394,16 @@ vAPI.DOMFilterer.prototype = { this.unhideNode(node); }, + unexcludeNode: function(node) { + this.excludedNodeSet.delete(node); + }, + hideNode: function(node) { if ( this.excludedNodeSet.has(node) ) { return; } if ( this.hiddenNodeset.has(node) ) { return; } this.hiddenNodeset.add(node); if ( this.hideNodeExpando === undefined ) { this.hideNodeInit(); } - node.setAttribute(this.hideNodeId, ''); + node.setAttribute(this.hideNodeAttr, ''); if ( node[this.hideNodeExpando] === undefined ) { node[this.hideNodeExpando] = node.hasAttribute('style') && @@ -442,7 +416,7 @@ vAPI.DOMFilterer.prototype = { unhideNode: function(node) { if ( this.hiddenNodeset.has(node) === false ) { return; } - node.removeAttribute(this.hideNodeId); + node.removeAttribute(this.hideNodeAttr); this.hiddenNodesetToProcess.delete(node); if ( this.hideNodeExpando === undefined ) { return; } var attr = node[this.hideNodeExpando]; @@ -469,9 +443,9 @@ vAPI.DOMFilterer.prototype = { }, toggle: function(state) { - this.userStylesheet.toggle(state); - var disabled = this.userStylesheet.disabled, - nodes = document.querySelectorAll('[' + this.hideNodeId + ']'); + vAPI.userStylesheet.toggle(state); + var disabled = vAPI.userStylesheet.disabled, + nodes = document.querySelectorAll('[' + this.hideNodeAttr + ']'); for ( var node of nodes ) { if ( disabled ) { this.showNode(node); @@ -484,19 +458,37 @@ vAPI.DOMFilterer.prototype = { } }, + getAllDeclarativeSelectors_: function(all) { + var out = []; + if ( this.specificSimpleHide.size !== 0 ) { + out.push(Array.from(this.specificSimpleHide).join(',\n')); + } + if ( this.specificComplexHide.size !== 0 ) { + out.push(Array.from(this.specificComplexHide).join(',\n')); + } + if ( this.genericSimpleHide.size !== 0 ) { + out.push(Array.from(this.genericSimpleHide).join(',\n')); + } + if ( this.genericComplexHide.size !== 0 ) { + out.push(Array.from(this.genericComplexHide).join(',\n')); + } + if ( all ) { + out.push('[' + this.hideNodeAttr + ']'); + } + for ( var entry of this.specificOthers ) { + out.push(entry.selectors); + } + return out.join(',\n'); + }, + getFilteredElementCount: function() { - return document.querySelectorAll( - this.userStylesheet.getAllSelectors().join(',\n') - ).length; + var selectors = this.getAllDeclarativeSelectors_(true); + if ( selectors.length === 0 ) { return 0; } + return document.querySelectorAll(selectors).length; }, getAllDeclarativeSelectors: function() { - return [].concat( - Array.from(this.specificSimpleHide), - Array.from(this.specificComplexHide), - Array.from(this.genericSimpleHide), - Array.from(this.genericComplexHide) - ).join(',\n'); + return this.getAllDeclarativeSelectors_(); } }; diff --git a/platform/webext/vapi-usercss.js b/platform/webext/vapi-usercss.js index 690ab0093..46a9b64c2 100644 --- a/platform/webext/vapi-usercss.js +++ b/platform/webext/vapi-usercss.js @@ -28,75 +28,43 @@ if ( typeof vAPI === 'object' ) { // >>>>>>>> start of HUGE-IF-BLOCK /******************************************************************************/ /******************************************************************************/ +vAPI.userStylesheet = { + added: new Set(), + removed: new Set(), + apply: function() { + if ( this.added.size === 0 && this.removed.size === 0 ) { return; } + vAPI.messaging.send('vapi-background', { + what: 'userCSS', + add: Array.from(this.added), + remove: Array.from(this.removed) + }); + this.added.clear(); + this.removed.clear(); + }, + add: function(cssText, now) { + if ( cssText === '' ) { return; } + this.added.add(cssText); + if ( now ) { this.apply(); } + }, + remove: function(cssText, now) { + if ( cssText === '' ) { return; } + this.removed.add(cssText); + if ( now ) { this.apply(); } + } +}; + +/******************************************************************************/ + vAPI.DOMFilterer = function() { this.commitTimer = new vAPI.SafeAnimationFrame(this.commitNow.bind(this)); this.domIsReady = document.readyState !== 'loading'; + this.disabled = false; this.listeners = []; + this.filterset = new Set(); this.hideNodeId = vAPI.randomToken(); this.hideNodeStylesheet = false; this.excludedNodeSet = new WeakSet(); - this.addedCSSRules = []; - this.removedCSSRules = []; - this.internalRules = new Set(); - - this.userStylesheets = { - current: new Set(), - added: new Set(), - removed: new Set(), - disabled: false, - apply: function() { - for ( let cssText of this.added ) { - if ( this.current.has(cssText) || this.removed.has(cssText) ) { - this.added.delete(cssText); - } else { - this.current.add(cssText); - } - } - for ( let cssText of this.removed ) { - if ( this.current.has(cssText) === false ) { - this.removed.delete(cssText); - } else { - this.current.delete(cssText); - } - } - if ( this.added.size === 0 && this.removed.size === 0 ) { return; } - if ( this.disabled === false ) { - vAPI.messaging.send('vapi-background', { - what: 'userCSS', - add: Array.from(this.added), - remove: Array.from(this.removed) - }); - } - this.added.clear(); - this.removed.clear(); - }, - add: function(cssText) { - if ( cssText === '' ) { return; } - this.added.add(cssText); - }, - remove: function(cssText) { - if ( cssText === '' ) { return; } - this.removed.add(cssText); - }, - toggle: function(state) { - if ( state === undefined ) { state = this.disabled; } - if ( state !== this.disabled ) { return; } - this.disabled = !state; - if ( this.current.size === 0 ) { return; } - var all = Array.from(this.current); - var toAdd = [], toRemove = []; - if ( this.disabled ) { - toRemove = all; - } else { - toAdd = all; - } - vAPI.messaging.send('vapi-background', { - what: 'userCSS', - add: toAdd, - remove: toRemove - }); - } - }; + this.addedCSSRules = new Set(); if ( this.domIsReady !== true ) { document.addEventListener('DOMContentLoaded', () => { @@ -108,30 +76,27 @@ vAPI.DOMFilterer = function() { vAPI.DOMFilterer.prototype = { reOnlySelectors: /\n\{[^\n]+/g, + + // Here we will deal with: + // - Injecting low priority user styles; + // - Notifying listeners about changed filterset. commitNow: function() { this.commitTimer.clear(); - var i, entry, ruleText; - i = this.addedCSSRules.length; - while ( i-- ) { - entry = this.addedCSSRules[i]; - if ( entry.lazy !== true || this.domIsReady ) { - ruleText = entry.selectors + '\n{ ' + entry.declarations + ' }'; - this.userStylesheets.add(ruleText); - this.addedCSSRules.splice(i, 1); - if ( entry.internal ) { - this.internalRules.add(ruleText); - } + var userStylesheet = vAPI.userStylesheet, + addedSelectors = []; + for ( var entry of this.addedCSSRules ) { + if ( this.disabled === false && entry.lazy ) { + userStylesheet.add( + entry.selectors + '\n{' + entry.declarations + '}' + ); } + addedSelectors.push(entry.selectors); } - i = this.removedCSSRules.length; - while ( i-- ) { - entry = this.removedCSSRules[i]; - ruleText = entry.selectors + '\n{ ' + entry.declarations + ' }'; - this.userStylesheets.remove(ruleText); - this.internalRules.delete(ruleText); + this.addedCSSRules.clear(); + userStylesheet.apply(); + if ( addedSelectors.length !== 0 ) { + this.triggerListeners('declarative', addedSelectors.join(',\n')); } - this.removedCSSRules = []; - this.userStylesheets.apply(); }, commit: function(commitNow) { @@ -149,25 +114,17 @@ vAPI.DOMFilterer.prototype = { ? selectors.join(',\n') : selectors; if ( selectorsStr.length === 0 ) { return; } - this.addedCSSRules.push({ + var entry = { selectors: selectorsStr, declarations, - lazy: details && details.lazy === true, + lazy: details !== undefined && details.lazy === true, internal: details && details.internal === true - }); - this.commit(); - this.triggerListeners('declarative', selectorsStr); - }, - - removeCSSRule: function(selectors, declarations) { - var selectorsStr = Array.isArray(selectors) - ? selectors.join(',\n') - : selectors; - if ( selectorsStr.length === 0 ) { return; } - this.removedCSSRules.push({ - selectors: selectorsStr, - declarations, - }); + }; + this.addedCSSRules.add(entry); + this.filterset.add(entry); + if ( this.disabled === false && entry.lazy !== true ) { + vAPI.userStylesheet.add(selectorsStr + '\n{' + declarations + '}'); + } this.commit(); }, @@ -194,6 +151,10 @@ vAPI.DOMFilterer.prototype = { this.unhideNode(node); }, + unexcludeNode: function(node) { + this.excludedNodeSet.delete(node); + }, + hideNode: function(node) { if ( this.excludedNodeSet.has(node) ) { return; } node.setAttribute(this.hideNodeId, ''); @@ -212,16 +173,26 @@ vAPI.DOMFilterer.prototype = { }, toggle: function(state) { - this.userStylesheets.toggle(state); + if ( state === undefined ) { state = this.disabled; } + if ( state !== this.disabled ) { return; } + this.disabled = !state; + var userStylesheet = vAPI.userStylesheet; + for ( var entry of this.filterset ) { + var rule = entry.selectors + '\n{' + entry.declarations + '}'; + if ( this.disabled ) { + userStylesheet.remove(rule); + } else { + userStylesheet.add(rule); + } + } + userStylesheet.apply(); }, getAllDeclarativeSelectors_: function(all) { let selectors = []; - for ( var sheet of this.userStylesheets.current ) { - if ( all === false && this.internalRules.has(sheet) ) { continue; } - selectors.push( - sheet.replace(this.reOnlySelectors, ',').trim().slice(0, -1) - ); + for ( var entry of this.filterset ) { + if ( all === false && entry.internal ) { continue; } + selectors.push(entry.selectors); } return selectors.join(',\n'); }, diff --git a/src/js/contentscript.js b/src/js/contentscript.js index 297504f65..54e61205e 100644 --- a/src/js/contentscript.js +++ b/src/js/contentscript.js @@ -774,7 +774,7 @@ vAPI.domCollapser = (function() { iframeLoadEventPatch(target); } } - /* + if ( selectors.length !== 0 ) { messaging.send( 'contentscript', @@ -786,7 +786,6 @@ vAPI.domCollapser = (function() { } ); } - */ }; var send = function() { @@ -1292,25 +1291,21 @@ vAPI.domSurveyor = (function() { domFilterer.addProceduralSelectors(cfeDetails.proceduralFilters); } + if ( cfeDetails.netFilters.length !== 0 ) { + vAPI.userStylesheet.add( + cfeDetails.netFilters + '\n{ display: none !important; }'); + } + + vAPI.userStylesheet.apply(); + var parent = document.head || document.documentElement; if ( parent ) { - var elem, text; - if ( cfeDetails.netHide.length !== 0 ) { - elem = document.createElement('style'); - elem.setAttribute('type', 'text/css'); - text = cfeDetails.netHide.join(',\n'); - text += response.collapseBlocked ? - '\n{ display:none !important; }' : - '\n{ visibility:hidden !important; }'; - elem.appendChild(document.createTextNode(text)); - parent.appendChild(elem); - } // Library of resources is located at: // https://github.com/gorhill/uBlock/blob/master/assets/ublock/resources.txt if ( cfeDetails.scripts ) { // Have the injected script tag remove itself when execution completes: // to keep DOM as clean as possible. - text = cfeDetails.scripts + + var text = cfeDetails.scripts + "\n" + "(function() {\n" + " var c = document.currentScript,\n" + diff --git a/src/js/cosmetic-filtering.js b/src/js/cosmetic-filtering.js index c3d15e554..70de97e2d 100644 --- a/src/js/cosmetic-filtering.js +++ b/src/js/cosmetic-filtering.js @@ -408,7 +408,7 @@ SelectorCacheEntry.factory = function() { /******************************************************************************/ -// var netSelectorCacheLowWaterMark = 20; +var netSelectorCacheLowWaterMark = 20; var netSelectorCacheHighWaterMark = 30; /******************************************************************************/ @@ -451,8 +451,7 @@ SelectorCacheEntry.prototype.addCosmetic = function(details) { /******************************************************************************/ -SelectorCacheEntry.prototype.addNet = function(/* selectors */) { -/* +SelectorCacheEntry.prototype.addNet = function(selectors) { if ( typeof selectors === 'string' ) { this.addNetOne(selectors, Date.now()); } else { @@ -470,7 +469,6 @@ SelectorCacheEntry.prototype.addNet = function(/* selectors */) { while ( i-- ) { dict.delete(keys[i]); } -*/ }; /******************************************************************************/ @@ -515,11 +513,25 @@ SelectorCacheEntry.prototype.remove = function(type) { /******************************************************************************/ +SelectorCacheEntry.prototype.retrieveToArray = function(iterator, out) { + for ( var selector of iterator ) { + out.push(selector); + } +}; + +SelectorCacheEntry.prototype.retrieveToSet = function(iterator, out) { + for ( var selector of iterator ) { + out.add(selector); + } +}; + SelectorCacheEntry.prototype.retrieve = function(type, out) { this.lastAccessTime = Date.now(); - var dict = type === 'cosmetic' ? this.cosmetic : this.net; - for ( var selector of dict ) { - out.add(selector); + var iterator = type === 'cosmetic' ? this.cosmetic : this.net.keys(); + if ( Array.isArray(out) ) { + this.retrieveToArray(iterator, out); + } else { + this.retrieveToSet(iterator, out); } }; @@ -1964,7 +1976,8 @@ FilterContainer.prototype.retrieveDomainSelectors = function( var hostname = this.µburi.hostnameFromURI(request.locationURL), domain = this.µburi.domainFromHostname(hostname) || hostname, pos = domain.indexOf('.'), - entity = pos === -1 ? '' : domain.slice(0, pos - domain.length) + '.*'; + entity = pos === -1 ? '' : domain.slice(0, pos - domain.length) + '.*', + cacheEntry = this.selectorCache.get(hostname); // https://github.com/chrisaljoudi/uBlock/issues/587 // r.ready will tell the content script the cosmetic filtering engine is @@ -1982,9 +1995,10 @@ FilterContainer.prototype.retrieveDomainSelectors = function( exceptionFilters: [], highGenericSimple: '', highGenericComplex: '', - netHide: [], + netFilters: '', proceduralFilters: [], - scripts: undefined + scripts: undefined, + cssRulesInjected: false }; if ( options.noCosmeticFiltering !== true ) { @@ -2038,7 +2052,6 @@ FilterContainer.prototype.retrieveDomainSelectors = function( bucket.retrieve(hostname, specificSet); } // Cached cosmetic filters: these are always declarative. - var cacheEntry = this.selectorCache.get(hostname); if ( cacheEntry !== undefined ) { cacheEntry.retrieve('cosmetic', specificSet); if ( r.noDOMSurveying === false ) { @@ -2107,13 +2120,11 @@ FilterContainer.prototype.retrieveDomainSelectors = function( // Scriptlet injection. r.scripts = this.retrieveUserScripts(domain, hostname); - // TODO: Is it *really* worth to cache selectors of collapsed resources? - // This adds code complexity and I am having doubts about the - // benefits. Investigate. - // Collapsible blocked resources. - //if ( cacheEntry ) { - // cacheEntry.retrieve('net', r.netHide); - //} + if ( cacheEntry ) { + var netFilters = []; + cacheEntry.retrieve('net', netFilters); + r.netFilters = netFilters.join(',\n'); + } console.timeEnd('cosmeticFilteringEngine.retrieveDomainSelectors'); diff --git a/src/js/scriptlets/element-picker.js b/src/js/scriptlets/element-picker.js index dd15d019e..9b7abaa2b 100644 --- a/src/js/scriptlets/element-picker.js +++ b/src/js/scriptlets/element-picker.js @@ -1400,9 +1400,11 @@ var stopPicker = function() { // https://github.com/gorhill/uBlock/issues/2060 if ( vAPI.domFilterer instanceof Object ) { - vAPI.domFilterer.removeCSSRule(pickerCSSSelector1, pickerCSSDeclaration1); - vAPI.domFilterer.removeCSSRule(pickerCSSSelector2, pickerCSSDeclaration2); + vAPI.userStylesheet.remove(pickerCSS1); + vAPI.userStylesheet.remove(pickerCSS2); + vAPI.userStylesheet.apply(); } + vAPI.domFilterer.unexcludeNode(pickerRoot); window.removeEventListener('scroll', onScrolled, true); pickerRoot.contentWindow.removeEventListener('keydown', onKeyPressed, true); @@ -1546,47 +1548,46 @@ var bootstrapPicker = function() { pickerRoot = document.createElement('iframe'); pickerRoot.id = vAPI.sessionId; -var pickerCSSSelector1 = '#' + pickerRoot.id; -var pickerCSSDeclaration1 = [ - 'background: transparent', - 'border: 0', - 'border-radius: 0', - 'box-shadow: none', - 'display: block', - 'height: 100%', - 'left: 0', - 'margin: 0', - 'max-height: none', - 'max-width: none', - 'opacity: 1', - 'outline: 0', - 'padding: 0', - 'position: fixed', - 'top: 0', - 'visibility: visible', - 'width: 100%', - 'z-index: 2147483647', - '' - ].join(' !important;'); -var pickerCSSSelector2 = '[' + pickerRoot.id + '-clickblind]'; -var pickerCSSDeclaration2 = 'pointer-events: none !important;'; +var pickerCSSStyle = [ + 'background: transparent', + 'border: 0', + 'border-radius: 0', + 'box-shadow: none', + 'display: block', + 'height: 100%', + 'left: 0', + 'margin: 0', + 'max-height: none', + 'max-width: none', + 'opacity: 1', + 'outline: 0', + 'padding: 0', + 'position: fixed', + 'top: 0', + 'visibility: visible', + 'width: 100%', + 'z-index: 2147483647', + '' +].join(' !important;'); +pickerRoot.style.cssText = pickerCSSStyle; - -pickerRoot.style.cssText = pickerCSSDeclaration1; +var pickerCSS1 = [ + '#' + pickerRoot.id + ' {', + pickerCSSStyle, + '}' +].join('\n'); +var pickerCSS2 = [ + '[' + pickerRoot.id + '-clickblind] {', + 'pointer-events: none !important;', + '}' +].join('\n'); // https://github.com/gorhill/uBlock/issues/1529 // In addition to inline styles, harden the element picker styles by using // dedicated CSS rules. -vAPI.domFilterer.addCSSRule( - pickerCSSSelector1, - pickerCSSDeclaration1, - { internal: true } -); -vAPI.domFilterer.addCSSRule( - pickerCSSSelector2, - pickerCSSDeclaration2, - { internal: true } -); +vAPI.userStylesheet.add(pickerCSS1); +vAPI.userStylesheet.add(pickerCSS2); +vAPI.userStylesheet.apply(); // https://github.com/gorhill/uBlock/issues/2060 vAPI.domFilterer.excludeNode(pickerRoot);