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

Side by Side Diff: chrome/content/elemHideEmulation.js

Issue 29383960: Issue 3143 - Filter elements with :-abp-has() (Closed) Base URL: https://hg.adblockplus.org/adblockpluscore
Patch Set: Hide elements and not using styles. Created March 16, 2017, 5:16 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
« no previous file with comments | « no previous file | lib/filterClasses.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 // We are currently limited to ECMAScript 5 in this file, because it is being 1 // We are currently limited to ECMAScript 5 in this file, because it is being
2 // used in the browser tests. See https://issues.adblockplus.org/ticket/4796 2 // used in the browser tests. See https://issues.adblockplus.org/ticket/4796
3 3
4 var propertySelectorRegExp = /\[\-abp\-properties=(["'])([^"']+)\1\]/; 4 var propertySelectorRegExp = /\[\-abp\-properties=(["'])([^"']+)\1\]/;
5 var pseudoClassHasSelectorRegExp = /:has\((.*)\)/;
5 6
6 function splitSelector(selector) 7 function splitSelector(selector)
7 { 8 {
8 if (selector.indexOf(",") == -1) 9 if (selector.indexOf(",") == -1)
9 return [selector]; 10 return [selector];
10 11
11 var selectors = []; 12 var selectors = [];
12 var start = 0; 13 var start = 0;
13 var level = 0; 14 var level = 0;
14 var sep = ""; 15 var sep = "";
(...skipping 19 matching lines...) Expand all
34 selectors.push(selector.substring(start, i)); 35 selectors.push(selector.substring(start, i));
35 start = i + 1; 36 start = i + 1;
36 } 37 }
37 } 38 }
38 } 39 }
39 40
40 selectors.push(selector.substring(start)); 41 selectors.push(selector.substring(start));
41 return selectors; 42 return selectors;
42 } 43 }
43 44
44 function ElemHideEmulation(window, getFiltersFunc, addSelectorsFunc) 45 // matcher for the pseudo CSS4 class :has
46 // For those browser that don't have it yet.
47 function PseudoHasMatcher(selector)
48 {
49 this.hasSelector = selector;
50 }
51
52 PseudoHasMatcher.prototype = {
53 match: function(elem)
54 {
55 var matches = [];
56 // look up for all elements that match the :has().
57 var children = elem.children;
58 for (var i = 0; i < children.length; i++)
59 {
60 var hasElem = elem.querySelector(this.hasSelector);
61 if (hasElem != null)
62 {
63 matches.push(hasElem);
64 }
65 }
66 return matches;
67 }
68 };
69
70 function ElemHideEmulation(window, getFiltersFunc, addSelectorsFunc, hideElement sFunc)
45 { 71 {
46 this.window = window; 72 this.window = window;
47 this.getFiltersFunc = getFiltersFunc; 73 this.getFiltersFunc = getFiltersFunc;
48 this.addSelectorsFunc = addSelectorsFunc; 74 this.addSelectorsFunc = addSelectorsFunc;
75 this.hideElementsFunc = hideElementsFunc;
49 } 76 }
50 77
51 ElemHideEmulation.prototype = { 78 ElemHideEmulation.prototype = {
52 stringifyStyle: function(style) 79 stringifyStyle: function(style)
53 { 80 {
54 var styles = []; 81 var styles = [];
55 for (var i = 0; i < style.length; i++) 82 for (var i = 0; i < style.length; i++)
56 { 83 {
57 var property = style.item(i); 84 var property = style.item(i);
58 var value = style.getPropertyValue(property); 85 var value = style.getPropertyValue(property);
(...skipping 28 matching lines...) Expand all
87 if (!rules) 114 if (!rules)
88 return; 115 return;
89 116
90 for (var i = 0; i < rules.length; i++) 117 for (var i = 0; i < rules.length; i++)
91 { 118 {
92 var rule = rules[i]; 119 var rule = rules[i];
93 if (rule.type != rule.STYLE_RULE) 120 if (rule.type != rule.STYLE_RULE)
94 continue; 121 continue;
95 122
96 var style = this.stringifyStyle(rule.style); 123 var style = this.stringifyStyle(rule.style);
97 for (var j = 0; j < this.patterns.length; j++) 124 for (var j = 0; j < this.propSelPatterns.length; j++)
98 { 125 {
99 var pattern = this.patterns[j]; 126 var pattern = this.propSelPatterns[j];
100 if (pattern.regexp.test(style)) 127 if (pattern.regexp.test(style))
101 { 128 {
102 var subSelectors = splitSelector(rule.selectorText); 129 var subSelectors = splitSelector(rule.selectorText);
103 for (var k = 0; k < subSelectors.length; k++) 130 for (var k = 0; k < subSelectors.length; k++)
104 { 131 {
105 var subSelector = subSelectors[k]; 132 var subSelector = subSelectors[k];
106 selectors.push(pattern.prefix + subSelector + pattern.suffix); 133 selectors.push(pattern.prefix + subSelector + pattern.suffix);
107 filters.push(pattern.text); 134 filters.push(pattern.text);
108 } 135 }
109 } 136 }
110 } 137 }
111 } 138 }
112 }, 139 },
113 140
141 findPseudoClassHasSelectors: function(selectors, filters)
142 {
143 for (var i = 0; i < this.pseudoHasPatterns.length; i++)
144 {
145 var pattern = this.pseudoHasPatterns[i];
146
147 var prefixes = document.querySelectorAll(pattern.prefix);
148 for (var j = 0; j < prefixes.length; j++)
149 {
150 var matched = pattern.elementMatcher.match(prefixes[j]);
151 if (matched.length == 0)
152 {
153 continue;
154 }
155 // XXX make sure we don't have performance problems here
156 matched.forEach(function(e)
157 {
158 var subSelector = e.id;
159 if (!subSelector)
160 {
161 var findUniqueId = function()
162 {
163 var id = "elemHideEmulationHide-" +
164 Math.floor(Math.random() * 10000);
165 if (!document.getElementById(id))
166 return id;
167 return findUniqueId();
168 };
169 subSelector = findUniqueId();
170 e.id = subSelector;
171 }
172 var newSelector = pattern.prefix ? pattern.prefix + " > " : "";
173 newSelector += "#" + subSelector;
174 newSelector += pattern.suffix ? " > " + pattern.suffix : "";
175 selectors.push(newSelector);
176 filters.push(pattern.text);
177 });
178 }
179 }
180 },
181
182 findPseudoClassHasElements: function(elements, filters)
183 {
184 for (var i = 0; i < this.pseudoHasPatterns.length; i++)
185 {
186 var pattern = this.pseudoHasPatterns[i];
187
188 var prefixes = document.querySelectorAll(pattern.prefix);
189 for (var j = 0; j < prefixes.length; j++)
190 {
191 var matched = pattern.elementMatcher.match(prefixes[j]);
192 if (matched.length == 0)
193 {
194 continue;
195 }
196
197 matched.forEach(function(e)
198 {
199 var toHide = [];
200 var hidingPatterns = []
201 if (pattern.suffix)
202 {
203 hidingPatterns.push(pattern.text);
204 console.log('pattern.suffix', pattern.suffix);
205 var sel = pattern.suffix;
206 // XXX we should have a more elegant way
207 // also startsWith isn't available in PhantomJS.
208 if (sel.substr(0, 1) == ">")
209 {
210 sel = sel.substr(1);
211 }
212 var subElements = e.querySelectorAll(sel);
213 for (var k = 0; k < subElements.length; k++)
214 {
215 elements.push(subElements[i]);
216 filters.push(pattern.text);
217 }
218 }
219 else
220 {
221 elements.push(e);
222 filters.push(pattern.text);
223 }
224 });
225 }
226 }
227 },
228
114 addSelectors: function(stylesheets) 229 addSelectors: function(stylesheets)
115 { 230 {
116 var selectors = []; 231 var selectors = [];
117 var filters = []; 232 var filters = [];
118 for (var i = 0; i < stylesheets.length; i++) 233 for (var i = 0; i < stylesheets.length; i++)
119 this.findSelectors(stylesheets[i], selectors, filters); 234 this.findSelectors(stylesheets[i], selectors, filters);
120 this.addSelectorsFunc(selectors, filters); 235 this.addSelectorsFunc(selectors, filters);
121 }, 236 },
122 237
238 hideElements: function()
239 {
240 var elements = [];
241 var filters = [];
242 this.findPseudoClassHasElements(elements, filters);
243 console.log("hideElements", elements.length);
244 this.hideElementsFunc(elements, filters);
245 },
246
123 onLoad: function(event) 247 onLoad: function(event)
124 { 248 {
125 var stylesheet = event.target.sheet; 249 var stylesheet = event.target.sheet;
126 if (stylesheet) 250 if (stylesheet)
127 this.addSelectors([stylesheet]); 251 this.addSelectors([stylesheet]);
252 this.hideElements();
128 }, 253 },
129 254
130 apply: function() 255 apply: function()
131 { 256 {
132 this.getFiltersFunc(function(patterns) 257 this.getFiltersFunc(function(patterns)
133 { 258 {
134 this.patterns = []; 259 this.propSelPatterns = [];
260 this.pseudoHasPatterns = [];
135 for (var i = 0; i < patterns.length; i++) 261 for (var i = 0; i < patterns.length; i++)
136 { 262 {
137 var pattern = patterns[i]; 263 var pattern = patterns[i];
138 var match = propertySelectorRegExp.exec(pattern.selector); 264 var match = propertySelectorRegExp.exec(pattern.selector);
139 if (!match) 265 if (match)
140 continue; 266 {
267 var regexpMatcher;
268 var regexpString;
269 var propertyExpression = match[2];
270 if (propertyExpression.length >= 2 && propertyExpression[0] == "/" &&
271 propertyExpression[propertyExpression.length - 1] == "/")
272 regexpString = propertyExpression.slice(1, -1)
273 .replace("\\x7B ", "{").replace("\\x7D ", "}");
274 else
275 regexpString = filterToRegExp(propertyExpression);
276 regexpMatcher = new RegExp(regexpString, "i");
277 this.propSelPatterns.push({
278 text: pattern.text,
279 regexp: regexpMatcher,
280 prefix: pattern.selector.substr(0, match.index),
281 suffix: pattern.selector.substr(match.index + match[0].length)
282 });
283 }
284 else
285 {
286 var elementMatcher;
287 match = pseudoClassHasSelectorRegExp.exec(pattern.selector);
288 if (!match)
289 continue;
141 290
142 var propertyExpression = match[2]; 291 var pseudoHasSelector = match[1];
143 var regexpString; 292 elementMatcher = new PseudoHasMatcher(pseudoHasSelector);
144 if (propertyExpression.length >= 2 && propertyExpression[0] == "/" && 293 this.pseudoHasPatterns.push({
145 propertyExpression[propertyExpression.length - 1] == "/") 294 text: pattern.text,
146 regexpString = propertyExpression.slice(1, -1) 295 elementMatcher: elementMatcher,
147 .replace("\\x7B ", "{").replace("\\x7D ", "}"); 296 prefix: pattern.selector.substr(0, match.index).trim(),
148 else 297 suffix: pattern.selector.substr(match.index + match[0].length).trim( )
149 regexpString = filterToRegExp(propertyExpression); 298 });
299 }
150 300
151 this.patterns.push({
152 text: pattern.text,
153 regexp: new RegExp(regexpString, "i"),
154 prefix: pattern.selector.substr(0, match.index),
155 suffix: pattern.selector.substr(match.index + match[0].length)
156 });
157 } 301 }
158 302
159 if (this.patterns.length > 0) 303 if (this.pseudoHasPatterns.length > 0 || this.propSelPatterns.length > 0)
160 { 304 {
161 var document = this.window.document; 305 var document = this.window.document;
162 this.addSelectors(document.styleSheets); 306 this.addSelectors(document.styleSheets);
307 this.hideElements();
163 document.addEventListener("load", this.onLoad.bind(this), true); 308 document.addEventListener("load", this.onLoad.bind(this), true);
164 } 309 }
165 }.bind(this)); 310 }.bind(this));
166 } 311 }
167 }; 312 };
OLDNEW
« no previous file with comments | « no previous file | lib/filterClasses.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld