Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Side by Side Diff: include.preload.js

Issue 6393086494113792: Issue 154 - Added devtools panel showing blocked and blockable items (Closed)
Patch Set: Rebased and fixed various issues Created March 12, 2015, 3:32 p.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « chrome/ext/devtools.js ('k') | lib/devtools.js » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * This file is part of Adblock Plus <https://adblockplus.org/>, 2 * This file is part of Adblock Plus <https://adblockplus.org/>,
3 * Copyright (C) 2006-2015 Eyeo GmbH 3 * Copyright (C) 2006-2015 Eyeo GmbH
4 * 4 *
5 * Adblock Plus is free software: you can redistribute it and/or modify 5 * Adblock Plus is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 3 as 6 * it under the terms of the GNU General Public License version 3 as
7 * published by the Free Software Foundation. 7 * published by the Free Software Foundation.
8 * 8 *
9 * Adblock Plus is distributed in the hope that it will be useful, 9 * Adblock Plus is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after
97 if (!contentDocument) 97 if (!contentDocument)
98 return false; 98 return false;
99 99
100 // Return true, if the element is a first-party frame which doesn't 100 // Return true, if the element is a first-party frame which doesn't
101 // have this function, hence our content script isn't running there. 101 // have this function, hence our content script isn't running there.
102 // Those are dynamically created frames as well as frames 102 // Those are dynamically created frames as well as frames
103 // with "about:blank", "about:srcdoc" and "javascript:" URL. 103 // with "about:blank", "about:srcdoc" and "javascript:" URL.
104 return !("isFrameWithoutContentScript" in contentDocument.defaultView); 104 return !("isFrameWithoutContentScript" in contentDocument.defaultView);
105 } 105 }
106 106
107 function traceHiddenElements(document, selectors)
108 {
109 var changedNodes = [];
110 var timeout = null;
111
112 var checkNodes = function(nodes)
113 {
114 var matchedSelectors = [];
115
116 // Find all selectors that match any hidden element inside the given nodes.
117 for (var i = 0; i < selectors.length; i++)
118 {
119 var selector = selectors[i];
120
121 for (var j = 0; j < nodes.length; j++)
122 {
123 var elements = nodes[j].querySelectorAll(selector);
124 var matched = false;
125
126 for (var k = 0; k < elements.length; k++)
127 {
128 // Only consider selectors that actually have an effect on the
129 // computed styles, and aren't overridden by rules with higher
130 // priority, or haven't been circumvented in a different way.
131 if (getComputedStyle(elements[k]).display == "none")
132 {
133 matchedSelectors.push(selector);
134 matched = true;
135 break;
136 }
137 }
138
139 if (matched)
140 break;
141 }
142 }
143
144 if (matchedSelectors.length > 0)
145 ext.backgroundPage.sendMessage({type: "trace-elemhide", selectors: matched Selectors});
146 };
147
148 var observer = new MutationObserver(function(mutations)
149 {
150 // Forget previously changed nodes that are no longer in the DOM.
151 for (var i = 0; i < changedNodes.length; i++)
152 {
153 if (!document.contains(changedNodes[i]))
154 changedNodes.splice(i--, 1);
155 }
156
157 for (var j = 0; j < mutations.length; j++)
158 {
159 var mutation = mutations[j];
160 var node = mutation.target;
161
162 // Ignore mutations of nodes that aren't in the DOM anymore.
163 if (!document.contains(node))
164 continue;
165
166 // Since querySelectorAll() doesn't consider the root itself
167 // and since CSS selectors can also match siblings, we have
168 // to consider the parent node for attribute mutations.
169 if (mutation.type == "attributes")
170 node = node.parentNode;
171
172 var addNode = true;
173 for (var k = 0; k < changedNodes.length; k++)
174 {
175 var previouslyChangedNode = changedNodes[k];
176
177 // If we are already going to check an ancestor of this node,
178 // we can ignore this node, since it will be considered anyway
179 // when checking one of its ancestors.
180 if (previouslyChangedNode.contains(node))
181 {
182 addNode = false;
183 break;
184 }
185
186 // If this node is an ancestor of a node that previously changed,
187 // we can ignore that node, since it will be considered anyway
188 // when checking one of its ancestors.
189 if (node.contains(previouslyChangedNode))
190 changedNodes.splice(k--, 1);
191 }
192
193 if (addNode)
194 changedNodes.push(node);
195 }
196
197 // Check only nodes whose descendants have changed, and not more often
198 // than once a second. Otherwise large pages with a lot of DOM mutations
199 // (like YouTube) freeze when the devtools panel is active.
200 if (!timeout)
201 {
202 timeout = setTimeout(function()
203 {
204 checkNodes(changedNodes);
205 changedNodes = [];
206 timeout = null;
207 }, 1000);
208 }
209 });
210
211 var startTracing = function()
212 {
213 checkNodes([document]);
214
215 observer.observe(
216 document,
217 {
218 childList: true,
219 attributes: true,
220 subtree: true
221 }
222 );
223 };
224
225 var stopTracing = function()
226 {
227 document.removeEventListener("DOMContentLoaded", startTracing);
228 observer.disconnect();
229 clearTimeout(timeout);
230 };
231
232 if (document.readyState == "loading")
233 document.addEventListener("DOMContentLoaded", startTracing);
234 else
235 startTracing();
236
237 return stopTracing;
238 }
239
107 function reinjectRulesWhenRemoved(document, style) 240 function reinjectRulesWhenRemoved(document, style)
108 { 241 {
109 var MutationObserver = window.MutationObserver || window.WebKitMutationObserve r; 242 var MutationObserver = window.MutationObserver || window.WebKitMutationObserve r;
110 if (!MutationObserver) 243 if (!MutationObserver)
111 return; 244 return;
112 245
113 var observer = new MutationObserver(function(mutations) 246 var observer = new MutationObserver(function(mutations)
114 { 247 {
115 var isStyleRemoved = false; 248 var isStyleRemoved = false;
116 for (var i = 0; i < mutations.length; i++) 249 for (var i = 0; i < mutations.length; i++)
(...skipping 10 matching lines...) Expand all
127 observer.disconnect(); 260 observer.disconnect();
128 261
129 var n = document.styleSheets.length; 262 var n = document.styleSheets.length;
130 if (n == 0) 263 if (n == 0)
131 return; 264 return;
132 265
133 var stylesheet = document.styleSheets[n - 1]; 266 var stylesheet = document.styleSheets[n - 1];
134 ext.backgroundPage.sendMessage( 267 ext.backgroundPage.sendMessage(
135 {type: "get-selectors"}, 268 {type: "get-selectors"},
136 269
137 function(selectors) 270 function(response)
138 { 271 {
272 var selectors = response.selectors;
139 while (selectors.length > 0) 273 while (selectors.length > 0)
140 { 274 {
141 var selector = selectors.splice(0, SELECTOR_GROUP_SIZE).join(", "); 275 var selector = selectors.splice(0, SELECTOR_GROUP_SIZE).join(", ");
142 276
143 // Using non-standard addRule() here. This is the only way 277 // Using non-standard addRule() here. This is the only way
144 // to add rules at the end of a cross-origin stylesheet 278 // to add rules at the end of a cross-origin stylesheet
145 // because we don't know how many rules are already in there 279 // because we don't know how many rules are already in there
146 stylesheet.addRule(selector, "display: none !important;"); 280 stylesheet.addRule(selector, "display: none !important;");
147 } 281 }
148 } 282 }
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
183 result.push(prefix + selector.substring(start)); 317 result.push(prefix + selector.substring(start));
184 } 318 }
185 319
186 return result; 320 return result;
187 } 321 }
188 322
189 function init(document) 323 function init(document)
190 { 324 {
191 var shadow = null; 325 var shadow = null;
192 var style = null; 326 var style = null;
193 var observer = null; 327
328 var reinjectObserver = null;
329 var stopTracing = null;
194 330
195 // Use Shadow DOM if available to don't mess with web pages that rely on 331 // Use Shadow DOM if available to don't mess with web pages that rely on
196 // the order of their own <style> tags (#309). 332 // the order of their own <style> tags (#309).
197 // 333 //
198 // However, creating a shadow root breaks running CSS transitions. So we 334 // However, creating a shadow root breaks running CSS transitions. So we
199 // have to create the shadow root before transistions might start (#452). 335 // have to create the shadow root before transistions might start (#452).
200 // 336 //
201 // Also, we can't use shadow DOM on Google Docs, since it breaks printing 337 // Also, we can't use shadow DOM on Google Docs, since it breaks printing
202 // there (#1770). 338 // there (#1770).
203 if ("createShadowRoot" in document.documentElement && document.domain != "docs .google.com") 339 if ("createShadowRoot" in document.documentElement && document.domain != "docs .google.com")
204 { 340 {
205 shadow = document.documentElement.createShadowRoot(); 341 shadow = document.documentElement.createShadowRoot();
206 shadow.appendChild(document.createElement("shadow")); 342 shadow.appendChild(document.createElement("shadow"));
207 } 343 }
208 344
209 var updateStylesheet = function(reinject) 345 var updateStylesheet = function(reinject)
210 { 346 {
211 ext.backgroundPage.sendMessage({type: "get-selectors"}, function(selectors) 347 ext.backgroundPage.sendMessage({type: "get-selectors"}, function(response)
212 { 348 {
213 if (observer) 349 if (reinjectObserver)
214 { 350 {
215 observer.disconnect(); 351 reinjectObserver.disconnect();
216 observer = null; 352 reinjectObserver = null;
353 }
354
355 if (stopTracing)
356 {
357 stopTracing();
358 stopTracing = null;
217 } 359 }
218 360
219 if (style && style.parentElement) 361 if (style && style.parentElement)
220 { 362 {
221 style.parentElement.removeChild(style); 363 style.parentElement.removeChild(style);
222 style = null; 364 style = null;
223 } 365 }
224 366
367 var selectors = response.selectors;
225 if (selectors.length > 0) 368 if (selectors.length > 0)
226 { 369 {
227 // Create <style> element lazily, only if we add styles. Add it to 370 // Create <style> element lazily, only if we add styles. Add it to
228 // the shadow DOM if possible. Otherwise fallback to the <head> or 371 // the shadow DOM if possible. Otherwise fallback to the <head> or
229 // <html> element. If we have injected a style element before that 372 // <html> element. If we have injected a style element before that
230 // has been removed (the sheet property is null), create a new one. 373 // has been removed (the sheet property is null), create a new one.
231 style = document.createElement("style"); 374 style = document.createElement("style");
232 (shadow || document.head || document.documentElement).appendChild(style) ; 375 (shadow || document.head || document.documentElement).appendChild(style) ;
233 376
234 // It can happen that the frame already navigated to a different 377 // It can happen that the frame already navigated to a different
235 // document while we were waiting for the background page to respond. 378 // document while we were waiting for the background page to respond.
236 // In that case the sheet property will stay null, after addind the 379 // In that case the sheet property will stay null, after addind the
237 // <style> element to the shadow DOM. 380 // <style> element to the shadow DOM.
238 if (style.sheet) 381 if (style.sheet)
239 { 382 {
240 // If using shadow DOM, we have to add the ::content pseudo-element 383 // If using shadow DOM, we have to add the ::content pseudo-element
241 // before each selector, in order to match elements within the 384 // before each selector, in order to match elements within the
242 // insertion point. 385 // insertion point.
243 if (shadow) 386 if (shadow)
244 selectors = convertSelectorsForShadowDOM(selectors); 387 selectors = convertSelectorsForShadowDOM(selectors);
245 388
246 // WebKit (and Blink?) apparently chokes when the selector list in a 389 // WebKit (and Blink?) apparently chokes when the selector list in a
247 // CSS rule is huge. So we split the elemhide selectors into groups. 390 // CSS rule is huge. So we split the elemhide selectors into groups.
248 for (var i = 0; selectors.length > 0; i++) 391 for (var i = 0; selectors.length > 0; i++)
249 { 392 {
250 var selector = selectors.splice(0, SELECTOR_GROUP_SIZE).join(", "); 393 var selector = selectors.splice(0, SELECTOR_GROUP_SIZE).join(", ");
251 style.sheet.insertRule(selector + " { display: none !important; }", i); 394 style.sheet.insertRule(selector + " { display: none !important; }", i);
252 } 395 }
396
397 reinjectObserver = reinjectRulesWhenRemoved(document, style);
398
399 if (response.trace)
400 stopTracing = traceHiddenElements(document, response.selectors);
253 } 401 }
254
255 observer = reinjectRulesWhenRemoved(document, style);
256 } 402 }
257 }); 403 });
258 }; 404 };
259 405
260 updateStylesheet(); 406 updateStylesheet();
261 407
262 document.addEventListener("error", function(event) 408 document.addEventListener("error", function(event)
263 { 409 {
264 checkCollapse(event.target); 410 checkCollapse(event.target);
265 }, true); 411 }, true);
(...skipping 19 matching lines...) Expand all
285 }, true); 431 }, true);
286 432
287 return updateStylesheet; 433 return updateStylesheet;
288 } 434 }
289 435
290 if (document instanceof HTMLDocument) 436 if (document instanceof HTMLDocument)
291 { 437 {
292 checkSitekey(); 438 checkSitekey();
293 window.updateStylesheet = init(document); 439 window.updateStylesheet = init(document);
294 } 440 }
OLDNEW
« no previous file with comments | « chrome/ext/devtools.js ('k') | lib/devtools.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld