| 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-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 /** | 20 /** | 
| 21  * @fileOverview Element hiding implementation. | 21  * @fileOverview Element hiding implementation. | 
| 22  */ | 22  */ | 
| 23 | 23 | 
| 24 const {ElemHideException} = require("./filterClasses"); | 24 const {ElemHideException} = require("./filterClasses"); | 
| 25 const {FilterNotifier} = require("./filterNotifier"); | 25 const {FilterNotifier} = require("./filterNotifier"); | 
| 26 | 26 | 
| 27 /** | 27 /** | 
| 28  * Lookup table, active flag, by filter by domain. | 28  * Lookup table, active flag, by filter by domain. | 
| 29  * (Only contains filters that aren't unconditionally matched for all domains.) | 29  * (Only contains filters that aren't unconditionally matched for all domains.) | 
| 30  * @type {Map.<string,Map.<Filter,boolean>>} | 30  * @type {Map.<string,?Map.<Filter,boolean>>} | 
| 31  */ | 31  */ | 
| 32 let filtersByDomain = new Map(); | 32 let filtersByDomain = new Map(); | 
| 33 | 33 | 
| 34 /** | 34 /** | 
| 35  * Lookup table, filter by selector. (Only used for selectors that are | 35  * Lookup table, filter by selector. (Only used for selectors that are | 
| 36  * unconditionally matched for all domains.) | 36  * unconditionally matched for all domains.) | 
| 37  * @type {Map.<string,Filter>} | 37  * @type {Map.<string,Filter>} | 
| 38  */ | 38  */ | 
| 39 let filterBySelector = new Map(); | 39 let filterBySelector = new Map(); | 
| 40 | 40 | 
| (...skipping 10 matching lines...) Expand all  Loading... | 
| 51 let defaultDomains = new Map([["", true]]); | 51 let defaultDomains = new Map([["", true]]); | 
| 52 | 52 | 
| 53 /** | 53 /** | 
| 54  * Set containing known element hiding and exception filters | 54  * Set containing known element hiding and exception filters | 
| 55  * @type {Set.<ElemHideBase>} | 55  * @type {Set.<ElemHideBase>} | 
| 56  */ | 56  */ | 
| 57 let knownFilters = new Set(); | 57 let knownFilters = new Set(); | 
| 58 | 58 | 
| 59 /** | 59 /** | 
| 60  * Lookup table, lists of element hiding exceptions by selector | 60  * Lookup table, lists of element hiding exceptions by selector | 
| 61  * @type {Map.<string,Filter>} | 61  * @type {Map.<string,Filter[]>} | 
| 62  */ | 62  */ | 
| 63 let exceptions = new Map(); | 63 let exceptions = new Map(); | 
| 64 | 64 | 
|  | 65 /* | 
|  | 66  * Lookup table, lists of generic element hiding exceptions by selector | 
|  | 67  * @type {Map.<string,Filter[]>} | 
|  | 68  */ | 
|  | 69 let genericExceptions = new Map(); | 
|  | 70 | 
|  | 71 /** | 
|  | 72  * Checks whether a filter has an exception | 
|  | 73  * @param {Filter} filter | 
|  | 74  * @param {string} [domain] | 
|  | 75  * @returns {boolean} | 
|  | 76  */ | 
|  | 77 function hasException(filter, domain) | 
|  | 78 { | 
|  | 79   if (!domain) | 
|  | 80     return genericExceptions.has(filter.selector); | 
|  | 81 | 
|  | 82   return !!ElemHide.getException(filter, domain); | 
|  | 83 } | 
|  | 84 | 
|  | 85 /* | 
|  | 86  * Returns a list of domain-specific filters matching a domain | 
|  | 87  * @param {string} [domain] | 
|  | 88  * @returns {Array.<?Map.<Filter,boolean>>} | 
|  | 89  */ | 
|  | 90 function getSpecificFiltersForDomain(domain) | 
|  | 91 { | 
|  | 92   let filtersList = []; | 
|  | 93 | 
|  | 94   if (domain) | 
|  | 95     domain = domain.toUpperCase(); | 
|  | 96 | 
|  | 97   while (domain) | 
|  | 98   { | 
|  | 99     // Note that we also push null values into the list, because | 
|  | 100     // ElemHide.getSelectorsForDomain still needs to know if there are any | 
|  | 101     // entries for the domain. | 
|  | 102     let filters = filtersByDomain.get(domain); | 
|  | 103     if (typeof filters != "undefined") | 
|  | 104       filtersList.push(filters); | 
|  | 105 | 
|  | 106     let nextDot = domain.indexOf("."); | 
|  | 107     domain = nextDot == -1 ? null : domain.substring(nextDot + 1); | 
|  | 108   } | 
|  | 109 | 
|  | 110   return filtersList; | 
|  | 111 } | 
|  | 112 | 
|  | 113 /** | 
|  | 114  * Returns a list of selectors from a given list of filters that apply on a | 
|  | 115  * domain | 
|  | 116  * @param {string} [domain] | 
|  | 117  * @param {Array.<?Map.<Filter,boolean>>} filtersList | 
|  | 118  * @param {boolean} genericOnly | 
|  | 119  * @returns {string[]} | 
|  | 120  */ | 
|  | 121 function getConditionalSelectorsForDomain(domain, filtersList, genericOnly) | 
|  | 122 { | 
|  | 123   let selectors = []; | 
|  | 124 | 
|  | 125   let excluded = !genericOnly ? new Set() : null; | 
|  | 126   let domainForExceptionCheck = !genericOnly ? domain : null; | 
|  | 127 | 
|  | 128   // This code is a performance hot-spot, which is why we've made certain | 
|  | 129   // micro-optimisations. Please be careful before making changes. | 
|  | 130   for (let i = 0; i < filtersList.length; i++) | 
|  | 131   { | 
|  | 132     if (!filtersList[i]) | 
|  | 133       continue; | 
|  | 134 | 
|  | 135     for (let [filter, isIncluded] of filtersList[i]) | 
|  | 136     { | 
|  | 137       if (!isIncluded) | 
|  | 138       { | 
|  | 139         excluded.add(filter); | 
|  | 140       } | 
|  | 141       else if ((!excluded || excluded.size == 0 || !excluded.has(filter)) && | 
|  | 142                !hasException(filter, domainForExceptionCheck)) | 
|  | 143       { | 
|  | 144         selectors.push(filter.selector); | 
|  | 145       } | 
|  | 146     } | 
|  | 147   } | 
|  | 148 | 
|  | 149   return selectors; | 
|  | 150 } | 
|  | 151 | 
| 65 /** | 152 /** | 
| 66  * Returns a list of selectors that apply on each website unconditionally. | 153  * Returns a list of selectors that apply on each website unconditionally. | 
| 67  * @returns {string[]} | 154  * @returns {string[]} | 
| 68  */ | 155  */ | 
| 69 function getUnconditionalSelectors() | 156 function getUnconditionalSelectors() | 
| 70 { | 157 { | 
| 71   if (!unconditionalSelectors) | 158   if (!unconditionalSelectors) | 
| 72     unconditionalSelectors = [...filterBySelector.keys()]; | 159     unconditionalSelectors = [...filterBySelector.keys()]; | 
| 73 | 160 | 
| 74   return unconditionalSelectors; | 161   return unconditionalSelectors; | 
| 75 } | 162 } | 
| 76 | 163 | 
| 77 /** | 164 /** | 
| 78  * Container for element hiding filters | 165  * Container for element hiding filters | 
| 79  * @class | 166  * @class | 
| 80  */ | 167  */ | 
| 81 let ElemHide = exports.ElemHide = { | 168 let ElemHide = exports.ElemHide = { | 
| 82   /** | 169   /** | 
| 83    * Removes all known filters | 170    * Removes all known filters | 
| 84    */ | 171    */ | 
| 85   clear() | 172   clear() | 
| 86   { | 173   { | 
| 87     for (let collection of [filtersByDomain, filterBySelector, | 174     for (let collection of [filtersByDomain, filterBySelector, | 
| 88                             knownFilters, exceptions]) | 175                             knownFilters, exceptions, | 
|  | 176                             genericExceptions]) | 
| 89     { | 177     { | 
| 90       collection.clear(); | 178       collection.clear(); | 
| 91     } | 179     } | 
| 92     unconditionalSelectors = null; | 180     unconditionalSelectors = null; | 
| 93     FilterNotifier.emit("elemhideupdate"); | 181     FilterNotifier.emit("elemhideupdate"); | 
| 94   }, | 182   }, | 
| 95 | 183 | 
| 96   _addToFiltersByDomain(filter) | 184   _addToFiltersByDomain(filter) | 
| 97   { | 185   { | 
| 98     let domains = filter.domains || defaultDomains; | 186     let domains = filter.domains || defaultDomains; | 
| 99     for (let [domain, isIncluded] of domains) | 187     if (filter instanceof ElemHideException) | 
| 100     { | 188     { | 
| 101       // There's no need to note that a filter is generically disabled. | 189       for (let domain of domains.keys()) | 
| 102       if (!isIncluded && domain == "") | 190       { | 
| 103         continue; | 191         // Add an entry for each domain, but without any filters. This makes | 
|  | 192         // the domain "known" and helps us avoid certain optimizations that | 
|  | 193         // would otherwise yield incorrect results. | 
|  | 194         if (domain != "" && !filtersByDomain.has(domain)) | 
|  | 195           filtersByDomain.set(domain, null); | 
|  | 196       } | 
|  | 197     } | 
|  | 198     else | 
|  | 199     { | 
|  | 200       for (let [domain, isIncluded] of domains) | 
|  | 201       { | 
|  | 202         // There's no need to note that a filter is generically disabled. | 
|  | 203         if (!isIncluded && domain == "") | 
|  | 204           continue; | 
| 104 | 205 | 
| 105       let filters = filtersByDomain.get(domain); | 206         let filters = filtersByDomain.get(domain); | 
| 106       if (!filters) | 207         if (!filters) | 
| 107         filtersByDomain.set(domain, filters = new Map()); | 208           filtersByDomain.set(domain, filters = new Map()); | 
| 108       filters.set(filter, isIncluded); | 209         filters.set(filter, isIncluded); | 
|  | 210       } | 
| 109     } | 211     } | 
| 110   }, | 212   }, | 
| 111 | 213 | 
| 112   /** | 214   /** | 
| 113    * Add a new element hiding filter | 215    * Add a new element hiding filter | 
| 114    * @param {ElemHideBase} filter | 216    * @param {ElemHideBase} filter | 
| 115    */ | 217    */ | 
| 116   add(filter) | 218   add(filter) | 
| 117   { | 219   { | 
| 118     if (knownFilters.has(filter)) | 220     if (knownFilters.has(filter)) | 
| 119       return; | 221       return; | 
| 120 | 222 | 
| 121     if (filter instanceof ElemHideException) | 223     if (filter instanceof ElemHideException) | 
| 122     { | 224     { | 
| 123       let {selector} = filter; | 225       let {selector, domains} = filter; | 
|  | 226 | 
| 124       let list = exceptions.get(selector); | 227       let list = exceptions.get(selector); | 
| 125       if (list) | 228       if (list) | 
| 126         list.push(filter); | 229         list.push(filter); | 
| 127       else | 230       else | 
| 128         exceptions.set(selector, [filter]); | 231         exceptions.set(selector, [filter]); | 
| 129 | 232 | 
|  | 233       if (domains) | 
|  | 234         this._addToFiltersByDomain(filter); | 
|  | 235 | 
|  | 236       if (filter.isGeneric()) | 
|  | 237       { | 
|  | 238         list = genericExceptions.get(selector); | 
|  | 239         if (list) | 
|  | 240           list.push(filter); | 
|  | 241         else | 
|  | 242           genericExceptions.set(selector, [filter]); | 
|  | 243       } | 
|  | 244 | 
| 130       // If this is the first exception for a previously unconditionally | 245       // If this is the first exception for a previously unconditionally | 
| 131       // applied element hiding selector we need to take care to update the | 246       // applied element hiding selector we need to take care to update the | 
| 132       // lookups. | 247       // lookups. | 
| 133       let unconditionalFilterForSelector = filterBySelector.get(selector); | 248       let unconditionalFilterForSelector = filterBySelector.get(selector); | 
| 134       if (unconditionalFilterForSelector) | 249       if (unconditionalFilterForSelector) | 
| 135       { | 250       { | 
| 136         this._addToFiltersByDomain(unconditionalFilterForSelector); | 251         this._addToFiltersByDomain(unconditionalFilterForSelector); | 
| 137         filterBySelector.delete(selector); | 252         filterBySelector.delete(selector); | 
| 138         unconditionalSelectors = null; | 253         unconditionalSelectors = null; | 
| 139       } | 254       } | 
| (...skipping 23 matching lines...) Expand all  Loading... | 
| 163     if (!knownFilters.has(filter)) | 278     if (!knownFilters.has(filter)) | 
| 164       return; | 279       return; | 
| 165 | 280 | 
| 166     // Whitelisting filters | 281     // Whitelisting filters | 
| 167     if (filter instanceof ElemHideException) | 282     if (filter instanceof ElemHideException) | 
| 168     { | 283     { | 
| 169       let list = exceptions.get(filter.selector); | 284       let list = exceptions.get(filter.selector); | 
| 170       let index = list.indexOf(filter); | 285       let index = list.indexOf(filter); | 
| 171       if (index >= 0) | 286       if (index >= 0) | 
| 172         list.splice(index, 1); | 287         list.splice(index, 1); | 
|  | 288 | 
|  | 289       if (filter.isGeneric()) | 
|  | 290       { | 
|  | 291         list = genericExceptions.get(filter.selector); | 
|  | 292         index = list.indexOf(filter); | 
|  | 293         if (index >= 0) | 
|  | 294           list.splice(index, 1); | 
|  | 295 | 
|  | 296         if (list.length == 0) | 
|  | 297           genericExceptions.delete(filter.selector); | 
|  | 298       } | 
| 173     } | 299     } | 
| 174     // Unconditially applied element hiding filters | 300     // Unconditially applied element hiding filters | 
| 175     else if (filterBySelector.get(filter.selector) == filter) | 301     else if (filterBySelector.get(filter.selector) == filter) | 
| 176     { | 302     { | 
| 177       filterBySelector.delete(filter.selector); | 303       filterBySelector.delete(filter.selector); | 
| 178       unconditionalSelectors = null; | 304       unconditionalSelectors = null; | 
| 179     } | 305     } | 
| 180     // Conditionally applied element hiding filters | 306     // Conditionally applied element hiding filters | 
| 181     else | 307     else | 
| 182     { | 308     { | 
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 238    * on a particular host name. | 364    * on a particular host name. | 
| 239    * @param {string} domain | 365    * @param {string} domain | 
| 240    * @param {number} [criteria] | 366    * @param {number} [criteria] | 
| 241    *   One of the following: ElemHide.ALL_MATCHING, ElemHide.NO_UNCONDITIONAL or | 367    *   One of the following: ElemHide.ALL_MATCHING, ElemHide.NO_UNCONDITIONAL or | 
| 242    *                         ElemHide.SPECIFIC_ONLY. | 368    *                         ElemHide.SPECIFIC_ONLY. | 
| 243    * @returns {string[]} | 369    * @returns {string[]} | 
| 244    *   List of selectors. | 370    *   List of selectors. | 
| 245    */ | 371    */ | 
| 246   getSelectorsForDomain(domain, criteria) | 372   getSelectorsForDomain(domain, criteria) | 
| 247   { | 373   { | 
| 248     let selectors = []; |  | 
| 249 |  | 
| 250     if (typeof criteria == "undefined") | 374     if (typeof criteria == "undefined") | 
| 251       criteria = ElemHide.ALL_MATCHING; | 375       criteria = ElemHide.ALL_MATCHING; | 
| 252 | 376 | 
| 253     let specificOnly = (criteria >= ElemHide.SPECIFIC_ONLY); | 377     let specificOnly = criteria >= ElemHide.SPECIFIC_ONLY; | 
| 254     let excluded = new Set(); | 378     let filtersList = getSpecificFiltersForDomain(domain); | 
| 255     let currentDomain = domain ? domain.toUpperCase() : ""; |  | 
| 256 | 379 | 
| 257     // This code is a performance hot-spot, which is why we've made certain | 380     let genericOnly = filtersList.length == 0; | 
| 258     // micro-optimisations. Please be careful before making changes. |  | 
| 259     while (true) |  | 
| 260     { |  | 
| 261       if (specificOnly && currentDomain == "") |  | 
| 262         break; |  | 
| 263 | 381 | 
| 264       let filters = filtersByDomain.get(currentDomain); | 382     if (!specificOnly) | 
| 265       if (filters) | 383       filtersList.push(filtersByDomain.get("")); | 
| 266       { |  | 
| 267         for (let [filter, isIncluded] of filters) |  | 
| 268         { |  | 
| 269           if (!isIncluded) |  | 
| 270           { |  | 
| 271             excluded.add(filter); |  | 
| 272           } |  | 
| 273           else if ((excluded.size == 0 || !excluded.has(filter)) && |  | 
| 274                    !this.getException(filter, domain)) |  | 
| 275           { |  | 
| 276             selectors.push(filter.selector); |  | 
| 277           } |  | 
| 278         } |  | 
| 279       } |  | 
| 280 | 384 | 
| 281       if (currentDomain == "") | 385     let selectors = getConditionalSelectorsForDomain(domain, filtersList, | 
| 282         break; | 386                                                      genericOnly); | 
| 283 |  | 
| 284       let nextDot = currentDomain.indexOf("."); |  | 
| 285       currentDomain = nextDot == -1 ? "" : currentDomain.substr(nextDot + 1); |  | 
| 286     } |  | 
| 287 | 387 | 
| 288     if (criteria < ElemHide.NO_UNCONDITIONAL) | 388     if (criteria < ElemHide.NO_UNCONDITIONAL) | 
| 289       selectors = getUnconditionalSelectors().concat(selectors); | 389       selectors = getUnconditionalSelectors().concat(selectors); | 
| 290 | 390 | 
| 291     return selectors; | 391     return selectors; | 
| 292   } | 392   } | 
| 293 }; | 393 }; | 
| OLD | NEW | 
|---|