Skip to content
This repository has been archived by the owner on Nov 6, 2023. It is now read-only.

Embedded WebExtension #11760

Closed
wants to merge 11 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
[submodule "addon-sdk"]
path = addon-sdk
url = https://github.com/mozilla/addon-sdk.git
[submodule "translations"]
path = translations
url = https://git.torproject.org/translation.git
Expand Down
1 change: 0 additions & 1 deletion addon-sdk
Submodule addon-sdk deleted from 12f7d5
3 changes: 3 additions & 0 deletions chromium/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
{
"parserOptions": {
"ecmaVersion": 2017
},
"env": {
"browser": true,
"commonjs": true,
Expand Down
153 changes: 138 additions & 15 deletions chromium/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,15 @@ storage.get({enableMixedRulesets: false}, function(item) {
all_rules.addFromXml(loadExtensionFile('rules/default.rulesets', 'xml'));
});

// Load in the legacy custom rulesets, if any
storage.get({legacy_custom_rulesets: false}, item => {
if(item.legacy_custom_rulesets){
for(let legacy_custom_ruleset of item.legacy_custom_rulesets){
all_rules.addFromXml((new DOMParser()).parseFromString(legacy_custom_ruleset, 'text/xml'));
}
}
});

var USER_RULE_KEY = 'userRules';
// Records which tabId's are active in the HTTPS Switch Planner (see
// devtools-panel.js).
Expand Down Expand Up @@ -61,12 +70,16 @@ chrome.storage.onChanged.addListener(function(changes, areaName) {
}
}
});
chrome.tabs.onActivated.addListener(function() {
updateState();
});
chrome.windows.onFocusChanged.addListener(function() {
updateState();
});
if (chrome.tabs) {
chrome.tabs.onActivated.addListener(function() {
updateState();
});
}
if (chrome.windows) {
chrome.windows.onFocusChanged.addListener(function() {
updateState();
});
}
chrome.webNavigation.onCompleted.addListener(function() {
updateState();
});
Expand Down Expand Up @@ -106,6 +119,9 @@ loadStoredUserRules();
* disabled: extension is disabled from the popup menu.
*/
var updateState = function() {
if (!chrome.tabs) {
return;
}
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
if (!tabs || tabs.length === 0) {
return;
Expand Down Expand Up @@ -174,21 +190,20 @@ var removeRule = function(ruleset) {
* */
function AppliedRulesets() {
this.active_tab_rules = {};
this.active_tab_hostnames = {};

var that = this;
chrome.tabs.onRemoved.addListener(function(tabId, info) {
that.removeTab(tabId);
});
if (chrome.tabs) {
chrome.tabs.onRemoved.addListener(function(tabId, info) {
that.removeTab(tabId);
});
}
}

AppliedRulesets.prototype = {
addRulesetToTab: function(tabId, ruleset) {
if (tabId in this.active_tab_rules) {
this.active_tab_rules[tabId][ruleset.name] = ruleset;
} else {
this.active_tab_rules[tabId] = {};
this.active_tab_rules[tabId][ruleset.name] = ruleset;
}
this.active_tab_rules[tabId] = this.active_tab_rules[tabId] || {};
this.active_tab_rules[tabId][ruleset.name] = ruleset;
},

getRulesets: function(tabId) {
Expand All @@ -198,8 +213,21 @@ AppliedRulesets.prototype = {
return null;
},

addHostnameToTab: function(tabId, ruleset_name, hostname) {
this.active_tab_hostnames[tabId] = this.active_tab_hostnames[tabId] || {};
this.active_tab_hostnames[tabId][ruleset_name] = hostname;
},

getHostnames: function(tabId) {
if (tabId in this.active_tab_hostnames) {
return this.active_tab_hostnames[tabId];
}
return null;
},

removeTab: function(tabId) {
delete this.active_tab_rules[tabId];
delete this.active_tab_hostnames[tabId];
}
};

Expand Down Expand Up @@ -283,6 +311,7 @@ function onBeforeRequest(details) {

for (let ruleset of potentiallyApplicable) {
activeRulesets.addRulesetToTab(details.tabId, ruleset);
activeRulesets.addHostnameToTab(details.tabId, ruleset.name, uri.hostname);
if (ruleset.active && !newuristr) {
newuristr = ruleset.apply(canonical_url);
}
Expand Down Expand Up @@ -618,3 +647,97 @@ 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){
if (message.type == "get_option") {
storage.get(message.object, function(item){
sendResponse(item);
});
return true;
} else if (message.type == "set_option") {
storage.set(message.object);
} else if (message.type == "get_is_extension_enabled") {
sendResponse(isExtensionEnabled);
} else if (message.type == "set_is_extension_enabled") {
isExtensionEnabled = message.object;
sendResponse(isExtensionEnabled);
} else if (message.type == "delete_from_ruleset_cache") {
all_rules.ruleCache.delete(message.object);
} else if (message.type == "get_active_rulesets_and_hostnames") {
sendResponse({
rulesets: activeRulesets.getRulesets(message.object),
hostnames: activeRulesets.getHostnames(message.object)
});
} else if (message.type == "set_ruleset_active_status") {
var ruleset = activeRulesets.getRulesets(message.object.tab_id)[message.object.name];
ruleset.active = message.object.active;
sendResponse(true);
} else if (message.type == "add_new_rule") {
addNewRule(message.object, function() {
sendResponse(true);
});
return true;
} else if (message.type == "update_state") {
updateState();
} else if (message.type == "remove_rule") {
removeRule(message.object);
} else if (message.type == "import_settings") {
import_settings(message.object).then(() => {
sendResponse(true);
});
}
});

// Send a message to the embedded webextension bootstrap.js to get settings to import
chrome.runtime.sendMessage("import-legacy-data", function(settings){
import_settings(settings);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can just provide import_settings directly as the callback parameter to sendMessage, right?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in #11792

});

/**
* Enable switch planner for specific tab
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Incorrect comment

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in #11792

* @param settings the settings object
*/
async function import_settings(settings){
if(settings.changed){
// Load custom rulesets and add to storage
await new Promise(resolve => {
for(let ruleset of settings.custom_rulesets){
all_rules.addFromXml((new DOMParser()).parseFromString(ruleset, 'text/xml'));
}
storage.set({"legacy_custom_rulesets": settings.custom_rulesets}, item => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly, no need to define a closure here, just pass resolve as the callback.

Why store this under "legacy_custom_rulesets" rather than store the rulesets in the same way as the other custom rulesets? That way we could just call the appropriate function to load all custom rulesets when we're done, eliminating a possible edge case.

Copy link
Member Author

@Hainish Hainish Aug 9, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jsha the other custom rulesets are specially formatted objects that do not allow specifying, for instance, exclusions. Here is one example:

[{"host":"example.com","redirectTo":"https://example.com/","urlMatcher":"^http://example\\.com/"}]

This was never in XML format, since it is added via the WebExtensions UI. From that format, a RuleSet object is created. This format, notably, does not in its current form support exclusions & securecookies. We'd have to build support for that, as well as code that converts XML to the custom_rule format. I'm not against doing this, but it seems like the job for a future migration.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removal of closure resolved in #11792

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds reasonable on the custom rulesets vs XML thing.

resolve(item);
});
});

// Load all the ruleset toggles into memory and store
let rule_toggle_promises = [];
for(let ruleset_name in settings.rule_toggle){
localStorage[ruleset_name] = settings.rule_toggle[ruleset_name];

rule_toggle_promises.push((ruleset_name, ruleset_active => {
return new Promise(resolve => {
for(let host in all_rules.targets){
for(let ruleset of all_rules.targets[host]){
if(ruleset.name == ruleset_name){
ruleset.active = ruleset_active;
resolve(true);
return;
}
}
}
resolve(false);
})
})(ruleset_name, settings.rule_toggle[ruleset_name]));
}
await Promise.all(rule_toggle_promises);

all_rules.ruleCache = new Map();

// Set/store globals
storage.set({'httpNowhere': settings.prefs.http_nowhere_enabled});
storage.set({'showCounter': settings.prefs.show_counter});
isExtensionEnabled = settings.prefs.global_enabled;
}
}
8 changes: 6 additions & 2 deletions chromium/incognito-cache-clearing.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,11 @@ function detect_incognito_destruction(windowId) {


// Listen to window creation, so we can detect if an incognito window is created
chrome.windows.onCreated.addListener(detect_incognito_creation);
if (chrome.windows) {
chrome.windows.onCreated.addListener(detect_incognito_creation);
}

// Listen to window destruction, so we can clear caches if all incognito windows are destroyed
chrome.windows.onRemoved.addListener(detect_incognito_destruction);
if (chrome.windows) {
chrome.windows.onRemoved.addListener(detect_incognito_destruction);
}
15 changes: 11 additions & 4 deletions chromium/manifest.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
{
"author": {
"email": "eff.software.projects@gmail.com"
},
"applications": {
"gecko": {
"id": "https-everywhere-eff@eff.org",
"update_url": "https://www.eff.org/files/https-everywhere-updates.json"
}
},
"author": "eff.software.projects@gmail.com",
"background": {
"scripts": [
"rules.js",
Expand All @@ -11,6 +15,9 @@
"incognito-cache-clearing.js"
]
},
"options_ui": {
"page": "options.html"
},
"browser_action": {
"default_icon": {
"38": "icons/icon-active-38.png"
Expand All @@ -28,7 +35,7 @@
},
"incognito": "spanning",
"manifest_version": 2,
"minimum_chrome_version": "52",
"minimum_chrome_version": "55",
"name": "__MSG_about_ext_name__",
"permissions": [
"webNavigation",
Expand Down
17 changes: 17 additions & 0 deletions chromium/options.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
.section-header{
margin-bottom: 10px;
}

.section-header-span{
border-bottom: 1px solid #ccc;
font-size: 15px;
}

#import{
margin-bottom: 10px;
float: right;
}

#import-confirmed{
display: none;
}
22 changes: 22 additions & 0 deletions chromium/options.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link href="options.css" rel="stylesheet" type="text/css"/>
<script src="options.js"></script>
<script src="translation.js"></script>
<script src="send-message.js"></script>
</head>
<body>
<div id='import-confirmed' i18n="options_imported"></div>

<form>
<div class="section-header"><span class="section-header-span" i18n="options_importSettings"></span></div>
<section id="ImportSettings" class="options">
<input id="import-settings" type="file" accept="application/json" value="importSettings" />
</section>
<button type="submit" id="import" i18n="options_import" disabled></button>
</form>

</body>
</html>
28 changes: 28 additions & 0 deletions chromium/options.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
document.addEventListener("DOMContentLoaded", () => {

let json_data;
let import_button = document.querySelector("#import");

function import_json(e) {
e.preventDefault();

let settings = JSON.parse(json_data);
sendMessage("import_settings", settings, resp => {
document.querySelector("#import-confirmed").style.display = "block";
document.querySelector("form").style.display = "none";
});
}

document.querySelector("#import-settings").addEventListener("change", event => {
const file = event.target.files[0];
const reader = new FileReader();
reader.onload = event => {
json_data = event.target.result;
import_button.disabled = false;
};

reader.readAsText(file);
});

document.querySelector("form").addEventListener("submit", import_json);
});
1 change: 1 addition & 0 deletions chromium/popup.css
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ section.rules {
/* Override display attribute of text labels in widgets.css. */
.rule span {
display: inline !important;
margin-left: 0.6em;
}

body {
Expand Down
2 changes: 2 additions & 0 deletions chromium/popup.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

<link href="popup.css" rel="stylesheet" type="text/css"/>
<script src="popup.js"></script>
<script src="translation.js"></script>
<script src="send-message.js"></script>

</head>

Expand Down
Loading