| OLD | NEW | 
|---|
| 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  Loading... | 
| 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 | 
|  | 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 | 
| 44 function ElemHideEmulation(window, getFiltersFunc, addSelectorsFunc) | 70 function ElemHideEmulation(window, getFiltersFunc, addSelectorsFunc) | 
| 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; | 
| 49 } | 75 } | 
| 50 | 76 | 
| 51 ElemHideEmulation.prototype = { | 77 ElemHideEmulation.prototype = { | 
| 52   stringifyStyle: function(style) | 78   stringifyStyle: function(style) | 
| 53   { | 79   { | 
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 90     for (var i = 0; i < rules.length; i++) | 116     for (var i = 0; i < rules.length; i++) | 
| 91     { | 117     { | 
| 92       var rule = rules[i]; | 118       var rule = rules[i]; | 
| 93       if (rule.type != rule.STYLE_RULE) | 119       if (rule.type != rule.STYLE_RULE) | 
| 94         continue; | 120         continue; | 
| 95 | 121 | 
| 96       var style = this.stringifyStyle(rule.style); | 122       var style = this.stringifyStyle(rule.style); | 
| 97       for (var j = 0; j < this.patterns.length; j++) | 123       for (var j = 0; j < this.patterns.length; j++) | 
| 98       { | 124       { | 
| 99         var pattern = this.patterns[j]; | 125         var pattern = this.patterns[j]; | 
| 100         if (pattern.regexp.test(style)) | 126         if (pattern.type == "prop" && pattern.regexp.test(style)) | 
| 101         { | 127         { | 
| 102           var subSelectors = splitSelector(rule.selectorText); | 128           var subSelectors = splitSelector(rule.selectorText); | 
| 103           for (var k = 0; k < subSelectors.length; k++) | 129           for (var k = 0; k < subSelectors.length; k++) | 
| 104           { | 130           { | 
| 105             var subSelector = subSelectors[k]; | 131             var subSelector = subSelectors[k]; | 
| 106             selectors.push(pattern.prefix + subSelector + pattern.suffix); | 132             selectors.push(pattern.prefix + subSelector + pattern.suffix); | 
| 107             filters.push(pattern.text); | 133             filters.push(pattern.text); | 
| 108           } | 134           } | 
| 109         } | 135         } | 
| 110       } | 136       } | 
| 111     } | 137     } | 
|  | 138     this.findPseudoClassHasSelectors(selectors, filters); | 
|  | 139   }, | 
|  | 140 | 
|  | 141   findPseudoClassHasSelectors: function(selectors, filters) | 
|  | 142   { | 
|  | 143     // XXX shall we split the patterns? | 
|  | 144     for (var i = 0; i < this.patterns.length; i++) | 
|  | 145     { | 
|  | 146       var pattern = this.patterns[i]; | 
|  | 147       if (pattern.type != ":has") | 
|  | 148         continue; | 
|  | 149 | 
|  | 150       var prefixes = document.querySelectorAll(pattern.prefix); | 
|  | 151       for (var j = 0; j < prefixes.length; j++) | 
|  | 152       { | 
|  | 153         var matched = pattern.elementMatcher.match(prefixes[j]); | 
|  | 154         if (matched.length == 0) | 
|  | 155         { | 
|  | 156           continue; | 
|  | 157         } | 
|  | 158         // XXX make sure we don't have performance problems here | 
|  | 159         matched.forEach(function(e) | 
|  | 160         { | 
|  | 161           var subSelector = e.id; | 
|  | 162           if (!subSelector) | 
|  | 163           { | 
|  | 164             var findUniqueId = function() | 
|  | 165             { | 
|  | 166               var id = "elemHideEmulationHide-" + | 
|  | 167                   Math.floor(Math.random() * 10000); | 
|  | 168               if (!document.getElementById(id)) | 
|  | 169                 return id; | 
|  | 170               return findUniqueId(); | 
|  | 171             }; | 
|  | 172             subSelector = findUniqueId(); | 
|  | 173             e.id = subSelector; | 
|  | 174           } | 
|  | 175           var newSelector = pattern.prefix ? pattern.prefix + "> " : ""; | 
|  | 176           newSelector += "#" + subSelector; | 
|  | 177           newSelector += pattern.suffix ? " > " + pattern.suffix : ""; | 
|  | 178           selectors.push(newSelector); | 
|  | 179           filters.push(pattern.text); | 
|  | 180         }); | 
|  | 181       } | 
|  | 182     } | 
| 112   }, | 183   }, | 
| 113 | 184 | 
| 114   addSelectors: function(stylesheets) | 185   addSelectors: function(stylesheets) | 
| 115   { | 186   { | 
| 116     var selectors = []; | 187     var selectors = []; | 
| 117     var filters = []; | 188     var filters = []; | 
| 118     for (var i = 0; i < stylesheets.length; i++) | 189     for (var i = 0; i < stylesheets.length; i++) | 
| 119       this.findSelectors(stylesheets[i], selectors, filters); | 190       this.findSelectors(stylesheets[i], selectors, filters); | 
| 120     this.addSelectorsFunc(selectors, filters); | 191     this.addSelectorsFunc(selectors, filters); | 
| 121   }, | 192   }, | 
| 122 | 193 | 
| 123   onLoad: function(event) | 194   onLoad: function(event) | 
| 124   { | 195   { | 
| 125     var stylesheet = event.target.sheet; | 196     var stylesheet = event.target.sheet; | 
| 126     if (stylesheet) | 197     if (stylesheet) | 
| 127       this.addSelectors([stylesheet]); | 198       this.addSelectors([stylesheet]); | 
| 128   }, | 199   }, | 
| 129 | 200 | 
| 130   apply: function() | 201   apply: function() | 
| 131   { | 202   { | 
| 132     this.getFiltersFunc(function(patterns) | 203     this.getFiltersFunc(function(patterns) | 
| 133     { | 204     { | 
| 134       this.patterns = []; | 205       this.patterns = []; | 
| 135       for (var i = 0; i < patterns.length; i++) | 206       for (var i = 0; i < patterns.length; i++) | 
| 136       { | 207       { | 
|  | 208         var regexpMatcher; | 
|  | 209         var elementMatcher; | 
| 137         var pattern = patterns[i]; | 210         var pattern = patterns[i]; | 
| 138         var match = propertySelectorRegExp.exec(pattern.selector); | 211         var match = propertySelectorRegExp.exec(pattern.selector); | 
| 139         if (!match) | 212         var type; | 
| 140           continue; | 213         if (match) | 
|  | 214         { | 
|  | 215           var regexpString; | 
|  | 216           var propertyExpression = match[2]; | 
|  | 217           if (propertyExpression.length >= 2 && propertyExpression[0] == "/" && | 
|  | 218               propertyExpression[propertyExpression.length - 1] == "/") | 
|  | 219             regexpString = propertyExpression.slice(1, -1) | 
|  | 220                 .replace("\\x7B ", "{").replace("\\x7D ", "}"); | 
|  | 221           else | 
|  | 222             regexpString = filterToRegExp(propertyExpression); | 
|  | 223           regexpMatcher = new RegExp(regexpString, "i"); | 
|  | 224           type = "prop"; | 
|  | 225         } | 
|  | 226         else | 
|  | 227         { | 
|  | 228           match = pseudoClassHasSelectorRegExp.exec(pattern.selector); | 
|  | 229           if (!match) | 
|  | 230             continue; | 
| 141 | 231 | 
| 142         var propertyExpression = match[2]; | 232           var pseudoHasSelector = match[1]; | 
| 143         var regexpString; | 233           elementMatcher = new PseudoHasMatcher(pseudoHasSelector); | 
| 144         if (propertyExpression.length >= 2 && propertyExpression[0] == "/" && | 234           type = ":has"; | 
| 145             propertyExpression[propertyExpression.length - 1] == "/") | 235         } | 
| 146           regexpString = propertyExpression.slice(1, -1) |  | 
| 147               .replace("\\x7B ", "{").replace("\\x7D ", "}"); |  | 
| 148         else |  | 
| 149           regexpString = filterToRegExp(propertyExpression); |  | 
| 150 | 236 | 
| 151         this.patterns.push({ | 237         this.patterns.push({ | 
|  | 238           type: type, | 
| 152           text: pattern.text, | 239           text: pattern.text, | 
| 153           regexp: new RegExp(regexpString, "i"), | 240           regexp: regexpMatcher, | 
|  | 241           elementMatcher: elementMatcher, | 
| 154           prefix: pattern.selector.substr(0, match.index), | 242           prefix: pattern.selector.substr(0, match.index), | 
| 155           suffix: pattern.selector.substr(match.index + match[0].length) | 243           suffix: pattern.selector.substr(match.index + match[0].length) | 
| 156         }); | 244         }); | 
| 157       } | 245       } | 
| 158 | 246 | 
| 159       if (this.patterns.length > 0) | 247       if (this.patterns.length > 0) | 
| 160       { | 248       { | 
| 161         var document = this.window.document; | 249         var document = this.window.document; | 
| 162         this.addSelectors(document.styleSheets); | 250         this.addSelectors(document.styleSheets); | 
| 163         document.addEventListener("load", this.onLoad.bind(this), true); | 251         document.addEventListener("load", this.onLoad.bind(this), true); | 
| 164       } | 252       } | 
| 165     }.bind(this)); | 253     }.bind(this)); | 
| 166   } | 254   } | 
| 167 }; | 255 }; | 
| OLD | NEW | 
|---|