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

Delta Between Two Patch Sets: lib/elemHide.js

Issue 29773570: Issue 6652 - Implement fast selector lookups for unknown domains (Closed) Base URL: https://hg.adblockplus.org/adblockpluscore/
Left Patch Set: Rebase on master Created May 11, 2018, 1:30 p.m.
Right Patch Set: Avoid unnecessary Array.concat Created May 23, 2018, 12:22 a.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-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
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
71 */ 71 */
72 let genericExceptions = new Map(); 72 let genericExceptions = new Map();
73 73
74 /** 74 /**
75 * List of selectors that apply on any unknown domain 75 * List of selectors that apply on any unknown domain
76 * @type {?string[]} 76 * @type {?string[]}
77 */ 77 */
78 let conditionalGenericSelectors = null; 78 let conditionalGenericSelectors = null;
79 79
80 /** 80 /**
81 * Domains that are known not to be specifically excluded from any generic
82 * filters
83 * @type {Set.<string>}
84 */
85 let genericFriendlyDomains = new Set();
86
87 /**
88 * Adds a filter to the lookup table of filters by domain. 81 * Adds a filter to the lookup table of filters by domain.
89 * @param {Filter} filter 82 * @param {Filter} filter
90 */ 83 */
91 function addToFiltersByDomain(filter) 84 function addToFiltersByDomain(filter)
92 { 85 {
93 let domains = filter.domains || defaultDomains; 86 let domains = filter.domains || defaultDomains;
94 if (filter instanceof ElemHideException) 87 if (filter instanceof ElemHideException)
95 { 88 {
96 for (let domain of domains.keys()) 89 for (let domain of domains.keys())
97 { 90 {
(...skipping 27 matching lines...) Expand all
125 */ 118 */
126 function getSpecificFiltersForDomain(domain) 119 function getSpecificFiltersForDomain(domain)
127 { 120 {
128 let filtersList = []; 121 let filtersList = [];
129 122
130 if (domain) 123 if (domain)
131 domain = domain.toUpperCase(); 124 domain = domain.toUpperCase();
132 125
133 while (domain) 126 while (domain)
134 { 127 {
135 // Note that we also push null values into the list, because
136 // ElemHide.getSelectorsForDomain still needs to know if there are any
137 // entries for the domain.
138 let filters = filtersByDomain.get(domain); 128 let filters = filtersByDomain.get(domain);
139 if (typeof filters != "undefined") 129 if (typeof filters != "undefined")
140 filtersList.push(filters); 130 filtersList.push(filters);
141 131
142 let nextDot = domain.indexOf("."); 132 let nextDot = domain.indexOf(".");
143 domain = nextDot == -1 ? null : domain.substring(nextDot + 1); 133 domain = nextDot == -1 ? null : domain.substring(nextDot + 1);
144 } 134 }
145 135
146 return filtersList; 136 return filtersList;
147 } 137 }
148 138
149 /** 139 /**
150 * Returns a list of selectors from a given list of filters that apply on a 140 * Returns a list of selectors that apply on a domain from a given list of
151 * domain 141 * filters
152 * @param {string} [domain] 142 * @param {string} [domain]
153 * @param {Array.<?Map.<Filter,boolean>>} filtersList 143 * @param {Array.<?Map.<Filter,boolean>>} filtersList
154 * @param {boolean} specificOnly 144 * @param {Set.<Filter>} excludeSet
155 * @returns {string[]} 145 * @returns {string[]}
156 */ 146 */
157 function getConditionalSelectorsForDomain(domain, filtersList, specificOnly) 147 function matchSelectors(domain, filtersList, excludeSet)
158 { 148 {
159 let selectors = []; 149 let matches = [];
160
161 let genericFilters = !specificOnly ? filtersList.pop() : null;
162 let excluded = new Set();
163 150
164 // This code is a performance hot-spot, which is why we've made certain 151 // This code is a performance hot-spot, which is why we've made certain
165 // micro-optimisations. Please be careful before making changes. 152 // micro-optimisations. Please be careful before making changes.
166 for (let i = 0; i < filtersList.length; i++) 153 for (let i = 0; i < filtersList.length; i++)
167 { 154 {
168 if (!filtersList[i]) 155 let filters = filtersList[i];
169 continue; 156 if (filters)
170 157 {
171 for (let [filter, isIncluded] of filtersList[i]) 158 for (let [filter, isIncluded] of filters)
172 {
173 if (!isIncluded)
174 { 159 {
175 excluded.add(filter); 160 if (!isIncluded)
161 {
162 excludeSet.add(filter);
163 }
164 else if ((excludeSet.size == 0 || !excludeSet.has(filter)) &&
165 !exports.ElemHide.getException(filter, domain))
166 {
167 matches.push(filter.selector);
168 }
176 } 169 }
177 else if ((excluded.size == 0 || !excluded.has(filter)) && 170 }
178 !ElemHide.getException(filter, domain)) 171 }
179 { 172
180 selectors.push(filter.selector); 173 return matches;
181 }
182 }
183 }
184
185 if (!genericFilters)
186 return selectors;
187
188 if (genericFriendlyDomains.has(domain))
189 return selectors.concat(getConditionalGenericSelectors());
190
191 let genericSelectors = [];
192
193 for (let filter of genericFilters.keys())
194 {
195 if ((excluded.size == 0 || !excluded.has(filter)) &&
196 !ElemHide.getException(filter, domain))
197 {
198 genericSelectors.push(filter.selector);
199 }
200 }
201
202 // If the number of conditional generic selectors that apply on this domain
203 // is the same as the total number of conditional generic selectors, the
204 // domain is "generic friendly". In that case, we mark it is as such for
205 // faster lookups.
206 if (conditionalGenericSelectors &&
207 genericSelectors.length == conditionalGenericSelectors.length)
208 {
209 if (genericFriendlyDomains.size >= 1000)
210 genericFriendlyDomains.clear();
211
212 genericFriendlyDomains.add(domain);
213 }
214
215 return selectors.concat(genericSelectors);
216 } 174 }
217 175
218 /** 176 /**
219 * Returns a list of selectors that apply on any unknown domain 177 * Returns a list of selectors that apply on any unknown domain
220 * @returns {string[]} 178 * @returns {string[]}
221 */ 179 */
222 function getConditionalGenericSelectors() 180 function getConditionalGenericSelectors()
223 { 181 {
224 if (conditionalGenericSelectors) 182 if (conditionalGenericSelectors)
225 return conditionalGenericSelectors; 183 return conditionalGenericSelectors;
(...skipping 22 matching lines...) Expand all
248 if (!unconditionalSelectors) 206 if (!unconditionalSelectors)
249 unconditionalSelectors = [...filterBySelector.keys()]; 207 unconditionalSelectors = [...filterBySelector.keys()];
250 208
251 return unconditionalSelectors; 209 return unconditionalSelectors;
252 } 210 }
253 211
254 /** 212 /**
255 * Container for element hiding filters 213 * Container for element hiding filters
256 * @class 214 * @class
257 */ 215 */
258 let ElemHide = exports.ElemHide = { 216 exports.ElemHide = {
259 /** 217 /**
260 * Removes all known filters 218 * Removes all known filters
261 */ 219 */
262 clear() 220 clear()
263 { 221 {
264 for (let collection of [filtersByDomain, filterBySelector, 222 for (let collection of [filtersByDomain, filterBySelector,
265 knownFilters, exceptions, 223 knownFilters, exceptions,
266 genericExceptions, genericFriendlyDomains]) 224 genericExceptions])
267 { 225 {
268 collection.clear(); 226 collection.clear();
269 } 227 }
270 unconditionalSelectors = null; 228 unconditionalSelectors = null;
271 conditionalGenericSelectors = null; 229 conditionalGenericSelectors = null;
272 FilterNotifier.emit("elemhideupdate"); 230 FilterNotifier.emit("elemhideupdate");
273 }, 231 },
274 232
275 /** 233 /**
276 * Add a new element hiding filter 234 * Add a new element hiding filter
277 * @param {ElemHideBase} filter 235 * @param {ElemHideBase} filter
278 */ 236 */
279 add(filter) 237 add(filter)
280 { 238 {
281 if (knownFilters.has(filter)) 239 if (knownFilters.has(filter))
282 return; 240 return;
283 241
284 conditionalGenericSelectors = null; 242 conditionalGenericSelectors = null;
285 genericFriendlyDomains.clear();
286 243
287 if (filter instanceof ElemHideException) 244 if (filter instanceof ElemHideException)
288 { 245 {
289 let {selector, domains} = filter; 246 let {selector, domains} = filter;
290 247
291 let list = exceptions.get(selector); 248 let list = exceptions.get(selector);
292 if (list) 249 if (list)
293 list.push(filter); 250 list.push(filter);
294 else 251 else
295 exceptions.set(selector, [filter]); 252 exceptions.set(selector, [filter]);
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
336 /** 293 /**
337 * Removes an element hiding filter 294 * Removes an element hiding filter
338 * @param {ElemHideBase} filter 295 * @param {ElemHideBase} filter
339 */ 296 */
340 remove(filter) 297 remove(filter)
341 { 298 {
342 if (!knownFilters.has(filter)) 299 if (!knownFilters.has(filter))
343 return; 300 return;
344 301
345 conditionalGenericSelectors = null; 302 conditionalGenericSelectors = null;
346 genericFriendlyDomains.clear();
347 303
348 // Whitelisting filters 304 // Whitelisting filters
349 if (filter instanceof ElemHideException) 305 if (filter instanceof ElemHideException)
350 { 306 {
351 let list = exceptions.get(filter.selector); 307 let list = exceptions.get(filter.selector);
352 let index = list.indexOf(filter); 308 let index = list.indexOf(filter);
353 if (index >= 0) 309 if (index >= 0)
354 list.splice(index, 1); 310 list.splice(index, 1);
355 311
356 if (filter.isGeneric()) 312 if (filter.isGeneric())
357 { 313 {
358 list = genericExceptions.get(filter.selector); 314 list = genericExceptions.get(filter.selector);
359 index = list.indexOf(filter); 315 index = list.indexOf(filter);
360 if (index >= 0) 316 if (index >= 0)
361 list.splice(index, 1); 317 list.splice(index, 1);
362 318
319 // It's important to delete the entry here so the selector no longer
320 // appears to have any generic exceptions.
363 if (list.length == 0) 321 if (list.length == 0)
364 genericExceptions.delete(filter.selector); 322 genericExceptions.delete(filter.selector);
365 } 323 }
366 } 324 }
367 // Unconditially applied element hiding filters 325 // Unconditially applied element hiding filters
368 else if (filterBySelector.get(filter.selector) == filter) 326 else if (filterBySelector.get(filter.selector) == filter)
369 { 327 {
370 filterBySelector.delete(filter.selector); 328 filterBySelector.delete(filter.selector);
371 unconditionalSelectors = null; 329 unconditionalSelectors = null;
372 } 330 }
(...skipping 29 matching lines...) Expand all
402 for (let i = list.length - 1; i >= 0; i--) 360 for (let i = list.length - 1; i >= 0; i--)
403 { 361 {
404 if (list[i].isActiveOnDomain(docDomain)) 362 if (list[i].isActiveOnDomain(docDomain))
405 return list[i]; 363 return list[i];
406 } 364 }
407 365
408 return null; 366 return null;
409 }, 367 },
410 368
411 /** 369 /**
412 * Constant used by getSelectorsForDomain to return all selectors applying to
413 * a particular hostname.
414 * @type {number}
415 * @const
416 */
417 ALL_MATCHING: 0,
418
419 /**
420 * Constant used by getSelectorsForDomain to exclude selectors which apply to
421 * all websites without exception.
422 * @type {number}
423 * @const
424 */
425 NO_UNCONDITIONAL: 1,
426
427 /**
428 * Constant used by getSelectorsForDomain to return only selectors for filters
429 * which specifically match the given host name.
430 * @type {number}
431 * @const
432 */
433 SPECIFIC_ONLY: 2,
434
435 /**
436 * Determines from the current filter list which selectors should be applied 370 * Determines from the current filter list which selectors should be applied
437 * on a particular host name. 371 * on a particular host name.
438 * @param {string} domain 372 * @param {string} domain
439 * @param {number} [criteria] 373 * @param {boolean} [specificOnly] true if generic filters should not apply.
440 * One of the following: ElemHide.ALL_MATCHING, ElemHide.NO_UNCONDITIONAL or 374 * @returns {string[]} List of selectors.
441 * ElemHide.SPECIFIC_ONLY.
442 * @returns {string[]}
443 * List of selectors.
444 */ 375 */
445 getSelectorsForDomain(domain, criteria = ElemHide.ALL_MATCHING) 376 getSelectorsForDomain(domain, specificOnly = false)
446 { 377 {
447 let selectors = []; 378 let specificFilters = getSpecificFiltersForDomain(domain);
448 379
449 let specificOnly = criteria >= ElemHide.SPECIFIC_ONLY; 380 // If there are no specific filters (nor any specific exceptions), we can
450 let filtersList = getSpecificFiltersForDomain(domain); 381 // just return the selectors from all the generic filters modulo any
451 382 // generic exceptions.
452 if (filtersList.length > 0) 383 if (specificFilters.length == 0)
453 { 384 {
454 if (!specificOnly) 385 return specificOnly ? [] :
455 filtersList.push(filtersByDomain.get("")); 386 getUnconditionalSelectors()
456 387 .concat(getConditionalGenericSelectors());
457 selectors = getConditionalSelectorsForDomain(domain, filtersList, 388 }
458 specificOnly); 389
459 } 390 let excluded = new Set();
460 else if (!specificOnly) 391 let selectors = matchSelectors(domain, specificFilters, excluded);
461 { 392
462 selectors = getConditionalGenericSelectors(); 393 if (specificOnly)
463 } 394 return selectors;
464 395
465 if (criteria < ElemHide.NO_UNCONDITIONAL) 396 return getUnconditionalSelectors()
466 selectors = getUnconditionalSelectors().concat(selectors); 397 .concat(selectors,
467 398 matchSelectors(domain, [filtersByDomain.get("")],
468 // If the above logic leaves us with a reference to our internal cache of 399 excluded));
469 // selectors, we make a copy here.
470 if (selectors == conditionalGenericSelectors)
471 selectors = selectors.slice();
472
473 return selectors;
474 } 400 }
475 }; 401 };
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