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