Skip to content

Commit 616191c

Browse files
authored
Merge pull request EFForg#13208 from cowlicks/cschanaj-user-nullIterable
Rebase and test cschanaj's nullIterable PR
2 parents a58e7fc + be18d9c commit 616191c

File tree

2 files changed

+115
-40
lines changed

2 files changed

+115
-40
lines changed

chromium/rules.js

Lines changed: 47 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,19 @@ const trivial_rule_from_c = new RegExp("^http:");
1515
const trivial_cookie_name_c = new RegExp(".*");
1616
const trivial_cookie_host_c = new RegExp(".*");
1717

18+
// Empty iterable singleton to reduce memory usage
19+
const nullIterable = Object.create(null, {
20+
[Symbol.iterator]: {
21+
value: function* () {
22+
// do nothing
23+
}
24+
},
25+
26+
size: {
27+
value: 0
28+
},
29+
});
30+
1831
/**
1932
* A single rule
2033
* @param from
@@ -341,7 +354,7 @@ RuleSets.prototype = {
341354
util.log(util.INFO, 'done removing rule');
342355
return true;
343356
},
344-
357+
345358
/**
346359
* Retrieve stored user rules from localStorage
347360
**/
@@ -499,63 +512,70 @@ RuleSets.prototype = {
499512
*/
500513
potentiallyApplicableRulesets: function(host) {
501514
// Have we cached this result? If so, return it!
502-
var cached_item = this.ruleCache.get(host);
503-
if (cached_item !== undefined) {
504-
util.log(util.DBUG, "Ruleset cache hit for " + host + " items:" + cached_item.length);
515+
if (this.ruleCache.has(host)) {
516+
let cached_item = this.ruleCache.get(host);
517+
util.log(util.DBUG, "Ruleset cache hit for " + host + " items:" + cached_item.size);
505518
return cached_item;
519+
} else {
520+
util.log(util.DBUG, "Ruleset cache miss for " + host);
506521
}
507-
util.log(util.DBUG, "Ruleset cache miss for " + host);
508522

509-
var results = [];
510-
if (this.targets.has(host)) {
511-
// Copy the host targets so we don't modify them.
512-
results = results.concat(this.targets.get(host));
513-
}
523+
// Let's begin search
524+
// Copy the host targsts so we don't modify them.
525+
let results = (this.targets.has(host) ?
526+
new Set([...this.targets.get(host)]) :
527+
new Set());
514528

515529
// Ensure host is well-formed (RFC 1035)
516-
if (host.indexOf("..") != -1 || host.length > 255) {
530+
if (host.length > 255 || host.indexOf("..") != -1) {
517531
util.log(util.WARN,"Malformed host passed to potentiallyApplicableRulesets: " + host);
518-
return null;
532+
return nullIterable;
519533
}
520534

521535
// Replace each portion of the domain with a * in turn
522-
var segmented = host.split(".");
523-
for (let i=0; i < segmented.length; i++) {
536+
let segmented = host.split(".");
537+
for (let i = 0; i < segmented.length; i++) {
524538
let tmp = segmented[i];
525539
segmented[i] = "*";
526-
results = results.concat(this.targets.get(segmented.join(".")));
540+
541+
results = (this.targets.has(segmented.join(".")) ?
542+
new Set([...results, ...this.targets.get(segmented.join("."))]) :
543+
results);
544+
527545
segmented[i] = tmp;
528546
}
547+
529548
// now eat away from the left, with *, so that for x.y.z.google.com we
530549
// check *.z.google.com and *.google.com (we did *.y.z.google.com above)
531-
for (var i = 2; i <= segmented.length - 2; ++i) {
532-
var t = "*." + segmented.slice(i,segmented.length).join(".");
533-
results = results.concat(this.targets.get(t));
550+
for (let i = 2; i <= segmented.length - 2; i++) {
551+
let t = "*." + segmented.slice(i, segmented.length).join(".");
552+
553+
results = (this.targets.has(t) ?
554+
new Set([...results, ...this.targets.get(t)]) :
555+
results);
534556
}
535557

536558
// Clean the results list, which may contain duplicates or undefined entries
537-
var resultSet = new Set(results);
538-
resultSet.delete(undefined);
559+
results.delete(undefined);
539560

540561
util.log(util.DBUG,"Applicable rules for " + host + ":");
541-
if (resultSet.size == 0) {
562+
if (results.size == 0) {
542563
util.log(util.DBUG, " None");
564+
results = nullIterable;
543565
} else {
544-
for (let target of resultSet.values()) {
545-
util.log(util.DBUG, " " + target.name);
546-
}
566+
results.forEach(result => util.log(util.DBUG, " " + result.name));
547567
}
548568

549569
// Insert results into the ruleset cache
550-
this.ruleCache.set(host, resultSet);
570+
this.ruleCache.set(host, results);
551571

552572
// Cap the size of the cache. (Limit chosen somewhat arbitrarily)
553573
if (this.ruleCache.size > 1000) {
554574
// Map.prototype.keys() returns keys in insertion order, so this is a FIFO.
555575
this.ruleCache.delete(this.ruleCache.keys().next().value);
556576
}
557577

558-
return resultSet;
578+
return results;
559579
},
560580

561581
/**
@@ -665,6 +685,7 @@ RuleSets.prototype = {
665685
};
666686

667687
Object.assign(exports, {
688+
nullIterable,
668689
settings,
669690
trivial_rule_to,
670691
trivial_rule_from_c,

chromium/test/rules_test.js

Lines changed: 68 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,30 @@ const Exclusion = rules.Exclusion,
1212
describe('rules.js', function() {
1313
let test_str = 'test';
1414

15+
describe('nullIterable', function() {
16+
it('is iterable zero times and is size 0', function() {
17+
let count = 0;
18+
for (let _ of rules.nullIterable) { // eslint-disable-line no-unused-vars
19+
count += 1;
20+
}
21+
assert.strictEqual(count, 0);
22+
assert.strictEqual(rules.nullIterable.size, 0);
23+
assert.isEmpty(rules.nullIterable);
24+
});
25+
});
26+
1527
describe('Exclusion', function() {
1628
it('constructs', function() {
1729
let exclusion = new Exclusion(test_str);
18-
assert(exclusion.pattern_c.test(test_str), true);
30+
assert.isTrue(exclusion.pattern_c.test(test_str), true);
1931
});
2032
});
2133

2234
describe('Rule', function() {
2335
it('constructs trivial rule', function() {
2436
let rule = new Rule('^http:', 'https:');
25-
assert(rule.to, rules.trivial_rule_to);
26-
assert(rule.from_c, rules.trivial_rule_from);
37+
assert.equal(rule.to, rules.trivial_rule_to);
38+
assert.equal(rule.from_c, rules.trivial_rule_from_c);
2739
});
2840
});
2941

@@ -41,7 +53,7 @@ describe('rules.js', function() {
4153
it('rewrites uris', function() {
4254
let rule = new Rule('^http:', 'https:');
4355
this.ruleset.rules.push(rule);
44-
assert(this.ruleset.apply('http://example.com/'), 'https://example.com/');
56+
assert.equal(this.ruleset.apply('http://example.com/'), 'https://example.com/');
4557
});
4658

4759
it('does nothing when empty', function() {
@@ -85,28 +97,70 @@ describe('rules.js', function() {
8597

8698
describe('RuleSets', function() {
8799
describe('#potentiallyApplicableRulesets', function() {
88-
let example_host = 'example.com';
100+
let host = 'example.com',
101+
value = [host];
89102

90103
beforeEach(function() {
91104
this.rsets = new RuleSets();
92105
});
93106

94107
it('returns nothing when empty', function() {
95-
assert.isEmpty(this.rsets.potentiallyApplicableRulesets(example_host));
108+
assert.isEmpty(this.rsets.potentiallyApplicableRulesets(host));
109+
});
110+
111+
it('returns nothing for malformed hosts', function() {
112+
assert.isEmpty(this.rsets.potentiallyApplicableRulesets('....'));
96113
});
97114

98115
it('returns cached rulesets', function() {
99-
this.rsets.ruleCache.set(example_host, 'value');
100-
assert(this.rsets.potentiallyApplicableRulesets(example_host), 'value');
116+
this.rsets.ruleCache.set(host, value);
117+
assert.deepEqual(this.rsets.potentiallyApplicableRulesets(host), value);
101118
});
102119

103-
it('returns applicable rule sets', function() {
104-
let target = ['value'];
105-
this.rsets.targets.set(example_host, target);
120+
it('caches results', function() {
121+
this.rsets.targets.set(host, value);
122+
123+
assert.isEmpty(this.rsets.ruleCache);
124+
this.rsets.potentiallyApplicableRulesets(host);
125+
assert.isTrue(this.rsets.ruleCache.has(host));
126+
});
127+
128+
describe('wildcard matching', function() {
129+
130+
it('no wildcard', function() {
131+
let target = host;
132+
this.rsets.targets.set(target, value);
133+
134+
let result = this.rsets.potentiallyApplicableRulesets(target),
135+
expected = new Set(value);
136+
137+
assert.deepEqual(result, expected);
138+
});
139+
140+
it('matches left hand side wildcards', function() {
141+
let target = '*.' + host;
142+
this.rsets.targets.set(target, value);
143+
144+
let res1 = this.rsets.potentiallyApplicableRulesets('sub.' + host);
145+
assert.deepEqual(res1, new Set(value), 'default case');
146+
147+
let res2 = this.rsets.potentiallyApplicableRulesets(host);
148+
assert.isEmpty(res2, 'wildcard does not match parent domains');
149+
150+
let res3 = this.rsets.potentiallyApplicableRulesets('moresub.sub.' + host);
151+
assert.deepEqual(res3, new Set(value), 'wildcard matches sub domains');
152+
});
153+
154+
it('matches middle wildcards', function() {
155+
let target = 'sub.*.' + host;
156+
this.rsets.targets.set(target, value);
157+
158+
let res1 = this.rsets.potentiallyApplicableRulesets('sub.star.' + host);
159+
assert.deepEqual(res1, new Set(value), 'default case');
106160

107-
let result = this.rsets.potentiallyApplicableRulesets(example_host),
108-
expected = new Set(target);
109-
assert.deepEqual(result, expected);
161+
let res2 = this.rsets.potentiallyApplicableRulesets('sub.foo.bar.' + host);
162+
assert.isEmpty(res2, new Set(value), 'only matches one label');
163+
});
110164
});
111165
});
112166
});

0 commit comments

Comments
 (0)