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

Side by Side Diff: lib/abp2blocklist.js

Issue 29426594: Issue 3673 - Merge closely matching rules (Closed) Base URL: https://hg.adblockplus.org/abp2blocklist
Patch Set: Map async callees asynchronously Created July 28, 2017, 9:07 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 | « abp2blocklist.js ('k') | test/abp2blocklist.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 /* 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 29 matching lines...) Expand all
40 const rawRequestTypes = typeMap.XMLHTTPREQUEST | 40 const rawRequestTypes = typeMap.XMLHTTPREQUEST |
41 typeMap.WEBSOCKET | 41 typeMap.WEBSOCKET |
42 typeMap.WEBRTC | 42 typeMap.WEBRTC |
43 typeMap.OBJECT_SUBREQUEST | 43 typeMap.OBJECT_SUBREQUEST |
44 typeMap.PING | 44 typeMap.PING |
45 typeMap.OTHER; 45 typeMap.OTHER;
46 const whitelistableRequestTypes = httpRequestTypes | 46 const whitelistableRequestTypes = httpRequestTypes |
47 typeMap.WEBSOCKET | 47 typeMap.WEBSOCKET |
48 typeMap.WEBRTC; 48 typeMap.WEBRTC;
49 49
50 function callLater(func)
51 {
52 return new Promise(resolve =>
53 {
54 let call = () => resolve(func());
55
56 // If this looks like Node.js, call process.nextTick, otherwise call
57 // setTimeout.
58 if (typeof process != "undefined")
59 process.nextTick(call);
60 else
61 setTimeout(call, 0);
62 });
63 }
64
65 function async(callees, mapFunction)
66 {
67 if (!(Symbol.iterator in callees))
68 callees = [callees];
69
70 let lastPause = Date.now();
71 let index = 0;
72
73 let promise = Promise.resolve();
74
75 for (let next of callees)
76 {
77 let currentIndex = index;
78
79 promise = promise.then(() =>
80 {
81 if (mapFunction)
82 next = mapFunction(next, currentIndex);
83
84 // If it has been 100ms or longer since the last call, take a pause. This
85 // keeps the browser from freezing up.
86 let now = Date.now();
87 if (now - lastPause >= 100)
88 {
89 lastPause = now;
90 return callLater(next);
91 }
92
93 return next();
94 });
95
96 index++;
97 }
98
99 return promise;
100 }
101
50 function parseDomains(domains, included, excluded) 102 function parseDomains(domains, included, excluded)
51 { 103 {
52 for (let domain in domains) 104 for (let domain in domains)
53 { 105 {
54 if (domain != "") 106 if (domain != "")
55 { 107 {
56 let enabled = domains[domain]; 108 let enabled = domains[domain];
57 domain = punycode.toASCII(domain.toLowerCase()); 109 domain = punycode.toASCII(domain.toLowerCase());
58 110
59 if (!enabled) 111 if (!enabled)
(...skipping 547 matching lines...) Expand 10 before | Expand all | Expand 10 after
607 selector: selector} 659 selector: selector}
608 }; 660 };
609 661
610 if (unlessDomain) 662 if (unlessDomain)
611 rule.trigger["unless-domain"] = unlessDomain; 663 rule.trigger["unless-domain"] = unlessDomain;
612 664
613 rules.push(rule); 665 rules.push(rule);
614 } 666 }
615 } 667 }
616 668
669 /**
670 * Check if two strings are a close match
671 *
672 * This function returns an edit operation, one of "substitute", "delete", and
673 * "insert", along with an index in the source string where the edit must occur
674 * in order to arrive at the target string. If the strings are not a close
675 * match, it returns null.
676 *
677 * Two strings are considered to be a close match if they are one edit
678 * operation apart.
679 *
680 * Deletions or insertions of a contiguous range of characters from one string
681 * into the other, at the same index, are treated as a single edit. For
682 * example, "internal" and "international" are considered to be one edit apart
683 * and therefore a close match.
684 *
685 * A few things to note:
686 *
687 * 1) This function does not care about the format of the input strings. For
688 * example, the caller may pass in regular expressions, where "[ab]" and
689 * "[bc]" could be considered to be a close match, since the order within the
690 * brackets doesn't matter. This function will still return null for this set
691 * of inputs since they are two edits apart.
692 *
693 * 2) To be friendly to calling code that might be passing in regular
694 * expressions, this function will simply return null if it encounters a
695 * special character (e.g. "\", "?", "+", etc.) in the delta. For example,
696 * given "Hello" and "Hello, how are you?", it will return null.
697 *
698 * 3) If the caller does indeed pass in regular expressions, it must make the
699 * important assumption that the parts where two such regular expressions may
700 * differ can always be treated as normal strings. For example,
701 * "^https?://example.com/ads" and "^https?://example.com/adv" differ only in
702 * the last character, therefore the regular expressions can safely be merged
703 * into "^https?://example.com/ad[sv]".
704 *
705 * @param {string} s The source string
706 * @param {string} t The target string
707 *
708 * @returns {object} An object describing the single edit operation that must
709 * occur in the source string in order to arrive at the
710 * target string
711 */
712 function closeMatch(s, t)
713 {
714 let diff = s.length - t.length;
715
716 // If target is longer than source, swap them for the purpose of our
717 // calculation.
718 if (diff < 0)
719 {
720 let tmp = s;
721 s = t;
722 t = tmp;
723 }
724
725 let edit = null;
726
727 let i = 0;
728 let j = 0;
729
730 // Start from the beginning and keep going until we hit a character that
731 // doesn't match.
732 for (; i < s.length; i++)
733 {
734 if (s[i] != t[i])
735 break;
736 }
737
738 // Now do exactly the same from the end, but also stop if we reach the
739 // position where we terminated the previous loop.
740 for (; j < t.length; j++)
741 {
742 if (t.length - j == i || s[s.length - j - 1] != t[t.length - j - 1])
743 break;
744 }
745
746 if (diff == 0)
747 {
748 // If the strings are equal in length and the delta isn't exactly one
749 // character, it's not a close match.
750 if (t.length - j - i != 1)
751 return null;
752 }
753 else if (i != t.length - j)
754 {
755 // For strings of unequal length, if we haven't found a match for every
756 // single character in the shorter string counting from both the beginning
757 // and the end, it's not a close match.
758 return null;
759 }
760
761 for (let k = i; k < s.length - j; k++)
762 {
763 // If the delta contains any special characters, it's not a close match.
764 if (s[k] == "." || s[k] == "+" || s[k] == "$" || s[k] == "?" ||
765 s[k] == "{" || s[k] == "}" || s[k] == "(" || s[k] == ")" ||
766 s[k] == "[" || s[k] == "]" || s[k] == "\\")
767 return null;
768 }
769
770 if (diff == 0)
771 {
772 edit = {type: "substitute", index: i};
773 }
774 else if (diff > 0)
775 {
776 edit = {type: "delete", index: i};
777
778 if (diff > 1)
779 edit.endIndex = s.length - j;
780 }
781 else
782 {
783 edit = {type: "insert", index: i};
784
785 if (diff < -1)
786 edit.endIndex = s.length - j;
787 }
788
789 return edit;
790 }
791
792 function eliminateRedundantRulesByURLFilter(rulesInfo, exhaustive)
793 {
794 const heuristicRange = 1000;
795
796 let ol = rulesInfo.length;
797
798 // Throw out obviously redundant rules.
799 return async(rulesInfo, (ruleInfo, index) => () =>
800 {
801 // If this rule is already marked as redundant, don't bother comparing it
802 // with other rules.
803 if (rulesInfo[index].redundant)
804 return;
805
806 let limit = exhaustive ? rulesInfo.length :
807 Math.min(index + heuristicRange, rulesInfo.length);
808
809 for (let i = index, j = i + 1; j < limit; j++)
810 {
811 if (rulesInfo[j].redundant)
812 continue;
813
814 let source = rulesInfo[i].rule.trigger["url-filter"];
815 let target = rulesInfo[j].rule.trigger["url-filter"];
816
817 if (source.length >= target.length)
818 {
819 // If one URL filter is a substring of the other starting at the
820 // beginning, the other one is clearly redundant.
821 if (source.substring(0, target.length) == target)
822 {
823 rulesInfo[i].redundant = true;
824 break;
825 }
826 }
827 else if (target.substring(0, source.length) == source)
828 {
829 rulesInfo[j].redundant = true;
830 }
831 }
832 })
833 .then(() => rulesInfo.filter(ruleInfo => !ruleInfo.redundant));
834 }
835
836 function findMatchesForRuleByURLFilter(rulesInfo, index, exhaustive)
837 {
838 // Closely matching rules are likely to be within a certain range. We only
839 // look for matches within this range by default. If we increase this value,
840 // it can give us more matches and a smaller resulting rule set, but possibly
841 // at a significant performance cost.
842 //
843 // If the exhaustive option is true, we simply ignore this value and look for
844 // matches throughout the rule set.
845 const heuristicRange = 1000;
846
847 let limit = exhaustive ? rulesInfo.length :
848 Math.min(index + heuristicRange, rulesInfo.length);
849
850 for (let i = index, j = i + 1; j < limit; j++)
851 {
852 let source = rulesInfo[i].rule.trigger["url-filter"];
853 let target = rulesInfo[j].rule.trigger["url-filter"];
854
855 let edit = closeMatch(source, target);
856
857 if (edit)
858 {
859 let urlFilter, ruleInfo, match = {edit};
860
861 if (edit.type == "insert")
862 {
863 // Convert the insertion into a deletion and stick it on the target
864 // rule instead. We can only group deletions and substitutions;
865 // therefore insertions must be treated as deletions on the target
866 // rule.
867 urlFilter = target;
868 ruleInfo = rulesInfo[j];
869 match.index = i;
870 edit.type = "delete";
871 }
872 else
873 {
874 urlFilter = source;
875 ruleInfo = rulesInfo[i];
876 match.index = j;
877 }
878
879 // If the edit has an end index, it represents a multiple character
880 // edit.
881 let multiEdit = !!edit.endIndex;
882
883 if (multiEdit)
884 {
885 // We only care about a single multiple character edit because the
886 // number of characters for such a match doesn't matter, we can
887 // only merge with one other rule.
888 if (!ruleInfo.multiEditMatch)
889 ruleInfo.multiEditMatch = match;
890 }
891 else
892 {
893 // For single character edits, multiple rules can be merged into
894 // one. e.g. "ad", "ads", and "adv" can be merged into "ad[sv]?".
895 if (!ruleInfo.matches)
896 ruleInfo.matches = new Array(urlFilter.length);
897
898 // Matches at a particular index. For example, for a source string
899 // "ads", both target strings "ad" (deletion) and "adv"
900 // (substitution) match at index 2, hence they are grouped together
901 // to possibly be merged later into "ad[sv]?".
902 let matchesForIndex = ruleInfo.matches[edit.index];
903
904 if (matchesForIndex)
905 {
906 matchesForIndex.push(match);
907 }
908 else
909 {
910 matchesForIndex = [match];
911 ruleInfo.matches[edit.index] = matchesForIndex;
912 }
913
914 // Keep track of the best set of matches. We later sort by this to
915 // get best results.
916 if (!ruleInfo.bestMatches ||
917 matchesForIndex.length > ruleInfo.bestMatches.length)
918 ruleInfo.bestMatches = matchesForIndex;
919 }
920 }
921 }
922 }
923
924 function mergeCandidateRulesByURLFilter(rulesInfo)
925 {
926 // Filter out rules that have no matches at all.
927 let candidateRulesInfo = rulesInfo.filter(ruleInfo =>
928 {
929 return ruleInfo.bestMatches || ruleInfo.multiEditMatch
930 });
931
932 // For best results, we have to sort the candidates by the largest set of
933 // matches.
934 //
935 // For example, we want "ads", "bds", "adv", "bdv", "adx", and "bdx" to
936 // generate "ad[svx]" and "bd[svx]" (2 rules), not "[ab]ds", "[ab]dv", and
937 // "[ab]dx" (3 rules).
938 candidateRulesInfo.sort((ruleInfo1, ruleInfo2) =>
939 {
940 let weight1 = ruleInfo1.bestMatches ? ruleInfo1.bestMatches.length :
941 ruleInfo1.multiEditMatch ? 1 : 0;
942 let weight2 = ruleInfo2.bestMatches ? ruleInfo2.bestMatches.length :
943 ruleInfo2.multiEditMatch ? 1 : 0;
944
945 return weight2 - weight1;
946 });
947
948 for (let ruleInfo of candidateRulesInfo)
949 {
950 let rule = ruleInfo.rule;
951
952 // If this rule has already been merged into another rule, we skip it.
953 if (ruleInfo.merged)
954 continue;
955
956 // Find the best set of rules to group, which is simply the largest set.
957 let best = (ruleInfo.matches || []).reduce((best, matchesForIndex) =>
958 {
959 matchesForIndex = (matchesForIndex || []).filter(match =>
960 {
961 // Filter out rules that have either already been merged into other
962 // rules or have had other rules merged into them.
963 return !rulesInfo[match.index].merged &&
964 !rulesInfo[match.index].mergedInto;
965 });
966
967 return matchesForIndex.length > best.length ? matchesForIndex : best;
968 },
969 []);
970
971 let multiEdit = false;
972
973 // If we couldn't find a single rule to merge with, let's see if we have a
974 // multiple character edit. e.g. we could merge "ad" and "adserver" into
975 // "ad(server)?".
976 if (best.length == 0 && ruleInfo.multiEditMatch &&
977 !rulesInfo[ruleInfo.multiEditMatch.index].merged &&
978 !rulesInfo[ruleInfo.multiEditMatch.index].mergedInto)
979 {
980 best = [ruleInfo.multiEditMatch];
981 multiEdit = true;
982 }
983
984 if (best.length > 0)
985 {
986 let urlFilter = rule.trigger["url-filter"];
987
988 let editIndex = best[0].edit.index;
989
990 if (!multiEdit)
991 {
992 // Merge all the matching rules into this one.
993
994 let characters = [urlFilter[editIndex]];
995 let quantifier = "";
996
997 for (let match of best)
998 {
999 if (match.edit.type == "delete")
1000 {
1001 quantifier = "?";
1002 }
1003 else
1004 {
1005 let character = rulesInfo[match.index].rule
1006 .trigger["url-filter"][editIndex];
1007
1008 // Insert any hyphen at the beginning so it gets interpreted as a
1009 // literal hyphen.
1010 if (character == "-")
1011 characters.unshift(character);
1012 else
1013 characters.push(character);
1014 }
1015
1016 // Mark the target rule as merged so other rules don't try to merge
1017 // it again.
1018 rulesInfo[match.index].merged = true;
1019 }
1020
1021 urlFilter = urlFilter.substring(0, editIndex + 1) + quantifier +
1022 urlFilter.substring(editIndex + 1);
1023 if (characters.length > 1)
1024 {
1025 urlFilter = urlFilter.substring(0, editIndex) + "[" +
1026 characters.join("") + "]" +
1027 urlFilter.substring(editIndex + 1);
1028 }
1029 }
1030 else
1031 {
1032 let editEndIndex = best[0].edit.endIndex;
1033
1034 // Mark the target rule as merged so other rules don't try to merge it
1035 // again.
1036 rulesInfo[best[0].index].merged = true;
1037
1038 urlFilter = urlFilter.substring(0, editIndex) + "(" +
1039 urlFilter.substring(editIndex, editEndIndex) + ")?" +
1040 urlFilter.substring(editEndIndex);
1041 }
1042
1043 rule.trigger["url-filter"] = urlFilter;
1044
1045 // Mark this rule as one that has had other rules merged into it.
1046 ruleInfo.mergedInto = true;
1047 }
1048 }
1049 }
1050
1051 function mergeRulesByURLFilter(rulesInfo, exhaustive)
1052 {
1053 return async(rulesInfo, (ruleInfo, index) => () =>
1054 findMatchesForRuleByURLFilter(rulesInfo, index, exhaustive)
1055 )
1056 .then(() => mergeCandidateRulesByURLFilter(rulesInfo));
1057 }
1058
1059 function mergeRulesByArrayProperty(rulesInfo, propertyType, property)
1060 {
1061 if (rulesInfo.length <= 1)
1062 return;
1063
1064 let valueSet = new Set(rulesInfo[0].rule[propertyType][property]);
1065
1066 for (let i = 1; i < rulesInfo.length; i++)
1067 {
1068 for (let value of rulesInfo[i].rule[propertyType][property] || [])
1069 valueSet.add(value);
1070
1071 rulesInfo[i].merged = true;
1072 }
1073
1074 if (valueSet.size > 0)
1075 rulesInfo[0].rule[propertyType][property] = Array.from(valueSet);
1076
1077 rulesInfo[0].mergedInto = true;
1078 }
1079
1080 function groupRulesByMergeableProperty(rulesInfo, propertyType, property)
1081 {
1082 let mergeableRulesInfoByGroup = new Map();
1083
1084 for (let ruleInfo of rulesInfo)
1085 {
1086 let copy = {
1087 trigger: Object.assign({}, ruleInfo.rule.trigger),
1088 action: Object.assign({}, ruleInfo.rule.action)
1089 };
1090
1091 delete copy[propertyType][property];
1092
1093 let groupKey = JSON.stringify(copy);
1094
1095 let mergeableRulesInfo = mergeableRulesInfoByGroup.get(groupKey);
1096
1097 if (mergeableRulesInfo)
1098 mergeableRulesInfo.push(ruleInfo);
1099 else
1100 mergeableRulesInfoByGroup.set(groupKey, [ruleInfo]);
1101 }
1102
1103 return mergeableRulesInfoByGroup;
1104 }
1105
1106 function mergeRules(rules, exhaustive)
1107 {
1108 let rulesInfo = rules.map(rule => ({rule}));
1109
1110 let arrayPropertiesToMergeBy = ["resource-type", "if-domain"];
1111
1112 return async(() =>
1113 {
1114 let map = groupRulesByMergeableProperty(rulesInfo, "trigger", "url-filter");
1115 return async(map.values(), mergeableRulesInfo => () =>
1116 eliminateRedundantRulesByURLFilter(mergeableRulesInfo, exhaustive)
1117 .then(rulesInfo => mergeRulesByURLFilter(rulesInfo, exhaustive))
1118 )
1119 .then(() =>
1120 {
1121 // Filter out rules that are redundant or have been merged into other
1122 // rules.
1123 rulesInfo = rulesInfo.filter(ruleInfo => !ruleInfo.redundant &&
1124 !ruleInfo.merged);
1125 });
1126 })
1127 .then(() => async(arrayPropertiesToMergeBy, arrayProperty => () =>
1128 {
1129 let map = groupRulesByMergeableProperty(rulesInfo, "trigger",
1130 arrayProperty);
1131 return async(map.values(), mergeableRulesInfo => () =>
1132 mergeRulesByArrayProperty(mergeableRulesInfo, "trigger", arrayProperty)
1133 )
1134 .then(() =>
1135 {
1136 rulesInfo = rulesInfo.filter(ruleInfo => !ruleInfo.merged);
1137 });
1138 }))
1139 .then(() => rulesInfo.map(ruleInfo => ruleInfo.rule));
1140 }
1141
617 let ContentBlockerList = 1142 let ContentBlockerList =
618 /** 1143 /**
619 * Create a new Adblock Plus filter to content blocker list converter 1144 * Create a new Adblock Plus filter to content blocker list converter
620 * 1145 *
1146 * @param {object} options Options for content blocker list generation
1147 *
621 * @constructor 1148 * @constructor
622 */ 1149 */
623 exports.ContentBlockerList = function () 1150 exports.ContentBlockerList = function (options)
624 { 1151 {
1152 const defaultOptions = {
1153 merge: "auto"
1154 };
1155
1156 this.options = Object.assign({}, defaultOptions, options);
1157
625 this.requestFilters = []; 1158 this.requestFilters = [];
626 this.requestExceptions = []; 1159 this.requestExceptions = [];
627 this.elemhideFilters = []; 1160 this.elemhideFilters = [];
628 this.elemhideExceptions = []; 1161 this.elemhideExceptions = [];
629 this.genericblockExceptions = []; 1162 this.genericblockExceptions = [];
630 this.generichideExceptions = []; 1163 this.generichideExceptions = [];
631 this.elemhideSelectorExceptions = new Map(); 1164 this.elemhideSelectorExceptions = new Map();
632 }; 1165 };
633 1166
634 /** 1167 /**
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
669 let domains = this.elemhideSelectorExceptions[filter.selector]; 1202 let domains = this.elemhideSelectorExceptions[filter.selector];
670 if (!domains) 1203 if (!domains)
671 domains = this.elemhideSelectorExceptions[filter.selector] = []; 1204 domains = this.elemhideSelectorExceptions[filter.selector] = [];
672 1205
673 parseDomains(filter.domains, domains, []); 1206 parseDomains(filter.domains, domains, []);
674 } 1207 }
675 }; 1208 };
676 1209
677 /** 1210 /**
678 * Generate content blocker list for all filters that were added 1211 * Generate content blocker list for all filters that were added
679 *
680 * @returns {Filter} filter Filter to convert
681 */ 1212 */
682 ContentBlockerList.prototype.generateRules = function(filter) 1213 ContentBlockerList.prototype.generateRules = function()
683 { 1214 {
684 let rules = []; 1215 let cssRules = [];
1216 let cssExceptionRules = [];
1217 let blockingRules = [];
1218 let blockingExceptionRules = [];
1219
1220 let ruleGroups = [cssRules, cssExceptionRules,
1221 blockingRules, blockingExceptionRules];
685 1222
686 let genericSelectors = []; 1223 let genericSelectors = [];
687 let groupedElemhideFilters = new Map(); 1224 let groupedElemhideFilters = new Map();
688 1225
689 for (let filter of this.elemhideFilters) 1226 for (let filter of this.elemhideFilters)
690 { 1227 {
691 let result = convertElemHideFilter(filter, this.elemhideSelectorExceptions); 1228 let result = convertElemHideFilter(filter, this.elemhideSelectorExceptions);
692 if (!result) 1229 if (!result)
693 continue; 1230 continue;
694 1231
(...skipping 26 matching lines...) Expand all
721 // --max_old_space_size=4096 1258 // --max_old_space_size=4096
722 let elemhideExceptionDomains = extractFilterDomains(this.elemhideExceptions); 1259 let elemhideExceptionDomains = extractFilterDomains(this.elemhideExceptions);
723 1260
724 let genericSelectorExceptionDomains = 1261 let genericSelectorExceptionDomains =
725 extractFilterDomains(this.generichideExceptions); 1262 extractFilterDomains(this.generichideExceptions);
726 elemhideExceptionDomains.forEach(name => 1263 elemhideExceptionDomains.forEach(name =>
727 { 1264 {
728 genericSelectorExceptionDomains.add(name); 1265 genericSelectorExceptionDomains.add(name);
729 }); 1266 });
730 1267
731 addCSSRules(rules, genericSelectors, "^https?://", 1268 addCSSRules(cssRules, genericSelectors, "^https?://",
732 genericSelectorExceptionDomains); 1269 genericSelectorExceptionDomains);
733 1270
734 groupedElemhideFilters.forEach((selectors, matchDomain) => 1271 groupedElemhideFilters.forEach((selectors, matchDomain) =>
735 { 1272 {
736 addCSSRules(rules, selectors, matchDomain, elemhideExceptionDomains); 1273 addCSSRules(cssRules, selectors, matchDomain, elemhideExceptionDomains);
737 }); 1274 });
738 1275
739 let requestFilterExceptionDomains = []; 1276 let requestFilterExceptionDomains = [];
740 for (let filter of this.genericblockExceptions) 1277 for (let filter of this.genericblockExceptions)
741 { 1278 {
742 let parsed = parseFilterRegexpSource(filter.regexpSource); 1279 let parsed = parseFilterRegexpSource(filter.regexpSource);
743 if (parsed.hostname) 1280 if (parsed.hostname)
744 requestFilterExceptionDomains.push(parsed.hostname); 1281 requestFilterExceptionDomains.push(parsed.hostname);
745 } 1282 }
746 1283
747 for (let filter of this.requestFilters) 1284 for (let filter of this.requestFilters)
748 { 1285 {
749 convertFilterAddRules(rules, filter, "block", true, 1286 convertFilterAddRules(blockingRules, filter, "block", true,
750 requestFilterExceptionDomains); 1287 requestFilterExceptionDomains);
751 } 1288 }
752 1289
753 for (let filter of this.requestExceptions) 1290 for (let filter of this.requestExceptions)
754 convertFilterAddRules(rules, filter, "ignore-previous-rules", true); 1291 {
1292 convertFilterAddRules(blockingExceptionRules, filter,
1293 "ignore-previous-rules", true);
1294 }
755 1295
756 return rules; 1296 return async(ruleGroups, (group, index) => () =>
1297 {
1298 let next = () =>
1299 {
1300 if (index == ruleGroups.length - 1)
1301 return ruleGroups.reduce((all, rules) => all.concat(rules), []);
1302 };
1303
1304 if (this.options.merge == "all" ||
1305 (this.options.merge == "auto" &&
1306 ruleGroups.reduce((n, group) => n + group.length, 0) > 50000))
1307 {
1308 return mergeRules(ruleGroups[index], this.options.merge == "all")
1309 .then(rules =>
1310 {
1311 ruleGroups[index] = rules;
1312 return next();
1313 });
1314 }
1315
1316 return next();
1317 });
757 }; 1318 };
OLDNEW
« no previous file with comments | « abp2blocklist.js ('k') | test/abp2blocklist.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld