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

Side by Side Diff: test/browser/elemHideEmulation.js

Issue 29383960: Issue 3143 - Filter elements with :-abp-has() (Closed) Base URL: https://hg.adblockplus.org/adblockpluscore
Patch Set: Last comments. Created June 8, 2017, 12:15 a.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
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-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 "use strict"; 18 "use strict";
19 19
20 /* globals ElemHideEmulation */ 20 /* globals ElemHideEmulation, splitSelector,
21 parseSelectorContent,
22 parseSelector, positionInParent, makeSelector,
23 PlainSelector, HasSelector, PropsSelector */
21 24
22 let myUrl = document.currentScript.src; 25 let myUrl = document.currentScript.src;
23 26
24 exports.tearDown = function(callback) 27 exports.tearDown = function(callback)
25 { 28 {
26 let styleElements = document.head.getElementsByTagName("style"); 29 let styleElements = document.head.getElementsByTagName("style");
27 while (styleElements.length) 30 while (styleElements.length)
28 styleElements[0].parentNode.removeChild(styleElements[0]); 31 styleElements[0].parentNode.removeChild(styleElements[0]);
32
33 let child;
34 while (child = document.body.firstChild)
35 document.body.removeChild(child);
Wladimir Palant 2017/06/08 13:12:42 If you store the child in an intermediate variable
hub 2017/06/08 15:45:48 Done.
36
29 callback(); 37 callback();
30 }; 38 };
31 39
32 function unexpectedError(error) 40 function unexpectedError(error)
33 { 41 {
34 console.error(error); 42 console.error(error);
35 this.ok(false, "Unexpected error: " + error); 43 this.ok(false, "Unexpected error: " + error);
36 } 44 }
37 45
38 function expectHidden(test, element) 46 function expectHidden(test, element)
(...skipping 23 matching lines...) Expand all
62 if (styleElements.length) 70 if (styleElements.length)
63 styleElement = styleElements[0]; 71 styleElement = styleElements[0];
64 else 72 else
65 { 73 {
66 styleElement = document.createElement("style"); 74 styleElement = document.createElement("style");
67 document.head.appendChild(styleElement); 75 document.head.appendChild(styleElement);
68 } 76 }
69 styleElement.sheet.insertRule(rule, styleElement.sheet.cssRules.length); 77 styleElement.sheet.insertRule(rule, styleElement.sheet.cssRules.length);
70 } 78 }
71 79
72 function createElementWithStyle(styleBlock) 80 // insert a <div> with a unique id and and empty CSS rule
Wladimir Palant 2017/06/08 13:12:43 You have "and" twice here. Also, how is this an em
hub 2017/06/08 15:45:50 Done.
81 // for the the selector matching the id.
82 function createElementWithStyle(styleBlock, parent)
73 { 83 {
74 let element = document.createElement("div"); 84 let element = document.createElement("div");
75 element.id = findUniqueId(); 85 element.id = findUniqueId();
76 document.body.appendChild(element); 86 if (!parent)
87 document.body.appendChild(element);
88 else
89 parent.appendChild(element);
77 insertStyleRule("#" + element.id + " " + styleBlock); 90 insertStyleRule("#" + element.id + " " + styleBlock);
78 return element; 91 return element;
79 } 92 }
80 93
81 function applyElemHideEmulation(selectors) 94 // Will ensure the class ElemHideEmulation is loaded
95 // and then will call the callback.
Wladimir Palant 2017/06/08 13:12:41 There are no callbacks here, this function returns
hub 2017/06/08 15:45:50 I forgot to update the comment when we switched th
96 // NOTE: if it never loads, this will probably hang in an infinite
97 // loop
Wladimir Palant 2017/06/08 13:12:42 There is no loop here. But - yes, loadScript() mig
hub 2017/06/08 15:45:52 Done.
98 function loadElemHideEmulation()
82 { 99 {
83 if (typeof ElemHideEmulation == "undefined") 100 if (typeof ElemHideEmulation == "undefined")
84 { 101 {
85 return loadScript(myUrl + "/../../../lib/common.js").then(() => 102 return loadScript(myUrl + "/../../../lib/common.js").then(() =>
86 { 103 {
87 return loadScript(myUrl + "/../../../chrome/content/elemHideEmulation.js") ; 104 return loadScript(myUrl + "/../../../chrome/content/elemHideEmulation.js") ;
88 }).then(() => 105 }).then(() =>
89 { 106 {
90 return applyElemHideEmulation(selectors); 107 return loadElemHideEmulation();
91 }); 108 });
92 } 109 }
93 110
94 let elemHideEmulation = new ElemHideEmulation( 111 return Promise.resolve();
95 window, 112 }
96 callback => 113
97 { 114 // instantiate a ElemHideEmulation with @selectors.
Wladimir Palant 2017/06/08 13:12:43 "an ElemHideEmulation instance"?
hub 2017/06/08 15:45:48 Done.
98 let patterns = []; 115 function applyElemHideEmulation(selectors)
99 selectors.forEach(selector => 116 {
117 return loadElemHideEmulation().then(() =>
118 {
119 let elemHideEmulation = new ElemHideEmulation(
120 window,
121 callback =>
100 { 122 {
101 patterns.push({selector}); 123 let patterns = [];
102 }); 124 selectors.forEach(selector =>
103 callback(patterns); 125 {
104 }, newSelectors => 126 patterns.push({selector});
105 { 127 });
106 if (!newSelectors.length) 128 callback(patterns);
107 return; 129 },
108 let selector = newSelectors.join(", "); 130 newSelectors =>
109 insertStyleRule(selector + "{display: none !important;}"); 131 {
110 } 132 if (!newSelectors.length)
111 ); 133 return;
112 134 let selector = newSelectors.join(", ");
113 elemHideEmulation.apply(); 135 insertStyleRule(selector + "{display: none !important;}");
114 return Promise.resolve(); 136 },
115 } 137 elems =>
138 {
139 if (!elems.length)
140 return;
Wladimir Palant 2017/06/08 13:12:42 This check is pointless, we can just let the loop
hub 2017/06/08 15:45:49 Done.
141 for (let elem of elems)
142 elem.style.display = "none";
143 }
144 );
145
146 elemHideEmulation.apply();
147 return Promise.resolve();
Wladimir Palant 2017/06/08 13:12:43 While it was like that before your changes already
hub 2017/06/08 15:45:50 Done.
148 });
149 }
150
151 exports.testParseSelectorContent = function(test)
152 {
153 loadElemHideEmulation().then(() =>
154 {
155 let parsed = parseSelectorContent(":-abp-has(> div) > div", 10);
156 test.equal(parsed.text, "> div");
157 test.equal(parsed.end, 15);
158
159 parsed = parseSelectorContent("\"> div\") > div", 0);
160 test.equal(parsed.text, "\"> div\"");
161 test.equal(parsed.end, 7);
162 parsed = parseSelectorContent(" \"> div\" ) > div", 0);
163 test.equal(parsed.text, " \"> div\" ");
164 test.equal(parsed.end, 9);
165
166 // parens not closed.
167 parsed = parseSelectorContent("> div > div", 0);
168 test.equal(parsed, null);
169 }).catch(unexpectedError.bind(test)).then(() => test.done());
Wladimir Palant 2017/06/08 13:12:40 I'm not really a friend of testing implementation
hub 2017/06/08 15:45:49 I actually think that *also* testing internal func
Wladimir Palant 2017/06/13 13:16:25 As you wish but I certainly won't object to anybod
170 };
171
172 exports.testParseSelector = function(test)
173 {
174 loadElemHideEmulation().then(() =>
175 {
176 let selectors = parseSelector("");
177 test.equal(selectors.length, 0);
178
179 let selector = "div > :-abp-properties('background-color: rgb(0, 0, 0)')";
180 selectors = parseSelector(selector);
181 test.equal(selectors.length, 2);
182 test.ok(selectors[0] instanceof PlainSelector);
183 test.ok(selectors[1] instanceof PropsSelector);
184
185 selector = "div > :-abp-has(> div.inside) > div";
186 selectors = parseSelector(selector);
187 test.equal(selectors.length, 3);
188 test.ok(selectors[0] instanceof PlainSelector);
189 test.ok(selectors[1] instanceof HasSelector);
190 test.ok(selectors[2] instanceof PlainSelector);
191
192 selector = "div > div:-abp-has(> div.inside) > div";
193 selectors = parseSelector(selector);
194
195 test.equal(selectors.length, 3);
196 test.ok(selectors[0] instanceof PlainSelector);
197 test.ok(selectors[1] instanceof HasSelector);
198 test.ok(selectors[2] instanceof PlainSelector);
199
200 selector = "div > :-abp-has(> div.inside) > :-abp-properties(background-colo r: rgb(0, 0, 0))";
201 selectors = parseSelector(selector);
202
203 test.equal(selectors.length, 4);
204 test.ok(selectors[0] instanceof PlainSelector);
205 test.ok(selectors[1] instanceof HasSelector);
206 test.ok(selectors[2] instanceof PlainSelector);
207 test.ok(selectors[3] instanceof PropsSelector);
208
209 selector = "div > :-abp-has(> div.inside > :-abp-properties(background-color : rgb(0, 0, 0))";
210 selectors = parseSelector(selector);
211 test.equal(selectors, null);
212
213 // -abp-has-unsupported() is unknown. Ensure we fail parsing.
214 selector = 'div[arial-label="Story"]:-abp-has(> div > div > span > span:-abp -unsupported("Suggested Post"))';
215 selectors = parseSelector(selector);
216 test.equal(selectors, null);
217 }).catch(unexpectedError.bind(test)).then(() => test.done());
218 };
219
220 function buildDom(doc)
221 {
222 doc.body.innerHTML = `<div id="parent">
223 <div id="middle">
224 <div id="middle1"><div id="inside" class="inside"></div></div>
225 </div>
226 <div id="sibling">
227 <div id="tohide">to hide</div>
228 </div>
229 <div id="sibling2">
230 <div id="sibling21"><div id="sibling211" class="inside"></div></div>
231 </div>
232 </div>`;
233 let parent = document.getElementById("parent");
234 let middle = document.getElementById("middle");
235 let middle1 = document.getElementById("middle1");
236 let inside = document.getElementById("inside");
237 let sibling = document.getElementById("sibling");
238 let sibling2 = document.getElementById("sibling2");
239 let toHide = document.getElementById("tohide");
240 return {parent, middle, middle1, inside, sibling, sibling2, toHide};
241 }
242
243 exports.testPositionInParent = function(test)
244 {
245 let nodes = buildDom(document);
246
247 loadElemHideEmulation().then(() =>
248 {
249 test.equal(positionInParent(nodes.middle1), 1);
250 test.equal(positionInParent(nodes.inside), 1);
251 test.equal(positionInParent(nodes.sibling2), 3);
252 }).catch(unexpectedError.bind(test)).then(() => test.done());
253 };
254
255 exports.testMakeSelector = function(test)
256 {
257 let nodes = buildDom(document);
258
259 loadElemHideEmulation().then(() =>
260 {
261 test.equal(makeSelector(nodes.middle, ""),
262 ":root > BODY:nth-child(2) > DIV:nth-child(1) > DIV:nth-child(1)" );
263 test.equal(makeSelector(nodes.toHide, ""),
264 ":root > BODY:nth-child(2) > DIV:nth-child(1) > DIV:nth-child(2) > DIV:nth-child(1)");
265 }).catch(unexpectedError.bind(test)).then(() => test.done());
266 };
267
268 exports.testPlainSelector = function(test)
269 {
270 buildDom(document);
271
272 loadElemHideEmulation().then(() =>
273 {
274 let selector = new PlainSelector("div > div");
275
276 let iter = selector.getSelectors("foo > ");
277 let value = iter.next();
278 test.equal(value.value[0], "foo > div > div");
279 test.ok(iter.next().done);
280 }).catch(unexpectedError.bind(test)).then(() => test.done());
281 };
282
283 exports.testHasSelector = function(test)
284 {
285 buildDom(document);
286
287 loadElemHideEmulation().then(() =>
288 {
289 let selector = new HasSelector("> div.inside");
290
291 let iter = selector.getSelectors("", document, document.sheet);
292 let value = iter.next();
293 test.ok(!value.done);
294 test.equal(value.value[0],
295 ":root > BODY:nth-child(2) > DIV:nth-child(1) > DIV:nth-child(1) > DIV:nth-child(1)");
296
297 iter = selector.getElements("", document, document.sheet);
298 value = iter.next();
299 test.ok(!value.done);
300 test.equal(value.value.id, "middle1");
301 value = iter.next();
302 test.ok(!value.done);
303 test.equal(value.value.id, "sibling21");
304 value = iter.next();
305 test.ok(value.done);
306
307 selector = new HasSelector(":-abp-has(> div.inside)");
308
309 test.ok(selector._innerSelectors);
310 }).catch(unexpectedError.bind(test)).then(() => test.done());
311 };
312
313 exports.testSplitStyleRule = function(test)
314 {
315 loadElemHideEmulation().then(() =>
316 {
317 let selectors = splitSelector("div:-abp-has(div) > [-abp-properties='backgro und-color: rgb(0, 0, 0)'] > span");
318 test.ok(selectors);
319 test.equal(selectors.length, 1, "There is only one selector");
320
321 selectors = splitSelector("div:-abp-has(div), [-abp-properties='background-c olor: rgb(0, 0, 0)']");
322 test.ok(selectors);
323 test.equal(selectors.length, 2, "There are two selectors");
324 }).catch(unexpectedError.bind(test)).then(() => test.done());
325 };
116 326
117 exports.testVerbatimPropertySelector = function(test) 327 exports.testVerbatimPropertySelector = function(test)
118 { 328 {
119 let toHide = createElementWithStyle("{background-color: #000}"); 329 let toHide = createElementWithStyle("{background-color: #000}");
120 applyElemHideEmulation( 330 applyElemHideEmulation(
121 [":-abp-properties(background-color: rgb(0, 0, 0))"] 331 [":-abp-properties(background-color: rgb(0, 0, 0))"]
122 ).then(() => 332 ).then(() =>
123 { 333 {
124 expectHidden(test, toHide); 334 expectHidden(test, toHide);
125 }).catch(unexpectedError.bind(test)).then(() => test.done()); 335 }).catch(unexpectedError.bind(test)).then(() => test.done());
126 }; 336 };
127 337
338 exports.testVerbatimPropertySelectorWithPrefix = function(test)
339 {
340 let parent = createElementWithStyle("{background-color: #000}");
341 let toHide = createElementWithStyle("{background-color: #000}", parent);
342 applyElemHideEmulation(
343 ["div > :-abp-properties(background-color: rgb(0, 0, 0))"]
344 ).then(() =>
345 {
346 expectVisible(test, parent);
347 expectHidden(test, toHide);
348 }).catch(unexpectedError.bind(test)).then(() => test.done());
349 };
350
351 exports.testVerbatimPropertySelectorWithPrefixNoMatch = function(test)
352 {
353 let parent = createElementWithStyle("{background-color: #000}");
354 let toHide = createElementWithStyle("{background-color: #fff}", parent);
355 applyElemHideEmulation(
356 ["div > :-abp-properties(background-color: rgb(0, 0, 0))"]
357 ).then(() =>
358 {
359 expectVisible(test, parent);
360 expectVisible(test, toHide);
361 }).catch(unexpectedError.bind(test)).then(() => test.done());
362 };
363
364 exports.testVerbatimPropertySelectorWithSuffix = function(test)
365 {
366 let parent = createElementWithStyle("{background-color: #000}");
367 let toHide = createElementWithStyle("{background-color: #000}", parent);
368 applyElemHideEmulation(
369 [":-abp-properties(background-color: rgb(0, 0, 0)) > div"]
370 ).then(() =>
371 {
372 expectVisible(test, parent);
373 expectHidden(test, toHide);
374 }).catch(unexpectedError.bind(test)).then(() => test.done());
375 };
376
377 exports.testVerbatimPropertyPseudoSelectorWithPrefixAndSuffix = function(test)
378 {
379 let parent = createElementWithStyle("{background-color: #000}");
380 let middle = createElementWithStyle("{background-color: #000}", parent);
381 let toHide = createElementWithStyle("{background-color: #000}", middle);
382 applyElemHideEmulation(
383 ["div > :-abp-properties(background-color: rgb(0, 0, 0)) > div"]
384 ).then(() =>
385 {
386 expectVisible(test, parent);
387 expectVisible(test, middle);
388 expectHidden(test, toHide);
389 }).catch(unexpectedError.bind(test)).then(() => test.done());
390 };
391
128 exports.testPropertySelectorWithWildcard = function(test) 392 exports.testPropertySelectorWithWildcard = function(test)
129 { 393 {
130 let toHide = createElementWithStyle("{background-color: #000}"); 394 let toHide = createElementWithStyle("{background-color: #000}");
131 applyElemHideEmulation( 395 applyElemHideEmulation(
132 [":-abp-properties(*color: rgb(0, 0, 0))"] 396 [":-abp-properties(*color: rgb(0, 0, 0))"]
133 ).then(() => 397 ).then(() =>
134 { 398 {
135 expectHidden(test, toHide); 399 expectHidden(test, toHide);
136 }).catch(unexpectedError.bind(test)).then(() => test.done()); 400 }).catch(unexpectedError.bind(test)).then(() => test.done());
137 }; 401 };
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
181 return new Promise((resolve, reject) => 445 return new Promise((resolve, reject) =>
182 { 446 {
183 window.setTimeout(() => 447 window.setTimeout(() =>
184 { 448 {
185 expectHidden(test, toHide); 449 expectHidden(test, toHide);
186 resolve(); 450 resolve();
187 }, 0); 451 }, 0);
188 }); 452 });
189 }).catch(unexpectedError.bind(test)).then(() => test.done()); 453 }).catch(unexpectedError.bind(test)).then(() => test.done());
190 }; 454 };
455
456 exports.testPseudoClassHasSelector = function(test)
457 {
458 let toHide = createElementWithStyle("{}");
459 applyElemHideEmulation(
460 ["div:-abp-has(div)"]
461 ).then(() =>
462 {
463 expectVisible(test, toHide);
464 }).catch(unexpectedError.bind(test)).then(() => test.done());
465 };
466
467 exports.testPseudoClassHasSelectorWithPrefix = function(test)
468 {
469 let parent = createElementWithStyle("{}");
470 let child = createElementWithStyle("{}", parent);
471 applyElemHideEmulation(
472 ["div:-abp-has(div)"]
473 ).then(() =>
474 {
475 expectHidden(test, parent);
476 expectVisible(test, child);
477 }).catch(unexpectedError.bind(test)).then(() => test.done());
478 };
479
480 exports.testPseudoClassHasSelectorWithSuffix = function(test)
481 {
482 let parent = createElementWithStyle("{}");
483 let middle = createElementWithStyle("{}", parent);
484 let child = createElementWithStyle("{}", middle);
485 applyElemHideEmulation(
486 ["div:-abp-has(div) > div"]
487 ).then(() =>
488 {
489 expectVisible(test, parent);
490 expectHidden(test, middle);
491 expectHidden(test, child);
492 }).catch(unexpectedError.bind(test)).then(() => test.done());
493 };
494
495 exports.testPseudoClassHasSelectorWithSuffixSibling = function(test)
496 {
497 let parent = createElementWithStyle("{}");
498 let middle = createElementWithStyle("{}", parent);
499 let toHide = createElementWithStyle("{}");
500 applyElemHideEmulation(
501 ["div:-abp-has(div) + div"]
502 ).then(() =>
503 {
504 expectVisible(test, parent);
505 expectVisible(test, middle);
506 expectHidden(test, toHide);
507 }).catch(unexpectedError.bind(test)).then(() => test.done());
508 };
509
510 exports.testPseudoClassHasSelectorWithSuffixSiblingChild = function(test)
511 {
512 // <div>
513 // <div></div>
514 // <div>
515 // <div>to hide</div>
516 // </div>
517 // </div>
518 let parent = createElementWithStyle("{}");
519 let middle = createElementWithStyle("{}", parent);
520 let sibling = createElementWithStyle("{}");
521 let toHide = createElementWithStyle("{}", sibling);
522 applyElemHideEmulation(
523 ["div:-abp-has(div) + div > div"]
524 ).then(() =>
525 {
526 expectVisible(test, parent);
527 expectVisible(test, middle);
528 expectVisible(test, sibling);
529 expectHidden(test, toHide);
530 }).catch(unexpectedError.bind(test)).then(() => test.done());
531 };
532
533 function runTestPseudoClassHasSelectorWithHasAndWithSuffixSibling(test, selector )
534 {
535 document.body.innerHTML = `<div id="parent">
536 <div id="middle">
537 <div id="middle1"><div id="inside" class="inside"></div></div>
538 </div>
539 <div id="sibling">
540 <div id="tohide">to hide</div>
541 </div>
542 <div id="sibling2">
543 <div id="sibling21"><div id="sibling211" class="inside"></div></div>
544 </div>
545 </div>`;
546 let parent = document.getElementById("parent");
547 let middle = document.getElementById("middle");
548 let inside = document.getElementById("inside");
549 let sibling = document.getElementById("sibling");
550 let sibling2 = document.getElementById("sibling2");
551 let toHide = document.getElementById("tohide");
552
553 insertStyleRule(".inside {}");
554
555 applyElemHideEmulation(
556 [selector]
557 ).then(() =>
558 {
559 expectVisible(test, parent);
560 expectVisible(test, middle);
561 expectVisible(test, inside);
562 expectVisible(test, sibling);
563 expectVisible(test, sibling2);
564 expectHidden(test, toHide);
565 }).catch(unexpectedError.bind(test)).then(() => test.done());
566 }
567
568 exports.testPseudoClassHasSelectorWithHasAndWithSuffixSibling = function(test)
569 {
570 runTestPseudoClassHasSelectorWithHasAndWithSuffixSibling(test, "div:-abp-has(: -abp-has(div.inside)) + div > div");
571 };
572
573 exports.testPseudoClassHasSelectorWithHasAndWithSuffixSibling2 = function(test)
574 {
575 runTestPseudoClassHasSelectorWithHasAndWithSuffixSibling(test, "div:-abp-has(: -abp-has(> div.inside)) + div > div");
576 };
577
578 exports.testPseudoClassHasSelectorWithPropSelector = function(test)
579 {
580 let parent = createElementWithStyle("{}");
581 let child = createElementWithStyle("{background-color: #000}", parent);
582 applyElemHideEmulation(
583 ["div:-abp-has(:-abp-properties(background-color: rgb(0, 0, 0)))"]
584 ).then(() =>
585 {
586 expectVisible(test, child);
587 expectHidden(test, parent);
588 }).catch(unexpectedError.bind(test)).then(() => test.done());
589 };
OLDNEW
« chrome/content/elemHideEmulation.js ('K') | « chrome/content/elemHideEmulation.js ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld