Commit 7cb1f949 authored by Richard Pospesel's avatar Richard Pospesel Committed by Matthew Finkel
Browse files

Bug 25658: Replace security slider with security level UI

This patch adds a new 'securitylevel' component to Tor Browser intended
to replace the torbutton 'Security Slider'.

This component adds a new Security Level toolbar button which visually
indicates the current global security level via icon (as defined by the
extensions.torbutton.security_slider pref), a drop-down hanger with a
short description of the current security level, and a new section in
the about:preferences#privacy page where users can change their current
security level. In addition, the hanger and the preferences page will
show a visual warning when the user has modified prefs associated with
the security level and provide a one-click 'Restore Defaults' button to
get the user back on recommended settings.

Strings used by this patch are pulled from the torbutton extension, but
en-US defaults are provided if there is an error loading from the
extension. With this patch applied, the usual work-flow of "./mach build
&& ./mach run" work as expected, even if the torbutton extension is
disabled.
parent 09349a91
......@@ -223,6 +223,11 @@ XPCOMUtils.defineLazyScriptGetter(
["DownloadsButton", "DownloadsIndicatorView"],
"chrome://browser/content/downloads/indicator.js"
);
XPCOMUtils.defineLazyScriptGetter(
this,
["SecurityLevelButton"],
"chrome://browser/content/securitylevel/securityLevel.js"
);
XPCOMUtils.defineLazyScriptGetter(
this,
"gEditItemOverlay",
......@@ -1837,6 +1842,9 @@ var gBrowserInit = {
// doesn't flicker as the window is being shown.
DownloadsButton.init();
// Init the SecuritySettingsButton
SecurityLevelButton.init();
// Certain kinds of automigration rely on this notification to complete
// their tasks BEFORE the browser window is shown. SessionStore uses it to
// restore tabs into windows AFTER important parts like gMultiProcessBrowser
......@@ -2564,6 +2572,8 @@ var gBrowserInit = {
DownloadsButton.uninit();
SecurityLevelButton.uninit();
gAccessibilityServiceIndicator.uninit();
if (gToolbarKeyNavEnabled) {
......
......@@ -20,6 +20,8 @@
<?xml-stylesheet href="chrome://browser/content/browser.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/content/tabbrowser.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/content/downloads/downloads.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/content/securitylevel/securityLevelPanel.css"?>
<?xml-stylesheet href="chrome://browser/content/securitylevel/securityLevelButton.css"?>
<?xml-stylesheet href="chrome://browser/content/places/places.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/content/usercontext/usercontext.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/" type="text/css"?>
......@@ -656,6 +658,7 @@
#include ../../components/controlcenter/content/protectionsPanel.inc.xhtml
#include ../../components/downloads/content/downloadsPanel.inc.xhtml
#include ../../../devtools/startup/enableDevToolsPopup.inc.xhtml
#include ../../components/securitylevel/content/securityLevelPanel.inc.xhtml
#include browser-allTabsMenu.inc.xhtml
<hbox id="downloads-animation-container">
......@@ -2104,6 +2107,8 @@
</stack>
</toolbarbutton>
#include ../../components/securitylevel/content/securityLevelButton.inc.xhtml
<toolbarbutton id="library-button" class="toolbarbutton-1 chromeclass-toolbar-additional subviewbutton-nav"
removable="true"
onmousedown="PanelUI.showSubView('appMenu-libraryView', this, event);"
......
......@@ -50,6 +50,7 @@ DIRS += [
"protocolhandler",
"resistfingerprinting",
"search",
"securitylevel",
"sessionstore",
"shell",
"syncedtabs",
......
......@@ -12,6 +12,7 @@
<?xml-stylesheet href="chrome://browser/skin/preferences/search.css"?>
<?xml-stylesheet href="chrome://browser/skin/preferences/containers.css"?>
<?xml-stylesheet href="chrome://browser/skin/preferences/privacy.css"?>
<?xml-stylesheet href="chrome://browser/content/securitylevel/securityLevelPreferences.css"?>
<!DOCTYPE html>
......
......@@ -924,6 +924,8 @@
<html:h1 data-l10n-id="security-header"/>
</hbox>
#include ../securitylevel/content/securityLevelPreferences.inc.xhtml
<!-- addons, forgery (phishing) UI Security -->
<groupbox id="browsingProtectionGroup" data-category="panePrivacy" hidden="true">
<label><html:h2 data-l10n-id="security-browsing-protection"/></label>
......
......@@ -80,6 +80,12 @@ XPCOMUtils.defineLazyGetter(this, "AlertsServiceDND", function() {
}
});
XPCOMUtils.defineLazyScriptGetter(
this,
["SecurityLevelPreferences"],
"chrome://browser/content/securitylevel/securityLevel.js"
);
XPCOMUtils.defineLazyServiceGetter(
this,
"listManager",
......@@ -305,6 +311,18 @@ function setUpContentBlockingWarnings() {
var gPrivacyPane = {
_pane: null,
/**
* Show the Security Level UI
*/
_initSecurityLevel() {
SecurityLevelPreferences.init();
let unload = () => {
window.removeEventListener("unload", unload);
SecurityLevelPreferences.uninit();
};
window.addEventListener("unload", unload);
},
/**
* Whether the prompt to restart Firefox should appear when changing the autostart pref.
*/
......@@ -504,6 +522,7 @@ var gPrivacyPane = {
this.trackingProtectionReadPrefs();
this.networkCookieBehaviorReadPrefs();
this._initTrackingProtectionExtensionControl();
this._initSecurityLevel();
Services.telemetry.setEventRecordingEnabled("pwmgr", true);
......
"use strict";
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyModuleGetters(this, {
CustomizableUI: "resource:///modules/CustomizableUI.jsm",
PanelMultiView: "resource:///modules/PanelMultiView.jsm",
});
ChromeUtils.defineModuleGetter(
this,
"TorStrings",
"resource:///modules/TorStrings.jsm"
);
/*
Security Level Prefs
Getters and Setters for relevant torbutton prefs
*/
const SecurityLevelPrefs = {
security_slider_pref : "extensions.torbutton.security_slider",
security_custom_pref : "extensions.torbutton.security_custom",
get securitySlider() {
try {
return Services.prefs.getIntPref(this.security_slider_pref);
} catch(e) {
// init pref to 4 (standard)
const val = 4;
Services.prefs.setIntPref(this.security_slider_pref, val);
return val;
}
},
set securitySlider(val) {
Services.prefs.setIntPref(this.security_slider_pref, val);
},
get securityCustom() {
try {
return Services.prefs.getBoolPref(this.security_custom_pref);
} catch(e) {
// init custom to false
const val = false;
Services.prefs.setBoolPref(this.security_custom_pref, val);
return val;
}
},
set securityCustom(val) {
Services.prefs.setBoolPref(this.security_custom_pref, val);
},
}; /* Security Level Prefs */
/*
Security Level Button Code
Controls init and update of the security level toolbar button
*/
const SecurityLevelButton = {
_securityPrefsBranch : null,
_populateXUL : function(securityLevelButton) {
if (securityLevelButton != null) {
securityLevelButton.setAttribute("tooltiptext", TorStrings.securityLevel.securityLevel);
securityLevelButton.setAttribute("label", TorStrings.securityLevel.securityLevel);
}
},
_configUIFromPrefs : function(securityLevelButton) {
if (securityLevelButton != null) {
let securitySlider = SecurityLevelPrefs.securitySlider;
let classList = securityLevelButton.classList;
classList.remove("standard", "safer", "safest");
switch(securitySlider) {
case 4:
classList.add("standard");
securityLevelButton.setAttribute("tooltiptext", TorStrings.securityLevel.standard.tooltip);
break;
case 2:
classList.add("safer");
securityLevelButton.setAttribute("tooltiptext", TorStrings.securityLevel.safer.tooltip);
break;
case 1:
classList.add("safest");
securityLevelButton.setAttribute("tooltiptext", TorStrings.securityLevel.safest.tooltip);
break;
}
}
},
get button() {
let button = document.getElementById("security-level-button");
if (!button) {
return null;
}
return button;
},
get anchor() {
let anchor = this.button.icon;
if (!anchor) {
return null;
}
anchor.setAttribute("consumeanchor", SecurityLevelButton.button.id);
return anchor;
},
init : function() {
// set the initial class based off of the current pref
let button = this.button;
this._populateXUL(button);
this._configUIFromPrefs(button);
this._securityPrefsBranch = Services.prefs.getBranch("extensions.torbutton.");
this._securityPrefsBranch.addObserver("", this, false);
CustomizableUI.addListener(this);
SecurityLevelPanel.init();
},
uninit : function() {
CustomizableUI.removeListener(this);
this._securityPrefsBranch.removeObserver("", this);
this._securityPrefsBranch = null;
SecurityLevelPanel.uninit();
},
observe : function(subject, topic, data) {
switch(topic) {
case "nsPref:changed":
if (data == "security_slider") {
this._configUIFromPrefs(this.button);
}
break;
}
},
// callback for entering the 'Customize Firefox' screen to set icon
onCustomizeStart : function(window) {
let navigatorToolbox = document.getElementById("navigator-toolbox");
let button = navigatorToolbox.palette.querySelector("#security-level-button");
this._populateXUL(button);
this._configUIFromPrefs(button);
},
// callback when CustomizableUI modifies DOM
onWidgetAfterDOMChange : function(aNode, aNextNode, aContainer, aWasRemoval) {
if (aNode.id == "security-level-button" && !aWasRemoval) {
this._populateXUL(aNode);
this._configUIFromPrefs(aNode);
}
},
// for when the toolbar button needs to be activated and displays the Security Level panel
//
// In the toolbarbutton xul you'll notice we register this callback for both onkeypress and
// onmousedown. We do this to match the behavior of other panel spawning buttons such as Downloads,
// Library, and the Hamburger menus. Using oncommand alone would result in only getting fired
// after onclick, which is mousedown followed by mouseup.
onCommand : function(aEvent) {
// snippet stolen from /browser/components/downloads/indicator.js DownloadsIndicatorView.onCommand(evt)
if (
(aEvent.type == "mousedown" && aEvent.button != 0) ||
(aEvent.type == "keypress" && aEvent.key != " " && aEvent.key != "Enter")
) {
return;
}
// we need to set this attribute for the button to be shaded correctly to look like it is pressed
// while the security level panel is open
this.button.setAttribute("open", "true");
SecurityLevelPanel.show();
},
}; /* Security Level Button */
/*
Security Level Panel Code
Controls init and update of the panel in the security level hanger
*/
const SecurityLevelPanel = {
_securityPrefsBranch : null,
_panel : null,
_anchor : null,
_populated : false,
_populateXUL : function() {
// get the panel elements we need to populate
let panelview = document.getElementById("securityLevel-panelview");
let labelHeader = panelview.querySelector("#securityLevel-header");
let labelCustomWarning = panelview.querySelector("#securityLevel-customWarning")
let labelLearnMore = panelview.querySelector("#securityLevel-learnMore");
let buttonRestoreDefaults = panelview.querySelector("#securityLevel-restoreDefaults");
let buttonAdvancedSecuritySettings = panelview.querySelector("#securityLevel-advancedSecuritySettings");
labelHeader.setAttribute("value", TorStrings.securityLevel.securityLevel);
labelCustomWarning.setAttribute("value", TorStrings.securityLevel.customWarning);
labelLearnMore.setAttribute("value", TorStrings.securityLevel.learnMore);
labelLearnMore.setAttribute("href", TorStrings.securityLevel.learnMoreURL);
buttonRestoreDefaults.setAttribute("label", TorStrings.securityLevel.restoreDefaults);
buttonAdvancedSecuritySettings.setAttribute("label", TorStrings.securityLevel.advancedSecuritySettings);
// rest of the XUL is set based on security prefs
this._configUIFromPrefs();
this._populated = true;
},
_configUIFromPrefs : function() {
// get security prefs
let securitySlider = SecurityLevelPrefs.securitySlider;
let securityCustom = SecurityLevelPrefs.securityCustom;
// get the panel elements we need to populate
let panelview = document.getElementById("securityLevel-panelview");
let labelLevel = panelview.querySelector("#securityLevel-level");
let labelCustomWarning = panelview.querySelector("#securityLevel-customWarning")
let summary = panelview.querySelector("#securityLevel-summary");
let buttonRestoreDefaults = panelview.querySelector("#securityLevel-restoreDefaults");
let buttonAdvancedSecuritySettings = panelview.querySelector("#securityLevel-advancedSecuritySettings");
// only visible when user is using custom settings
labelCustomWarning.hidden = !securityCustom;
buttonRestoreDefaults.hidden = !securityCustom;
// Descriptions change based on security level
switch(securitySlider) {
// standard
case 4:
labelLevel.setAttribute("value", TorStrings.securityLevel.standard.level);
summary.textContent = TorStrings.securityLevel.standard.summary;
break;
// safer
case 2:
labelLevel.setAttribute("value", TorStrings.securityLevel.safer.level);
summary.textContent = TorStrings.securityLevel.safer.summary;
break;
// safest
case 1:
labelLevel.setAttribute("value", TorStrings.securityLevel.safest.level);
summary.textContent = TorStrings.securityLevel.safest.summary;
break;
}
// override the summary text with custom warning
if (securityCustom) {
summary.textContent = TorStrings.securityLevel.custom.summary;
}
},
init : function() {
this._securityPrefsBranch = Services.prefs.getBranch("extensions.torbutton.");
this._securityPrefsBranch.addObserver("", this, false);
},
uninit : function() {
this._securityPrefsBranch.removeObserver("", this);
this._securityPrefsBranch = null;
},
show : function() {
// we have to defer this until after the browser has finished init'ing before
// we can populate the panel
if (!this._populated) {
this._populateXUL();
}
let panel = document.getElementById("securityLevel-panel");
panel.hidden = false;
PanelMultiView.openPopup(panel, SecurityLevelButton.anchor, "bottomcenter topright",
0, 0, false, null).catch(Cu.reportError);
},
hide : function() {
let panel = document.getElementById("securityLevel-panel");
PanelMultiView.hidePopup(panel);
},
restoreDefaults : function() {
SecurityLevelPrefs.securityCustom = false;
// hide and reshow so that layout re-renders properly
this.hide();
this.show(this._anchor);
},
openAdvancedSecuritySettings : function() {
openPreferences("privacy-securitylevel");
this.hide();
},
// callback when prefs change
observe : function(subject, topic, data) {
switch(topic) {
case "nsPref:changed":
if (data == "security_slider" || data == "security_custom") {
this._configUIFromPrefs();
}
break;
}
},
// callback when the panel is displayed
onPopupShown : function(event) {
SecurityLevelButton.button.setAttribute("open", "true");
},
// callback when the panel is hidden
onPopupHidden : function(event) {
SecurityLevelButton.button.removeAttribute("open");
}
}; /* Security Level Panel */
/*
Security Level Preferences Code
Code to handle init and update of security level section in about:preferences#privacy
*/
const SecurityLevelPreferences =
{
_securityPrefsBranch : null,
_populateXUL : function() {
let groupbox = document.getElementById("securityLevel-groupbox");
let labelHeader = groupbox.querySelector("#securityLevel-header");
labelHeader.textContent = TorStrings.securityLevel.securityLevel;
let spanOverview = groupbox.querySelector("#securityLevel-overview");
spanOverview.textContent = TorStrings.securityLevel.overview;
let labelLearnMore = groupbox.querySelector("#securityLevel-learnMore");
labelLearnMore.setAttribute("value", TorStrings.securityLevel.learnMore);
labelLearnMore.setAttribute("href", TorStrings.securityLevel.learnMoreURL);
let radiogroup = document.getElementById("securityLevel-radiogroup");
radiogroup.addEventListener("command", SecurityLevelPreferences.selectSecurityLevel);
let populateRadioElements = function(vboxQuery, stringStruct) {
let vbox = groupbox.querySelector(vboxQuery);
let radio = vbox.querySelector("radio");
radio.setAttribute("label", stringStruct.level);
let customWarning = vbox.querySelector("#securityLevel-customWarning");
customWarning.setAttribute("value", TorStrings.securityLevel.customWarning);
let labelSummary = vbox.querySelector("#securityLevel-summary");
labelSummary.textContent = stringStruct.summary;
let labelRestoreDefaults = vbox.querySelector("#securityLevel-restoreDefaults");
labelRestoreDefaults.setAttribute("value", TorStrings.securityLevel.restoreDefaults);
labelRestoreDefaults.addEventListener("click", SecurityLevelPreferences.restoreDefaults);
let description1 = vbox.querySelector("#securityLevel-description1");
if (description1) {
description1.textContent = stringStruct.description1;
}
let description2 = vbox.querySelector("#securityLevel-description2");
if (description2) {
description2.textContent = stringStruct.description2;
}
let description3 = vbox.querySelector("#securityLevel-description3");
if (description3) {
description3.textContent = stringStruct.description3;
}
};
populateRadioElements("#securityLevel-vbox-standard", TorStrings.securityLevel.standard);
populateRadioElements("#securityLevel-vbox-safer", TorStrings.securityLevel.safer);
populateRadioElements("#securityLevel-vbox-safest", TorStrings.securityLevel.safest);
},
_configUIFromPrefs : function() {
// read our prefs
let securitySlider = SecurityLevelPrefs.securitySlider;
let securityCustom = SecurityLevelPrefs.securityCustom;
// get our elements
let groupbox = document.getElementById("securityLevel-groupbox");
let radiogroup = groupbox.querySelector("#securityLevel-radiogroup");
let labelStandardCustom = groupbox.querySelector("#securityLevel-vbox-standard label#securityLevel-customWarning");
let labelSaferCustom = groupbox.querySelector("#securityLevel-vbox-safer label#securityLevel-customWarning");
let labelSafestCustom = groupbox.querySelector("#securityLevel-vbox-safest label#securityLevel-customWarning");
let labelStandardRestoreDefaults = groupbox.querySelector("#securityLevel-vbox-standard label#securityLevel-restoreDefaults");
let labelSaferRestoreDefaults = groupbox.querySelector("#securityLevel-vbox-safer label#securityLevel-restoreDefaults");
let labelSafestRestoreDefaults = groupbox.querySelector("#securityLevel-vbox-safest label#securityLevel-restoreDefaults");
// hide custom label by default until we know which level we're at
labelStandardCustom.hidden = true;
labelSaferCustom.hidden = true;
labelSafestCustom.hidden = true;
labelStandardRestoreDefaults.hidden = true;
labelSaferRestoreDefaults.hidden = true;
labelSafestRestoreDefaults.hidden = true;
switch(securitySlider) {
// standard
case 4:
radiogroup.value = "standard";
labelStandardCustom.hidden = !securityCustom;
labelStandardRestoreDefaults.hidden = !securityCustom;
break;
// safer
case 2:
radiogroup.value = "safer";
labelSaferCustom.hidden = !securityCustom;
labelSaferRestoreDefaults.hidden = !securityCustom;
break;
// safest
case 1:
radiogroup.value = "safest";
labelSafestCustom.hidden = !securityCustom;
labelSafestRestoreDefaults.hidden = !securityCustom;
break;
}
},
init : function() {
// populate XUL with localized strings
this._populateXUL();
// read prefs and populate UI
this._configUIFromPrefs();
// register for pref chagnes
this._securityPrefsBranch = Services.prefs.getBranch("extensions.torbutton.");
this._securityPrefsBranch.addObserver("", this, false);
},
uninit : function() {
// unregister for pref change events
this._securityPrefsBranch.removeObserver("", this);
this._securityPrefsBranch = null;
},
// callback for when prefs change
observe : function(subject, topic, data) {
switch(topic) {
case "nsPref:changed":
if (data == "security_slider" ||
data == "security_custom") {
this._configUIFromPrefs();
}
break;
}
},
selectSecurityLevel : function() {
// radio group elements
let radiogroup = document.getElementById("securityLevel-radiogroup");
// update pref based on selected radio option
switch (radiogroup.value) {
case "standard":