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

Side by Side Diff: lib/elemHide.js

Issue 29342830: Issue 4057 - Further speedup ElemHide.getSelectorsforDomain (Closed)
Patch Set: Rebased, unconditionalSelectors Created May 24, 2016, 2:39 p.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') | test/elemHide.js » ('J')
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-2016 Eyeo GmbH 3 * Copyright (C) 2006-2016 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 22 matching lines...) Expand all
33 */ 33 */
34 var filterByKey = []; 34 var filterByKey = [];
35 35
36 /** 36 /**
37 * Lookup table, keys of the filters by filter text 37 * Lookup table, keys of the filters by filter text
38 * @type Object 38 * @type Object
39 */ 39 */
40 var keyByFilter = Object.create(null); 40 var keyByFilter = Object.create(null);
41 41
42 /** 42 /**
43 * Nested lookup table, filter (or false if inactive) by filter text by domain 43 * Indicates whether we are using the getSelectorsForDomain function and
44 * therefore mainting the required filtersByDomain, filtersBySelector and
45 * unconditionalSelectors lookups. (Will be false for Firefox)
46 * @type Boolean
47 */
48 var usingGetSelectorsForDomain = !("nsIStyleSheetService" in Ci);
49
50 /**
51 * Nested lookup table, filter (or false if inactive) by filter key by domain.
52 * (Only contains filters that aren't unconditionally matched for all domains.)
44 * @type Object 53 * @type Object
45 */ 54 */
46 var filtersByDomain = Object.create(null); 55 var filtersByDomain = Object.create(null);
47 56
48 /** 57 /**
49 * Indicates whether we are using (and maintaining) the filtersByDomain lookup. 58 * Lookup table, filters by selector. (Only contains filters that have a
50 * (Will be false for Firefox) 59 * selector that is unconditionally matched for all domains.)
51 * @type Boolean
52 */ 60 */
53 var usingFiltersByDomain = !("nsIStyleSheetService" in Ci); 61 var filtersBySelector = Object.create(null);
62
63 /**
64 * Array of selectors which unconditionally apply to all domains.
65 */
66 var unconditionalSelectors = [];
54 67
55 /** 68 /**
56 * Object to be used instead when a filter has a blank domains property. 69 * Object to be used instead when a filter has a blank domains property.
57 */ 70 */
58 var defaultDomains = Object.create(null); 71 var defaultDomains = Object.create(null);
59 defaultDomains[""] = true; 72 defaultDomains[""] = true;
60 73
61 /** 74 /**
62 * Lookup table, keys are known element hiding exceptions 75 * Lookup table, keys are known element hiding exceptions
63 * @type Object 76 * @type Object
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
112 }, 125 },
113 126
114 /** 127 /**
115 * Removes all known filters 128 * Removes all known filters
116 */ 129 */
117 clear: function() 130 clear: function()
118 { 131 {
119 filterByKey = []; 132 filterByKey = [];
120 keyByFilter = Object.create(null); 133 keyByFilter = Object.create(null);
121 filtersByDomain = Object.create(null); 134 filtersByDomain = Object.create(null);
135 filtersBySelector = Object.create(null);
136 unconditionalSelectors = [];
122 knownExceptions = Object.create(null); 137 knownExceptions = Object.create(null);
123 exceptions = Object.create(null); 138 exceptions = Object.create(null);
124 ElemHide.isDirty = false; 139 ElemHide.isDirty = false;
125 ElemHide.unapply(); 140 ElemHide.unapply();
126 }, 141 },
127 142
143 _addToFiltersByDomain: function(filter)
144 {
145 let key = keyByFilter[filter.text];
146 let domains = filter.domains || defaultDomains;
147 for (let domain in domains)
148 {
149 let filters = filtersByDomain[domain];
150 if (!filters)
151 filters = filtersByDomain[domain] = Object.create(null);
152
153 if (domains[domain])
154 filters[key] = filter;
155 else
156 filters[key] = false;
157 }
158 },
159
128 /** 160 /**
129 * Add a new element hiding filter 161 * Add a new element hiding filter
130 * @param {ElemHideFilter} filter 162 * @param {ElemHideFilter} filter
131 */ 163 */
132 add: function(filter) 164 add: function(filter)
133 { 165 {
134 if (filter instanceof ElemHideException) 166 if (filter instanceof ElemHideException)
135 { 167 {
136 if (filter.text in knownExceptions) 168 if (filter.text in knownExceptions)
137 return; 169 return;
138 170
139 let selector = filter.selector; 171 let selector = filter.selector;
140 if (!(selector in exceptions)) 172 if (!(selector in exceptions))
141 exceptions[selector] = []; 173 exceptions[selector] = [];
142 exceptions[selector].push(filter); 174 exceptions[selector].push(filter);
175
176 if (usingGetSelectorsForDomain)
177 {
178 // If this is the first exception for a previously unconditionally
179 // applied element hiding selector we need to take care to update the
180 // lookups.
181 let unconditionalFilters = filtersBySelector[selector];
182 if (unconditionalFilters)
183 {
184 for (let f of unconditionalFilters)
185 this._addToFiltersByDomain(f);
186 delete filtersBySelector[selector];
187 let index = unconditionalSelectors.indexOf(selector);
188 unconditionalSelectors.splice(index, 1);
189 }
190 }
191
143 knownExceptions[filter.text] = true; 192 knownExceptions[filter.text] = true;
144 } 193 }
145 else 194 else
146 { 195 {
147 if (filter.text in keyByFilter) 196 if (filter.text in keyByFilter)
148 return; 197 return;
149 198
150 let key = filterByKey.push(filter) - 1; 199 let key = filterByKey.push(filter) - 1;
151 keyByFilter[filter.text] = key; 200 keyByFilter[filter.text] = key;
152 201
153 if (usingFiltersByDomain) 202 if (usingGetSelectorsForDomain)
154 { 203 {
155 let domains = filter.domains || defaultDomains; 204 // The new filter's selector is unconditionally applied to all domains
Wladimir Palant 2016/05/24 15:16:18 This comment belongs inside the if block since it
kzar 2016/05/25 05:12:24 Done.
156 for (let domain in domains) 205 if (!filter.domains && !exceptions[filter.selector])
Wladimir Palant 2016/05/24 15:16:19 We don't really care about the value of exceptions
kzar 2016/05/25 05:12:24 Done.
157 { 206 {
158 let filters = filtersByDomain[domain]; 207 let filters = filtersBySelector[filter.selector];
159 if (!filters) 208 if (filters)
160 filters = filtersByDomain[domain] = Object.create(null); 209 {
161 210 filters.push(filter);
162 if (domains[domain]) 211 }
163 filters[filter.text] = filter;
164 else 212 else
165 filters[filter.text] = false; 213 {
214 filtersBySelector[filter.selector] = [filter];
215 unconditionalSelectors.push(filter.selector);
216 }
217 }
218 // The new filter's selector only applies to some domains
Wladimir Palant 2016/05/24 15:16:18 Same here, comment inside the block please.
kzar 2016/05/25 05:12:24 Done.
219 else
220 {
221 this._addToFiltersByDomain(filter);
166 } 222 }
167 } 223 }
168 224
169 ElemHide.isDirty = true; 225 ElemHide.isDirty = true;
170 } 226 }
171 }, 227 },
172 228
173 /** 229 /**
174 * Removes an element hiding filter 230 * Removes an element hiding filter
175 * @param {ElemHideFilter} filter 231 * @param {ElemHideFilter} filter
(...skipping 14 matching lines...) Expand all
190 else 246 else
191 { 247 {
192 if (!(filter.text in keyByFilter)) 248 if (!(filter.text in keyByFilter))
193 return; 249 return;
194 250
195 let key = keyByFilter[filter.text]; 251 let key = keyByFilter[filter.text];
196 delete filterByKey[key]; 252 delete filterByKey[key];
197 delete keyByFilter[filter.text]; 253 delete keyByFilter[filter.text];
198 ElemHide.isDirty = true; 254 ElemHide.isDirty = true;
199 255
200 if (usingFiltersByDomain) 256 if (usingGetSelectorsForDomain)
kzar 2016/05/24 14:45:22 I'm worried about the performance of ElemHide.remo
Wladimir Palant 2016/05/24 15:16:19 A remove isn't a very common operation. Did you me
kzar 2016/05/24 17:47:09 Yea, it's pretty bad - something like 2325ms spent
kzar 2016/05/24 17:52:12 Another idea, maybe crazy... removing is expensive
kzar 2016/05/25 04:57:16 With Patch Set 5 ElemHide.remove is now down to ab
201 { 257 {
202 let domains = filter.domains || defaultDomains; 258 let filters = filtersBySelector[filter.selector];
203 for (let domain in domains) 259 if (filters)
204 { 260 {
205 let filters = filtersByDomain[domain]; 261 if (filters.length > 1)
206 if (filters) 262 {
207 delete filters[filter.text]; 263 let index = filters.indexOf(filter);
264 filters.splice(index, 1);
265 }
266 else
267 {
268 delete filtersBySelector[filter.selector];
269 let index = unconditionalSelectors.indexOf(filter.selector);
270 unconditionalSelectors.splice(index, 1);
271 }
272 }
273 else
274 {
275 let domains = filter.domains || defaultDomains;
276 for (let domain in domains)
277 {
278 let filters = filtersByDomain[domain];
279 if (filters)
280 delete filters[key];
281 }
208 } 282 }
209 } 283 }
210 } 284 }
211 }, 285 },
212 286
213 /** 287 /**
214 * Checks whether an exception rule is registered for a filter on a particular 288 * Checks whether an exception rule is registered for a filter on a particular
215 * domain. 289 * domain.
216 */ 290 */
217 getException: function(/**Filter*/ filter, /**String*/ docDomain) /**ElemHideE xception*/ 291 getException: function(/**Filter*/ filter, /**String*/ docDomain) /**ElemHideE xception*/
(...skipping 191 matching lines...) Expand 10 before | Expand all | Expand 10 after
409 /** 483 /**
410 * Retrieves an element hiding filter by the corresponding protocol key 484 * Retrieves an element hiding filter by the corresponding protocol key
411 */ 485 */
412 getFilterByKey: function(/**String*/ key) /**Filter*/ 486 getFilterByKey: function(/**String*/ key) /**Filter*/
413 { 487 {
414 return (key in filterByKey ? filterByKey[key] : null); 488 return (key in filterByKey ? filterByKey[key] : null);
415 }, 489 },
416 490
417 /** 491 /**
418 * Returns a list of all selectors active on a particular domain, must not be 492 * Returns a list of all selectors active on a particular domain, must not be
419 * used in Firefox (when usingFiltersByDomain is false). 493 * used in Firefox (when usingGetSelectorsForDomain is false).
420 */ 494 */
421 getSelectorsForDomain: function(/**String*/ domain, /**Boolean*/ specificOnly) 495 getSelectorsForDomain: function(/**String*/ domain, /**Boolean*/ specificOnly)
422 { 496 {
423 if (!usingFiltersByDomain) 497 if (!usingGetSelectorsForDomain)
424 throw new Error("getSelectorsForDomain can not be used in Firefox!"); 498 throw new Error("getSelectorsForDomain can not be used in Firefox!");
425 499
426 let selectors = []; 500 let selectors = [];
427 501
428 let seenFilters = Object.create(null); 502 let seenFilters = Object.create(null);
429 let currentDomain = domain ? domain.toUpperCase() : ""; 503 let currentDomain = domain ? domain.toUpperCase() : "";
430 while (true) 504 while (true)
431 { 505 {
432 if (specificOnly && currentDomain == "") 506 if (specificOnly && currentDomain == "")
433 break; 507 break;
434 508
435 let filters = filtersByDomain[currentDomain]; 509 let filters = filtersByDomain[currentDomain];
436 if (filters) 510 if (filters)
437 { 511 {
438 for (let filterText in filters) 512 for (let filterKey in filters)
439 { 513 {
440 if (filterText in seenFilters) 514 if (filterKey in seenFilters)
441 continue; 515 continue;
442 seenFilters[filterText] = true; 516 seenFilters[filterKey] = true;
443 517
444 let filter = filters[filterText]; 518 let filter = filters[filterKey];
445 if (filter && !this.getException(filter, domain)) 519 if (filter && !this.getException(filter, domain))
446 selectors.push(filter.selector); 520 selectors.push(filter.selector);
447 } 521 }
448 } 522 }
449 523
450 if (currentDomain == "") 524 if (currentDomain == "")
451 break; 525 break;
452 526
453 let nextDot = currentDomain.indexOf("."); 527 let nextDot = currentDomain.indexOf(".");
454 currentDomain = nextDot == -1 ? "" : currentDomain.substr(nextDot + 1); 528 currentDomain = nextDot == -1 ? "" : currentDomain.substr(nextDot + 1);
455 } 529 }
456 530
457 return selectors; 531 if (specificOnly)
Sebastian Noack 2016/05/24 15:20:07 Why do you still account for specificOnly inside t
Wladimir Palant 2016/05/24 15:26:06 Not all generic filters are in unconditionalSelect
532 return selectors;
533 else
Sebastian Noack 2016/05/24 15:20:07 Nit: extraneous else
kzar 2016/05/25 05:12:24 Done.
534 return unconditionalSelectors.concat(selectors);
Wladimir Palant 2016/05/24 15:16:19 I think we should avoid concatenating two arrays h
kzar 2016/05/25 05:12:24 Done.
458 } 535 }
459 }; 536 };
OLDNEW
« no previous file with comments | « no previous file | test/elemHide.js » ('j') | test/elemHide.js » ('J')

Powered by Google App Engine
This is Rietveld