 Issue 29473555:
  Issue 5345 - Whitelist $elemhide and $generichide domains where possible  (Closed) 
  Base URL: https://hg.adblockplus.org/abp2blocklist
    
  
    Issue 29473555:
  Issue 5345 - Whitelist $elemhide and $generichide domains where possible  (Closed) 
  Base URL: https://hg.adblockplus.org/abp2blocklist| Index: lib/abp2blocklist.js | 
| =================================================================== | 
| --- a/lib/abp2blocklist.js | 
| +++ b/lib/abp2blocklist.js | 
| @@ -73,16 +73,28 @@ | 
| { | 
| if (name.length > suffixLength && name.slice(-suffixLength) == "." + domain) | 
| subdomains.push(name.slice(0, -suffixLength)); | 
| } | 
| return subdomains; | 
| } | 
| +function extractFilterDomains(filters) | 
| +{ | 
| + let domains = new Set(); | 
| + for (let filter of filters) | 
| + { | 
| + let parsed = parseFilterRegexpSource(filter.regexpSource); | 
| + if (parsed.justHostname) | 
| + domains.add(parsed.hostname); | 
| + } | 
| + return domains; | 
| +} | 
| + | 
| function convertElemHideFilter(filter, elemhideSelectorExceptions) | 
| { | 
| let included = []; | 
| let excluded = []; | 
| let rules = []; | 
| parseDomains(filter.domains, included, excluded); | 
| @@ -138,16 +150,17 @@ | 
| { | 
| let endingChar = (c == "*" || c == "^" || | 
| c == "?" || c == "/" || c == "|"); | 
| if (!endingChar && i != lastIndex) | 
| continue; | 
| hostname = punycode.toASCII( | 
| characters.slice(hostnameStart, endingChar ? i : i + 1).join("") | 
| 
Manish Jethani
2017/07/12 08:59:56
This was the only conflict during rebase, since te
 | 
| + .toLowerCase() | 
| ); | 
| hostnameFinished = justHostname = true; | 
| regexp.push(escapeRegExp(hostname)); | 
| if (!endingChar) | 
| break; | 
| } | 
| switch (c) | 
| @@ -399,32 +412,41 @@ | 
| newSelector.push('[id=', selector.substring(pos.start + 1, pos.end), ']'); | 
| i = pos.end; | 
| } | 
| newSelector.push(selector.substring(i)); | 
| return newSelector.join(""); | 
| } | 
| -function addCSSRules(rules, selectors, matchDomain) | 
| +function addCSSRules(rules, selectors, matchDomain, exceptionDomains) | 
| { | 
| + let unlessDomain = exceptionDomains.size > 0 ? [] : null; | 
| + | 
| + exceptionDomains.forEach(name => unlessDomain.push("*" + name)); | 
| + | 
| while (selectors.length) | 
| { | 
| let selector = selectors.splice(0, selectorLimit).join(", "); | 
| // As of Safari 9.0 element IDs are matched as lowercase. We work around | 
| // this by converting to the attribute format [id="elementID"] | 
| selector = convertIDSelectorsToAttributeSelectors(selector); | 
| - rules.push({ | 
| + let rule = { | 
| trigger: {"url-filter": matchDomain, | 
| "url-filter-is-case-sensitive": true}, | 
| action: {type: "css-display-none", | 
| selector: selector} | 
| - }); | 
| + }; | 
| + | 
| + if (unlessDomain) | 
| + rule.trigger["unless-domain"] = unlessDomain; | 
| + | 
| + rules.push(rule); | 
| } | 
| } | 
| let ContentBlockerList = | 
| /** | 
| * Create a new Adblock Plus filter to content blocker list converter | 
| * | 
| * @constructor | 
| @@ -511,31 +533,45 @@ | 
| { | 
| let group = groupedElemhideFilters.get(matchDomain) || []; | 
| group.push(result.selector); | 
| groupedElemhideFilters.set(matchDomain, group); | 
| } | 
| } | 
| } | 
| - addCSSRules(rules, genericSelectors, "^https?://"); | 
| + // Separate out the element hiding exceptions that have only a hostname part | 
| + // from the rest. This allows us to implement a workaround for issue #5345 | 
| + // (WebKit bug #167423), but as a bonus it also reduces the number of | 
| + // generated rules. The downside is that the exception will only apply to the | 
| + // top-level document, not to iframes. We have to live with this until the | 
| + // WebKit bug is fixed in all supported versions of Safari. | 
| + // https://bugs.webkit.org/show_bug.cgi?id=167423 | 
| + // | 
| + // Note that as a result of this workaround we end up with a huge rule set in | 
| + // terms of the amount of memory used. This can cause Node.js to throw | 
| + // "JavaScript heap out of memory". To avoid this, call Node.js with | 
| + // --max_old_space_size=4096 | 
| + let elemhideExceptionDomains = extractFilterDomains(this.elemhideExceptions); | 
| - // Right after the generic element hiding filters, add the exceptions that | 
| - // should apply only to those filters. | 
| - for (let filter of this.generichideExceptions) | 
| - convertFilterAddRules(rules, filter, "ignore-previous-rules", false); | 
| + let genericSelectorExceptionDomains = | 
| + extractFilterDomains(this.generichideExceptions); | 
| + elemhideExceptionDomains.forEach(name => | 
| + { | 
| + genericSelectorExceptionDomains.add(name); | 
| + }); | 
| + | 
| + addCSSRules(rules, genericSelectors, "^https?://", | 
| + genericSelectorExceptionDomains); | 
| groupedElemhideFilters.forEach((selectors, matchDomain) => | 
| { | 
| - addCSSRules(rules, selectors, matchDomain); | 
| + addCSSRules(rules, selectors, matchDomain, elemhideExceptionDomains); | 
| }); | 
| - for (let filter of this.elemhideExceptions) | 
| - convertFilterAddRules(rules, filter, "ignore-previous-rules", false); | 
| - | 
| let requestFilterExceptionDomains = []; | 
| for (let filter of this.genericblockExceptions) | 
| { | 
| let parsed = parseFilterRegexpSource(filter.regexpSource); | 
| if (parsed.hostname) | 
| requestFilterExceptionDomains.push(parsed.hostname); | 
| } |