-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Embedded WebExtension #11760
Embedded WebExtension #11760
Changes from all commits
66cd627
16f2545
f5188b4
95f62d1
a2d1b1d
2948c3d
00dd74c
aba46a8
1993ef3
fdd933f
48d8d3e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,7 @@ | ||
{ | ||
"parserOptions": { | ||
"ecmaVersion": 2017 | ||
}, | ||
"env": { | ||
"browser": true, | ||
"commonjs": true, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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). | ||
|
@@ -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(); | ||
}); | ||
|
@@ -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; | ||
|
@@ -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) { | ||
|
@@ -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]; | ||
} | ||
}; | ||
|
||
|
@@ -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); | ||
} | ||
|
@@ -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); | ||
}); | ||
|
||
/** | ||
* Enable switch planner for specific tab | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Incorrect comment There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Removal of closure resolved in #11792 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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; | ||
} | ||
} |
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; | ||
} |
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> |
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); | ||
}); |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in #11792