Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Side by Side Diff: lib/elemHide.js

Issue 29935564: Issue 7452 - Do not cache element hiding filter objects by default Base URL: https://hg.adblockplus.org/adblockpluscore/
Patch Set: Rename filter* to filterText* Created Nov. 23, 2018, 2:01 a.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | test/elemHide.js » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details. 12 * GNU General Public License for more details.
13 * 13 *
14 * You should have received a copy of the GNU General Public License 14 * You should have received a copy of the GNU General Public License
15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. 15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
16 */ 16 */
17 17
18 "use strict"; 18 "use strict";
19 19
20 /** 20 /**
21 * @fileOverview Element hiding implementation. 21 * @fileOverview Element hiding implementation.
22 */ 22 */
23 23
24 const {Filter} = require("./filterClasses");
24 const {ElemHideExceptions} = require("./elemHideExceptions"); 25 const {ElemHideExceptions} = require("./elemHideExceptions");
25 const {filterNotifier} = require("./filterNotifier"); 26 const {filterNotifier} = require("./filterNotifier");
26 27
27 /** 28 /**
28 * The maximum number of selectors in a CSS rule. This is used by 29 * The maximum number of selectors in a CSS rule. This is used by
29 * <code>{@link createStyleSheet}</code> to split up a long list of selectors 30 * <code>{@link createStyleSheet}</code> to split up a long list of selectors
30 * into multiple rules. 31 * into multiple rules.
31 * @const {number} 32 * @const {number}
32 * @default 33 * @default
33 */ 34 */
34 const selectorGroupSize = 1024; 35 const selectorGroupSize = 1024;
35 36
36 /** 37 /**
37 * Lookup table, active flag, by filter by domain. 38 * Lookup table, active flag, by filter text by domain.
38 * (Only contains filters that aren't unconditionally matched for all domains.) 39 * (Only contains text for filters that aren't unconditionally matched for all
39 * @type {Map.<string,Map.<Filter,boolean>>} 40 * domains.)
41 * @type {Map.<string,Map.<string,boolean>>}
40 */ 42 */
41 let filtersByDomain = new Map(); 43 let filterTextByDomain = new Map();
42 44
43 /** 45 /**
44 * Lookup table, filter by selector. (Only used for selectors that are 46 * Lookup table, filter text by selector. (Only used for selectors that are
45 * unconditionally matched for all domains.) 47 * unconditionally matched for all domains.)
46 * @type {Map.<string,Filter>} 48 * @type {Map.<string,string>}
47 */ 49 */
48 let filterBySelector = new Map(); 50 let filterTextBySelector = new Map();
49 51
50 /** 52 /**
51 * This array caches the keys of filterBySelector table (selectors 53 * This array caches the keys of filterTextBySelector table (selectors
52 * which unconditionally apply on all domains). It will be null if the 54 * which unconditionally apply on all domains). It will be null if the
53 * cache needs to be rebuilt. 55 * cache needs to be rebuilt.
54 * @type {?string[]} 56 * @type {?string[]}
55 */ 57 */
56 let unconditionalSelectors = null; 58 let unconditionalSelectors = null;
57 59
58 /** 60 /**
59 * The default style sheet that applies on all domains. This is based on the 61 * The default style sheet that applies on all domains. This is based on the
60 * value of <code>{@link unconditionalSelectors}</code>. 62 * value of <code>{@link unconditionalSelectors}</code>.
61 * @type {?string} 63 * @type {?string}
(...skipping 10 matching lines...) Expand all
72 let commonStyleSheet = null; 74 let commonStyleSheet = null;
73 75
74 /** 76 /**
75 * Map to be used instead when a filter has a blank domains property. 77 * Map to be used instead when a filter has a blank domains property.
76 * @type {Map.<string,boolean>} 78 * @type {Map.<string,boolean>}
77 * @const 79 * @const
78 */ 80 */
79 let defaultDomains = new Map([["", true]]); 81 let defaultDomains = new Map([["", true]]);
80 82
81 /** 83 /**
82 * Set containing known element hiding filters 84 * Set containing the text of known element hiding filters
83 * @type {Set.<ElemHideFilter>} 85 * @type {Set.<string>}
84 */ 86 */
85 let knownFilters = new Set(); 87 let knownFilterText = new Set();
86 88
87 /** 89 /**
88 * All domains known to occur in exceptions 90 * All domains known to occur in exceptions
89 * @type {Set.<string>} 91 * @type {Set.<string>}
90 */ 92 */
91 let knownExceptionDomains = new Set(); 93 let knownExceptionDomains = new Set();
92 94
93 /** 95 /**
94 * Returns the suffix of the given domain that is known. If no suffix is known, 96 * Returns the suffix of the given domain that is known. If no suffix is known,
95 * an empty string is returned. 97 * an empty string is returned.
96 * @param {?string} domain 98 * @param {?string} domain
97 * @returns {string} 99 * @returns {string}
98 */ 100 */
99 function getKnownSuffix(domain) 101 function getKnownSuffix(domain)
100 { 102 {
101 while (domain && !filtersByDomain.has(domain) && 103 while (domain && !filterTextByDomain.has(domain) &&
102 !knownExceptionDomains.has(domain)) 104 !knownExceptionDomains.has(domain))
103 { 105 {
104 let index = domain.indexOf("."); 106 let index = domain.indexOf(".");
105 domain = index == -1 ? "" : domain.substring(index + 1); 107 domain = index == -1 ? "" : domain.substring(index + 1);
106 } 108 }
107 109
108 return domain; 110 return domain;
109 } 111 }
110 112
111 /** 113 /**
112 * Adds a filter to the lookup table of filters by domain. 114 * Adds a filter to the lookup table of filter text by domain.
113 * @param {Filter} filter 115 * @param {Filter} filter
114 * @param {?Map.<string,boolean>} [domains] 116 * @param {?Map.<string,boolean>} [domains]
115 */ 117 */
116 function addToFiltersByDomain(filter, domains = filter.domains) 118 function addToFiltersByDomain(filter, domains = filter.domains)
117 { 119 {
118 for (let [domain, isIncluded] of domains || defaultDomains) 120 for (let [domain, isIncluded] of domains || defaultDomains)
119 { 121 {
120 // There's no need to note that a filter is generically disabled. 122 // There's no need to note that a filter is generically disabled.
121 if (!isIncluded && domain == "") 123 if (!isIncluded && domain == "")
122 continue; 124 continue;
123 125
124 let filters = filtersByDomain.get(domain); 126 let map = filterTextByDomain.get(domain);
125 if (!filters) 127 if (!map)
126 filtersByDomain.set(domain, filters = new Map()); 128 filterTextByDomain.set(domain, map = new Map());
127 filters.set(filter, isIncluded); 129 map.set(filter.text, isIncluded ? filter.selector : null);
128 } 130 }
129 } 131 }
130 132
131 /** 133 /**
132 * Returns a list of selectors that apply on each website unconditionally. 134 * Returns a list of selectors that apply on each website unconditionally.
133 * @returns {string[]} 135 * @returns {string[]}
134 */ 136 */
135 function getUnconditionalSelectors() 137 function getUnconditionalSelectors()
136 { 138 {
137 if (!unconditionalSelectors) 139 if (!unconditionalSelectors)
138 unconditionalSelectors = [...filterBySelector.keys()]; 140 unconditionalSelectors = [...filterTextBySelector.keys()];
139 141
140 return unconditionalSelectors; 142 return unconditionalSelectors;
141 } 143 }
142 144
143 /** 145 /**
144 * Returns the list of selectors that apply on a given domain from the subset 146 * Returns the list of selectors that apply on a given domain from the subset
145 * of filters that do not apply unconditionally on all domains. 147 * of filters that do not apply unconditionally on all domains.
146 * 148 *
147 * @param {string} domain The domain. 149 * @param {string} domain The domain.
148 * @param {boolean} specificOnly Whether selectors from generic filters should 150 * @param {boolean} specificOnly Whether selectors from generic filters should
149 * be included. 151 * be included.
150 * 152 *
151 * @returns {Array.<string>} The list of selectors. 153 * @returns {Array.<string>} The list of selectors.
152 */ 154 */
153 function getConditionalSelectors(domain, specificOnly) 155 function getConditionalSelectors(domain, specificOnly)
154 { 156 {
155 let selectors = []; 157 let selectors = [];
156 158
157 let excluded = new Set(); 159 let excluded = new Set();
158 let currentDomain = domain; 160 let currentDomain = domain;
159 161
160 // This code is a performance hot-spot, which is why we've made certain 162 // This code is a performance hot-spot, which is why we've made certain
161 // micro-optimisations. Please be careful before making changes. 163 // micro-optimisations. Please be careful before making changes.
162 while (true) 164 while (true)
163 { 165 {
164 if (specificOnly && currentDomain == "") 166 if (specificOnly && currentDomain == "")
165 break; 167 break;
166 168
167 let filters = filtersByDomain.get(currentDomain); 169 let map = filterTextByDomain.get(currentDomain);
168 if (filters) 170 if (map)
169 { 171 {
170 for (let [filter, isIncluded] of filters) 172 for (let [text, selector] of map)
171 { 173 {
172 if (!isIncluded) 174 if (selector == null)
173 { 175 {
174 excluded.add(filter); 176 excluded.add(text);
175 } 177 }
176 else 178 else if ((excluded.size == 0 || !excluded.has(text)) &&
179 !ElemHideExceptions.getException(selector, domain))
177 { 180 {
178 let {selector} = filter; 181 selectors.push(selector);
179 if ((excluded.size == 0 || !excluded.has(filter)) &&
180 !ElemHideExceptions.getException(selector, domain))
181 {
182 selectors.push(selector);
183 }
184 } 182 }
185 } 183 }
186 } 184 }
187 185
188 if (currentDomain == "") 186 if (currentDomain == "")
189 break; 187 break;
190 188
191 let nextDot = currentDomain.indexOf("."); 189 let nextDot = currentDomain.indexOf(".");
192 currentDomain = nextDot == -1 ? "" : currentDomain.substr(nextDot + 1); 190 currentDomain = nextDot == -1 ? "" : currentDomain.substr(nextDot + 1);
193 } 191 }
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
233 // Note: Once an exception domain is known it never becomes unknown, even 231 // 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 232 // when all the exceptions containing that domain are removed. This is a
235 // best-case optimization. 233 // best-case optimization.
236 if (domain != "") 234 if (domain != "")
237 knownExceptionDomains.add(domain); 235 knownExceptionDomains.add(domain);
238 } 236 }
239 } 237 }
240 238
241 // If this is the first exception for a previously unconditionally applied 239 // If this is the first exception for a previously unconditionally applied
242 // element hiding selector we need to take care to update the lookups. 240 // element hiding selector we need to take care to update the lookups.
243 let unconditionalFilterForSelector = filterBySelector.get(selector); 241 let unconditionalFilterTextForSelector = filterTextBySelector.get(selector);
244 if (unconditionalFilterForSelector) 242 if (unconditionalFilterTextForSelector)
245 { 243 {
246 addToFiltersByDomain(unconditionalFilterForSelector); 244 addToFiltersByDomain(Filter.fromText(unconditionalFilterTextForSelector));
hub 2018/11/28 23:21:31 So here, we call Filter.fromText() while before we
Manish Jethani 2019/04/07 18:05:09 Yes, unfortunately this is the price. I looked at
Manish Jethani 2019/04/07 18:55:11 OK, so I looked into this [1], and parsing of gene
247 filterBySelector.delete(selector); 245 filterTextBySelector.delete(selector);
248 unconditionalSelectors = null; 246 unconditionalSelectors = null;
249 defaultStyleSheet = null; 247 defaultStyleSheet = null;
250 } 248 }
251 }); 249 });
252 250
253 /** 251 /**
254 * Container for element hiding filters 252 * Container for element hiding filters
255 * @class 253 * @class
256 */ 254 */
257 exports.ElemHide = { 255 exports.ElemHide = {
258 /** 256 /**
259 * Removes all known filters 257 * Removes all known filters
260 */ 258 */
261 clear() 259 clear()
262 { 260 {
263 commonStyleSheet = null; 261 commonStyleSheet = null;
264 262
265 for (let collection of [filtersByDomain, filterBySelector, knownFilters, 263 for (let collection of [filterTextByDomain, filterTextBySelector,
266 knownExceptionDomains]) 264 knownFilterText, knownExceptionDomains])
267 { 265 {
268 collection.clear(); 266 collection.clear();
269 } 267 }
270 268
271 unconditionalSelectors = null; 269 unconditionalSelectors = null;
272 defaultStyleSheet = null; 270 defaultStyleSheet = null;
273 271
274 filterNotifier.emit("elemhideupdate"); 272 filterNotifier.emit("elemhideupdate");
275 }, 273 },
276 274
277 /** 275 /**
278 * Add a new element hiding filter 276 * Add a new element hiding filter
279 * @param {ElemHideFilter} filter 277 * @param {ElemHideFilter} filter
280 */ 278 */
281 add(filter) 279 add(filter)
282 { 280 {
283 if (knownFilters.has(filter)) 281 let {text} = filter;
282
283 if (knownFilterText.has(text))
284 return; 284 return;
285 285
286 commonStyleSheet = null; 286 commonStyleSheet = null;
287 287
288 let {domains, selector} = filter; 288 let {domains, selector} = filter;
289 289
290 if (!(domains || ElemHideExceptions.hasExceptions(selector))) 290 if (!(domains || ElemHideExceptions.hasExceptions(selector)))
291 { 291 {
292 // The new filter's selector is unconditionally applied to all domains 292 // The new filter's selector is unconditionally applied to all domains
293 filterBySelector.set(selector, filter); 293 filterTextBySelector.set(selector, text);
294 unconditionalSelectors = null; 294 unconditionalSelectors = null;
295 defaultStyleSheet = null; 295 defaultStyleSheet = null;
296 } 296 }
297 else 297 else
298 { 298 {
299 // The new filter's selector only applies to some domains 299 // The new filter's selector only applies to some domains
300 addToFiltersByDomain(filter, domains); 300 addToFiltersByDomain(filter, domains);
301 } 301 }
302 302
303 knownFilters.add(filter); 303 knownFilterText.add(text);
304 filterNotifier.emit("elemhideupdate"); 304 filterNotifier.emit("elemhideupdate");
305 }, 305 },
306 306
307 /** 307 /**
308 * Removes an element hiding filter 308 * Removes an element hiding filter
309 * @param {ElemHideFilter} filter 309 * @param {ElemHideFilter} filter
310 */ 310 */
311 remove(filter) 311 remove(filter)
312 { 312 {
313 if (!knownFilters.has(filter)) 313 let {text} = filter;
314
315 if (!knownFilterText.has(text))
314 return; 316 return;
315 317
316 commonStyleSheet = null; 318 commonStyleSheet = null;
317 319
318 let {selector} = filter; 320 let {selector} = filter;
319 321
320 // Unconditially applied element hiding filters 322 // Unconditially applied element hiding filters
321 if (filterBySelector.get(selector) == filter) 323 if (filterTextBySelector.get(selector) == text)
322 { 324 {
323 filterBySelector.delete(selector); 325 filterTextBySelector.delete(selector);
324 unconditionalSelectors = null; 326 unconditionalSelectors = null;
325 defaultStyleSheet = null; 327 defaultStyleSheet = null;
326 } 328 }
327 // Conditionally applied element hiding filters 329 // Conditionally applied element hiding filters
328 else 330 else
329 { 331 {
330 let domains = filter.domains || defaultDomains; 332 let domains = filter.domains || defaultDomains;
331 for (let domain of domains.keys()) 333 for (let domain of domains.keys())
332 { 334 {
333 let filters = filtersByDomain.get(domain); 335 let map = filterTextByDomain.get(domain);
334 if (filters) 336 if (map)
335 { 337 {
336 filters.delete(filter); 338 map.delete(text);
337 339
338 if (filters.size == 0) 340 if (map.size == 0)
339 filtersByDomain.delete(domain); 341 filterTextByDomain.delete(domain);
340 } 342 }
341 } 343 }
342 } 344 }
343 345
344 knownFilters.delete(filter); 346 knownFilterText.delete(text);
345 filterNotifier.emit("elemhideupdate"); 347 filterNotifier.emit("elemhideupdate");
346 }, 348 },
347 349
348 /** 350 /**
349 * @typedef {object} ElemHideStyleSheet 351 * @typedef {object} ElemHideStyleSheet
350 * @property {string} code CSS code. 352 * @property {string} code CSS code.
351 * @property {Array.<string>} selectors List of selectors. 353 * @property {Array.<string>} selectors List of selectors.
352 */ 354 */
353 355
354 /** 356 /**
(...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after
479 { 481 {
480 let styleSheet = ""; 482 let styleSheet = "";
481 483
482 for (let selectorGroup of splitSelectors(selectors)) 484 for (let selectorGroup of splitSelectors(selectors))
483 styleSheet += createRule(selectorGroup); 485 styleSheet += createRule(selectorGroup);
484 486
485 return styleSheet; 487 return styleSheet;
486 } 488 }
487 489
488 exports.createStyleSheet = createStyleSheet; 490 exports.createStyleSheet = createStyleSheet;
OLDNEW
« no previous file with comments | « no previous file | test/elemHide.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld