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

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

Issue 29585589: Issue 6423 - Observe only relevant mutation types (Closed) Base URL: https://hg.adblockplus.org/adblockpluscore/
Left Patch Set: Account for nested selectors Created Oct. 23, 2017, 12:10 a.m.
Right Patch Set: Add brackets Created Feb. 22, 2018, 7:13 a.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 | no next file » | 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-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
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 "use strict"; 18 "use strict";
19 19
20 const {filterToRegExp, splitSelector} = require("common"); 20 const {filterToRegExp, splitSelector} = require("../common");
21 21
22 let MIN_INVOCATION_INTERVAL = 3000; 22 let MIN_INVOCATION_INTERVAL = 3000;
23 const MAX_SYNCHRONOUS_PROCESSING_TIME = 50; 23 const MAX_SYNCHRONOUS_PROCESSING_TIME = 50;
24 const abpSelectorRegexp = /:-abp-([\w-]+)\(/i; 24 const abpSelectorRegexp = /:-abp-([\w-]+)\(/i;
25 25
26 /** Return position of node from parent. 26 /** Return position of node from parent.
27 * @param {Node} node the node to find the position of. 27 * @param {Node} node the node to find the position of.
28 * @return {number} One-based index like for :nth-child(), or 0 on error. 28 * @return {number} One-based index like for :nth-child(), or 0 on error.
29 */ 29 */
30 function positionInParent(node) 30 function positionInParent(node)
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after
115 let priority = rule.style.getPropertyPriority(property); 115 let priority = rule.style.getPropertyPriority(property);
116 styles.push(`${property}: ${value}${priority ? " !" + priority : ""};`); 116 styles.push(`${property}: ${value}${priority ? " !" + priority : ""};`);
117 } 117 }
118 styles.sort(); 118 styles.sort();
119 return { 119 return {
120 style: styles.join(" "), 120 style: styles.join(" "),
121 subSelectors: splitSelector(rule.selectorText) 121 subSelectors: splitSelector(rule.selectorText)
122 }; 122 };
123 } 123 }
124 124
125 let scopeSupported = null;
126
127 function tryQuerySelector(subtree, selector, all)
128 {
129 let elements = null;
130 try
131 {
132 elements = all ? subtree.querySelectorAll(selector) :
133 subtree.querySelector(selector);
134 scopeSupported = true;
135 }
136 catch (e)
137 {
138 // Edge doesn't support ":scope"
139 scopeSupported = false;
140 }
141 return elements;
142 }
143
144 /**
145 * Query selector. If it is relative, will try :scope.
146 * @param {Node} subtree the element to query selector
147 * @param {string} selector the selector to query
148 * @param {bool} [all=false] true to perform querySelectorAll()
149 * @returns {?(Node|NodeList)} result of the query. null in case of error.
150 */
151 function scopedQuerySelector(subtree, selector, all)
152 {
153 if (selector[0] == ">")
154 {
155 selector = ":scope" + selector;
156 if (scopeSupported)
157 {
158 return all ? subtree.querySelectorAll(selector) :
159 subtree.querySelector(selector);
160 }
161 if (scopeSupported == null)
162 return tryQuerySelector(subtree, selector, all);
163 return null;
164 }
165 return all ? subtree.querySelectorAll(selector) :
166 subtree.querySelector(selector);
167 }
168
169 function scopedQuerySelectorAll(subtree, selector)
170 {
171 return scopedQuerySelector(subtree, selector, true);
172 }
173
125 function* evaluate(chain, index, prefix, subtree, styles) 174 function* evaluate(chain, index, prefix, subtree, styles)
126 { 175 {
127 if (index >= chain.length) 176 if (index >= chain.length)
128 { 177 {
129 yield prefix; 178 yield prefix;
130 return; 179 return;
131 } 180 }
132 for (let [selector, element] of 181 for (let [selector, element] of
133 chain[index].getSelectors(prefix, subtree, styles)) 182 chain[index].getSelectors(prefix, subtree, styles))
134 { 183 {
135 if (selector == null) 184 if (selector == null)
136 yield null; 185 yield null;
137 else 186 else
138 yield* evaluate(chain, index + 1, selector, element, styles); 187 yield* evaluate(chain, index + 1, selector, element, styles);
139 } 188 }
140 // Just in case the getSelectors() generator above had to run some heavy 189 // Just in case the getSelectors() generator above had to run some heavy
141 // document.querySelectorAll() call which didn't produce any results, make 190 // document.querySelectorAll() call which didn't produce any results, make
142 // sure there is at least one point where execution can pause. 191 // sure there is at least one point where execution can pause.
143 yield null; 192 yield null;
144 } 193 }
145 194
146 function PlainSelector(selector) 195 function PlainSelector(selector)
147 { 196 {
148 this._selector = selector; 197 this._selector = selector;
149 this.maybeDependsOnAttributes = /\[.+\]/.test(selector); 198 this.maybeDependsOnAttributes = /[#.]|\[.+\]/.test(selector);
hub 2017/10/23 13:18:55 What about the changes of `class` and `id` ?
Manish Jethani 2017/10/23 22:52:07 Done.
150 } 199 }
151 200
152 PlainSelector.prototype = { 201 PlainSelector.prototype = {
153 /** 202 /**
154 * Generator function returning a pair of selector 203 * Generator function returning a pair of selector
155 * string and subtree. 204 * string and subtree.
156 * @param {string} prefix the prefix for the selector. 205 * @param {string} prefix the prefix for the selector.
157 * @param {Node} subtree the subtree we work on. 206 * @param {Node} subtree the subtree we work on.
158 * @param {StringifiedStyle[]} styles the stringified style objects. 207 * @param {StringifiedStyle[]} styles the stringified style objects.
159 */ 208 */
160 *getSelectors(prefix, subtree, styles) 209 *getSelectors(prefix, subtree, styles)
161 { 210 {
162 yield [prefix + this._selector, subtree]; 211 yield [prefix + this._selector, subtree];
163 } 212 }
164 }; 213 };
165 214
166 const incompletePrefixRegexp = /[\s>+~]$/; 215 const incompletePrefixRegexp = /[\s>+~]$/;
167 const relativeSelectorRegexp = /^[>+~]/;
168 216
169 function HasSelector(selectors) 217 function HasSelector(selectors)
170 { 218 {
171 this._innerSelectors = selectors; 219 this._innerSelectors = selectors;
172 } 220 }
173 221
174 HasSelector.prototype = { 222 HasSelector.prototype = {
175 requiresHiding: true, 223 requiresHiding: true,
176 224
177 get dependsOnStyles() 225 get dependsOnStyles()
(...skipping 24 matching lines...) Expand all
202 /** 250 /**
203 * Generator function returning selected elements. 251 * Generator function returning selected elements.
204 * @param {string} prefix the prefix for the selector. 252 * @param {string} prefix the prefix for the selector.
205 * @param {Node} subtree the subtree we work on. 253 * @param {Node} subtree the subtree we work on.
206 * @param {StringifiedStyle[]} styles the stringified style objects. 254 * @param {StringifiedStyle[]} styles the stringified style objects.
207 */ 255 */
208 *getElements(prefix, subtree, styles) 256 *getElements(prefix, subtree, styles)
209 { 257 {
210 let actualPrefix = (!prefix || incompletePrefixRegexp.test(prefix)) ? 258 let actualPrefix = (!prefix || incompletePrefixRegexp.test(prefix)) ?
211 prefix + "*" : prefix; 259 prefix + "*" : prefix;
212 let elements = subtree.querySelectorAll(actualPrefix); 260 let elements = scopedQuerySelectorAll(subtree, actualPrefix);
213 for (let element of elements) 261 if (elements)
214 { 262 {
215 let iter = evaluate(this._innerSelectors, 0, "", element, styles); 263 for (let element of elements)
216 for (let selector of iter) 264 {
217 { 265 let iter = evaluate(this._innerSelectors, 0, "", element, styles);
218 if (selector == null) 266 for (let selector of iter)
219 { 267 {
220 yield null; 268 if (selector == null)
221 continue; 269 yield null;
222 } 270 else if (scopedQuerySelector(element, selector))
223 if (relativeSelectorRegexp.test(selector))
224 selector = ":scope" + selector;
225 try
226 {
227 if (element.querySelector(selector))
228 yield element; 271 yield element;
229 } 272 }
230 catch (e) 273 yield null;
231 {
232 // :scope isn't supported on Edge, ignore error caused by it.
233 }
234 } 274 }
235 yield null;
236 } 275 }
237 } 276 }
238 }; 277 };
239 278
240 function ContainsSelector(textContent) 279 function ContainsSelector(textContent)
241 { 280 {
242 this._text = textContent; 281 this._text = textContent;
243 } 282 }
244 283
245 ContainsSelector.prototype = { 284 ContainsSelector.prototype = {
246 requiresHiding: true, 285 requiresHiding: true,
247 dependsOnCharacterData: true, 286 dependsOnCharacterData: true,
248 287
249 *getSelectors(prefix, subtree, stylesheet) 288 *getSelectors(prefix, subtree, stylesheet)
250 { 289 {
251 for (let element of this.getElements(prefix, subtree, stylesheet)) 290 for (let element of this.getElements(prefix, subtree, stylesheet))
252 yield [makeSelector(element, ""), subtree]; 291 yield [makeSelector(element, ""), subtree];
253 }, 292 },
254 293
255 *getElements(prefix, subtree, stylesheet) 294 *getElements(prefix, subtree, stylesheet)
256 { 295 {
257 let actualPrefix = (!prefix || incompletePrefixRegexp.test(prefix)) ? 296 let actualPrefix = (!prefix || incompletePrefixRegexp.test(prefix)) ?
258 prefix + "*" : prefix; 297 prefix + "*" : prefix;
259 let elements = subtree.querySelectorAll(actualPrefix); 298
260 299 let elements = scopedQuerySelectorAll(subtree, actualPrefix);
261 for (let element of elements) 300 if (elements)
262 { 301 {
263 if (element.textContent.includes(this._text)) 302 for (let element of elements)
264 yield element; 303 {
265 else 304 if (element.textContent.includes(this._text))
266 yield null; 305 yield element;
306 else
307 yield null;
308 }
267 } 309 }
268 } 310 }
269 }; 311 };
270 312
271 function PropsSelector(propertyExpression) 313 function PropsSelector(propertyExpression)
272 { 314 {
273 let regexpString; 315 let regexpString;
274 if (propertyExpression.length >= 2 && propertyExpression[0] == "/" && 316 if (propertyExpression.length >= 2 && propertyExpression[0] == "/" &&
275 propertyExpression[propertyExpression.length - 1] == "/") 317 propertyExpression[propertyExpression.length - 1] == "/")
276 { 318 {
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
314 356
315 function isSelectorHidingOnlyPattern(pattern) 357 function isSelectorHidingOnlyPattern(pattern)
316 { 358 {
317 return pattern.selectors.some(s => s.preferHideWithSelector) && 359 return pattern.selectors.some(s => s.preferHideWithSelector) &&
318 !pattern.selectors.some(s => s.requiresHiding); 360 !pattern.selectors.some(s => s.requiresHiding);
319 } 361 }
320 362
321 function shouldObserveAttributes(patterns) 363 function shouldObserveAttributes(patterns)
322 { 364 {
323 // Observe changes to attributes if either there's a plain selector that 365 // Observe changes to attributes if either there's a plain selector that
324 // looks like an attribute selector in one of the patterns 366 // looks like an ID selector, class selector, or attribute selector in one of
325 // (e.g. "a[href='https://example.com/']") 367 // the patterns (e.g. "a[href='https://example.com/']")
326 // or there's a properties selector nested inside a has selector 368 // or there's a properties selector nested inside a has selector
327 // (e.g. "div:-abp-has(:-abp-properties(color: blue))") 369 // (e.g. "div:-abp-has(:-abp-properties(color: blue))")
328 return patterns.some( 370 return patterns.some(
329 pattern => pattern.selectors.some( 371 pattern => pattern.selectors.some(
330 selector => selector.maybeDependsOnAttributes || 372 selector => selector.maybeDependsOnAttributes ||
331 selector instanceof HasSelector && 373 (selector instanceof HasSelector &&
332 selector.dependsOnStyles 374 selector.dependsOnStyles)
333 ) 375 )
334 ); 376 );
335 } 377 }
336 378
337 function shouldObserveCharacterData(patterns) 379 function shouldObserveCharacterData(patterns)
338 { 380 {
339 // Observe changes to character data only if there's a contains selector in 381 // Observe changes to character data only if there's a contains selector in
340 // one of the patterns. 382 // one of the patterns.
341 return patterns.some( 383 return patterns.some(
342 pattern => pattern.selectors.some( 384 pattern => pattern.selectors.some(
(...skipping 244 matching lines...) Expand 10 before | Expand all | Expand 10 after
587 this._scheduledProcessing = {stylesheets}; 629 this._scheduledProcessing = {stylesheets};
588 setTimeout(() => 630 setTimeout(() =>
589 { 631 {
590 let newStylesheets = this._scheduledProcessing.stylesheets; 632 let newStylesheets = this._scheduledProcessing.stylesheets;
591 this._filteringInProgress = true; 633 this._filteringInProgress = true;
592 this._scheduledProcessing = null; 634 this._scheduledProcessing = null;
593 this._addSelectors(newStylesheets, completion); 635 this._addSelectors(newStylesheets, completion);
594 }, 636 },
595 MIN_INVOCATION_INTERVAL - (performance.now() - this._lastInvocation)); 637 MIN_INVOCATION_INTERVAL - (performance.now() - this._lastInvocation));
596 } 638 }
639 else if (this.document.readyState == "loading")
640 {
641 this._scheduledProcessing = {stylesheets};
642 let handler = () =>
643 {
644 document.removeEventListener("DOMContentLoaded", handler);
645 let newStylesheets = this._scheduledProcessing.stylesheets;
646 this._filteringInProgress = true;
647 this._scheduledProcessing = null;
648 this._addSelectors(newStylesheets, completion);
649 };
650 document.addEventListener("DOMContentLoaded", handler);
651 }
597 else 652 else
598 { 653 {
599 this._filteringInProgress = true; 654 this._filteringInProgress = true;
600 this._addSelectors(stylesheets, completion); 655 this._addSelectors(stylesheets, completion);
601 } 656 }
602 }, 657 },
603 658
604 onLoad(event) 659 onLoad(event)
605 { 660 {
606 let stylesheet = event.target.sheet; 661 let stylesheet = event.target.sheet;
(...skipping 27 matching lines...) Expand all
634 characterData: shouldObserveCharacterData(this.patterns), 689 characterData: shouldObserveCharacterData(this.patterns),
635 subtree: true 690 subtree: true
636 } 691 }
637 ); 692 );
638 this.document.addEventListener("load", this.onLoad.bind(this), true); 693 this.document.addEventListener("load", this.onLoad.bind(this), true);
639 } 694 }
640 } 695 }
641 }; 696 };
642 697
643 exports.ElemHideEmulation = ElemHideEmulation; 698 exports.ElemHideEmulation = ElemHideEmulation;
LEFTRIGHT
« no previous file | no next file » | Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Toggle Comments ('s')

Powered by Google App Engine
This is Rietveld