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

Delta Between Two Patch Sets: chrome/content/elemHideEmulation.js

Issue 29448560: Issue 5249 - Implement :-abp-contains() (Closed) Base URL: https://hg.adblockplus.org/adblockpluscore/
Left Patch Set: Created May 25, 2017, 1:03 p.m.
Right Patch Set: Use includes() and improve test. Created June 28, 2017, 4:11 p.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/browser/elemHideEmulation.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-2017 eyeo GmbH 3 * Copyright (C) 2006-2017 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
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details. 12 * GNU General Public License for more details.
13 * 13 *
14 * You should have received a copy of the GNU General Public License 14 * You should have received a copy of the GNU General Public License
15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. 15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
16 */ 16 */
17 17
18 /* globals filterToRegExp */ 18 /* globals filterToRegExp */
19 19
20 "use strict"; 20 "use strict";
21 21
22 const abpSelectorRegexp = /:-abp-([\w-]+)\(/i;
23
24 let reportError = () => {};
25
22 function splitSelector(selector) 26 function splitSelector(selector)
23 { 27 {
24 if (selector.indexOf(",") == -1) 28 if (selector.indexOf(",") == -1)
25 return [selector]; 29 return [selector];
26 30
27 let selectors = []; 31 let selectors = [];
28 let start = 0; 32 let start = 0;
29 let level = 0; 33 let level = 0;
30 let sep = ""; 34 let sep = "";
31 35
(...skipping 19 matching lines...) Expand all
51 start = i + 1; 55 start = i + 1;
52 } 56 }
53 } 57 }
54 } 58 }
55 59
56 selectors.push(selector.substring(start)); 60 selectors.push(selector.substring(start));
57 return selectors; 61 return selectors;
58 } 62 }
59 63
60 /** Return position of node from parent. 64 /** Return position of node from parent.
61 * @param {Node} node - the node to find the position of. 65 * @param {Node} node the node to find the position of.
62 * @return {number} 1 base index like for :nth-child(), or 0 on error. 66 * @return {number} One-based index like for :nth-child(), or 0 on error.
63 */ 67 */
64 function positionInParent(node) 68 function positionInParent(node)
65 { 69 {
66 if (!node)
67 return 0;
68 let {children} = node.parentNode; 70 let {children} = node.parentNode;
69 for (let i = 0; i < children.length; i++) 71 for (let i = 0; i < children.length; i++)
70 if (children[i] == node) 72 if (children[i] == node)
71 return i + 1; 73 return i + 1;
72 return 0; 74 return 0;
73 } 75 }
74 76
75 function makeSelector(node, selector) 77 function makeSelector(node, selector)
76 { 78 {
77 if (!node.parentElement) 79 if (!node.parentElement)
78 { 80 {
79 let newSelector = ":root"; 81 let newSelector = ":root";
80 if (selector) 82 if (selector)
81 newSelector += " > "; 83 newSelector += " > " + selector;
82 return newSelector + selector; 84 return newSelector;
83 } 85 }
84 let idx = positionInParent(node); 86 let idx = positionInParent(node);
85 if (idx > 0) 87 if (idx > 0)
86 { 88 {
87 let newSelector = `${node.tagName}:nth-child(${idx})`; 89 let newSelector = `${node.tagName}:nth-child(${idx})`;
88 if (selector) 90 if (selector)
89 newSelector += " > "; 91 newSelector += " > " + selector;
90 return makeSelector(node.parentElement, newSelector + selector); 92 return makeSelector(node.parentElement, newSelector);
91 } 93 }
92 94
93 return selector; 95 return selector;
94 } 96 }
95 97
96 const abpSelectorRegexp = /:-abp-(properties|has|contains|[A-Za-z\d-]*)\(/i; 98 function parseSelectorContent(content, startIndex)
97
98 function parseSelectorContent(content, quoted = false)
99 { 99 {
100 let parens = 1; 100 let parens = 1;
101 let i = 0;
102 let quote = null; 101 let quote = null;
103 let originalLength = content.length; 102 let i = startIndex;
104 if (quoted) 103 for (; i < content.length; i++)
105 content = content.trim();
106 while (i < content.length)
107 { 104 {
108 let c = content[i]; 105 let c = content[i];
109 if (quoted && i == 0)
110 {
111 if (c != "'" && c != '"')
112 return null;
113 }
114 if (c == "\\") 106 if (c == "\\")
107 {
108 // Ignore escaped characters
115 i++; 109 i++;
110 }
116 else if (quote) 111 else if (quote)
117 { 112 {
118 if (c == quote) 113 if (c == quote)
119 quote = null; 114 quote = null;
120 } 115 }
121 else if (c == "'" || c == '"') 116 else if (c == "'" || c == '"')
122 {
123 quote = c; 117 quote = c;
124 }
125 else if (c == "(") 118 else if (c == "(")
126 parens++; 119 parens++;
127 else if (c == ")") 120 else if (c == ")")
128 { 121 {
129 parens--; 122 parens--;
130 if (parens == 0) 123 if (parens == 0)
131 break; 124 break;
132 } 125 }
133 i++; 126 }
134 } 127
135 if (parens > 0) 128 if (parens > 0)
136 return null; 129 return null;
137 if (quoted) 130 return {text: content.substring(startIndex, i), end: i};
138 { 131 }
139 let end = content.substr(0, i).lastIndexOf(content[0]); 132
140 return {text: content.substr(1, end - 1), 133 /** Stringified style objects
141 end: i + (originalLength - content.length)}; 134 * @typedef {Object} StringifiedStyle
142 } 135 * @property {string} style CSS style represented by a string.
143 return {text: content.substr(0, i), end: i}; 136 * @property {string[]} subSelectors selectors the CSS properties apply to.
144 } 137 */
145 138
146 function parseSelector(selector, level = 0) 139 /**
147 { 140 * Produce a string representation of the stylesheet entry.
148 if (selector.length == 0) 141 * @param {CSSStyleRule} rule the CSS style rule.
149 return []; 142 * @return {StringifiedStyle} the stringified style.
150 143 */
151 let match = abpSelectorRegexp.exec(selector); 144 function stringifyStyle(rule)
152 if (!match)
153 return [new PlainSelector(selector)];
154
155 let selectors = [];
156 let suffixStart = match.index;
157 if (suffixStart > 0)
158 selectors.push(new PlainSelector(selector.substr(0, suffixStart)));
159
160 let startIndex = match.index + match[0].length;
161 let content = null;
162 if (match[1] == "properties")
163 {
164 content = parseSelectorContent(selector.substr(startIndex), true);
165 if (content == null)
166 {
167 console.error(new SyntaxError("Failed to parse AdBlock Plus " +
168 `selector ${selector}, invalid ` +
169 "properties string."));
170 return null;
171 }
172
173 selectors.push(new PropsSelector(content.text));
174 }
175 else if (match[1] == "has")
176 {
177 if (level > 0)
178 {
179 console.error(new SyntaxError("Failed to parse AdBlock Plus " +
180 `selector ${selector}, invalid ` +
181 "nested :-abp-has()."));
182 return null;
183 }
184
185 content = parseSelectorContent(selector.substr(startIndex));
186 if (content == null)
187 {
188 console.error(new SyntaxError("Failed parsing AdBlock Plus " +
189 `selector ${selector}, didn't ` +
190 "find closing parenthesis."));
191 return null;
192 }
193
194 let hasSelector = new HasSelector(content.text);
195 if (!hasSelector.valid())
196 return null;
197 selectors.push(hasSelector);
198 }
199 else if (match[1] == "contains")
200 {
201 content = parseSelectorContent(selector.substr(startIndex), true);
202
203 selectors.push(new ContainsSelector(content.text));
204 }
205 else
206 {
207 // this is an error, can't parse selector.
208 console.error(new SyntaxError("Failed parsing AdBlock Plus " +
209 `selector ${selector}, invalid ` +
210 `pseudo-class -abp-${match[1]}.`));
211 return null;
212 }
213
214 suffixStart = startIndex + content.end + 1;
215
216 let suffix = parseSelector(selector.substr(suffixStart), level);
217 if (suffix == null)
218 return null;
219
220 selectors.push(...suffix);
221
222 return selectors;
223 }
224
225 function *findPropsSelectors(styles, prefix, regexp)
226 {
227 for (let style of styles)
228 if (regexp.test(style.style))
229 for (let subSelector of style.subSelectors)
230 yield prefix + subSelector;
231 }
232
233 function stringifyStyle(style)
234 { 145 {
235 let styles = []; 146 let styles = [];
236 for (let i = 0; i < style.length; i++) 147 for (let i = 0; i < rule.style.length; i++)
237 { 148 {
238 let property = style.item(i); 149 let property = rule.style.item(i);
239 let value = style.getPropertyValue(property); 150 let value = rule.style.getPropertyValue(property);
240 let priority = style.getPropertyPriority(property); 151 let priority = rule.style.getPropertyPriority(property);
241 styles.push(property + ": " + value + (priority ? " !" + priority : "") + 152 styles.push(`${property}: ${value}${priority ? " !" + priority : ""};`);
242 ";");
243 } 153 }
244 styles.sort(); 154 styles.sort();
245 return styles.join(" "); 155 return {
156 style: styles.join(" "),
157 subSelectors: splitSelector(rule.selectorText)
158 };
246 } 159 }
247 160
248 function* evaluate(chain, index, prefix, subtree, styles) 161 function* evaluate(chain, index, prefix, subtree, styles)
249 { 162 {
250 if (index >= chain.length) 163 if (index >= chain.length)
251 { 164 {
252 yield prefix; 165 yield prefix;
253 return; 166 return;
254 } 167 }
255 for (let [selector, element] of 168 for (let [selector, element] of
256 chain[index].getSelectors(prefix, subtree, styles)) 169 chain[index].getSelectors(prefix, subtree, styles))
257 yield* evaluate(chain, index + 1, selector, element, styles); 170 yield* evaluate(chain, index + 1, selector, element, styles);
258 } 171 }
259 172
260 function PlainSelector(selector) 173 function PlainSelector(selector)
261 { 174 {
262 this._selector = selector; 175 this._selector = selector;
263 } 176 }
264 177
265 PlainSelector.prototype = { 178 PlainSelector.prototype = {
266 /** 179 /**
267 * Generator function returning a pair of selector 180 * Generator function returning a pair of selector
268 * string and subtree. 181 * string and subtree.
269 * @param {String} prefix - the prefix for the selector. 182 * @param {string} prefix the prefix for the selector.
270 * @param {Node} subtree - the subtree we work on. 183 * @param {Node} subtree the subtree we work on.
271 * @param {Array} styles - the stringified stylesheets. 184 * @param {StringifiedStyle[]} styles the stringified style objects.
272 */ 185 */
273 *getSelectors(prefix, subtree, styles) 186 *getSelectors(prefix, subtree, styles)
274 { 187 {
275 yield [prefix + this._selector, subtree]; 188 yield [prefix + this._selector, subtree];
189 }
190 };
191
192 const incompletePrefixRegexp = /[\s>+~]$/;
193
194 function HasSelector(selectors)
195 {
196 this._innerSelectors = selectors;
197 }
198
199 HasSelector.prototype = {
200 requiresHiding: true,
201
202 *getSelectors(prefix, subtree, styles)
203 {
204 for (let element of this.getElements(prefix, subtree, styles))
205 yield [makeSelector(element, ""), element];
276 }, 206 },
277 207
278 /** 208 /**
279 * Generator function returning selected elements. 209 * Generator function returning selected elements.
280 * @param {String} prefix - the prefix for the selector. 210 * @param {string} prefix the prefix for the selector.
281 * @param {Node} subtree - the subtree we work on. 211 * @param {Node} subtree the subtree we work on.
282 * @param {Array} styles - the stringified stylesheets. 212 * @param {StringifiedStyle[]} styles the stringified style objects.
283 */ 213 */
284 *getElements(prefix, subtree, styles) 214 *getElements(prefix, subtree, styles)
285 { 215 {
286 for (let [selector] of this.getSelectors(prefix, subtree, styles)) 216 let actualPrefix = (!prefix || incompletePrefixRegexp.test(prefix)) ?
287 for (let element of subtree.querySelectorAll(selector))
288 yield element;
289 }
290 };
291
292 const prefixEndRegexp = /[\s>+~]$/;
293
294 function HasSelector(selector, level = 0)
295 {
296 this._innerSelectors = parseSelector(selector, level + 1);
297 }
298
299 HasSelector.prototype = {
300 valid()
301 {
302 return this._innerSelectors != null;
303 },
304
305 *getSelectors(prefix, subtree, styles)
306 {
307 for (let element of this.getElements(prefix, subtree, styles))
308 yield [makeSelector(element, ""), element];
309 },
310
311 *getElements(prefix, subtree, styles)
312 {
313 let actualPrefix = (!prefix || prefixEndRegexp.test(prefix)) ?
314 prefix + "*" : prefix; 217 prefix + "*" : prefix;
315 let elements = subtree.querySelectorAll(actualPrefix); 218 let elements = subtree.querySelectorAll(actualPrefix);
316 for (let element of elements) 219 for (let element of elements)
317 { 220 {
318 let newPrefix = makeSelector(element, ""); 221 let newPrefix = makeSelector(element, "");
319 let iter = evaluate(this._innerSelectors, 0, "", element, styles); 222 let iter = evaluate(this._innerSelectors, 0, newPrefix + " ",
223 element, styles);
320 for (let selector of iter) 224 for (let selector of iter)
321 // we insert a space between the two. It becomes a no-op if selector 225 // we insert a space between the two. It becomes a no-op if selector
322 // doesn't have a combinator 226 // doesn't have a combinator
323 if (subtree.querySelector(newPrefix + " " + selector)) 227 if (subtree.querySelector(selector))
324 yield element; 228 yield element;
325 } 229 }
326 } 230 }
327 }; 231 };
328 232
329 function ContainsSelector(textContent) 233 function ContainsSelector(textContent)
330 { 234 {
331 this._text = textContent; 235 this._text = textContent;
332 } 236 }
333 237
334 ContainsSelector.prototype = { 238 ContainsSelector.prototype = {
239 requiresHiding: true,
335 240
336 *getSelectors(prefix, subtree, stylesheet) 241 *getSelectors(prefix, subtree, stylesheet)
337 { 242 {
338 for (let element of this.getElements(prefix, subtree, stylesheet)) 243 for (let element of this.getElements(prefix, subtree, stylesheet))
339 yield [makeSelector(element, ""), subtree]; 244 yield [makeSelector(element, ""), subtree];
340 }, 245 },
341 246
342 *getElements(prefix, subtree, stylesheet) 247 *getElements(prefix, subtree, stylesheet)
343 { 248 {
344 let actualPrefix = (!prefix || prefixEndRegexp.test(prefix)) ? 249 let actualPrefix = (!prefix || incompletePrefixRegexp.test(prefix)) ?
345 prefix + "*" : prefix; 250 prefix + "*" : prefix;
346 let elements = subtree.querySelectorAll(actualPrefix); 251 let elements = subtree.querySelectorAll(actualPrefix);
347 for (let element of elements) 252 for (let element of elements)
348 if (element.textContent == this._text) 253 if (element.textContent.includes(this._text))
349 yield element; 254 yield element;
350 } 255 }
351 }; 256 };
352 257
353 function PropsSelector(propertyExpression) 258 function PropsSelector(propertyExpression)
354 { 259 {
355 let regexpString; 260 let regexpString;
356 if (propertyExpression.length >= 2 && propertyExpression[0] == "/" && 261 if (propertyExpression.length >= 2 && propertyExpression[0] == "/" &&
357 propertyExpression[propertyExpression.length - 1] == "/") 262 propertyExpression[propertyExpression.length - 1] == "/")
358 { 263 {
359 regexpString = propertyExpression.slice(1, -1) 264 regexpString = propertyExpression.slice(1, -1)
360 .replace("\\x7B ", "{").replace("\\x7D ", "}"); 265 .replace("\\x7B ", "{").replace("\\x7D ", "}");
361 } 266 }
362 else 267 else
363 regexpString = filterToRegExp(propertyExpression); 268 regexpString = filterToRegExp(propertyExpression);
364 269
365 this._regexp = new RegExp(regexpString, "i"); 270 this._regexp = new RegExp(regexpString, "i");
366 } 271 }
367 272
368 PropsSelector.prototype = { 273 PropsSelector.prototype = {
274 preferHideWithSelector: true,
275
276 *findPropsSelectors(styles, prefix, regexp)
277 {
278 for (let style of styles)
279 if (regexp.test(style.style))
280 for (let subSelector of style.subSelectors)
281 yield prefix + subSelector;
282 },
283
369 *getSelectors(prefix, subtree, styles) 284 *getSelectors(prefix, subtree, styles)
370 { 285 {
371 for (let selector of findPropsSelectors(styles, prefix, this._regexp)) 286 for (let selector of this.findPropsSelectors(styles, prefix, this._regexp))
372 yield [selector, subtree]; 287 yield [selector, subtree];
373 },
374
375 *getElements(prefix, subtree, styles)
376 {
377 for (let [selector] of this.getSelectors(prefix, subtree, styles))
378 for (let subElement of subtree.querySelectorAll(selector))
379 yield subElement;
380 } 288 }
381 }; 289 };
382 290
383 function ElemHideEmulation(window, getFiltersFunc, addSelectorsFunc) 291 function ElemHideEmulation(window, getFiltersFunc, addSelectorsFunc,
292 hideElemsFunc)
384 { 293 {
385 this.window = window; 294 this.window = window;
386 this.getFiltersFunc = getFiltersFunc; 295 this.getFiltersFunc = getFiltersFunc;
387 this.addSelectorsFunc = addSelectorsFunc; 296 this.addSelectorsFunc = addSelectorsFunc;
297 this.hideElemsFunc = hideElemsFunc;
388 } 298 }
389 299
390 ElemHideEmulation.prototype = { 300 ElemHideEmulation.prototype = {
391 isSameOrigin(stylesheet) 301 isSameOrigin(stylesheet)
392 { 302 {
393 try 303 try
394 { 304 {
395 return new URL(stylesheet.href).origin == this.window.location.origin; 305 return new URL(stylesheet.href).origin == this.window.location.origin;
396 } 306 }
397 catch (e) 307 catch (e)
398 { 308 {
399 // Invalid URL, assume that it is first-party. 309 // Invalid URL, assume that it is first-party.
400 return true; 310 return true;
401 } 311 }
402 }, 312 },
403 313
314 /** Parse the selector
315 * @param {string} selector the selector to parse
316 * @return {Array} selectors is an array of objects,
317 * or null in case of errors.
318 */
319 parseSelector(selector)
320 {
321 if (selector.length == 0)
322 return [];
323
324 let match = abpSelectorRegexp.exec(selector);
325 if (!match)
326 return [new PlainSelector(selector)];
327
328 let selectors = [];
329 if (match.index > 0)
330 selectors.push(new PlainSelector(selector.substr(0, match.index)));
331
332 let startIndex = match.index + match[0].length;
333 let content = parseSelectorContent(selector, startIndex);
334 if (!content)
335 {
336 this.window.console.error(
337 new SyntaxError("Failed to parse Adblock Plus " +
338 `selector ${selector} ` +
339 "due to unmatched parentheses."));
340 return null;
341 }
342 if (match[1] == "properties")
343 selectors.push(new PropsSelector(content.text));
344 else if (match[1] == "has")
345 {
346 let hasSelectors = this.parseSelector(content.text);
347 if (hasSelectors == null)
348 return null;
349 selectors.push(new HasSelector(hasSelectors));
350 }
351 else if (match[1] == "contains")
352 selectors.push(new ContainsSelector(content.text));
353 else
354 {
355 // this is an error, can't parse selector.
356 this.window.console.error(
357 new SyntaxError("Failed to parse Adblock Plus " +
358 `selector ${selector}, invalid ` +
359 `pseudo-class :-abp-${match[1]}().`));
360 return null;
361 }
362
363 let suffix = this.parseSelector(selector.substr(content.end + 1));
364 if (suffix == null)
365 return null;
366
367 selectors.push(...suffix);
368
369 if (selectors.length == 1 && selectors[0] instanceof ContainsSelector)
370 {
371 this.window.console.error(
372 new SyntaxError("Failed to parse Adblock Plus " +
373 `selector ${selector}, can't ` +
374 "have a lonely :-abp-contains()."));
375 return null;
376 }
377 return selectors;
378 },
379
404 addSelectors(stylesheets) 380 addSelectors(stylesheets)
405 { 381 {
406 let selectors = []; 382 let selectors = [];
407 let filters = []; 383 let selectorFilters = [];
384
385 let elements = [];
386 let elementFilters = [];
408 387
409 let cssStyles = []; 388 let cssStyles = [];
410 389
411 for (let stylesheet of stylesheets) 390 for (let stylesheet of stylesheets)
412 { 391 {
413 // Explicitly ignore third-party stylesheets to ensure consistent behavior 392 // Explicitly ignore third-party stylesheets to ensure consistent behavior
414 // between Firefox and Chrome. 393 // between Firefox and Chrome.
415 if (!this.isSameOrigin(stylesheet)) 394 if (!this.isSameOrigin(stylesheet))
416 continue; 395 continue;
417 396
418 let rules = stylesheet.cssRules; 397 let rules = stylesheet.cssRules;
419 if (!rules) 398 if (!rules)
420 continue; 399 continue;
421 400
422 for (let rule of rules) 401 for (let rule of rules)
423 { 402 {
424 if (rule.type != rule.STYLE_RULE) 403 if (rule.type != rule.STYLE_RULE)
425 continue; 404 continue;
426 405
427 let style = stringifyStyle(rule.style); 406 cssStyles.push(stringifyStyle(rule));
428 let subSelectors = splitSelector(rule.selectorText);
429 cssStyles.push({style, subSelectors});
430 } 407 }
431 } 408 }
432 409
433 for (let patterns of this.selPatterns) 410 let {document} = this.window;
434 for (let selector of evaluate(patterns.selectors, 411 for (let pattern of this.patterns)
412 {
413 for (let selector of evaluate(pattern.selectors,
435 0, "", document, cssStyles)) 414 0, "", document, cssStyles))
436 { 415 {
437 selectors.push(selector); 416 if (pattern.selectors.some(s => s.preferHideWithSelector) &&
438 filters.push(patterns.text); 417 !pattern.selectors.some(s => s.requiresHiding))
418 {
419 selectors.push(selector);
420 selectorFilters.push(pattern.text);
421 }
422 else
423 {
424 for (let element of document.querySelectorAll(selector))
425 {
426 elements.push(element);
427 elementFilters.push(pattern.text);
428 }
429 }
439 } 430 }
440 431 }
441 this.addSelectorsFunc(selectors, filters); 432
433 this.addSelectorsFunc(selectors, selectorFilters);
434 this.hideElemsFunc(elements, elementFilters);
442 }, 435 },
443 436
444 onLoad(event) 437 onLoad(event)
445 { 438 {
446 let stylesheet = event.target.sheet; 439 let stylesheet = event.target.sheet;
447 if (stylesheet) 440 if (stylesheet)
448 this.addSelectors([stylesheet]); 441 this.addSelectors([stylesheet]);
449 }, 442 },
450 443
451 apply() 444 apply()
452 { 445 {
453 this.getFiltersFunc(patterns => 446 this.getFiltersFunc(patterns =>
454 { 447 {
455 this.selPatterns = []; 448 this.patterns = [];
456
457 for (let pattern of patterns) 449 for (let pattern of patterns)
458 { 450 {
459 let selectors = parseSelector(pattern.selector); 451 let selectors = this.parseSelector(pattern.selector);
460 if (selectors != null && selectors.length > 0) 452 if (selectors != null && selectors.length > 0)
461 this.selPatterns.push({selectors, text: pattern.text}); 453 this.patterns.push({selectors, text: pattern.text});
462 } 454 }
463 455
464 if (this.selPatterns.length > 0) 456 if (this.patterns.length > 0)
465 { 457 {
466 let {document} = this.window; 458 let {document} = this.window;
467 this.addSelectors(document.styleSheets); 459 this.addSelectors(document.styleSheets);
468 document.addEventListener("load", this.onLoad.bind(this), true); 460 document.addEventListener("load", this.onLoad.bind(this), true);
469 } 461 }
470 }); 462 });
471 } 463 }
472 }; 464 };
LEFTRIGHT

Powered by Google App Engine
This is Rietveld