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.js111
-rw-r--r--data/extensions/https-everywhere@eff.org/background-scripts/rules.js43
-rw-r--r--data/extensions/https-everywhere@eff.org/background-scripts/store.js8
-rw-r--r--data/extensions/https-everywhere@eff.org/background-scripts/update.js87
-rw-r--r--data/extensions/https-everywhere@eff.org/background-scripts/update_channels.js3
-rw-r--r--data/extensions/https-everywhere@eff.org/background-scripts/util.js4
6 files changed, 156 insertions, 100 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 28c7ead..c432d74 100644
--- a/data/extensions/https-everywhere@eff.org/background-scripts/background.js
+++ b/data/extensions/https-everywhere@eff.org/background-scripts/background.js
@@ -33,26 +33,22 @@ async function initializeAllRules() {
* Load preferences. Structure is:
* {
* httpNowhere: Boolean,
- * showCounter: Boolean,
* isExtensionEnabled: Boolean
* }
*/
var httpNowhereOn = false;
-var showCounter = true;
var isExtensionEnabled = true;
let disabledList = new Set();
-function initializeStoredGlobals(){
+function initializeStoredGlobals() {
return new Promise(resolve => {
store.get({
httpNowhere: false,
- showCounter: true,
globalEnabled: true,
enableMixedRulesets: false,
- disabledList: [],
+ disabledList: []
}, function(item) {
httpNowhereOn = item.httpNowhere;
- showCounter = item.showCounter;
isExtensionEnabled = item.globalEnabled;
for (let disabledSite of item.disabledList) {
disabledList.add(disabledSite);
@@ -92,10 +88,6 @@ chrome.storage.onChanged.addListener(async function(changes, areaName) {
httpNowhereOn = changes.httpNowhere.newValue;
updateState();
}
- if ('showCounter' in changes) {
- showCounter = changes.showCounter.newValue;
- updateState();
- }
if ('globalEnabled' in changes) {
isExtensionEnabled = changes.globalEnabled.newValue;
updateState();
@@ -160,7 +152,6 @@ function updateState () {
if (!tabs || tabs.length === 0) {
return;
}
- const tabId = tabs[0].id;
const tabUrl = new URL(tabs[0].url);
if (disabledList.has(tabUrl.host) || iconState == "disabled") {
@@ -172,7 +163,6 @@ function updateState () {
});
}
} else {
-
if ('setIcon' in chrome.browserAction) {
chrome.browserAction.setIcon({
path: {
@@ -180,18 +170,6 @@ function updateState () {
}
});
}
-
- const activeCount = appliedRulesets.getActiveRulesetCount(tabId);
-
- if ('setBadgeBackgroundColor' in chrome.browserAction) {
- chrome.browserAction.setBadgeBackgroundColor({ color: '#666666', tabId });
- }
-
- const showBadge = activeCount > 0 && isExtensionEnabled && showCounter;
-
- if ('setBadgeText' in chrome.browserAction) {
- chrome.browserAction.setBadgeText({ text: showBadge ? String(activeCount) : '', tabId });
- }
}
});
}
@@ -200,7 +178,7 @@ function updateState () {
* The following allows fennec to interact with the popup ui
* */
chrome.browserAction.onClicked.addListener(e => {
- const url = chrome.extension.getURL("/pages/popup/index.html?tabId=" + e.id);
+ const url = chrome.runtime.getURL("/pages/popup/index.html?tabId=" + e.id);
chrome.tabs.create({
url
});
@@ -299,13 +277,13 @@ var redirectCounter = new Map();
// HTTP Nowhere redirect.
let simpleHTTPNowhereRedirect = new Map();
-const cancelUrl = chrome.extension.getURL("/pages/cancel/index.html");
+const cancelUrl = chrome.runtime.getURL("/pages/cancel/index.html");
-function redirectOnCancel(shouldCancel, originURL){
+function redirectOnCancel(shouldCancel, originURL) {
return shouldCancel ? {redirectUrl: newCancelUrl(originURL)} : {cancel: false};
}
-function newCancelUrl(originURL){
+function newCancelUrl(originURL) {
return cancelUrl + "?originURL=" + encodeURI(originURL);
}
@@ -320,6 +298,14 @@ function onBeforeRequest(details) {
return;
}
+ // Clear the content shown in the extension popup.
+ // This needed to be done before this listener returns,
+ // otherwise, the extension page might include rulesets
+ // from previous page.
+ if (details.type == "main_frame") {
+ appliedRulesets.removeTab(details.tabId);
+ }
+
let uri = new URL(details.url);
// Check if a user has disabled HTTPS Everywhere on this site. We should
@@ -380,12 +366,6 @@ function onBeforeRequest(details) {
return redirectOnCancel(shouldCancel, details.url);
}
- if (details.type == "main_frame") {
- appliedRulesets.removeTab(details.tabId);
- }
-
- let potentiallyApplicable = all_rules.potentiallyApplicableRulesets(uri.hostname);
-
if (redirectCounter.get(details.requestId) >= 8) {
util.log(util.NOTE, "Redirect counter hit for " + uri.href);
urlBlacklist.add(uri.href);
@@ -398,6 +378,8 @@ function onBeforeRequest(details) {
let upgradeToSecure = false;
let newuristr = null;
+ let potentiallyApplicable = all_rules.potentiallyApplicableRulesets(uri.hostname);
+
for (let ruleset of potentiallyApplicable) {
if (details.url.match(ruleset.scope)) {
appliedRulesets.addRulesetToTab(details.tabId, details.type, ruleset);
@@ -549,7 +531,9 @@ function sortSwitchPlanner(tab_id, rewritten) {
var score = activeCount * 100 + passiveCount;
asset_host_list.push([score, activeCount, passiveCount, asset_host]);
}
- asset_host_list.sort(function(a,b){return a[0]-b[0];});
+ asset_host_list.sort(function(a,b) {
+ return a[0]-b[0];
+ });
return asset_host_list;
}
@@ -645,8 +629,8 @@ function onErrorOccurred(details) {
details.error.indexOf("net::ERR_CERT_") == 0 ||
details.error.indexOf("net::ERR_CONNECTION_") == 0 ||
details.error.indexOf("net::ERR_ABORTED") == 0 ||
+ details.error.indexOf("net::ERR_SSL_PROTOCOL_ERROR") == 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 ||
@@ -655,14 +639,14 @@ function onErrorOccurred(details) {
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("Peer reports it experienced an internal error.") == 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:";
@@ -685,13 +669,34 @@ function onErrorOccurred(details) {
*/
function onHeadersReceived(details) {
if (isExtensionEnabled && httpNowhereOn) {
- // Do not upgrade the .onion requests in HTTP Nowhere Mode,
+ // Do not upgrade the .onion requests in EASE mode,
// See https://github.com/EFForg/https-everywhere/pull/14600#discussion_r168072480
const uri = new URL(details.url);
if (uri.hostname.slice(-6) == '.onion') {
return {};
}
+ // Do not upgrade resources if the first-party domain disbled EASE mode
+ // This is needed for HTTPS sites serve mixed content and is broken
+ 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 {};
+ }
+
let responseHeadersChanged = false;
let cspHeaderFound = false;
@@ -778,7 +783,7 @@ function enableSwitchPlannerFor(tabId) {
// Listen for connection from the DevTools panel so we can set up communication.
chrome.runtime.onConnect.addListener(function (port) {
if (port.name == "devtools-page") {
- chrome.runtime.onMessage.addListener(function(message, sender, sendResponse){
+ chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) {
var tabId = message.tabId;
var disableOnCloseCallback = function() {
@@ -811,9 +816,9 @@ chrome.runtime.onConnect.addListener(function (port) {
// This is necessary for communication with the popup in Firefox Private
// Browsing Mode, see https://bugzilla.mozilla.org/show_bug.cgi?id=1329304
-chrome.runtime.onMessage.addListener(function(message, sender, sendResponse){
+chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) {
- function get_update_channels_generic(update_channels){
+ function get_update_channels_generic(update_channels) {
let last_updated_promises = [];
for(let update_channel of update_channels) {
last_updated_promises.push(new Promise(resolve => {
@@ -940,7 +945,7 @@ chrome.runtime.onMessage.addListener(function(message, sender, sendResponse){
return obj;
}, new Set());
- if(update_channel_names.has(message.object)){
+ if(update_channel_names.has(message.object)) {
return sendResponse(false);
}
@@ -979,8 +984,8 @@ chrome.runtime.onMessage.addListener(function(message, sender, sendResponse){
store.get({update_channels: []}, item => {
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){
+ if(update_channel.name == message.object.name) {
+ if(update_channel.scope != message.object.scope) {
scope_changed = true;
}
update_channel = message.object;
@@ -996,7 +1001,7 @@ chrome.runtime.onMessage.addListener(function(message, sender, sendResponse){
// necesssary to avoid a race condition, see #16673
update.loadUpdateChannelsKeys().then(() => {
update.resetTimer();
- if(scope_changed){
+ if(scope_changed) {
initializeAllRules();
}
sendResponse(true);
@@ -1023,6 +1028,20 @@ chrome.runtime.onMessage.addListener(function(message, sender, sendResponse){
check_if_site_disabled: () => {
sendResponse(disabledList.has(message.object));
return true;
+ },
+ is_firefox: () => {
+ if(typeof(browser) != "undefined") {
+ browser.runtime.getBrowserInfo().then(function(info) {
+ if (info.name == "Firefox") {
+ sendResponse(true);
+ } else {
+ sendResponse(false);
+ }
+ });
+ } else {
+ sendResponse(false);
+ }
+ 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 6b42cc6..c4ac18d 100644
--- a/data/extensions/https-everywhere@eff.org/background-scripts/rules.js
+++ b/data/extensions/https-everywhere@eff.org/background-scripts/rules.js
@@ -29,7 +29,7 @@ const nullIterable = Object.create(null, {
const scopes = new Map();
/* Returns the scope object from the map for the given scope string */
-function getScope(scope){
+function getScope(scope) {
if (!scopes.has(scope)) {
scopes.set(scope, new RegExp(scope));
}
@@ -174,7 +174,7 @@ RuleSet.prototype = {
}
if(this_rules_length > 0) {
- for(let x = 0; x < this.rules.length; x++){
+ for(let x = 0; x < this.rules.length; x++) {
if(this.rules[x].to != ruleset.rules[x].to) {
return false;
}
@@ -363,7 +363,7 @@ RuleSets.prototype = {
if (src === 'options') {
/**
* FIXME: There is nothing we can do if the call comes from the
- * option page because isEquivalentTo cannot work reliably.
+ * option page because isEquivalentTo cannot work reliably.
* Leave the heavy duties to background.js to call initializeAllRules
*/
}
@@ -431,7 +431,7 @@ RuleSets.prototype = {
}
},
- addStoredCustomRulesets: function(){
+ addStoredCustomRulesets: function() {
return new Promise(resolve => {
this.store.get({
legacy_custom_rulesets: [],
@@ -445,17 +445,17 @@ RuleSets.prototype = {
},
// Load in the legacy custom rulesets, if any
- loadCustomRulesets: function(legacy_custom_rulesets){
- for(let legacy_custom_ruleset of legacy_custom_rulesets){
+ loadCustomRulesets: function(legacy_custom_rulesets) {
+ for(let legacy_custom_ruleset of legacy_custom_rulesets) {
this.loadCustomRuleset(legacy_custom_ruleset);
}
},
- loadCustomRuleset: function(ruleset_string){
+ loadCustomRuleset: function(ruleset_string) {
this.addFromXml((new DOMParser()).parseFromString(ruleset_string, 'text/xml'));
},
- setRuleActiveState: async function(ruleset_name, active){
+ setRuleActiveState: async function(ruleset_name, active) {
if (active == undefined) {
delete this.ruleActiveStates[ruleset_name];
} else {
@@ -562,22 +562,23 @@ RuleSets.prototype = {
return nullIterable;
}
- // Replace each portion of the domain with a * in turn
+ // Replace www.example.com with www.example.*
+ // eat away from the right for once and only once
let segmented = host.split(".");
- for (let i = 0; i < segmented.length; i++) {
- let tmp = segmented[i];
- segmented[i] = "*";
+ if (segmented.length > 1) {
+ const tmp = segmented[segmented.length - 1];
+ segmented[segmented.length - 1] = "*";
results = (this.targets.has(segmented.join(".")) ?
new Set([...results, ...this.targets.get(segmented.join("."))]) :
results);
- segmented[i] = tmp;
+ segmented[segmented.length - 1] = tmp;
}
// now eat away from the left, with *, so that for x.y.z.google.com we
- // check *.z.google.com and *.google.com (we did *.y.z.google.com above)
- for (let i = 2; i <= segmented.length - 2; i++) {
+ // check *.y.z.google.com, *.z.google.com and *.google.com
+ for (let i = 1; i <= segmented.length - 2; i++) {
let t = "*." + segmented.slice(i, segmented.length).join(".");
results = (this.targets.has(t) ?
@@ -625,11 +626,10 @@ RuleSets.prototype = {
}
var potentiallyApplicable = this.potentiallyApplicableRulesets(hostname);
- for (let ruleset of potentiallyApplicable) {
+ for (const ruleset of potentiallyApplicable) {
if (ruleset.cookierules !== null && ruleset.active) {
- for (let cookierules of ruleset.cookierules) {
- var cr = cookierules;
- if (cr.host_c.test(cookie.domain) && cr.name_c.test(cookie.name)) {
+ for (const cookierule of ruleset.cookierules) {
+ if (cookierule.host_c.test(cookie.domain) && cookierule.name_c.test(cookie.name)) {
return ruleset;
}
}
@@ -682,10 +682,7 @@ RuleSets.prototype = {
util.log(util.INFO, "Testing securecookie applicability with " + test_uri);
var potentiallyApplicable = this.potentiallyApplicableRulesets(domain);
for (let ruleset of potentiallyApplicable) {
- if (!ruleset.active) {
- continue;
- }
- if (ruleset.apply(test_uri)) {
+ if (ruleset.active && ruleset.apply(test_uri)) {
util.log(util.INFO, "Cookie domain could be secured.");
this.cookieHostCache.set(domain, true);
return true;
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 c41ccd0..9698c18 100644
--- a/data/extensions/https-everywhere@eff.org/background-scripts/store.js
+++ b/data/extensions/https-everywhere@eff.org/background-scripts/store.js
@@ -8,7 +8,7 @@ function initialize() {
return new Promise(resolve => {
if (chrome.storage.sync) {
chrome.storage.sync.set({"sync-set-test": true}, () => {
- if(chrome.runtime.lastError){
+ if(chrome.runtime.lastError) {
setStorage(chrome.storage.local);
} else {
setStorage(chrome.storage.sync);
@@ -99,9 +99,9 @@ async function performMigrations() {
}
const local = {
- get: chrome.storage.local.get,
- set: chrome.storage.local.set,
- remove: chrome.storage.local.remove,
+ get: (...args) => chrome.storage.local.get(...args),
+ set: (...args) => chrome.storage.local.set(...args),
+ remove: (...args) => chrome.storage.local.remove(...args),
get_promise: local_get_promise,
set_promise: local_set_promise
};
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 7e4eedf..1363384 100644
--- a/data/extensions/https-everywhere@eff.org/background-scripts/update.js
+++ b/data/extensions/https-everywhere@eff.org/background-scripts/update.js
@@ -2,7 +2,7 @@
"use strict";
-let combined_update_channels;
+let combined_update_channels, extension_version;
const { update_channels } = require('./update_channels');
// Determine if we're in the tests. If so, define some necessary components.
@@ -14,9 +14,14 @@ if (typeof window === "undefined") {
pako = require('../external/pako-1.0.5/pako_inflate.min.js'),
{ TextDecoder } = require('text-encoding'),
chrome = require("sinon-chrome"),
- window = { atob, btoa, chrome, crypto, pako, TextDecoder };
+ window = { atob, btoa, chrome, crypto, pako, TextDecoder },
+ fs = require('fs');
+
+ extension_version = JSON.parse(fs.readFileSync('./manifest.json')).version;
combined_update_channels = update_channels;
+} else {
+ extension_version = chrome.runtime.getManifest().version;
}
(function(exports) {
@@ -29,6 +34,9 @@ let store,
// how often we should check for new rulesets
const periodicity = 86400;
+const extension_date = new Date(extension_version.split('.').slice(0,3).join('-'));
+const extension_timestamp = extension_date.getTime() / 1000;
+
let imported_keys;
// update channels are loaded from `background-scripts/update_channels.js` as well as the storage api
@@ -41,7 +49,7 @@ async function loadUpdateChannelsKeys() {
imported_keys = {};
combined_update_channels = [];
- for(let update_channel of combined_update_channels_preflight){
+ for(let update_channel of combined_update_channels_preflight) {
try{
imported_keys[update_channel.name] = await window.crypto.subtle.importKey(
@@ -88,7 +96,7 @@ async function checkForNewRulesets(update_channel) {
if(timestamp_result.status == 200) {
let rulesets_timestamp = Number(await timestamp_result.text());
- if((await store.local.get_promise('rulesets-timestamp: ' + update_channel.name, 0)) < rulesets_timestamp){
+ if((await store.local.get_promise('rulesets-timestamp: ' + update_channel.name, 0)) < rulesets_timestamp) {
return rulesets_timestamp;
}
}
@@ -96,12 +104,12 @@ async function checkForNewRulesets(update_channel) {
}
// Retrieve the timestamp for when a stored ruleset bundle was published
-async function getRulesetTimestamps(){
+async function getRulesetTimestamps() {
let timestamp_promises = [];
- for(let update_channel of combined_update_channels){
+ for(let update_channel of combined_update_channels) {
timestamp_promises.push(new Promise(async resolve => {
let timestamp = await store.local.get_promise('rulesets-stored-timestamp: ' + update_channel.name, 0);
- resolve([update_channel.name, timestamp]);
+ resolve([update_channel, timestamp]);
}));
}
let timestamps = await Promise.all(timestamp_promises);
@@ -135,7 +143,7 @@ async function getNewRulesets(rulesets_timestamp, update_channel) {
// Returns a promise which verifies that the rulesets have a valid EFF
// signature, and if so, stores them and returns true.
// Otherwise, it throws an exception.
-function verifyAndStoreNewRulesets(new_rulesets, rulesets_timestamp, update_channel){
+function verifyAndStoreNewRulesets(new_rulesets, rulesets_timestamp, update_channel) {
return new Promise((resolve, reject) => {
window.crypto.subtle.verify(
{
@@ -154,7 +162,7 @@ function verifyAndStoreNewRulesets(new_rulesets, rulesets_timestamp, update_chan
const rulesets = new TextDecoder("utf-8").decode(rulesets_byte_array);
const rulesets_json = JSON.parse(rulesets);
- if(rulesets_json.timestamp != rulesets_timestamp){
+ if(rulesets_json.timestamp != rulesets_timestamp) {
reject(update_channel.name + ': Downloaded ruleset had an incorrect timestamp. This may be an attempted downgrade attack. Aborting.');
} else {
await store.local.set_promise('rulesets: ' + update_channel.name, window.btoa(rulesets_gz));
@@ -170,13 +178,13 @@ function verifyAndStoreNewRulesets(new_rulesets, rulesets_timestamp, update_chan
}
// Unzip and apply the rulesets we have stored.
-async function applyStoredRulesets(rulesets_obj){
+async function applyStoredRulesets(rulesets_obj) {
let rulesets_promises = [];
- for(let update_channel of combined_update_channels){
+ for(let update_channel of combined_update_channels) {
rulesets_promises.push(new Promise(resolve => {
const key = 'rulesets: ' + update_channel.name;
chrome.storage.local.get(key, root => {
- if(root[key]){
+ if(root[key]) {
util.log(util.NOTE, update_channel.name + ': Applying stored rulesets.');
const rulesets_gz = window.atob(root[key]);
@@ -184,7 +192,7 @@ async function applyStoredRulesets(rulesets_obj){
const rulesets_string = new TextDecoder("utf-8").decode(rulesets_byte_array);
const rulesets_json = JSON.parse(rulesets_string);
- resolve({json: rulesets_json, scope: update_channel.scope});
+ resolve({json: rulesets_json, scope: update_channel.scope, replaces: update_channel.replaces_default_rulesets});
} else {
resolve();
}
@@ -192,17 +200,21 @@ async function applyStoredRulesets(rulesets_obj){
}));
}
- function isNotUndefined(subject){
+ 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);
+ let replaces = false;
+ for(const channel_result of channel_results) {
+ if(channel_result.replaces === true) {
+ replaces = true;
}
- } else {
+ rulesets_obj.addFromJson(channel_result.json.rulesets, channel_result.scope);
+ }
+
+ if(!replaces) {
rulesets_obj.addFromJson(util.loadExtensionFile('rules/default.rulesets', 'json'));
}
}
@@ -215,9 +227,15 @@ async function performCheck() {
store.local.set_promise('last-checked', current_timestamp);
let num_updates = 0;
- for(let update_channel of combined_update_channels){
+ for(let update_channel of combined_update_channels) {
let new_rulesets_timestamp = await checkForNewRulesets(update_channel);
- if(new_rulesets_timestamp){
+ if(new_rulesets_timestamp) {
+
+ if(update_channel.replaces_default_rulesets && extension_timestamp > new_rulesets_timestamp) {
+ util.log(util.NOTE, update_channel.name + ': A new ruleset bundle has been released, but it is older than the extension-bundled rulesets it replaces. Skipping.');
+ continue;
+ }
+
util.log(util.NOTE, update_channel.name + ': A new ruleset bundle has been released. Downloading now.');
let new_rulesets = await getNewRulesets(new_rulesets_timestamp, update_channel);
try{
@@ -229,7 +247,7 @@ async function performCheck() {
}
}
}
- if(num_updates > 0){
+ if(num_updates > 0) {
background_callback();
}
};
@@ -253,7 +271,7 @@ chrome.storage.onChanged.addListener(async function(changes, areaName) {
let initialCheck,
subsequentChecks;
-async function createTimer(){
+async function createTimer() {
const time_to_next_check = await timeToNextCheck();
initialCheck = setTimeout(() => {
@@ -262,7 +280,7 @@ async function createTimer(){
}, time_to_next_check * 1000);
}
-function destroyTimer(){
+function destroyTimer() {
if (initialCheck) {
clearTimeout(initialCheck);
}
@@ -271,12 +289,33 @@ function destroyTimer(){
}
}
-async function initialize(store_param, cb){
+function clear_replacement_update_channels() {
+ let keys = [];
+ for (const update_channel of combined_update_channels) {
+ if(update_channel.replaces_default_rulesets) {
+ util.log(util.NOTE, update_channel.name + ': You have a new version of the extension. Clearing any stored rulesets, which replace the new extension-bundled ones.');
+ keys.push('rulesets-timestamp: ' + update_channel.name);
+ keys.push('rulesets-stored-timestamp: ' + update_channel.name);
+ keys.push('rulesets: ' + update_channel.name);
+ }
+ }
+
+ return new Promise(resolve => {
+ chrome.storage.local.remove(keys, resolve);
+ });
+}
+
+async function initialize(store_param, cb) {
store = store_param;
background_callback = cb;
await loadUpdateChannelsKeys();
+ if (await store.local.get_promise('extensionTimestamp', 0) !== extension_timestamp) {
+ await clear_replacement_update_channels();
+ await store.local.set_promise('extensionTimestamp', extension_timestamp);
+ }
+
if (await store.get_promise('autoUpdateRulesets', false)) {
await createTimer();
}
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 71b1080..f21b332 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
@@ -18,7 +18,8 @@ exports.update_channels = [{
'-sbyMKGJ5j1BWTnibCklDXtWzXtuiz18EgE'
},
update_path_prefix: 'https://www.https-rulesets.org/v1/',
- scope: ''
+ scope: '',
+ replaces_default_rulesets: true
}];
})(typeof exports === 'undefined' ? require.scopes.update_channels = {} : exports);
diff --git a/data/extensions/https-everywhere@eff.org/background-scripts/util.js b/data/extensions/https-everywhere@eff.org/background-scripts/util.js
index 06c87ad..1946e14 100644
--- a/data/extensions/https-everywhere@eff.org/background-scripts/util.js
+++ b/data/extensions/https-everywhere@eff.org/background-scripts/util.js
@@ -45,7 +45,7 @@ function loadExtensionFile(url, returnType) {
var xhr = new XMLHttpRequest();
// Use blocking XHR to ensure everything is loaded by the time
// we return.
- xhr.open("GET", chrome.extension.getURL(url), false);
+ xhr.open("GET", chrome.runtime.getURL(url), false);
xhr.send(null);
// Get file contents
if (xhr.readyState !== 4) {
@@ -69,7 +69,7 @@ function ArrayBufferToString(ab) {
let array = new Uint8Array(ab);
let string = "";
- for (let byte of array){
+ for (let byte of array) {
string += String.fromCharCode(byte);
}