OLD | NEW |
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 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
92 if (styleElements.length) | 92 if (styleElements.length) |
93 styleElement = styleElements[0]; | 93 styleElement = styleElements[0]; |
94 else | 94 else |
95 { | 95 { |
96 styleElement = testDocument.createElement("style"); | 96 styleElement = testDocument.createElement("style"); |
97 testDocument.head.appendChild(styleElement); | 97 testDocument.head.appendChild(styleElement); |
98 } | 98 } |
99 styleElement.sheet.insertRule(rule, styleElement.sheet.cssRules.length); | 99 styleElement.sheet.insertRule(rule, styleElement.sheet.cssRules.length); |
100 } | 100 } |
101 | 101 |
102 // Insert a <div> with a unique id and a CSS rule | 102 function createElement(parent) |
103 // for the the selector matching the id. | |
104 function createElementWithStyle(styleBlock, parent) | |
105 { | 103 { |
106 let element = testDocument.createElement("div"); | 104 let element = testDocument.createElement("div"); |
107 element.id = findUniqueId(); | 105 element.id = findUniqueId(); |
108 if (!parent) | 106 if (!parent) |
109 testDocument.body.appendChild(element); | 107 testDocument.body.appendChild(element); |
110 else | 108 else |
111 parent.appendChild(element); | 109 parent.appendChild(element); |
| 110 return element; |
| 111 } |
| 112 |
| 113 // Insert a <div> with a unique id and a CSS rule |
| 114 // for the the selector matching the id. |
| 115 function createElementWithStyle(styleBlock, parent) |
| 116 { |
| 117 let element = createElement(parent); |
112 insertStyleRule("#" + element.id + " " + styleBlock); | 118 insertStyleRule("#" + element.id + " " + styleBlock); |
113 return element; | 119 return element; |
114 } | 120 } |
115 | 121 |
116 // Create a new ElemHideEmulation instance with @selectors. | 122 // Create a new ElemHideEmulation instance with @selectors. |
117 function applyElemHideEmulation(selectors) | 123 function applyElemHideEmulation(selectors) |
118 { | 124 { |
119 return Promise.resolve().then(() => | 125 return Promise.resolve().then(() => |
120 { | 126 { |
121 let elemHideEmulation = new ElemHideEmulation( | 127 let elemHideEmulation = new ElemHideEmulation( |
122 newSelectors => | 128 newSelectors => |
123 { | 129 { |
124 if (!newSelectors.length) | 130 if (!newSelectors.length) |
125 return; | 131 return; |
126 let selector = newSelectors.join(", "); | 132 let selector = newSelectors.join(", "); |
127 insertStyleRule(selector + "{display: none !important;}"); | 133 insertStyleRule(selector + "{display: none !important;}"); |
128 }, | 134 }, |
129 elems => | 135 elems => |
130 { | 136 { |
131 for (let elem of elems) | 137 for (let elem of elems) |
132 elem.style.display = "none"; | 138 elem.style.display = "none"; |
133 } | 139 } |
134 ); | 140 ); |
135 | 141 |
136 elemHideEmulation.document = testDocument; | 142 elemHideEmulation.document = testDocument; |
137 elemHideEmulation.MIN_INVOCATION_INTERVAL = REFRESH_INTERVAL / 2; | 143 elemHideEmulation.MIN_INVOCATION_INTERVAL = REFRESH_INTERVAL / 2; |
138 elemHideEmulation.apply(selectors.map(selector => ({selector}))); | 144 elemHideEmulation.apply(selectors.map( |
| 145 selector => ({selector, text: selector}) |
| 146 )); |
139 return elemHideEmulation; | 147 return elemHideEmulation; |
140 }); | 148 }); |
141 } | 149 } |
142 | 150 |
143 exports.testVerbatimPropertySelector = function(test) | 151 exports.testVerbatimPropertySelector = function(test) |
144 { | 152 { |
145 let toHide = createElementWithStyle("{background-color: #000}"); | 153 let toHide = createElementWithStyle("{background-color: #000}"); |
146 applyElemHideEmulation( | 154 applyElemHideEmulation( |
147 [":-abp-properties(background-color: rgb(0, 0, 0))"] | 155 [":-abp-properties(background-color: rgb(0, 0, 0))"] |
148 ).then(() => | 156 ).then(() => |
(...skipping 543 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
692 expectVisible(test, child2); | 700 expectVisible(test, child2); |
693 return timeout(REFRESH_INTERVAL); | 701 return timeout(REFRESH_INTERVAL); |
694 }).then(() => | 702 }).then(() => |
695 { | 703 { |
696 expectHidden(test, parent); | 704 expectHidden(test, parent); |
697 expectVisible(test, child); | 705 expectVisible(test, child); |
698 expectHidden(test, sibling); | 706 expectHidden(test, sibling); |
699 expectVisible(test, child2); | 707 expectVisible(test, child2); |
700 }).catch(unexpectedError.bind(test)).then(() => test.done()); | 708 }).catch(unexpectedError.bind(test)).then(() => test.done()); |
701 }; | 709 }; |
| 710 |
| 711 exports.testPseudoClassPropertiesOnStyleSheetLoad = function(test) |
| 712 { |
| 713 let parent = createElement(); |
| 714 let child = createElement(parent); |
| 715 applyElemHideEmulation( |
| 716 ["div:-abp-properties(background-color: rgb(0, 0, 0))", |
| 717 "div:-abp-contains(hide me)", |
| 718 "div:-abp-has(> div.hideMe)"] |
| 719 ).then(() => timeout(REFRESH_INTERVAL) |
| 720 ).then(() => |
| 721 { |
| 722 expectVisible(test, parent); |
| 723 expectVisible(test, child); |
| 724 |
| 725 // Load a style sheet that targets the parent element. This should run only |
| 726 // the "div:-abp-properties(background-color: rgb(0, 0, 0))" pattern. |
| 727 insertStyleRule("#" + parent.id + " {background-color: #000}"); |
| 728 |
| 729 return timeout(REFRESH_INTERVAL); |
| 730 }).then(() => |
| 731 { |
| 732 expectHidden(test, parent); |
| 733 expectVisible(test, child); |
| 734 }).catch(unexpectedError.bind(test)).then(() => test.done()); |
| 735 }; |
| 736 |
| 737 exports.testPlainAttributeOnDomMutation = function(test) |
| 738 { |
| 739 let parent = createElement(); |
| 740 let child = createElement(parent); |
| 741 applyElemHideEmulation( |
| 742 ["div:-abp-properties(background-color: rgb(0, 0, 0))", |
| 743 "div[data-hide-me]", |
| 744 "div:-abp-contains(hide me)", |
| 745 "div:-abp-has(> div.hideMe)"] |
| 746 ).then(() => timeout(REFRESH_INTERVAL) |
| 747 ).then(() => |
| 748 { |
| 749 expectVisible(test, parent); |
| 750 expectVisible(test, child); |
| 751 |
| 752 // Set the "data-hide-me" attribute on the child element. |
| 753 // |
| 754 // Note: Since the "div[data-hide-me]" pattern has already been processed |
| 755 // and the selector added to the document's style sheet, this will in fact |
| 756 // not do anything at our end, but the browser will just match the selector |
| 757 // and hide the element. |
| 758 child.setAttribute("data-hide-me", ""); |
| 759 |
| 760 return timeout(REFRESH_INTERVAL); |
| 761 }).then(() => |
| 762 { |
| 763 expectVisible(test, parent); |
| 764 expectHidden(test, child); |
| 765 }).catch(unexpectedError.bind(test)).then(() => test.done()); |
| 766 }; |
| 767 |
| 768 exports.testPseudoClassContainsOnDomMutation = function(test) |
| 769 { |
| 770 let parent = createElement(); |
| 771 let child = createElement(parent); |
| 772 |
| 773 child.innerText = "do nothing"; |
| 774 |
| 775 applyElemHideEmulation( |
| 776 ["div:-abp-properties(background-color: rgb(0, 0, 0))", |
| 777 "div[data-hide-me]", |
| 778 "div:-abp-contains(hide me)", |
| 779 "div:-abp-has(> div.hideMe)"] |
| 780 ).then(() => timeout(REFRESH_INTERVAL) |
| 781 ).then(() => |
| 782 { |
| 783 expectVisible(test, parent); |
| 784 expectVisible(test, child); |
| 785 |
| 786 // Set the child element's text to "hide me". This should run only the |
| 787 // "div:-abp-contains(hide me)" pattern. |
| 788 // |
| 789 // Note: We need to set Node.innerText here in order to trigger the |
| 790 // "characterData" DOM mutation on Chromium. If we set Node.textContent |
| 791 // instead, it triggers the "childList" DOM mutation instead. |
| 792 child.innerText = "hide me"; |
| 793 |
| 794 return timeout(REFRESH_INTERVAL); |
| 795 }).then(() => |
| 796 { |
| 797 expectHidden(test, parent); |
| 798 expectVisible(test, child); |
| 799 }).catch(unexpectedError.bind(test)).then(() => test.done()); |
| 800 }; |
| 801 |
| 802 exports.testPseudoClassHasOnDomMutation = function(test) |
| 803 { |
| 804 let parent = createElement(); |
| 805 let child = null; |
| 806 applyElemHideEmulation( |
| 807 ["div:-abp-properties(background-color: rgb(0, 0, 0))", |
| 808 "div[data-hide-me]", |
| 809 "div:-abp-contains(hide me)", |
| 810 "div:-abp-has(> div)"] |
| 811 ).then(() => timeout(REFRESH_INTERVAL) |
| 812 ).then(() => |
| 813 { |
| 814 expectVisible(test, parent); |
| 815 |
| 816 // Add the child element. This should run all the DOM-dependent patterns |
| 817 // ("div:-abp-contains(hide me)" and "div:-abp-has(> div)"). |
| 818 child = createElement(parent); |
| 819 |
| 820 return timeout(REFRESH_INTERVAL); |
| 821 }).then(() => |
| 822 { |
| 823 expectHidden(test, parent); |
| 824 expectVisible(test, child); |
| 825 }).catch(unexpectedError.bind(test)).then(() => test.done()); |
| 826 }; |
| 827 |
| 828 exports.testPseudoClassHasWithClassOnDomMutation = function(test) |
| 829 { |
| 830 let parent = createElement(); |
| 831 let child = createElement(parent); |
| 832 applyElemHideEmulation( |
| 833 ["div:-abp-properties(background-color: rgb(0, 0, 0))", |
| 834 "div[data-hide-me]", |
| 835 "div:-abp-contains(hide me)", |
| 836 "div:-abp-has(> div.hideMe)"] |
| 837 ).then(() => timeout(REFRESH_INTERVAL) |
| 838 ).then(() => |
| 839 { |
| 840 expectVisible(test, parent); |
| 841 expectVisible(test, child); |
| 842 |
| 843 // Set the child element's class to "hideMe". This should run only the |
| 844 // "div:-abp-has(> div.hideMe)" pattern. |
| 845 child.className = "hideMe"; |
| 846 |
| 847 return timeout(REFRESH_INTERVAL); |
| 848 }).then(() => |
| 849 { |
| 850 expectHidden(test, parent); |
| 851 expectVisible(test, child); |
| 852 }).catch(unexpectedError.bind(test)).then(() => test.done()); |
| 853 }; |
| 854 |
| 855 exports.testPseudoClassHasWithPseudoClassContainsOnDomMutation = function(test) |
| 856 { |
| 857 let parent = createElement(); |
| 858 let child = createElement(parent); |
| 859 |
| 860 child.innerText = "do nothing"; |
| 861 |
| 862 applyElemHideEmulation( |
| 863 ["div:-abp-properties(background-color: rgb(0, 0, 0))", |
| 864 "div[data-hide-me]", |
| 865 "div:-abp-contains(hide me)", |
| 866 "div:-abp-has(> div:-abp-contains(hide me))"] |
| 867 ).then(() => timeout(REFRESH_INTERVAL) |
| 868 ).then(() => |
| 869 { |
| 870 expectVisible(test, parent); |
| 871 expectVisible(test, child); |
| 872 |
| 873 // Set the child element's text to "hide me". This should run only the |
| 874 // "div:-abp-contains(hide me)" and |
| 875 // "div:-abp-has(> div:-abp-contains(hide me))" patterns. |
| 876 child.innerText = "hide me"; |
| 877 |
| 878 return timeout(REFRESH_INTERVAL); |
| 879 }).then(() => |
| 880 { |
| 881 // Note: Even though it runs both the :-abp-contains() patterns, it only |
| 882 // hides the parent element because of revision d7d51d29aa34. |
| 883 expectHidden(test, parent); |
| 884 expectVisible(test, child); |
| 885 }).catch(unexpectedError.bind(test)).then(() => test.done()); |
| 886 }; |
OLD | NEW |