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: Split variable declaration statement Created May 31, 2017, 6:37 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 20 matching lines...) Expand all
31 | typeMap.FONT 31 | typeMap.FONT
32 | typeMap.MEDIA 32 | typeMap.MEDIA
33 | typeMap.POPUP 33 | typeMap.POPUP
34 | typeMap.OBJECT 34 | typeMap.OBJECT
35 | typeMap.OBJECT_SUBREQUEST 35 | typeMap.OBJECT_SUBREQUEST
36 | typeMap.XMLHTTPREQUEST 36 | typeMap.XMLHTTPREQUEST
37 | typeMap.PING 37 | typeMap.PING
38 | typeMap.SUBDOCUMENT 38 | typeMap.SUBDOCUMENT
39 | typeMap.OTHER); 39 | typeMap.OTHER);
40 40
41 function callLater(func)
42 {
43 return new Promise(resolve =>
44 {
45 let call = () => resolve(func());
46
47 // If this looks like Node.js, call process.nextTick, otherwise call
48 // setTimeout.
49 if (typeof process != "undefined")
50 process.nextTick(call);
51 else
52 setTimeout(call, 0);
53 });
54 }
55
56 function async(funcs)
57 {
58 if (!Array.isArray(funcs))
59 funcs = Array.from(arguments);
60
61 let lastPause = Date.now();
62
63 return funcs.reduce((promise, next) => promise.then(() =>
64 {
65 // If it has been 100ms or longer since the last call, take a pause. This
66 // keeps the browser from freezing up.
67 let now = Date.now();
68 if (now - lastPause >= 100)
69 {
70 lastPause = now;
71 return callLater(next);
72 }
73
74 return next();
75 }),
76 Promise.resolve());
77 }
78
41 function parseDomains(domains, included, excluded) 79 function parseDomains(domains, included, excluded)
42 { 80 {
43 for (let domain in domains) 81 for (let domain in domains)
44 { 82 {
45 if (domain != "") 83 if (domain != "")
46 { 84 {
47 let enabled = domains[domain]; 85 let enabled = domains[domain];
48 domain = punycode.toASCII(domain.toLowerCase()); 86 domain = punycode.toASCII(domain.toLowerCase());
49 87
50 if (!enabled) 88 if (!enabled)
(...skipping 308 matching lines...) Expand 10 before | Expand all | Expand 10 after
359 { 397 {
360 newSelector.push(selector.substring(i, pos.start)); 398 newSelector.push(selector.substring(i, pos.start));
361 newSelector.push('[id=', selector.substring(pos.start + 1, pos.end), ']'); 399 newSelector.push('[id=', selector.substring(pos.start + 1, pos.end), ']');
362 i = pos.end; 400 i = pos.end;
363 } 401 }
364 newSelector.push(selector.substring(i)); 402 newSelector.push(selector.substring(i));
365 403
366 return newSelector.join(""); 404 return newSelector.join("");
367 } 405 }
368 406
407 /**
408 * Check if two strings are a close match
409 *
410 * This function returns an edit operation, one of "substitute", "delete", and
411 * "insert", along with an index in the source string where the edit must occur
412 * in order to arrive at the target string. If the strings are not a close
413 * match, it returns null.
414 *
415 * Two strings are considered to be a close match if they are one edit
416 * operation apart.
417 *
418 * Deletions or insertions of a contiguous range of characters from one string
419 * into the other, at the same index, are treated as a single edit. For
420 * example, "internal" and "international" are considered to be one edit apart
421 * and therefore a close match.
422 *
423 * A few things to note:
424 *
425 * 1) This function does not care about the format of the input strings. For
426 * example, the caller may pass in regular expressions, where "[ab]" and
427 * "[bc]" could be considered to be a close match, since the order within the
428 * brackets doesn't matter. This function will still return null for this set
429 * of inputs since they are two edits apart.
430 *
431 * 2) To be friendly to calling code that might be passing in regular
432 * expressions, this function will simply return null if it encounters a
433 * special character (e.g. "\", "?", "+", etc.) in the delta. For example,
434 * given "Hello" and "Hello, how are you?", it will return null.
435 *
436 * 3) If the caller does indeed pass in regular expressions, it must make the
437 * important assumption that the parts where two such regular expressions may
438 * differ can always be treated as normal strings. For example,
439 * "^https?://example.com/ads" and "^https?://example.com/adv" differ only in
440 * the last character, therefore the regular expressions can safely be merged
441 * into "^https?://example.com/ad[sv]".
442 *
443 * @param {string} s The source string
444 * @param {string} t The target string
445 *
446 * @returns {object} An object describing the single edit operation that must
447 * occur in the source string in order to arrive at the
448 * target string
449 */
450 function closeMatch(s, t)
451 {
452 let diff = s.length - t.length;
453
454 // If target is longer than source, swap them for the purpose of our
455 // calculation.
456 if (diff < 0)
457 {
458 let tmp = s;
459 s = t;
460 t = tmp;
461 }
462
463 let edit = null;
464
465 let i = 0;
466 let j = 0;
467
468 // Start from the beginning and keep going until we hit a character that
469 // doesn't match.
470 for (; i < s.length; i++)
471 {
472 if (s[i] != t[i])
473 break;
474 }
475
476 // Now do exactly the same from the end, but also stop if we reach the
477 // position where we terminated the previous loop.
478 for (; j < t.length; j++)
479 {
480 if (t.length - j == i || s[s.length - j - 1] != t[t.length - j - 1])
481 break;
482 }
483
484 if (diff == 0)
485 {
486 // If the strings are equal in length and the delta isn't exactly one
487 // character, it's not a close match.
488 if (t.length - j - i != 1)
489 return null;
490 }
491 else if (i != t.length - j)
492 {
493 // For strings of unequal length, if we haven't found a match for every
494 // single character in the shorter string counting from both the beginning
495 // and the end, it's not a close match.
496 return null;
497 }
498
499 for (let k = i; k < s.length - j; k++)
500 {
501 // If the delta contains any special characters, it's not a close match.
502 if (s[k] == "." || s[k] == "+" || s[k] == "$" || s[k] == "?" ||
503 s[k] == "{" || s[k] == "}" || s[k] == "(" || s[k] == ")" ||
504 s[k] == "[" || s[k] == "]" || s[k] == "\\")
505 return null;
506 }
507
508 if (diff == 0)
509 {
510 edit = {type: "substitute", index: i};
511 }
512 else if (diff > 0)
513 {
514 edit = {type: "delete", index: i};
515
516 if (diff > 1)
517 edit.endIndex = s.length - j;
518 }
519 else
520 {
521 edit = {type: "insert", index: i};
522
523 if (diff < -1)
524 edit.endIndex = s.length - j;
525 }
526
527 return edit;
528 }
529
530 function eliminateRedundantRulesByURLFilter(rulesInfo, exhaustive)
531 {
532 const heuristicRange = 1000;
533
534 // Throw out obviously redundant rules.
535 return async(rulesInfo.map((ruleInfo, index) => () =>
536 {
537 // If this rule is already marked as redundant, don't bother comparing it
538 // with other rules.
539 if (rulesInfo[index].redundant)
540 return;
541
542 let limit = exhaustive ? rulesInfo.length :
543 Math.min(index + heuristicRange, rulesInfo.length);
544
545 for (let i = index, j = i + 1; j < limit; j++)
546 {
547 if (rulesInfo[j].redundant)
548 continue;
549
550 let source = rulesInfo[i].rule.trigger["url-filter"];
551 let target = rulesInfo[j].rule.trigger["url-filter"];
552
553 if (source.length >= target.length)
554 {
555 // If one URL filter is a substring of the other starting at the
556 // beginning, the other one is clearly redundant.
557 if (source.substring(0, target.length) == target)
558 {
559 rulesInfo[i].redundant = true;
560 break;
561 }
562 }
563 else if (target.substring(0, source.length) == source)
564 {
565 rulesInfo[j].redundant = true;
566 }
567 }
568 }))
569 .then(() => rulesInfo.filter(ruleInfo => !ruleInfo.redundant));
570 }
571
572 function findMatchesForRuleByURLFilter(rulesInfo, index, exhaustive)
573 {
574 // Closely matching rules are likely to be within a certain range. We only
575 // look for matches within this range by default. If we increase this value,
576 // it can give us more matches and a smaller resulting rule set, but possibly
577 // at a significant performance cost.
578 //
579 // If the exhaustive option is true, we simply ignore this value and look for
580 // matches throughout the rule set.
581 const heuristicRange = 1000;
582
583 let limit = exhaustive ? rulesInfo.length :
584 Math.min(index + heuristicRange, rulesInfo.length);
585
586 for (let i = index, j = i + 1; j < limit; j++)
587 {
588 let source = rulesInfo[i].rule.trigger["url-filter"];
589 let target = rulesInfo[j].rule.trigger["url-filter"];
590
591 let edit = closeMatch(source, target);
592
593 if (edit)
594 {
595 let urlFilter, ruleInfo, match = {edit};
596
597 if (edit.type == "insert")
598 {
599 // Convert the insertion into a deletion and stick it on the target
600 // rule instead. We can only group deletions and substitutions;
601 // therefore insertions must be treated as deletions on the target
602 // rule.
603 urlFilter = target;
604 ruleInfo = rulesInfo[j];
605 match.index = i;
606 edit.type = "delete";
607 }
608 else
609 {
610 urlFilter = source;
611 ruleInfo = rulesInfo[i];
612 match.index = j;
613 }
614
615 // If the edit has an end index, it represents a multiple character
616 // edit.
617 let multiEdit = !!edit.endIndex;
618
619 if (multiEdit)
620 {
621 // We only care about a single multiple character edit because the
622 // number of characters for such a match doesn't matter, we can
623 // only merge with one other rule.
624 if (!ruleInfo.multiEditMatch)
625 ruleInfo.multiEditMatch = match;
626 }
627 else
628 {
629 // For single character edits, multiple rules can be merged into
630 // one. e.g. "ad", "ads", and "adv" can be merged into "ad[sv]?".
631 if (!ruleInfo.matches)
632 ruleInfo.matches = new Array(urlFilter.length);
633
634 // Matches at a particular index. For example, for a source string
635 // "ads", both target strings "ad" (deletion) and "adv"
636 // (substitution) match at index 2, hence they are grouped together
637 // to possibly be merged later into "ad[sv]?".
638 let matchesForIndex = ruleInfo.matches[edit.index];
639
640 if (matchesForIndex)
641 {
642 matchesForIndex.push(match);
643 }
644 else
645 {
646 matchesForIndex = [match];
647 ruleInfo.matches[edit.index] = matchesForIndex;
648 }
649
650 // Keep track of the best set of matches. We later sort by this to
651 // get best results.
652 if (!ruleInfo.bestMatches ||
653 matchesForIndex.length > ruleInfo.bestMatches.length)
654 ruleInfo.bestMatches = matchesForIndex;
655 }
656 }
657 }
658 }
659
660 function mergeCandidateRulesByURLFilter(rulesInfo)
661 {
662 // Filter out rules that have no matches at all.
663 let candidateRulesInfo = rulesInfo.filter(ruleInfo =>
664 {
665 return ruleInfo.bestMatches || ruleInfo.multiEditMatch
666 });
667
668 // For best results, we have to sort the candidates by the largest set of
669 // matches.
670 //
671 // For example, we want "ads", "bds", "adv", "bdv", "adx", and "bdx" to
672 // generate "ad[svx]" and "bd[svx]" (2 rules), not "[ab]ds", "[ab]dv", and
673 // "[ab]dx" (3 rules).
674 candidateRulesInfo.sort((ruleInfo1, ruleInfo2) =>
675 {
676 let weight1 = ruleInfo1.bestMatches ? ruleInfo1.bestMatches.length :
677 ruleInfo1.multiEditMatch ? 1 : 0;
678 let weight2 = ruleInfo2.bestMatches ? ruleInfo2.bestMatches.length :
679 ruleInfo2.multiEditMatch ? 1 : 0;
680
681 return weight2 - weight1;
682 });
683
684 for (let ruleInfo of candidateRulesInfo)
685 {
686 let rule = ruleInfo.rule;
687
688 // If this rule has already been merged into another rule, we skip it.
689 if (ruleInfo.merged)
690 continue;
691
692 // Find the best set of rules to group, which is simply the largest set.
693 let best = (ruleInfo.matches || []).reduce((best, matchesForIndex) =>
694 {
695 matchesForIndex = (matchesForIndex || []).filter(match =>
696 {
697 // Filter out rules that have either already been merged into other
698 // rules or have had other rules merged into them.
699 return !rulesInfo[match.index].merged &&
700 !rulesInfo[match.index].mergedInto;
701 });
702
703 return matchesForIndex.length > best.length ? matchesForIndex : best;
704 },
705 []);
706
707 let multiEdit = false;
708
709 // If we couldn't find a single rule to merge with, let's see if we have a
710 // multiple character edit. e.g. we could merge "ad" and "adserver" into
711 // "ad(server)?".
712 if (best.length == 0 && ruleInfo.multiEditMatch &&
713 !rulesInfo[ruleInfo.multiEditMatch.index].merged &&
714 !rulesInfo[ruleInfo.multiEditMatch.index].mergedInto)
715 {
716 best = [ruleInfo.multiEditMatch];
717 multiEdit = true;
718 }
719
720 if (best.length > 0)
721 {
722 let urlFilter = rule.trigger["url-filter"];
723
724 let editIndex = best[0].edit.index;
725
726 if (!multiEdit)
727 {
728 // Merge all the matching rules into this one.
729
730 let characters = [];
731 let quantifier = "";
732
733 for (let match of best)
734 {
735 if (match.edit.type == "delete")
736 {
737 quantifier = "?";
738 }
739 else
740 {
741 let character = rulesInfo[match.index].rule
742 .trigger["url-filter"][editIndex];
743 characters.push(character);
744 }
745
746 // Mark the target rule as merged so other rules don't try to merge
747 // it again.
748 rulesInfo[match.index].merged = true;
749 }
750
751 urlFilter = urlFilter.substring(0, editIndex + 1) + quantifier +
752 urlFilter.substring(editIndex + 1);
753 if (characters.length > 0)
754 {
755 urlFilter = urlFilter.substring(0, editIndex) + "[" +
756 urlFilter[editIndex] + characters.join("") + "]" +
757 urlFilter.substring(editIndex + 1);
758 }
759 }
760 else
761 {
762 let editEndIndex = best[0].edit.endIndex;
763
764 // Mark the target rule as merged so other rules don't try to merge it
765 // again.
766 rulesInfo[best[0].index].merged = true;
767
768 urlFilter = urlFilter.substring(0, editIndex) + "(" +
769 urlFilter.substring(editIndex, editEndIndex) + ")?" +
770 urlFilter.substring(editEndIndex);
771 }
772
773 rule.trigger["url-filter"] = urlFilter;
774
775 // Mark this rule as one that has had other rules merged into it.
776 ruleInfo.mergedInto = true;
777 }
778 }
779 }
780
781 function mergeRulesByURLFilter(rulesInfo, exhaustive)
782 {
783 return async(rulesInfo.map((ruleInfo, index) => () =>
784 findMatchesForRuleByURLFilter(rulesInfo, index, exhaustive)
785 ))
786 .then(() => mergeCandidateRulesByURLFilter(rulesInfo));
787 }
788
789 function mergeRulesByArrayProperty(rulesInfo, propertyType, property)
790 {
791 if (rulesInfo.length <= 1)
792 return;
793
794 let oneRuleInfo = rulesInfo.shift();
795 let valueSet = new Set(oneRuleInfo.rule[propertyType][property]);
796
797 for (let ruleInfo of rulesInfo)
798 {
799 if (ruleInfo.rule[propertyType][property])
800 {
801 for (let value of ruleInfo.rule[propertyType][property])
802 valueSet.add(value);
803 }
804
805 ruleInfo.merged = true;
806 }
807
808 if (valueSet.size > 0)
809 oneRuleInfo.rule[propertyType][property] = Array.from(valueSet);
810
811 oneRuleInfo.mergedInto = true;
812 }
813
814 function groupRulesByMergeableProperty(rulesInfo, propertyType, property)
815 {
816 let mergeableRulesInfoByGroup = new Map();
817
818 for (let ruleInfo of rulesInfo)
819 {
820 let copy = {
821 trigger: Object.assign({}, ruleInfo.rule.trigger),
822 action: Object.assign({}, ruleInfo.rule.action)
823 };
824
825 delete copy[propertyType][property];
826
827 let groupKey = JSON.stringify(copy);
828
829 let mergeableRulesInfo = mergeableRulesInfoByGroup.get(groupKey);
830
831 if (mergeableRulesInfo)
832 mergeableRulesInfo.push(ruleInfo);
833 else
834 mergeableRulesInfoByGroup.set(groupKey, [ruleInfo]);
835 }
836
837 return mergeableRulesInfoByGroup;
838 }
839
840 function mergeRules(rules, exhaustive)
841 {
842 let rulesInfo = rules.map(rule => ({rule}));
843
844 let arrayPropertiesToMergeBy = ["resource-type", "if-domain"];
845
846 return async(() =>
847 {
848 let map = groupRulesByMergeableProperty(rulesInfo, "trigger", "url-filter");
849 return async(Array.from(map.values()).map(mergeableRulesInfo => () =>
850 eliminateRedundantRulesByURLFilter(mergeableRulesInfo, exhaustive)
851 .then(rulesInfo => mergeRulesByURLFilter(rulesInfo, exhaustive))
852 ))
853 .then(() =>
854 {
855 // Filter out rules that are redundant or have been merged into other
856 // rules.
857 rulesInfo = rulesInfo.filter(ruleInfo => !ruleInfo.redundant &&
858 !ruleInfo.merged);
859 });
860 })
861 .then(() => async(arrayPropertiesToMergeBy.map(arrayProperty => () =>
862 {
863 let map = groupRulesByMergeableProperty(rulesInfo, "trigger",
864 arrayProperty);
865 return async(Array.from(map.values()).map(mergeableRulesInfo => () =>
866 mergeRulesByArrayProperty(mergeableRulesInfo, "trigger", arrayProperty)
867 ))
868 .then(() =>
869 {
870 rulesInfo = rulesInfo.filter(ruleInfo => !ruleInfo.merged);
871 });
872 })))
873 .then(() => rulesInfo.map(ruleInfo => ruleInfo.rule));
874 }
875
369 let ContentBlockerList = 876 let ContentBlockerList =
370 /** 877 /**
371 * Create a new Adblock Plus filter to content blocker list converter 878 * Create a new Adblock Plus filter to content blocker list converter
372 * 879 *
880 * @param {object} options Options for content blocker list generation
881 *
373 * @constructor 882 * @constructor
374 */ 883 */
375 exports.ContentBlockerList = function () 884 exports.ContentBlockerList = function(options)
376 { 885 {
886 const defaultOptions = {
887 merge: "auto"
888 };
889
890 this.options = Object.assign({}, defaultOptions, options);
891
377 this.requestFilters = []; 892 this.requestFilters = [];
378 this.requestExceptions = []; 893 this.requestExceptions = [];
379 this.elemhideFilters = []; 894 this.elemhideFilters = [];
380 this.elemhideExceptions = []; 895 this.elemhideExceptions = [];
381 this.elemhideSelectorExceptions = new Map(); 896 this.elemhideSelectorExceptions = new Map();
382 }; 897 };
383 898
384 /** 899 /**
385 * Add Adblock Plus filter to be converted 900 * Add Adblock Plus filter to be converted
386 * 901 *
(...skipping 27 matching lines...) Expand all
414 let domains = this.elemhideSelectorExceptions[filter.selector]; 929 let domains = this.elemhideSelectorExceptions[filter.selector];
415 if (!domains) 930 if (!domains)
416 domains = this.elemhideSelectorExceptions[filter.selector] = []; 931 domains = this.elemhideSelectorExceptions[filter.selector] = [];
417 932
418 parseDomains(filter.domains, domains, []); 933 parseDomains(filter.domains, domains, []);
419 } 934 }
420 }; 935 };
421 936
422 /** 937 /**
423 * Generate content blocker list for all filters that were added 938 * Generate content blocker list for all filters that were added
424 *
425 * @returns {Filter} filter Filter to convert
426 */ 939 */
427 ContentBlockerList.prototype.generateRules = function(filter) 940 ContentBlockerList.prototype.generateRules = function()
428 { 941 {
429 let rules = []; 942 console.error("Generating rules...");
943
944 let cssRules = [];
945 let cssExceptionRules = [];
946 let blockingRules = [];
947 let blockingExceptionRules = [];
948
949 let ruleGroups = [cssRules, cssExceptionRules,
950 blockingRules, blockingExceptionRules];
430 951
431 let groupedElemhideFilters = new Map(); 952 let groupedElemhideFilters = new Map();
432 for (let filter of this.elemhideFilters) 953 for (let filter of this.elemhideFilters)
433 { 954 {
434 let result = convertElemHideFilter(filter, this.elemhideSelectorExceptions); 955 let result = convertElemHideFilter(filter, this.elemhideSelectorExceptions);
435 if (!result) 956 if (!result)
436 continue; 957 continue;
437 958
438 if (result.matchDomains.length == 0) 959 if (result.matchDomains.length == 0)
439 result.matchDomains = ["^https?://"]; 960 result.matchDomains = ["^https?://"];
440 961
441 for (let matchDomain of result.matchDomains) 962 for (let matchDomain of result.matchDomains)
442 { 963 {
443 let group = groupedElemhideFilters.get(matchDomain) || []; 964 let group = groupedElemhideFilters.get(matchDomain) || [];
444 group.push(result.selector); 965 group.push(result.selector);
445 groupedElemhideFilters.set(matchDomain, group); 966 groupedElemhideFilters.set(matchDomain, group);
446 } 967 }
447 } 968 }
448 969
449 groupedElemhideFilters.forEach((selectors, matchDomain) => 970 groupedElemhideFilters.forEach((selectors, matchDomain) =>
450 { 971 {
451 while (selectors.length) 972 while (selectors.length)
452 { 973 {
453 let selector = selectors.splice(0, selectorLimit).join(", "); 974 let selector = selectors.splice(0, selectorLimit).join(", ");
454 975
455 // As of Safari 9.0 element IDs are matched as lowercase. We work around 976 // As of Safari 9.0 element IDs are matched as lowercase. We work around
456 // this by converting to the attribute format [id="elementID"] 977 // this by converting to the attribute format [id="elementID"]
457 selector = convertIDSelectorsToAttributeSelectors(selector); 978 selector = convertIDSelectorsToAttributeSelectors(selector);
458 979
459 rules.push({ 980 cssRules.push({
460 trigger: {"url-filter": matchDomain, 981 trigger: {"url-filter": matchDomain,
461 "url-filter-is-case-sensitive": true}, 982 "url-filter-is-case-sensitive": true},
462 action: {type: "css-display-none", 983 action: {type: "css-display-none",
463 selector: selector} 984 selector: selector}
464 }); 985 });
465 } 986 }
466 }); 987 });
467 988
468 for (let filter of this.elemhideExceptions) 989 for (let filter of this.elemhideExceptions)
469 convertFilterAddRules(rules, filter, "ignore-previous-rules", false); 990 {
991 convertFilterAddRules(cssExceptionRules, filter,
992 "ignore-previous-rules", false);
993 }
994
470 for (let filter of this.requestFilters) 995 for (let filter of this.requestFilters)
471 convertFilterAddRules(rules, filter, "block", true); 996 convertFilterAddRules(blockingRules, filter, "block", true);
997
472 for (let filter of this.requestExceptions) 998 for (let filter of this.requestExceptions)
473 convertFilterAddRules(rules, filter, "ignore-previous-rules", true); 999 {
1000 convertFilterAddRules(blockingExceptionRules, filter,
1001 "ignore-previous-rules", true);
1002 }
474 1003
475 return rules.filter(rule => !hasNonASCI(rule)); 1004 let l = ruleGroups.reduce((n, g) => n + g.length, 0);
1005 let t = Date.now();
1006
1007 return async(ruleGroups.map((group, index) => () =>
1008 {
1009 let next = () =>
1010 {
1011 if (index == ruleGroups.length - 1)
1012 return ruleGroups.reduce((all, rules) => all.concat(rules), []);
1013 };
1014
1015 ruleGroups[index] = ruleGroups[index].filter(rule => !hasNonASCI(rule));
1016
1017 if (this.options.merge == "all" ||
1018 (this.options.merge == "auto" &&
1019 ruleGroups.reduce((n, group) => n + group.length, 0) > 50000))
1020 {
1021 return mergeRules(ruleGroups[index], this.options.merge == "all")
1022 .then(rules =>
1023 {
1024 ruleGroups[index] = rules;
1025 return next();
1026 });
1027 }
1028
1029 return next();
1030 }))
1031 .then(rules =>
1032 {
1033 console.error("Rule merging took " + (Date.now() - t) + "ms");
kzar 2017/07/07 13:23:28 I guess these changes were included by mistake?
Manish Jethani 2017/07/08 12:13:19 Uh, jeez, this was a mistake. By the way, let's p
kzar 2017/07/10 12:41:26 Sure thing, just let me know when you're ready to
1034 console.error("Before: " + l + " rules");
1035 console.error("After: " + rules.length + " rules");
1036
1037 return rules;
1038 });
476 }; 1039 };
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