| LEFT | RIGHT | 
|---|
| (no file at all) |  | 
| 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-present eyeo GmbH | 3  * Copyright (C) 2006-present 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 "use strict"; | 18 "use strict"; | 
| 19 | 19 | 
| 20 const {ElemHideEmulation} = require("../../lib/content/elemHideEmulation"); | 20 const {ElemHideEmulation} = require("../../lib/content/elemHideEmulation"); | 
| 21 | 21 | 
|  | 22 const REFRESH_INTERVAL = 200; | 
|  | 23 | 
|  | 24 let testDocument = null; | 
|  | 25 | 
|  | 26 exports.setUp = function(callback) | 
|  | 27 { | 
|  | 28   let iframe = document.createElement("iframe"); | 
|  | 29   document.body.appendChild(iframe); | 
|  | 30   testDocument = iframe.contentDocument; | 
|  | 31 | 
|  | 32   callback(); | 
|  | 33 }; | 
|  | 34 | 
| 22 exports.tearDown = function(callback) | 35 exports.tearDown = function(callback) | 
| 23 { | 36 { | 
| 24   let styleElements = document.head.getElementsByTagName("style"); | 37   let iframe = testDocument.defaultView.frameElement; | 
| 25   while (styleElements.length) | 38   iframe.parentNode.removeChild(iframe); | 
| 26     styleElements[0].parentNode.removeChild(styleElements[0]); | 39   testDocument = null; | 
| 27 |  | 
| 28   let child; |  | 
| 29   while (child = document.body.firstChild) |  | 
| 30     child.parentNode.removeChild(child); |  | 
| 31 | 40 | 
| 32   callback(); | 41   callback(); | 
| 33 }; | 42 }; | 
|  | 43 | 
|  | 44 function timeout(delay) | 
|  | 45 { | 
|  | 46   return new Promise((resolve, reject) => | 
|  | 47   { | 
|  | 48     window.setTimeout(resolve, delay); | 
|  | 49   }); | 
|  | 50 } | 
| 34 | 51 | 
| 35 function unexpectedError(error) | 52 function unexpectedError(error) | 
| 36 { | 53 { | 
| 37   console.error(error); | 54   console.error(error); | 
| 38   this.ok(false, "Unexpected error: " + error); | 55   this.ok(false, "Unexpected error: " + error); | 
| 39 } | 56 } | 
| 40 | 57 | 
| 41 function expectHidden(test, element) | 58 function expectHidden(test, element) | 
| 42 { | 59 { | 
| 43   test.equal(window.getComputedStyle(element).display, "none", | 60   test.equal(window.getComputedStyle(element).display, "none", | 
| 44              "The element's display property should be set to 'none'"); | 61              "The element's display property should be set to 'none'"); | 
| 45 } | 62 } | 
| 46 | 63 | 
| 47 function expectVisible(test, element) | 64 function expectVisible(test, element) | 
| 48 { | 65 { | 
| 49   test.notEqual(window.getComputedStyle(element).display, "none", | 66   test.notEqual(window.getComputedStyle(element).display, "none", | 
| 50                 "The element's display property should not be set to 'none'"); | 67                 "The element's display property should not be set to 'none'"); | 
| 51 } | 68 } | 
| 52 | 69 | 
| 53 function findUniqueId() | 70 function findUniqueId() | 
| 54 { | 71 { | 
| 55   let id = "elemHideEmulationTest-" + Math.floor(Math.random() * 10000); | 72   let id = "elemHideEmulationTest-" + Math.floor(Math.random() * 10000); | 
| 56   if (!document.getElementById(id)) | 73   if (!testDocument.getElementById(id)) | 
| 57     return id; | 74     return id; | 
| 58   return findUniqueId(); | 75   return findUniqueId(); | 
| 59 } | 76 } | 
| 60 | 77 | 
| 61 function insertStyleRule(rule) | 78 function insertStyleRule(rule) | 
| 62 { | 79 { | 
| 63   let styleElement; | 80   let styleElement; | 
| 64   let styleElements = document.head.getElementsByTagName("style"); | 81   let styleElements = testDocument.head.getElementsByTagName("style"); | 
| 65   if (styleElements.length) | 82   if (styleElements.length) | 
| 66     styleElement = styleElements[0]; | 83     styleElement = styleElements[0]; | 
| 67   else | 84   else | 
| 68   { | 85   { | 
| 69     styleElement = document.createElement("style"); | 86     styleElement = testDocument.createElement("style"); | 
| 70     document.head.appendChild(styleElement); | 87     testDocument.head.appendChild(styleElement); | 
| 71   } | 88   } | 
| 72   styleElement.sheet.insertRule(rule, styleElement.sheet.cssRules.length); | 89   styleElement.sheet.insertRule(rule, styleElement.sheet.cssRules.length); | 
| 73 } | 90 } | 
| 74 | 91 | 
| 75 // Insert a <div> with a unique id and a CSS rule | 92 // Insert a <div> with a unique id and a CSS rule | 
| 76 // for the the selector matching the id. | 93 // for the the selector matching the id. | 
| 77 function createElementWithStyle(styleBlock, parent) | 94 function createElementWithStyle(styleBlock, parent) | 
| 78 { | 95 { | 
| 79   let element = document.createElement("div"); | 96   let element = testDocument.createElement("div"); | 
| 80   element.id = findUniqueId(); | 97   element.id = findUniqueId(); | 
| 81   if (!parent) | 98   if (!parent) | 
| 82     document.body.appendChild(element); | 99     testDocument.body.appendChild(element); | 
| 83   else | 100   else | 
| 84     parent.appendChild(element); | 101     parent.appendChild(element); | 
| 85   insertStyleRule("#" + element.id + " " + styleBlock); | 102   insertStyleRule("#" + element.id + " " + styleBlock); | 
| 86   return element; | 103   return element; | 
| 87 } | 104 } | 
| 88 | 105 | 
| 89 // Create a new ElemHideEmulation instance with @selectors. | 106 // Create a new ElemHideEmulation instance with @selectors. | 
| 90 function applyElemHideEmulation(selectors) | 107 function applyElemHideEmulation(selectors) | 
| 91 { | 108 { | 
| 92   return Promise.resolve().then(() => | 109   return Promise.resolve().then(() => | 
| 93   { | 110   { | 
| 94     let elemHideEmulation = new ElemHideEmulation( | 111     let elemHideEmulation = new ElemHideEmulation( | 
| 95       window, | 112       testDocument.defaultView, | 
| 96       callback => | 113       callback => | 
| 97       { | 114       { | 
| 98         let patterns = []; | 115         let patterns = []; | 
| 99         selectors.forEach(selector => | 116         selectors.forEach(selector => | 
| 100         { | 117         { | 
| 101           patterns.push({selector}); | 118           patterns.push({selector}); | 
| 102         }); | 119         }); | 
| 103         callback(patterns); | 120         callback(patterns); | 
| 104       }, | 121       }, | 
| 105       newSelectors => | 122       newSelectors => | 
| 106       { | 123       { | 
| 107         if (!newSelectors.length) | 124         if (!newSelectors.length) | 
| 108           return; | 125           return; | 
| 109         let selector = newSelectors.join(", "); | 126         let selector = newSelectors.join(", "); | 
| 110         insertStyleRule(selector + "{display: none !important;}"); | 127         insertStyleRule(selector + "{display: none !important;}"); | 
| 111       }, | 128       }, | 
| 112       elems => | 129       elems => | 
| 113       { | 130       { | 
| 114         for (let elem of elems) | 131         for (let elem of elems) | 
| 115           elem.style.display = "none"; | 132           elem.style.display = "none"; | 
| 116       } | 133       } | 
| 117     ); | 134     ); | 
| 118 | 135 | 
|  | 136     elemHideEmulation.MIN_INVOCATION_INTERVAL = REFRESH_INTERVAL / 2; | 
| 119     elemHideEmulation.apply(); | 137     elemHideEmulation.apply(); | 
| 120     return elemHideEmulation; | 138     return elemHideEmulation; | 
| 121   }); | 139   }); | 
| 122 } | 140 } | 
| 123 | 141 | 
| 124 exports.testVerbatimPropertySelector = function(test) | 142 exports.testVerbatimPropertySelector = function(test) | 
| 125 { | 143 { | 
| 126   let toHide = createElementWithStyle("{background-color: #000}"); | 144   let toHide = createElementWithStyle("{background-color: #000}"); | 
| 127   applyElemHideEmulation( | 145   applyElemHideEmulation( | 
| 128     [":-abp-properties(background-color: rgb(0, 0, 0))"] | 146     [":-abp-properties(background-color: rgb(0, 0, 0))"] | 
| (...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 232 | 250 | 
| 233 exports.testDynamicallyChangedProperty = function(test) | 251 exports.testDynamicallyChangedProperty = function(test) | 
| 234 { | 252 { | 
| 235   let toHide = createElementWithStyle("{}"); | 253   let toHide = createElementWithStyle("{}"); | 
| 236   applyElemHideEmulation( | 254   applyElemHideEmulation( | 
| 237     [":-abp-properties(background-color: rgb(0, 0, 0))"] | 255     [":-abp-properties(background-color: rgb(0, 0, 0))"] | 
| 238   ).then(() => | 256   ).then(() => | 
| 239   { | 257   { | 
| 240     expectVisible(test, toHide); | 258     expectVisible(test, toHide); | 
| 241     insertStyleRule("#" + toHide.id + " {background-color: #000}"); | 259     insertStyleRule("#" + toHide.id + " {background-color: #000}"); | 
| 242     return new Promise((resolve, reject) => | 260 | 
| 243     { | 261     return timeout(0); | 
| 244       // Re-evaluation will only happen after a few seconds | 262   }).then(() => | 
| 245       expectVisible(test, toHide); | 263   { | 
| 246       window.setTimeout(() => | 264     // Re-evaluation will only happen after a delay | 
| 247       { | 265     expectVisible(test, toHide); | 
| 248         expectHidden(test, toHide); | 266     return timeout(REFRESH_INTERVAL); | 
| 249         resolve(); | 267   }).then(() => | 
| 250       }, 4000); | 268   { | 
| 251     }); | 269     expectHidden(test, toHide); | 
| 252   }).catch(unexpectedError.bind(test)).then(() => test.done()); | 270   }).catch(unexpectedError.bind(test)).then(() => test.done()); | 
| 253 }; | 271 }; | 
| 254 | 272 | 
| 255 exports.testPseudoClassWithPropBeforeSelector = function(test) | 273 exports.testPseudoClassWithPropBeforeSelector = function(test) | 
| 256 { | 274 { | 
| 257   let parent = createElementWithStyle("{}"); | 275   let parent = createElementWithStyle("{}"); | 
| 258   let child = createElementWithStyle("{background-color: #000}", parent); | 276   let child = createElementWithStyle("{background-color: #000}", parent); | 
| 259   insertStyleRule(`#${child.id}::before {content: "publicite"}`); | 277   insertStyleRule(`#${child.id}::before {content: "publicite"}`); | 
| 260 | 278 | 
| 261   applyElemHideEmulation( | 279   applyElemHideEmulation( | 
| (...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 339   { | 357   { | 
| 340     expectVisible(test, parent); | 358     expectVisible(test, parent); | 
| 341     expectVisible(test, middle); | 359     expectVisible(test, middle); | 
| 342     expectVisible(test, sibling); | 360     expectVisible(test, sibling); | 
| 343     expectHidden(test, toHide); | 361     expectHidden(test, toHide); | 
| 344   }).catch(unexpectedError.bind(test)).then(() => test.done()); | 362   }).catch(unexpectedError.bind(test)).then(() => test.done()); | 
| 345 }; | 363 }; | 
| 346 | 364 | 
| 347 function runTestPseudoClassHasSelectorWithHasAndWithSuffixSibling(test, selector
     , expectations) | 365 function runTestPseudoClassHasSelectorWithHasAndWithSuffixSibling(test, selector
     , expectations) | 
| 348 { | 366 { | 
| 349   document.body.innerHTML = `<div id="parent"> | 367   testDocument.body.innerHTML = `<div id="parent"> | 
| 350       <div id="middle"> | 368       <div id="middle"> | 
| 351         <div id="middle1"><div id="inside" class="inside"></div></div> | 369         <div id="middle1"><div id="inside" class="inside"></div></div> | 
| 352       </div> | 370       </div> | 
| 353       <div id="sibling"> | 371       <div id="sibling"> | 
| 354         <div id="tohide">to hide</div> | 372         <div id="tohide">to hide</div> | 
| 355       </div> | 373       </div> | 
| 356       <div id="sibling2"> | 374       <div id="sibling2"> | 
| 357         <div id="sibling21"><div id="sibling211" class="inside"></div></div> | 375         <div id="sibling21"><div id="sibling211" class="inside"></div></div> | 
| 358       </div> | 376       </div> | 
| 359     </div>`; | 377     </div>`; | 
| 360   let elems = { | 378   let elems = { | 
| 361     parent: document.getElementById("parent"), | 379     parent: testDocument.getElementById("parent"), | 
| 362     middle: document.getElementById("middle"), | 380     middle: testDocument.getElementById("middle"), | 
| 363     inside: document.getElementById("inside"), | 381     inside: testDocument.getElementById("inside"), | 
| 364     sibling: document.getElementById("sibling"), | 382     sibling: testDocument.getElementById("sibling"), | 
| 365     sibling2: document.getElementById("sibling2"), | 383     sibling2: testDocument.getElementById("sibling2"), | 
| 366     toHide: document.getElementById("tohide") | 384     toHide: testDocument.getElementById("tohide") | 
| 367   }; | 385   }; | 
| 368 | 386 | 
| 369   insertStyleRule(".inside {}"); | 387   insertStyleRule(".inside {}"); | 
| 370 | 388 | 
| 371   applyElemHideEmulation( | 389   applyElemHideEmulation( | 
| 372     [selector] | 390     [selector] | 
| 373   ).then(() => | 391   ).then(() => | 
| 374   { | 392   { | 
| 375     for (let elem in expectations) | 393     for (let elem in expectations) | 
| 376       if (elems[elem]) | 394       if (elems[elem]) | 
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 420     sibling: true, | 438     sibling: true, | 
| 421     sibling2: true, | 439     sibling2: true, | 
| 422     toHide: true | 440     toHide: true | 
| 423   }; | 441   }; | 
| 424   runTestPseudoClassHasSelectorWithHasAndWithSuffixSibling( | 442   runTestPseudoClassHasSelectorWithHasAndWithSuffixSibling( | 
| 425     test, "div:-abp-has(> body div.inside) + div > div", expectations); | 443     test, "div:-abp-has(> body div.inside) + div > div", expectations); | 
| 426 }; | 444 }; | 
| 427 | 445 | 
| 428 exports.testPseudoClassContains = function(test) | 446 exports.testPseudoClassContains = function(test) | 
| 429 { | 447 { | 
| 430   document.body.innerHTML = `<div id="parent"> | 448   testDocument.body.innerHTML = `<div id="parent"> | 
| 431       <div id="middle"> | 449       <div id="middle"> | 
| 432         <div id="middle1"><div id="inside" class="inside"></div></div> | 450         <div id="middle1"><div id="inside" class="inside"></div></div> | 
| 433       </div> | 451       </div> | 
| 434       <div id="sibling"> | 452       <div id="sibling"> | 
| 435         <div id="tohide">to hide</div> | 453         <div id="tohide">to hide</div> | 
| 436       </div> | 454       </div> | 
| 437       <div id="sibling2"> | 455       <div id="sibling2"> | 
| 438         <div id="sibling21"><div id="sibling211" class="inside"></div></div> | 456         <div id="sibling21"><div id="sibling211" class="inside"></div></div> | 
| 439       </div> | 457       </div> | 
| 440     </div>`; | 458     </div>`; | 
| 441   let parent = document.getElementById("parent"); | 459   let parent = testDocument.getElementById("parent"); | 
| 442   let middle = document.getElementById("middle"); | 460   let middle = testDocument.getElementById("middle"); | 
| 443   let inside = document.getElementById("inside"); | 461   let inside = testDocument.getElementById("inside"); | 
| 444   let sibling = document.getElementById("sibling"); | 462   let sibling = testDocument.getElementById("sibling"); | 
| 445   let sibling2 = document.getElementById("sibling2"); | 463   let sibling2 = testDocument.getElementById("sibling2"); | 
| 446   let toHide = document.getElementById("tohide"); | 464   let toHide = testDocument.getElementById("tohide"); | 
| 447 | 465 | 
| 448   applyElemHideEmulation( | 466   applyElemHideEmulation( | 
| 449     ["#parent div:-abp-contains(to hide)"] | 467     ["#parent div:-abp-contains(to hide)"] | 
| 450   ).then(() => | 468   ).then(() => | 
| 451   { | 469   { | 
| 452     expectVisible(test, parent); | 470     expectVisible(test, parent); | 
| 453     expectVisible(test, middle); | 471     expectVisible(test, middle); | 
| 454     expectVisible(test, inside); | 472     expectVisible(test, inside); | 
| 455     expectHidden(test, sibling); | 473     expectHidden(test, sibling); | 
| 456     expectVisible(test, sibling2); | 474     expectVisible(test, sibling2); | 
| (...skipping 20 matching lines...) Expand all  Loading... | 
| 477   let child = createElementWithStyle("{}", parent); | 495   let child = createElementWithStyle("{}", parent); | 
| 478   insertStyleRule("body #" + parent.id + " > div { background-color: #000}"); | 496   insertStyleRule("body #" + parent.id + " > div { background-color: #000}"); | 
| 479   applyElemHideEmulation( | 497   applyElemHideEmulation( | 
| 480     ["div:-abp-has(:-abp-properties(background-color: rgb(0, 0, 0)))"] | 498     ["div:-abp-has(:-abp-properties(background-color: rgb(0, 0, 0)))"] | 
| 481   ).then(() => | 499   ).then(() => | 
| 482   { | 500   { | 
| 483     expectVisible(test, child); | 501     expectVisible(test, child); | 
| 484     expectHidden(test, parent); | 502     expectHidden(test, parent); | 
| 485   }).catch(unexpectedError.bind(test)).then(() => test.done()); | 503   }).catch(unexpectedError.bind(test)).then(() => test.done()); | 
| 486 }; | 504 }; | 
|  | 505 | 
|  | 506 exports.testDomUpdatesStyle = function(test) | 
|  | 507 { | 
|  | 508   let parent = createElementWithStyle("{}"); | 
|  | 509   let child = createElementWithStyle("{}", parent); | 
|  | 510   applyElemHideEmulation( | 
|  | 511     ["div:-abp-has(:-abp-properties(background-color: rgb(0, 0, 0)))"] | 
|  | 512   ).then(() => | 
|  | 513   { | 
|  | 514     expectVisible(test, child); | 
|  | 515     expectVisible(test, parent); | 
|  | 516 | 
|  | 517     insertStyleRule("body #" + parent.id + " > div { background-color: #000}"); | 
|  | 518     return timeout(0); | 
|  | 519   }).then(() => | 
|  | 520   { | 
|  | 521     expectVisible(test, child); | 
|  | 522     expectVisible(test, parent); | 
|  | 523     return timeout(REFRESH_INTERVAL); | 
|  | 524   }).then(() => | 
|  | 525   { | 
|  | 526     expectVisible(test, child); | 
|  | 527     expectHidden(test, parent); | 
|  | 528   }).catch(unexpectedError.bind(test)).then(() => test.done()); | 
|  | 529 }; | 
|  | 530 | 
|  | 531 exports.testDomUpdatesContent = function(test) | 
|  | 532 { | 
|  | 533   let parent = createElementWithStyle("{}"); | 
|  | 534   let child = createElementWithStyle("{}", parent); | 
|  | 535   applyElemHideEmulation( | 
|  | 536     ["div > div:-abp-contains(hide me)"] | 
|  | 537   ).then(() => | 
|  | 538   { | 
|  | 539     expectVisible(test, parent); | 
|  | 540     expectVisible(test, child); | 
|  | 541 | 
|  | 542     child.textContent = "hide me"; | 
|  | 543     return timeout(0); | 
|  | 544   }).then(() => | 
|  | 545   { | 
|  | 546     expectVisible(test, parent); | 
|  | 547     expectVisible(test, child); | 
|  | 548     return timeout(REFRESH_INTERVAL); | 
|  | 549   }).then(() => | 
|  | 550   { | 
|  | 551     expectVisible(test, parent); | 
|  | 552     expectHidden(test, child); | 
|  | 553   }).catch(unexpectedError.bind(test)).then(() => test.done()); | 
|  | 554 }; | 
|  | 555 | 
|  | 556 exports.testDomUpdatesNewElement = function(test) | 
|  | 557 { | 
|  | 558   let parent = createElementWithStyle("{}"); | 
|  | 559   let child = createElementWithStyle("{ background-color: #000}", parent); | 
|  | 560   let sibling; | 
|  | 561   let child2; | 
|  | 562   applyElemHideEmulation( | 
|  | 563     ["div:-abp-has(:-abp-properties(background-color: rgb(0, 0, 0)))"] | 
|  | 564   ).then(() => | 
|  | 565   { | 
|  | 566     expectHidden(test, parent); | 
|  | 567     expectVisible(test, child); | 
|  | 568 | 
|  | 569     sibling = createElementWithStyle("{}"); | 
|  | 570     return timeout(0); | 
|  | 571   }).then(() => | 
|  | 572   { | 
|  | 573     expectHidden(test, parent); | 
|  | 574     expectVisible(test, child); | 
|  | 575     expectVisible(test, sibling); | 
|  | 576 | 
|  | 577     return timeout(REFRESH_INTERVAL); | 
|  | 578   }).then(() => | 
|  | 579   { | 
|  | 580     expectHidden(test, parent); | 
|  | 581     expectVisible(test, child); | 
|  | 582     expectVisible(test, sibling); | 
|  | 583 | 
|  | 584     child2 = createElementWithStyle("{ background-color: #000}", | 
|  | 585                                     sibling); | 
|  | 586     return timeout(0); | 
|  | 587   }).then(() => | 
|  | 588   { | 
|  | 589     expectVisible(test, child2); | 
|  | 590     return timeout(REFRESH_INTERVAL); | 
|  | 591   }).then(() => | 
|  | 592   { | 
|  | 593     expectHidden(test, parent); | 
|  | 594     expectVisible(test, child); | 
|  | 595     expectHidden(test, sibling); | 
|  | 596     expectVisible(test, child2); | 
|  | 597   }).catch(unexpectedError.bind(test)).then(() => test.done()); | 
|  | 598 }; | 
| LEFT | RIGHT | 
|---|