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

Delta Between Two Patch Sets: lib/elemHide.js

Issue 29349187: Issue 4167 - getSelectorsForDomain criteria + keys (Closed)
Left Patch Set: Only store one filter key per unconditional selector Created Sept. 19, 2016, 4:51 p.m.
Right Patch Set: Improved comments Created Sept. 27, 2016, 1:52 p.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
« no previous file with change/comment | « no previous file | test/elemHide.js » ('j') | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
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 25 matching lines...) Expand all
36 var keyByFilter = Object.create(null); 36 var keyByFilter = Object.create(null);
37 37
38 /** 38 /**
39 * Nested lookup table, filter (or false if inactive) by filter key by domain. 39 * Nested lookup table, filter (or false if inactive) by filter key by domain.
40 * (Only contains filters that aren't unconditionally matched for all domains.) 40 * (Only contains filters that aren't unconditionally matched for all domains.)
41 * @type Object 41 * @type Object
42 */ 42 */
43 var filtersByDomain = Object.create(null); 43 var filtersByDomain = Object.create(null);
44 44
45 /** 45 /**
46 * Lookup table, filters key by selector. (Only contains filters that have a 46 * Lookup table, filter keys by selector. (Only contains filters that have a
47 * selector that is unconditionally matched for all domains.) 47 * selector that is unconditionally matched for all domains.)
48 */ 48 */
49 var filterKeyBySelector = Object.create(null); 49 var filterKeysBySelector = Object.create(null);
50 50
51 /** 51 /**
52 * This array caches the keys of filterKeyBySelector table (selectors which 52 * This array caches the keys of filterKeysBySelector table (selectors which
53 * unconditionally apply on all domains). It will be null if the cache needs to 53 * unconditionally apply on all domains). It will be null if the cache needs to
54 * be rebuilt. 54 * be rebuilt.
55 */ 55 */
56 var unconditionalSelectors = null; 56 var unconditionalSelectors = null;
57 57
58 /** 58 /**
59 * This array caches the values of filterKeyBySelector table (filterIds for 59 * This array caches the values of filterKeysBySelector table (filterIds for
60 * selectors which unconditionally apply on all domains). It will be null if the 60 * selectors which unconditionally apply on all domains). It will be null if the
61 * cache needs to be rebuilt. 61 * cache needs to be rebuilt. Note: Only the first filter key for each selector
62 * is cached.
62 */ 63 */
63 var unconditionalFilterKeys = null; 64 var unconditionalFilterKeys = null;
64 65
65 /** 66 /**
66 * Object to be used instead when a filter has a blank domains property. 67 * Object to be used instead when a filter has a blank domains property.
67 */ 68 */
68 var defaultDomains = Object.create(null); 69 var defaultDomains = Object.create(null);
69 defaultDomains[""] = true; 70 defaultDomains[""] = true;
70 71
71 /** 72 /**
(...skipping 15 matching lines...) Expand all
87 var ElemHide = exports.ElemHide = 88 var ElemHide = exports.ElemHide =
88 { 89 {
89 /** 90 /**
90 * Removes all known filters 91 * Removes all known filters
91 */ 92 */
92 clear: function() 93 clear: function()
93 { 94 {
94 filterByKey = []; 95 filterByKey = [];
95 keyByFilter = Object.create(null); 96 keyByFilter = Object.create(null);
96 filtersByDomain = Object.create(null); 97 filtersByDomain = Object.create(null);
97 filterKeyBySelector = Object.create(null); 98 filterKeysBySelector = Object.create(null);
98 unconditionalSelectors = unconditionalFilterKeys = null; 99 unconditionalSelectors = unconditionalFilterKeys = null;
99 knownExceptions = Object.create(null); 100 knownExceptions = Object.create(null);
100 exceptions = Object.create(null); 101 exceptions = Object.create(null);
101 FilterNotifier.emit("elemhideupdate"); 102 FilterNotifier.emit("elemhideupdate");
102 }, 103 },
103 104
104 _addToFiltersByDomain: function(key, filter) 105 _addToFiltersByDomain: function(key, filter)
105 { 106 {
106 let domains = filter.domains || defaultDomains; 107 let domains = filter.domains || defaultDomains;
107 for (let domain in domains) 108 for (let domain in domains)
(...skipping 21 matching lines...) Expand all
129 return; 130 return;
130 131
131 let selector = filter.selector; 132 let selector = filter.selector;
132 if (!(selector in exceptions)) 133 if (!(selector in exceptions))
133 exceptions[selector] = []; 134 exceptions[selector] = [];
134 exceptions[selector].push(filter); 135 exceptions[selector].push(filter);
135 136
136 // If this is the first exception for a previously unconditionally 137 // If this is the first exception for a previously unconditionally
137 // applied element hiding selector we need to take care to update the 138 // applied element hiding selector we need to take care to update the
138 // lookups. 139 // lookups.
139 let filterKey = filterKeyBySelector[selector]; 140 let filterKeys = filterKeysBySelector[selector];
140 if (filterKey) 141 if (filterKeys)
141 { 142 {
142 this._addToFiltersByDomain(filterKey, filterByKey[filterKey]); 143 for (let filterKey of filterKeys)
143 delete filterKeyBySelector[selector]; 144 this._addToFiltersByDomain(filterKey, filterByKey[filterKey]);
145 delete filterKeysBySelector[selector];
144 unconditionalSelectors = unconditionalFilterKeys = null; 146 unconditionalSelectors = unconditionalFilterKeys = null;
145 } 147 }
146 148
147 knownExceptions[filter.text] = true; 149 knownExceptions[filter.text] = true;
148 } 150 }
149 else 151 else
150 { 152 {
151 if (filter.text in keyByFilter) 153 if (filter.text in keyByFilter)
152 return; 154 return;
153 155
154 let key = filterByKey.push(filter) - 1; 156 let key = filterByKey.push(filter) - 1;
155 keyByFilter[filter.text] = key; 157 keyByFilter[filter.text] = key;
156 158
157 if (!(filter.domains || filter.selector in exceptions)) 159 if (!(filter.domains || filter.selector in exceptions))
158 { 160 {
159 // The new filter's selector is unconditionally applied to all domains 161 // The new filter's selector is unconditionally applied to all domains
160 filterKeyBySelector[filter.selector] = key; 162 let filterKeys = filterKeysBySelector[filter.selector];
161 unconditionalSelectors = unconditionalFilterKeys = null; 163 if (filterKeys)
164 {
165 filterKeys.push(key);
166 }
167 else
168 {
169 filterKeysBySelector[filter.selector] = [key];
170 unconditionalSelectors = unconditionalFilterKeys = null;
171 }
162 } 172 }
163 else 173 else
164 { 174 {
165 // The new filter's selector only applies to some domains 175 // The new filter's selector only applies to some domains
166 this._addToFiltersByDomain(key, filter); 176 this._addToFiltersByDomain(key, filter);
167 } 177 }
168 } 178 }
169 179
170 FilterNotifier.emit("elemhideupdate"); 180 FilterNotifier.emit("elemhideupdate");
171 }, 181 },
172 182
183 _removeFilterKey: function(key, filter)
184 {
185 let filterKeys = filterKeysBySelector[filter.selector];
186 if (filterKeys)
187 {
188 let index = filterKeys.indexOf(key);
189 if (index >= 0)
190 {
191 if (filterKeys.length > 1)
192 {
193 filterKeys.splice(index, 1);
194 if (index == 0)
195 unconditionalFilterKeys = null;
196 }
197 else
198 {
199 delete filterKeysBySelector[filter.selector];
200 unconditionalSelectors = unconditionalFilterKeys = null;
201 }
202 return;
203 }
204 }
205
206 // We haven't found this filter in unconditional filters, look in
207 // filtersByDomain.
208 let domains = filter.domains || defaultDomains;
209 for (let domain in domains)
210 {
211 let filters = filtersByDomain[domain];
212 if (filters)
213 delete filters[key];
214 }
215 },
216
173 /** 217 /**
174 * Removes an element hiding filter 218 * Removes an element hiding filter
175 * @param {ElemHideFilter} filter 219 * @param {ElemHideFilter} filter
176 */ 220 */
177 remove: function(filter) 221 remove: function(filter)
178 { 222 {
179 if (filter instanceof ElemHideException) 223 if (filter instanceof ElemHideException)
180 { 224 {
181 if (!(filter.text in knownExceptions)) 225 if (!(filter.text in knownExceptions))
182 return; 226 return;
183 227
184 let list = exceptions[filter.selector]; 228 let list = exceptions[filter.selector];
185 let index = list.indexOf(filter); 229 let index = list.indexOf(filter);
186 if (index >= 0) 230 if (index >= 0)
187 list.splice(index, 1); 231 list.splice(index, 1);
188 delete knownExceptions[filter.text]; 232 delete knownExceptions[filter.text];
189 } 233 }
190 else 234 else
191 { 235 {
192 if (!(filter.text in keyByFilter)) 236 if (!(filter.text in keyByFilter))
193 return; 237 return;
194 238
195 let key = keyByFilter[filter.text]; 239 let key = keyByFilter[filter.text];
196 delete filterByKey[key]; 240 delete filterByKey[key];
197 delete keyByFilter[filter.text]; 241 delete keyByFilter[filter.text];
198 242 this._removeFilterKey(key, filter);
199 if (filter.selector in filterKeyBySelector)
200 {
201 delete filterKeyBySelector[filter.selector];
202 unconditionalSelectors = unconditionalFilterKeys = null;
203 }
204 else
205 {
206 let domains = filter.domains || defaultDomains;
207 for (let domain in domains)
208 {
209 let filters = filtersByDomain[domain];
210 if (filters)
211 delete filters[key];
212 }
213 }
214 } 243 }
215 244
216 FilterNotifier.emit("elemhideupdate"); 245 FilterNotifier.emit("elemhideupdate");
217 }, 246 },
218 247
219 /** 248 /**
220 * Checks whether an exception rule is registered for a filter on a particular 249 * Checks whether an exception rule is registered for a filter on a particular
221 * domain. 250 * domain.
222 */ 251 */
223 getException: function(/**Filter*/ filter, /**String*/ docDomain) /**ElemHideE xception*/ 252 getException: function(/**Filter*/ filter, /**String*/ docDomain) /**ElemHideE xception*/
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
268 return domains; 297 return domains;
269 }, 298 },
270 299
271 /** 300 /**
272 * Returns a list of selectors that apply on each website unconditionally. 301 * Returns a list of selectors that apply on each website unconditionally.
273 * @returns {String[]} 302 * @returns {String[]}
274 */ 303 */
275 getUnconditionalSelectors: function() 304 getUnconditionalSelectors: function()
276 { 305 {
277 if (!unconditionalSelectors) 306 if (!unconditionalSelectors)
278 unconditionalSelectors = Object.keys(filterKeyBySelector); 307 unconditionalSelectors = Object.keys(filterKeysBySelector);
279 return unconditionalSelectors.slice(); 308 return unconditionalSelectors.slice();
280 }, 309 },
281 310
282 /** 311 /**
283 * Returns a list of all selectors active on a particular domain. 312 * Returns a list of all selectors active on a particular domain.
284 * Returns a list of filterKeys for selectors that apply on each website 313 * Returns a list of filterKeys for selectors that apply on each website
285 * unconditionally. 314 * unconditionally.
286 * @returns {Number[]} 315 * @returns {Number[]}
287 */ 316 */
288 getUnconditionalFilterKeys: function() 317 getUnconditionalFilterKeys: function()
289 { 318 {
290 if (!unconditionalFilterKeys) 319 if (!unconditionalFilterKeys)
291 { 320 {
292 let selectors = this.getUnconditionalSelectors(); 321 let selectors = this.getUnconditionalSelectors();
293 unconditionalFilterKeys = []; 322 unconditionalFilterKeys = [];
294 for (let selector of selectors) 323 for (let selector of selectors)
295 unconditionalFilterKeys.push(filterKeyBySelector[selector]); 324 unconditionalFilterKeys.push(filterKeysBySelector[selector][0]);
296 } 325 }
297 return unconditionalFilterKeys.slice(); 326 return unconditionalFilterKeys.slice();
298 }, 327 },
299 328
300 /** 329
301 * Returns a list of all selectors active on a particular domain. Optionally 330 /**
302 * a list of corresponding filter keys for the selectors can be returned too. 331 * Constant used by getSelectorsForDomain to return all selectors applying to
303 */ 332 * a particular hostname.
304 getSelectorsForDomain: function(/**String*/ domain, /**Boolean*/ specificOnly, 333 */
305 /**Boolean*/ noUnconditional, 334 ALL_MATCHING: 0,
306 /**Boolean*/ provideFilterKeys) 335
336 /**
337 * Constant used by getSelectorsForDomain to exclude selectors which apply to
338 * all websites without exception.
339 */
340 NO_UNCONDITIONAL: 1,
341
342 /**
343 * Constant used by getSelectorsForDomain to return only selectors for filters
344 * which specifically match the given host name.
345 */
346 SPECIFIC_ONLY: 2,
347
348 /**
349 * Determines from the current filter list which selectors should be applied
350 * on a particular host name. Optionally returns the corresponding filter
351 * keys.
352 * @param {String} domain
353 * @param {Number} [criteria]
354 * One of the following: ElemHide.ALL_MATCHING, ElemHide.NO_UNCONDITIONAL or
355 * ElemHide.SPECIFIC_ONLY.
356 * @param {Boolean} [provideFilterKeys]
357 * If true, the function will return a list of corresponding filter keys in
358 * addition to selectors.
359 * @returns {string[]|Array.<string[]>}
360 * List of selectors or an array with two elements (list of selectors and
361 * list of corresponding keys) if provideFilterKeys is true.
362 */
363 getSelectorsForDomain: function(domain, criteria, provideFilterKeys)
307 { 364 {
308 let filterKeys = []; 365 let filterKeys = [];
309 let selectors = []; 366 let selectors = [];
310 if (!specificOnly && !noUnconditional) 367
368 if (typeof criteria == "undefined")
369 criteria = ElemHide.ALL_MATCHING;
370 if (criteria < ElemHide.NO_UNCONDITIONAL)
311 { 371 {
312 selectors = this.getUnconditionalSelectors(); 372 selectors = this.getUnconditionalSelectors();
313 if (provideFilterKeys) 373 if (provideFilterKeys)
314 filterKeys = this.getUnconditionalFilterKeys(); 374 filterKeys = this.getUnconditionalFilterKeys();
315 } 375 }
316 376
377 let specificOnly = (criteria >= ElemHide.SPECIFIC_ONLY);
317 let seenFilters = Object.create(null); 378 let seenFilters = Object.create(null);
318 let currentDomain = domain ? domain.toUpperCase() : ""; 379 let currentDomain = domain ? domain.toUpperCase() : "";
319 while (true) 380 while (true)
320 { 381 {
321 if (specificOnly && currentDomain == "") 382 if (specificOnly && currentDomain == "")
322 break; 383 break;
323 384
324 let filters = filtersByDomain[currentDomain]; 385 let filters = filtersByDomain[currentDomain];
325 if (filters) 386 if (filters)
326 { 387 {
(...skipping 19 matching lines...) Expand all
346 let nextDot = currentDomain.indexOf("."); 407 let nextDot = currentDomain.indexOf(".");
347 currentDomain = nextDot == -1 ? "" : currentDomain.substr(nextDot + 1); 408 currentDomain = nextDot == -1 ? "" : currentDomain.substr(nextDot + 1);
348 } 409 }
349 410
350 if (provideFilterKeys) 411 if (provideFilterKeys)
351 return [selectors, filterKeys]; 412 return [selectors, filterKeys];
352 else 413 else
353 return selectors; 414 return selectors;
354 } 415 }
355 }; 416 };
LEFTRIGHT
« no previous file | test/elemHide.js » ('j') | Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Toggle Comments ('s')

Powered by Google App Engine
This is Rietveld