| LEFT | RIGHT | 
|---|
| 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 {filterToRegExp, splitSelector} = require("../common"); | 20 const {textToRegExp, filterToRegExp, splitSelector} = require("../common"); | 
| 21 | 21 | 
| 22 let MIN_INVOCATION_INTERVAL = 3000; | 22 let MIN_INVOCATION_INTERVAL = 3000; | 
| 23 const MAX_SYNCHRONOUS_PROCESSING_TIME = 50; | 23 const MAX_SYNCHRONOUS_PROCESSING_TIME = 50; | 
| 24 const abpSelectorRegexp = /:-abp-([\w-]+)\(/i; | 24 const abpSelectorRegexp = /:-abp-([\w-]+)\(/i; | 
| 25 | 25 | 
| 26 /** Return position of node from parent. | 26 /** Return position of node from parent. | 
| 27  * @param {Node} node the node to find the position of. | 27  * @param {Node} node the node to find the position of. | 
| 28  * @return {number} One-based index like for :nth-child(), or 0 on error. | 28  * @return {number} One-based index like for :nth-child(), or 0 on error. | 
| 29  */ | 29  */ | 
| 30 function positionInParent(node) | 30 function positionInParent(node) | 
| (...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 115     let priority = rule.style.getPropertyPriority(property); | 115     let priority = rule.style.getPropertyPriority(property); | 
| 116     styles.push(`${property}: ${value}${priority ? " !" + priority : ""};`); | 116     styles.push(`${property}: ${value}${priority ? " !" + priority : ""};`); | 
| 117   } | 117   } | 
| 118   styles.sort(); | 118   styles.sort(); | 
| 119   return { | 119   return { | 
| 120     style: styles.join(" "), | 120     style: styles.join(" "), | 
| 121     subSelectors: splitSelector(rule.selectorText) | 121     subSelectors: splitSelector(rule.selectorText) | 
| 122   }; | 122   }; | 
| 123 } | 123 } | 
| 124 | 124 | 
|  | 125 let scopeSupported = null; | 
|  | 126 | 
|  | 127 function tryQuerySelector(subtree, selector, all) | 
|  | 128 { | 
|  | 129   let elements = null; | 
|  | 130   try | 
|  | 131   { | 
|  | 132     elements = all ? subtree.querySelectorAll(selector) : | 
|  | 133       subtree.querySelector(selector); | 
|  | 134     scopeSupported = true; | 
|  | 135   } | 
|  | 136   catch (e) | 
|  | 137   { | 
|  | 138     // Edge doesn't support ":scope" | 
|  | 139     scopeSupported = false; | 
|  | 140   } | 
|  | 141   return elements; | 
|  | 142 } | 
|  | 143 | 
| 125 /** | 144 /** | 
| 126  * Check if a text argument is a regexp. | 145  * Query selector. If it is relative, will try :scope. | 
| 127  * @param {string} text argument. | 146  * @param {Node} subtree the element to query selector | 
| 128  * @return {string} an unescaped RegExp string. null if it is not a | 147  * @param {string} selector the selector to query | 
| 129  * regexp, ie not surrounded by '/'. | 148  * @param {bool} [all=false] true to perform querySelectorAll() | 
|  | 149  * @returns {?(Node|NodeList)} result of the query. null in case of error. | 
| 130  */ | 150  */ | 
| 131 function checkRegExpParameter(text) | 151 function scopedQuerySelector(subtree, selector, all) | 
| 132 { | 152 { | 
| 133   let regexpString = null; | 153   if (selector[0] == ">") | 
| 134   if (text.length >= 2 && text[0] == "/" && | 154   { | 
| 135       text[text.length - 1] == "/") | 155     selector = ":scope" + selector; | 
| 136   { | 156     if (scopeSupported) | 
| 137     regexpString = text.slice(1, -1) | 157     { | 
| 138       .replace("\\7B ", "{").replace("\\7D ", "}"); | 158       return all ? subtree.querySelectorAll(selector) : | 
| 139   } | 159         subtree.querySelector(selector); | 
| 140   return regexpString; | 160     } | 
|  | 161     if (scopeSupported == null) | 
|  | 162       return tryQuerySelector(subtree, selector, all); | 
|  | 163     return null; | 
|  | 164   } | 
|  | 165   return all ? subtree.querySelectorAll(selector) : | 
|  | 166     subtree.querySelector(selector); | 
|  | 167 } | 
|  | 168 | 
|  | 169 function scopedQuerySelectorAll(subtree, selector) | 
|  | 170 { | 
|  | 171   return scopedQuerySelector(subtree, selector, true); | 
|  | 172 } | 
|  | 173 | 
|  | 174 const regexpRegexp = /^\/(.*)\/([im]*)$/; | 
|  | 175 | 
|  | 176 /** | 
|  | 177  * Make a regular expression from a text argument. If it can be parsed as a | 
|  | 178  * regular expression, parse it and the flags. | 
|  | 179  * @param {string} text the text argument. | 
|  | 180  * @return {?RegExp} a RegExp object or null in case of error. | 
|  | 181  */ | 
|  | 182 function makeRegExpParameter(text) | 
|  | 183 { | 
|  | 184   let [, pattern, flags] = | 
|  | 185       regexpRegexp.exec(text) || [undefined, textToRegExp(text)]; | 
|  | 186 | 
|  | 187   try | 
|  | 188   { | 
|  | 189     return new RegExp(pattern, flags); | 
|  | 190   } | 
|  | 191   catch (e) | 
|  | 192   { | 
|  | 193   } | 
|  | 194   return null; | 
| 141 } | 195 } | 
| 142 | 196 | 
| 143 function* evaluate(chain, index, prefix, subtree, styles) | 197 function* evaluate(chain, index, prefix, subtree, styles) | 
| 144 { | 198 { | 
| 145   if (index >= chain.length) | 199   if (index >= chain.length) | 
| 146   { | 200   { | 
| 147     yield prefix; | 201     yield prefix; | 
| 148     return; | 202     return; | 
| 149   } | 203   } | 
| 150   for (let [selector, element] of | 204   for (let [selector, element] of | 
| (...skipping 23 matching lines...) Expand all  Loading... | 
| 174    * @param {Node} subtree the subtree we work on. | 228    * @param {Node} subtree the subtree we work on. | 
| 175    * @param {StringifiedStyle[]} styles the stringified style objects. | 229    * @param {StringifiedStyle[]} styles the stringified style objects. | 
| 176    */ | 230    */ | 
| 177   *getSelectors(prefix, subtree, styles) | 231   *getSelectors(prefix, subtree, styles) | 
| 178   { | 232   { | 
| 179     yield [prefix + this._selector, subtree]; | 233     yield [prefix + this._selector, subtree]; | 
| 180   } | 234   } | 
| 181 }; | 235 }; | 
| 182 | 236 | 
| 183 const incompletePrefixRegexp = /[\s>+~]$/; | 237 const incompletePrefixRegexp = /[\s>+~]$/; | 
| 184 const relativeSelectorRegexp = /^[>+~]/; |  | 
| 185 | 238 | 
| 186 function HasSelector(selectors) | 239 function HasSelector(selectors) | 
| 187 { | 240 { | 
| 188   this._innerSelectors = selectors; | 241   this._innerSelectors = selectors; | 
| 189 } | 242 } | 
| 190 | 243 | 
| 191 HasSelector.prototype = { | 244 HasSelector.prototype = { | 
| 192   requiresHiding: true, | 245   requiresHiding: true, | 
| 193 | 246 | 
| 194   get dependsOnStyles() | 247   get dependsOnStyles() | 
| (...skipping 10 matching lines...) Expand all  Loading... | 
| 205   /** | 258   /** | 
| 206    * Generator function returning selected elements. | 259    * Generator function returning selected elements. | 
| 207    * @param {string} prefix the prefix for the selector. | 260    * @param {string} prefix the prefix for the selector. | 
| 208    * @param {Node} subtree the subtree we work on. | 261    * @param {Node} subtree the subtree we work on. | 
| 209    * @param {StringifiedStyle[]} styles the stringified style objects. | 262    * @param {StringifiedStyle[]} styles the stringified style objects. | 
| 210    */ | 263    */ | 
| 211   *getElements(prefix, subtree, styles) | 264   *getElements(prefix, subtree, styles) | 
| 212   { | 265   { | 
| 213     let actualPrefix = (!prefix || incompletePrefixRegexp.test(prefix)) ? | 266     let actualPrefix = (!prefix || incompletePrefixRegexp.test(prefix)) ? | 
| 214         prefix + "*" : prefix; | 267         prefix + "*" : prefix; | 
| 215     let elements = subtree.querySelectorAll(actualPrefix); | 268     let elements = scopedQuerySelectorAll(subtree, actualPrefix); | 
| 216     for (let element of elements) | 269     if (elements) | 
| 217     { | 270     { | 
| 218       let iter = evaluate(this._innerSelectors, 0, "", element, styles); | 271       for (let element of elements) | 
| 219       for (let selector of iter) | 272       { | 
| 220       { | 273         let iter = evaluate(this._innerSelectors, 0, "", element, styles); | 
| 221         if (selector == null) | 274         for (let selector of iter) | 
| 222         { | 275         { | 
| 223           yield null; | 276           if (selector == null) | 
| 224           continue; | 277             yield null; | 
| 225         } | 278           else if (scopedQuerySelector(element, selector)) | 
| 226         if (relativeSelectorRegexp.test(selector)) |  | 
| 227           selector = ":scope" + selector; |  | 
| 228         try |  | 
| 229         { |  | 
| 230           if (element.querySelector(selector)) |  | 
| 231             yield element; | 279             yield element; | 
| 232         } | 280         } | 
| 233         catch (e) | 281         yield null; | 
| 234         { |  | 
| 235           // :scope isn't supported on Edge, ignore error caused by it. |  | 
| 236         } |  | 
| 237       } | 282       } | 
| 238       yield null; |  | 
| 239     } | 283     } | 
| 240   } | 284   } | 
| 241 }; | 285 }; | 
| 242 | 286 | 
| 243 function ContainsSelector(textContent) | 287 function ContainsSelector(textContent) | 
| 244 { | 288 { | 
| 245   let regexpString = checkRegExpParameter(textContent); | 289   this._regexp = makeRegExpParameter(textContent); | 
| 246   if (regexpString) |  | 
| 247     this._regexp = new RegExp(regexpString); |  | 
| 248   else |  | 
| 249     this._text = textContent; |  | 
| 250 } | 290 } | 
| 251 | 291 | 
| 252 ContainsSelector.prototype = { | 292 ContainsSelector.prototype = { | 
| 253   requiresHiding: true, | 293   requiresHiding: true, | 
| 254 | 294 | 
| 255   match(element) |  | 
| 256   { |  | 
| 257     if (this._regexp) |  | 
| 258       return this._regexp.test(element.textContent); |  | 
| 259 |  | 
| 260     return element.textContent.includes(this._text); |  | 
| 261   }, |  | 
| 262 |  | 
| 263   *getSelectors(prefix, subtree, stylesheet) | 295   *getSelectors(prefix, subtree, stylesheet) | 
| 264   { | 296   { | 
| 265     for (let element of this.getElements(prefix, subtree, stylesheet)) | 297     for (let element of this.getElements(prefix, subtree, stylesheet)) | 
| 266       yield [makeSelector(element, ""), subtree]; | 298       yield [makeSelector(element, ""), subtree]; | 
| 267   }, | 299   }, | 
| 268 | 300 | 
| 269   *getElements(prefix, subtree, stylesheet) | 301   *getElements(prefix, subtree, stylesheet) | 
| 270   { | 302   { | 
| 271     let actualPrefix = (!prefix || incompletePrefixRegexp.test(prefix)) ? | 303     let actualPrefix = (!prefix || incompletePrefixRegexp.test(prefix)) ? | 
| 272         prefix + "*" : prefix; | 304         prefix + "*" : prefix; | 
| 273     let elements = subtree.querySelectorAll(actualPrefix); | 305 | 
| 274 | 306     let elements = scopedQuerySelectorAll(subtree, actualPrefix); | 
| 275     for (let element of elements) | 307     if (elements) | 
| 276     { | 308     { | 
| 277       if (this.match(element)) | 309       for (let element of elements) | 
| 278         yield element; | 310       { | 
| 279       else | 311         if (this._regexp && this._regexp.test(element.textContent)) | 
| 280         yield null; | 312           yield element; | 
|  | 313         else | 
|  | 314           yield null; | 
|  | 315       } | 
| 281     } | 316     } | 
| 282   } | 317   } | 
| 283 }; | 318 }; | 
| 284 | 319 | 
| 285 function PropsSelector(propertyExpression) | 320 function PropsSelector(propertyExpression) | 
| 286 { | 321 { | 
| 287   let regexpString = checkRegExpParameter(propertyExpression); | 322   let regexpString; | 
| 288   if (!regexpString) | 323   if (propertyExpression.length >= 2 && propertyExpression[0] == "/" && | 
|  | 324       propertyExpression[propertyExpression.length - 1] == "/") | 
|  | 325   { | 
|  | 326     regexpString = propertyExpression.slice(1, -1) | 
|  | 327       .replace("\\7B ", "{").replace("\\7D ", "}"); | 
|  | 328   } | 
|  | 329   else | 
| 289     regexpString = filterToRegExp(propertyExpression); | 330     regexpString = filterToRegExp(propertyExpression); | 
| 290 | 331 | 
| 291   this._regexp = new RegExp(regexpString, "i"); | 332   this._regexp = new RegExp(regexpString, "i"); | 
| 292 } | 333 } | 
| 293 | 334 | 
| 294 PropsSelector.prototype = { | 335 PropsSelector.prototype = { | 
| 295   preferHideWithSelector: true, | 336   preferHideWithSelector: true, | 
| 296   dependsOnStyles: true, | 337   dependsOnStyles: true, | 
| 297 | 338 | 
| 298   *findPropsSelectors(styles, prefix, regexp) | 339   *findPropsSelectors(styles, prefix, regexp) | 
| (...skipping 329 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 628           characterData: true, | 669           characterData: true, | 
| 629           subtree: true | 670           subtree: true | 
| 630         } | 671         } | 
| 631       ); | 672       ); | 
| 632       this.document.addEventListener("load", this.onLoad.bind(this), true); | 673       this.document.addEventListener("load", this.onLoad.bind(this), true); | 
| 633     } | 674     } | 
| 634   } | 675   } | 
| 635 }; | 676 }; | 
| 636 | 677 | 
| 637 exports.ElemHideEmulation = ElemHideEmulation; | 678 exports.ElemHideEmulation = ElemHideEmulation; | 
| LEFT | RIGHT | 
|---|