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