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: Minor improvements Created Sept. 27, 2018, 9: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 | lib/elemHideExceptions.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 <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 /**
82 * Set containing all known domains
83 * @type {Set.<string>}
84 */
85 let knownDomains = new Set();
86
87 /**
73 * Set containing known element hiding filters 88 * Set containing known element hiding filters
74 * @type {Set.<ElemHideFilter>} 89 * @type {Set.<ElemHideFilter>}
75 */ 90 */
76 let knownFilters = new Set(); 91 let knownFilters = new Set();
77 92
78 /** 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 if (domain[domain.length - 1] == ".")
102 domain = domain.replace(/\.+$/, "");
103
104 domain = domain.toLowerCase();
105
106 while (domain)
107 {
108 if (knownDomains.has(domain) || ElemHideExceptions.isKnownDomain(domain))
109 break;
110
111 let index = domain.indexOf(".");
112 domain = index == -1 ? "" : domain.substring(index + 1);
113 }
114
115 return domain;
116 }
117
118 /**
79 * Adds a filter to the lookup table of filters by domain. 119 * Adds a filter to the lookup table of filters by domain.
80 * @param {Filter} filter 120 * @param {Filter} filter
81 */ 121 */
82 function addToFiltersByDomain(filter) 122 function addToFiltersByDomain(filter)
83 { 123 {
84 let domains = filter.domains || defaultDomains; 124 let domains = filter.domains || defaultDomains;
85 for (let [domain, isIncluded] of domains) 125 for (let [domain, isIncluded] of domains)
86 { 126 {
87 // There's no need to note that a filter is generically disabled. 127 // There's no need to note that a filter is generically disabled.
88 if (!isIncluded && domain == "") 128 if (domain == "")
89 continue; 129 {
130 if (!isIncluded)
131 continue;
132 }
133 else
134 {
135 // Note: Once a domain is known it never becomes unknown, even when all
136 // the filters containing that domain are removed. This is a best-case
137 // optimization.
138 knownDomains.add(domain);
139 }
90 140
91 let filters = filtersByDomain.get(domain); 141 let filters = filtersByDomain.get(domain);
92 if (!filters) 142 if (!filters)
93 filtersByDomain.set(domain, filters = new Map()); 143 filtersByDomain.set(domain, filters = new Map());
94 filters.set(filter, isIncluded); 144 filters.set(filter, isIncluded);
95 } 145 }
96 } 146 }
97 147
98 /** 148 /**
99 * Returns a list of selectors that apply on each website unconditionally. 149 * Returns a list of selectors that apply on each website unconditionally.
(...skipping 15 matching lines...) Expand all
115 * @param {boolean} [specificOnly=false] Whether selectors from generic filters 165 * @param {boolean} [specificOnly=false] Whether selectors from generic filters
116 * should be included. 166 * should be included.
117 * 167 *
118 * @returns {Array.<string>} The list of selectors. 168 * @returns {Array.<string>} The list of selectors.
119 */ 169 */
120 function getConditionalSelectorsForDomain(domain, specificOnly = false) 170 function getConditionalSelectorsForDomain(domain, specificOnly = false)
121 { 171 {
122 let selectors = []; 172 let selectors = [];
123 173
124 let excluded = new Set(); 174 let excluded = new Set();
125 let currentDomain = domain ? domain.replace(/\.+$/, "").toLowerCase() : ""; 175 let currentDomain = domain;
126 176
127 // This code is a performance hot-spot, which is why we've made certain 177 // This code is a performance hot-spot, which is why we've made certain
128 // micro-optimisations. Please be careful before making changes. 178 // micro-optimisations. Please be careful before making changes.
129 while (true) 179 while (true)
130 { 180 {
131 if (specificOnly && currentDomain == "") 181 if (specificOnly && currentDomain == "")
132 break; 182 break;
133 183
134 let filters = filtersByDomain.get(currentDomain); 184 let filters = filtersByDomain.get(currentDomain);
135 if (filters) 185 if (filters)
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
167 * @returns {string} 217 * @returns {string}
168 */ 218 */
169 function getDefaultStyleSheet() 219 function getDefaultStyleSheet()
170 { 220 {
171 if (!defaultStyleSheet) 221 if (!defaultStyleSheet)
172 defaultStyleSheet = createStyleSheet(getUnconditionalSelectors()); 222 defaultStyleSheet = createStyleSheet(getUnconditionalSelectors());
173 223
174 return defaultStyleSheet; 224 return defaultStyleSheet;
175 } 225 }
176 226
227 /**
228 * Returns the common style sheet that applies on all unknown domains.
229 * @returns {string}
230 */
231 function getCommonStyleSheet()
232 {
233 if (!commonStyleSheet)
234 commonStyleSheet = getDefaultStyleSheet() +
235 createStyleSheet(getConditionalSelectorsForDomain(""));
236
237 return commonStyleSheet;
238 }
239
177 ElemHideExceptions.on("added", ({selector}) => 240 ElemHideExceptions.on("added", ({selector}) =>
178 { 241 {
242 commonStyleSheet = null;
243
179 // If this is the first exception for a previously unconditionally applied 244 // If this is the first exception for a previously unconditionally applied
180 // element hiding selector we need to take care to update the lookups. 245 // element hiding selector we need to take care to update the lookups.
181 let unconditionalFilterForSelector = filterBySelector.get(selector); 246 let unconditionalFilterForSelector = filterBySelector.get(selector);
182 if (unconditionalFilterForSelector) 247 if (unconditionalFilterForSelector)
183 { 248 {
184 addToFiltersByDomain(unconditionalFilterForSelector); 249 addToFiltersByDomain(unconditionalFilterForSelector);
185 filterBySelector.delete(selector); 250 filterBySelector.delete(selector);
186 unconditionalSelectors = null; 251 unconditionalSelectors = null;
187 defaultStyleSheet = null; 252 defaultStyleSheet = null;
188 } 253 }
189 }); 254 });
190 255
191 /** 256 /**
192 * Container for element hiding filters 257 * Container for element hiding filters
193 * @class 258 * @class
194 */ 259 */
195 exports.ElemHide = { 260 exports.ElemHide = {
196 /** 261 /**
197 * Removes all known filters 262 * Removes all known filters
198 */ 263 */
199 clear() 264 clear()
200 { 265 {
201 for (let collection of [filtersByDomain, filterBySelector, knownFilters]) 266 commonStyleSheet = null;
267
268 for (let collection of [filtersByDomain, filterBySelector, knownDomains,
269 knownFilters])
202 collection.clear(); 270 collection.clear();
203 271
204 unconditionalSelectors = null; 272 unconditionalSelectors = null;
205 defaultStyleSheet = null; 273 defaultStyleSheet = null;
206 274
207 filterNotifier.emit("elemhideupdate"); 275 filterNotifier.emit("elemhideupdate");
208 }, 276 },
209 277
210 /** 278 /**
211 * Add a new element hiding filter 279 * Add a new element hiding filter
212 * @param {ElemHideFilter} filter 280 * @param {ElemHideFilter} filter
213 */ 281 */
214 add(filter) 282 add(filter)
215 { 283 {
216 if (knownFilters.has(filter)) 284 if (knownFilters.has(filter))
217 return; 285 return;
218 286
287 commonStyleSheet = null;
288
219 let {selector} = filter; 289 let {selector} = filter;
220 290
221 if (!(filter.domains || ElemHideExceptions.hasExceptions(selector))) 291 if (!(filter.domains || ElemHideExceptions.hasExceptions(selector)))
222 { 292 {
223 // The new filter's selector is unconditionally applied to all domains 293 // The new filter's selector is unconditionally applied to all domains
224 filterBySelector.set(selector, filter); 294 filterBySelector.set(selector, filter);
225 unconditionalSelectors = null; 295 unconditionalSelectors = null;
226 defaultStyleSheet = null; 296 defaultStyleSheet = null;
227 } 297 }
228 else 298 else
229 { 299 {
230 // The new filter's selector only applies to some domains 300 // The new filter's selector only applies to some domains
231 addToFiltersByDomain(filter); 301 addToFiltersByDomain(filter);
232 } 302 }
233 303
234 knownFilters.add(filter); 304 knownFilters.add(filter);
235 filterNotifier.emit("elemhideupdate"); 305 filterNotifier.emit("elemhideupdate");
236 }, 306 },
237 307
238 /** 308 /**
239 * Removes an element hiding filter 309 * Removes an element hiding filter
240 * @param {ElemHideFilter} filter 310 * @param {ElemHideFilter} filter
241 */ 311 */
242 remove(filter) 312 remove(filter)
243 { 313 {
244 if (!knownFilters.has(filter)) 314 if (!knownFilters.has(filter))
245 return; 315 return;
246 316
317 commonStyleSheet = null;
318
247 let {selector} = filter; 319 let {selector} = filter;
248 320
249 // Unconditially applied element hiding filters 321 // Unconditially applied element hiding filters
250 if (filterBySelector.get(selector) == filter) 322 if (filterBySelector.get(selector) == filter)
251 { 323 {
252 filterBySelector.delete(selector); 324 filterBySelector.delete(selector);
253 unconditionalSelectors = null; 325 unconditionalSelectors = null;
254 defaultStyleSheet = null; 326 defaultStyleSheet = null;
255 } 327 }
256 // Conditionally applied element hiding filters 328 // Conditionally applied element hiding filters
(...skipping 29 matching lines...) Expand all
286 * 358 *
287 * @param {string} domain The domain. 359 * @param {string} domain The domain.
288 * @param {boolean} [specificOnly=false] Whether selectors from generic 360 * @param {boolean} [specificOnly=false] Whether selectors from generic
289 * filters should be included. 361 * filters should be included.
290 * 362 *
291 * @returns {ElemHideStyleSheet} An object containing the CSS code and the 363 * @returns {ElemHideStyleSheet} An object containing the CSS code and the
292 * list of selectors. 364 * list of selectors.
293 */ 365 */
294 generateStyleSheetForDomain(domain, specificOnly = false) 366 generateStyleSheetForDomain(domain, specificOnly = false)
295 { 367 {
296 let selectors = getConditionalSelectorsForDomain(domain, specificOnly); 368 let knownSuffix = domain ? getKnownSuffix(domain) : "";
369
370 let selectors = getConditionalSelectorsForDomain(knownSuffix, specificOnly);
297 let code = specificOnly ? createStyleSheet(selectors) : 371 let code = specificOnly ? createStyleSheet(selectors) :
298 (getDefaultStyleSheet() + createStyleSheet(selectors)); 372 knownSuffix == "" ? getCommonStyleSheet() :
373 (getDefaultStyleSheet() + createStyleSheet(selectors));
299 374
300 if (!specificOnly) 375 if (!specificOnly)
301 selectors = getUnconditionalSelectors().concat(selectors); 376 selectors = getUnconditionalSelectors().concat(selectors);
302 377
303 return {code, selectors}; 378 return {code, selectors};
304 } 379 }
305 }; 380 };
306 381
307 /** 382 /**
308 * Splits a list of selectors into groups determined by the value of 383 * Splits a list of selectors into groups determined by the value of
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
355 { 430 {
356 let styleSheet = ""; 431 let styleSheet = "";
357 432
358 for (let selectorGroup of splitSelectors(selectors)) 433 for (let selectorGroup of splitSelectors(selectors))
359 styleSheet += createRule(selectorGroup); 434 styleSheet += createRule(selectorGroup);
360 435
361 return styleSheet; 436 return styleSheet;
362 } 437 }
363 438
364 exports.createStyleSheet = createStyleSheet; 439 exports.createStyleSheet = createStyleSheet;
OLDNEW
« no previous file with comments | « no previous file | lib/elemHideExceptions.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld