Commit e18194b1 authored by Kathleen Brade's avatar Kathleen Brade
Browse files

Bug 11773: setup wizard UI flow improvements

Add a new "Reconfigure" prompt that is displayed after
  bootstrapping fails. This makes it so users experience the
  same configuration sequence each time rather than forcing
  them to move backwards through the wizard pages.
Add a new "Remove Settings and Connect" prompt that is
  displayed if the user clicks "Connect" while some bridge or
  proxy settings are present. This avoids a problem where old
  settings may be used without users being aware of them.
Improve some of the configuration prompts and status text based
  on early feedback from UC Berkeley usability studies.
parent 099454f2
......@@ -19,7 +19,7 @@
windowtype="TorLauncher:NetworkSettings"
persist="screenX screenY"
buttonlabelextra2="&torsettings.copyLog;"
onwizardfinish="return applySettings();"
onwizardfinish="return applySettings(false);"
onwizardcancel="return onCancel();"
onload="initDialog();"
onunload="deinitDialog();">
......@@ -46,7 +46,8 @@
<separator/>
<label>&torSettings.connectPrompt2;</label>
<label>&torSettings.connectPrompt3;</label>
<button label="&torSettings.connect;" oncommand="useSettings();"/>
<button label="&torSettings.connect;"
oncommand="onWizardFirstPanelConnect();"/>
<separator class="tall"/>
<label>&torSettings.configurePrompt1;</label>
<label>&torSettings.configurePrompt2;</label>
......@@ -74,7 +75,9 @@
<radio id="bridgesRadioYes" label="&torSettings.yes;" />
<radio id="bridgesRadioNo" label="&torSettings.no;" selected="true" />
</radiogroup>
<description class="questionHelp">&torSettings.bridgeHelp;
<description class="questionHelp">&torSettings.bridgeExplanation1;
</description>
<description class="questionHelp">&torSettings.bridgeExplanation2;
</description>
</vbox>
</hbox>
......@@ -94,7 +97,8 @@
<separator />
<vbox>
<label id="bridgeSettingsPrompt"
class="question">&torSettings.bridgeSettingsPrompt;</label>
class="question">&torSettings.bridgeSettingsPrompt;&#160;
&torsettings.useBridges.note;</label>
<groupbox id="bridgeSpecificSettings" />
</vbox>
</wizardpage>
......@@ -120,7 +124,9 @@
<radio id="proxyRadioYes" label="&torSettings.yes;" />
<radio id="proxyRadioNo" label="&torSettings.no;" selected="true" />
</radiogroup>
<description class="questionHelp">&torSettings.proxyHelp;
<description class="questionHelp">&torSettings.proxyExplanation1;
</description>
<description class="questionHelp">&torSettings.proxyExplanation2;
</description>
</vbox>
</hbox>
......@@ -168,8 +174,36 @@
<separator/>
<hbox>
<spring flex="1" />
<button id="restartTorButton" label="&torsettings.restartTor;" hidden="true"
oncommand="onRestartTor()" />
<button id="restartTorButton" label="&torsettings.restartTor;"
hidden="true" oncommand="onRestartTor()" />
<button id="reconfigTorButton" label="&torsettings.reconfigTor;"
hidden="true" oncommand="onWizardReconfig()" />
<spring flex="1" />
</hbox>
<spring flex="1" />
</wizardpage>
<wizardpage pageid="discardSettings" next="notUsed"
onpageshow="showWizardNavButtons(false);"
onextra2="onCopyLog();">
<hbox class="tbb-header">
<vbox class="tbb-logo-box" align="start">
<image class="tbb-logo" />
</vbox>
</hbox>
<spring flex="1" />
<hbox>
<spring flex="1" />
<description flex="1">&torsettings.discardSettings.prompt;</description>
<spring flex="1" />
</hbox>
<separator/>
<hbox>
<spring flex="1" />
<button id="discardSettingsGoBack" oncommand="showPanel();"/>
<separator/>
<button label="&torsettings.discardSettings.proceed;"
oncommand="removeSettingsAndConnect()" />
<spring flex="1" />
</hbox>
<spring flex="1" />
......
......@@ -35,6 +35,8 @@ const kTorLogHasWarnOrErrTopic = "TorLogHasWarnOrErr";
const kWizardProxyRadioGroup = "proxyRadioGroup";
const kWizardUseBridgesRadioGroup = "useBridgesRadioGroup";
const kWizardFirstPageID = "first";
const kLocaleList = "localeList";
const kUseProxyCheckbox = "useProxy";
const kProxyTypeMenulist = "proxyType";
......@@ -200,6 +202,12 @@ function initDialog()
if (accessKey)
helpBtn.setAttribute("accesskey", accessKey);
}
// Set Discard Settings back button label to match the wizard Back button.
let wizardBackBtn = document.documentElement.getButton("back");
let backBtn = document.getElementById("discardSettingsGoBack");
if (wizardBackBtn && backBtn)
backBtn.label = wizardBackBtn.label;
}
initDefaultBridgeTypeMenu();
......@@ -214,7 +222,7 @@ function initDialog()
(status != gTorProcessService.kStatusRunning))
{
if (status == gTorProcessService.kStatusExited)
showErrorMessage(true, null);
showErrorMessage(true, null, false);
else
showStartingTorPanel();
addObserver(kTorProcessReadyTopic);
......@@ -384,6 +392,25 @@ function getWizard()
}
function onWizardFirstPanelConnect()
{
// If the user configured bridge or proxy settings, prompt before
// discarding their data.
if (isBridgeConfigured() || isProxyConfigured())
showPanel("discardSettings");
else
removeSettingsAndConnect()
}
function removeSettingsAndConnect()
{
applySettings(true); // Use default settings.
if (!gIsBootstrapComplete)
readTorSettings(); // Ensure UI matches the settings that were used.
}
function onWizardConfigure()
{
getWizard().advance("bridges");
......@@ -497,12 +524,12 @@ var gObserver = {
{
removeObserver(kTorProcessReadyTopic);
removeObserver(kTorProcessDidNotStartTopic);
showErrorMessage(false, aData);
showErrorMessage(false, aData, false);
}
else if (kTorProcessExitedTopic == aTopic)
{
removeObserver(kTorProcessExitedTopic);
showErrorMessage(true, null);
showErrorMessage(true, null, false);
}
else if (kTorOpenProgressTopic == aTopic)
{
......@@ -560,7 +587,7 @@ function readTorSettings()
if (!didSucceed)
{
// Unable to communicate with tor. Hide settings and display an error.
showErrorMessage(false, null);
showErrorMessage(false, null, false);
setTimeout(function()
{
......@@ -581,7 +608,7 @@ function showPanel(aPanelID)
{
var wizard = getWizard();
if (!aPanelID)
aPanelID = (wizard) ? "first" : "settings";
aPanelID = (wizard) ? kWizardFirstPageID : "settings";
var deckElem = document.getElementById("deck");
if (deckElem)
......@@ -589,7 +616,7 @@ function showPanel(aPanelID)
else if (wizard.currentPage.pageid != aPanelID)
wizard.goTo(aPanelID);
if (wizard && (aPanelID == "first"))
if (wizard && (aPanelID == kWizardFirstPageID))
setTimeout( function() { showWizardNavButtons(false); }, 0);
showOrHideButton("accept", (aPanelID == "settings"), true);
......@@ -627,7 +654,7 @@ function showStartingTorPanel()
}
function showErrorMessage(aTorExited, aErrorMsg)
function showErrorMessage(aTorExited, aErrorMsg, aShowReconfigButton)
{
var elem = document.getElementById("errorPanelMessage");
var btn = document.getElementById("restartTorButton");
......@@ -653,6 +680,15 @@ function showErrorMessage(aTorExited, aErrorMsg)
if (elem)
elem.textContent = (aErrorMsg) ? aErrorMsg : "";
let reconfigBtn = document.getElementById("reconfigTorButton");
if (reconfigBtn)
{
if (aShowReconfigButton)
reconfigBtn.removeAttribute("hidden");
else
reconfigBtn.setAttribute("hidden", true);
}
showPanel("errorPanel");
var haveWizard = (getWizard() != null);
......@@ -856,6 +892,17 @@ function onRestartTor()
}
function onWizardReconfig()
{
showPanel(kWizardFirstPageID);
onWizardConfigure();
// Because a similar delayed call is used to hide the buttons when the
// first wizard page is displayed, we use setTimeout() here to ensure
// that the navigation buttons are visible.
window.setTimeout(function() { showWizardNavButtons(true); }, 0);
}
function onCancel()
{
if (gRestoreAfterHelpPanelID) // Is help open?
......@@ -927,7 +974,9 @@ function onOpenHelp()
forAssistance.setAttribute("hidden", true);
}
else
{
overrideButtonLabel("cancel", "done");
}
}
......@@ -948,7 +997,9 @@ function closeHelp()
forAssistance.removeAttribute("hidden");
}
else
{
restoreButtonLabel("cancel");
}
showPanel(gRestoreAfterHelpPanelID);
gRestoreAfterHelpPanelID = null;
......@@ -1130,15 +1181,16 @@ function initBridgeSettings()
// Returns true if settings were successfully applied.
function applySettings()
function applySettings(aUseDefaults)
{
TorLauncherLogger.log(2, "applySettings ---------------------" +
"----------------------------------------------");
var didSucceed = false;
try
{
didSucceed = applyBridgeSettings() &&
applyProxySettings() && applyFirewallSettings();
didSucceed = applyBridgeSettings(aUseDefaults) &&
applyProxySettings(aUseDefaults) &&
applyFirewallSettings(aUseDefaults);
}
catch (e) { TorLauncherLogger.safelog(4, "Error in applySettings: ", e); }
......@@ -1164,10 +1216,26 @@ function useSettings()
if (!gIsBootstrapComplete)
openProgressDialog();
let wizardElem = getWizard();
if (gIsBootstrapComplete)
{
close();
}
else if (wizardElem)
{
// If the user went down the "Configure" path and another error (e.g.,
// Tor Exited) has not already been shown, display a generic message
// with a "Reconfigure" button.
let pageid = wizardElem.currentPage.pageid;
if ((pageid != kWizardFirstPageID) && (pageid != "errorPanel"))
{
let msg = TorLauncherUtil.getLocalizedString("tor_bootstrap_failed");
showErrorMessage(false, msg, true);
}
}
}
function openProgressDialog()
{
var chromeURL = "chrome://torlauncher/content/progress.xul";
......@@ -1184,9 +1252,10 @@ function onProgressDialogClose(aBootstrapCompleted)
// Returns true if settings were successfully applied.
function applyProxySettings()
function applyProxySettings(aUseDefaults)
{
var settings = getAndValidateProxySettings();
let settings = aUseDefaults ? getDefaultProxySettings()
: getAndValidateProxySettings();
if (!settings)
return false;
......@@ -1194,34 +1263,40 @@ function applyProxySettings()
}
// Return a settings object if successful and null if not.
function getAndValidateProxySettings()
function getDefaultProxySettings()
{
// TODO: validate user-entered data. See Vidalia's NetworkPage::save()
var settings = {};
let settings = {};
settings[kTorConfKeySocks4Proxy] = null;
settings[kTorConfKeySocks5Proxy] = null;
settings[kTorConfKeySocks5ProxyUsername] = null;
settings[kTorConfKeySocks5ProxyPassword] = null;
settings[kTorConfKeyHTTPSProxy] = null;
settings[kTorConfKeyHTTPSProxyAuthenticator] = null;
return settings;
}
// Return a settings object if successful and null if not.
function getAndValidateProxySettings()
{
var settings = getDefaultProxySettings();
// TODO: validate user-entered data. See Vidalia's NetworkPage::save()
var proxyType, proxyAddrPort, proxyUsername, proxyPassword;
if (isProxyConfigured())
{
proxyAddrPort = createColonStr(getElemValue(kProxyAddr, null),
getElemValue(kProxyPort, null));
if (!proxyAddrPort)
proxyType = getElemValue(kProxyTypeMenulist, null);
if (!proxyType)
{
reportValidationError("error_proxy_addr_missing");
reportValidationError("error_proxy_type_missing");
return null;
}
proxyType = getElemValue(kProxyTypeMenulist, null);
if (!proxyType)
proxyAddrPort = createColonStr(getElemValue(kProxyAddr, null),
getElemValue(kProxyPort, null));
if (!proxyAddrPort)
{
reportValidationError("error_proxy_type_missing");
reportValidationError("error_proxy_addr_missing");
return null;
}
......@@ -1268,10 +1343,16 @@ function reportValidationError(aStrKey)
// Returns true if settings were successfully applied.
function applyFirewallSettings()
function applyFirewallSettings(aUseDefaults)
{
var settings = (getWizard()) ? getAutoFirewallSettings()
: getAndValidateFirewallSettings();
let settings;
if (aUseDefaults)
settings = getDefaultFirewallSettings();
else if (getWizard())
settings = getAutoFirewallSettings();
else
settings = getAndValidateFirewallSettings();
if (!settings)
return false;
......@@ -1296,6 +1377,12 @@ function getAndValidateFirewallSettings()
}
function getDefaultFirewallSettings()
{
return constructFirewallSettings(undefined);
}
// Return a settings object if successful and null if not.
// Only used for the wizard.
function getAutoFirewallSettings()
......@@ -1386,9 +1473,10 @@ function initDefaultBridgeTypeMenu()
// Returns true if settings were successfully applied.
function applyBridgeSettings()
function applyBridgeSettings(aUseDefaults)
{
var settings = getAndValidateBridgeSettings();
let settings = (aUseDefaults) ? getDefaultBridgeSettings()
: getAndValidateBridgeSettings();
if (!settings)
return false;
......@@ -1396,13 +1484,19 @@ function applyBridgeSettings()
}
// Return a settings object if successful and null if not.
function getAndValidateBridgeSettings()
function getDefaultBridgeSettings()
{
var settings = {};
let settings = {};
settings[kTorConfKeyUseBridges] = null;
settings[kTorConfKeyBridgeList] = null;
return settings;
}
// Return a settings object if successful and null if not.
function getAndValidateBridgeSettings()
{
var settings = getDefaultBridgeSettings();
var useBridges = isBridgeConfigured();
var defaultBridgeType;
var bridgeList;
......@@ -1432,8 +1526,9 @@ function getAndValidateBridgeSettings()
}
}
// Since it returns a filterd list of bridges, TorLauncherUtil.defaultBridges
// must be called after setting the kPrefDefaultBridgeType pref.
// Since it returns a filtered list of bridges,
// TorLauncherUtil.defaultBridges must be called after setting the
// kPrefDefaultBridgeType pref.
TorLauncherUtil.setCharPref(kPrefDefaultBridgeType, defaultBridgeType);
if (defaultBridgeType)
bridgeList = TorLauncherUtil.defaultBridges;
......@@ -1651,7 +1746,9 @@ function parseColonStr(aStr)
rv[1] = aStr.substring(idx + 1);
}
else
{
rv[0] = aStr;
}
return rv;
}
......
......@@ -20,7 +20,7 @@
persist="screenX screenY"
buttons="accept,cancel,extra2,help"
buttonlabelextra2="&torsettings.copyLog;"
ondialogaccept="return applySettings();"
ondialogaccept="return applySettings(false);"
ondialogcancel="return onCancel();"
ondialogextra2="onCopyLog();"
ondialoghelp="onOpenHelp();"
......
......@@ -170,6 +170,8 @@ var gObserver = {
// TODO: provide a way to access tor log e.g., leave this dialog open
// and display the open settings button or provide a way to do
// that from our error alerts.
if (kTorBootstrapErrorTopic == aTopic)
stopTorBootstrap();
cleanup();
window.close();
}
......
......@@ -13,26 +13,32 @@
<!ENTITY torSettings.firstQuestion "Which of the following best describes your situation?">
<!ENTITY torSettings.configurePrompt1 "This computer's Internet connection is censored or proxied.">
<!ENTITY torSettings.configurePrompt2 "I need to configure bridge or local proxy settings.">
<!ENTITY torSettings.configurePrompt2 "I need to configure bridge or local proxy settings before I connect to the Tor&#160;network.">
<!ENTITY torSettings.configure "Configure">
<!ENTITY torSettings.connectPrompt2 "I would like to connect directly to the Tor network.">
<!ENTITY torSettings.connectPrompt2 "I would like to make a direct connection to the Tor network.">
<!ENTITY torSettings.connectPrompt3 "This will work in most situations.">
<!ENTITY torSettings.connect "Connect">
<!ENTITY torSettings.proxyPageTitle "Local Proxy Configuration">
<!ENTITY torSettings.proxyQuestion "Does this computer need to use a local proxy to access the Internet?">
<!-- see https://www.torproject.org/docs/proxychain.html.en -->
<!ENTITY torSettings.proxyHelp "If you are not sure how to answer this question, look at the Internet settings in another browser to see whether it is configured to use a local proxy.">
<!ENTITY torSettings.proxyExplanation1 "In most cases a local proxy is not needed, but it may be required when connecting through a company, school, or university network.">
<!ENTITY torSettings.proxyExplanation2 "If you are not sure how to answer this question, look at the Internet settings in another browser or check your system's network settings to see whether a local proxy is needed.">
<!ENTITY torSettings.enterProxy "Enter the proxy settings.">
<!ENTITY torSettings.bridgePageTitle "Tor Bridges Configuration">
<!ENTITY torSettings.bridgeQuestion "Does your Internet Service Provider (ISP) block or otherwise censor connections to the Tor Network?">
<!ENTITY torSettings.bridgeHelp "If you are not sure how to answer this question, choose No.&#160; If you choose Yes, you will be asked to configure Tor Bridges, which are unlisted relays that make it more difficult to block connections to the Tor Network.">
<!ENTITY torSettings.bridgeExplanation1 "If you are not sure how to answer this question, choose No (if you are unable to connect to the Tor network without a bridge, you can add one later).">
<!ENTITY torSettings.bridgeExplanation2 "If you choose Yes, you will be asked to configure Tor Bridges, which are unlisted relays that make it more difficult to block connections to the Tor Network.">
<!ENTITY torSettings.bridgeSettingsPrompt "You may use the provided set of bridges or you may obtain and enter a custom set of bridges.">
<!-- Other: -->
<!ENTITY torsettings.startingTor "Waiting for Tor to start…">
<!ENTITY torsettings.restartTor "Restart Tor">
<!ENTITY torsettings.reconfigTor "Reconfigure">
<!ENTITY torsettings.discardSettings.prompt "You have configured Tor bridges or you have entered local proxy settings.&#160; To make a direct connection to the Tor network, these settings must be removed.">
<!ENTITY torsettings.discardSettings.proceed "Remove Settings and Connect">
<!ENTITY torsettings.optional "Optional">
......@@ -50,6 +56,7 @@
<!ENTITY torsettings.firewall.allowedPorts "Allowed Ports:">
<!ENTITY torsettings.useBridges.checkbox "My Internet Service Provider (ISP) blocks connections to the Tor network">
<!ENTITY torsettings.useBridges.default "Connect with provided bridges">
<!ENTITY torsettings.useBridges.note "Each type of bridge uses a different method to avoid censorship.&#160; If one bridge does not work, try again using a different one.">
<!ENTITY torsettings.useBridges.type "Transport type:">
<!ENTITY torsettings.useBridges.custom "Enter custom bridges">
<!ENTITY torsettings.useBridges.label "Enter one or more bridge relays (one per line).">
......
<!ENTITY torprogress.dialog.title "Tor Status">
<!ENTITY torprogress.openSettings "Open Settings">
<!ENTITY torprogress.heading "Connecting to the Tor network">
<!ENTITY torprogress.pleaseWait "Please wait while we establish a connection to the Tor network.">
<!ENTITY torprogress.pleaseWait "Please wait while we establish a connection to the Tor network.&#160; This may take several minutes.">
......@@ -72,7 +72,7 @@ separator.tall {
}
.questionHelp {
margin-right: 20px;
margin: 0px 0px 12px 20px;
}
.instructions {
......@@ -109,6 +109,7 @@ wizard#TorLauncherLocalePicker button[dlgtype="next"] {
font-weight: bold;
}
#bridgeNote,
#bridgeDefaultEntry,
#bridgeCustomEntry {
margin-left: 1.8em;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment