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

Side by Side Diff: lib/elemHide.js

Issue 29886555: Issue 6957 - Add generateStyleSheetForDomain function to ElemHide module (Closed) Base URL: https://hg.adblockplus.org/adblockpluscore/
Patch Set: Rebase, remove getSelectorsForDomain Created Sept. 27, 2018, 3:55 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 | test/elemHide.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 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
49 49
50 /** 50 /**
51 * This array caches the keys of filterBySelector table (selectors 51 * This array caches the keys of filterBySelector table (selectors
52 * which unconditionally apply on all domains). It will be null if the 52 * which unconditionally apply on all domains). It will be null if the
53 * cache needs to be rebuilt. 53 * cache needs to be rebuilt.
54 * @type {?string[]} 54 * @type {?string[]}
55 */ 55 */
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
60 * value of <code>{@link unconditionalSelectors}</code>.
61 * @type {?string}
62 */
63 let defaultStyleSheet = null;
64
65 /**
59 * Map to be used instead when a filter has a blank domains property. 66 * Map to be used instead when a filter has a blank domains property.
60 * @type {Map.<string,boolean>} 67 * @type {Map.<string,boolean>}
61 * @const 68 * @const
62 */ 69 */
63 let defaultDomains = new Map([["", true]]); 70 let defaultDomains = new Map([["", true]]);
64 71
65 /** 72 /**
66 * Set containing known element hiding filters 73 * Set containing known element hiding filters
67 * @type {Set.<ElemHideFilter>} 74 * @type {Set.<ElemHideFilter>}
68 */ 75 */
(...skipping 24 matching lines...) Expand all
93 * @returns {string[]} 100 * @returns {string[]}
94 */ 101 */
95 function getUnconditionalSelectors() 102 function getUnconditionalSelectors()
96 { 103 {
97 if (!unconditionalSelectors) 104 if (!unconditionalSelectors)
98 unconditionalSelectors = [...filterBySelector.keys()]; 105 unconditionalSelectors = [...filterBySelector.keys()];
99 106
100 return unconditionalSelectors; 107 return unconditionalSelectors;
101 } 108 }
102 109
110 /**
111 * 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.
113 *
114 * @param {string} domain The domain.
115 * @param {boolean} [specificOnly=false] Whether selectors from generic filters
116 * should be included.
117 *
118 * @returns {Array.<string>} The list of selectors.
119 */
120 function getConditionalSelectorsForDomain(domain, specificOnly = false)
121 {
122 let selectors = [];
123
124 let excluded = new Set();
125 let currentDomain = domain ? domain.replace(/\.+$/, "").toLowerCase() : "";
126
127 // This code is a performance hot-spot, which is why we've made certain
128 // micro-optimisations. Please be careful before making changes.
129 while (true)
130 {
131 if (specificOnly && currentDomain == "")
132 break;
133
134 let filters = filtersByDomain.get(currentDomain);
135 if (filters)
136 {
137 for (let [filter, isIncluded] of filters)
138 {
139 if (!isIncluded)
140 {
141 excluded.add(filter);
142 }
143 else
144 {
145 let {selector} = filter;
146 if ((excluded.size == 0 || !excluded.has(filter)) &&
147 !ElemHideExceptions.getException(selector, domain))
148 {
149 selectors.push(selector);
150 }
151 }
152 }
153 }
154
155 if (currentDomain == "")
156 break;
157
158 let nextDot = currentDomain.indexOf(".");
159 currentDomain = nextDot == -1 ? "" : currentDomain.substr(nextDot + 1);
160 }
161
162 return selectors;
163 }
164
165 /**
166 * Returns the default style sheet that applies on all domains.
167 * @returns {string}
168 */
169 function getDefaultStyleSheet()
170 {
171 if (!defaultStyleSheet)
172 defaultStyleSheet = createStyleSheet(getUnconditionalSelectors());
173
174 return defaultStyleSheet;
175 }
176
103 ElemHideExceptions.on("added", ({selector}) => 177 ElemHideExceptions.on("added", ({selector}) =>
104 { 178 {
105 // If this is the first exception for a previously unconditionally applied 179 // If this is the first exception for a previously unconditionally applied
106 // element hiding selector we need to take care to update the lookups. 180 // element hiding selector we need to take care to update the lookups.
107 let unconditionalFilterForSelector = filterBySelector.get(selector); 181 let unconditionalFilterForSelector = filterBySelector.get(selector);
108 if (unconditionalFilterForSelector) 182 if (unconditionalFilterForSelector)
109 { 183 {
110 addToFiltersByDomain(unconditionalFilterForSelector); 184 addToFiltersByDomain(unconditionalFilterForSelector);
111 filterBySelector.delete(selector); 185 filterBySelector.delete(selector);
112 unconditionalSelectors = null; 186 unconditionalSelectors = null;
187 defaultStyleSheet = null;
113 } 188 }
114 }); 189 });
115 190
116 /** 191 /**
117 * Container for element hiding filters 192 * Container for element hiding filters
118 * @class 193 * @class
119 */ 194 */
120 exports.ElemHide = { 195 exports.ElemHide = {
121 /** 196 /**
122 * Removes all known filters 197 * Removes all known filters
123 */ 198 */
124 clear() 199 clear()
125 { 200 {
126 for (let collection of [filtersByDomain, filterBySelector, knownFilters]) 201 for (let collection of [filtersByDomain, filterBySelector, knownFilters])
127 collection.clear(); 202 collection.clear();
128 203
129 unconditionalSelectors = null; 204 unconditionalSelectors = null;
205 defaultStyleSheet = null;
206
130 filterNotifier.emit("elemhideupdate"); 207 filterNotifier.emit("elemhideupdate");
131 }, 208 },
132 209
133 /** 210 /**
134 * Add a new element hiding filter 211 * Add a new element hiding filter
135 * @param {ElemHideFilter} filter 212 * @param {ElemHideFilter} filter
136 */ 213 */
137 add(filter) 214 add(filter)
138 { 215 {
139 if (knownFilters.has(filter)) 216 if (knownFilters.has(filter))
140 return; 217 return;
141 218
142 let {selector} = filter; 219 let {selector} = filter;
143 220
144 if (!(filter.domains || ElemHideExceptions.hasExceptions(selector))) 221 if (!(filter.domains || ElemHideExceptions.hasExceptions(selector)))
145 { 222 {
146 // The new filter's selector is unconditionally applied to all domains 223 // The new filter's selector is unconditionally applied to all domains
147 filterBySelector.set(selector, filter); 224 filterBySelector.set(selector, filter);
148 unconditionalSelectors = null; 225 unconditionalSelectors = null;
226 defaultStyleSheet = null;
149 } 227 }
150 else 228 else
151 { 229 {
152 // The new filter's selector only applies to some domains 230 // The new filter's selector only applies to some domains
153 addToFiltersByDomain(filter); 231 addToFiltersByDomain(filter);
154 } 232 }
155 233
156 knownFilters.add(filter); 234 knownFilters.add(filter);
157 filterNotifier.emit("elemhideupdate"); 235 filterNotifier.emit("elemhideupdate");
158 }, 236 },
159 237
160 /** 238 /**
161 * Removes an element hiding filter 239 * Removes an element hiding filter
162 * @param {ElemHideFilter} filter 240 * @param {ElemHideFilter} filter
163 */ 241 */
164 remove(filter) 242 remove(filter)
165 { 243 {
166 if (!knownFilters.has(filter)) 244 if (!knownFilters.has(filter))
167 return; 245 return;
168 246
169 let {selector} = filter; 247 let {selector} = filter;
170 248
171 // Unconditially applied element hiding filters 249 // Unconditially applied element hiding filters
172 if (filterBySelector.get(selector) == filter) 250 if (filterBySelector.get(selector) == filter)
173 { 251 {
174 filterBySelector.delete(selector); 252 filterBySelector.delete(selector);
175 unconditionalSelectors = null; 253 unconditionalSelectors = null;
254 defaultStyleSheet = null;
176 } 255 }
177 // Conditionally applied element hiding filters 256 // Conditionally applied element hiding filters
178 else 257 else
179 { 258 {
180 let domains = filter.domains || defaultDomains; 259 let domains = filter.domains || defaultDomains;
181 for (let domain of domains.keys()) 260 for (let domain of domains.keys())
182 { 261 {
183 let filters = filtersByDomain.get(domain); 262 let filters = filtersByDomain.get(domain);
184 if (filters) 263 if (filters)
185 { 264 {
186 filters.delete(filter); 265 filters.delete(filter);
187 266
188 if (filters.size == 0) 267 if (filters.size == 0)
189 filtersByDomain.delete(domain); 268 filtersByDomain.delete(domain);
190 } 269 }
191 } 270 }
192 } 271 }
193 272
194 knownFilters.delete(filter); 273 knownFilters.delete(filter);
195 filterNotifier.emit("elemhideupdate"); 274 filterNotifier.emit("elemhideupdate");
196 }, 275 },
197 276
198 /** 277 /**
199 * Determines from the current filter list which selectors should be applied 278 * @typedef {object} ElemHideStyleSheet
200 * on a particular host name. 279 * @property {string} code CSS code.
201 * @param {string} domain 280 * @property {Array.<string>} selectors List of selectors.
202 * @param {boolean} [specificOnly] true if generic filters should not apply.
203 * @returns {string[]} List of selectors.
204 */ 281 */
205 getSelectorsForDomain(domain, specificOnly = false) 282
283 /**
284 * Generates a style sheet for a given domain based on the current set of
285 * filters.
286 *
287 * @param {string} domain The domain.
288 * @param {boolean} [specificOnly=false] Whether selectors from generic
289 * filters should be included.
290 *
291 * @returns {ElemHideStyleSheet} An object containing the CSS code and the
292 * list of selectors.
293 */
294 generateStyleSheetForDomain(domain, specificOnly = false)
206 { 295 {
207 let selectors = []; 296 let selectors = getConditionalSelectorsForDomain(domain, specificOnly);
208 297 let code = specificOnly ? createStyleSheet(selectors) :
209 let excluded = new Set(); 298 (getDefaultStyleSheet() + createStyleSheet(selectors));
210 let currentDomain = domain ? domain.replace(/\.+$/, "").toLowerCase() : "";
211
212 // This code is a performance hot-spot, which is why we've made certain
213 // micro-optimisations. Please be careful before making changes.
214 while (true)
215 {
216 if (specificOnly && currentDomain == "")
217 break;
218
219 let filters = filtersByDomain.get(currentDomain);
220 if (filters)
221 {
222 for (let [filter, isIncluded] of filters)
223 {
224 if (!isIncluded)
225 {
226 excluded.add(filter);
227 }
228 else
229 {
230 let {selector} = filter;
231 if ((excluded.size == 0 || !excluded.has(filter)) &&
232 !ElemHideExceptions.getException(selector, domain))
233 {
234 selectors.push(selector);
235 }
236 }
237 }
238 }
239
240 if (currentDomain == "")
241 break;
242
243 let nextDot = currentDomain.indexOf(".");
244 currentDomain = nextDot == -1 ? "" : currentDomain.substr(nextDot + 1);
245 }
246 299
247 if (!specificOnly) 300 if (!specificOnly)
248 selectors = getUnconditionalSelectors().concat(selectors); 301 selectors = getUnconditionalSelectors().concat(selectors);
249 302
250 return selectors; 303 return {code, selectors};
251 } 304 }
252 }; 305 };
253 306
254 /** 307 /**
255 * Splits a list of selectors into groups determined by the value of 308 * Splits a list of selectors into groups determined by the value of
256 * <code>{@link selectorGroupSize}</code>. 309 * <code>{@link selectorGroupSize}</code>.
257 * 310 *
258 * @param {Array.<string>} selectors 311 * @param {Array.<string>} selectors
259 * @yields {Array.<string>} 312 * @yields {Array.<string>}
260 */ 313 */
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
302 { 355 {
303 let styleSheet = ""; 356 let styleSheet = "";
304 357
305 for (let selectorGroup of splitSelectors(selectors)) 358 for (let selectorGroup of splitSelectors(selectors))
306 styleSheet += createRule(selectorGroup); 359 styleSheet += createRule(selectorGroup);
307 360
308 return styleSheet; 361 return styleSheet;
309 } 362 }
310 363
311 exports.createStyleSheet = createStyleSheet; 364 exports.createStyleSheet = createStyleSheet;
OLDNEW
« no previous file with comments | « no previous file | test/elemHide.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld