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 |