| 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 24 matching lines...) Expand all  Loading... | 
| 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 | 
| 41 /** | 41 /** | 
| 42  * This array caches the keys of filterBySelector table (selectors | 42  * This array caches the keys of filterBySelector table (selectors | 
| 43  * which unconditionally apply on all domains). It will be null if the | 43  * which unconditionally apply on all domains). It will be null if the | 
| 44  * cache needs to be rebuilt. | 44  * cache needs to be rebuilt. | 
|  | 45  * @type {?string[]} | 
| 45  */ | 46  */ | 
| 46 let unconditionalSelectors = null; | 47 let unconditionalSelectors = null; | 
| 47 | 48 | 
| 48 /** | 49 /** | 
| 49  * Map to be used instead when a filter has a blank domains property. | 50  * Map to be used instead when a filter has a blank domains property. | 
|  | 51  * @type {Map.<string,boolean>} | 
|  | 52  * @const | 
| 50  */ | 53  */ | 
| 51 let defaultDomains = new Map([["", true]]); | 54 let defaultDomains = new Map([["", true]]); | 
| 52 | 55 | 
| 53 /** | 56 /** | 
| 54  * Set containing known element hiding and exception filters | 57  * Set containing known element hiding and exception filters | 
| 55  * @type {Set.<ElemHideBase>} | 58  * @type {Set.<ElemHideBase>} | 
| 56  */ | 59  */ | 
| 57 let knownFilters = new Set(); | 60 let knownFilters = new Set(); | 
| 58 | 61 | 
| 59 /** | 62 /** | 
| 60  * Lookup table, lists of element hiding exceptions by selector | 63  * Lookup table, lists of element hiding exceptions by selector | 
| 61  * @type {Map.<string,Filter>} | 64  * @type {Map.<string,Filter[]>} | 
| 62  */ | 65  */ | 
| 63 let exceptions = new Map(); | 66 let exceptions = new Map(); | 
| 64 | 67 | 
| 65 /* | 68 /** | 
| 66  * Set containing selectors with generic exceptions | 69  * Lookup table, lists of generic element hiding exceptions by selector | 
| 67  * @type {Set.<string>} | 70  * @type {Map.<string,Filter[]>} | 
| 68  */ | 71  */ | 
| 69 let genericExceptionSelectors = new Set(); | 72 let genericExceptions = new Map(); | 
| 70 | 73 | 
| 71 /* | 74 /** | 
|  | 75  * List of selectors that apply on any unknown domain | 
|  | 76  * @type {?string[]} | 
|  | 77  */ | 
|  | 78 let conditionalGenericSelectors = null; | 
|  | 79 | 
|  | 80 /** | 
|  | 81  * Adds a filter to the lookup table of filters by domain. | 
|  | 82  * @param {Filter} filter | 
|  | 83  */ | 
|  | 84 function addToFiltersByDomain(filter) | 
|  | 85 { | 
|  | 86   let domains = filter.domains || defaultDomains; | 
|  | 87   if (filter instanceof ElemHideException) | 
|  | 88   { | 
|  | 89     for (let domain of domains.keys()) | 
|  | 90     { | 
|  | 91       // Add an entry for each domain, but without any filters. This makes | 
|  | 92       // the domain "known" and helps us avoid certain optimizations that | 
|  | 93       // would otherwise yield incorrect results. | 
|  | 94       if (domain != "" && !filtersByDomain.has(domain)) | 
|  | 95         filtersByDomain.set(domain, null); | 
|  | 96     } | 
|  | 97   } | 
|  | 98   else | 
|  | 99   { | 
|  | 100     for (let [domain, isIncluded] of domains) | 
|  | 101     { | 
|  | 102       // There's no need to note that a filter is generically disabled. | 
|  | 103       if (!isIncluded && domain == "") | 
|  | 104         continue; | 
|  | 105 | 
|  | 106       let filters = filtersByDomain.get(domain); | 
|  | 107       if (!filters) | 
|  | 108         filtersByDomain.set(domain, filters = new Map()); | 
|  | 109       filters.set(filter, isIncluded); | 
|  | 110     } | 
|  | 111   } | 
|  | 112 } | 
|  | 113 | 
|  | 114 /** | 
| 72  * Returns a list of domain-specific filters matching a domain | 115  * Returns a list of domain-specific filters matching a domain | 
| 73  * @param {string} [domain] | 116  * @param {string} [domain] | 
| 74  * @returns {Array.<?Map.<Filter,boolean>>} | 117  * @returns {Array.<?Map.<Filter,boolean>>} | 
| 75  */ | 118  */ | 
| 76 function getSpecificFiltersForDomain(domain) | 119 function getSpecificFiltersForDomain(domain) | 
| 77 { | 120 { | 
| 78   let filtersList = []; | 121   let filtersList = []; | 
| 79 | 122 | 
| 80   if (domain) | 123   if (domain) | 
| 81     domain = domain.toUpperCase(); | 124     domain = domain.toUpperCase(); | 
| 82 | 125 | 
| 83   while (domain) | 126   while (domain) | 
| 84   { | 127   { | 
| 85     // Note that we also push null values into the list, because |  | 
| 86     // ElemHide.getSelectorsForDomain still needs to know if there are any |  | 
| 87     // entries for the domain. |  | 
| 88     let filters = filtersByDomain.get(domain); | 128     let filters = filtersByDomain.get(domain); | 
| 89     if (typeof filters != "undefined") | 129     if (typeof filters != "undefined") | 
| 90       filtersList.push(filters); | 130       filtersList.push(filters); | 
| 91 | 131 | 
| 92     let nextDot = domain.indexOf("."); | 132     let nextDot = domain.indexOf("."); | 
| 93     domain = nextDot == -1 ? null : domain.substring(nextDot + 1); | 133     domain = nextDot == -1 ? null : domain.substring(nextDot + 1); | 
| 94   } | 134   } | 
| 95 | 135 | 
| 96   return filtersList; | 136   return filtersList; | 
| 97 } | 137 } | 
| 98 | 138 | 
| 99 /** | 139 /** | 
| 100  * 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 | 
| 101  * domain | 141  * filters | 
| 102  * @param {string} [domain] | 142  * @param {string} [domain] | 
| 103  * @param {Array.<?Map.<Filter,boolean>>} filtersList | 143  * @param {Array.<?Map.<Filter,boolean>>} filtersList | 
|  | 144  * @param {Set.<Filter>} excludeSet | 
| 104  * @returns {string[]} | 145  * @returns {string[]} | 
| 105  */ | 146  */ | 
| 106 function getConditionalSelectorsForDomain(domain, filtersList) | 147 function matchSelectors(domain, filtersList, excludeSet) | 
| 107 { | 148 { | 
| 108   let selectors = []; | 149   let matches = []; | 
| 109 |  | 
| 110   let excluded = new Set(); |  | 
| 111 | 150 | 
| 112   // 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 | 
| 113   // micro-optimisations. Please be careful before making changes. | 152   // micro-optimisations. Please be careful before making changes. | 
| 114   for (let i = 0; i < filtersList.length; i++) | 153   for (let i = 0; i < filtersList.length; i++) | 
| 115   { | 154   { | 
| 116     if (!filtersList[i]) | 155     let filters = filtersList[i]; | 
| 117       continue; | 156     if (filters) | 
| 118 | 157     { | 
| 119     for (let [filter, isIncluded] of filtersList[i]) | 158       for (let [filter, isIncluded] of filters) | 
| 120     { |  | 
| 121       if (!isIncluded) |  | 
| 122       { | 159       { | 
| 123         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         } | 
| 124       } | 169       } | 
| 125       else if ((excluded.size == 0 || !excluded.has(filter)) && | 170     } | 
| 126                !ElemHide.getException(filter, domain)) | 171   } | 
| 127       { | 172 | 
| 128         selectors.push(filter.selector); | 173   return matches; | 
| 129       } |  | 
| 130     } |  | 
| 131   } |  | 
| 132 |  | 
| 133   return selectors; |  | 
| 134 } | 174 } | 
| 135 | 175 | 
| 136 /* | 176 /** | 
| 137  * Returns a list of selectors that apply on any unknown domain | 177  * Returns a list of selectors that apply on any unknown domain | 
| 138  * @returns {string[]} | 178  * @returns {string[]} | 
| 139  */ | 179  */ | 
| 140 function getConditionalGenericSelectors() | 180 function getConditionalGenericSelectors() | 
| 141 { | 181 { | 
| 142   let selectors = []; | 182   if (conditionalGenericSelectors) | 
|  | 183     return conditionalGenericSelectors; | 
|  | 184 | 
|  | 185   conditionalGenericSelectors = []; | 
| 143 | 186 | 
| 144   let filters = filtersByDomain.get(""); | 187   let filters = filtersByDomain.get(""); | 
| 145   if (!filters) | 188   if (!filters) | 
| 146     return selectors; | 189     return conditionalGenericSelectors; | 
| 147 | 190 | 
| 148   for (let {selector} of filters.keys()) | 191   for (let {selector} of filters.keys()) | 
| 149   { | 192   { | 
| 150     if (genericExceptionSelectors.size == 0 || | 193     if (genericExceptions.size == 0 || !genericExceptions.has(selector)) | 
| 151         !genericExceptionSelectors.has(selector)) | 194       conditionalGenericSelectors.push(selector); | 
| 152     { | 195   } | 
| 153       selectors.push(selector); | 196 | 
| 154     } | 197   return conditionalGenericSelectors; | 
| 155   } |  | 
| 156 |  | 
| 157   return selectors; |  | 
| 158 } | 198 } | 
| 159 | 199 | 
| 160 /** | 200 /** | 
| 161  * Returns a list of selectors that apply on each website unconditionally. | 201  * Returns a list of selectors that apply on each website unconditionally. | 
| 162  * @returns {string[]} | 202  * @returns {string[]} | 
| 163  */ | 203  */ | 
| 164 function getUnconditionalSelectors() | 204 function getUnconditionalSelectors() | 
| 165 { | 205 { | 
| 166   if (!unconditionalSelectors) | 206   if (!unconditionalSelectors) | 
| 167     unconditionalSelectors = [...filterBySelector.keys()]; | 207     unconditionalSelectors = [...filterBySelector.keys()]; | 
| 168 | 208 | 
| 169   return unconditionalSelectors; | 209   return unconditionalSelectors; | 
| 170 } | 210 } | 
| 171 | 211 | 
| 172 /** | 212 /** | 
| 173  * Container for element hiding filters | 213  * Container for element hiding filters | 
| 174  * @class | 214  * @class | 
| 175  */ | 215  */ | 
| 176 let ElemHide = exports.ElemHide = { | 216 exports.ElemHide = { | 
| 177   /** | 217   /** | 
| 178    * Removes all known filters | 218    * Removes all known filters | 
| 179    */ | 219    */ | 
| 180   clear() | 220   clear() | 
| 181   { | 221   { | 
| 182     for (let collection of [filtersByDomain, filterBySelector, | 222     for (let collection of [filtersByDomain, filterBySelector, | 
| 183                             knownFilters, exceptions, | 223                             knownFilters, exceptions, | 
| 184                             genericExceptionSelectors]) | 224                             genericExceptions]) | 
| 185     { | 225     { | 
| 186       collection.clear(); | 226       collection.clear(); | 
| 187     } | 227     } | 
| 188     unconditionalSelectors = null; | 228     unconditionalSelectors = null; | 
|  | 229     conditionalGenericSelectors = null; | 
| 189     FilterNotifier.emit("elemhideupdate"); | 230     FilterNotifier.emit("elemhideupdate"); | 
| 190   }, |  | 
| 191 |  | 
| 192   _addToFiltersByDomain(filter) |  | 
| 193   { |  | 
| 194     let domains = filter.domains || defaultDomains; |  | 
| 195     if (filter instanceof ElemHideException) |  | 
| 196     { |  | 
| 197       for (let domain of domains.keys()) |  | 
| 198       { |  | 
| 199         // Add an entry for each domain, but without any filters. This makes |  | 
| 200         // the domain "known" and helps us avoid certain optimizations that |  | 
| 201         // would otherwise yield incorrect results. |  | 
| 202         if (domain != "" && !filtersByDomain.has(domain)) |  | 
| 203           filtersByDomain.set(domain, null); |  | 
| 204       } |  | 
| 205     } |  | 
| 206     else |  | 
| 207     { |  | 
| 208       for (let [domain, isIncluded] of domains) |  | 
| 209       { |  | 
| 210         // There's no need to note that a filter is generically disabled. |  | 
| 211         if (!isIncluded && domain == "") |  | 
| 212           continue; |  | 
| 213 |  | 
| 214         let filters = filtersByDomain.get(domain); |  | 
| 215         if (!filters) |  | 
| 216           filtersByDomain.set(domain, filters = new Map()); |  | 
| 217         filters.set(filter, isIncluded); |  | 
| 218       } |  | 
| 219     } |  | 
| 220   }, | 231   }, | 
| 221 | 232 | 
| 222   /** | 233   /** | 
| 223    * Add a new element hiding filter | 234    * Add a new element hiding filter | 
| 224    * @param {ElemHideBase} filter | 235    * @param {ElemHideBase} filter | 
| 225    */ | 236    */ | 
| 226   add(filter) | 237   add(filter) | 
| 227   { | 238   { | 
| 228     if (knownFilters.has(filter)) | 239     if (knownFilters.has(filter)) | 
| 229       return; | 240       return; | 
| 230 | 241 | 
|  | 242     conditionalGenericSelectors = null; | 
|  | 243 | 
| 231     if (filter instanceof ElemHideException) | 244     if (filter instanceof ElemHideException) | 
| 232     { | 245     { | 
| 233       let {selector, domains} = filter; | 246       let {selector, domains} = filter; | 
| 234 | 247 | 
| 235       let list = exceptions.get(selector); | 248       let list = exceptions.get(selector); | 
| 236       if (list) | 249       if (list) | 
| 237         list.push(filter); | 250         list.push(filter); | 
| 238       else | 251       else | 
| 239         exceptions.set(selector, [filter]); | 252         exceptions.set(selector, [filter]); | 
| 240 | 253 | 
| 241       if (domains) | 254       if (domains) | 
| 242         this._addToFiltersByDomain(filter); | 255         addToFiltersByDomain(filter); | 
| 243 | 256 | 
| 244       if (filter.isGeneric()) | 257       if (filter.isGeneric()) | 
| 245         genericExceptionSelectors.add(filter.selector); | 258       { | 
|  | 259         list = genericExceptions.get(selector); | 
|  | 260         if (list) | 
|  | 261           list.push(filter); | 
|  | 262         else | 
|  | 263           genericExceptions.set(selector, [filter]); | 
|  | 264       } | 
| 246 | 265 | 
| 247       // If this is the first exception for a previously unconditionally | 266       // If this is the first exception for a previously unconditionally | 
| 248       // applied element hiding selector we need to take care to update the | 267       // applied element hiding selector we need to take care to update the | 
| 249       // lookups. | 268       // lookups. | 
| 250       let unconditionalFilterForSelector = filterBySelector.get(selector); | 269       let unconditionalFilterForSelector = filterBySelector.get(selector); | 
| 251       if (unconditionalFilterForSelector) | 270       if (unconditionalFilterForSelector) | 
| 252       { | 271       { | 
| 253         this._addToFiltersByDomain(unconditionalFilterForSelector); | 272         addToFiltersByDomain(unconditionalFilterForSelector); | 
| 254         filterBySelector.delete(selector); | 273         filterBySelector.delete(selector); | 
| 255         unconditionalSelectors = null; | 274         unconditionalSelectors = null; | 
| 256       } | 275       } | 
| 257     } | 276     } | 
| 258     else if (!(filter.domains || exceptions.has(filter.selector))) | 277     else if (!(filter.domains || exceptions.has(filter.selector))) | 
| 259     { | 278     { | 
| 260       // The new filter's selector is unconditionally applied to all domains | 279       // The new filter's selector is unconditionally applied to all domains | 
| 261       filterBySelector.set(filter.selector, filter); | 280       filterBySelector.set(filter.selector, filter); | 
| 262       unconditionalSelectors = null; | 281       unconditionalSelectors = null; | 
| 263     } | 282     } | 
| 264     else | 283     else | 
| 265     { | 284     { | 
| 266       // The new filter's selector only applies to some domains | 285       // The new filter's selector only applies to some domains | 
| 267       this._addToFiltersByDomain(filter); | 286       addToFiltersByDomain(filter); | 
| 268     } | 287     } | 
| 269 | 288 | 
| 270     knownFilters.add(filter); | 289     knownFilters.add(filter); | 
| 271     FilterNotifier.emit("elemhideupdate"); | 290     FilterNotifier.emit("elemhideupdate"); | 
| 272   }, | 291   }, | 
| 273 | 292 | 
| 274   /** | 293   /** | 
| 275    * Removes an element hiding filter | 294    * Removes an element hiding filter | 
| 276    * @param {ElemHideBase} filter | 295    * @param {ElemHideBase} filter | 
| 277    */ | 296    */ | 
| 278   remove(filter) | 297   remove(filter) | 
| 279   { | 298   { | 
| 280     if (!knownFilters.has(filter)) | 299     if (!knownFilters.has(filter)) | 
| 281       return; | 300       return; | 
| 282 | 301 | 
|  | 302     conditionalGenericSelectors = null; | 
|  | 303 | 
| 283     // Whitelisting filters | 304     // Whitelisting filters | 
| 284     if (filter instanceof ElemHideException) | 305     if (filter instanceof ElemHideException) | 
| 285     { | 306     { | 
| 286       let list = exceptions.get(filter.selector); | 307       let list = exceptions.get(filter.selector); | 
| 287       let index = list.indexOf(filter); | 308       let index = list.indexOf(filter); | 
| 288       if (index >= 0) | 309       if (index >= 0) | 
| 289         list.splice(index, 1); | 310         list.splice(index, 1); | 
| 290 | 311 | 
| 291       if (filter.isGeneric()) | 312       if (filter.isGeneric()) | 
| 292         genericExceptionSelectors.delete(filter.selector); | 313       { | 
|  | 314         list = genericExceptions.get(filter.selector); | 
|  | 315         index = list.indexOf(filter); | 
|  | 316         if (index >= 0) | 
|  | 317           list.splice(index, 1); | 
|  | 318 | 
|  | 319         // It's important to delete the entry here so the selector no longer | 
|  | 320         // appears to have any generic exceptions. | 
|  | 321         if (list.length == 0) | 
|  | 322           genericExceptions.delete(filter.selector); | 
|  | 323       } | 
| 293     } | 324     } | 
| 294     // Unconditially applied element hiding filters | 325     // Unconditially applied element hiding filters | 
| 295     else if (filterBySelector.get(filter.selector) == filter) | 326     else if (filterBySelector.get(filter.selector) == filter) | 
| 296     { | 327     { | 
| 297       filterBySelector.delete(filter.selector); | 328       filterBySelector.delete(filter.selector); | 
| 298       unconditionalSelectors = null; | 329       unconditionalSelectors = null; | 
| 299     } | 330     } | 
| 300     // Conditionally applied element hiding filters | 331     // Conditionally applied element hiding filters | 
| 301     else | 332     else | 
| 302     { | 333     { | 
| 303       let domains = filter.domains || defaultDomains; | 334       let domains = filter.domains || defaultDomains; | 
| 304       for (let domain of domains.keys()) | 335       for (let domain of domains.keys()) | 
| 305       { | 336       { | 
| 306         let filters = filtersByDomain.get(domain); | 337         let filters = filtersByDomain.get(domain); | 
| 307         if (filters) | 338         if (filters) | 
| 308           filters.delete(filter); | 339           filters.delete(filter); | 
| 309       } | 340       } | 
| 310     } | 341     } | 
| 311 | 342 | 
| 312     knownFilters.delete(filter); | 343     knownFilters.delete(filter); | 
| 313     FilterNotifier.emit("elemhideupdate"); | 344     FilterNotifier.emit("elemhideupdate"); | 
| 314   }, | 345   }, | 
| 315 | 346 | 
| 316   /** | 347   /** | 
| 317    * Checks whether an exception rule is registered for a filter on a particular | 348    * Checks whether an exception rule is registered for a filter on a particular | 
| 318    * domain. | 349    * domain. | 
| 319    * @param {Filter} filter | 350    * @param {Filter} filter | 
| 320    * @param {string} docDomain | 351    * @param {?string} docDomain | 
| 321    * @return {ElemHideException} | 352    * @return {?ElemHideException} | 
| 322    */ | 353    */ | 
| 323   getException(filter, docDomain) | 354   getException(filter, docDomain) | 
| 324   { | 355   { | 
| 325     let list = exceptions.get(filter.selector); | 356     let list = exceptions.get(filter.selector); | 
| 326     if (!list) | 357     if (!list) | 
| 327       return null; | 358       return null; | 
| 328 | 359 | 
| 329     for (let i = list.length - 1; i >= 0; i--) | 360     for (let i = list.length - 1; i >= 0; i--) | 
| 330     { | 361     { | 
| 331       if (list[i].isActiveOnDomain(docDomain)) | 362       if (list[i].isActiveOnDomain(docDomain)) | 
| 332         return list[i]; | 363         return list[i]; | 
| 333     } | 364     } | 
| 334 | 365 | 
| 335     return null; | 366     return null; | 
| 336   }, | 367   }, | 
| 337 |  | 
| 338   /** |  | 
| 339    * Constant used by getSelectorsForDomain to return all selectors applying to |  | 
| 340    * a particular hostname. |  | 
| 341    */ |  | 
| 342   ALL_MATCHING: 0, |  | 
| 343 |  | 
| 344   /** |  | 
| 345    * Constant used by getSelectorsForDomain to exclude selectors which apply to |  | 
| 346    * all websites without exception. |  | 
| 347    */ |  | 
| 348   NO_UNCONDITIONAL: 1, |  | 
| 349 |  | 
| 350   /** |  | 
| 351    * Constant used by getSelectorsForDomain to return only selectors for filters |  | 
| 352    * which specifically match the given host name. |  | 
| 353    */ |  | 
| 354   SPECIFIC_ONLY: 2, |  | 
| 355 | 368 | 
| 356   /** | 369   /** | 
| 357    * Determines from the current filter list which selectors should be applied | 370    * Determines from the current filter list which selectors should be applied | 
| 358    * on a particular host name. | 371    * on a particular host name. | 
| 359    * @param {string} domain | 372    * @param {string} domain | 
| 360    * @param {number} [criteria] | 373    * @param {boolean} [specificOnly] true if generic filters should not apply. | 
| 361    *   One of the following: ElemHide.ALL_MATCHING, ElemHide.NO_UNCONDITIONAL or | 374    * @returns {string[]} List of selectors. | 
| 362    *                         ElemHide.SPECIFIC_ONLY. |  | 
| 363    * @returns {string[]} |  | 
| 364    *   List of selectors. |  | 
| 365    */ | 375    */ | 
| 366   getSelectorsForDomain(domain, criteria) | 376   getSelectorsForDomain(domain, specificOnly = false) | 
| 367   { | 377   { | 
| 368     let selectors = []; | 378     let specificFilters = getSpecificFiltersForDomain(domain); | 
| 369 | 379 | 
| 370     if (typeof criteria == "undefined") | 380     // If there are no specific filters (nor any specific exceptions), we can | 
| 371       criteria = ElemHide.ALL_MATCHING; | 381     // just return the selectors from all the generic filters modulo any | 
| 372 | 382     // generic exceptions. | 
| 373     let specificOnly = criteria >= ElemHide.SPECIFIC_ONLY; | 383     if (specificFilters.length == 0) | 
| 374     let filtersList = getSpecificFiltersForDomain(domain); | 384     { | 
| 375 | 385       return specificOnly ? [] : | 
| 376     if (filtersList.length > 0) | 386                getUnconditionalSelectors() | 
| 377     { | 387                .concat(getConditionalGenericSelectors()); | 
| 378       // We only got domain-specific filters, now add the generic filters. | 388     } | 
| 379       if (!specificOnly) | 389 | 
| 380         filtersList.push(filtersByDomain.get("")); | 390     let excluded = new Set(); | 
| 381 | 391     let selectors = matchSelectors(domain, specificFilters, excluded); | 
| 382       selectors = getConditionalSelectorsForDomain(domain, filtersList); | 392 | 
| 383     } | 393     if (specificOnly) | 
| 384     else if (!specificOnly) | 394       return selectors; | 
| 385     { | 395 | 
| 386       // Since there are no domain-specific filters matching this domain, we | 396     return getUnconditionalSelectors() | 
| 387       // can follow an optimized code path where we only check for generic | 397            .concat(selectors, | 
| 388       // filters and generic exceptions. | 398                    matchSelectors(domain, [filtersByDomain.get("")], | 
| 389       selectors = getConditionalGenericSelectors(); | 399                                   excluded)); | 
| 390     } |  | 
| 391 |  | 
| 392     if (criteria < ElemHide.NO_UNCONDITIONAL) |  | 
| 393       selectors = getUnconditionalSelectors().concat(selectors); |  | 
| 394 |  | 
| 395     return selectors; |  | 
| 396   } | 400   } | 
| 397 }; | 401 }; | 
| LEFT | RIGHT | 
|---|