Skip to content

Commit ddaa7bd

Browse files
authored
Adding https-everywhere-lib submodule (EFForg#18093)
* Adding https-everywhere-lib submodule, which moves much of the functionality of `rules.js` into WASM for memory efficiency and performance.
1 parent 1d70af9 commit ddaa7bd

9 files changed

Lines changed: 156 additions & 51 deletions

File tree

.gitmodules

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,7 @@
22
path = translations
33
url = https://git.torproject.org/translation.git
44
branch = https_everywhere
5+
6+
[submodule "https-everywhere-lib"]
7+
path = https-everywhere-lib
8+
url = https://github.com/EFForg/https-everywhere-lib.git

chromium/.eslintrc.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
},
2020
"globals": {
2121
"exports": true,
22-
"require": true
22+
"require": true,
23+
"wasm_bindgen": true
2324
}
2425
}

chromium/background-scripts/background.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,14 @@ const rules = require('./rules'),
77
incognito = require('./incognito'),
88
util = require('./util'),
99
update = require('./update'),
10-
{ update_channels } = require('./update_channels');
10+
{ update_channels } = require('./update_channels'),
11+
wasm = require('./wasm');
1112

1213

1314
let all_rules = new rules.RuleSets();
1415

1516
async function initialize() {
17+
await wasm.initialize();
1618
await store.initialize();
1719
await store.performMigrations();
1820
await initializeStoredGlobals();

chromium/background-scripts/rules.js

Lines changed: 105 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22

33
(function(exports) {
44

5-
const util = require('./util');
5+
const util = require('./util'),
6+
wasm = require('./wasm');
67

78
let settings = {
89
enableMixedRulesets: false,
@@ -220,6 +221,11 @@ RuleSets.prototype = {
220221
loadFromBrowserStorage: async function(store, applyStoredFunc) {
221222
this.store = store;
222223
this.ruleActiveStates = await this.store.get_promise('ruleActiveStates', {});
224+
try {
225+
this.wasm_rs = wasm.RuleSets.new();
226+
} catch(e) {
227+
util.log(util.WARN, 'Falling back to pure JS implementation: ' + e);
228+
}
223229
await applyStoredFunc(this);
224230
await this.loadStoredUserRules();
225231
await this.addStoredCustomRulesets();
@@ -241,11 +247,20 @@ RuleSets.prototype = {
241247

242248
addFromJson: function(ruleJson, scope) {
243249
const scope_obj = getScope(scope);
244-
for (let ruleset of ruleJson) {
245-
try {
246-
this.parseOneJsonRuleset(ruleset, scope_obj);
247-
} catch(e) {
248-
util.log(util.WARN, 'Error processing ruleset:' + e);
250+
251+
if (this.wasm_rs) {
252+
this.wasm_rs.add_all_from_js_array(
253+
ruleJson,
254+
settings.enableMixedRulesets,
255+
this.ruleActiveStates,
256+
scope);
257+
} else {
258+
for (let ruleset of ruleJson) {
259+
try {
260+
this.parseOneJsonRuleset(ruleset, scope_obj);
261+
} catch(e) {
262+
util.log(util.WARN, 'Error processing ruleset:' + e);
263+
}
249264
}
250265
}
251266
},
@@ -322,7 +337,15 @@ RuleSets.prototype = {
322337
*/
323338
addUserRule : function(params, scope) {
324339
util.log(util.INFO, 'adding new user rule for ' + JSON.stringify(params));
325-
this.parseOneJsonRuleset(params, scope);
340+
if (this.wasm_rs) {
341+
this.wasm_rs.add_all_from_js_array(
342+
[params],
343+
settings.enableMixedRulesets,
344+
this.ruleActiveStates,
345+
scope);
346+
} else {
347+
this.parseOneJsonRuleset(params, scope);
348+
}
326349

327350
// clear cache so new rule take effect immediately
328351
for (const target of params.target) {
@@ -351,11 +374,16 @@ RuleSets.prototype = {
351374
this.ruleCache.delete(ruleset.name);
352375

353376
if (src === 'popup') {
354-
const tmp = this.targets.get(ruleset.name).filter(r => !r.isEquivalentTo(ruleset))
355-
this.targets.set(ruleset.name, tmp);
356377

357-
if (this.targets.get(ruleset.name).length == 0) {
358-
this.targets.delete(ruleset.name);
378+
if (this.wasm_rs) {
379+
this.wasm_rs.remove_ruleset(ruleset);
380+
} else {
381+
const tmp = this.targets.get(ruleset.name).filter(r => !r.isEquivalentTo(ruleset))
382+
this.targets.set(ruleset.name, tmp);
383+
384+
if (this.targets.get(ruleset.name).length == 0) {
385+
this.targets.delete(ruleset.name);
386+
}
359387
}
360388
}
361389

@@ -546,51 +574,80 @@ RuleSets.prototype = {
546574
util.log(util.DBUG, "Ruleset cache miss for " + host);
547575
}
548576

549-
// Let's begin search
550-
// Copy the host targets so we don't modify them.
551-
let results = (this.targets.has(host) ?
552-
new Set([...this.targets.get(host)]) :
553-
new Set());
577+
let results;
578+
if (this.wasm_rs) {
579+
let pa = this.wasm_rs.potentially_applicable(host);
580+
results = new Set([...pa].map(ruleset => {
581+
let rs = new RuleSet(ruleset.name, ruleset.default_state, getScope(ruleset.scope), ruleset.note);
554582

555-
// Ensure host is well-formed (RFC 1035)
556-
if (host.length <= 0 || host.length > 255 || host.indexOf("..") != -1) {
557-
util.log(util.WARN, "Malformed host passed to potentiallyApplicableRulesets: " + host);
558-
return nullIterable;
559-
}
583+
if (ruleset.cookierules) {
584+
let cookierules = ruleset.cookierules.map(cookierule => {
585+
return new CookieRule(cookierule.host, cookierule.name);
586+
});
587+
rs.cookierules = cookierules;
588+
} else {
589+
rs.cookierules = null;
590+
}
560591

561-
// Replace www.example.com with www.example.*
562-
// eat away from the right for once and only once
563-
let segmented = host.split(".");
564-
if (segmented.length > 1) {
565-
const tmp = segmented[segmented.length - 1];
566-
segmented[segmented.length - 1] = "*";
592+
let rules = ruleset.rules.map(rule => {
593+
return getRule(rule.from, rule.to);
594+
});
595+
rs.rules = rules;
567596

568-
results = (this.targets.has(segmented.join(".")) ?
569-
new Set([...results, ...this.targets.get(segmented.join("."))]) :
570-
results);
597+
if (ruleset.exclusions) {
598+
rs.exclusions = new RegExp(ruleset.exclusions);
599+
} else {
600+
rs.exclusions = null;
601+
}
602+
return rs;
603+
}));
604+
} else {
605+
// Let's begin search
606+
// Copy the host targets so we don't modify them.
607+
results = (this.targets.has(host) ?
608+
new Set([...this.targets.get(host)]) :
609+
new Set());
610+
611+
// Ensure host is well-formed (RFC 1035)
612+
if (host.length <= 0 || host.length > 255 || host.indexOf("..") != -1) {
613+
util.log(util.WARN, "Malformed host passed to potentiallyApplicableRulesets: " + host);
614+
return nullIterable;
615+
}
571616

572-
segmented[segmented.length - 1] = tmp;
573-
}
617+
// Replace www.example.com with www.example.*
618+
// eat away from the right for once and only once
619+
let segmented = host.split(".");
620+
if (segmented.length > 1) {
621+
const tmp = segmented[segmented.length - 1];
622+
segmented[segmented.length - 1] = "*";
574623

575-
// now eat away from the left, with *, so that for x.y.z.google.com we
576-
// check *.y.z.google.com, *.z.google.com and *.google.com
577-
for (let i = 1; i <= segmented.length - 2; i++) {
578-
let t = "*." + segmented.slice(i, segmented.length).join(".");
624+
results = (this.targets.has(segmented.join(".")) ?
625+
new Set([...results, ...this.targets.get(segmented.join("."))]) :
626+
results);
579627

580-
results = (this.targets.has(t) ?
581-
new Set([...results, ...this.targets.get(t)]) :
582-
results);
583-
}
628+
segmented[segmented.length - 1] = tmp;
629+
}
584630

585-
// Clean the results list, which may contain duplicates or undefined entries
586-
results.delete(undefined);
631+
// now eat away from the left, with *, so that for x.y.z.google.com we
632+
// check *.y.z.google.com, *.z.google.com and *.google.com
633+
for (let i = 1; i <= segmented.length - 2; i++) {
634+
let t = "*." + segmented.slice(i, segmented.length).join(".");
587635

588-
util.log(util.DBUG,"Applicable rules for " + host + ":");
589-
if (results.size == 0) {
590-
util.log(util.DBUG, " None");
591-
results = nullIterable;
592-
} else {
593-
results.forEach(result => util.log(util.DBUG, " " + result.name));
636+
results = (this.targets.has(t) ?
637+
new Set([...results, ...this.targets.get(t)]) :
638+
results);
639+
}
640+
641+
// Clean the results list, which may contain duplicates or undefined entries
642+
results.delete(undefined);
643+
644+
util.log(util.DBUG,"Applicable rules for " + host + ":");
645+
if (results.size == 0) {
646+
util.log(util.DBUG, " None");
647+
results = nullIterable;
648+
} else {
649+
results.forEach(result => util.log(util.DBUG, " " + result.name));
650+
}
594651
}
595652

596653
// Insert results into the ruleset cache
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
"use strict";
2+
3+
(function(exports) {
4+
5+
const util = require('./util'),
6+
{ RuleSets } = wasm_bindgen;
7+
8+
async function initialize() {
9+
try {
10+
await wasm_bindgen(chrome.runtime.getURL('wasm/https_everywhere_lib_bg.wasm'));
11+
} catch(e) {
12+
util.log(util.WARN, 'The wasm library has not loaded correctly: ' + e);
13+
}
14+
}
15+
16+
function is_enabled() {
17+
return true;
18+
}
19+
20+
Object.assign(exports, {
21+
initialize,
22+
RuleSets,
23+
is_enabled,
24+
});
25+
26+
})(typeof exports == 'undefined' ? require.scopes.wasm = {} : exports);

chromium/manifest.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,13 @@
66
}
77
},
88
"author": "extension-devs@eff.org",
9+
"content_security_policy": "script-src 'self' 'wasm-eval'; object-src 'self'",
910
"background": {
1011
"scripts": [
1112
"background-scripts/bootstrap.js",
1213
"background-scripts/util.js",
14+
"wasm/https_everywhere_lib.js",
15+
"background-scripts/wasm.js",
1316
"background-scripts/update_channels.js",
1417
"background-scripts/update.js",
1518
"background-scripts/rules.js",
@@ -55,4 +58,4 @@
5558
"web_accessible_resources": [
5659
"/pages/cancel/index.html"
5760
]
58-
}
61+
}

chromium/test/rules_test.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
'use strict'
22

3+
const text_encoding = require('text-encoding');
4+
global.TextDecoder = text_encoding.TextDecoder;
5+
global.TextEncoder = text_encoding.TextEncoder;
6+
global.self = global;
7+
require("../../https-everywhere-lib/pkg/https_everywhere_lib.js");
8+
39
const assert = require('chai').assert,
410
rules = require('../background-scripts/rules');
511

https-everywhere-lib

Submodule https-everywhere-lib added at 58d40d6

make.sh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,11 @@ python3.6 ../../utils/chromium-translations.py ../../translations/ _locales/
108108
python3.6 ../../utils/chromium-translations.py ../../src/chrome/locale/ _locales/
109109
do_not_ship="*.py *.xml"
110110
rm -f $do_not_ship
111+
112+
mkdir wasm
113+
cp ../../https-everywhere-lib/pkg/*.wasm wasm
114+
cp ../../https-everywhere-lib/pkg/*.js wasm
115+
111116
cd ../..
112117

113118
python3.6 ./utils/merge-rulesets.py || exit 5

0 commit comments

Comments
 (0)