| 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 | 
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
|   71  */ |   71  */ | 
|   72 let genericExceptions = new Map(); |   72 let genericExceptions = new Map(); | 
|   73  |   73  | 
|   74 /** |   74 /** | 
|   75  * List of selectors that apply on any unknown domain |   75  * List of selectors that apply on any unknown domain | 
|   76  * @type {?string[]} |   76  * @type {?string[]} | 
|   77  */ |   77  */ | 
|   78 let conditionalGenericSelectors = null; |   78 let conditionalGenericSelectors = null; | 
|   79  |   79  | 
|   80 /** |   80 /** | 
|   81  * Domains that are known not to be specifically excluded from any generic |  | 
|   82  * filters |  | 
|   83  * @type {Set.<string>} |  | 
|   84  */ |  | 
|   85 let genericFriendlyDomains = new Set(); |  | 
|   86  |  | 
|   87 /** |  | 
|   88  * Adds a filter to the lookup table of filters by domain. |   81  * Adds a filter to the lookup table of filters by domain. | 
|   89  * @param {Filter} filter |   82  * @param {Filter} filter | 
|   90  */ |   83  */ | 
|   91 function addToFiltersByDomain(filter) |   84 function addToFiltersByDomain(filter) | 
|   92 { |   85 { | 
|   93   let domains = filter.domains || defaultDomains; |   86   let domains = filter.domains || defaultDomains; | 
|   94   if (filter instanceof ElemHideException) |   87   if (filter instanceof ElemHideException) | 
|   95   { |   88   { | 
|   96     for (let domain of domains.keys()) |   89     for (let domain of domains.keys()) | 
|   97     { |   90     { | 
| (...skipping 27 matching lines...) Expand all  Loading... | 
|  125  */ |  118  */ | 
|  126 function getSpecificFiltersForDomain(domain) |  119 function getSpecificFiltersForDomain(domain) | 
|  127 { |  120 { | 
|  128   let filtersList = []; |  121   let filtersList = []; | 
|  129  |  122  | 
|  130   if (domain) |  123   if (domain) | 
|  131     domain = domain.toUpperCase(); |  124     domain = domain.toUpperCase(); | 
|  132  |  125  | 
|  133   while (domain) |  126   while (domain) | 
|  134   { |  127   { | 
|  135     // Note that we also push null values into the list, because |  | 
|  136     // ElemHide.getSelectorsForDomain still needs to know if there are any |  | 
|  137     // entries for the domain. |  | 
|  138     let filters = filtersByDomain.get(domain); |  128     let filters = filtersByDomain.get(domain); | 
|  139     if (typeof filters != "undefined") |  129     if (typeof filters != "undefined") | 
|  140       filtersList.push(filters); |  130       filtersList.push(filters); | 
|  141  |  131  | 
|  142     let nextDot = domain.indexOf("."); |  132     let nextDot = domain.indexOf("."); | 
|  143     domain = nextDot == -1 ? null : domain.substring(nextDot + 1); |  133     domain = nextDot == -1 ? null : domain.substring(nextDot + 1); | 
|  144   } |  134   } | 
|  145  |  135  | 
|  146   return filtersList; |  136   return filtersList; | 
|  147 } |  137 } | 
|  148  |  138  | 
|  149 /** |  139 /** | 
|  150  * Returns a list of selectors from a given list of filters that apply on a |  140  * Returns a list of selectors that apply on a domain from a given list of | 
|  151  * domain |  141  * filters | 
|  152  * @param {string} [domain] |  142  * @param {string} [domain] | 
|  153  * @param {Array.<?Map.<Filter,boolean>>} filtersList |  143  * @param {Array.<?Map.<Filter,boolean>>} filtersList | 
|  154  * @param {boolean} specificOnly |  144  * @param {Set.<Filter>} excludeSet | 
|  155  * @returns {string[]} |  145  * @returns {string[]} | 
|  156  */ |  146  */ | 
|  157 function getConditionalSelectorsForDomain(domain, filtersList, specificOnly) |  147 function matchSelectors(domain, filtersList, excludeSet) | 
|  158 { |  148 { | 
|  159   let selectors = []; |  149   let matches = []; | 
|  160  |  | 
|  161   let genericFilters = !specificOnly ? filtersList.pop() : null; |  | 
|  162   let excluded = new Set(); |  | 
|  163  |  150  | 
|  164   // This code is a performance hot-spot, which is why we've made certain |  151   // This code is a performance hot-spot, which is why we've made certain | 
|  165   // micro-optimisations. Please be careful before making changes. |  152   // micro-optimisations. Please be careful before making changes. | 
|  166   for (let i = 0; i < filtersList.length; i++) |  153   for (let i = 0; i < filtersList.length; i++) | 
|  167   { |  154   { | 
|  168     if (!filtersList[i]) |  155     let filters = filtersList[i]; | 
|  169       continue; |  156     if (filters) | 
|  170  |  157     { | 
|  171     for (let [filter, isIncluded] of filtersList[i]) |  158       for (let [filter, isIncluded] of filters) | 
|  172     { |  | 
|  173       if (!isIncluded) |  | 
|  174       { |  159       { | 
|  175         excluded.add(filter); |  160         if (!isIncluded) | 
 |  161         { | 
 |  162           excludeSet.add(filter); | 
 |  163         } | 
 |  164         else if ((excludeSet.size == 0 || !excludeSet.has(filter)) && | 
 |  165                   !exports.ElemHide.getException(filter, domain)) | 
 |  166         { | 
 |  167           matches.push(filter.selector); | 
 |  168         } | 
|  176       } |  169       } | 
|  177       else if ((excluded.size == 0 || !excluded.has(filter)) && |  170     } | 
|  178                !ElemHide.getException(filter, domain)) |  171   } | 
|  179       { |  172  | 
|  180         selectors.push(filter.selector); |  173   return matches; | 
|  181       } |  | 
|  182     } |  | 
|  183   } |  | 
|  184  |  | 
|  185   if (!genericFilters) |  | 
|  186     return selectors; |  | 
|  187  |  | 
|  188   if (genericFriendlyDomains.has(domain)) |  | 
|  189     return selectors.concat(getConditionalGenericSelectors()); |  | 
|  190  |  | 
|  191   let genericSelectors = []; |  | 
|  192  |  | 
|  193   for (let filter of genericFilters.keys()) |  | 
|  194   { |  | 
|  195     if ((excluded.size == 0 || !excluded.has(filter)) && |  | 
|  196         !ElemHide.getException(filter, domain)) |  | 
|  197     { |  | 
|  198       genericSelectors.push(filter.selector); |  | 
|  199     } |  | 
|  200   } |  | 
|  201  |  | 
|  202   // If the number of conditional generic selectors that apply on this domain |  | 
|  203   // is the same as the total number of conditional generic selectors, the |  | 
|  204   // domain is "generic friendly". In that case, we mark it is as such for |  | 
|  205   // faster lookups. |  | 
|  206   if (conditionalGenericSelectors && |  | 
|  207       genericSelectors.length == conditionalGenericSelectors.length) |  | 
|  208   { |  | 
|  209     if (genericFriendlyDomains.size >= 1000) |  | 
|  210       genericFriendlyDomains.clear(); |  | 
|  211  |  | 
|  212     genericFriendlyDomains.add(domain); |  | 
|  213   } |  | 
|  214  |  | 
|  215   return selectors.concat(genericSelectors); |  | 
|  216 } |  174 } | 
|  217  |  175  | 
|  218 /** |  176 /** | 
|  219  * Returns a list of selectors that apply on any unknown domain |  177  * Returns a list of selectors that apply on any unknown domain | 
|  220  * @returns {string[]} |  178  * @returns {string[]} | 
|  221  */ |  179  */ | 
|  222 function getConditionalGenericSelectors() |  180 function getConditionalGenericSelectors() | 
|  223 { |  181 { | 
|  224   if (conditionalGenericSelectors) |  182   if (conditionalGenericSelectors) | 
|  225     return conditionalGenericSelectors; |  183     return conditionalGenericSelectors; | 
| (...skipping 22 matching lines...) Expand all  Loading... | 
|  248   if (!unconditionalSelectors) |  206   if (!unconditionalSelectors) | 
|  249     unconditionalSelectors = [...filterBySelector.keys()]; |  207     unconditionalSelectors = [...filterBySelector.keys()]; | 
|  250  |  208  | 
|  251   return unconditionalSelectors; |  209   return unconditionalSelectors; | 
|  252 } |  210 } | 
|  253  |  211  | 
|  254 /** |  212 /** | 
|  255  * Container for element hiding filters |  213  * Container for element hiding filters | 
|  256  * @class |  214  * @class | 
|  257  */ |  215  */ | 
|  258 let ElemHide = exports.ElemHide = { |  216 exports.ElemHide = { | 
|  259   /** |  217   /** | 
|  260    * Removes all known filters |  218    * Removes all known filters | 
|  261    */ |  219    */ | 
|  262   clear() |  220   clear() | 
|  263   { |  221   { | 
|  264     for (let collection of [filtersByDomain, filterBySelector, |  222     for (let collection of [filtersByDomain, filterBySelector, | 
|  265                             knownFilters, exceptions, |  223                             knownFilters, exceptions, | 
|  266                             genericExceptions, genericFriendlyDomains]) |  224                             genericExceptions]) | 
|  267     { |  225     { | 
|  268       collection.clear(); |  226       collection.clear(); | 
|  269     } |  227     } | 
|  270     unconditionalSelectors = null; |  228     unconditionalSelectors = null; | 
|  271     conditionalGenericSelectors = null; |  229     conditionalGenericSelectors = null; | 
|  272     FilterNotifier.emit("elemhideupdate"); |  230     FilterNotifier.emit("elemhideupdate"); | 
|  273   }, |  231   }, | 
|  274  |  232  | 
|  275   /** |  233   /** | 
|  276    * Add a new element hiding filter |  234    * Add a new element hiding filter | 
|  277    * @param {ElemHideBase} filter |  235    * @param {ElemHideBase} filter | 
|  278    */ |  236    */ | 
|  279   add(filter) |  237   add(filter) | 
|  280   { |  238   { | 
|  281     if (knownFilters.has(filter)) |  239     if (knownFilters.has(filter)) | 
|  282       return; |  240       return; | 
|  283  |  241  | 
|  284     conditionalGenericSelectors = null; |  242     conditionalGenericSelectors = null; | 
|  285     genericFriendlyDomains.clear(); |  | 
|  286  |  243  | 
|  287     if (filter instanceof ElemHideException) |  244     if (filter instanceof ElemHideException) | 
|  288     { |  245     { | 
|  289       let {selector, domains} = filter; |  246       let {selector, domains} = filter; | 
|  290  |  247  | 
|  291       let list = exceptions.get(selector); |  248       let list = exceptions.get(selector); | 
|  292       if (list) |  249       if (list) | 
|  293         list.push(filter); |  250         list.push(filter); | 
|  294       else |  251       else | 
|  295         exceptions.set(selector, [filter]); |  252         exceptions.set(selector, [filter]); | 
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
|  336   /** |  293   /** | 
|  337    * Removes an element hiding filter |  294    * Removes an element hiding filter | 
|  338    * @param {ElemHideBase} filter |  295    * @param {ElemHideBase} filter | 
|  339    */ |  296    */ | 
|  340   remove(filter) |  297   remove(filter) | 
|  341   { |  298   { | 
|  342     if (!knownFilters.has(filter)) |  299     if (!knownFilters.has(filter)) | 
|  343       return; |  300       return; | 
|  344  |  301  | 
|  345     conditionalGenericSelectors = null; |  302     conditionalGenericSelectors = null; | 
|  346     genericFriendlyDomains.clear(); |  | 
|  347  |  303  | 
|  348     // Whitelisting filters |  304     // Whitelisting filters | 
|  349     if (filter instanceof ElemHideException) |  305     if (filter instanceof ElemHideException) | 
|  350     { |  306     { | 
|  351       let list = exceptions.get(filter.selector); |  307       let list = exceptions.get(filter.selector); | 
|  352       let index = list.indexOf(filter); |  308       let index = list.indexOf(filter); | 
|  353       if (index >= 0) |  309       if (index >= 0) | 
|  354         list.splice(index, 1); |  310         list.splice(index, 1); | 
|  355  |  311  | 
|  356       if (filter.isGeneric()) |  312       if (filter.isGeneric()) | 
|  357       { |  313       { | 
|  358         list = genericExceptions.get(filter.selector); |  314         list = genericExceptions.get(filter.selector); | 
|  359         index = list.indexOf(filter); |  315         index = list.indexOf(filter); | 
|  360         if (index >= 0) |  316         if (index >= 0) | 
|  361           list.splice(index, 1); |  317           list.splice(index, 1); | 
|  362  |  318  | 
 |  319         // It's important to delete the entry here so the selector no longer | 
 |  320         // appears to have any generic exceptions. | 
|  363         if (list.length == 0) |  321         if (list.length == 0) | 
|  364           genericExceptions.delete(filter.selector); |  322           genericExceptions.delete(filter.selector); | 
|  365       } |  323       } | 
|  366     } |  324     } | 
|  367     // Unconditially applied element hiding filters |  325     // Unconditially applied element hiding filters | 
|  368     else if (filterBySelector.get(filter.selector) == filter) |  326     else if (filterBySelector.get(filter.selector) == filter) | 
|  369     { |  327     { | 
|  370       filterBySelector.delete(filter.selector); |  328       filterBySelector.delete(filter.selector); | 
|  371       unconditionalSelectors = null; |  329       unconditionalSelectors = null; | 
|  372     } |  330     } | 
| (...skipping 29 matching lines...) Expand all  Loading... | 
|  402     for (let i = list.length - 1; i >= 0; i--) |  360     for (let i = list.length - 1; i >= 0; i--) | 
|  403     { |  361     { | 
|  404       if (list[i].isActiveOnDomain(docDomain)) |  362       if (list[i].isActiveOnDomain(docDomain)) | 
|  405         return list[i]; |  363         return list[i]; | 
|  406     } |  364     } | 
|  407  |  365  | 
|  408     return null; |  366     return null; | 
|  409   }, |  367   }, | 
|  410  |  368  | 
|  411   /** |  369   /** | 
|  412    * Constant used by getSelectorsForDomain to return all selectors applying to |  | 
|  413    * a particular hostname. |  | 
|  414    * @type {number} |  | 
|  415    * @const |  | 
|  416    */ |  | 
|  417   ALL_MATCHING: 0, |  | 
|  418  |  | 
|  419   /** |  | 
|  420    * Constant used by getSelectorsForDomain to exclude selectors which apply to |  | 
|  421    * all websites without exception. |  | 
|  422    * @type {number} |  | 
|  423    * @const |  | 
|  424    */ |  | 
|  425   NO_UNCONDITIONAL: 1, |  | 
|  426  |  | 
|  427   /** |  | 
|  428    * Constant used by getSelectorsForDomain to return only selectors for filters |  | 
|  429    * which specifically match the given host name. |  | 
|  430    * @type {number} |  | 
|  431    * @const |  | 
|  432    */ |  | 
|  433   SPECIFIC_ONLY: 2, |  | 
|  434  |  | 
|  435   /** |  | 
|  436    * Determines from the current filter list which selectors should be applied |  370    * Determines from the current filter list which selectors should be applied | 
|  437    * on a particular host name. |  371    * on a particular host name. | 
|  438    * @param {string} domain |  372    * @param {string} domain | 
|  439    * @param {number} [criteria] |  373    * @param {boolean} [specificOnly] true if generic filters should not apply. | 
|  440    *   One of the following: ElemHide.ALL_MATCHING, ElemHide.NO_UNCONDITIONAL or |  374    * @returns {string[]} List of selectors. | 
|  441    *                         ElemHide.SPECIFIC_ONLY. |  | 
|  442    * @returns {string[]} |  | 
|  443    *   List of selectors. |  | 
|  444    */ |  375    */ | 
|  445   getSelectorsForDomain(domain, criteria = ElemHide.ALL_MATCHING) |  376   getSelectorsForDomain(domain, specificOnly = false) | 
|  446   { |  377   { | 
|  447     let selectors = []; |  378     let specificFilters = getSpecificFiltersForDomain(domain); | 
|  448  |  379  | 
|  449     let specificOnly = criteria >= ElemHide.SPECIFIC_ONLY; |  380     // If there are no specific filters (nor any specific exceptions), we can | 
|  450     let filtersList = getSpecificFiltersForDomain(domain); |  381     // just return the selectors from all the generic filters modulo any | 
|  451  |  382     // generic exceptions. | 
|  452     if (filtersList.length > 0) |  383     if (specificFilters.length == 0) | 
|  453     { |  384     { | 
|  454       if (!specificOnly) |  385       return specificOnly ? [] : | 
|  455         filtersList.push(filtersByDomain.get("")); |  386                getUnconditionalSelectors() | 
|  456  |  387                .concat(getConditionalGenericSelectors()); | 
|  457       selectors = getConditionalSelectorsForDomain(domain, filtersList, |  388     } | 
|  458                                                    specificOnly); |  389  | 
|  459     } |  390     let excluded = new Set(); | 
|  460     else if (!specificOnly) |  391     let selectors = matchSelectors(domain, specificFilters, excluded); | 
|  461     { |  392  | 
|  462       selectors = getConditionalGenericSelectors(); |  393     if (specificOnly) | 
|  463     } |  394       return selectors; | 
|  464  |  395  | 
|  465     if (criteria < ElemHide.NO_UNCONDITIONAL) |  396     return getUnconditionalSelectors() | 
|  466       selectors = getUnconditionalSelectors().concat(selectors); |  397            .concat(selectors, | 
|  467  |  398                    matchSelectors(domain, [filtersByDomain.get("")], | 
|  468     // If the above logic leaves us with a reference to our internal cache of |  399                                   excluded)); | 
|  469     // selectors, we make a copy here. |  | 
|  470     if (selectors == conditionalGenericSelectors) |  | 
|  471       selectors = selectors.slice(); |  | 
|  472  |  | 
|  473     return selectors; |  | 
|  474   } |  400   } | 
|  475 }; |  401 }; | 
| LEFT | RIGHT |