| Index: include.preload.js | 
| =================================================================== | 
| --- a/include.preload.js | 
| +++ b/include.preload.js | 
| @@ -104,6 +104,139 @@ | 
| return !("isFrameWithoutContentScript" in contentDocument.defaultView); | 
| } | 
| +function traceHiddenElements(document, selectors) | 
| +{ | 
| + var changedNodes = []; | 
| + var timeout = null; | 
| + | 
| + var checkNodes = function(nodes) | 
| + { | 
| + var matchedSelectors = []; | 
| + | 
| + // Find all selectors that match any hidden element inside the given nodes. | 
| + for (var i = 0; i < selectors.length; i++) | 
| + { | 
| + var selector = selectors[i]; | 
| + | 
| + for (var j = 0; j < nodes.length; j++) | 
| + { | 
| + var elements = nodes[j].querySelectorAll(selector); | 
| + var matched = false; | 
| + | 
| + for (var k = 0; k < elements.length; k++) | 
| + { | 
| + // Only consider selectors that actually have an effect on the | 
| + // computed styles, and aren't overridden by rules with higher | 
| + // priority, or haven't been circumvented in a different way. | 
| + if (getComputedStyle(elements[k]).display == "none") | 
| + { | 
| + matchedSelectors.push(selector); | 
| + matched = true; | 
| + break; | 
| + } | 
| + } | 
| + | 
| + if (matched) | 
| + break; | 
| + } | 
| + } | 
| + | 
| + if (matchedSelectors.length > 0) | 
| + ext.backgroundPage.sendMessage({type: "trace-elemhide", selectors: matchedSelectors}); | 
| + }; | 
| + | 
| + var observer = new MutationObserver(function(mutations) | 
| + { | 
| + // Forget previously changed nodes that are no longer in the DOM. | 
| + for (var i = 0; i < changedNodes.length; i++) | 
| + { | 
| + if (!document.contains(changedNodes[i])) | 
| + changedNodes.splice(i--, 1); | 
| + } | 
| + | 
| + for (var j = 0; j < mutations.length; j++) | 
| + { | 
| + var mutation = mutations[j]; | 
| + var node = mutation.target; | 
| + | 
| + // Ignore mutations of nodes that aren't in the DOM anymore. | 
| + if (!document.contains(node)) | 
| + continue; | 
| + | 
| + // Since querySelectorAll() doesn't consider the root itself | 
| + // and since CSS selectors can also match siblings, we have | 
| + // to consider the parent node for attribute mutations. | 
| + if (mutation.type == "attributes") | 
| + node = node.parentNode; | 
| + | 
| + var addNode = true; | 
| + for (var k = 0; k < changedNodes.length; k++) | 
| + { | 
| + var previouslyChangedNode = changedNodes[k]; | 
| + | 
| + // If we are already going to check an ancestor of this node, | 
| + // we can ignore this node, since it will be considered anyway | 
| + // when checking one of its ancestors. | 
| + if (previouslyChangedNode.contains(node)) | 
| + { | 
| + addNode = false; | 
| + break; | 
| + } | 
| + | 
| + // If this node is an ancestor of a node that previously changed, | 
| + // we can ignore that node, since it will be considered anyway | 
| + // when checking one of its ancestors. | 
| + if (node.contains(previouslyChangedNode)) | 
| + changedNodes.splice(k--, 1); | 
| + } | 
| + | 
| + if (addNode) | 
| + changedNodes.push(node); | 
| + } | 
| + | 
| + // Check only nodes whose descendants have changed, and not more often | 
| + // than once a second. Otherwise large pages with a lot of DOM mutations | 
| + // (like YouTube) freeze when the devtools panel is active. | 
| + if (!timeout) | 
| + { | 
| + timeout = setTimeout(function() | 
| + { | 
| + checkNodes(changedNodes); | 
| + changedNodes = []; | 
| + timeout = null; | 
| + }, 1000); | 
| + } | 
| + }); | 
| + | 
| + var startTracing = function() | 
| + { | 
| + checkNodes([document]); | 
| + | 
| + observer.observe( | 
| + document, | 
| + { | 
| + childList: true, | 
| + attributes: true, | 
| + subtree: true | 
| + } | 
| + ); | 
| + }; | 
| + | 
| + var stopTracing = function() | 
| + { | 
| + document.removeEventListener("DOMContentLoaded", startTracing); | 
| + observer.disconnect(); | 
| + clearTimeout(timeout); | 
| + }; | 
| + | 
| + if (document.readyState == "loading") | 
| + document.addEventListener("DOMContentLoaded", startTracing); | 
| + else | 
| + startTracing(); | 
| + | 
| + return stopTracing; | 
| +} | 
| + | 
| function reinjectRulesWhenRemoved(document, style) | 
| { | 
| var MutationObserver = window.MutationObserver || window.WebKitMutationObserver; | 
| @@ -134,8 +267,9 @@ | 
| ext.backgroundPage.sendMessage( | 
| {type: "get-selectors"}, | 
| - function(selectors) | 
| + function(response) | 
| { | 
| + var selectors = response.selectors; | 
| while (selectors.length > 0) | 
| { | 
| var selector = selectors.splice(0, SELECTOR_GROUP_SIZE).join(", "); | 
| @@ -190,7 +324,9 @@ | 
| { | 
| var shadow = null; | 
| var style = null; | 
| - var observer = null; | 
| + | 
| + var reinjectObserver = null; | 
| + var stopTracing = null; | 
| // Use Shadow DOM if available to don't mess with web pages that rely on | 
| // the order of their own <style> tags (#309). | 
| @@ -208,12 +344,18 @@ | 
| var updateStylesheet = function(reinject) | 
| { | 
| - ext.backgroundPage.sendMessage({type: "get-selectors"}, function(selectors) | 
| + ext.backgroundPage.sendMessage({type: "get-selectors"}, function(response) | 
| { | 
| - if (observer) | 
| + if (reinjectObserver) | 
| { | 
| - observer.disconnect(); | 
| - observer = null; | 
| + reinjectObserver.disconnect(); | 
| + reinjectObserver = null; | 
| + } | 
| + | 
| + if (stopTracing) | 
| + { | 
| + stopTracing(); | 
| + stopTracing = null; | 
| } | 
| if (style && style.parentElement) | 
| @@ -222,6 +364,7 @@ | 
| style = null; | 
| } | 
| + var selectors = response.selectors; | 
| if (selectors.length > 0) | 
| { | 
| // Create <style> element lazily, only if we add styles. Add it to | 
| @@ -250,9 +393,12 @@ | 
| var selector = selectors.splice(0, SELECTOR_GROUP_SIZE).join(", "); | 
| style.sheet.insertRule(selector + " { display: none !important; }", i); | 
| } | 
| + | 
| + reinjectObserver = reinjectRulesWhenRemoved(document, style); | 
| + | 
| + if (response.trace) | 
| + stopTracing = traceHiddenElements(document, response.selectors); | 
| } | 
| - | 
| - observer = reinjectRulesWhenRemoved(document, style); | 
| } | 
| }); | 
| }; |