diff --git a/platform/firefox/frameModule.js b/platform/firefox/frameModule.js index 77f99a561..66c75fa90 100644 --- a/platform/firefox/frameModule.js +++ b/platform/firefox/frameModule.js @@ -384,23 +384,34 @@ var contentObserver = { svc.scriptloader.loadSubScript(script, sandbox); }; - sandbox.injectCSS = function(sheetURI) { + let canUserStyles = (function() { try { - let wu = win.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils); - wu.loadSheetUsingURIString(sheetURI, wu.USER_SHEET); + return win.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowUtils) + .loadSheetUsingURIString instanceof Function; } catch(ex) { } - }; + return false; + })(); - sandbox.removeCSS = function(sheetURI) { - try { - let wu = win.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils); - wu.removeSheetUsingURIString(sheetURI, wu.USER_SHEET); - } catch (ex) { - } - }; + if ( canUserStyles ) { + sandbox.injectCSS = function(sheetURI) { + try { + let wu = win.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowUtils); + wu.loadSheetUsingURIString(sheetURI, wu.USER_SHEET); + } catch(ex) { + } + }; + sandbox.removeCSS = function(sheetURI) { + try { + let wu = win.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowUtils); + wu.removeSheetUsingURIString(sheetURI, wu.USER_SHEET); + } catch (ex) { + } + }; + } sandbox.topContentScript = win === win.top; diff --git a/platform/firefox/vapi-client.js b/platform/firefox/vapi-client.js index c9f8ccf12..b34f2183a 100644 --- a/platform/firefox/vapi-client.js +++ b/platform/firefox/vapi-client.js @@ -505,6 +505,7 @@ if ( self.injectCSS ) { return state ? this._load() : this._unload(); } }; + vAPI.hideNode = vAPI.unhideNode = function(){}; } /******************************************************************************/ diff --git a/src/js/contentscript.js b/src/js/contentscript.js index 6106d6a33..ae0a99303 100644 --- a/src/js/contentscript.js +++ b/src/js/contentscript.js @@ -126,10 +126,6 @@ vAPI.domFilterer = (function() { /******************************************************************************/ -var shadowId = document.documentElement.shadowRoot !== undefined ? - vAPI.randomToken(): - undefined; - var jobQueue = [ { t: 'css-hide', _0: [] }, // to inject in style tag { t: 'css-style', _0: [] }, // to inject in style tag @@ -165,6 +161,87 @@ var cosmeticFiltersActivated = function() { /******************************************************************************/ +// If a platform does not provide its own (improved) vAPI.hideNode, we assign +// a default one to try to override author styles as best as can be. + +var platformHideNode = vAPI.hideNode, + platformUnhideNode = vAPI.unhideNode; + +(function() { + if ( platformHideNode instanceof Function ) { + return; + } + + var uid, + timerId, + observer, + changedNodes = []; + var observerOptions = { + attributes: true, + attributeFilter: [ 'style' ] + }; + + var overrideInlineStyle = function(node) { + var style = window.getComputedStyle(node), + display = style.getPropertyValue('display'), + attr = node.getAttribute('style') || ''; + if ( node[uid] === undefined ) { + node[uid] = node.hasAttribute('style') && attr; + } + if ( display !== '' && display !== 'none' ) { + if ( attr !== '' ) { attr += '; '; } + node.setAttribute('style', attr + 'display: none !important;'); + } + }; + + var timerHandler = function() { + timerId = undefined; + var nodes = changedNodes, + i = nodes.length, node; + while ( i-- ) { + node = nodes[i]; + if ( node[uid] !== undefined ) { + overrideInlineStyle(node); + } + } + nodes.length = 0; + }; + + var observerHandler = function(mutations) { + var i = mutations.length; + while ( i-- ) { + changedNodes.push(mutations[i].target); + } + if ( timerId === undefined ) { + timerId = vAPI.setTimeout(timerHandler, 1); + } + }; + + platformHideNode = function(node) { + if ( uid === undefined ) { + uid = vAPI.randomToken(); + } + overrideInlineStyle(node); + if ( observer === undefined ) { + observer = new MutationObserver(observerHandler); + } + observer.observe(node, observerOptions); + }; + + platformUnhideNode = function(node) { + if ( uid === undefined ) { return; } + var attr = node[uid]; + if ( attr === false ) { + node.removeAttribute('style'); + } else if ( typeof attr === 'string' ) { + node.setAttribute('style', attr); + } + delete node[uid]; + }; +})(); + +/******************************************************************************/ + var runSimpleSelectorJob = function(job, root, fn) { if ( job._1 === undefined ) { job._1 = job._0.join(cssNotHiddenId + ','); @@ -515,30 +592,7 @@ var domFilterer = { this.hiddenNodeCount += 1; node.hidden = true; node[this.hiddenId] = null; - var style = window.getComputedStyle(node), - display = style.getPropertyValue('display'); - if ( display !== '' && display !== 'none' ) { - var styleAttr = node.getAttribute('style') || ''; - node[this.hiddenId] = node.hasAttribute('style') && styleAttr; - if ( styleAttr !== '' ) { styleAttr += '; '; } - node.setAttribute('style', styleAttr + 'display: none !important;'); - } - if ( shadowId === undefined ) { return; } - var shadow = node.shadowRoot; - if ( shadow ) { - if ( shadow[shadowId] && shadow.firstElementChild !== null ) { - shadow.removeChild(shadow.firstElementChild); - } - return; - } - // https://github.com/gorhill/uBlock/pull/555 - // Not all nodes can be shadowed: - // https://github.com/w3c/webcomponents/issues/102 - try { - shadow = node.createShadowRoot(); - shadow[shadowId] = true; - } catch (ex) { - } + platformHideNode(node); }, init: function() { @@ -561,19 +615,7 @@ var domFilterer = { showNode: function(node) { node.hidden = false; - var styleAttr = node[this.hiddenId]; - if ( styleAttr === false ) { - node.removeAttribute('style'); - } else if ( typeof styleAttr === 'string' ) { - node.setAttribute('style', node[this.hiddenId]); - } - var shadow = node.shadowRoot; - if ( shadow && shadow[shadowId] ) { - if ( shadow.firstElementChild !== null ) { - shadow.removeChild(shadow.firstElementChild); - } - shadow.appendChild(document.createElement('content')); - } + platformUnhideNode(node); }, toggleLogging: function(state) { @@ -601,27 +643,12 @@ var domFilterer = { node.removeAttribute(this.hiddenId); node[this.hiddenId] = undefined; node.hidden = false; - var shadow = node.shadowRoot; - if ( shadow && shadow[shadowId] ) { - if ( shadow.firstElementChild !== null ) { - shadow.removeChild(shadow.firstElementChild); - } - shadow.appendChild(document.createElement('content')); - } + platformUnhideNode(node); }, unshowNode: function(node) { node.hidden = true; - var styleAttr = node[this.hiddenId]; - if ( styleAttr === false ) { - node.setAttribute('style', 'display: none !important;'); - } else if ( typeof styleAttr === 'string' ) { - node.setAttribute('style', node[this.hiddenId] + '; display: none !important;'); - } - var shadow = node.shadowRoot; - if ( shadow && shadow[shadowId] && shadow.firstElementChild !== null ) { - shadow.removeChild(shadow.firstElementChild); - } + platformHideNode(node); }, domChangedHandler: function(addedNodes, removedNodes) {