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: Created Aug. 8, 2016, 3:24 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 18 matching lines...) Expand all
29 */ 29 */
30 var filterByKey = []; 30 var filterByKey = [];
31 31
32 /** 32 /**
33 * Lookup table, keys of the filters by filter text 33 * Lookup table, keys of the filters by filter text
34 * @type Object 34 * @type Object
35 */ 35 */
36 var keyByFilter = Object.create(null); 36 var keyByFilter = Object.create(null);
37 37
38 /** 38 /**
39 * Indicates whether we are using the getSelectorsForDomain function and
40 * therefore maintaining the required filtersByDomain, filterKeysBySelector,
41 * unconditionalSelectors and unconditionalFilterKeys lookups.
42 * (Will be false for Firefox)
43 * @type Boolean
44 */
45 var usingGetSelectorsForDomain = !("nsIStyleSheetService" in Ci);
Wladimir Palant 2016/09/19 08:53:59 This flag needs to go - we'll be using this functi
kzar 2016/09/19 16:54:31 Done.
46
47 /**
48 * 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.
49 * (Only contains filters that aren't unconditionally matched for all domains.) 40 * (Only contains filters that aren't unconditionally matched for all domains.)
50 * @type Object 41 * @type Object
51 */ 42 */
52 var filtersByDomain = Object.create(null); 43 var filtersByDomain = Object.create(null);
53 44
54 /** 45 /**
55 * Lookup table, filters keys by selector. (Only contains filters that have a 46 * Lookup table, filter keys by selector. (Only contains filters that have a
56 * selector that is unconditionally matched for all domains.) 47 * selector that is unconditionally matched for all domains.)
57 */ 48 */
58 var filterKeysBySelector = Object.create(null); 49 var filterKeysBySelector = Object.create(null);
59 50
60 /** 51 /**
61 * This array caches the keys of filterKeysBySelector table (selectors which 52 * This array caches the keys of filterKeysBySelector table (selectors which
62 * 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
63 * be rebuilt. 54 * be rebuilt.
64 */ 55 */
65 var unconditionalSelectors = null; 56 var unconditionalSelectors = null;
66 57
67 /** 58 /**
68 * This array caches the values of filterKeysBySelector table (filterIds for 59 * This array caches the values of filterKeysBySelector table (filterIds for
69 * 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
70 * cache needs to be rebuilt. 61 * cache needs to be rebuilt. Note: Only the first filter key for each selector
62 * is cached.
71 */ 63 */
72 var unconditionalFilterKeys = null; 64 var unconditionalFilterKeys = null;
73 65
74 /** 66 /**
75 * 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.
76 */ 68 */
77 var defaultDomains = Object.create(null); 69 var defaultDomains = Object.create(null);
78 defaultDomains[""] = true; 70 defaultDomains[""] = true;
79 71
80 /** 72 /**
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
135 if (filter instanceof ElemHideException) 127 if (filter instanceof ElemHideException)
136 { 128 {
137 if (filter.text in knownExceptions) 129 if (filter.text in knownExceptions)
138 return; 130 return;
139 131
140 let selector = filter.selector; 132 let selector = filter.selector;
141 if (!(selector in exceptions)) 133 if (!(selector in exceptions))
142 exceptions[selector] = []; 134 exceptions[selector] = [];
143 exceptions[selector].push(filter); 135 exceptions[selector].push(filter);
144 136
145 if (usingGetSelectorsForDomain) 137 // If this is the first exception for a previously unconditionally
138 // applied element hiding selector we need to take care to update the
139 // lookups.
140 let filterKeys = filterKeysBySelector[selector];
141 if (filterKeys)
146 { 142 {
147 // If this is the first exception for a previously unconditionally 143 for (let filterKey of filterKeys)
148 // applied element hiding selector we need to take care to update the 144 this._addToFiltersByDomain(filterKey, filterByKey[filterKey]);
149 // lookups. 145 delete filterKeysBySelector[selector];
150 let unconditionalFilterKeys = filterKeysBySelector[selector]; 146 unconditionalSelectors = unconditionalFilterKeys = null;
151 if (unconditionalFilterKeys) 147 }
148
149 knownExceptions[filter.text] = true;
150 }
151 else
152 {
153 if (filter.text in keyByFilter)
154 return;
155
156 let key = filterByKey.push(filter) - 1;
157 keyByFilter[filter.text] = key;
158
159 if (!(filter.domains || filter.selector in exceptions))
160 {
161 // The new filter's selector is unconditionally applied to all domains
162 let filterKeys = filterKeysBySelector[filter.selector];
163 if (filterKeys)
152 { 164 {
153 for (let filterKey of unconditionalFilterKeys) 165 filterKeys.push(key);
154 this._addToFiltersByDomain(filterKey, filterByKey[filterKey]); 166 }
155 delete filterKeysBySelector[selector]; 167 else
168 {
169 filterKeysBySelector[filter.selector] = [key];
156 unconditionalSelectors = unconditionalFilterKeys = null; 170 unconditionalSelectors = unconditionalFilterKeys = null;
157 } 171 }
158 } 172 }
159 173 else
160 knownExceptions[filter.text] = true;
161 }
162 else
163 {
164 if (filter.text in keyByFilter)
165 return;
166
167 let key = filterByKey.push(filter) - 1;
168 keyByFilter[filter.text] = key;
169
170 if (usingGetSelectorsForDomain)
171 { 174 {
172 if (!(filter.domains || filter.selector in exceptions)) 175 // The new filter's selector only applies to some domains
176 this._addToFiltersByDomain(key, filter);
177 }
178 }
179
180 FilterNotifier.emit("elemhideupdate");
181 },
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)
173 { 192 {
174 // The new filter's selector is unconditionally applied to all domains 193 filterKeys.splice(index, 1);
175 let filterKeys = filterKeysBySelector[filter.selector]; 194 if (index == 0)
176 if (filterKeys) 195 unconditionalFilterKeys = null;
177 {
178 filterKeys.push(filter);
Wladimir Palant 2016/09/19 08:53:59 Shouldn't you push `key` here? Given that you hav
kzar 2016/09/19 16:54:30 As discussed in IRC it turns out this branch was r
179 }
180 else
181 {
182 filterKeysBySelector[filter.selector] = [key];
183 unconditionalSelectors = unconditionalFilterKeys = null;
184 }
185 } 196 }
186 else 197 else
187 { 198 {
188 // The new filter's selector only applies to some domains 199 delete filterKeysBySelector[filter.selector];
189 this._addToFiltersByDomain(key, filter); 200 unconditionalSelectors = unconditionalFilterKeys = null;
190 } 201 }
202 return;
191 } 203 }
192 } 204 }
193 205
194 FilterNotifier.emit("elemhideupdate"); 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 }
195 }, 215 },
196 216
197 /** 217 /**
198 * Removes an element hiding filter 218 * Removes an element hiding filter
199 * @param {ElemHideFilter} filter 219 * @param {ElemHideFilter} filter
200 */ 220 */
201 remove: function(filter) 221 remove: function(filter)
202 { 222 {
203 if (filter instanceof ElemHideException) 223 if (filter instanceof ElemHideException)
204 { 224 {
205 if (!(filter.text in knownExceptions)) 225 if (!(filter.text in knownExceptions))
206 return; 226 return;
207 227
208 let list = exceptions[filter.selector]; 228 let list = exceptions[filter.selector];
209 let index = list.indexOf(filter); 229 let index = list.indexOf(filter);
210 if (index >= 0) 230 if (index >= 0)
211 list.splice(index, 1); 231 list.splice(index, 1);
212 delete knownExceptions[filter.text]; 232 delete knownExceptions[filter.text];
213 } 233 }
214 else 234 else
215 { 235 {
216 if (!(filter.text in keyByFilter)) 236 if (!(filter.text in keyByFilter))
217 return; 237 return;
218 238
219 let key = keyByFilter[filter.text]; 239 let key = keyByFilter[filter.text];
220 delete filterByKey[key]; 240 delete filterByKey[key];
221 delete keyByFilter[filter.text]; 241 delete keyByFilter[filter.text];
222 242 this._removeFilterKey(key, filter);
223 if (usingGetSelectorsForDomain)
224 {
225 let filterKeys = filterKeysBySelector[filter.selector];
226 if (filterKeys)
227 {
228 if (filterKeys.length > 1)
229 {
230 let index = filterKeys.indexOf(key);
231 filterKeys.splice(index, 1);
232 }
233 else
234 {
235 delete filterKeysBySelector[filter.selector];
236 unconditionalSelectors = unconditionalFilterKeys = null;
237 }
238 }
239 else
240 {
241 let domains = filter.domains || defaultDomains;
242 for (let domain in domains)
243 {
244 let filters = filtersByDomain[domain];
245 if (filters)
246 delete filters[key];
247 }
248 }
249 }
250 } 243 }
251 244
252 FilterNotifier.emit("elemhideupdate"); 245 FilterNotifier.emit("elemhideupdate");
253 }, 246 },
254 247
255 /** 248 /**
256 * 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
257 * domain. 250 * domain.
258 */ 251 */
259 getException: function(/**Filter*/ filter, /**String*/ docDomain) /**ElemHideE xception*/ 252 getException: function(/**Filter*/ filter, /**String*/ docDomain) /**ElemHideE xception*/
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
303 296
304 return domains; 297 return domains;
305 }, 298 },
306 299
307 /** 300 /**
308 * Returns a list of selectors that apply on each website unconditionally. 301 * Returns a list of selectors that apply on each website unconditionally.
309 * @returns {String[]} 302 * @returns {String[]}
310 */ 303 */
311 getUnconditionalSelectors: function() 304 getUnconditionalSelectors: function()
312 { 305 {
313 if (!usingGetSelectorsForDomain)
314 throw new Error("getUconditionalSelectors can not be used in Firefox!");
315
316 if (!unconditionalSelectors) 306 if (!unconditionalSelectors)
317 unconditionalSelectors = Object.keys(filterKeysBySelector); 307 unconditionalSelectors = Object.keys(filterKeysBySelector);
318 return unconditionalSelectors.slice(); 308 return unconditionalSelectors.slice();
319 }, 309 },
320 310
321 /** 311 /**
322 * Returns a list of all selectors active on a particular domain. 312 * Returns a list of all selectors active on a particular domain.
323 * 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
324 * unconditionally. 314 * unconditionally.
325 * @returns {Number[]} 315 * @returns {Number[]}
326 */ 316 */
327 getUnconditionalFilterKeys: function() 317 getUnconditionalFilterKeys: function()
328 { 318 {
329 if (!usingGetSelectorsForDomain)
330 throw new Error("getUconditionalFilterKeys can not be used in Firefox!");
331
332 if (!unconditionalFilterKeys) 319 if (!unconditionalFilterKeys)
333 { 320 {
334 let selectors = this.getUnconditionalSelectors(); 321 let selectors = this.getUnconditionalSelectors();
335 unconditionalFilterKeys = []; 322 unconditionalFilterKeys = [];
336 for (let selector of selectors) 323 for (let selector of selectors)
337 for (let key of filterKeysBySelector[selector]) 324 unconditionalFilterKeys.push(filterKeysBySelector[selector][0]);
338 unconditionalFilterKeys.push(key);
Wladimir Palant 2016/09/19 08:53:59 On Firefox we actually need a mapping from selecto
kzar 2016/09/19 16:54:31 Since we only store one key per unconditional sele
339 } 325 }
340 return unconditionalFilterKeys.slice(); 326 return unconditionalFilterKeys.slice();
341 }, 327 },
342 328
343 /** 329
344 * Returns a list of all selectors active on a particular domain. Optionally 330 /**
345 * a list of corresponding filter keys for the selectors can be returned too. 331 * Constant used by getSelectorsForDomain to return all selectors applying to
346 * (Must not be used in Firefox - when usingGetSelectorsForDomain is false). 332 * a particular hostname.
Wladimir Palant 2016/09/19 08:53:59 Actually, it will be used in Firefox now, that's t
kzar 2016/09/19 16:54:31 Done.
347 */ 333 */
348 getSelectorsForDomain: function(/**String*/ domain, /**Boolean*/ specificOnly, 334 ALL_MATCHING: 0,
349 /**Boolean*/ noUnconditional, 335
350 /**Boolean*/ provideFilterKeys) 336 /**
351 { 337 * Constant used by getSelectorsForDomain to exclude selectors which apply to
352 if (!usingGetSelectorsForDomain) 338 * all websites without exception.
353 throw new Error("getSelectorsForDomain can not be used in Firefox!"); 339 */
354 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)
364 {
355 let filterKeys = []; 365 let filterKeys = [];
356 let selectors = []; 366 let selectors = [];
357 if (!(specificOnly || noUnconditional)) 367
Wladimir Palant 2016/09/19 08:53:59 Personally, I find this easier to understand if yo
kzar 2016/09/19 16:54:30 Done.
368 if (typeof criteria == "undefined")
369 criteria = ElemHide.ALL_MATCHING;
370 if (criteria < ElemHide.NO_UNCONDITIONAL)
358 { 371 {
359 selectors = this.getUnconditionalSelectors(); 372 selectors = this.getUnconditionalSelectors();
360 if (provideFilterKeys) 373 if (provideFilterKeys)
361 filterKeys = this.getUnconditionalFilterKeys(); 374 filterKeys = this.getUnconditionalFilterKeys();
362 } 375 }
363 376
377 let specificOnly = (criteria >= ElemHide.SPECIFIC_ONLY);
364 let seenFilters = Object.create(null); 378 let seenFilters = Object.create(null);
365 let currentDomain = domain ? domain.toUpperCase() : ""; 379 let currentDomain = domain ? domain.toUpperCase() : "";
366 while (true) 380 while (true)
367 { 381 {
368 if (specificOnly && currentDomain == "") 382 if (specificOnly && currentDomain == "")
369 break; 383 break;
370 384
371 let filters = filtersByDomain[currentDomain]; 385 let filters = filtersByDomain[currentDomain];
372 if (filters) 386 if (filters)
373 { 387 {
(...skipping 19 matching lines...) Expand all
393 let nextDot = currentDomain.indexOf("."); 407 let nextDot = currentDomain.indexOf(".");
394 currentDomain = nextDot == -1 ? "" : currentDomain.substr(nextDot + 1); 408 currentDomain = nextDot == -1 ? "" : currentDomain.substr(nextDot + 1);
395 } 409 }
396 410
397 if (provideFilterKeys) 411 if (provideFilterKeys)
398 return [selectors, filterKeys]; 412 return [selectors, filterKeys];
399 else 413 else
400 return selectors; 414 return selectors;
401 } 415 }
402 }; 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