| OLD | NEW | 
|---|
| 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  Loading... | 
| 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  Loading... | 
| 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  Loading... | 
| 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 } | 
| OLD | NEW | 
|---|