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

Delta Between Two Patch Sets: include.preload.js

Issue 6393086494113792: Issue 154 - Added devtools panel showing blocked and blockable items (Closed)
Left Patch Set: Rebased and fixed various issues Created March 12, 2015, 3:32 p.m.
Right Patch Set: Adapt for UI changes generating domain specific filters when necessary Created Feb. 3, 2016, 10:40 a.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
« no previous file with change/comment | « dependencies ('k') | lib/devtools.js » ('j') | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
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-2016 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
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details. 12 * GNU General Public License for more details.
13 * 13 *
14 * You should have received a copy of the GNU General Public License 14 * You should have received a copy of the GNU General Public License
15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. 15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
16 */ 16 */
17 17
18 var SELECTOR_GROUP_SIZE = 20; 18 var SELECTOR_GROUP_SIZE = 20;
19 19
20 var typeMap = { 20 var typeMap = {
21 "img": "IMAGE", 21 "img": "IMAGE",
22 "input": "IMAGE", 22 "input": "IMAGE",
23 "picture": "IMAGE", 23 "picture": "IMAGE",
24 "audio": "MEDIA", 24 "audio": "MEDIA",
25 "video": "MEDIA", 25 "video": "MEDIA",
26 "frame": "SUBDOCUMENT", 26 "frame": "SUBDOCUMENT",
27 "iframe": "SUBDOCUMENT", 27 "iframe": "SUBDOCUMENT",
28 "object": "OBJECT", 28 "object": "OBJECT",
29 "embed": "OBJECT" 29 "embed": "OBJECT"
30 }; 30 };
31 31
32 function getURLsFromObjectElement(element)
33 {
34 var url = element.getAttribute("data");
35 if (url)
36 return [url];
37
38 for (var i = 0; i < element.children.length; i++)
39 {
40 var child = element.children[i];
41 if (child.localName != "param")
42 continue;
43
44 var name = child.getAttribute("name");
45 if (name != "movie" && // Adobe Flash
46 name != "source" && // Silverlight
47 name != "src" && // Real Media + Quicktime
48 name != "FileName") // Windows Media
49 continue;
50
51 var value = child.getAttribute("value");
52 if (!value)
53 continue;
54
55 return [value];
56 }
57
58 return [];
59 }
60
61 function getURLsFromAttributes(element)
62 {
63 var urls = [];
64
65 if (element.src)
66 urls.push(element.src);
67
68 if (element.srcset)
69 {
70 var candidates = element.srcset.split(",");
71 for (var i = 0; i < candidates.length; i++)
72 {
73 var url = candidates[i].trim().replace(/\s+\S+$/, "");
74 if (url)
75 urls.push(url);
76 }
77 }
78
79 return urls;
80 }
81
82 function getURLsFromMediaElement(element)
83 {
84 var urls = getURLsFromAttributes(element);
85
86 for (var i = 0; i < element.children.length; i++)
87 {
88 var child = element.children[i];
89 if (child.localName == "source" || child.localName == "track")
90 urls.push.apply(urls, getURLsFromAttributes(child));
91 }
92
93 if (element.poster)
94 urls.push(element.poster);
95
96 return urls;
97 }
98
99 function getURLsFromElement(element)
100 {
101 var urls;
102 switch (element.localName)
103 {
104 case "object":
105 urls = getURLsFromObjectElement(element);
106 break;
107
108 case "video":
109 case "audio":
110 case "picture":
111 urls = getURLsFromMediaElement(element);
112 break;
113
114 default:
115 urls = getURLsFromAttributes(element);
116 break;
117 }
118
119 for (var i = 0; i < urls.length; i++)
120 {
121 if (/^(?!https?:)[\w-]+:/i.test(urls[i]))
122 urls.splice(i--, 1);
123 }
124
125 return urls;
126 }
127
32 function checkCollapse(element) 128 function checkCollapse(element)
33 { 129 {
34 var tag = element.localName; 130 var tag = element.localName;
35 if (tag in typeMap) 131 if (tag in typeMap)
36 { 132 {
37 // This element failed loading, did we block it? 133 // This element failed loading, did we block it?
38 var url = element.src; 134 var urls = getURLsFromElement(element);
39 if (!url || !/^https?:/i.test(url)) 135 if (urls.length == 0)
40 return; 136 return;
41 137
42 ext.backgroundPage.sendMessage( 138 ext.backgroundPage.sendMessage(
43 { 139 {
44 type: "should-collapse", 140 type: "should-collapse",
45 url: url, 141 urls: urls,
46 mediatype: typeMap[tag] 142 mediatype: typeMap[tag],
143 baseURL: document.location.href
47 }, 144 },
48 145
49 function(response) 146 function(response)
50 { 147 {
51 if (response && element.parentNode) 148 if (response && element.parentNode)
52 { 149 {
53 var property = "display"; 150 var property = "display";
54 var value = "none"; 151 var value = "none";
55 152
56 // <frame> cannot be removed, doing that will mess up the frameset 153 // <frame> cannot be removed, doing that will mess up the frameset
57 if (tag == "frame") 154 if (tag == "frame")
58 { 155 {
59 property = "visibility"; 156 property = "visibility";
60 value = "hidden"; 157 value = "hidden";
61 } 158 }
62 159
63 // <input type="image"> elements try to load their image again 160 // <input type="image"> elements try to load their image again
64 // when the "display" CSS property is set. So we have to check 161 // when the "display" CSS property is set. So we have to check
65 // that it isn't already collapsed to avoid an infinite recursion. 162 // that it isn't already collapsed to avoid an infinite recursion.
66 if (element.style.getPropertyValue(property) != value || 163 if (element.style.getPropertyValue(property) != value ||
67 element.style.getPropertyPriority(property) != "important") 164 element.style.getPropertyPriority(property) != "important")
68 element.style.setProperty(property, value, "important"); 165 element.style.setProperty(property, value, "important");
69 } 166 }
70 } 167 }
71 ); 168 );
72 } 169 }
170
171 window.collapsing = true;
73 } 172 }
74 173
75 function checkSitekey() 174 function checkSitekey()
76 { 175 {
77 var attr = document.documentElement.getAttribute("data-adblockkey"); 176 var attr = document.documentElement.getAttribute("data-adblockkey");
78 if (attr) 177 if (attr)
79 ext.backgroundPage.sendMessage({type: "add-sitekey", token: attr}); 178 ext.backgroundPage.sendMessage({type: "add-sitekey", token: attr});
80 } 179 }
81 180
82 function isFrameWithoutContentScript(element) 181 function getContentDocument(element)
83 { 182 {
84 var contentDocument;
85 try 183 try
86 { 184 {
87 contentDocument = element.contentDocument; 185 return element.contentDocument;
88 } 186 }
89 catch (e) 187 catch (e)
90 { 188 {
91 // This is a third-party frame. Hence we can't access it. 189 return null;
92 // But that's fine, our content script should already run there. 190 }
93 return false; 191 }
94 } 192
95 193 function ElementHidingTracer(document, selectors)
96 // The element isn't a <frame>, <iframe> or <object> with "data" attribute. 194 {
97 if (!contentDocument) 195 this.document = document;
98 return false; 196 this.selectors = selectors;
99 197
100 // Return true, if the element is a first-party frame which doesn't 198 this.changedNodes = [];
101 // have this function, hence our content script isn't running there. 199 this.timeout = null;
102 // Those are dynamically created frames as well as frames 200
103 // with "about:blank", "about:srcdoc" and "javascript:" URL. 201 this.observer = new MutationObserver(this.observe.bind(this));
104 return !("isFrameWithoutContentScript" in contentDocument.defaultView); 202 this.trace = this.trace.bind(this);
105 } 203
106 204 if (document.readyState == "loading")
107 function traceHiddenElements(document, selectors) 205 document.addEventListener("DOMContentLoaded", this.trace);
108 { 206 else
109 var changedNodes = []; 207 this.trace();
110 var timeout = null; 208 }
111 209 ElementHidingTracer.prototype = {
112 var checkNodes = function(nodes) 210 checkNodes: function(nodes)
113 { 211 {
114 var matchedSelectors = []; 212 var matchedSelectors = [];
115 213
116 // Find all selectors that match any hidden element inside the given nodes. 214 // Find all selectors that match any hidden element inside the given nodes.
117 for (var i = 0; i < selectors.length; i++) 215 for (var i = 0; i < this.selectors.length; i++)
118 { 216 {
119 var selector = selectors[i]; 217 var selector = this.selectors[i];
120 218
121 for (var j = 0; j < nodes.length; j++) 219 for (var j = 0; j < nodes.length; j++)
122 { 220 {
123 var elements = nodes[j].querySelectorAll(selector); 221 var elements = nodes[j].querySelectorAll(selector);
124 var matched = false; 222 var matched = false;
125 223
126 for (var k = 0; k < elements.length; k++) 224 for (var k = 0; k < elements.length; k++)
127 { 225 {
128 // Only consider selectors that actually have an effect on the 226 // Only consider selectors that actually have an effect on the
129 // computed styles, and aren't overridden by rules with higher 227 // computed styles, and aren't overridden by rules with higher
130 // priority, or haven't been circumvented in a different way. 228 // priority, or haven't been circumvented in a different way.
131 if (getComputedStyle(elements[k]).display == "none") 229 if (getComputedStyle(elements[k]).display == "none")
132 { 230 {
133 matchedSelectors.push(selector); 231 matchedSelectors.push(selector);
134 matched = true; 232 matched = true;
135 break; 233 break;
136 } 234 }
137 } 235 }
138 236
139 if (matched) 237 if (matched)
140 break; 238 break;
141 } 239 }
142 } 240 }
143 241
144 if (matchedSelectors.length > 0) 242 if (matchedSelectors.length > 0)
145 ext.backgroundPage.sendMessage({type: "trace-elemhide", selectors: matched Selectors}); 243 ext.backgroundPage.sendMessage({
146 }; 244 type: "trace-elemhide",
147 245 selectors: matchedSelectors
148 var observer = new MutationObserver(function(mutations) 246 });
247 },
248
249 onTimeout: function()
250 {
251 this.checkNodes(this.changedNodes);
252 this.changedNodes = [];
253 this.timeout = null;
254 },
255
256 observe: function(mutations)
149 { 257 {
150 // Forget previously changed nodes that are no longer in the DOM. 258 // Forget previously changed nodes that are no longer in the DOM.
151 for (var i = 0; i < changedNodes.length; i++) 259 for (var i = 0; i < this.changedNodes.length; i++)
152 { 260 {
153 if (!document.contains(changedNodes[i])) 261 if (!this.document.contains(this.changedNodes[i]))
154 changedNodes.splice(i--, 1); 262 this.changedNodes.splice(i--, 1);
155 } 263 }
156 264
157 for (var j = 0; j < mutations.length; j++) 265 for (var j = 0; j < mutations.length; j++)
158 { 266 {
159 var mutation = mutations[j]; 267 var mutation = mutations[j];
160 var node = mutation.target; 268 var node = mutation.target;
161 269
162 // Ignore mutations of nodes that aren't in the DOM anymore. 270 // Ignore mutations of nodes that aren't in the DOM anymore.
163 if (!document.contains(node)) 271 if (!this.document.contains(node))
164 continue; 272 continue;
165 273
166 // Since querySelectorAll() doesn't consider the root itself 274 // Since querySelectorAll() doesn't consider the root itself
167 // and since CSS selectors can also match siblings, we have 275 // and since CSS selectors can also match siblings, we have
168 // to consider the parent node for attribute mutations. 276 // to consider the parent node for attribute mutations.
169 if (mutation.type == "attributes") 277 if (mutation.type == "attributes")
170 node = node.parentNode; 278 node = node.parentNode;
171 279
172 var addNode = true; 280 var addNode = true;
173 for (var k = 0; k < changedNodes.length; k++) 281 for (var k = 0; k < this.changedNodes.length; k++)
174 { 282 {
175 var previouslyChangedNode = changedNodes[k]; 283 var previouslyChangedNode = this.changedNodes[k];
176 284
177 // If we are already going to check an ancestor of this node, 285 // If we are already going to check an ancestor of this node,
178 // we can ignore this node, since it will be considered anyway 286 // we can ignore this node, since it will be considered anyway
179 // when checking one of its ancestors. 287 // when checking one of its ancestors.
180 if (previouslyChangedNode.contains(node)) 288 if (previouslyChangedNode.contains(node))
181 { 289 {
182 addNode = false; 290 addNode = false;
183 break; 291 break;
184 } 292 }
185 293
186 // If this node is an ancestor of a node that previously changed, 294 // If this node is an ancestor of a node that previously changed,
187 // we can ignore that node, since it will be considered anyway 295 // we can ignore that node, since it will be considered anyway
188 // when checking one of its ancestors. 296 // when checking one of its ancestors.
189 if (node.contains(previouslyChangedNode)) 297 if (node.contains(previouslyChangedNode))
190 changedNodes.splice(k--, 1); 298 this.changedNodes.splice(k--, 1);
191 } 299 }
192 300
193 if (addNode) 301 if (addNode)
194 changedNodes.push(node); 302 this.changedNodes.push(node);
195 } 303 }
196 304
197 // Check only nodes whose descendants have changed, and not more often 305 // 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 306 // than once a second. Otherwise large pages with a lot of DOM mutations
199 // (like YouTube) freeze when the devtools panel is active. 307 // (like YouTube) freeze when the devtools panel is active.
200 if (!timeout) 308 if (this.timeout == null)
201 { 309 this.timeout = setTimeout(this.onTimeout.bind(this), 1000);
202 timeout = setTimeout(function() 310 },
203 { 311
204 checkNodes(changedNodes); 312 trace: function()
205 changedNodes = []; 313 {
206 timeout = null; 314 this.checkNodes([this.document]);
207 }, 1000); 315
208 } 316 this.observer.observe(
209 }); 317 this.document,
210
211 var startTracing = function()
212 {
213 checkNodes([document]);
214
215 observer.observe(
216 document,
217 { 318 {
218 childList: true, 319 childList: true,
219 attributes: true, 320 attributes: true,
220 subtree: true 321 subtree: true
221 } 322 }
222 ); 323 );
223 }; 324 },
224 325
225 var stopTracing = function() 326 disconnect: function()
226 { 327 {
227 document.removeEventListener("DOMContentLoaded", startTracing); 328 this.document.removeEventListener("DOMContentLoaded", this.trace);
228 observer.disconnect(); 329 this.observer.disconnect();
229 clearTimeout(timeout); 330 clearTimeout(this.timeout);
230 }; 331 }
231 332 };
232 if (document.readyState == "loading")
233 document.addEventListener("DOMContentLoaded", startTracing);
234 else
235 startTracing();
236
237 return stopTracing;
238 }
239 333
240 function reinjectRulesWhenRemoved(document, style) 334 function reinjectRulesWhenRemoved(document, style)
241 { 335 {
242 var MutationObserver = window.MutationObserver || window.WebKitMutationObserve r; 336 var MutationObserver = window.MutationObserver || window.WebKitMutationObserve r;
243 if (!MutationObserver) 337 if (!MutationObserver)
244 return; 338 return;
245 339
246 var observer = new MutationObserver(function(mutations) 340 var observer = new MutationObserver(function(mutations)
247 { 341 {
248 var isStyleRemoved = false; 342 var isStyleRemoved = false;
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
288 } 382 }
289 383
290 function convertSelectorsForShadowDOM(selectors) 384 function convertSelectorsForShadowDOM(selectors)
291 { 385 {
292 var result = []; 386 var result = [];
293 var prefix = "::content "; 387 var prefix = "::content ";
294 388
295 for (var i = 0; i < selectors.length; i++) 389 for (var i = 0; i < selectors.length; i++)
296 { 390 {
297 var selector = selectors[i]; 391 var selector = selectors[i];
392 if (selector.indexOf(",") == -1)
393 {
394 result.push(prefix + selector);
395 continue;
396 }
397
298 var start = 0; 398 var start = 0;
299 var sep = ""; 399 var sep = "";
300
301 for (var j = 0; j < selector.length; j++) 400 for (var j = 0; j < selector.length; j++)
302 { 401 {
303 var chr = selector[j]; 402 var chr = selector[j];
304 if (chr == "\\") 403 if (chr == "\\")
305 j++; 404 j++;
306 else if (chr == sep) 405 else if (chr == sep)
307 sep = ""; 406 sep = "";
308 else if (chr == '"' || chr == "'") 407 else if (sep == "")
309 sep = chr; 408 {
310 else if (chr == "," && sep == "") 409 if (chr == '"' || chr == "'")
311 { 410 sep = chr;
312 result.push(prefix + selector.substring(start, j)); 411 else if (chr == ",")
313 start = j + 1; 412 {
413 result.push(prefix + selector.substring(start, j));
414 start = j + 1;
415 }
314 } 416 }
315 } 417 }
316 418
317 result.push(prefix + selector.substring(start)); 419 result.push(prefix + selector.substring(start));
318 } 420 }
319 421
320 return result; 422 return result;
321 } 423 }
322 424
323 function init(document) 425 function init(document)
324 { 426 {
325 var shadow = null; 427 var shadow = null;
326 var style = null; 428 var style = null;
327 429 var observer = null;
328 var reinjectObserver = null; 430 var tracer = null;
329 var stopTracing = null; 431 var propertyFilters = new CSSPropertyFilters(window, addElemHideSelectors);
330 432
331 // Use Shadow DOM if available to don't mess with web pages that rely on 433 // Use Shadow DOM if available to don't mess with web pages that rely on
332 // the order of their own <style> tags (#309). 434 // the order of their own <style> tags (#309).
333 // 435 //
334 // However, creating a shadow root breaks running CSS transitions. So we 436 // However, creating a shadow root breaks running CSS transitions. So we
335 // have to create the shadow root before transistions might start (#452). 437 // have to create the shadow root before transistions might start (#452).
336 // 438 //
337 // Also, we can't use shadow DOM on Google Docs, since it breaks printing 439 // Also, using shadow DOM causes issues on some Google websites,
338 // there (#1770). 440 // including Google Docs and Gmail (#1770, #2602).
339 if ("createShadowRoot" in document.documentElement && document.domain != "docs .google.com") 441 if ("createShadowRoot" in document.documentElement && !/\.google\.com$/.test(d ocument.domain))
340 { 442 {
341 shadow = document.documentElement.createShadowRoot(); 443 shadow = document.documentElement.createShadowRoot();
342 shadow.appendChild(document.createElement("shadow")); 444 shadow.appendChild(document.createElement("shadow"));
343 } 445 }
344 446
345 var updateStylesheet = function(reinject) 447 function addElemHideSelectors(selectors)
346 { 448 {
449 if (selectors.length == 0)
450 return;
451
452 if (!style)
453 {
454 // Create <style> element lazily, only if we add styles. Add it to
455 // the shadow DOM if possible. Otherwise fallback to the <head> or
456 // <html> element. If we have injected a style element before that
457 // has been removed (the sheet property is null), create a new one.
458 style = document.createElement("style");
459 (shadow || document.head || document.documentElement).appendChild(style);
460
461 // It can happen that the frame already navigated to a different
462 // document while we were waiting for the background page to respond.
463 // In that case the sheet property will stay null, after addind the
464 // <style> element to the shadow DOM.
465 if (!style.sheet)
466 return;
467
468 observer = reinjectRulesWhenRemoved(document, style);
469 }
470
471 // If using shadow DOM, we have to add the ::content pseudo-element
472 // before each selector, in order to match elements within the
473 // insertion point.
474 if (shadow)
475 selectors = convertSelectorsForShadowDOM(selectors);
476
477 // WebKit (and Blink?) apparently chokes when the selector list in a
478 // CSS rule is huge. So we split the elemhide selectors into groups.
479 while (selectors.length > 0)
480 {
481 var selector = selectors.splice(0, SELECTOR_GROUP_SIZE).join(", ");
482 style.sheet.addRule(selector, "display: none !important;");
483 }
484 };
485
486 var updateStylesheet = function()
487 {
488 var selectors = null;
489 var CSSPropertyFiltersLoaded = false;
490
491 var checkLoaded = function()
492 {
493 if (!selectors || !CSSPropertyFiltersLoaded)
494 return;
495
496 if (observer)
497 observer.disconnect();
498 observer = null;
499
500 if (tracer)
501 tracer.disconnect();
502 tracer = null;
503
504 if (style && style.parentElement)
505 style.parentElement.removeChild(style);
506 style = null;
507
508 addElemHideSelectors(selectors.selectors);
509 propertyFilters.apply();
510
511 if (selectors.trace)
512 tracer = new ElementHidingTracer(document, selectors.selectors);
513 };
514
347 ext.backgroundPage.sendMessage({type: "get-selectors"}, function(response) 515 ext.backgroundPage.sendMessage({type: "get-selectors"}, function(response)
348 { 516 {
349 if (reinjectObserver) 517 selectors = response;
350 { 518 checkLoaded();
351 reinjectObserver.disconnect(); 519 });
352 reinjectObserver = null; 520
353 } 521 propertyFilters.load(function()
354 522 {
355 if (stopTracing) 523 CSSPropertyFiltersLoaded = true;
356 { 524 checkLoaded();
357 stopTracing();
358 stopTracing = null;
359 }
360
361 if (style && style.parentElement)
362 {
363 style.parentElement.removeChild(style);
364 style = null;
365 }
366
367 var selectors = response.selectors;
368 if (selectors.length > 0)
369 {
370 // Create <style> element lazily, only if we add styles. Add it to
371 // the shadow DOM if possible. Otherwise fallback to the <head> or
372 // <html> element. If we have injected a style element before that
373 // has been removed (the sheet property is null), create a new one.
374 style = document.createElement("style");
375 (shadow || document.head || document.documentElement).appendChild(style) ;
376
377 // It can happen that the frame already navigated to a different
378 // document while we were waiting for the background page to respond.
379 // In that case the sheet property will stay null, after addind the
380 // <style> element to the shadow DOM.
381 if (style.sheet)
382 {
383 // If using shadow DOM, we have to add the ::content pseudo-element
384 // before each selector, in order to match elements within the
385 // insertion point.
386 if (shadow)
387 selectors = convertSelectorsForShadowDOM(selectors);
388
389 // WebKit (and Blink?) apparently chokes when the selector list in a
390 // CSS rule is huge. So we split the elemhide selectors into groups.
391 for (var i = 0; selectors.length > 0; i++)
392 {
393 var selector = selectors.splice(0, SELECTOR_GROUP_SIZE).join(", ");
394 style.sheet.insertRule(selector + " { display: none !important; }", i);
395 }
396
397 reinjectObserver = reinjectRulesWhenRemoved(document, style);
398
399 if (response.trace)
400 stopTracing = traceHiddenElements(document, response.selectors);
401 }
402 }
403 }); 525 });
404 }; 526 };
405 527
406 updateStylesheet(); 528 updateStylesheet();
407 529
408 document.addEventListener("error", function(event) 530 document.addEventListener("error", function(event)
409 { 531 {
410 checkCollapse(event.target); 532 checkCollapse(event.target);
411 }, true); 533 }, true);
412 534
413 document.addEventListener("load", function(event) 535 document.addEventListener("load", function(event)
414 { 536 {
415 var element = event.target; 537 var element = event.target;
416 538
417 if (/^i?frame$/.test(element.localName)) 539 if (/^i?frame$/.test(element.localName))
418 checkCollapse(element); 540 checkCollapse(element);
419 541
420 // prior to Chrome 37, content scripts cannot run on about:blank, 542 if (/\bChrome\//.test(navigator.userAgent))
421 // about:srcdoc and javascript: URLs. Moreover, as of Chrome 40 543 {
422 // "load" and "error" events aren't dispatched there. So we have 544 var contentDocument = getContentDocument(element);
423 // to apply element hiding and collapsing from the parent frame. 545 if (contentDocument)
424 if (/\bChrome\//.test(navigator.userAgent) && isFrameWithoutContentScript(el ement)) 546 {
425 { 547 var contentWindow = contentDocument.defaultView;
426 init(element.contentDocument); 548 if (contentDocument instanceof contentWindow.HTMLDocument)
427 549 {
428 for (var tagName in typeMap) 550 // Prior to Chrome 37, content scripts cannot run in
429 Array.prototype.forEach.call(element.contentDocument.getElementsByTagNam e(tagName), checkCollapse); 551 // dynamically created frames. Also on Chrome 37-40
552 // document_start content scripts (like this one) don't
553 // run either in those frames due to https://crbug.com/416907.
554 // So we have to apply element hiding from the parent frame.
555 if (!("init" in contentWindow))
556 init(contentDocument);
557
558 // Moreover, "load" and "error" events aren't dispatched for elements
559 // in dynamically created frames due to https://crbug.com/442107.
560 // So we also have to apply element collpasing from the parent frame.
561 if (!contentWindow.collapsing)
562 Array.prototype.forEach.call(
563 contentDocument.querySelectorAll(Object.keys(typeMap).join(",")),
564 checkCollapse
565 );
566 }
567 }
430 } 568 }
431 }, true); 569 }, true);
432 570
433 return updateStylesheet; 571 return updateStylesheet;
434 } 572 }
435 573
436 if (document instanceof HTMLDocument) 574 if (document instanceof HTMLDocument)
437 { 575 {
438 checkSitekey(); 576 checkSitekey();
439 window.updateStylesheet = init(document); 577 window.updateStylesheet = init(document);
440 } 578 }
LEFTRIGHT

Powered by Google App Engine
This is Rietveld