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-2017 eyeo GmbH | 3 * Copyright (C) 2006-2017 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 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
64 } | 64 } |
65 } | 65 } |
66 | 66 |
67 function escapeRegExp(s) | 67 function escapeRegExp(s) |
68 { | 68 { |
69 return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); | 69 return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); |
70 } | 70 } |
71 | 71 |
72 function matchDomain(domain) | 72 function matchDomain(domain) |
73 { | 73 { |
| 74 if (!domain) |
| 75 return "^https?://"; |
| 76 |
74 return "^https?://([^/:]*\\.)?" + escapeRegExp(domain).toLowerCase() + "[/:]"; | 77 return "^https?://([^/:]*\\.)?" + escapeRegExp(domain).toLowerCase() + "[/:]"; |
75 } | 78 } |
76 | 79 |
77 function getURLSchemes(contentType) | 80 function getURLSchemes(contentType) |
78 { | 81 { |
79 // If the given content type includes all supported URL schemes, simply | 82 // If the given content type includes all supported URL schemes, simply |
80 // return a single generic URL scheme pattern. This minimizes the size of the | 83 // return a single generic URL scheme pattern. This minimizes the size of the |
81 // generated rule set. The downside to this is that it will also match | 84 // generated rule set. The downside to this is that it will also match |
82 // schemes that we do not want to match (e.g. "ftp://"), but this can be | 85 // schemes that we do not want to match (e.g. "ftp://"), but this can be |
83 // mitigated by adding exceptions for those schemes. | 86 // mitigated by adding exceptions for those schemes. |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
122 if (parsed.justHostname) | 125 if (parsed.justHostname) |
123 domains.add(parsed.hostname); | 126 domains.add(parsed.hostname); |
124 } | 127 } |
125 return domains; | 128 return domains; |
126 } | 129 } |
127 | 130 |
128 function convertElemHideFilter(filter, elemhideSelectorExceptions) | 131 function convertElemHideFilter(filter, elemhideSelectorExceptions) |
129 { | 132 { |
130 let included = []; | 133 let included = []; |
131 let excluded = []; | 134 let excluded = []; |
132 let rules = []; | |
133 | 135 |
134 parseDomains(filter.domains, included, excluded); | 136 parseDomains(filter.domains, included, excluded); |
135 | 137 |
136 if (excluded.length == 0 && !(filter.selector in elemhideSelectorExceptions)) | 138 if (excluded.length == 0 && !(filter.selector in elemhideSelectorExceptions)) |
137 return {matchDomains: included.map(matchDomain), selector: filter.selector}; | 139 return {matchDomains: included, selector: filter.selector}; |
138 } | 140 } |
139 | 141 |
140 /** | 142 /** |
141 * Parse the given filter "regexpSource" string. Producing a regular expression, | 143 * Parse the given filter "regexpSource" string. Producing a regular expression, |
142 * extracting the hostname (if any), deciding if the regular expression is safe | 144 * extracting the hostname (if any), deciding if the regular expression is safe |
143 * to be converted + matched as lower case and noting if the source contains | 145 * to be converted + matched as lower case and noting if the source contains |
144 * anything after the hostname.) | 146 * anything after the hostname.) |
145 * | 147 * |
146 * @param {string} text regexpSource property of a filter | 148 * @param {string} text regexpSource property of a filter |
147 * @param {string} urlScheme The URL scheme to use in the regular expression | 149 * @param {string} urlScheme The URL scheme to use in the regular expression |
(...skipping 431 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
579 { | 581 { |
580 newSelector.push(selector.substring(i, pos.start)); | 582 newSelector.push(selector.substring(i, pos.start)); |
581 newSelector.push('[id=', selector.substring(pos.start + 1, pos.end), ']'); | 583 newSelector.push('[id=', selector.substring(pos.start + 1, pos.end), ']'); |
582 i = pos.end; | 584 i = pos.end; |
583 } | 585 } |
584 newSelector.push(selector.substring(i)); | 586 newSelector.push(selector.substring(i)); |
585 | 587 |
586 return newSelector.join(""); | 588 return newSelector.join(""); |
587 } | 589 } |
588 | 590 |
589 function addCSSRules(rules, selectors, matchDomain, exceptionDomains) | 591 function addCSSRules(rules, selectors, domain, exceptionDomains) |
590 { | 592 { |
591 let unlessDomain = exceptionDomains.size > 0 ? [] : null; | 593 let unlessDomain = exceptionDomains.size > 0 ? [] : null; |
592 | 594 |
593 exceptionDomains.forEach(name => unlessDomain.push("*" + name)); | 595 exceptionDomains.forEach(name => |
| 596 { |
| 597 // For domain-specific filters, include the exception domains only if |
| 598 // they're subdomains of the given domain. |
| 599 if (!domain || name.substr(-domain.length - 1) == "." + domain) |
| 600 unlessDomain.push("*" + name); |
| 601 }); |
594 | 602 |
595 while (selectors.length) | 603 while (selectors.length) |
596 { | 604 { |
597 let selector = selectors.splice(0, selectorLimit).join(", "); | 605 let selector = selectors.splice(0, selectorLimit).join(", "); |
598 | 606 |
599 // As of Safari 9.0 element IDs are matched as lowercase. We work around | 607 // As of Safari 9.0 element IDs are matched as lowercase. We work around |
600 // this by converting to the attribute format [id="elementID"] | 608 // this by converting to the attribute format [id="elementID"] |
601 selector = convertIDSelectorsToAttributeSelectors(selector); | 609 selector = convertIDSelectorsToAttributeSelectors(selector); |
602 | 610 |
603 let rule = { | 611 let rule = { |
604 trigger: {"url-filter": matchDomain, | 612 trigger: {"url-filter": matchDomain(domain), |
605 "url-filter-is-case-sensitive": true}, | 613 "url-filter-is-case-sensitive": true}, |
606 action: {type: "css-display-none", | 614 action: {type: "css-display-none", |
607 selector: selector} | 615 selector: selector} |
608 }; | 616 }; |
609 | 617 |
610 if (unlessDomain) | 618 if (unlessDomain) |
611 rule.trigger["unless-domain"] = unlessDomain; | 619 rule.trigger["unless-domain"] = unlessDomain; |
612 | 620 |
613 rules.push(rule); | 621 rules.push(rule); |
614 } | 622 } |
(...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
721 // --max_old_space_size=4096 | 729 // --max_old_space_size=4096 |
722 let elemhideExceptionDomains = extractFilterDomains(this.elemhideExceptions); | 730 let elemhideExceptionDomains = extractFilterDomains(this.elemhideExceptions); |
723 | 731 |
724 let genericSelectorExceptionDomains = | 732 let genericSelectorExceptionDomains = |
725 extractFilterDomains(this.generichideExceptions); | 733 extractFilterDomains(this.generichideExceptions); |
726 elemhideExceptionDomains.forEach(name => | 734 elemhideExceptionDomains.forEach(name => |
727 { | 735 { |
728 genericSelectorExceptionDomains.add(name); | 736 genericSelectorExceptionDomains.add(name); |
729 }); | 737 }); |
730 | 738 |
731 addCSSRules(rules, genericSelectors, "^https?://", | 739 addCSSRules(rules, genericSelectors, null, genericSelectorExceptionDomains); |
732 genericSelectorExceptionDomains); | 740 |
| 741 // Filter out whitelisted domains. |
| 742 elemhideExceptionDomains.forEach(domain => |
| 743 groupedElemhideFilters.delete(domain)); |
733 | 744 |
734 groupedElemhideFilters.forEach((selectors, matchDomain) => | 745 groupedElemhideFilters.forEach((selectors, matchDomain) => |
735 { | 746 { |
736 addCSSRules(rules, selectors, matchDomain, elemhideExceptionDomains); | 747 addCSSRules(rules, selectors, matchDomain, elemhideExceptionDomains); |
737 }); | 748 }); |
738 | 749 |
739 let requestFilterExceptionDomains = []; | 750 let requestFilterExceptionDomains = []; |
740 for (let filter of this.genericblockExceptions) | 751 for (let filter of this.genericblockExceptions) |
741 { | 752 { |
742 let parsed = parseFilterRegexpSource(filter.regexpSource); | 753 let parsed = parseFilterRegexpSource(filter.regexpSource); |
743 if (parsed.hostname) | 754 if (parsed.hostname) |
744 requestFilterExceptionDomains.push(parsed.hostname); | 755 requestFilterExceptionDomains.push(parsed.hostname); |
745 } | 756 } |
746 | 757 |
747 for (let filter of this.requestFilters) | 758 for (let filter of this.requestFilters) |
748 { | 759 { |
749 convertFilterAddRules(rules, filter, "block", true, | 760 convertFilterAddRules(rules, filter, "block", true, |
750 requestFilterExceptionDomains); | 761 requestFilterExceptionDomains); |
751 } | 762 } |
752 | 763 |
753 for (let filter of this.requestExceptions) | 764 for (let filter of this.requestExceptions) |
754 convertFilterAddRules(rules, filter, "ignore-previous-rules", true); | 765 convertFilterAddRules(rules, filter, "ignore-previous-rules", true); |
755 | 766 |
756 return rules; | 767 return rules; |
757 }; | 768 }; |
OLD | NEW |