diff options
Diffstat (limited to 'data/extensions/https-everywhere@eff.org/background-scripts')
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); |