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

Side by Side Diff: lib/elemHide.js

Issue 29893618: Issue 6957 - Return common style sheet for unknown domains (Closed) Base URL: https://hg.adblockplus.org/adblockpluscore/
Patch Set: Slightly refactored Created Sept. 28, 2018, 3:50 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 | no next file » | 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 <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 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
56 let unconditionalSelectors = null; 56 let unconditionalSelectors = null;
57 57
58 /** 58 /**
59 * The default style sheet that applies on all domains. This is based on the 59 * The default style sheet that applies on all domains. This is based on the
60 * value of <code>{@link unconditionalSelectors}</code>. 60 * value of <code>{@link unconditionalSelectors}</code>.
61 * @type {?string} 61 * @type {?string}
62 */ 62 */
63 let defaultStyleSheet = null; 63 let defaultStyleSheet = null;
64 64
65 /** 65 /**
66 * The common style sheet that applies on all unknown domains. This is a
67 * concatenation of the default style sheet and an additional style sheet based
68 * on selectors from all generic filters that are not in the
69 * <code>{@link unconditionalSelectors}</code> list.
70 * @type {?string}
71 */
72 let commonStyleSheet = null;
73
74 /**
66 * Map to be used instead when a filter has a blank domains property. 75 * Map to be used instead when a filter has a blank domains property.
67 * @type {Map.<string,boolean>} 76 * @type {Map.<string,boolean>}
68 * @const 77 * @const
69 */ 78 */
70 let defaultDomains = new Map([["", true]]); 79 let defaultDomains = new Map([["", true]]);
71 80
72 /** 81 /**
73 * Set containing known element hiding filters 82 * Set containing known element hiding filters
74 * @type {Set.<ElemHideFilter>} 83 * @type {Set.<ElemHideFilter>}
75 */ 84 */
76 let knownFilters = new Set(); 85 let knownFilters = new Set();
77 86
78 /** 87 /**
88 * All domains known to occur in exceptions
89 * @type {Set.<string>}
90 */
91 let knownExceptionDomains = new Set();
92
93 /**
94 * Returns the suffix of the given domain that is known. If no suffix is known,
95 * an empty string is returned.
96 * @param {?string} domain
97 * @returns {string}
98 */
99 function getKnownSuffix(domain)
100 {
101 while (domain && !filtersByDomain.has(domain) &&
102 !knownExceptionDomains.has(domain))
103 {
104 let index = domain.indexOf(".");
105 domain = index == -1 ? "" : domain.substring(index + 1);
106 }
107
108 return domain;
109 }
110
111 /**
79 * Adds a filter to the lookup table of filters by domain. 112 * Adds a filter to the lookup table of filters by domain.
80 * @param {Filter} filter 113 * @param {Filter} filter
81 */ 114 */
82 function addToFiltersByDomain(filter) 115 function addToFiltersByDomain(filter)
83 { 116 {
84 let domains = filter.domains || defaultDomains; 117 let domains = filter.domains || defaultDomains;
85 for (let [domain, isIncluded] of domains) 118 for (let [domain, isIncluded] of domains)
86 { 119 {
87 // There's no need to note that a filter is generically disabled. 120 // There's no need to note that a filter is generically disabled.
88 if (!isIncluded && domain == "") 121 if (!isIncluded && domain == "")
(...skipping 16 matching lines...) Expand all
105 unconditionalSelectors = [...filterBySelector.keys()]; 138 unconditionalSelectors = [...filterBySelector.keys()];
106 139
107 return unconditionalSelectors; 140 return unconditionalSelectors;
108 } 141 }
109 142
110 /** 143 /**
111 * Returns the list of selectors that apply on a given domain from the subset 144 * Returns the list of selectors that apply on a given domain from the subset
112 * of filters that do not apply unconditionally on all domains. 145 * of filters that do not apply unconditionally on all domains.
113 * 146 *
114 * @param {string} domain The domain. 147 * @param {string} domain The domain.
115 * @param {boolean} [specificOnly=false] Whether selectors from generic filters 148 * @param {boolean} specificOnly Whether selectors from generic filters should
116 * should be included. 149 * be included.
117 * 150 *
118 * @returns {Array.<string>} The list of selectors. 151 * @returns {Array.<string>} The list of selectors.
119 */ 152 */
120 function getConditionalSelectorsForDomain(domain, specificOnly = false) 153 function getConditionalSelectors(domain, specificOnly)
121 { 154 {
122 let selectors = []; 155 let selectors = [];
123 156
124 let excluded = new Set(); 157 let excluded = new Set();
125 let currentDomain = domain ? domain.replace(/\.+$/, "").toLowerCase() : ""; 158 let currentDomain = domain;
126 159
127 // This code is a performance hot-spot, which is why we've made certain 160 // This code is a performance hot-spot, which is why we've made certain
128 // micro-optimisations. Please be careful before making changes. 161 // micro-optimisations. Please be careful before making changes.
129 while (true) 162 while (true)
130 { 163 {
131 if (specificOnly && currentDomain == "") 164 if (specificOnly && currentDomain == "")
132 break; 165 break;
133 166
134 let filters = filtersByDomain.get(currentDomain); 167 let filters = filtersByDomain.get(currentDomain);
135 if (filters) 168 if (filters)
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
167 * @returns {string} 200 * @returns {string}
168 */ 201 */
169 function getDefaultStyleSheet() 202 function getDefaultStyleSheet()
170 { 203 {
171 if (!defaultStyleSheet) 204 if (!defaultStyleSheet)
172 defaultStyleSheet = createStyleSheet(getUnconditionalSelectors()); 205 defaultStyleSheet = createStyleSheet(getUnconditionalSelectors());
173 206
174 return defaultStyleSheet; 207 return defaultStyleSheet;
175 } 208 }
176 209
177 ElemHideExceptions.on("added", ({selector}) => 210 /**
211 * Returns the common style sheet that applies on all unknown domains.
212 * @returns {string}
213 */
214 function getCommonStyleSheet()
178 { 215 {
216 if (!commonStyleSheet)
217 {
218 commonStyleSheet = getDefaultStyleSheet() +
219 createStyleSheet(getConditionalSelectors("", false));
220 }
221
222 return commonStyleSheet;
223 }
224
225 ElemHideExceptions.on("added", ({domains, selector}) =>
226 {
227 commonStyleSheet = null;
228
229 if (domains)
230 {
231 for (let domain of domains.keys())
232 {
233 // Note: Once an exception domain is known it never becomes unknown, even
234 // when all the exceptions containing that domain are removed. This is a
235 // best-case optimization.
236 if (domain != "")
237 knownExceptionDomains.add(domain);
238 }
239 }
240
179 // If this is the first exception for a previously unconditionally applied 241 // If this is the first exception for a previously unconditionally applied
180 // element hiding selector we need to take care to update the lookups. 242 // element hiding selector we need to take care to update the lookups.
181 let unconditionalFilterForSelector = filterBySelector.get(selector); 243 let unconditionalFilterForSelector = filterBySelector.get(selector);
182 if (unconditionalFilterForSelector) 244 if (unconditionalFilterForSelector)
183 { 245 {
184 addToFiltersByDomain(unconditionalFilterForSelector); 246 addToFiltersByDomain(unconditionalFilterForSelector);
185 filterBySelector.delete(selector); 247 filterBySelector.delete(selector);
186 unconditionalSelectors = null; 248 unconditionalSelectors = null;
187 defaultStyleSheet = null; 249 defaultStyleSheet = null;
188 } 250 }
189 }); 251 });
190 252
191 /** 253 /**
192 * Container for element hiding filters 254 * Container for element hiding filters
193 * @class 255 * @class
194 */ 256 */
195 exports.ElemHide = { 257 exports.ElemHide = {
196 /** 258 /**
197 * Removes all known filters 259 * Removes all known filters
198 */ 260 */
199 clear() 261 clear()
200 { 262 {
201 for (let collection of [filtersByDomain, filterBySelector, knownFilters]) 263 commonStyleSheet = null;
264
265 for (let collection of [filtersByDomain, filterBySelector, knownFilters,
266 knownExceptionDomains])
267 {
202 collection.clear(); 268 collection.clear();
269 }
203 270
204 unconditionalSelectors = null; 271 unconditionalSelectors = null;
205 defaultStyleSheet = null; 272 defaultStyleSheet = null;
206 273
207 filterNotifier.emit("elemhideupdate"); 274 filterNotifier.emit("elemhideupdate");
208 }, 275 },
209 276
210 /** 277 /**
211 * Add a new element hiding filter 278 * Add a new element hiding filter
212 * @param {ElemHideFilter} filter 279 * @param {ElemHideFilter} filter
213 */ 280 */
214 add(filter) 281 add(filter)
215 { 282 {
216 if (knownFilters.has(filter)) 283 if (knownFilters.has(filter))
217 return; 284 return;
218 285
286 commonStyleSheet = null;
287
219 let {selector} = filter; 288 let {selector} = filter;
220 289
221 if (!(filter.domains || ElemHideExceptions.hasExceptions(selector))) 290 if (!(filter.domains || ElemHideExceptions.hasExceptions(selector)))
222 { 291 {
223 // The new filter's selector is unconditionally applied to all domains 292 // The new filter's selector is unconditionally applied to all domains
224 filterBySelector.set(selector, filter); 293 filterBySelector.set(selector, filter);
225 unconditionalSelectors = null; 294 unconditionalSelectors = null;
226 defaultStyleSheet = null; 295 defaultStyleSheet = null;
227 } 296 }
228 else 297 else
229 { 298 {
230 // The new filter's selector only applies to some domains 299 // The new filter's selector only applies to some domains
231 addToFiltersByDomain(filter); 300 addToFiltersByDomain(filter);
232 } 301 }
233 302
234 knownFilters.add(filter); 303 knownFilters.add(filter);
235 filterNotifier.emit("elemhideupdate"); 304 filterNotifier.emit("elemhideupdate");
236 }, 305 },
237 306
238 /** 307 /**
239 * Removes an element hiding filter 308 * Removes an element hiding filter
240 * @param {ElemHideFilter} filter 309 * @param {ElemHideFilter} filter
241 */ 310 */
242 remove(filter) 311 remove(filter)
243 { 312 {
244 if (!knownFilters.has(filter)) 313 if (!knownFilters.has(filter))
245 return; 314 return;
246 315
316 commonStyleSheet = null;
317
247 let {selector} = filter; 318 let {selector} = filter;
248 319
249 // Unconditially applied element hiding filters 320 // Unconditially applied element hiding filters
250 if (filterBySelector.get(selector) == filter) 321 if (filterBySelector.get(selector) == filter)
251 { 322 {
252 filterBySelector.delete(selector); 323 filterBySelector.delete(selector);
253 unconditionalSelectors = null; 324 unconditionalSelectors = null;
254 defaultStyleSheet = null; 325 defaultStyleSheet = null;
255 } 326 }
256 // Conditionally applied element hiding filters 327 // Conditionally applied element hiding filters
(...skipping 29 matching lines...) Expand all
286 * 357 *
287 * @param {string} domain The domain. 358 * @param {string} domain The domain.
288 * @param {boolean} [specificOnly=false] Whether selectors from generic 359 * @param {boolean} [specificOnly=false] Whether selectors from generic
289 * filters should be included. 360 * filters should be included.
290 * 361 *
291 * @returns {ElemHideStyleSheet} An object containing the CSS code and the 362 * @returns {ElemHideStyleSheet} An object containing the CSS code and the
292 * list of selectors. 363 * list of selectors.
293 */ 364 */
294 generateStyleSheetForDomain(domain, specificOnly = false) 365 generateStyleSheetForDomain(domain, specificOnly = false)
295 { 366 {
296 let selectors = getConditionalSelectorsForDomain(domain, specificOnly); 367 let code = null;
297 let code = specificOnly ? createStyleSheet(selectors) : 368 let selectors = null;
298 (getDefaultStyleSheet() + createStyleSheet(selectors));
299 369
300 if (!specificOnly) 370 if (domain[domain.length - 1] == ".")
371 domain = domain.replace(/\.+$/, "");
372
373 domain = domain.toLowerCase();
374
375 if (specificOnly)
376 {
377 selectors = getConditionalSelectors(domain, true);
378 code = createStyleSheet(selectors);
379 }
380 else
381 {
382 let knownSuffix = getKnownSuffix(domain);
383
384 selectors = getConditionalSelectors(knownSuffix, false);
385 code = knownSuffix == "" ? getCommonStyleSheet() :
386 (getDefaultStyleSheet() + createStyleSheet(selectors));
387
301 selectors = getUnconditionalSelectors().concat(selectors); 388 selectors = getUnconditionalSelectors().concat(selectors);
389 }
302 390
303 return {code, selectors}; 391 return {code, selectors};
304 } 392 }
305 }; 393 };
306 394
307 /** 395 /**
308 * Splits a list of selectors into groups determined by the value of 396 * Splits a list of selectors into groups determined by the value of
309 * <code>{@link selectorGroupSize}</code>. 397 * <code>{@link selectorGroupSize}</code>.
310 * 398 *
311 * @param {Array.<string>} selectors 399 * @param {Array.<string>} selectors
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
355 { 443 {
356 let styleSheet = ""; 444 let styleSheet = "";
357 445
358 for (let selectorGroup of splitSelectors(selectors)) 446 for (let selectorGroup of splitSelectors(selectors))
359 styleSheet += createRule(selectorGroup); 447 styleSheet += createRule(selectorGroup);
360 448
361 return styleSheet; 449 return styleSheet;
362 } 450 }
363 451
364 exports.createStyleSheet = createStyleSheet; 452 exports.createStyleSheet = createStyleSheet;
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld