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

Side by Side Diff: lib/elemHide.js

Issue 4875173639487488: Optmize filter list in elemHide.js for memory usage (Closed)
Patch Set: So I think I found a good solution. In filterListener.js we already have the concept of flushing el… Created May 5, 2014, 3:47 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 | lib/filterListener.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 <http://adblockplus.org/>, 2 * This file is part of Adblock Plus <http://adblockplus.org/>,
3 * Copyright (C) 2006-2014 Eyeo GmbH 3 * Copyright (C) 2006-2014 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 12 matching lines...) Expand all
23 23
24 let {Utils} = require("utils"); 24 let {Utils} = require("utils");
25 let {IO} = require("io"); 25 let {IO} = require("io");
26 let {Prefs} = require("prefs"); 26 let {Prefs} = require("prefs");
27 let {ElemHideException} = require("filterClasses"); 27 let {ElemHideException} = require("filterClasses");
28 let {FilterNotifier} = require("filterNotifier"); 28 let {FilterNotifier} = require("filterNotifier");
29 let {AboutHandler} = require("elemHideHitRegistration"); 29 let {AboutHandler} = require("elemHideHitRegistration");
30 let {TimeLine} = require("timeline"); 30 let {TimeLine} = require("timeline");
31 31
32 /** 32 /**
33 * Lookup table, filters by their associated key 33 * Array of filters, index as "key".
34 * @type Array
35 */
36 let filters = [];
37 /**
38 * Lookup table, keys of the filters by filter text.
39 * Can be null.
34 * @type Object 40 * @type Object
35 */ 41 */
36 let filterByKey = {__proto__: null}; 42 let keyByFilter = new Map();
37
38 /**
39 * Lookup table, keys of the filters by filter text
40 * @type Object
41 */
42 let keyByFilter = {__proto__: null};
43 43
44 /** 44 /**
45 * Lookup table, keys are known element hiding exceptions 45 * Lookup table, keys are known element hiding exceptions
46 * @type Object 46 * @type Object
47 */ 47 */
48 let knownExceptions = {__proto__: null}; 48 let knownExceptions = {__proto__: null};
49 49
50 /** 50 /**
51 * Lookup table, lists of element hiding exceptions by selector 51 * Lookup table, lists of element hiding exceptions by selector
52 * @type Object 52 * @type Object
53 */ 53 */
54 let exceptions = {__proto__: null}; 54 let exceptions = {__proto__: null};
55 55
56 /** 56 /**
57 * Currently applied stylesheet URL 57 * Currently applied stylesheet URL
58 * @type nsIURI 58 * @type nsIURI
59 */ 59 */
60 let styleURL = null; 60 let styleURL = null;
61 61
62 /** 62 /**
63 * Filters removed since last compact operation
64 * @type number
65 */
66 let filtersRemoved = 0;
67
68 /**
63 * Element hiding component 69 * Element hiding component
64 * @class 70 * @class
65 */ 71 */
66 let ElemHide = exports.ElemHide = 72 let ElemHide = exports.ElemHide =
67 { 73 {
68 /** 74 /**
69 * Indicates whether filters have been added or removed since the last apply() call. 75 * Indicates whether filters have been added or removed since the last apply() call.
70 * @type Boolean 76 * @type Boolean
71 */ 77 */
72 isDirty: false, 78 isDirty: false,
(...skipping 28 matching lines...) Expand all
101 TimeLine.log("done determining stylesheet URL"); 107 TimeLine.log("done determining stylesheet URL");
102 108
103 TimeLine.leave("ElemHide.init() done"); 109 TimeLine.leave("ElemHide.init() done");
104 }, 110 },
105 111
106 /** 112 /**
107 * Removes all known filters 113 * Removes all known filters
108 */ 114 */
109 clear: function() 115 clear: function()
110 { 116 {
111 filterByKey = {__proto__: null}; 117 dump("clear\n");
112 keyByFilter = {__proto__: null}; 118 keyByFilter = null;
119 filters = [];
113 knownExceptions = {__proto__: null}; 120 knownExceptions = {__proto__: null};
114 exceptions = {__proto__: null}; 121 exceptions = {__proto__: null};
115 ElemHide.isDirty = false; 122 ElemHide.isDirty = false;
116 ElemHide.unapply(); 123 ElemHide.unapply();
117 }, 124 },
118 125
119 /** 126 /**
120 * Add a new element hiding filter 127 * Add a new element hiding filter
121 * @param {ElemHideFilter} filter 128 * @param {ElemHideFilter} filter
122 */ 129 */
123 add: function(filter) 130 add: function(filter)
124 { 131 {
125 if (filter instanceof ElemHideException) 132 if (filter instanceof ElemHideException)
126 { 133 {
127 if (filter.text in knownExceptions) 134 if (filter.text in knownExceptions)
128 return; 135 return;
129 136
130 let selector = filter.selector; 137 let selector = filter.selector;
131 if (!(selector in exceptions)) 138 if (!(selector in exceptions))
132 exceptions[selector] = []; 139 exceptions[selector] = [];
133 exceptions[selector].push(filter); 140 exceptions[selector].push(filter);
134 knownExceptions[filter.text] = true; 141 knownExceptions[filter.text] = true;
135 } 142 }
136 else 143 else
137 { 144 {
138 if (filter.text in keyByFilter) 145 this._ensureFilterMap();
146
147 if (keyByFilter.has(filter.text))
139 return; 148 return;
140 149
141 let key; 150 filters.push(filter);
142 do { 151 keyByFilter.set(filter.text, filters.length - 1);
143 key = Math.random().toFixed(15).substr(5);
144 } while (key in filterByKey);
145 152
146 filterByKey[key] = filter;
147 keyByFilter[filter.text] = key;
148 ElemHide.isDirty = true; 153 ElemHide.isDirty = true;
149 } 154 }
150 }, 155 },
151 156
152 /** 157 /**
153 * Removes an element hiding filter 158 * Removes an element hiding filter
154 * @param {ElemHideFilter} filter 159 * @param {ElemHideFilter} filter
155 */ 160 */
156 remove: function(filter) 161 remove: function(filter)
157 { 162 {
158 if (filter instanceof ElemHideException) 163 if (filter instanceof ElemHideException)
159 { 164 {
160 if (!(filter.text in knownExceptions)) 165 if (!(filter.text in knownExceptions))
161 return; 166 return;
162 167
163 let list = exceptions[filter.selector]; 168 let list = exceptions[filter.selector];
164 let index = list.indexOf(filter); 169 let index = list.indexOf(filter);
165 if (index >= 0) 170 if (index >= 0)
166 list.splice(index, 1); 171 list.splice(index, 1);
167 delete knownExceptions[filter.text]; 172 delete knownExceptions[filter.text];
168 } 173 }
169 else 174 else
170 { 175 {
171 if (!(filter.text in keyByFilter)) 176 this._ensureFilterMap();
177
178 let key = keyByFilter.get(filter.text);
179 if (key === undefined)
172 return; 180 return;
173 181
174 let key = keyByFilter[filter.text]; 182 keyByFilter.delete(filter.text);
175 delete filterByKey[key]; 183 filters[key] = null;
176 delete keyByFilter[filter.text];
177 ElemHide.isDirty = true; 184 ElemHide.isDirty = true;
185 filtersRemoved += 1;
178 } 186 }
179 }, 187 },
180 188
181 /** 189 /**
190 * Clean up and compact memory after filters
191 * are removed.
192 */
193 compact: function()
194 {
195 if (filtersRemoved == 0) {
196 return;
197 }
198
199 if (filtersRemoved == 1) {
200 for (let i = 0; i < filters; i++) {
201 if (filters[i] === null)
202 filters.splice(i, 1);
203 }
204 } else {
205 let newList = [];
206 for (let filter in filters) {
207 if (filter)
208 newList.push(filter);
209 }
210 filters = newList;
211 }
212
213 filtersRemoved = 0;
214 },
215
216 /**
217 * Ensure that we have a map of filter text to key.
218 * Might have to recreate it from the array of filters.
219 */
220 _ensureFilterMap: function()
221 {
222 if (keyByFilter)
223 return;
224
225 keyByFilter = new Map();
226 for (let i = 0; i < filters.length; i++)
227 keyByFilter.set(filters[i].text, i);
228 },
229
230 /**
182 * Checks whether an exception rule is registered for a filter on a particular 231 * Checks whether an exception rule is registered for a filter on a particular
183 * domain. 232 * domain.
184 */ 233 */
185 getException: function(/**Filter*/ filter, /**String*/ docDomain) /**ElemHideE xception*/ 234 getException: function(/**Filter*/ filter, /**String*/ docDomain) /**ElemHideE xception*/
186 { 235 {
187 let selector = filter.selector; 236 let selector = filter.selector;
188 if (!(filter.selector in exceptions)) 237 if (!(filter.selector in exceptions))
189 return null; 238 return null;
190 239
191 let list = exceptions[filter.selector]; 240 let list = exceptions[filter.selector];
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after
296 TimeLine.leave("ElemHide.apply() write callback done"); 345 TimeLine.leave("ElemHide.apply() write callback done");
297 }.bind(this), "ElemHideWrite"); 346 }.bind(this), "ElemHideWrite");
298 347
299 this._applying = true; 348 this._applying = true;
300 349
301 TimeLine.leave("ElemHide.apply() done", "ElemHideWrite"); 350 TimeLine.leave("ElemHide.apply() done", "ElemHideWrite");
302 }, 351 },
303 352
304 _generateCSSContent: function() 353 _generateCSSContent: function()
305 { 354 {
355 // We don't need keyByFilter anymore now, if we need it again,
356 // e.g. because of a subscription change, we will regenerate it.
357 keyByFilter = null;
358
306 // Grouping selectors by domains 359 // Grouping selectors by domains
307 TimeLine.log("start grouping selectors"); 360 TimeLine.log("start grouping selectors");
308 let domains = {__proto__: null}; 361 let domains = {__proto__: null};
309 let hasFilters = false; 362 let hasFilters = false;
310 for (let key in filterByKey) 363 for (let key = 0; key < filters.length; key++)
311 { 364 {
312 let filter = filterByKey[key]; 365 let filter = filters[key];
313 let domain = filter.selectorDomain || ""; 366 let domain = filter.selectorDomain || "";
314 367
315 let list; 368 let list;
316 if (domain in domains) 369 if (domain in domains)
317 list = domains[domain]; 370 list = domains[domain];
318 else 371 else
319 { 372 {
320 list = {__proto__: null}; 373 list = {__proto__: null};
321 domains[domain] = list; 374 domains[domain] = list;
322 } 375 }
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after
379 * Retrieves the currently applied stylesheet URL 432 * Retrieves the currently applied stylesheet URL
380 * @type String 433 * @type String
381 */ 434 */
382 get styleURL() ElemHide.applied ? styleURL.spec : null, 435 get styleURL() ElemHide.applied ? styleURL.spec : null,
383 436
384 /** 437 /**
385 * Retrieves an element hiding filter by the corresponding protocol key 438 * Retrieves an element hiding filter by the corresponding protocol key
386 */ 439 */
387 getFilterByKey: function(/**String*/ key) /**Filter*/ 440 getFilterByKey: function(/**String*/ key) /**Filter*/
388 { 441 {
389 return (key in filterByKey ? filterByKey[key] : null); 442 return (key < filters.length ? filters[key] : null);
390 }, 443 },
391 444
392 /** 445 /**
393 * Returns a list of all selectors active on a particular domain (currently 446 * Returns a list of all selectors active on a particular domain (currently
394 * used only in Chrome). 447 * used only in Chrome).
395 */ 448 */
396 getSelectorsForDomain: function(/**String*/ domain, /**Boolean*/ specificOnly) 449 getSelectorsForDomain: function(/**String*/ domain, /**Boolean*/ specificOnly)
397 { 450 {
398 let result = []; 451 let result = [];
399 for (let key in filterByKey) 452 for (let filter of filters)
400 { 453 {
401 let filter = filterByKey[key]; 454 if (!filter)
455 continue
456
402 if (specificOnly && (!filter.domains || filter.domains[""])) 457 if (specificOnly && (!filter.domains || filter.domains[""]))
403 continue; 458 continue;
404 459
405 if (filter.isActiveOnDomain(domain) && !this.getException(filter, domain)) 460 if (filter.isActiveOnDomain(domain) && !this.getException(filter, domain))
406 result.push(filter.selector); 461 result.push(filter.selector);
407 } 462 }
408 return result; 463 return result;
409 } 464 }
410 }; 465 };
OLDNEW
« no previous file with comments | « no previous file | lib/filterListener.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld