[tor-commits] [tor-launcher/master] Bug 23262: implement integrated progress bar (Part 2)

gk at torproject.org gk at torproject.org
Thu Nov 9 08:33:31 UTC 2017


commit 803bf1178bc8c66a7166309abd680eef4af7a548
Author: Kathy Brade <brade at pearlcrescent.com>
Date:   Tue Oct 31 10:06:32 2017 -0400

    Bug 23262: implement integrated progress bar (Part 2)
    
    Improve UX by greatly reducing the use of modal alert dialogs. In most
    cases errors are now displayed using one of three techniques:
      1. Via an overlaid error panel.
      2. As a message on the progress panel (with a Reconfigure button).
      3. On a standalone error page within the setup wizard.
    Move "Restart Tor" to a separate panel.
    Fix a problem where showAlert() would fail to display an alert: do not
      try to use a hidden window as the parent for the alert.
    Add a showOrHideElemById() utility function and use it.
---
 src/chrome/content/localePicker.xul             |   4 +-
 src/chrome/content/network-settings-overlay.xul |  30 ++-
 src/chrome/content/network-settings-wizard.xul  |  35 +--
 src/chrome/content/network-settings.js          | 328 +++++++++++++++++-------
 src/chrome/content/network-settings.xul         |  94 +++----
 src/chrome/skin/network-settings.css            |  23 +-
 src/components/tl-process.js                    |  61 +++--
 src/components/tl-protocol.js                   |  11 +-
 src/modules/tl-util.jsm                         |  34 ++-
 9 files changed, 434 insertions(+), 186 deletions(-)

diff --git a/src/chrome/content/localePicker.xul b/src/chrome/content/localePicker.xul
index 2017baa..6030468 100644
--- a/src/chrome/content/localePicker.xul
+++ b/src/chrome/content/localePicker.xul
@@ -29,7 +29,7 @@
     <hbox class="tbb-header" pack="center">
       <image class="tbb-logo"/>
     </hbox>
-    <separator />
+    <separator/>
     <vbox>
       <label class="question">&torlauncher.localePicker.prompt;</label>
       <separator/>
@@ -38,6 +38,6 @@
   </wizardpage>
 
   <hbox pack="start">
-    <label id="forAssistance" />
+    <label id="forAssistance"/>
   </hbox>
 </wizard>
diff --git a/src/chrome/content/network-settings-overlay.xul b/src/chrome/content/network-settings-overlay.xul
index a709f0e..b49dbab 100644
--- a/src/chrome/content/network-settings-overlay.xul
+++ b/src/chrome/content/network-settings-overlay.xul
@@ -83,7 +83,7 @@
     <hbox align="center">
       <label value="&torsettings.firewall.allowedPorts;"
              control="firewallAllowedPorts"/>
-      <textbox id="firewallAllowedPorts" value="80,443" />
+      <textbox id="firewallAllowedPorts" value="80,443"/>
     </hbox>
   </groupbox>
 
@@ -136,13 +136,37 @@
       <description id="progressPleaseWait"
                    hidden="true">&torprogress.pleaseWait;</description>
       <progressmeter id="progressMeter" mode="determined" value="0"/>
-      <description id="progressDesc"/>
+      <description id="progressDesc" errorElemId="message"/>
+      <label id="progressReconfigureLabel" hidden="true"
+             value="&torsettings.reconfigTor;"/>
     </vbox>
   </vbox>
 
+  <vbox id="restartContent">
+    <hbox pack="center">
+      <description id="restartPanelMessage" flex="1"/>
+    </hbox>
+    <separator/>
+    <hbox pack="center">
+      <button id="restartTorButton" label="&torsettings.restartTor;"
+              oncommand="onRestartTor()"/>
+    </hbox>
+  </vbox>
+
+  <vbox id="errorOverlayContent">
+    <hbox pack="center">
+      <description errorElemId="message" flex="1"/>
+    </hbox>
+    <separator/>
+    <hbox pack="center">
+      <button errorElemId="dismissButton" default="true"
+              oncommand="onDismissErrorOverlay()"/>
+    </hbox>
+  </vbox>
+
   <panel id="copyLogFeedbackPanel" type="arrow" fade="slow"
          onclick="closeCopyLogFeedbackPanel()">
-     <description flex="1" />
+     <description flex="1"/>
   </panel>
 </overlay>
 
diff --git a/src/chrome/content/network-settings-wizard.xul b/src/chrome/content/network-settings-wizard.xul
index 97e846c..86c2e01 100644
--- a/src/chrome/content/network-settings-wizard.xul
+++ b/src/chrome/content/network-settings-wizard.xul
@@ -57,6 +57,10 @@
         <separator/>
         <vbox id="proxySettings"/>
       </vbox>
+      <vbox id="configErrorOverlay" class="messagePanel" pack="center"
+            hidden="true">
+        <vbox id="errorOverlayContent"/>
+      </vbox>
     </stack>
   </wizardpage>
 
@@ -68,36 +72,37 @@
     <vbox id="progressContent"/>
   </wizardpage>
 
-  <wizardpage pageid="startingTor" next="notUsed" torShowNavButtons="false">
+  <wizardpage pageid="startingTor" class="messagePanel" next="notUsed"
+              torShowNavButtons="false">
     <hbox class="tbb-header" pack="center">
       <image class="tbb-logo"/>
     </hbox>
-    <spring flex="1" />
+    <spring flex="1"/>
     <hbox>
-      <spring flex="1" />
+      <spring flex="1"/>
       <description>&torsettings.startingTor;</description>
-      <spring flex="1" />
+      <spring flex="1"/>
     </hbox>
-    <spring flex="1" />
+    <spring flex="1"/>
   </wizardpage>
 
-  <wizardpage pageid="restartPanel" next="notUsed"
+  <wizardpage pageid="restartPanel" class="messagePanel" next="notUsed"
               pack="center" torShowNavButtons="false" onextra2="onCopyLog();">
     <vbox id="restartContent"/>
   </wizardpage>
 
-  <wizardpage pageid="errorPanel" next="notUsed"
+  <wizardpage pageid="errorPanel" class="messagePanel" next="notUsed"
               torShowNavButtons="false" onextra2="onCopyLog();">
-    <spring flex="1" />
+    <spring flex="1"/>
     <hbox pack="center">
       <description errorElemId="message" flex="1"/>
     </hbox>
     <separator/>
     <hbox pack="center">
       <button errorElemId="reconfigButton" label="&torsettings.reconfigTor;"
-              hidden="true" oncommand="onWizardReconfig()" />
+              hidden="true" oncommand="onWizardReconfig()"/>
     </hbox>
-    <spring flex="1" />
+    <spring flex="1"/>
   </wizardpage>
 
   <wizardpage pageid="discardSettings" next="notUsed" torShowNavButtons="false"
@@ -105,7 +110,7 @@
     <hbox class="tbb-header" pack="center">
       <image class="tbb-logo"/>
     </hbox>
-    <spring flex="1" />
+    <spring flex="1"/>
     <hbox pack="center">
       <description flex="1">&torsettings.discardSettings.prompt;</description>
     </hbox>
@@ -114,9 +119,9 @@
       <button id="discardSettingsGoBack" oncommand="showPanel();"/>
       <separator/>
       <button label="&torsettings.discardSettings.proceed;"
-              oncommand="removeSettingsAndConnect()" />
+              oncommand="removeSettingsAndConnect()"/>
     </hbox>
-    <spring flex="1" />
+    <spring flex="1"/>
   </wizardpage>
 
   <wizardpage class="help" pageid="helpPanel" next="notUsed"
@@ -126,7 +131,7 @@
   </wizardpage>
 
   <hbox pack="start">
-    <label id="forAssistance" />
+    <label id="forAssistance"/>
   </hbox>
-  <panel id="copyLogFeedbackPanel" />
+  <panel id="copyLogFeedbackPanel"/>
 </wizard>
diff --git a/src/chrome/content/network-settings.js b/src/chrome/content/network-settings.js
index 2237c85..da48bab 100644
--- a/src/chrome/content/network-settings.js
+++ b/src/chrome/content/network-settings.js
@@ -217,7 +217,7 @@ function initDialog()
      (status != gTorProcessService.kStatusRunning))
   {
     if (status == gTorProcessService.kStatusExited)
-      showErrorMessage(true, null, false);
+      showRestartPanel();
     else
       showStartingTorPanel();
     addObserver(kTorProcessReadyTopic);
@@ -395,7 +395,7 @@ function onWizardPageShow()
   setTimeout(function() {
       showOrHideButton("back", (val == "true"), false);
 
-      // The "next" button is only used by the bridgeHelp wizard panel.
+      // The "next" button is only used by the help wizard panel.
       let isShowingHelp = (wizardElem.currentPage.pageid == "helpPanel");
       showOrHideButton("next", isShowingHelp, false);
   }, 0);
@@ -500,6 +500,8 @@ function onShowProgressPanel()
 }
 
 
+// resetProgressNavButtons() is called when moving away from the progress
+// panel entirely, and when an error is displayed within the progress panel.
 function resetProgressNavButtons()
 {
   if (gShowProgressTimer)
@@ -521,6 +523,11 @@ var gObserver = {
          (kTorLogHasWarnOrErrTopic == aTopic))
     {
       showCopyLogButton(true);
+      if (kTorBootstrapErrorTopic == aTopic)
+      {
+        stopTorBootstrap();
+        showErrorMessage(aSubject.wrappedJSObject, true);
+      }
       return;
     }
 
@@ -534,12 +541,12 @@ var gObserver = {
     {
       removeObserver(kTorProcessReadyTopic);
       removeObserver(kTorProcessDidNotStartTopic);
-      showErrorMessage(false, aData, false);
+      showErrorMessage(aSubject.wrappedJSObject, false);
     }
     else if (kTorProcessExitedTopic == aTopic)
     {
       removeObserver(kTorProcessExitedTopic);
-      showErrorMessage(true, null, false);
+      showRestartPanel();
     }
     else if (kTorShowProgressPanelTopic == aTopic)
     {
@@ -639,17 +646,11 @@ function readTorSettings()
   if (!didSucceed)
   {
     // Unable to communicate with tor.  Hide settings and display an error.
-    showErrorMessage(false, null, false);
-
-    setTimeout(function()
-        {
-          let details = TorLauncherUtil.getLocalizedString(
-                                          "ensure_tor_is_running");
-          let s = TorLauncherUtil.getFormattedLocalizedString(
-                                      "failed_to_get_settings", [details], 1);
-          TorLauncherUtil.showAlert(window, s);
-          close();
-        }, 0);
+    let details = TorLauncherUtil.getLocalizedString("ensure_tor_is_running");
+    let s = TorLauncherUtil.getFormattedLocalizedString(
+                                "failed_to_get_settings", [details], 1);
+    let errorObj = { message: s };
+    showErrorMessage(errorObj, false);
   }
 
   TorLauncherLogger.log(2, "readTorSettings done; didSucceed: " + didSucceed);
@@ -728,49 +729,148 @@ function showStartingTorPanel()
 }
 
 
-function showErrorMessage(aTorExited, aErrorMsg, aShowReconfigButton)
+function showErrorMessage(aErrorObj, aShowReconfigButton)
 {
-  var elem = document.getElementById("errorPanelMessage");
-  var btn = document.getElementById("restartTorButton");
-  if (aTorExited)
+  if (aErrorObj && aErrorObj.handled)
+    return;
+
+  // Determine our strategy for displaying this error message.
+  const kShowErrorInErrorPanel = 1;
+  const kShowErrorUsingErrorOverlay = 2;
+  const kShowErrorInProgressPanel = 3;
+  let errorStrategy = kShowErrorInErrorPanel;
+
+  let wizard = getWizard();
+  if (isShowingProgress() && aShowReconfigButton)
+    errorStrategy = kShowErrorInProgressPanel;
+  else if (!wizard || (wizard.currentPage.pageid == "configureSettings"))
+    errorStrategy = kShowErrorUsingErrorOverlay;
+
+  let errorContainer;
+  if (errorStrategy == kShowErrorUsingErrorOverlay)
+    errorContainer = getErrorOverlay();
+  else if (errorStrategy == kShowErrorInProgressPanel)
+    errorContainer = document.getElementById("progressContent");
+  else
+    errorContainer = wizard.getPageById("errorPanel");
+  if (!errorContainer)
+    return;
+
+  let messageElem = getFirstElementByErrorOverlayID(errorContainer, "message");
+  if (messageElem)
   {
-    // Show "Tor exited" message and "Restart Tor" button.
-    aErrorMsg = TorLauncherUtil.getLocalizedString("tor_exited")
-                + "\n\n" + TorLauncherUtil.getLocalizedString("tor_exited2");
+    let msg = "";
+    if (aErrorObj && aErrorObj.message)
+    {
+      msg = aErrorObj.message;
+      if (aErrorObj.details)
+        msg += "\n\n" + aErrorObj.details;
+      aErrorObj.handled = true;
+    }
+    messageElem.textContent = msg;
+  }
 
-    if (btn)
-      btn.removeAttribute("hidden");
-    if (elem)
-      elem.style.textAlign = "start";
+  if (errorStrategy == kShowErrorUsingErrorOverlay)
+  {
+    showOrHideDialogButtons(false);
+
+    let dismissBtn = getFirstElementByErrorOverlayID(errorContainer,
+                                                     "dismissButton");
+    let bundle = Cc["@mozilla.org/intl/stringbundle;1"]
+             .getService(Ci.nsIStringBundleService)
+             .createBundle("chrome://global/locale/commonDialogs.properties");
+    dismissBtn.label = bundle.GetStringFromName("OK");
+    errorContainer.removeAttribute("hidden");
+    if (dismissBtn)
+      dismissBtn.focus();
   }
-  else
+  else if (errorStrategy == kShowErrorInProgressPanel)
   {
-    if (btn)
-      btn.setAttribute("hidden", true);
-    if (elem)
-      elem.style.textAlign = "center";
+    // In this case, we always show a "Reconfigure" button.
+    errorContainer.setAttribute("isShowingReconfigure", "true");
+    let btnLabel = document.getElementById("progressReconfigureLabel");
+    if (wizard)
+    {
+      showOrHideElemById("progressPleaseWait", false);
+      resetProgressNavButtons();  // Show Quit and clear "show progress" timer.
+      overrideButtonLabel("finish", btnLabel.value);
+    }
+    else if (btnLabel)
+    {
+      // Network Settings window (non-wizard) case.
+      overrideButtonLabel("cancel", btnLabel.value);
+    }
   }
+  else // if (errorStrategy == kShowErrorInErrorPanel)
+  {
+    let reconfigBtn = getFirstElementByErrorOverlayID(errorContainer,
+                                                      "reconfigButton");
+    if (reconfigBtn)
+    {
+      if (aShowReconfigButton)
+        reconfigBtn.removeAttribute("hidden");
+      else
+        reconfigBtn.setAttribute("hidden", true);
+    }
+
+    // Navigate to the wizard error panel.
+    showPanel("errorPanel");
+  }
+
+  let haveErrorOrWarning = (gTorProcessService.TorBootstrapErrorOccurred ||
+                            gProtocolSvc.TorLogHasWarnOrErr)
+  showCopyLogButton(haveErrorOrWarning);
+}
+
+
+function getErrorOverlay()
+{
+  return document.getElementById(getWizard() ? "configErrorOverlay"
+                                             : "errorOverlay");
+}
 
-  if (elem)
-    elem.textContent = (aErrorMsg) ? aErrorMsg : "";
 
-  let reconfigBtn = document.getElementById("reconfigTorButton");
-  if (reconfigBtn)
+function getFirstElementByErrorOverlayID(aContainer, aID)
+{
+  let nodeList = aContainer.getElementsByAttribute("errorElemId", aID);
+  return (nodeList && (nodeList.length > 0)) ? nodeList[0] : undefined;
+}
+
+
+function showRestartPanel()
+{
+  let elem = document.getElementById("restartPanelMessage");
+  if (elem)
   {
-    if (aShowReconfigButton)
-      reconfigBtn.removeAttribute("hidden");
-    else
-      reconfigBtn.setAttribute("hidden", true);
+    elem.textContent = TorLauncherUtil.getLocalizedString("tor_exited")
+                + "\n\n" + TorLauncherUtil.getLocalizedString("tor_exited2");
   }
 
-  showPanel("errorPanel");
+  showPanel("restartPanel");
 
-  var haveErrorOrWarning = (gTorProcessService.TorBootstrapErrorOccurred ||
+  let haveErrorOrWarning = (gTorProcessService.TorBootstrapErrorOccurred ||
                             gProtocolSvc.TorLogHasWarnOrErr)
   showCopyLogButton(haveErrorOrWarning);
 }
 
 
+function onDismissErrorOverlay()
+{
+  let errorOverlay = getErrorOverlay();
+  if (errorOverlay)
+    errorOverlay.setAttribute("hidden", true);
+
+  showOrHideDialogButtons(true);
+}
+
+
+function isShowingErrorOverlay()
+{
+  let errorOverlay = getErrorOverlay();
+  return errorOverlay && !errorOverlay.hasAttribute("hidden");
+}
+
+
 function showCopyLogButton(aHaveErrorOrWarning)
 {
   let copyLogBtn = document.documentElement.getButton("extra2");
@@ -809,6 +909,29 @@ function restoreCopyLogVisibility()
 }
 
 
+// Show or hide all of the buttons that are in the "footer" of the wizard or
+// Network Settings window.
+function showOrHideDialogButtons(aShow)
+{
+  let buttonContainer = document.getAnonymousElementByAttribute(
+                             document.documentElement, "anonid", "buttons");
+  if (!buttonContainer)
+  {
+    // The wizard uses "Buttons" (capital 'B').
+    buttonContainer = document.getAnonymousElementByAttribute(
+                             document.documentElement, "anonid", "Buttons");
+  }
+
+  if (buttonContainer)
+  {
+    if (aShow)
+      buttonContainer.removeAttribute("hidden");
+    else
+      buttonContainer.hidden = true;
+  }
+}
+
+
 function showOrHideButton(aID, aShow, aFocus)
 {
   var btn = setButtonAttr(aID, "hidden", !aShow);
@@ -843,6 +966,19 @@ function setButtonAttr(aID, aAttr, aValue)
 }
 
 
+function showOrHideElemById(aID, aShow)
+{
+  let elem = document.getElementById(aID);
+  if (elem)
+  {
+    if (aShow)
+      elem.removeAttribute("hidden");
+    else
+      elem.setAttribute("hidden", true);
+  }
+}
+
+
 // Sets or removes aAttr for aID as well as optional aID+"Label" element.
 function setBoolAttrForElemWithLabel(aID, aAttr, aValue)
 {
@@ -1010,9 +1146,22 @@ function onCancel()
     return false;
   }
 
+  if (isShowingErrorOverlay())
+  {
+    onDismissErrorOverlay();
+    return false;
+  }
+
+  let wizard = getWizard();
+  if (!wizard && isShowingProgress())
+  {
+    onProgressCancelOrReconfigure(undefined);
+    return false;
+  }
+
   // If this is a wizard (initial config or locale picker), the cancel
   // button is "Quit"
-  if (getWizard())
+  if (wizard)
   {
     try
     {
@@ -1031,11 +1180,15 @@ function onCancel()
 
 function onWizardFinish()
 {
+  if (isShowingErrorOverlay())
+  {
+    onDismissErrorOverlay();
+    return false;
+  }
+
   if (isShowingProgress())
   {
-    // When the progress panel is showing, the finish button is "Cancel"
-    stopTorBootstrap();
-    getWizard().rewind();
+    onProgressCancelOrReconfigure(getWizard());
     return false;
   }
   else
@@ -1053,10 +1206,39 @@ function onNetworkSettingsFinish()
     return false;
   }
 
+  if (isShowingErrorOverlay())
+  {
+    onDismissErrorOverlay();
+    return false;
+  }
+
   return applySettings(false);
 }
 
 
+// When the progress panel is open, cancel stops bootstrapping... unless
+// we are showing an error, in which case the action is "Reconfigure".
+function onProgressCancelOrReconfigure(aWizard)
+{
+  let progressContent = document.getElementById("progressContent");
+  if (!progressContent ||
+      !progressContent.hasAttribute("isShowingReconfigure"))
+  {
+    stopTorBootstrap();
+  }
+
+  if (aWizard)
+  {
+    aWizard.rewind();
+  }
+  else
+  {
+    restoreButtonLabel("cancel");
+    showPanel(undefined); // return to the Network Settings main panel.
+  }
+}
+
+
 function onCopyLog()
 {
   // Copy tor log messages to the system clipboard.
@@ -1107,12 +1289,8 @@ function onOpenHelp(aHelpContentID)
   if (getWizard())
   {
     showOrHideButton("cancel", false, false);
-    showOrHideButton("back", false, false);
     overrideButtonLabelWithKey("next", "done");
-    showOrHideButton("next", true, false);
-    let forAssistance = document.getElementById("forAssistance");
-    if (forAssistance)
-      forAssistance.setAttribute("hidden", true);
+    showOrHideElemById("forAssistance", false);
   }
   else
   {
@@ -1133,12 +1311,8 @@ function closeHelp()
   if (wizardElem)
   {
     showOrHideButton("cancel", true, false);
-    showOrHideButton("back", true, false);
-    showOrHideButton("next", false, false);
     restoreButtonLabel("next");
-    var forAssistance = document.getElementById("forAssistance");
-    if (forAssistance)
-      forAssistance.removeAttribute("hidden");
+    showOrHideElemById("forAssistance", true);
     helpPanel = wizardElem.currentPage;
   }
   else
@@ -1312,12 +1486,7 @@ function initBridgeSettings()
 
   setElemValue(kUseBridgesCheckbox, useBridges);
 
-  if (!canUseDefaultBridges)
-  {
-    var radioGroup = document.getElementById("bridgeTypeRadioGroup");
-    if (radioGroup)
-      radioGroup.setAttribute("hidden", true);
-  }
+  showOrHideElemById("bridgeTypeRadioGroup", canUseDefaultBridges);
 
   let radioID = (useDefault) ? "bridgeRadioDefault" : "bridgeRadioCustom";
   let radio = document.getElementById(radioID);
@@ -1375,22 +1544,6 @@ function useSettings()
   }
 
   showProgressPanel();
-
-/* TODO2017: is this needed? Used to be after modal progress dlog was displayed
-  let wizardElem = getWizard();
-  if (!gTorProcessService.TorIsBootstrapDone && 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);
-    }
-  }
-*/
 }
 
 
@@ -1416,12 +1569,12 @@ function stopTorBootstrap()
 
 function showProgressPanel()
 {
+  let progressContent = document.getElementById("progressContent");
+  if (progressContent)
+    progressContent.removeAttribute("isShowingReconfigure");
+
   if (gIsInitialBootstrap)
-  {
-    let pleaseWait = document.getElementById("progressPleaseWait");
-    if (pleaseWait)
-      pleaseWait.removeAttribute("hidden");
-  }
+    showOrHideElemById("progressPleaseWait", true);
 
   // Clear the description to avoid displaying any old messages.
   let desc = document.getElementById("progressDesc");
@@ -1555,7 +1708,7 @@ function isProxyConfigured()
 
 function reportValidationError(aStrKey)
 {
-  showSaveSettingsAlert(TorLauncherUtil.getLocalizedString(aStrKey));
+  showSaveSettingsError(TorLauncherUtil.getLocalizedString(aStrKey));
 }
 
 
@@ -1828,18 +1981,17 @@ function setConfAndReportErrors(aSettingsObj, aShowOnErrorPanelID)
       } catch (e) {}
     }
 
-    showSaveSettingsAlert(errObj.details);
+    showSaveSettingsError(errObj.details);
   }
 
   return didSucceed;
 }
 
 
-function showSaveSettingsAlert(aDetails)
+function showSaveSettingsError(aDetails)
 {
-  TorLauncherUtil.showSaveSettingsAlert(window, aDetails);
-  showOrHideButton("extra2", true, false);
-  gWizIsCopyLogBtnShowing = true;
+  let msg = TorLauncherUtil.getSaveSettingsErrorMessage(aDetails);
+  showErrorMessage({ message: msg }, true);
 }
 
 
diff --git a/src/chrome/content/network-settings.xul b/src/chrome/content/network-settings.xul
index 1b5ced0..707990a 100644
--- a/src/chrome/content/network-settings.xul
+++ b/src/chrome/content/network-settings.xul
@@ -5,7 +5,7 @@
    - vim: set sw=2 sts=2 ts=8 et syntax=xml:
   -->
 
-<?xml-stylesheet href="chrome://global/skin/" type="text/css"?> 
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
 <?xml-stylesheet href="chrome://torlauncher/skin/network-settings.css"
                  type="text/css"?>
 
@@ -29,53 +29,53 @@
   <script type="application/x-javascript"
           src="chrome://torlauncher/content/network-settings.js"/>
 
-  <deck id="deck" onselect="onDeckSelect();">
-    <vbox id="settings">
-      <vbox id="bridgeSettings"/>
-      <separator orient="horizontal" class="thin"/>
-      <vbox id="proxySettings"/>
+  <stack flex="1">
+    <vbox>
+      <deck id="deck" onselect="onDeckSelect();">
+        <vbox id="settings">
+          <vbox id="bridgeSettings"/>
+          <separator orient="horizontal" class="thin"/>
+          <vbox id="proxySettings"/>
+          <vbox>
+            <checkbox id="useFirewallPorts"
+                      groupboxID="firewallSpecificSettings"
+                      label="&torsettings.firewall.checkbox;"
+                      oncommand="toggleElemUI(this)"/>
+            <groupbox id="firewallSpecificSettings"/>
+          </vbox>
+        </vbox>
 
-      <vbox>
-        <checkbox id="useFirewallPorts" groupboxID="firewallSpecificSettings"
-                  label="&torsettings.firewall.checkbox;"
-                  oncommand="toggleElemUI(this)"/>
-        <groupbox id="firewallSpecificSettings" />
-      </vbox>
-    </vbox>
-    <vbox id="startingTor">
-      <spring flex="1" />
-      <hbox>
-        <spring flex="1" />
-        <description>&torsettings.startingTor;</description>
-        <spring flex="1" />
-      </hbox>
-      <spring flex="1" />
-    </vbox>
-    <vbox id="progressPanel">
-      <vbox id="progressContent"/>
-    </vbox>
-    <vbox id="errorPanel">
-      <spring flex="1" />
-      <hbox>
-        <spring flex="1" />
-        <description id="errorPanelMessage" flex="1" />
-        <spring flex="1" />
-      </hbox>
-      <separator/>
-      <hbox>
-        <spring flex="1" />
-        <button id="restartTorButton" label="&torsettings.restartTor;" hidden="true"
-                oncommand="onRestartTor()" />
-        <spring flex="1" />
-      </hbox>
-      <spring flex="1" />
+        <vbox id="startingTor" class="messagePanel">
+          <spring flex="1"/>
+          <hbox>
+            <spring flex="1"/>
+            <description>&torsettings.startingTor;</description>
+            <spring flex="1"/>
+          </hbox>
+          <spring flex="1"/>
+        </vbox>
+
+        <vbox id="progressPanel">
+          <vbox id="progressContent"/>
+        </vbox>
+
+        <vbox id="helpPanel" class="help">
+          <vbox id="bridgeHelpContent" hidden="true"/>
+          <vbox id="proxyHelpContent" hidden="true"/>
+        </vbox>
+
+        <vbox id="restartPanel" class="messagePanel" pack="center">
+          <vbox id="restartContent"/>
+        </vbox>
+      </deck>
+
+      <spring flex="1"/>
+      <label id="forAssistance"/>
+      <panel id="copyLogFeedbackPanel"/>
     </vbox>
-    <vbox id="helpPanel" class="help">
-      <vbox id="bridgeHelpContent" hidden="true"/>
-      <vbox id="proxyHelpContent" hidden="true"/>
+
+    <vbox id="errorOverlay" class="messagePanel" pack="center" hidden="true">
+      <vbox id="errorOverlayContent"/>
     </vbox>
-  </deck>
-  <spring flex="1" />
-  <label id="forAssistance" />
-  <panel id="copyLogFeedbackPanel" />
+  </stack>
 </dialog>
diff --git a/src/chrome/skin/network-settings.css b/src/chrome/skin/network-settings.css
index 34b1b3e..259e38d 100644
--- a/src/chrome/skin/network-settings.css
+++ b/src/chrome/skin/network-settings.css
@@ -157,16 +157,31 @@ wizard.os-mac #bridgeList {
   }
 }
 
-wizardpage[pageid="startingTor"] description,
-wizardpage[pageid="errorPanel"] description,
-#errorPanel description,
-#startingTor description {
+#progressContent[isShowingReconfigure] description,
+.messagePanel description {
+  margin: 20px;
   font-size: 120%;
   font-weight: bold;
   white-space: pre-wrap;
   text-align: center;
 }
 
+wizardpage[pageid="restartPanel"] description,
+#restartPanel description {
+  text-align: start;
+}
+
+#errorOverlayContent {
+  margin: 50px;
+  min-height: 12em;
+  background-color: rgba(251,251,251,1.0);
+  box-shadow: 0px 0px 50px rgba(0,0,0,0.9);
+}
+
+#errorOverlayContent button[errorElemId="dismissButton"] {
+  margin-bottom: 20px;
+}
+
 #restartButton {
   margin-top: 20px;
 }
diff --git a/src/components/tl-process.js b/src/components/tl-process.js
index 6f07fdb..53c9919 100644
--- a/src/components/tl-process.js
+++ b/src/components/tl-process.js
@@ -50,6 +50,9 @@ TorProcessService.prototype =
   kDefaultBridgesStatus_InUse: 1,
   kDefaultBridgesStatus_BadConfig: 2,
 
+  kTorProcessDidNotStartTopic: "TorProcessDidNotStart",
+  kTorBootstrapErrorTopic: "TorBootstrapError",
+
   // nsISupports implementation.
   QueryInterface: function(aIID)
   {
@@ -227,9 +230,8 @@ TorProcessService.prototype =
         else if ((Date.now() - this.mTorProcessStartTime)
                  > this.kControlConnTimeoutMS)
         {
-          var s = TorLauncherUtil.getLocalizedString("tor_controlconn_failed");
-          this.mObsSvc.notifyObservers(null, "TorProcessDidNotStart", s);
-          TorLauncherUtil.showAlert(null, s);
+          let s = TorLauncherUtil.getLocalizedString("tor_controlconn_failed");
+          this._notifyUserOfError(s, null, this.kTorProcessDidNotStartTopic);
           TorLauncherLogger.log(4, s);
         }
         else
@@ -373,7 +375,7 @@ TorProcessService.prototype =
         var key = "unable_to_start_tor";
         var err = TorLauncherUtil.getFormattedLocalizedString(key,
                                                                 [details], 1);
-        TorLauncherUtil.showAlert(null, err);
+        this._notifyUserOfError(err, null, this.kTorProcessDidNotStartTopic);
         return;
       }
 
@@ -461,7 +463,7 @@ TorProcessService.prototype =
         var key = "error_bridge_bad_default_type";
         var err = TorLauncherUtil.getFormattedLocalizedString(key,
                                                      [defaultBridgeType], 1);
-        TorLauncherUtil.showAlert(null, err);
+        this._notifyUserOfError(err, null, null);
       }
 
       if (aForceDisableNetwork || TorLauncherUtil.shouldShowNetworkSettings ||
@@ -506,7 +508,7 @@ TorProcessService.prototype =
     {
       this.mTorProcessStatus = this.kStatusExited;
       var s = TorLauncherUtil.getLocalizedString("tor_failed_to_start");
-      TorLauncherUtil.showAlert(null, s);
+      this._notifyUserOfError(s, null, this.kTorProcessDidNotStartTopic);
       TorLauncherLogger.safelog(4, "_startTor error: ", e);
     }
   }, // _startTor()
@@ -569,7 +571,7 @@ TorProcessService.prototype =
     {
       this.mTorProcessStatus = this.kStatusExited;
       var s = TorLauncherUtil.getLocalizedString("tor_control_failed");
-      TorLauncherUtil.showAlert(null, s);
+      this._notifyUserOfError(s, null, null);
       TorLauncherLogger.safelog(4, "_controlTor error: ", e);
     }
   }, // controlTor()
@@ -619,11 +621,11 @@ TorProcessService.prototype =
       {
         this.mBootstrapErrorOccurred = true;
         TorLauncherUtil.setBoolPref(this.kPrefPromptAtStartup, true);
-        var phase = TorLauncherUtil.getLocalizedBootstrapStatus(aStatusObj,
+        let phase = TorLauncherUtil.getLocalizedBootstrapStatus(aStatusObj,
                                                                 "TAG");
-        var reason = TorLauncherUtil.getLocalizedBootstrapStatus(aStatusObj,
+        let reason = TorLauncherUtil.getLocalizedBootstrapStatus(aStatusObj,
                                                                  "REASON");
-        var details = TorLauncherUtil.getFormattedLocalizedString(
+        let details = TorLauncherUtil.getFormattedLocalizedString(
                           "tor_bootstrap_failed_details", [phase, reason], 2);
         TorLauncherLogger.log(5, "Tor bootstrap error: [" + aStatusObj.TAG +
                                  "/" + aStatusObj.REASON + "] " + details);
@@ -634,12 +636,8 @@ TorProcessService.prototype =
           this.mLastTorWarningPhase = aStatusObj.TAG;
           this.mLastTorWarningReason = aStatusObj.REASON;
 
-          // Notify others that an error will be displayed.
-          this.mObsSvc.notifyObservers(null, "TorBootstrapError", reason);
-
-// TODO2017: "route" error message to wizard or settings dialog if it is open
-          var msg = TorLauncherUtil.getLocalizedString("tor_bootstrap_failed");
-          TorLauncherUtil.showAlert(null, msg + "\n\n" + details);
+          let msg = TorLauncherUtil.getLocalizedString("tor_bootstrap_failed");
+          this._notifyUserOfError(msg, details, this.kTorBootstrapErrorTopic);
         }
       }
     }
@@ -684,9 +682,14 @@ TorProcessService.prototype =
     }
 
     if (didSucceed)
+    {
       this.mProtocolSvc.TorSendCommand("SAVECONF");
+    }
     else
-      TorLauncherUtil.showSaveSettingsAlert(null, errObj.details);
+    {
+      let msg = TorLauncherUtil.getSaveSettingsErrorMessage(errObj.details);
+      this._notifyUserOfError(msg, null, null);
+    }
   },
 
   _openLocalePicker: function()
@@ -752,6 +755,30 @@ TorProcessService.prototype =
     return argsArray;
   },
 
+  _notifyUserOfError: function(aMessage, aDetails, aNotifyTopic)
+  {
+    let errorObj = { handled: false, message: aMessage };
+    if (aDetails)
+      errorObj.details = aDetails;
+
+    if (aNotifyTopic)
+    {
+      // Give other code an opportunity to handle this error, e.g., if the
+      // network settings window is open, errors are displayed using an
+      // overlaid XUL element.
+      errorObj.wrappedJSObject = errorObj;
+      this.mObsSvc.notifyObservers(errorObj, aNotifyTopic, null);
+    }
+
+    if (!errorObj.handled)
+    {
+      let msg = aMessage;
+      if (aDetails)
+        msg += "\n\n" + aDetails;
+      TorLauncherUtil.showAlert(null, msg);
+    }
+  },
+
   _getpid: function()
   {
     // Use nsIXULRuntime.processID if it is available.
diff --git a/src/components/tl-protocol.js b/src/components/tl-protocol.js
index d6323f4..68c6530 100644
--- a/src/components/tl-protocol.js
+++ b/src/components/tl-protocol.js
@@ -465,7 +465,7 @@ TorProtocolService.prototype =
     //  250 OK
     reply = this._parseReply(cmd, key, reply);
     if (reply.lineArray)
-      this._parseBootstrapStatus(reply.lineArray[0]);
+      this._parseBootstrapStatus(reply.lineArray[0], true);
   },
 
   // If successful, returns a JS object with these fields:
@@ -479,8 +479,10 @@ TorProtocolService.prototype =
   //   status.RECOMMENDATION  -- string (optional)
   //   status.HOSTADDR        -- string (optional)
   // A "TorBootstrapStatus" notification is also sent.
+  // If aSuppressErrors is true, errors are ignored. This is used when we
+  // are handling the response to a "GETINFO status/bootstrap-phase" command.
   // Returns null upon failure.
-  _parseBootstrapStatus: function(aStatusMsg)
+  _parseBootstrapStatus: function(aStatusMsg, aSuppressErrors)
   {
     if (!aStatusMsg || (0 == aStatusMsg.length))
       return null;
@@ -531,7 +533,8 @@ TorProtocolService.prototype =
     }
 
     // this._dumpObj("BootstrapStatus", statusObj);
-    statusObj._errorOccurred = (("NOTICE" != statusObj.TYPE) &&
+    statusObj._errorOccurred = (!aSuppressErrors &&
+                                ("NOTICE" != statusObj.TYPE) &&
                                 ("warn" == statusObj.RECOMMENDATION));
 
     // Notify observers.
@@ -1519,7 +1522,7 @@ TorProtocolService.prototype =
           }
           break;
         case "STATUS_CLIENT":
-          this._parseBootstrapStatus(msg);
+          this._parseBootstrapStatus(msg, false);
           break;
         default:
           this._dumpObj(eventType + "_event", aReply);
diff --git a/src/modules/tl-util.jsm b/src/modules/tl-util.jsm
index 2e45fd1..bb84bdf 100644
--- a/src/modules/tl-util.jsm
+++ b/src/modules/tl-util.jsm
@@ -50,9 +50,18 @@ let TorLauncherUtil =  // Public
       {
         var wm = Cc["@mozilla.org/appshell/window-mediator;1"]
                    .getService(Ci.nsIWindowMediator);
-        aParentWindow = wm.getMostRecentWindow("TorLauncher:NetworkSettings");
-        if (!aParentWindow)
-          aParentWindow = wm.getMostRecentWindow("navigator:browser");
+        let settingsWindow =
+                          wm.getMostRecentWindow("TorLauncher:NetworkSettings");
+        if (TLUtilInternal._isWindowVisible(settingsWindow))
+        {
+          aParentWindow = settingsWindow;
+        }
+        else
+        {
+          let browserWindow = wm.getMostRecentWindow("navigator:browser");
+          if (TLUtilInternal._isWindowVisible(browserWindow))
+            aParentWindow = browserWindow;
+        }
       }
 
       var ps = Cc["@mozilla.org/embedcomp/prompt-service;1"]
@@ -106,14 +115,13 @@ let TorLauncherUtil =  // Public
     return false;
   },
 
-  showSaveSettingsAlert: function(aParentWindow, aDetails)
+  getSaveSettingsErrorMessage: function(aDetails)
   {
     if (!aDetails)
       aDetails = TorLauncherUtil.getLocalizedString("ensure_tor_is_running");
 
-    var s = TorLauncherUtil.getFormattedLocalizedString(
+    return TorLauncherUtil.getFormattedLocalizedString(
                                   "failed_to_save_settings", [aDetails], 1);
-    this.showAlert(aParentWindow, s);
   },
 
   // Localized Strings
@@ -846,6 +854,20 @@ let TLUtilInternal =  // Private
       return null;
     }
   },
+
+  _isWindowVisible: function(aWindow)
+  {
+    if (!aWindow)
+      return false;
+
+    try {
+      let winUtils = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+                            .getInterface(Ci.nsIDOMWindowUtils);
+      return winUtils.isParentWindowMainWidgetVisible;
+    } catch(e) {}
+
+    return false;
+  },
 };
 
 



More information about the tor-commits mailing list