| Left: | ||
| Right: |
| 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 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 49 | 49 |
| 50 /** | 50 /** |
| 51 * This array caches the keys of filterBySelector table (selectors | 51 * This array caches the keys of filterBySelector table (selectors |
| 52 * which unconditionally apply on all domains). It will be null if the | 52 * which unconditionally apply on all domains). It will be null if the |
| 53 * cache needs to be rebuilt. | 53 * cache needs to be rebuilt. |
| 54 * @type {?string[]} | 54 * @type {?string[]} |
| 55 */ | 55 */ |
| 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 | |
| 60 * value of <code>{@link unconditionalSelectors}</code>. | |
| 61 * @type {?string} | |
| 62 */ | |
| 63 let defaultStyleSheet = null; | |
| 64 | |
| 65 /** | |
| 59 * Map to be used instead when a filter has a blank domains property. | 66 * Map to be used instead when a filter has a blank domains property. |
| 60 * @type {Map.<string,boolean>} | 67 * @type {Map.<string,boolean>} |
| 61 * @const | 68 * @const |
| 62 */ | 69 */ |
| 63 let defaultDomains = new Map([["", true]]); | 70 let defaultDomains = new Map([["", true]]); |
| 64 | 71 |
| 65 /** | 72 /** |
| 66 * Set containing known element hiding filters | 73 * Set containing known element hiding filters |
| 67 * @type {Set.<ElemHideFilter>} | 74 * @type {Set.<ElemHideFilter>} |
| 68 */ | 75 */ |
| (...skipping 24 matching lines...) Expand all Loading... | |
| 93 * @returns {string[]} | 100 * @returns {string[]} |
| 94 */ | 101 */ |
| 95 function getUnconditionalSelectors() | 102 function getUnconditionalSelectors() |
| 96 { | 103 { |
| 97 if (!unconditionalSelectors) | 104 if (!unconditionalSelectors) |
| 98 unconditionalSelectors = [...filterBySelector.keys()]; | 105 unconditionalSelectors = [...filterBySelector.keys()]; |
| 99 | 106 |
| 100 return unconditionalSelectors; | 107 return unconditionalSelectors; |
| 101 } | 108 } |
| 102 | 109 |
| 110 /** | |
| 111 * 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. | |
| 113 * | |
| 114 * @param {string} domain The domain. | |
| 115 * @param {boolean} [specificOnly=false] Whether selectors from generic filters | |
| 116 * should be included. | |
| 117 * | |
| 118 * @returns {Array.<string>} The list of selectors. | |
| 119 */ | |
| 120 function getConditionalSelectorsForDomain(domain, specificOnly = false) | |
| 121 { | |
| 122 let selectors = []; | |
| 123 | |
| 124 let excluded = new Set(); | |
| 125 let currentDomain = domain ? domain.replace(/\.+$/, "").toLowerCase() : ""; | |
| 126 | |
| 127 // This code is a performance hot-spot, which is why we've made certain | |
| 128 // micro-optimisations. Please be careful before making changes. | |
| 129 while (true) | |
| 130 { | |
| 131 if (specificOnly && currentDomain == "") | |
| 132 break; | |
| 133 | |
| 134 let filters = filtersByDomain.get(currentDomain); | |
| 135 if (filters) | |
| 136 { | |
| 137 for (let [filter, isIncluded] of filters) | |
| 138 { | |
| 139 if (!isIncluded) | |
| 140 { | |
| 141 excluded.add(filter); | |
| 142 } | |
| 143 else | |
| 144 { | |
| 145 let {selector} = filter; | |
| 146 if ((excluded.size == 0 || !excluded.has(filter)) && | |
| 147 !ElemHideExceptions.getException(selector, domain)) | |
| 148 { | |
| 149 selectors.push(selector); | |
| 150 } | |
| 151 } | |
| 152 } | |
| 153 } | |
| 154 | |
| 155 if (currentDomain == "") | |
| 156 break; | |
| 157 | |
| 158 let nextDot = currentDomain.indexOf("."); | |
| 159 currentDomain = nextDot == -1 ? "" : currentDomain.substr(nextDot + 1); | |
| 160 } | |
| 161 | |
| 162 return selectors; | |
| 163 } | |
| 164 | |
| 165 /** | |
| 166 * Returns the default style sheet that applies on all domains. | |
| 167 * @returns {string} | |
| 168 */ | |
| 169 function getDefaultStyleSheet() | |
| 170 { | |
| 171 if (!defaultStyleSheet) | |
| 172 defaultStyleSheet = createStyleSheet(getUnconditionalSelectors()); | |
| 173 | |
| 174 return defaultStyleSheet; | |
| 175 } | |
| 176 | |
| 103 ElemHideExceptions.on("added", ({selector}) => | 177 ElemHideExceptions.on("added", ({selector}) => |
| 104 { | 178 { |
| 105 // If this is the first exception for a previously unconditionally applied | 179 // If this is the first exception for a previously unconditionally applied |
| 106 // element hiding selector we need to take care to update the lookups. | 180 // element hiding selector we need to take care to update the lookups. |
| 107 let unconditionalFilterForSelector = filterBySelector.get(selector); | 181 let unconditionalFilterForSelector = filterBySelector.get(selector); |
| 108 if (unconditionalFilterForSelector) | 182 if (unconditionalFilterForSelector) |
| 109 { | 183 { |
| 110 addToFiltersByDomain(unconditionalFilterForSelector); | 184 addToFiltersByDomain(unconditionalFilterForSelector); |
| 111 filterBySelector.delete(selector); | 185 filterBySelector.delete(selector); |
| 112 unconditionalSelectors = null; | 186 unconditionalSelectors = null; |
| 187 defaultStyleSheet = null; | |
| 113 } | 188 } |
| 114 }); | 189 }); |
| 115 | 190 |
| 116 /** | 191 /** |
| 117 * Container for element hiding filters | 192 * Container for element hiding filters |
| 118 * @class | 193 * @class |
| 119 */ | 194 */ |
| 120 exports.ElemHide = { | 195 exports.ElemHide = { |
| 121 /** | 196 /** |
| 122 * Removes all known filters | 197 * Removes all known filters |
| 123 */ | 198 */ |
| 124 clear() | 199 clear() |
| 125 { | 200 { |
| 126 for (let collection of [filtersByDomain, filterBySelector, knownFilters]) | 201 for (let collection of [filtersByDomain, filterBySelector, knownFilters]) |
| 127 collection.clear(); | 202 collection.clear(); |
| 128 | 203 |
| 129 unconditionalSelectors = null; | 204 unconditionalSelectors = null; |
| 205 defaultStyleSheet = null; | |
| 206 | |
| 130 filterNotifier.emit("elemhideupdate"); | 207 filterNotifier.emit("elemhideupdate"); |
| 131 }, | 208 }, |
| 132 | 209 |
| 133 /** | 210 /** |
| 134 * Add a new element hiding filter | 211 * Add a new element hiding filter |
| 135 * @param {ElemHideFilter} filter | 212 * @param {ElemHideFilter} filter |
| 136 */ | 213 */ |
| 137 add(filter) | 214 add(filter) |
| 138 { | 215 { |
| 139 if (knownFilters.has(filter)) | 216 if (knownFilters.has(filter)) |
| 140 return; | 217 return; |
| 141 | 218 |
| 142 let {selector} = filter; | 219 let {selector} = filter; |
| 143 | 220 |
| 144 if (!(filter.domains || ElemHideExceptions.hasExceptions(selector))) | 221 if (!(filter.domains || ElemHideExceptions.hasExceptions(selector))) |
| 145 { | 222 { |
| 146 // The new filter's selector is unconditionally applied to all domains | 223 // The new filter's selector is unconditionally applied to all domains |
| 147 filterBySelector.set(selector, filter); | 224 filterBySelector.set(selector, filter); |
| 148 unconditionalSelectors = null; | 225 unconditionalSelectors = null; |
| 226 defaultStyleSheet = null; | |
| 149 } | 227 } |
| 150 else | 228 else |
| 151 { | 229 { |
| 152 // The new filter's selector only applies to some domains | 230 // The new filter's selector only applies to some domains |
| 153 addToFiltersByDomain(filter); | 231 addToFiltersByDomain(filter); |
| 154 } | 232 } |
| 155 | 233 |
| 156 knownFilters.add(filter); | 234 knownFilters.add(filter); |
| 157 filterNotifier.emit("elemhideupdate"); | 235 filterNotifier.emit("elemhideupdate"); |
| 158 }, | 236 }, |
| 159 | 237 |
| 160 /** | 238 /** |
| 161 * Removes an element hiding filter | 239 * Removes an element hiding filter |
| 162 * @param {ElemHideFilter} filter | 240 * @param {ElemHideFilter} filter |
| 163 */ | 241 */ |
| 164 remove(filter) | 242 remove(filter) |
| 165 { | 243 { |
| 166 if (!knownFilters.has(filter)) | 244 if (!knownFilters.has(filter)) |
| 167 return; | 245 return; |
| 168 | 246 |
| 169 let {selector} = filter; | 247 let {selector} = filter; |
| 170 | 248 |
| 171 // Unconditially applied element hiding filters | 249 // Unconditially applied element hiding filters |
| 172 if (filterBySelector.get(selector) == filter) | 250 if (filterBySelector.get(selector) == filter) |
| 173 { | 251 { |
| 174 filterBySelector.delete(selector); | 252 filterBySelector.delete(selector); |
| 175 unconditionalSelectors = null; | 253 unconditionalSelectors = null; |
| 254 defaultStyleSheet = null; | |
| 176 } | 255 } |
| 177 // Conditionally applied element hiding filters | 256 // Conditionally applied element hiding filters |
| 178 else | 257 else |
| 179 { | 258 { |
| 180 let domains = filter.domains || defaultDomains; | 259 let domains = filter.domains || defaultDomains; |
| 181 for (let domain of domains.keys()) | 260 for (let domain of domains.keys()) |
| 182 { | 261 { |
| 183 let filters = filtersByDomain.get(domain); | 262 let filters = filtersByDomain.get(domain); |
| 184 if (filters) | 263 if (filters) |
| 185 { | 264 { |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 197 | 276 |
| 198 /** | 277 /** |
| 199 * Determines from the current filter list which selectors should be applied | 278 * Determines from the current filter list which selectors should be applied |
| 200 * on a particular host name. | 279 * on a particular host name. |
| 201 * @param {string} domain | 280 * @param {string} domain |
| 202 * @param {boolean} [specificOnly] true if generic filters should not apply. | 281 * @param {boolean} [specificOnly] true if generic filters should not apply. |
| 203 * @returns {string[]} List of selectors. | 282 * @returns {string[]} List of selectors. |
| 204 */ | 283 */ |
| 205 getSelectorsForDomain(domain, specificOnly = false) | 284 getSelectorsForDomain(domain, specificOnly = false) |
| 206 { | 285 { |
| 207 let selectors = []; | 286 let selectors = getConditionalSelectorsForDomain(domain, specificOnly); |
|
Manish Jethani
2018/09/20 12:27:24
This entire block of code is moved into getConditi
| |
| 208 | |
| 209 let excluded = new Set(); | |
| 210 let currentDomain = domain ? domain.replace(/\.+$/, "").toLowerCase() : ""; | |
| 211 | |
| 212 // This code is a performance hot-spot, which is why we've made certain | |
| 213 // micro-optimisations. Please be careful before making changes. | |
| 214 while (true) | |
| 215 { | |
| 216 if (specificOnly && currentDomain == "") | |
| 217 break; | |
| 218 | |
| 219 let filters = filtersByDomain.get(currentDomain); | |
| 220 if (filters) | |
| 221 { | |
| 222 for (let [filter, isIncluded] of filters) | |
| 223 { | |
| 224 if (!isIncluded) | |
| 225 { | |
| 226 excluded.add(filter); | |
| 227 } | |
| 228 else | |
| 229 { | |
| 230 let {selector} = filter; | |
| 231 if ((excluded.size == 0 || !excluded.has(filter)) && | |
| 232 !ElemHideExceptions.getException(selector, domain)) | |
| 233 { | |
| 234 selectors.push(selector); | |
| 235 } | |
| 236 } | |
| 237 } | |
| 238 } | |
| 239 | |
| 240 if (currentDomain == "") | |
| 241 break; | |
| 242 | |
| 243 let nextDot = currentDomain.indexOf("."); | |
| 244 currentDomain = nextDot == -1 ? "" : currentDomain.substr(nextDot + 1); | |
| 245 } | |
| 246 | 287 |
| 247 if (!specificOnly) | 288 if (!specificOnly) |
| 248 selectors = getUnconditionalSelectors().concat(selectors); | 289 selectors = getUnconditionalSelectors().concat(selectors); |
| 249 | 290 |
| 250 return selectors; | 291 return selectors; |
| 292 }, | |
| 293 | |
| 294 /** | |
| 295 * @typedef {object} ElemHideStyleSheet | |
| 296 * @property {string} code CSS code. | |
| 297 * @property {Array.<string>} selectors List of selectors. | |
| 298 */ | |
| 299 | |
| 300 /** | |
| 301 * Generates a style sheet for a given domain based on the current set of | |
| 302 * filters. | |
| 303 * | |
| 304 * @param {string} domain The domain. | |
| 305 * @param {boolean} [specificOnly=false] Whether selectors from generic | |
| 306 * filters should be included. | |
| 307 * | |
| 308 * @returns {ElemHideStyleSheet} An object containing the CSS code and the | |
| 309 * list of selectors. | |
| 310 */ | |
| 311 generateStyleSheetForDomain(domain, specificOnly = false) | |
|
Manish Jethani
2018/09/20 12:27:24
The idea is that calling this function will be fas
Jon Sonesen
2018/09/23 18:01:59
Acknowledged.
| |
| 312 { | |
| 313 let selectors = getConditionalSelectorsForDomain(domain, specificOnly); | |
| 314 let code = (specificOnly ? "" : getDefaultStyleSheet()) + | |
| 315 createStyleSheet(selectors); | |
|
Jon Sonesen
2018/09/23 18:01:59
Regarding the use of this ternary operation, I won
Manish Jethani
2018/09/24 11:51:14
This function is supposed to be very performance c
Jon Sonesen
2018/09/24 15:09:44
Ah yeah, that's a good point here. My bad, haha I
Manish Jethani
2018/09/27 15:58:29
So I tested this by the way, it seems the Patch Se
Manish Jethani
2018/09/27 15:58:29
Acknowledged.
| |
| 316 | |
| 317 if (!specificOnly) | |
| 318 selectors = getUnconditionalSelectors().concat(selectors); | |
| 319 | |
| 320 return {code, selectors}; | |
| 251 } | 321 } |
| 252 }; | 322 }; |
| 253 | 323 |
| 254 /** | 324 /** |
| 255 * Splits a list of selectors into groups determined by the value of | 325 * Splits a list of selectors into groups determined by the value of |
| 256 * <code>{@link selectorGroupSize}</code>. | 326 * <code>{@link selectorGroupSize}</code>. |
| 257 * | 327 * |
| 258 * @param {Array.<string>} selectors | 328 * @param {Array.<string>} selectors |
| 259 * @yields {Array.<string>} | 329 * @yields {Array.<string>} |
| 260 */ | 330 */ |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 298 { | 368 { |
| 299 let styleSheet = ""; | 369 let styleSheet = ""; |
| 300 | 370 |
| 301 for (let rule of createRules(selectors)) | 371 for (let rule of createRules(selectors)) |
| 302 styleSheet += rule + "\n"; | 372 styleSheet += rule + "\n"; |
| 303 | 373 |
| 304 return styleSheet; | 374 return styleSheet; |
| 305 } | 375 } |
| 306 | 376 |
| 307 exports.createStyleSheet = createStyleSheet; | 377 exports.createStyleSheet = createStyleSheet; |
| OLD | NEW |