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: Rebased updated dependecies and addressed review comment Created Feb. 23, 2017, 10:26 a.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 let filter = filters[i] || selectors[i];
Sebastian Noack 2017/02/23 11:08:23 This is no longer necessary. You don't have to con
wspee 2017/02/23 11:20:21 Done.
230 matched = true; 238 matchedSelectors.push(filter.replace(/^.*?##/, ""));
231 break; 239 break nodes;
232 } 240 }
233 } 241 }
234
235 if (matched)
236 break;
237 } 242 }
238 } 243 }
239 244
240 if (matchedSelectors.length > 0) 245 if (matchedSelectors.length > 0)
241 ext.backgroundPage.sendMessage({ 246 ext.backgroundPage.sendMessage({
242 type: "devtools.traceElemHide", 247 type: "devtools.traceElemHide",
243 selectors: matchedSelectors 248 selectors: matchedSelectors
244 }); 249 });
245 }, 250 },
246 251
247 onTimeout() 252 onTimeout()
248 { 253 {
249 this.checkNodes(this.changedNodes); 254 this.checkNodes(this.changedNodes, this.selectors, this.filters);
250 this.changedNodes = []; 255 this.changedNodes = [];
251 this.timeout = null; 256 this.timeout = null;
252 }, 257 },
253 258
254 observe(mutations) 259 observe(mutations)
255 { 260 {
256 // Forget previously changed nodes that are no longer in the DOM. 261 // Forget previously changed nodes that are no longer in the DOM.
257 for (let i = 0; i < this.changedNodes.length; i++) 262 for (let i = 0; i < this.changedNodes.length; i++)
258 { 263 {
259 if (!document.contains(this.changedNodes[i])) 264 if (!document.contains(this.changedNodes[i]))
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
301 306
302 // Check only nodes whose descendants have changed, and not more often 307 // 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 308 // than once a second. Otherwise large pages with a lot of DOM mutations
304 // (like YouTube) freeze when the devtools panel is active. 309 // (like YouTube) freeze when the devtools panel is active.
305 if (this.timeout == null) 310 if (this.timeout == null)
306 this.timeout = setTimeout(this.onTimeout.bind(this), 1000); 311 this.timeout = setTimeout(this.onTimeout.bind(this), 1000);
307 }, 312 },
308 313
309 trace() 314 trace()
310 { 315 {
311 this.checkNodes([document]); 316 this.checkNodes([document], this.selectors, this.filters);
312 317
313 this.observer.observe( 318 this.observer.observe(
314 document, 319 document,
315 { 320 {
316 childList: true, 321 childList: true,
317 attributes: true, 322 attributes: true,
318 subtree: true 323 subtree: true
319 } 324 }
320 ); 325 );
321 }, 326 },
(...skipping 157 matching lines...) Expand 10 before | Expand all | Expand 10 after
479 let shadow = shadowRoot(this); 484 let shadow = shadowRoot(this);
480 return shadow == ourShadowRoot ? null : shadow; 485 return shadow == ourShadowRoot ? null : shadow;
481 } 486 }
482 }); 487 });
483 }, null); 488 }, null);
484 } 489 }
485 490
486 return shadow; 491 return shadow;
487 }, 492 },
488 493
489 addSelectors(selectors) 494 addSelectors(selectors, filters)
490 { 495 {
491 if (selectors.length == 0) 496 if (selectors.length == 0)
492 return; 497 return;
493 498
494 if (!this.style) 499 if (!this.style)
495 { 500 {
496 // Create <style> element lazily, only if we add styles. Add it to 501 // Create <style> element lazily, only if we add styles. Add it to
497 // the shadow DOM if possible. Otherwise fallback to the <head> or 502 // the shadow DOM if possible. Otherwise fallback to the <head> or
498 // <html> element. If we have injected a style element before that 503 // <html> element. If we have injected a style element before that
499 // has been removed (the sheet property is null), create a new one. 504 // has been removed (the sheet property is null), create a new one.
500 this.style = document.createElement("style"); 505 this.style = document.createElement("style");
501 (this.shadow || document.head 506 (this.shadow || document.head
502 || document.documentElement).appendChild(this.style); 507 || document.documentElement).appendChild(this.style);
503 508
504 // It can happen that the frame already navigated to a different 509 // It can happen that the frame already navigated to a different
505 // document while we were waiting for the background page to respond. 510 // document while we were waiting for the background page to respond.
506 // In that case the sheet property will stay null, after addind the 511 // In that case the sheet property will stay null, after addind the
507 // <style> element to the shadow DOM. 512 // <style> element to the shadow DOM.
508 if (!this.style.sheet) 513 if (!this.style.sheet)
509 return; 514 return;
510 } 515 }
511 516
512 // If using shadow DOM, we have to add the ::content pseudo-element 517 // If using shadow DOM, we have to add the ::content pseudo-element
513 // before each selector, in order to match elements within the 518 // before each selector, in order to match elements within the
514 // insertion point. 519 // insertion point.
520 let preparedSelectors = [];
515 if (this.shadow) 521 if (this.shadow)
516 { 522 {
517 let preparedSelectors = [];
518 for (let selector of selectors) 523 for (let selector of selectors)
519 { 524 {
520 let subSelectors = splitSelector(selector); 525 let subSelectors = splitSelector(selector);
521 for (let subSelector of subSelectors) 526 for (let subSelector of subSelectors)
522 preparedSelectors.push("::content " + subSelector); 527 preparedSelectors.push("::content " + subSelector);
523 } 528 }
524 selectors = preparedSelectors; 529 }
530 else
531 {
532 preparedSelectors = selectors;
525 } 533 }
526 534
527 // Safari only allows 8192 primitive selectors to be injected at once[1], we 535 // 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. 536 // 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 537 // (Chrome also has a limit, larger... but we're not certain exactly what it
530 // is! Edge apparently has no such limit.) 538 // is! Edge apparently has no such limit.)
531 // [1] - https://github.com/WebKit/webkit/blob/1cb2227f6b2a1035f7bdc46e5ab69 debb75fc1de/Source/WebCore/css/RuleSet.h#L68 539 // [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) 540 for (let i = 0; i < preparedSelectors.length; i += this.selectorGroupSize)
533 { 541 {
534 let selector = selectors.slice(i, i + this.selectorGroupSize).join(", "); 542 let selector = preparedSelectors.slice(i, i + this.selectorGroupSize).join (", ");
535 this.style.sheet.insertRule(selector + "{display: none !important;}", 543 this.style.sheet.insertRule(selector + "{display: none !important;}",
536 this.style.sheet.cssRules.length); 544 this.style.sheet.cssRules.length);
537 } 545 }
546
547 if (this.tracer)
548 this.tracer.addSelectors(selectors, filters);
538 }, 549 },
539 550
540 apply() 551 apply()
541 { 552 {
542 let selectors = null; 553 ext.backgroundPage.sendMessage({type: "get-selectors"}, response =>
543 let elemHideEmulationLoaded = false;
544
545 let checkLoaded = function()
546 { 554 {
547 if (!selectors || !elemHideEmulationLoaded)
548 return;
549
550 if (this.tracer) 555 if (this.tracer)
551 this.tracer.disconnect(); 556 this.tracer.disconnect();
552 this.tracer = null; 557 this.tracer = null;
553 558
554 if (this.style && this.style.parentElement) 559 if (this.style && this.style.parentElement)
555 this.style.parentElement.removeChild(this.style); 560 this.style.parentElement.removeChild(this.style);
556 this.style = null; 561 this.style = null;
557 562
558 this.addSelectors(selectors.selectors); 563 if (response.trace)
564 this.tracer = new ElementHidingTracer();
565
566 this.addSelectors(response.selectors, response.selectors);
Sebastian Noack 2017/02/23 11:24:03 I wonder whether it would be nicer to handle this
wspee 2017/02/23 14:24:48 Done.
567
559 this.elemHideEmulation.apply(); 568 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
Sebastian Noack 2017/02/23 11:08:22 Nit: I'd remove the blank line here (as it was bef
wspee 2017/02/23 11:20:21 Done.
571 this.elemHideEmulation.load(() =>
572 {
573 elemHideEmulationLoaded = true;
574 checkLoaded();
575 }); 569 });
576 } 570 }
577 }; 571 };
578 572
579 if (document instanceof HTMLDocument) 573 if (document instanceof HTMLDocument)
580 { 574 {
581 checkSitekey(); 575 checkSitekey();
582 wrapWebSocket(); 576 wrapWebSocket();
583 577
584 // This variable is also used by our other content scripts, outside of the 578 // This variable is also used by our other content scripts, outside of the
585 // current scope. 579 // current scope.
586 var elemhide = new ElemHide(); 580 var elemhide = new ElemHide();
587 elemhide.apply(); 581 elemhide.apply();
588 582
589 document.addEventListener("error", event => 583 document.addEventListener("error", event =>
590 { 584 {
591 checkCollapse(event.target); 585 checkCollapse(event.target);
592 }, true); 586 }, true);
593 587
594 document.addEventListener("load", event => 588 document.addEventListener("load", event =>
595 { 589 {
596 let element = event.target; 590 let element = event.target;
597 if (/^i?frame$/.test(element.localName)) 591 if (/^i?frame$/.test(element.localName))
598 checkCollapse(element); 592 checkCollapse(element);
599 }, true); 593 }, true);
600 } 594 }
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