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

Side by Side Diff: include.preload.js

Issue 29370970: [adblockpluschrome] Issue 3596 - Added support for CSS property filters to devtools panel (Closed)
Patch Set: Addressed review comment Created Feb. 23, 2017, 2:23 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 | « dependencies ('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-2016 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
(...skipping 173 matching lines...) Expand 10 before | Expand all | Expand 10 after
184 try 184 try
185 { 185 {
186 return element.contentDocument; 186 return element.contentDocument;
187 } 187 }
188 catch (e) 188 catch (e)
189 { 189 {
190 return null; 190 return null;
191 } 191 }
192 } 192 }
193 193
194 function ElementHidingTracer(selectors) 194 function ElementHidingTracer()
195 { 195 {
196 this.selectors = selectors; 196 this.selectors = [];
197 this.filters = [];
197 198
198 this.changedNodes = []; 199 this.changedNodes = [];
199 this.timeout = null; 200 this.timeout = null;
200 201
201 this.observer = new MutationObserver(this.observe.bind(this)); 202 this.observer = new MutationObserver(this.observe.bind(this));
202 this.trace = this.trace.bind(this); 203 this.trace = this.trace.bind(this);
203 204
204 if (document.readyState == "loading") 205 if (document.readyState == "loading")
205 document.addEventListener("DOMContentLoaded", this.trace); 206 document.addEventListener("DOMContentLoaded", this.trace);
206 else 207 else
207 this.trace(); 208 this.trace();
208 } 209 }
209 ElementHidingTracer.prototype = { 210 ElementHidingTracer.prototype = {
210 checkNodes(nodes) 211 addSelectors(selectors, filters)
212 {
213 if (document.readyState != "loading")
214 this.checkNodes([document], selectors, filters);
215
216 this.selectors.push(...selectors);
217 this.filters.push(...filters);
218 },
219
220 checkNodes(nodes, selectors, filters)
211 { 221 {
212 let matchedSelectors = []; 222 let matchedSelectors = [];
213 223
214 // Find all selectors that match any hidden element inside the given nodes. 224 for (let i = 0; i < selectors.length; i++)
215 for (let selector of this.selectors)
216 { 225 {
217 for (let node of nodes) 226 nodes: for (let node of nodes)
218 { 227 {
219 let elements = node.querySelectorAll(selector); 228 let elements = node.querySelectorAll(selectors[i]);
220 let matched = false;
221 229
222 for (let element of elements) 230 for (let element of elements)
223 { 231 {
224 // Only consider selectors that actually have an effect on the 232 // Only consider selectors that actually have an effect on the
225 // computed styles, and aren't overridden by rules with higher 233 // computed styles, and aren't overridden by rules with higher
226 // priority, or haven't been circumvented in a different way. 234 // priority, or haven't been circumvented in a different way.
227 if (getComputedStyle(element).display == "none") 235 if (getComputedStyle(element).display == "none")
228 { 236 {
229 matchedSelectors.push(selector); 237 matchedSelectors.push(filters[i].replace(/^.*?##/, ""));
230 matched = true; 238 break nodes;
231 break;
232 } 239 }
233 } 240 }
234
235 if (matched)
236 break;
237 } 241 }
238 } 242 }
239 243
240 if (matchedSelectors.length > 0) 244 if (matchedSelectors.length > 0)
241 ext.backgroundPage.sendMessage({ 245 ext.backgroundPage.sendMessage({
242 type: "devtools.traceElemHide", 246 type: "devtools.traceElemHide",
243 selectors: matchedSelectors 247 selectors: matchedSelectors
244 }); 248 });
245 }, 249 },
246 250
247 onTimeout() 251 onTimeout()
248 { 252 {
249 this.checkNodes(this.changedNodes); 253 this.checkNodes(this.changedNodes, this.selectors, this.filters);
250 this.changedNodes = []; 254 this.changedNodes = [];
251 this.timeout = null; 255 this.timeout = null;
252 }, 256 },
253 257
254 observe(mutations) 258 observe(mutations)
255 { 259 {
256 // Forget previously changed nodes that are no longer in the DOM. 260 // Forget previously changed nodes that are no longer in the DOM.
257 for (let i = 0; i < this.changedNodes.length; i++) 261 for (let i = 0; i < this.changedNodes.length; i++)
258 { 262 {
259 if (!document.contains(this.changedNodes[i])) 263 if (!document.contains(this.changedNodes[i]))
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
301 305
302 // Check only nodes whose descendants have changed, and not more often 306 // Check only nodes whose descendants have changed, and not more often
303 // than once a second. Otherwise large pages with a lot of DOM mutations 307 // than once a second. Otherwise large pages with a lot of DOM mutations
304 // (like YouTube) freeze when the devtools panel is active. 308 // (like YouTube) freeze when the devtools panel is active.
305 if (this.timeout == null) 309 if (this.timeout == null)
306 this.timeout = setTimeout(this.onTimeout.bind(this), 1000); 310 this.timeout = setTimeout(this.onTimeout.bind(this), 1000);
307 }, 311 },
308 312
309 trace() 313 trace()
310 { 314 {
311 this.checkNodes([document]); 315 this.checkNodes([document], this.selectors, this.filters);
312 316
313 this.observer.observe( 317 this.observer.observe(
314 document, 318 document,
315 { 319 {
316 childList: true, 320 childList: true,
317 attributes: true, 321 attributes: true,
318 subtree: true 322 subtree: true
319 } 323 }
320 ); 324 );
321 }, 325 },
(...skipping 157 matching lines...) Expand 10 before | Expand all | Expand 10 after
479 let shadow = shadowRoot(this); 483 let shadow = shadowRoot(this);
480 return shadow == ourShadowRoot ? null : shadow; 484 return shadow == ourShadowRoot ? null : shadow;
481 } 485 }
482 }); 486 });
483 }, null); 487 }, null);
484 } 488 }
485 489
486 return shadow; 490 return shadow;
487 }, 491 },
488 492
489 addSelectors(selectors) 493 addSelectors(selectors, filters)
490 { 494 {
491 if (selectors.length == 0) 495 if (selectors.length == 0)
492 return; 496 return;
493 497
494 if (!this.style) 498 if (!this.style)
495 { 499 {
496 // Create <style> element lazily, only if we add styles. Add it to 500 // Create <style> element lazily, only if we add styles. Add it to
497 // the shadow DOM if possible. Otherwise fallback to the <head> or 501 // the shadow DOM if possible. Otherwise fallback to the <head> or
498 // <html> element. If we have injected a style element before that 502 // <html> element. If we have injected a style element before that
499 // has been removed (the sheet property is null), create a new one. 503 // has been removed (the sheet property is null), create a new one.
500 this.style = document.createElement("style"); 504 this.style = document.createElement("style");
501 (this.shadow || document.head 505 (this.shadow || document.head
502 || document.documentElement).appendChild(this.style); 506 || document.documentElement).appendChild(this.style);
503 507
504 // It can happen that the frame already navigated to a different 508 // It can happen that the frame already navigated to a different
505 // document while we were waiting for the background page to respond. 509 // document while we were waiting for the background page to respond.
506 // In that case the sheet property will stay null, after addind the 510 // In that case the sheet property will stay null, after addind the
507 // <style> element to the shadow DOM. 511 // <style> element to the shadow DOM.
508 if (!this.style.sheet) 512 if (!this.style.sheet)
509 return; 513 return;
510 } 514 }
511 515
512 // If using shadow DOM, we have to add the ::content pseudo-element 516 // If using shadow DOM, we have to add the ::content pseudo-element
513 // before each selector, in order to match elements within the 517 // before each selector, in order to match elements within the
514 // insertion point. 518 // insertion point.
519 let preparedSelectors = [];
515 if (this.shadow) 520 if (this.shadow)
516 { 521 {
517 let preparedSelectors = [];
518 for (let selector of selectors) 522 for (let selector of selectors)
519 { 523 {
520 let subSelectors = splitSelector(selector); 524 let subSelectors = splitSelector(selector);
521 for (let subSelector of subSelectors) 525 for (let subSelector of subSelectors)
522 preparedSelectors.push("::content " + subSelector); 526 preparedSelectors.push("::content " + subSelector);
523 } 527 }
524 selectors = preparedSelectors; 528 }
529 else
530 {
531 preparedSelectors = selectors;
525 } 532 }
526 533
527 // Safari only allows 8192 primitive selectors to be injected at once[1], we 534 // Safari only allows 8192 primitive selectors to be injected at once[1], we
528 // therefore chunk the inserted selectors into groups of 200 to be safe. 535 // therefore chunk the inserted selectors into groups of 200 to be safe.
529 // (Chrome also has a limit, larger... but we're not certain exactly what it 536 // (Chrome also has a limit, larger... but we're not certain exactly what it
530 // is! Edge apparently has no such limit.) 537 // is! Edge apparently has no such limit.)
531 // [1] - https://github.com/WebKit/webkit/blob/1cb2227f6b2a1035f7bdc46e5ab69 debb75fc1de/Source/WebCore/css/RuleSet.h#L68 538 // [1] - https://github.com/WebKit/webkit/blob/1cb2227f6b2a1035f7bdc46e5ab69 debb75fc1de/Source/WebCore/css/RuleSet.h#L68
532 for (let i = 0; i < selectors.length; i += this.selectorGroupSize) 539 for (let i = 0; i < preparedSelectors.length; i += this.selectorGroupSize)
533 { 540 {
534 let selector = selectors.slice(i, i + this.selectorGroupSize).join(", "); 541 let selector = preparedSelectors.slice(i, i + this.selectorGroupSize).join (", ");
535 this.style.sheet.insertRule(selector + "{display: none !important;}", 542 this.style.sheet.insertRule(selector + "{display: none !important;}",
536 this.style.sheet.cssRules.length); 543 this.style.sheet.cssRules.length);
537 } 544 }
545
546 if (this.tracer)
547 this.tracer.addSelectors(selectors, filters || selectors);
538 }, 548 },
539 549
540 apply() 550 apply()
541 { 551 {
542 let selectors = null; 552 ext.backgroundPage.sendMessage({type: "get-selectors"}, response =>
543 let elemHideEmulationLoaded = false;
544
545 let checkLoaded = function()
546 { 553 {
547 if (!selectors || !elemHideEmulationLoaded)
548 return;
549
550 if (this.tracer) 554 if (this.tracer)
551 this.tracer.disconnect(); 555 this.tracer.disconnect();
552 this.tracer = null; 556 this.tracer = null;
553 557
554 if (this.style && this.style.parentElement) 558 if (this.style && this.style.parentElement)
555 this.style.parentElement.removeChild(this.style); 559 this.style.parentElement.removeChild(this.style);
556 this.style = null; 560 this.style = null;
557 561
558 this.addSelectors(selectors.selectors); 562 if (response.trace)
563 this.tracer = new ElementHidingTracer();
564
565 this.addSelectors(response.selectors);
559 this.elemHideEmulation.apply(); 566 this.elemHideEmulation.apply();
560
561 if (selectors.trace)
562 this.tracer = new ElementHidingTracer(selectors.selectors);
563 }.bind(this);
564
565 ext.backgroundPage.sendMessage({type: "get-selectors"}, response =>
566 {
567 selectors = response;
568 checkLoaded();
569 });
570
571 this.elemHideEmulation.load(() =>
572 {
573 elemHideEmulationLoaded = true;
574 checkLoaded();
575 }); 567 });
576 } 568 }
577 }; 569 };
578 570
579 if (document instanceof HTMLDocument) 571 if (document instanceof HTMLDocument)
580 { 572 {
581 checkSitekey(); 573 checkSitekey();
582 wrapWebSocket(); 574 wrapWebSocket();
583 575
584 // This variable is also used by our other content scripts, outside of the 576 // This variable is also used by our other content scripts, outside of the
585 // current scope. 577 // current scope.
586 var elemhide = new ElemHide(); 578 var elemhide = new ElemHide();
587 elemhide.apply(); 579 elemhide.apply();
588 580
589 document.addEventListener("error", event => 581 document.addEventListener("error", event =>
590 { 582 {
591 checkCollapse(event.target); 583 checkCollapse(event.target);
592 }, true); 584 }, true);
593 585
594 document.addEventListener("load", event => 586 document.addEventListener("load", event =>
595 { 587 {
596 let element = event.target; 588 let element = event.target;
597 if (/^i?frame$/.test(element.localName)) 589 if (/^i?frame$/.test(element.localName))
598 checkCollapse(element); 590 checkCollapse(element);
599 }, true); 591 }, true);
600 } 592 }
OLDNEW
« no previous file with comments | « dependencies ('k') | lib/devtools.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld