| 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 = []; |
| + for (let filter of filters) |
| + { |
| + let parsed = parseFilterRegexpSource(filter.regexpSource); |
| + if (parsed.justHostname) |
| + domains.push(parsed.hostname); |
| + } |
| + return domains; |
|
kzar
2017/07/07 11:40:13
Why not make domains a Set instead of an Array her
Manish Jethani
2017/07/08 05:33:59
That's a good point, it does seem to make a huge d
|
| +} |
| + |
| function convertElemHideFilter(filter, elemhideSelectorExceptions) |
| { |
| let included = []; |
| let excluded = []; |
| let rules = []; |
| parseDomains(filter.domains, included, excluded); |
| @@ -128,17 +140,17 @@ |
| if (hostnameStart != null && !hostnameFinished) |
| { |
| let endingChar = (c == "*" || c == "^" || |
| c == "?" || c == "/" || c == "|"); |
| if (!endingChar && i != lastIndex) |
| continue; |
| hostname = punycode.toASCII( |
| - text.substring(hostnameStart, endingChar ? i : i + 1) |
| + text.substring(hostnameStart, endingChar ? i : i + 1).toLowerCase() |
|
Manish Jethani
2017/06/24 14:54:15
punycode.toASCII doesn't lower-case the string, we
|
| ); |
| hostnameFinished = justHostname = true; |
| regexp.push(escapeRegExp(hostname)); |
| if (!endingChar) |
| break; |
| } |
| switch (c) |
| @@ -395,32 +407,39 @@ |
| 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) |
| { |
| + exceptionDomains = Array.from(new Set(exceptionDomains)); |
|
Manish Jethani
2017/06/24 14:54:15
Ensure no duplicates.
|
| + |
| 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 (exceptionDomains.length > 0) |
| + rule.trigger["unless-domain"] = exceptionDomains.map(name => "*" + name); |
|
kzar
2017/07/07 11:40:13
Maybe we should do this work outside of the while
Manish Jethani
2017/07/08 05:33:59
We have to make a copy of the array as a rule, bec
kzar
2017/07/10 12:33:07
I'd rather you did the work outside the loop here
Manish Jethani
2017/07/11 11:19:18
Done.
|
| + |
| + rules.push(rule); |
| } |
| } |
| let ContentBlockerList = |
| /** |
| * Create a new Adblock Plus filter to content blocker list converter |
| * |
| * @constructor |
| @@ -507,31 +526,39 @@ |
| { |
| 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 |
|
kzar
2017/07/07 11:40:13
Mind giving the full URL for the WebKit bug?
Manish Jethani
2017/07/08 05:33:59
Done.
By the way, there are new comments on that
kzar
2017/07/10 12:33:08
Acknowledged.
|
| + // 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. |
| + // |
| + // 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 |
|
kzar
2017/07/07 11:40:13
Have you tested that rule generation still works O
Manish Jethani
2017/07/08 05:33:59
I tested it there and it works without problems.
kzar
2017/07/10 12:33:07
Acknowledged.
|
| + // "JavaScript heap out of memory". To avoid this, call Node.js with |
| + // --max_old_space_size=4096 |
| + let generichideExceptionDomains = |
| + extractFilterDomains(this.generichideExceptions); |
| + let elemhideExceptionDomains = extractFilterDomains(this.elemhideExceptions); |
| - // Right after the generic element hiding filters, add the exceptions that |
|
Manish Jethani
2017/06/24 14:54:15
We could continue generating individual rules for
kzar
2017/07/07 11:40:13
Maybe add a comment explaining what needs to chang
|
| - // should apply only to those filters. |
| - for (let filter of this.generichideExceptions) |
| - convertFilterAddRules(rules, filter, "ignore-previous-rules", false); |
| + addCSSRules(rules, genericSelectors, "^https?://", |
| + generichideExceptionDomains.concat(elemhideExceptionDomains)); |
| 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); |
| } |