summaryrefslogtreecommitdiff
path: root/data/extensions/https-everywhere@eff.org/background-scripts
diff options
context:
space:
mode:
Diffstat (limited to 'data/extensions/https-everywhere@eff.org/background-scripts')
-rw-r--r--data/extensions/https-everywhere@eff.org/background-scripts/background.js210
-rw-r--r--data/extensions/https-everywhere@eff.org/background-scripts/rules.js125
-rw-r--r--data/extensions/https-everywhere@eff.org/background-scripts/store.js58
-rw-r--r--data/extensions/https-everywhere@eff.org/background-scripts/update.js33
-rw-r--r--data/extensions/https-everywhere@eff.org/background-scripts/update_channels.js3
5 files changed, 324 insertions, 105 deletions
diff --git a/data/extensions/https-everywhere@eff.org/background-scripts/background.js b/data/extensions/https-everywhere@eff.org/background-scripts/background.js
index 8a82be3..28c7ead 100644
--- a/data/extensions/https-everywhere@eff.org/background-scripts/background.js
+++ b/data/extensions/https-everywhere@eff.org/background-scripts/background.js
@@ -40,6 +40,7 @@ async function initializeAllRules() {
var httpNowhereOn = false;
var showCounter = true;
var isExtensionEnabled = true;
+let disabledList = new Set();
function initializeStoredGlobals(){
return new Promise(resolve => {
@@ -47,11 +48,15 @@ function initializeStoredGlobals(){
httpNowhere: false,
showCounter: true,
globalEnabled: true,
- enableMixedRulesets: false
+ enableMixedRulesets: false,
+ disabledList: [],
}, function(item) {
httpNowhereOn = item.httpNowhere;
showCounter = item.showCounter;
isExtensionEnabled = item.globalEnabled;
+ for (let disabledSite of item.disabledList) {
+ disabledList.add(disabledSite);
+ }
updateState();
rules.settings.enableMixedRulesets = item.enableMixedRulesets;
@@ -147,14 +152,6 @@ function updateState () {
iconState = 'blocking';
}
- if ('setIcon' in chrome.browserAction) {
- chrome.browserAction.setIcon({
- path: {
- 38: 'images/icons/icon-' + iconState + '-38.png'
- }
- });
- }
-
chrome.browserAction.setTitle({
title: 'HTTPS Everywhere' + ((iconState === 'active') ? '' : ' (' + iconState + ')')
});
@@ -164,16 +161,37 @@ function updateState () {
return;
}
const tabId = tabs[0].id;
- const activeCount = appliedRulesets.getActiveRulesetCount(tabId);
+ const tabUrl = new URL(tabs[0].url);
- if ('setBadgeBackgroundColor' in chrome.browserAction) {
- chrome.browserAction.setBadgeBackgroundColor({ color: '#666666', tabId });
- }
+ if (disabledList.has(tabUrl.host) || iconState == "disabled") {
+ if ('setIcon' in chrome.browserAction) {
+ chrome.browserAction.setIcon({
+ path: {
+ 38: 'images/icons/icon-disabled-38.png'
+ }
+ });
+ }
+ } else {
+
+ if ('setIcon' in chrome.browserAction) {
+ chrome.browserAction.setIcon({
+ path: {
+ 38: 'images/icons/icon-' + iconState + '-38.png'
+ }
+ });
+ }
+
+ const activeCount = appliedRulesets.getActiveRulesetCount(tabId);
+
+ if ('setBadgeBackgroundColor' in chrome.browserAction) {
+ chrome.browserAction.setBadgeBackgroundColor({ color: '#666666', tabId });
+ }
- const showBadge = activeCount > 0 && isExtensionEnabled && showCounter;
+ const showBadge = activeCount > 0 && isExtensionEnabled && showCounter;
- if ('setBadgeText' in chrome.browserAction) {
- chrome.browserAction.setBadgeText({ text: showBadge ? String(activeCount) : '', tabId });
+ if ('setBadgeText' in chrome.browserAction) {
+ chrome.browserAction.setBadgeText({ text: showBadge ? String(activeCount) : '', tabId });
+ }
}
});
}
@@ -277,10 +295,18 @@ var urlBlacklist = new Set();
// TODO: Remove this code if they ever give us a real counter
var redirectCounter = new Map();
+// Create a map to indicate whether a given request has been subject to a simple
+// HTTP Nowhere redirect.
+let simpleHTTPNowhereRedirect = new Map();
+
const cancelUrl = chrome.extension.getURL("/pages/cancel/index.html");
-function redirectOnCancel(shouldCancel){
- return shouldCancel ? {redirectUrl: cancelUrl} : {cancel: false};
+function redirectOnCancel(shouldCancel, originURL){
+ return shouldCancel ? {redirectUrl: newCancelUrl(originURL)} : {cancel: false};
+}
+
+function newCancelUrl(originURL){
+ return cancelUrl + "?originURL=" + encodeURI(originURL);
}
/**
@@ -296,6 +322,27 @@ function onBeforeRequest(details) {
let uri = new URL(details.url);
+ // Check if a user has disabled HTTPS Everywhere on this site. We should
+ // ensure that all subresources are not run through HTTPS Everywhere as well.
+ let firstPartyHost;
+ if (details.type == "main_frame") {
+ firstPartyHost = uri.host;
+ } else {
+ // In Firefox, documentUrl is preferable here, since it will always be the
+ // URL in the URL bar, but it was only introduced in FF 54. We should get
+ // rid of `originUrl` at some point.
+ if ('documentUrl' in details) { // Firefox 54+
+ firstPartyHost = new URL(details.documentUrl).host;
+ } else if ('originUrl' in details) { // Firefox < 54
+ firstPartyHost = new URL(details.originUrl).host;
+ } else if('initiator' in details) { // Chrome
+ firstPartyHost = new URL(details.initiator).host;
+ }
+ }
+ if (disabledList.has(firstPartyHost)) {
+ return;
+ }
+
// Normalise hosts with tailing dots, e.g. "www.example.com."
while (uri.hostname[uri.hostname.length - 1] === '.' && uri.hostname !== '.') {
uri.hostname = uri.hostname.slice(0, -1);
@@ -330,7 +377,7 @@ function onBeforeRequest(details) {
" changed before processing to " + uri.href);
}
if (urlBlacklist.has(uri.href)) {
- return redirectOnCancel(shouldCancel);
+ return redirectOnCancel(shouldCancel, details.url);
}
if (details.type == "main_frame") {
@@ -344,7 +391,7 @@ function onBeforeRequest(details) {
urlBlacklist.add(uri.href);
rules.settings.domainBlacklist.add(uri.hostname);
util.log(util.WARN, "Domain blacklisted " + uri.hostname);
- return redirectOnCancel(shouldCancel);
+ return redirectOnCancel(shouldCancel, details.url);
}
// whether to use mozilla's upgradeToSecure BlockingResponse if available
@@ -352,9 +399,11 @@ function onBeforeRequest(details) {
let newuristr = null;
for (let ruleset of potentiallyApplicable) {
- appliedRulesets.addRulesetToTab(details.tabId, details.type, ruleset);
- if (ruleset.active && !newuristr) {
- newuristr = ruleset.apply(uri.href);
+ if (details.url.match(ruleset.scope)) {
+ appliedRulesets.addRulesetToTab(details.tabId, details.type, ruleset);
+ if (ruleset.active && !newuristr) {
+ newuristr = ruleset.apply(uri.href);
+ }
}
}
@@ -396,6 +445,7 @@ function onBeforeRequest(details) {
if (shouldCancel) {
if (!newuristr) {
newuristr = uri.href.replace(/^http:/, "https:");
+ simpleHTTPNowhereRedirect.set(details.requestId, true);
upgradeToSecure = true;
} else {
newuristr = newuristr.replace(/^http:/, "https:");
@@ -409,7 +459,7 @@ function onBeforeRequest(details) {
)
) {
// Abort early if we're about to redirect to HTTP or FTP in HTTP Nowhere mode
- return {redirectUrl: cancelUrl};
+ return {redirectUrl: newCancelUrl(newuristr)};
}
}
@@ -421,7 +471,7 @@ function onBeforeRequest(details) {
return {redirectUrl: newuristr};
} else {
util.log(util.INFO, 'onBeforeRequest returning shouldCancel: ' + shouldCancel);
- return redirectOnCancel(shouldCancel);
+ return redirectOnCancel(shouldCancel, details.url);
}
}
@@ -577,6 +627,9 @@ function onCompleted(details) {
if (redirectCounter.has(details.requestId)) {
redirectCounter.delete(details.requestId);
}
+ if (simpleHTTPNowhereRedirect.has(details.requestId)) {
+ simpleHTTPNowhereRedirect.delete(details.requestId);
+ }
}
/**
@@ -584,9 +637,45 @@ function onCompleted(details) {
* @param details details for the chrome.webRequest (see chrome doc)
*/
function onErrorOccurred(details) {
+ if (httpNowhereOn &&
+ details.type == "main_frame" &&
+ simpleHTTPNowhereRedirect.get(details.requestId) &&
+ ( // Enumerate a class of errors that are likely due to HTTPS misconfigurations
+ details.error.indexOf("net::ERR_SSL_") == 0 ||
+ details.error.indexOf("net::ERR_CERT_") == 0 ||
+ details.error.indexOf("net::ERR_CONNECTION_") == 0 ||
+ details.error.indexOf("net::ERR_ABORTED") == 0 ||
+ details.error.indexOf("NS_ERROR_CONNECTION_REFUSED") == 0 ||
+ details.error.indexOf("NS_ERROR_UNKNOWN_HOST") == 0 ||
+ details.error.indexOf("NS_ERROR_NET_TIMEOUT") == 0 ||
+ details.error.indexOf("NS_ERROR_NET_ON_TLS_HANDSHAKE_ENDED") == 0 ||
+ details.error.indexOf("NS_BINDING_ABORTED") == 0 ||
+ details.error.indexOf("SSL received a record that exceeded the maximum permissible length.") == 0 ||
+ details.error.indexOf("Peer’s Certificate has expired.") == 0 ||
+ details.error.indexOf("Unable to communicate securely with peer: requested domain name does not match the server’s certificate.") == 0 ||
+ details.error.indexOf("Peer’s Certificate issuer is not recognized.") == 0 ||
+ details.error.indexOf("Peer’s Certificate has been revoked.") == 0 ||
+ details.error.indexOf("The server uses key pinning (HPKP) but no trusted certificate chain could be constructed that matches the pinset. Key pinning violations cannot be overridden.") == 0 ||
+ details.error.indexOf("SSL received a weak ephemeral Diffie-Hellman key in Server Key Exchange handshake message.") == 0 ||
+ details.error.indexOf("The certificate was signed using a signature algorithm that is disabled because it is not secure.") == 0 ||
+ details.error.indexOf("Unable to communicate securely with peer: requested domain name does not match the server’s certificate.") == 0 ||
+ details.error.indexOf("Cannot communicate securely with peer: no common encryption algorithm(s).") == 0 ||
+ details.error.indexOf("SSL peer has no certificate for the requested DNS name.") == 0
+ ))
+ {
+ let url = new URL(details.url);
+ if (url.protocol == "https:") {
+ url.protocol = "http:";
+ }
+ chrome.tabs.update(details.tabId, {url: newCancelUrl(url.toString())});
+ }
+
if (redirectCounter.has(details.requestId)) {
redirectCounter.delete(details.requestId);
}
+ if (simpleHTTPNowhereRedirect.has(details.requestId)) {
+ simpleHTTPNowhereRedirect.delete(details.requestId);
+ }
}
/**
@@ -742,6 +831,14 @@ chrome.runtime.onMessage.addListener(function(message, sender, sendResponse){
});
}
+ function storeDisabledList() {
+ const disabledListArray = Array.from(disabledList);
+ store.set({disabledList: disabledListArray}, () => {
+ sendResponse(true);
+ });
+ return true;
+ }
+
const responses = {
get_option: () => {
store.get(message.object, sendResponse);
@@ -791,6 +888,10 @@ chrome.runtime.onMessage.addListener(function(message, sender, sendResponse){
chrome.tabs.reload();
});
},
+ get_user_rules: () => {
+ store.get_promise(all_rules.USER_RULE_KEY, []).then(userRules => sendResponse(userRules));
+ return true;
+ },
add_new_rule: () => {
all_rules.addNewRuleAndStore(message.object).then(() => {
sendResponse(true);
@@ -798,7 +899,23 @@ chrome.runtime.onMessage.addListener(function(message, sender, sendResponse){
return true;
},
remove_rule: () => {
- all_rules.removeRuleAndStore(message.object);
+ all_rules.removeRuleAndStore(message.object.ruleset, message.object.src)
+ .then(() => {
+ /**
+ * FIXME: initializeAllRules is needed for calls from the option pages.
+ * Since message.object is not of type Ruleset, rules.removeUserRule
+ * is not usable...
+ */
+ if (message.object.src === 'options') {
+ return initializeAllRules();
+ }
+ })
+ .then(() => {
+ if (sendResponse !== null) {
+ sendResponse(true);
+ }
+ })
+ return true;
},
get_ruleset_timestamps: () => {
update.getRulesetTimestamps().then(timestamps => sendResponse(timestamps));
@@ -830,7 +947,8 @@ chrome.runtime.onMessage.addListener(function(message, sender, sendResponse){
item.update_channels.push({
name: message.object,
jwk: {},
- update_path_prefix: ''
+ update_path_prefix: '',
+ scope: ''
});
store.set({update_channels: item.update_channels}, () => {
@@ -859,14 +977,30 @@ chrome.runtime.onMessage.addListener(function(message, sender, sendResponse){
},
update_update_channel: () => {
store.get({update_channels: []}, item => {
- for(let i = 0; i < item.update_channels.length; i++){
- if(item.update_channels[i].name == message.object.name){
- item.update_channels[i] = message.object;
+ let scope_changed = false;
+ item.update_channels = item.update_channels.map(update_channel => {
+ if(update_channel.name == message.object.name){
+ if(update_channel.scope != message.object.scope){
+ scope_changed = true;
+ }
+ update_channel = message.object;
}
- }
+ return update_channel;
+ });
+ // Ensure that we check for new rulesets from the update channel immediately.
+ // If the scope has changed, make sure that the rulesets are re-initialized.
store.set({update_channels: item.update_channels}, () => {
- sendResponse(true);
+ // Since loadUpdateChannesKeys is already contained in chrome.storage.onChanged
+ // within update.js, the below call will make it run twice. This is
+ // necesssary to avoid a race condition, see #16673
+ update.loadUpdateChannelsKeys().then(() => {
+ update.resetTimer();
+ if(scope_changed){
+ initializeAllRules();
+ }
+ sendResponse(true);
+ });
});
});
@@ -877,6 +1011,18 @@ chrome.runtime.onMessage.addListener(function(message, sender, sendResponse){
sendResponse(item['last-checked']);
});
return true;
+ },
+ disable_on_site: () => {
+ disabledList.add(message.object);
+ return storeDisabledList();
+ },
+ enable_on_site: () => {
+ disabledList.delete(message.object);
+ return storeDisabledList();
+ },
+ check_if_site_disabled: () => {
+ sendResponse(disabledList.has(message.object));
+ return true;
}
};
if (message.type in responses) {
diff --git a/data/extensions/https-everywhere@eff.org/background-scripts/rules.js b/data/extensions/https-everywhere@eff.org/background-scripts/rules.js
index e2dac94..6b42cc6 100644
--- a/data/extensions/https-everywhere@eff.org/background-scripts/rules.js
+++ b/data/extensions/https-everywhere@eff.org/background-scripts/rules.js
@@ -25,6 +25,17 @@ const nullIterable = Object.create(null, {
},
});
+/* A map of all scope RegExp objects */
+const scopes = new Map();
+
+/* Returns the scope object from the map for the given scope string */
+function getScope(scope){
+ if (!scopes.has(scope)) {
+ scopes.set(scope, new RegExp(scope));
+ }
+ return scopes.get(scope);
+}
+
/**
* Constructs a single rule
* @param from
@@ -81,13 +92,14 @@ function CookieRule(host, cookiename) {
* @param note Note will be displayed in popup
* @constructor
*/
-function RuleSet(set_name, default_state, note) {
+function RuleSet(set_name, default_state, scope, note) {
this.name = set_name;
this.rules = [];
this.exclusions = null;
this.cookierules = null;
this.active = default_state;
this.default_state = default_state;
+ this.scope = scope;
this.note = note;
}
@@ -209,40 +221,45 @@ RuleSets.prototype = {
this.store = store;
this.ruleActiveStates = await this.store.get_promise('ruleActiveStates', {});
await applyStoredFunc(this);
- this.loadStoredUserRules();
+ await this.loadStoredUserRules();
await this.addStoredCustomRulesets();
},
/**
* Iterate through data XML and load rulesets
*/
- addFromXml: function(ruleXml) {
- var sets = ruleXml.getElementsByTagName("ruleset");
- for (let s of sets) {
+ addFromXml: function(ruleXml, scope) {
+ const scope_obj = getScope(scope);
+ const rulesets = ruleXml.getElementsByTagName("ruleset");
+ for (let ruleset of rulesets) {
try {
- this.parseOneXmlRuleset(s);
+ this.parseOneXmlRuleset(ruleset, scope_obj);
} catch (e) {
util.log(util.WARN, 'Error processing ruleset:' + e);
}
}
},
- addFromJson: function(ruleJson) {
+ addFromJson: function(ruleJson, scope) {
+ const scope_obj = getScope(scope);
for (let ruleset of ruleJson) {
try {
- this.parseOneJsonRuleset(ruleset);
+ this.parseOneJsonRuleset(ruleset, scope_obj);
} catch(e) {
util.log(util.WARN, 'Error processing ruleset:' + e);
}
}
},
- parseOneJsonRuleset: function(ruletag) {
+ parseOneJsonRuleset: function(ruletag, scope) {
var default_state = true;
var note = "";
var default_off = ruletag["default_off"];
if (default_off) {
default_state = false;
+ if (default_off === "user rule") {
+ default_state = true;
+ }
note += default_off + "\n";
}
@@ -257,7 +274,7 @@ RuleSets.prototype = {
note += "Platform(s): " + platform + "\n";
}
- var rule_set = new RuleSet(ruletag["name"], default_state, note.trim());
+ var rule_set = new RuleSet(ruletag["name"], default_state, scope, note.trim());
// Read user prefs
if (rule_set.name in this.ruleActiveStates) {
@@ -304,20 +321,16 @@ RuleSets.prototype = {
* @param params
* @returns {boolean}
*/
- addUserRule : function(params) {
+ addUserRule : function(params, scope) {
util.log(util.INFO, 'adding new user rule for ' + JSON.stringify(params));
- var new_rule_set = new RuleSet(params.host, true, "user rule");
- var new_rule = getRule(params.urlMatcher, params.redirectTo);
- new_rule_set.rules.push(new_rule);
- if (!this.targets.has(params.host)) {
- this.targets.set(params.host, []);
+ this.parseOneJsonRuleset(params, scope);
+
+ // clear cache so new rule take effect immediately
+ for (const target of params.target) {
+ this.ruleCache.delete(target);
}
- this.ruleCache.delete(params.host);
+
// TODO: maybe promote this rule?
- this.targets.get(params.host).push(new_rule_set);
- if (new_rule_set.name in this.ruleActiveStates) {
- new_rule_set.active = this.ruleActiveStates[new_rule_set.name];
- }
util.log(util.INFO, 'done adding rule');
return true;
},
@@ -327,20 +340,33 @@ RuleSets.prototype = {
* @param params
* @returns {boolean}
*/
- removeUserRule: function(ruleset) {
+ removeUserRule: function(ruleset, src) {
+ /**
+ * FIXME: We have to use ruleset.name here because the ruleset itself
+ * carries no information on the target it is applying on. This also
+ * made it impossible for users to set custom ruleset name.
+ */
util.log(util.INFO, 'removing user rule for ' + JSON.stringify(ruleset));
- this.ruleCache.delete(ruleset.name);
+ // Remove any cache from runtime
+ this.ruleCache.delete(ruleset.name);
- var tmp = this.targets.get(ruleset.name).filter(r =>
- !(r.isEquivalentTo(ruleset))
- );
- this.targets.set(ruleset.name, tmp);
+ if (src === 'popup') {
+ const tmp = this.targets.get(ruleset.name).filter(r => !r.isEquivalentTo(ruleset))
+ this.targets.set(ruleset.name, tmp);
- if (this.targets.get(ruleset.name).length == 0) {
- this.targets.delete(ruleset.name);
+ if (this.targets.get(ruleset.name).length == 0) {
+ this.targets.delete(ruleset.name);
+ }
}
+ if (src === 'options') {
+ /**
+ * FIXME: There is nothing we can do if the call comes from the
+ * option page because isEquivalentTo cannot work reliably.
+ * Leave the heavy duties to background.js to call initializeAllRules
+ */
+ }
util.log(util.INFO, 'done removing rule');
return true;
},
@@ -355,12 +381,12 @@ RuleSets.prototype = {
/**
* Load all stored user rules into this RuleSet object
*/
- loadStoredUserRules: async function() {
- const user_rules = await this.getStoredUserRules();
- for (let user_rule of user_rules) {
- this.addUserRule(user_rule);
- }
- util.log(util.INFO, 'loaded ' + user_rules.length + ' stored user rules');
+ loadStoredUserRules: function() {
+ return this.getStoredUserRules()
+ .then(userRules => {
+ this.addFromJson(userRules, getScope());
+ util.log(util.INFO, `loaded ${userRules.length} stored user rules`);
+ });
},
/**
@@ -369,7 +395,7 @@ RuleSets.prototype = {
* @param cb: Callback to call after success/fail
* */
addNewRuleAndStore: async function(params) {
- if (this.addUserRule(params)) {
+ if (this.addUserRule(params, getScope())) {
// If we successfully added the user rule, save it in the storage
// api so it's automatically applied when the extension is
// reloaded.
@@ -386,14 +412,21 @@ RuleSets.prototype = {
* Removes a user rule
* @param ruleset: the ruleset to remove
* */
- removeRuleAndStore: async function(ruleset) {
- if (this.removeUserRule(ruleset)) {
- // If we successfully removed the user rule, remove it in local storage too
+ removeRuleAndStore: async function(ruleset, src) {
+ if (this.removeUserRule(ruleset, src)) {
let userRules = await this.getStoredUserRules();
- userRules = userRules.filter(r =>
- !(r.host == ruleset.name &&
- r.redirectTo == ruleset.rules[0].to)
- );
+
+ if (src === 'popup') {
+ userRules = userRules.filter(r =>
+ !(r.name === ruleset.name && r.rule[0].to === ruleset.rules[0].to)
+ );
+ }
+
+ if (src === 'options') {
+ userRules = userRules.filter(r =>
+ !(r.name === ruleset.name && r.rule[0].to === ruleset.rule[0].to)
+ );
+ }
await this.store.set_promise(this.USER_RULE_KEY, userRules);
}
},
@@ -435,12 +468,15 @@ RuleSets.prototype = {
* Does the loading of a ruleset.
* @param ruletag The whole <ruleset> tag to parse
*/
- parseOneXmlRuleset: function(ruletag) {
+ parseOneXmlRuleset: function(ruletag, scope) {
var default_state = true;
var note = "";
var default_off = ruletag.getAttribute("default_off");
if (default_off) {
default_state = false;
+ if (default_off === "user rule") {
+ default_state = true;
+ }
note += default_off + "\n";
}
@@ -457,6 +493,7 @@ RuleSets.prototype = {
var rule_set = new RuleSet(ruletag.getAttribute("name"),
default_state,
+ scope,
note.trim());
// Read user prefs
diff --git a/data/extensions/https-everywhere@eff.org/background-scripts/store.js b/data/extensions/https-everywhere@eff.org/background-scripts/store.js
index 3b555c7..c41ccd0 100644
--- a/data/extensions/https-everywhere@eff.org/background-scripts/store.js
+++ b/data/extensions/https-everywhere@eff.org/background-scripts/store.js
@@ -51,29 +51,51 @@ function local_set_promise(key, value) {
async function performMigrations() {
- const migration_version = await get_promise('migration_version', 0);
-
- if (migration_version < 1) {
-
- let ls;
- try {
- ls = localStorage;
- } catch(e) {}
-
- let ruleActiveStates = {};
- for (const key in ls) {
- if (ls.hasOwnProperty(key)) {
- if (key == rules.RuleSets().USER_RULE_KEY){
- await set_promise(rules.RuleSets().USER_RULE_KEY, JSON.parse(ls[key]));
- } else {
- ruleActiveStates[key] = (ls[key] == "true");
+ let migration_version = await get_promise('migration_version', 0);
+
+ try {
+ if (migration_version === 0) {
+ let ls = localStorage;
+ let ruleActiveStates = {};
+
+ for (let key in ls) {
+ if (ls.hasOwnProperty(key)) {
+ if (rules.RuleSets().USER_RULE_KEY === key) {
+ await set_promise(rules.RuleSets().USER_RULE_KEY, JSON.parse(ls[key]));
+ } else {
+ ruleActiveStates[key] = (ls[key] === "true");
+ }
}
}
+ migration_version = 1;
+ await set_promise('migration_version', migration_version);
+ await set_promise('ruleActiveStates', ruleActiveStates);
}
- await set_promise('ruleActiveStates', ruleActiveStates);
+
+ } catch (e) {
+ // do nothing
}
- await set_promise('migration_version', 1);
+ if (migration_version <= 1) {
+ await get_promise(rules.RuleSets().USER_RULE_KEY, [])
+ .then(userRules => {
+ userRules = userRules.map(userRule => {
+ return {
+ name: userRule.host,
+ target: [userRule.host],
+ rule: [{ from: userRule.urlMatcher, to: userRule.redirectTo }],
+ default_off: "user rule"
+ }
+ })
+ return userRules;
+ })
+ .then(userRules => {
+ return set_promise(rules.RuleSets().USER_RULE_KEY, userRules);
+ })
+
+ migration_version = 2;
+ await set_promise('migration_version', migration_version);
+ }
}
const local = {
diff --git a/data/extensions/https-everywhere@eff.org/background-scripts/update.js b/data/extensions/https-everywhere@eff.org/background-scripts/update.js
index 795f968..7e4eedf 100644
--- a/data/extensions/https-everywhere@eff.org/background-scripts/update.js
+++ b/data/extensions/https-everywhere@eff.org/background-scripts/update.js
@@ -55,6 +55,7 @@ async function loadUpdateChannelsKeys() {
["verify"]
);
combined_update_channels.push(update_channel);
+ util.log(util.NOTE, update_channel.name + ': Update channel key loaded.');
} catch(err) {
util.log(util.WARN, update_channel.name + ': Could not import key. Aborting.');
}
@@ -74,6 +75,13 @@ async function timeToNextCheck() {
}
}
+// Check for new rulesets immediately
+async function resetTimer() {
+ await store.local.set_promise('last-checked', false);
+ destroyTimer();
+ await createTimer();
+}
+
// Check for new rulesets. If found, return the timestamp. If not, return false
async function checkForNewRulesets(update_channel) {
let timestamp_result = await fetch(update_channel.update_path_prefix + "/latest-rulesets-timestamp");
@@ -173,10 +181,10 @@ async function applyStoredRulesets(rulesets_obj){
const rulesets_gz = window.atob(root[key]);
const rulesets_byte_array = pako.inflate(rulesets_gz);
- const rulesets = new TextDecoder("utf-8").decode(rulesets_byte_array);
- const rulesets_json = JSON.parse(rulesets);
+ const rulesets_string = new TextDecoder("utf-8").decode(rulesets_byte_array);
+ const rulesets_json = JSON.parse(rulesets_string);
- resolve(rulesets_json);
+ resolve({json: rulesets_json, scope: update_channel.scope});
} else {
resolve();
}
@@ -184,12 +192,15 @@ async function applyStoredRulesets(rulesets_obj){
}));
}
- const rulesets_jsons = await Promise.all(rulesets_promises);
- if(rulesets_jsons.join("").length > 0){
- for(let rulesets_json of rulesets_jsons){
- if(typeof(rulesets_json) != 'undefined'){
- rulesets_obj.addFromJson(rulesets_json.rulesets);
- }
+ function isNotUndefined(subject){
+ return (typeof subject != 'undefined');
+ }
+
+ const channel_results = (await Promise.all(rulesets_promises)).filter(isNotUndefined);
+
+ if(channel_results.length > 0){
+ for(let channel_result of channel_results){
+ rulesets_obj.addFromJson(channel_result.json.rulesets, channel_result.scope);
}
} else {
rulesets_obj.addFromJson(util.loadExtensionFile('rules/default.rulesets', 'json'));
@@ -274,7 +285,9 @@ async function initialize(store_param, cb){
Object.assign(exports, {
applyStoredRulesets,
initialize,
- getRulesetTimestamps
+ getRulesetTimestamps,
+ resetTimer,
+ loadUpdateChannelsKeys
});
})(typeof exports == 'undefined' ? require.scopes.update = {} : exports);
diff --git a/data/extensions/https-everywhere@eff.org/background-scripts/update_channels.js b/data/extensions/https-everywhere@eff.org/background-scripts/update_channels.js
index 8bb21c9..71b1080 100644
--- a/data/extensions/https-everywhere@eff.org/background-scripts/update_channels.js
+++ b/data/extensions/https-everywhere@eff.org/background-scripts/update_channels.js
@@ -17,7 +17,8 @@ exports.update_channels = [{
'6agsuamOyiOtlZiRpEvoNg2ksJMZtwnj5xhBQydkdhMW2ZpHDzcLuZlhJYZL_l3_7wuzRM7vpyA9obP92CpZRFJErGZmFxJC93I4U9-0B0wg' +
'-sbyMKGJ5j1BWTnibCklDXtWzXtuiz18EgE'
},
- update_path_prefix: 'https://www.https-rulesets.org/v1/'
+ update_path_prefix: 'https://www.https-rulesets.org/v1/',
+ scope: ''
}];
})(typeof exports === 'undefined' ? require.scopes.update_channels = {} : exports);