| 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 | 
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
|   56 let unconditionalSelectors = null; |   56 let unconditionalSelectors = null; | 
|   57  |   57  | 
|   58 /** |   58 /** | 
|   59  * The default style sheet that applies on all domains. This is based on the |   59  * The default style sheet that applies on all domains. This is based on the | 
|   60  * value of <code>{@link unconditionalSelectors}</code>. |   60  * value of <code>{@link unconditionalSelectors}</code>. | 
|   61  * @type {?string} |   61  * @type {?string} | 
|   62  */ |   62  */ | 
|   63 let defaultStyleSheet = null; |   63 let defaultStyleSheet = null; | 
|   64  |   64  | 
|   65 /** |   65 /** | 
 |   66  * The common style sheet that applies on all unknown domains. This is a | 
 |   67  * concatenation of the default style sheet and an additional style sheet based | 
 |   68  * on selectors from all generic filters that are not in the | 
 |   69  * <code>{@link unconditionalSelectors}</code> list. | 
 |   70  * @type {?string} | 
 |   71  */ | 
 |   72 let commonStyleSheet = null; | 
 |   73  | 
 |   74 /** | 
|   66  * Map to be used instead when a filter has a blank domains property. |   75  * Map to be used instead when a filter has a blank domains property. | 
|   67  * @type {Map.<string,boolean>} |   76  * @type {Map.<string,boolean>} | 
|   68  * @const |   77  * @const | 
|   69  */ |   78  */ | 
|   70 let defaultDomains = new Map([["", true]]); |   79 let defaultDomains = new Map([["", true]]); | 
|   71  |   80  | 
|   72 /** |   81 /** | 
|   73  * Set containing known element hiding filters |   82  * Set containing known element hiding filters | 
|   74  * @type {Set.<ElemHideFilter>} |   83  * @type {Set.<ElemHideFilter>} | 
|   75  */ |   84  */ | 
|   76 let knownFilters = new Set(); |   85 let knownFilters = new Set(); | 
|   77  |   86  | 
|   78 /** |   87 /** | 
 |   88  * All domains known to occur in exceptions | 
 |   89  * @type {Set.<string>} | 
 |   90  */ | 
 |   91 let knownExceptionDomains = new Set(); | 
 |   92  | 
 |   93 /** | 
 |   94  * Returns the suffix of the given domain that is known. If no suffix is known, | 
 |   95  * an empty string is returned. | 
 |   96  * @param {?string} domain | 
 |   97  * @returns {string} | 
 |   98  */ | 
 |   99 function getKnownSuffix(domain) | 
 |  100 { | 
 |  101   if (domain[domain.length - 1] == ".") | 
 |  102     domain = domain.replace(/\.+$/, ""); | 
 |  103  | 
 |  104   domain = domain.toLowerCase(); | 
 |  105  | 
 |  106   while (domain && !filtersByDomain.has(domain) && | 
 |  107          !knownExceptionDomains.has(domain)) | 
 |  108   { | 
 |  109     let index = domain.indexOf("."); | 
 |  110     domain = index == -1 ? "" : domain.substring(index + 1); | 
 |  111   } | 
 |  112  | 
 |  113   return domain; | 
 |  114 } | 
 |  115  | 
 |  116 /** | 
|   79  * Adds a filter to the lookup table of filters by domain. |  117  * Adds a filter to the lookup table of filters by domain. | 
|   80  * @param {Filter} filter |  118  * @param {Filter} filter | 
|   81  */ |  119  */ | 
|   82 function addToFiltersByDomain(filter) |  120 function addToFiltersByDomain(filter) | 
|   83 { |  121 { | 
|   84   let domains = filter.domains || defaultDomains; |  122   let domains = filter.domains || defaultDomains; | 
|   85   for (let [domain, isIncluded] of domains) |  123   for (let [domain, isIncluded] of domains) | 
|   86   { |  124   { | 
|   87     // There's no need to note that a filter is generically disabled. |  125     // There's no need to note that a filter is generically disabled. | 
|   88     if (!isIncluded && domain == "") |  126     if (!isIncluded && domain == "") | 
| (...skipping 13 matching lines...) Expand all  Loading... | 
|  102 function getUnconditionalSelectors() |  140 function getUnconditionalSelectors() | 
|  103 { |  141 { | 
|  104   if (!unconditionalSelectors) |  142   if (!unconditionalSelectors) | 
|  105     unconditionalSelectors = [...filterBySelector.keys()]; |  143     unconditionalSelectors = [...filterBySelector.keys()]; | 
|  106  |  144  | 
|  107   return unconditionalSelectors; |  145   return unconditionalSelectors; | 
|  108 } |  146 } | 
|  109  |  147  | 
|  110 /** |  148 /** | 
|  111  * Returns the list of selectors that apply on a given domain from the subset |  149  * Returns the list of selectors that apply on a given domain from the subset | 
|  112  * of filters that do not apply unconditionally on all domains. |  150  * of filters that do not apply unconditionally on all domains. If no domain is | 
 |  151  * given, includes only selectors from generic filters. | 
|  113  * |  152  * | 
|  114  * @param {string} domain The domain. |  153  * @param {string} [domain] The domain. | 
|  115  * @param {boolean} [specificOnly=false] Whether selectors from generic filters |  154  * @param {boolean} [specificOnly=false] Whether selectors from generic filters | 
|  116  *   should be included. |  155  *   should be included. | 
|  117  * |  156  * | 
|  118  * @returns {Array.<string>} The list of selectors. |  157  * @returns {Array.<string>} The list of selectors. | 
|  119  */ |  158  */ | 
|  120 function getConditionalSelectorsForDomain(domain, specificOnly = false) |  159 function getConditionalSelectors(domain = "", specificOnly = false) | 
|  121 { |  160 { | 
|  122   let selectors = []; |  161   let selectors = []; | 
|  123  |  162  | 
|  124   let excluded = new Set(); |  163   let excluded = new Set(); | 
|  125   let currentDomain = domain ? domain.replace(/\.+$/, "").toLowerCase() : ""; |  164   let currentDomain = domain; | 
|  126  |  165  | 
|  127   // This code is a performance hot-spot, which is why we've made certain |  166   // This code is a performance hot-spot, which is why we've made certain | 
|  128   // micro-optimisations. Please be careful before making changes. |  167   // micro-optimisations. Please be careful before making changes. | 
|  129   while (true) |  168   while (true) | 
|  130   { |  169   { | 
|  131     if (specificOnly && currentDomain == "") |  170     if (specificOnly && currentDomain == "") | 
|  132       break; |  171       break; | 
|  133  |  172  | 
|  134     let filters = filtersByDomain.get(currentDomain); |  173     let filters = filtersByDomain.get(currentDomain); | 
|  135     if (filters) |  174     if (filters) | 
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
|  167  * @returns {string} |  206  * @returns {string} | 
|  168  */ |  207  */ | 
|  169 function getDefaultStyleSheet() |  208 function getDefaultStyleSheet() | 
|  170 { |  209 { | 
|  171   if (!defaultStyleSheet) |  210   if (!defaultStyleSheet) | 
|  172     defaultStyleSheet = createStyleSheet(getUnconditionalSelectors()); |  211     defaultStyleSheet = createStyleSheet(getUnconditionalSelectors()); | 
|  173  |  212  | 
|  174   return defaultStyleSheet; |  213   return defaultStyleSheet; | 
|  175 } |  214 } | 
|  176  |  215  | 
|  177 ElemHideExceptions.on("added", ({selector}) => |  216 /** | 
 |  217  * Returns the common style sheet that applies on all unknown domains. | 
 |  218  * @returns {string} | 
 |  219  */ | 
 |  220 function getCommonStyleSheet() | 
|  178 { |  221 { | 
 |  222   if (!commonStyleSheet) | 
 |  223   { | 
 |  224     commonStyleSheet = getDefaultStyleSheet() + | 
 |  225                        createStyleSheet(getConditionalSelectors()); | 
 |  226   } | 
 |  227  | 
 |  228   return commonStyleSheet; | 
 |  229 } | 
 |  230  | 
 |  231 ElemHideExceptions.on("added", ({domains, selector}) => | 
 |  232 { | 
 |  233   commonStyleSheet = null; | 
 |  234  | 
 |  235   if (domains) | 
 |  236   { | 
 |  237     for (let domain of domains.keys()) | 
 |  238     { | 
 |  239       // Note: Once an exception domain is known it never becomes unknown, even | 
 |  240       // when all the exceptions containing that domain are removed. This is a | 
 |  241       // best-case optimization. | 
 |  242       if (domain != "") | 
 |  243         knownExceptionDomains.add(domain); | 
 |  244     } | 
 |  245   } | 
 |  246  | 
|  179   // If this is the first exception for a previously unconditionally applied |  247   // If this is the first exception for a previously unconditionally applied | 
|  180   // element hiding selector we need to take care to update the lookups. |  248   // element hiding selector we need to take care to update the lookups. | 
|  181   let unconditionalFilterForSelector = filterBySelector.get(selector); |  249   let unconditionalFilterForSelector = filterBySelector.get(selector); | 
|  182   if (unconditionalFilterForSelector) |  250   if (unconditionalFilterForSelector) | 
|  183   { |  251   { | 
|  184     addToFiltersByDomain(unconditionalFilterForSelector); |  252     addToFiltersByDomain(unconditionalFilterForSelector); | 
|  185     filterBySelector.delete(selector); |  253     filterBySelector.delete(selector); | 
|  186     unconditionalSelectors = null; |  254     unconditionalSelectors = null; | 
|  187     defaultStyleSheet = null; |  255     defaultStyleSheet = null; | 
|  188   } |  256   } | 
|  189 }); |  257 }); | 
|  190  |  258  | 
|  191 /** |  259 /** | 
|  192  * Container for element hiding filters |  260  * Container for element hiding filters | 
|  193  * @class |  261  * @class | 
|  194  */ |  262  */ | 
|  195 exports.ElemHide = { |  263 exports.ElemHide = { | 
|  196   /** |  264   /** | 
|  197    * Removes all known filters |  265    * Removes all known filters | 
|  198    */ |  266    */ | 
|  199   clear() |  267   clear() | 
|  200   { |  268   { | 
|  201     for (let collection of [filtersByDomain, filterBySelector, knownFilters]) |  269     commonStyleSheet = null; | 
 |  270  | 
 |  271     for (let collection of [filtersByDomain, filterBySelector, knownFilters, | 
 |  272                             knownExceptionDomains]) | 
 |  273     { | 
|  202       collection.clear(); |  274       collection.clear(); | 
 |  275     } | 
|  203  |  276  | 
|  204     unconditionalSelectors = null; |  277     unconditionalSelectors = null; | 
|  205     defaultStyleSheet = null; |  278     defaultStyleSheet = null; | 
|  206  |  279  | 
|  207     filterNotifier.emit("elemhideupdate"); |  280     filterNotifier.emit("elemhideupdate"); | 
|  208   }, |  281   }, | 
|  209  |  282  | 
|  210   /** |  283   /** | 
|  211    * Add a new element hiding filter |  284    * Add a new element hiding filter | 
|  212    * @param {ElemHideFilter} filter |  285    * @param {ElemHideFilter} filter | 
|  213    */ |  286    */ | 
|  214   add(filter) |  287   add(filter) | 
|  215   { |  288   { | 
|  216     if (knownFilters.has(filter)) |  289     if (knownFilters.has(filter)) | 
|  217       return; |  290       return; | 
|  218  |  291  | 
 |  292     commonStyleSheet = null; | 
 |  293  | 
|  219     let {selector} = filter; |  294     let {selector} = filter; | 
|  220  |  295  | 
|  221     if (!(filter.domains || ElemHideExceptions.hasExceptions(selector))) |  296     if (!(filter.domains || ElemHideExceptions.hasExceptions(selector))) | 
|  222     { |  297     { | 
|  223       // The new filter's selector is unconditionally applied to all domains |  298       // The new filter's selector is unconditionally applied to all domains | 
|  224       filterBySelector.set(selector, filter); |  299       filterBySelector.set(selector, filter); | 
|  225       unconditionalSelectors = null; |  300       unconditionalSelectors = null; | 
|  226       defaultStyleSheet = null; |  301       defaultStyleSheet = null; | 
|  227     } |  302     } | 
|  228     else |  303     else | 
|  229     { |  304     { | 
|  230       // The new filter's selector only applies to some domains |  305       // The new filter's selector only applies to some domains | 
|  231       addToFiltersByDomain(filter); |  306       addToFiltersByDomain(filter); | 
|  232     } |  307     } | 
|  233  |  308  | 
|  234     knownFilters.add(filter); |  309     knownFilters.add(filter); | 
|  235     filterNotifier.emit("elemhideupdate"); |  310     filterNotifier.emit("elemhideupdate"); | 
|  236   }, |  311   }, | 
|  237  |  312  | 
|  238   /** |  313   /** | 
|  239    * Removes an element hiding filter |  314    * Removes an element hiding filter | 
|  240    * @param {ElemHideFilter} filter |  315    * @param {ElemHideFilter} filter | 
|  241    */ |  316    */ | 
|  242   remove(filter) |  317   remove(filter) | 
|  243   { |  318   { | 
|  244     if (!knownFilters.has(filter)) |  319     if (!knownFilters.has(filter)) | 
|  245       return; |  320       return; | 
|  246  |  321  | 
 |  322     commonStyleSheet = null; | 
 |  323  | 
|  247     let {selector} = filter; |  324     let {selector} = filter; | 
|  248  |  325  | 
|  249     // Unconditially applied element hiding filters |  326     // Unconditially applied element hiding filters | 
|  250     if (filterBySelector.get(selector) == filter) |  327     if (filterBySelector.get(selector) == filter) | 
|  251     { |  328     { | 
|  252       filterBySelector.delete(selector); |  329       filterBySelector.delete(selector); | 
|  253       unconditionalSelectors = null; |  330       unconditionalSelectors = null; | 
|  254       defaultStyleSheet = null; |  331       defaultStyleSheet = null; | 
|  255     } |  332     } | 
|  256     // Conditionally applied element hiding filters |  333     // Conditionally applied element hiding filters | 
| (...skipping 29 matching lines...) Expand all  Loading... | 
|  286    * |  363    * | 
|  287    * @param {string} domain The domain. |  364    * @param {string} domain The domain. | 
|  288    * @param {boolean} [specificOnly=false] Whether selectors from generic |  365    * @param {boolean} [specificOnly=false] Whether selectors from generic | 
|  289    *   filters should be included. |  366    *   filters should be included. | 
|  290    * |  367    * | 
|  291    * @returns {ElemHideStyleSheet} An object containing the CSS code and the |  368    * @returns {ElemHideStyleSheet} An object containing the CSS code and the | 
|  292    *   list of selectors. |  369    *   list of selectors. | 
|  293    */ |  370    */ | 
|  294   generateStyleSheetForDomain(domain, specificOnly = false) |  371   generateStyleSheetForDomain(domain, specificOnly = false) | 
|  295   { |  372   { | 
|  296     let selectors = getConditionalSelectorsForDomain(domain, specificOnly); |  373     let knownSuffix = domain ? getKnownSuffix(domain) : ""; | 
 |  374  | 
 |  375     let selectors = getConditionalSelectors(knownSuffix, specificOnly); | 
|  297     let code = specificOnly ? createStyleSheet(selectors) : |  376     let code = specificOnly ? createStyleSheet(selectors) : | 
|  298                  (getDefaultStyleSheet() + createStyleSheet(selectors)); |  377                  knownSuffix == "" ? getCommonStyleSheet() : | 
 |  378                    (getDefaultStyleSheet() + createStyleSheet(selectors)); | 
|  299  |  379  | 
|  300     if (!specificOnly) |  380     if (!specificOnly) | 
|  301       selectors = getUnconditionalSelectors().concat(selectors); |  381       selectors = getUnconditionalSelectors().concat(selectors); | 
|  302  |  382  | 
|  303     return {code, selectors}; |  383     return {code, selectors}; | 
|  304   } |  384   } | 
|  305 }; |  385 }; | 
|  306  |  386  | 
|  307 /** |  387 /** | 
|  308  * Splits a list of selectors into groups determined by the value of |  388  * Splits a list of selectors into groups determined by the value of | 
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
|  355 { |  435 { | 
|  356   let styleSheet = ""; |  436   let styleSheet = ""; | 
|  357  |  437  | 
|  358   for (let selectorGroup of splitSelectors(selectors)) |  438   for (let selectorGroup of splitSelectors(selectors)) | 
|  359     styleSheet += createRule(selectorGroup); |  439     styleSheet += createRule(selectorGroup); | 
|  360  |  440  | 
|  361   return styleSheet; |  441   return styleSheet; | 
|  362 } |  442 } | 
|  363  |  443  | 
|  364 exports.createStyleSheet = createStyleSheet; |  444 exports.createStyleSheet = createStyleSheet; | 
| OLD | NEW |