[tor-commits] [tor-browser] 04/05: squash! Bug 27476: Implement about:torconnect captive portal within Tor Browser

gitolite role git at cupani.torproject.org
Tue Apr 5 20:42:34 UTC 2022


This is an automated email from the git hooks/post-receive script.

richard pushed a commit to branch tor-browser-91.8.0esr-11.5-1
in repository tor-browser.

commit 76f70fba50817ec90a8e00725dad9405f07d4a1d
Author: Richard Pospesel <richard at torproject.org>
AuthorDate: Wed Jan 19 19:09:51 2022 +0100

    squash! Bug 27476: Implement about:torconnect captive portal within Tor Browser
    
    Bug 40773: Update the about:torconnect frontend page to match additional UI flows
---
 browser/components/torconnect/TorConnectParent.jsm |  59 ++-
 .../torconnect/content/aboutTorConnect.css         | 189 ++++---
 .../torconnect/content/aboutTorConnect.js          | 570 +++++++++++++++++----
 .../torconnect/content/aboutTorConnect.xhtml       |  43 +-
 .../components/torconnect/content/arrow-right.svg  |   4 +
 browser/components/torconnect/content/bridge.svg   |   5 +
 .../torconnect/content/connection-failure.svg      |   5 +
 .../torconnect/content/connection-location.svg     |   5 +
 browser/components/torconnect/content/globe.svg    |   4 +
 .../torconnect/content/onion-slash-fillable.svg    |   5 +
 browser/components/torconnect/jar.mn               |   6 +
 toolkit/modules/RemotePageAccessManager.jsm        |  13 +-
 12 files changed, 720 insertions(+), 188 deletions(-)

diff --git a/browser/components/torconnect/TorConnectParent.jsm b/browser/components/torconnect/TorConnectParent.jsm
index 2fbc2a5c7c7c7..dd3d1b2410f95 100644
--- a/browser/components/torconnect/TorConnectParent.jsm
+++ b/browser/components/torconnect/TorConnectParent.jsm
@@ -4,7 +4,7 @@ var EXPORTED_SYMBOLS = ["TorConnectParent"];
 
 const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
 const { TorStrings } = ChromeUtils.import("resource:///modules/TorStrings.jsm");
-const { TorConnect, TorConnectTopics, TorConnectState } = ChromeUtils.import(
+const { TorConnect, TorConnectTopics, TorConnectState, TorCensorshipLevel } = ChromeUtils.import(
   "resource:///modules/TorConnect.jsm"
 );
 const { TorSettings, TorSettingsTopics, TorSettingsData } = ChromeUtils.import(
@@ -24,13 +24,15 @@ class TorConnectParent extends JSWindowActorParent {
 
     this.state = {
       State: TorConnect.state,
+      DetectedCensorshiplevel: TorConnect.detectedCensorshiplevel,
       StateChanged: false,
       ErrorMessage: TorConnect.errorMessage,
       ErrorDetails: TorConnect.errorDetails,
       BootstrapProgress: TorConnect.bootstrapProgress,
       BootstrapStatus: TorConnect.bootstrapStatus,
-      ShowCopyLog: TorConnect.logHasWarningOrError,
+      ShowViewLog: TorConnect.logHasWarningOrError,
       QuickStartEnabled: TorSettings.quickstart.enabled,
+      CountryCodes: TorConnect.countryCodes,
     };
 
     // JSWindowActiveParent derived objects cannot observe directly, so create a member
@@ -46,10 +48,11 @@ class TorConnectParent extends JSWindowActorParent {
         // update our state struct based on received torconnect topics and forward on
         // to aboutTorConnect.js
         self.state.StateChanged = false;
-        switch(aTopic) {
+        switch (aTopic) {
           case TorConnectTopics.StateChange: {
             self.state.State = obj.state;
             self.state.StateChanged = true;
+
             // clear any previous error information if we are bootstrapping
             if (self.state.State === TorConnectState.Bootstrapping) {
               self.state.ErrorMessage = null;
@@ -60,7 +63,7 @@ class TorConnectParent extends JSWindowActorParent {
           case TorConnectTopics.BootstrapProgress: {
             self.state.BootstrapProgress = obj.progress;
             self.state.BootstrapStatus = obj.status;
-            self.state.ShowCopyLog = obj.hasWarnings;
+            self.state.ShowViewLog = obj.hasWarnings;
             break;
           }
           case TorConnectTopics.BootstrapComplete: {
@@ -70,14 +73,21 @@ class TorConnectParent extends JSWindowActorParent {
           case TorConnectTopics.BootstrapError: {
             self.state.ErrorMessage = obj.message;
             self.state.ErrorDetails = obj.details;
-            self.state.ShowCopyLog = true;
+            self.state.DetectedCensorshiplevel = obj.censorshipLevel;
+
+            // With severe censorshp, we offer user list of countries to try
+            if (self.state.DetectedCensorshiplevel == TorCensorshipLevel.Severe) {
+              self.state.CountryCodes = TorConnect.countryCodes;
+            }
+
+            self.state.ShowViewLog = true;
             break;
           }
           case TorConnectTopics.FatalError: {
             // TODO: handle
             break;
           }
-          case TorSettingsTopics.SettingChanged:{
+          case TorSettingsTopics.SettingChanged: {
             if (aData === TorSettingsData.QuickStartEnabled) {
               self.state.QuickStartEnabled = obj.value;
             } else {
@@ -100,7 +110,10 @@ class TorConnectParent extends JSWindowActorParent {
       const topic = TorConnectTopics[key];
       Services.obs.addObserver(this.torConnectObserver, topic);
     }
-    Services.obs.addObserver(this.torConnectObserver, TorSettingsTopics.SettingChanged);
+    Services.obs.addObserver(
+      this.torConnectObserver,
+      TorSettingsTopics.SettingChanged
+    );
   }
 
   willDestroy() {
@@ -109,10 +122,13 @@ class TorConnectParent extends JSWindowActorParent {
       const topic = TorConnectTopics[key];
       Services.obs.removeObserver(this.torConnectObserver, topic);
     }
-    Services.obs.removeObserver(this.torConnectObserver, TorSettingsTopics.SettingChanged);
+    Services.obs.removeObserver(
+      this.torConnectObserver,
+      TorSettingsTopics.SettingChanged
+    );
   }
 
-  receiveMessage(message) {
+  async receiveMessage(message) {
     switch (message.name) {
       case "torconnect:set-quickstart":
         TorSettings.quickstart.enabled = message.data;
@@ -121,14 +137,23 @@ class TorConnectParent extends JSWindowActorParent {
       case "torconnect:open-tor-preferences":
         TorConnect.openTorPreferences();
         break;
-      case "torconnect:copy-tor-logs":
-        return TorConnect.copyTorLogs();
+      case "torconnect:view-tor-logs":
+        TorConnect.viewTorLogs();
+        break;
       case "torconnect:cancel-bootstrap":
         TorConnect.cancelBootstrap();
         break;
       case "torconnect:begin-bootstrap":
         TorConnect.beginBootstrap();
         break;
+      case "torconnect:begin-autobootstrap":
+        TorConnect.beginAutoBootstrap(message.data);
+        break;
+      case "torconnect:restart":
+        Services.startup.quit(
+          Ci.nsIAppStartup.eRestart | Ci.nsIAppStartup.eAttemptQuit
+        );
+        break;
       case "torconnect:get-init-args":
         // called on AboutTorConnect.init(), pass down all state data it needs to init
 
@@ -136,11 +161,15 @@ class TorConnectParent extends JSWindowActorParent {
         // so we always get fresh UI
         this.state.StateChanged = true;
         return {
-            TorStrings: TorStrings,
-            TorConnectState: TorConnectState,
-            Direction: Services.locale.isAppLocaleRTL ? "rtl" : "ltr",
-            State: this.state,
+          TorStrings,
+          TorConnectState,
+          TorCensorshipLevel,
+          Direction: Services.locale.isAppLocaleRTL ? "rtl" : "ltr",
+          State: this.state,
+          CountryNames: TorConnect.countryNames,
         };
+      case "torconnect:get-country-codes":
+        return TorConnect.getCountryCodes();
     }
     return undefined;
   }
diff --git a/browser/components/torconnect/content/aboutTorConnect.css b/browser/components/torconnect/content/aboutTorConnect.css
index 14a3df2a59be3..0a3dc9fbd75fd 100644
--- a/browser/components/torconnect/content/aboutTorConnect.css
+++ b/browser/components/torconnect/content/aboutTorConnect.css
@@ -24,22 +24,133 @@
   }
 }
 
-#connectButton {
+#breadcrumbs {
+  display: flex;
+  margin: 0 0 24px 0;
+  color: var(--grey-40);
+}
+
+#breadcrumbs.hidden {
+  visibility: hidden;
+}
+
+.breadcrumb-item, .breadcrumb-separator {
+  display: flex;
+  margin: 0;
+  margin-inline-start: 20px;
+}
+
+.breadcrumb-item {
+  cursor: pointer;
+  color: var(--in-content-text-color);
+}
+
+.breadcrumb-item:hover {
+  color: var(--blue-60);
+}
+
+.breadcrumb-separator {
+  width: 15px;
+  list-style-image: url("chrome://browser/content/torconnect/arrow-right.svg");
+}
+
+.breadcrumb-separator:dir(rtl) {
+  scale: -1 1;
+}
+
+.breadcrumb-icon {
+  display: inline list-item;
+  list-style-position: inside;
+  fill: currentColor;
+  -moz-context-properties: fill;
+}
+
+.breadcrumb-label {
+  margin-top: -1px;
+}
+
+#breadcrumbs .active {
+  color: var(--blue-60);
+}
+
+#breadcrumbs .disabled, #breadcrumbs .disabled:hover {
+  color: var(--green-90-a40);
+  cursor: default;
+}
+
+#breadcrumbs .error {
+  color: var(--red-60);
+}
+
+#connection-assist {
+  margin-left: 0;
+}
+
+#connection-assist-icon {
+  list-style-image: url("chrome://browser/content/torconnect/onion-slash-fillable.svg");
+}
+
+#location-settings-icon {
+  list-style-image: url("chrome://browser/content/torconnect/globe.svg");
+}
+
+#try-bridge {
+  cursor: default;
+}
+
+#try-bridge-icon {
+  list-style-image: url("chrome://browser/content/torconnect/bridge.svg");
+}
+
+button.primary {
   background-color: var(--purple-60)!important;
-  color: white;
-  fill: white;
+  color: white!important;
+  fill: white!important;
 }
 
-#connectButton:hover {
+button.primary:hover {
   background-color: var(--purple-70)!important;
-  color: white;
-  fill: white;
+  color: white!important;
+  fill: white!important;
 }
 
-#connectButton:active {
+button.primary:active {
   background-color: var(--purple-80)!important;
-  color: white;
-  fill: white;
+  color: white!important;
+  fill: white!important;
+}
+
+div#locationDropdownLabel {
+  margin-block: auto;
+  margin-inline: 4px;
+}
+
+/* these two follow similar css in error-pages.css for buttons */
+ at media only screen and (min-width: 480px) {
+  form#locationDropdown {
+    margin-inline: 4px;
+    /* subtracting out the margin is needeed because by
+       default forms have different margins than buttons */
+    max-width: calc(100% - 8px);
+  }
+}
+
+ at media only screen and (max-width: 480px) {
+  form#locationDropdown,
+  div#locationDropdownLabel {
+    margin: 0.66em 0 0;
+  }
+
+  form#locationDropdown {
+    width: 100%;
+  }
+}
+
+form#locationDropdown select {
+  max-width: 100%;
+  padding-block: 0;
+  margin-inline: 0;
+  font-weight: 700;
 }
 
 /* checkbox css */
@@ -92,75 +203,28 @@ input[type="checkbox"]:not(:disabled):active:checked {
   vertical-align: middle;
 }
 
-#copyLogButton {
-  position: relative;
-}
-
 /* mirrors p element spacing */
-#copyLogContainer {
+#viewLogContainer {
   margin:  1em 0;
   height:  1.2em;
   min-height:  1.2em;
 }
 
-#copyLogLink {
+#viewLogLink {
   position:  relative;
   display:  inline-block;
   color:  var(--in-content-link-color);
 }
 
 /* hidden apparently only works if no display is set; who knew? */
-#copyLogLink[hidden="true"] {
+#viewLogLink[hidden="true"] {
   display:  none;
 }
 
-#copyLogLink:hover {
+#viewLogLink:hover {
   cursor:pointer;
 }
 
-/* This div:
-   - is centered over its parent
-   - centers its child
-   - has z-index above parent
-   - ignores mouse events from parent
-*/
-#copyLogTooltip {
-  pointer-events: none;
-  visibility: hidden;
-  display:  flex;
-  justify-content: center;
-  white-space: nowrap;
-  width: 0;
-  position:  absolute;
-
-  z-index:  1;
-  left: 50%;
-  bottom:  calc(100% + 0.25em);
-}
-
-/* tooltip content (any content could go here) */
-#copyLogTooltipText {
-  background-color: var(--green-50);
-  color: var(--green-90);
-  border-radius: 2px;
-  padding: 4px;
-  line-height: 13px;
-  font: 11px sans-serif;
-  font-weight: 400;
-}
-
-/* our speech bubble tail */
-#copyLogTooltipText::after {
-  content: "";
-  position: absolute;
-  top: 100%;
-  left: 50%;
-  margin-left: -4px;
-  border-width: 4px;
-  border-style: solid;
-  border-color: var(--green-50) transparent transparent transparent;
-}
-
 body {
   padding: 0px !important;
   justify-content: space-between;
@@ -175,6 +239,9 @@ body {
 }
 
 .title.error {
-  background-image: url("chrome://browser/content/torconnect/onion-slash.svg");
+  background-image: url("chrome://browser/content/torconnect/connection-failure.svg");
 }
 
+.title.location {
+  background-image: url("chrome://browser/content/torconnect/connection-location.svg");
+}
diff --git a/browser/components/torconnect/content/aboutTorConnect.js b/browser/components/torconnect/content/aboutTorConnect.js
index 26b17afb69383..8710eef95a49e 100644
--- a/browser/components/torconnect/content/aboutTorConnect.js
+++ b/browser/components/torconnect/content/aboutTorConnect.js
@@ -5,59 +5,139 @@
 // populated in AboutTorConnect.init()
 let TorStrings = {};
 let TorConnectState = {};
+let TorCensorshipLevel = {};
+
+const BreadcrumbStatus = Object.freeze({
+  Disabled: -1,
+  Default: 0,
+  Active: 1,
+  Error: 2,
+});
 
 class AboutTorConnect {
   selectors = Object.freeze({
     textContainer: {
       title: "div.title",
       titleText: "h1.title-text",
+      longContentText: "#connectLongContentText",
     },
     progress: {
       description: "p#connectShortDescText",
       meter: "div#progressBackground",
     },
-    copyLog: {
-      link: "span#copyLogLink",
-      tooltip: "div#copyLogTooltip",
-      tooltipText: "span#copyLogTooltipText",
+    breadcrumbs: {
+      container: "#breadcrumbs",
+      connectionAssist: {
+        link: "#connection-assist",
+        label: "#connection-assist .breadcrumb-label",
+      },
+      locationSettings: {
+        link: "#location-settings",
+        label: "#location-settings .breadcrumb-label",
+      },
+      tryBridge: {
+        link: "#try-bridge",
+        label: "#try-bridge .breadcrumb-label",
+      },
+    },
+    viewLog: {
+      link: "span#viewLogLink",
     },
     quickstart: {
+      container: "div#quickstartContainer",
       checkbox: "input#quickstartCheckbox",
       label: "label#quickstartCheckboxLabel",
     },
     buttons: {
-      connect: "button#connectButton",
+      restart: "button#restartButton",
+      configure: "button#configureButton",
       cancel: "button#cancelButton",
-      advanced: "button#advancedButton",
+      connect: "button#connectButton",
+      tryBridge: "button#tryBridgeButton",
+      locationDropdownLabel: "div#locationDropdownLabel",
+      locationDropdown: "form#locationDropdown",
+      locationDropdownSelect: "form#locationDropdown select",
+      tryAgain: "button#tryAgainButton",
     },
-  })
+  });
 
   elements = Object.freeze({
     title: document.querySelector(this.selectors.textContainer.title),
     titleText: document.querySelector(this.selectors.textContainer.titleText),
-    progressDescription: document.querySelector(this.selectors.progress.description),
+    longContentText: document.querySelector(
+      this.selectors.textContainer.longContentText
+    ),
+    progressDescription: document.querySelector(
+      this.selectors.progress.description
+    ),
     progressMeter: document.querySelector(this.selectors.progress.meter),
-    copyLogLink: document.querySelector(this.selectors.copyLog.link),
-    copyLogTooltip: document.querySelector(this.selectors.copyLog.tooltip),
-    copyLogTooltipText: document.querySelector(this.selectors.copyLog.tooltipText),
-    quickstartCheckbox: document.querySelector(this.selectors.quickstart.checkbox),
+    breadcrumbContainer: document.querySelector(
+      this.selectors.breadcrumbs.container
+    ),
+    connectionAssistLink: document.querySelector(
+      this.selectors.breadcrumbs.connectionAssist.link
+    ),
+    connectionAssistLabel: document.querySelector(
+      this.selectors.breadcrumbs.connectionAssist.label
+    ),
+    locationSettingsLink: document.querySelector(
+      this.selectors.breadcrumbs.locationSettings.link
+    ),
+    locationSettingsLabel: document.querySelector(
+      this.selectors.breadcrumbs.locationSettings.label
+    ),
+    tryBridgeLink: document.querySelector(
+      this.selectors.breadcrumbs.tryBridge.link
+    ),
+    tryBridgeLabel: document.querySelector(
+      this.selectors.breadcrumbs.tryBridge.label
+    ),
+    viewLogLink: document.querySelector(this.selectors.viewLog.link),
+    quickstartContainer: document.querySelector(
+      this.selectors.quickstart.container
+    ),
+    quickstartCheckbox: document.querySelector(
+      this.selectors.quickstart.checkbox
+    ),
     quickstartLabel: document.querySelector(this.selectors.quickstart.label),
-    connectButton: document.querySelector(this.selectors.buttons.connect),
+    restartButton: document.querySelector(this.selectors.buttons.restart),
+    configureButton: document.querySelector(this.selectors.buttons.configure),
     cancelButton: document.querySelector(this.selectors.buttons.cancel),
-    advancedButton: document.querySelector(this.selectors.buttons.advanced),
-  })
+    connectButton: document.querySelector(this.selectors.buttons.connect),
+    tryBridgeButton: document.querySelector(this.selectors.buttons.tryBridge),
+    locationDropdownLabel: document.querySelector(
+      this.selectors.buttons.locationDropdownLabel
+    ),
+    locationDropdown: document.querySelector(
+      this.selectors.buttons.locationDropdown
+    ),
+    locationDropdownSelect: document.querySelector(
+      this.selectors.buttons.locationDropdownSelect
+    ),
+    tryAgainButton: document.querySelector(this.selectors.buttons.tryAgain),
+  });
 
   // a redirect url can be passed as a query parameter for the page to
   // forward us to once bootstrap completes (otherwise the window will just close)
-  redirect = null
+  redirect = null;
+
+  locations = {};
 
   beginBootstrap() {
     this.hide(this.elements.connectButton);
+    this.hide(this.elements.quickstartContainer);
     this.show(this.elements.cancelButton);
     this.elements.cancelButton.focus();
     RPMSendAsyncMessage("torconnect:begin-bootstrap");
   }
 
+  beginAutoBootstrap(countryCode) {
+    this.hide(this.elements.tryBridgeButton);
+    this.show(this.elements.cancelButton);
+    this.elements.cancelButton.focus();
+    RPMSendAsyncMessage("torconnect:begin-autobootstrap", countryCode);
+  }
+
   cancelBootstrap() {
     RPMSendAsyncMessage("torconnect:cancel-bootstrap");
   }
@@ -66,7 +146,12 @@ class AboutTorConnect {
   Element helper methods
   */
 
-  show(element) {
+  show(element, primary) {
+    if (primary) {
+      element.classList.add("primary");
+    } else {
+      element.classList.remove("primary");
+    }
     element.removeAttribute("hidden");
   }
 
@@ -74,15 +159,121 @@ class AboutTorConnect {
     element.setAttribute("hidden", "true");
   }
 
-  setTitle(title, error) {
-    this.elements.titleText.textContent = title;
-    document.title = title;
+  hideButtons() {
+    this.hide(this.elements.restartButton);
+    this.hide(this.elements.configureButton);
+    this.hide(this.elements.cancelButton);
+    this.hide(this.elements.connectButton);
+    this.hide(this.elements.tryBridgeButton);
+    this.hide(this.elements.locationDropdownLabel);
+    this.hide(this.elements.locationDropdown);
+    this.hide(this.elements.tryAgainButton);
+  }
+
+  populateLocations() {
+    const selectCountryRegion = document.createElement("option");
+    selectCountryRegion.textContent = TorStrings.torConnect.selectCountryRegion;
+    selectCountryRegion.value = "";
+
+    // get all codes and names from TorStrings
+    const locationNodes = [];
+    for (const [code, name] of Object.entries(this.locations)) {
+      let option = document.createElement("option");
+      option.value = code;
+      option.textContent = name;
+      locationNodes.push(option);
+    }
+    // locale sort by name
+    locationNodes.sort((left, right) =>
+      left.textContent.localeCompare(right.textContent)
+    );
+
+    this.elements.locationDropdownSelect.append(
+      selectCountryRegion,
+      ...locationNodes
+    );
+  }
+
+  populateSpecialLocations(specialLocations) {
+    this.removeSpecialLocations();
+    if (!specialLocations || !specialLocations.length) {
+      return;
+    }
+
+    const locationNodes = [];
+    for (const code of specialLocations) {
+      const option = document.createElement("option");
+      option.value = code;
+
+      // codes (partially) come from rdsys service, so make sure we have a
+      // string defined for it
+      let name = this.locations[code];
+      if (!name) {
+        name = code;
+      }
+
+      option.textContent = name;
+      locationNodes.push(option);
+    }
+    // locale sort by name
+    locationNodes.sort((left, right) =>
+      left.textContent.localeCompare(right.textContent)
+    );
+
+    const disabledDividerNode = document.createElement("option");
+    disabledDividerNode.setAttribute("disabled", true);
+    disabledDividerNode.className = "divider";
+    this.elements.locationDropdownSelect.options[0].after(
+      ...locationNodes,
+      disabledDividerNode
+    );
+  }
 
-    if (error) {
-      this.elements.title.classList.add("error");
+  removeSpecialLocations() {
+    const select = this.elements.locationDropdownSelect;
+    if (select.querySelector(".divider") === null) {
+      return;
+    }
+
+    while (select.options.length > 1) {
+      // Skip the "select country/region" option
+      const opt = select.options[1];
+      opt.remove();
+      if (opt.className === "divider") {
+        break;
+      }
+    }
+  }
+
+  validateLocation() {
+    const selectedIndex = this.elements.locationDropdownSelect.selectedIndex;
+    const selectedOption = this.elements.locationDropdownSelect.options[
+      selectedIndex
+    ];
+    if (!selectedOption.value) {
+      this.elements.tryAgainButton.setAttribute("disabled", "disabled");
     } else {
+      this.elements.tryAgainButton.removeAttribute("disabled");
+    }
+  }
+
+  setTitle(title, className) {
+    this.elements.titleText.textContent = title;
+    if (className !== "error") {
       this.elements.title.classList.remove("error");
     }
+    if (className !== "location") {
+      this.elements.title.classList.remove("location");
+    }
+    if (className) {
+      this.elements.title.classList.add(className);
+    }
+    document.title = title;
+  }
+
+  setLongText(...args) {
+    this.elements.longContentText.textContent = "";
+    this.elements.longContentText.append(...args);
   }
 
   setProgress(description, visible, percent) {
@@ -95,13 +286,40 @@ class AboutTorConnect {
     }
   }
 
+  setBreadcrumbsStatus(connectionAssist, locationSettings, tryBridge) {
+    this.elements.breadcrumbContainer.classList.remove("hidden");
+    let elems = [
+      [this.elements.connectionAssistLink, connectionAssist],
+      [this.elements.locationSettingsLink, locationSettings],
+      [this.elements.tryBridgeLink, tryBridge],
+    ];
+    elems.forEach(([elem, status]) => {
+      elem.classList.remove("disabled");
+      elem.classList.remove("active");
+      elem.classList.remove("error");
+      switch (status) {
+        case BreadcrumbStatus.Disabled:
+          elem.classList.add("disabled");
+          break;
+        case BreadcrumbStatus.Active:
+          elem.classList.add("active");
+          break;
+        case BreadcrumbStatus.Error:
+          elem.classList.add("error");
+          break;
+      }
+    });
+  }
+
+  hideBreadcrumbs() {
+    this.elements.breadcrumbContainer.classList.add("hidden");
+  }
+
   /*
   These methods update the UI based on the current TorConnect state
   */
 
   updateUI(state) {
-    console.log(state);
-
     // calls update_$state()
     this[`update_${state.State}`](state);
     this.elements.quickstartCheckbox.checked = state.QuickStartEnabled;
@@ -113,82 +331,130 @@ class AboutTorConnect {
     const hasError = false;
     const showProgressbar = false;
 
-    this.setTitle(TorStrings.torConnect.torConnect, hasError);
-    this.setProgress(TorStrings.settings.torPreferencesDescription, showProgressbar);
-    this.hide(this.elements.copyLogLink);
-    this.hide(this.elements.connectButton);
-    this.hide(this.elements.advancedButton);
-    this.hide(this.elements.cancelButton);
+    this.setTitle(TorStrings.torConnect.torConnect, hasError ? "error" : "");
+    this.setProgress(
+      TorStrings.settings.torPreferencesDescription,
+      showProgressbar
+    );
+    this.hide(this.elements.quickstartContainer);
+    this.hide(this.elements.viewLogLink);
+    this.hideButtons();
   }
 
   update_Configuring(state) {
     const hasError = state.ErrorMessage != null;
     const showProgressbar = false;
 
+    this.hide(this.elements.quickstartContainer);
+    this.hide(this.elements.viewLogLink);
+    this.hideButtons();
+
     if (hasError) {
-      this.setTitle(state.ErrorMessage, hasError);
-      this.setProgress(state.ErrorDetails, showProgressbar);
-      this.show(this.elements.copyLogLink);
-      this.elements.connectButton.textContent = TorStrings.torConnect.tryAgain;
+      switch (state.DetectedCensorshiplevel) {
+        case TorCensorshipLevel.None:
+          // we shouldn't be able to get here
+          break;
+        case TorCensorshipLevel.Moderate:
+          // bootstrap failed once, offer auto bootstrap
+          this.showConnectionAssistant(state.ErrorDetails);
+          if (state.StateChanged) {
+            this.elements.tryBridgeButton.focus();
+          }
+          break;
+        case TorCensorshipLevel.Severe:
+          // autobootstrap failed, verify correct location
+          this.showLocationSettings(state.CountryCodes, state.ErrorMessage);
+          if (state.StateChanged) {
+            this.elements.tryAgainButton.focus();
+          }
+          break;
+        case TorCensorshipLevel.Extreme:
+          // finally offer to restart tor-browser or go to configure options
+          this.showFinalError(state);
+          break;
+      }
     } else {
-      this.setTitle(TorStrings.torConnect.torConnect, hasError);
-      this.setProgress(TorStrings.settings.torPreferencesDescription, showProgressbar);
-      this.hide(this.elements.copyLogLink);
-      this.elements.connectButton.textContent = TorStrings.torConnect.torConnectButton;
-    }
-    this.show(this.elements.connectButton);
-    if (state.StateChanged) {
-      this.elements.connectButton.focus();
+      this.setTitle(TorStrings.torConnect.torConnect, "");
+      this.setLongText(TorStrings.settings.torPreferencesDescription);
+      this.setProgress("", showProgressbar);
+      this.show(this.elements.quickstartContainer);
+      this.show(this.elements.configureButton);
+      this.show(this.elements.connectButton, true);
+      if (state.StateChanged) {
+        this.elements.connectButton.focus();
+      }
+      this.elements.connectButton.textContent =
+        TorStrings.torConnect.torConnectButton;
     }
-    this.show(this.elements.advancedButton);
-    this.hide(this.elements.cancelButton);
   }
 
   update_AutoBootstrapping(state) {
-    // TODO: noop until this state is used
+    const showProgressbar = true;
+
+    if (state.DetectedCensorshiplevel >= TorCensorshipLevel.Severe) {
+      this.setTitle(TorStrings.torConnect.tryingBridgeAgain, "");
+    } else {
+      this.setTitle(TorStrings.torConnect.tryingBridge, "");
+    }
+    this.showConfigureConnectionLink(TorStrings.torConnect.assistDescription);
+    this.setProgress(
+      state.BootstrapStatus,
+      showProgressbar,
+      state.BootstrapProgress
+    );
+    this.setBreadcrumbsStatus(
+      BreadcrumbStatus.Disabled,
+      BreadcrumbStatus.Disabled,
+      BreadcrumbStatus.Active
+    );
+    if (state.ShowViewLog) {
+      this.show(this.elements.viewLogLink);
+    } else {
+      this.hide(this.elements.viewLogLink);
+    }
+    this.hideButtons();
+    this.show(this.elements.cancelButton, true);
+    if (state.StateChanged) {
+      this.elements.cancelButton.focus();
+    }
   }
 
   update_Bootstrapping(state) {
-    const hasError = false;
     const showProgressbar = true;
 
-    this.setTitle(state.BootstrapStatus ? state.BootstrapStatus : TorStrings.torConnect.torConnecting, hasError);
-    this.setProgress(TorStrings.settings.torPreferencesDescription, showProgressbar, state.BootstrapProgress);
-    if (state.ShowCopyLog) {
-      this.show(this.elements.copyLogLink);
+    this.setTitle(TorStrings.torConnect.torConnecting, "");
+    this.setLongText(TorStrings.settings.torPreferencesDescription);
+    this.setProgress("", showProgressbar, state.BootstrapProgress);
+    this.hideBreadcrumbs();
+    if (state.ShowViewLog) {
+      this.show(this.elements.viewLogLink);
     } else {
-      this.hide(this.elements.copyLogLink);
+      this.hide(this.elements.viewLogLink);
     }
-    this.hide(this.elements.connectButton);
-    this.hide(this.elements.advancedButton);
-    this.show(this.elements.cancelButton);
+    this.hideButtons();
+    this.show(this.elements.cancelButton, true);
     if (state.StateChanged) {
       this.elements.cancelButton.focus();
     }
   }
 
   update_Error(state) {
-    const hasError = true;
     const showProgressbar = false;
 
-    this.setTitle(state.ErrorMessage, hasError);
+    this.setTitle(state.ErrorMessage, "error");
+    this.setLongText("");
     this.setProgress(state.ErrorDetails, showProgressbar);
-    this.show(this.elements.copyLogLink);
-    this.elements.connectButton.textContent = TorStrings.torConnect.tryAgain;
-    this.show(this.elements.connectButton);
-    this.show(this.elements.advancedButton);
-    this.hide(this.elements.cancelButton);
+    this.hideButtons();
+    this.show(this.elements.viewLogLink);
   }
 
   update_Bootstrapped(state) {
-    const hasError = false;
     const showProgressbar = true;
 
-    this.setTitle(TorStrings.torConnect.torConnected, hasError);
-    this.setProgress(TorStrings.settings.torPreferencesDescription, showProgressbar, 100);
-    this.hide(this.elements.connectButton);
-    this.hide(this.elements.advancedButton);
-    this.hide(this.elements.cancelButton);
+    this.setTitle(TorStrings.torConnect.torConnected, "");
+    this.setLongText(TorStrings.settings.torPreferencesDescription);
+    this.setProgress("", showProgressbar, 100);
+    this.hideButtons();
 
     // redirects page to the requested redirect url, removes about:torconnect
     // from the page stack, so users cannot accidentally go 'back' to the
@@ -201,45 +467,129 @@ class AboutTorConnect {
     // it isn't in use (eg using tor-launcher or system tor)
   }
 
-  async initElements(direction) {
+  showConnectionAssistant(error) {
+    const hasError = !!error;
+    this.setTitle(
+      TorStrings.torConnect.couldNotConnect,
+      hasError ? "error" : ""
+    );
+    this.showConfigureConnectionLink(TorStrings.torConnect.assistDescription);
+    this.setProgress(error, false);
+    this.setBreadcrumbsStatus(
+      BreadcrumbStatus.Active,
+      BreadcrumbStatus.Default,
+      BreadcrumbStatus.Disabled
+    );
+    this.hideButtons();
+    this.show(this.elements.configureButton);
+    this.show(this.elements.connectButton);
+    this.show(this.elements.tryBridgeButton, true);
+  }
+
+  showConfigureConnectionLink(text) {
+    const pieces = text.split("#1");
+    const link = document.createElement("a");
+    link.textContent = TorStrings.torConnect.configureConnection;
+    link.setAttribute("href", "#");
+    link.addEventListener("click", e => {
+      e.preventDefault();
+      RPMSendAsyncMessage("torconnect:open-tor-preferences");
+    });
+    this.setLongText(pieces[0], link, pieces[1]);
+  }
 
+  showLocationSettings(locations, error) {
+    const hasError = !!error;
+    if (hasError) {
+      this.setTitle(TorStrings.torConnect.errorLocation, "location");
+      this.setLongText(TorStrings.torConnect.errorLocationDescription);
+      this.setBreadcrumbsStatus(
+        BreadcrumbStatus.Disabled,
+        BreadcrumbStatus.Error,
+        BreadcrumbStatus.Disabled
+      );
+      this.elements.tryAgainButton.textContent = TorStrings.torConnect.tryAgain;
+    } else {
+      this.setTitle(TorStrings.torConnect.addLocation, "location");
+      this.showConfigureConnectionLink(
+        TorStrings.torConnect.addLocationDescription
+      );
+      this.setBreadcrumbsStatus(
+        BreadcrumbStatus.Default,
+        BreadcrumbStatus.Active,
+        BreadcrumbStatus.Disabled
+      );
+      this.elements.tryAgainButton.textContent =
+        TorStrings.torConnect.tryBridge;
+    }
+    this.setProgress(error, false);
+    this.hideButtons();
+    if (!locations || !locations.length) {
+      RPMSendQuery("torconnect:get-country-codes").then(codes => {
+        if (codes && codes.length) {
+          this.populateSpecialLocations(codes);
+        }
+      });
+    } else {
+      this.populateSpecialLocations(locations);
+    }
+    this.validateLocation();
+    this.show(this.elements.locationDropdownLabel);
+    this.show(this.elements.locationDropdown);
+    this.show(this.elements.tryAgainButton, true);
+  }
+
+  showFinalError(state) {
+    this.setTitle(TorStrings.torConnect.finalError, "error");
+    this.setLongText(TorStrings.torConnect.finalErrorDescription);
+    this.setProgress(state ? state.ErrorDetails : "", false);
+    this.hideButtons();
+    this.show(this.elements.restartButton);
+    this.show(this.elements.configureButton);
+    this.show(this.elements.connectButton, true);
+  }
+
+  initElements(direction) {
     document.documentElement.setAttribute("dir", direction);
 
-    // sets the text content while keeping the child elements intact
-    this.elements.copyLogLink.childNodes[0].nodeValue =
-      TorStrings.torConnect.copyLog;
-    this.elements.copyLogLink.addEventListener("click", async (event) => {
-      const copiedMessage = await RPMSendQuery("torconnect:copy-tor-logs");
-      this.elements.copyLogTooltipText.textContent = copiedMessage;
-      this.elements.copyLogTooltipText.style.visibility = "visible";
-
-      // clear previous timeout if one already exists
-      if (this.copyLogTimeoutId) {
-        clearTimeout(this.copyLogTimeoutId);
+    this.elements.connectionAssistLink.addEventListener("click", event => {
+      if (!this.elements.connectionAssistLink.classList.contains("disabled")) {
+        this.showConnectionAssistant();
       }
-
-      // hide tooltip after X ms
-      const TOOLTIP_TIMEOUT = 2000;
-      this.copyLogTimeoutId = setTimeout(() => {
-        this.elements.copyLogTooltipText.style.visibility = "hidden";
-        this.copyLogTimeoutId = 0;
-      }, TOOLTIP_TIMEOUT);
+    });
+    this.elements.connectionAssistLabel.textContent =
+      TorStrings.torConnect.breadcrumbAssist;
+    this.elements.locationSettingsLink.addEventListener("click", event => {
+      if (!this.elements.connectionAssistLink.classList.contains("disabled")) {
+        this.showLocationSettings();
+      }
+    });
+    this.elements.locationSettingsLabel.textContent =
+      TorStrings.torConnect.breadcrumbLocation;
+    this.elements.tryBridgeLabel.textContent =
+      TorStrings.torConnect.breadcrumbTryBridge;
+
+    this.elements.viewLogLink.textContent = TorStrings.torConnect.viewLog;
+    this.elements.viewLogLink.addEventListener("click", event => {
+      RPMSendAsyncMessage("torconnect:view-tor-logs");
     });
 
     this.elements.quickstartCheckbox.addEventListener("change", () => {
       const quickstart = this.elements.quickstartCheckbox.checked;
       RPMSendAsyncMessage("torconnect:set-quickstart", quickstart);
     });
-    this.elements.quickstartLabel.textContent = TorStrings.settings.quickstartCheckbox;
+    this.elements.quickstartLabel.textContent =
+      TorStrings.settings.quickstartCheckbox;
 
-    this.elements.connectButton.textContent =
-      TorStrings.torConnect.torConnectButton;
-    this.elements.connectButton.addEventListener("click", () => {
-      this.beginBootstrap();
+    this.elements.restartButton.textContent =
+      TorStrings.torConnect.restartTorBrowser;
+    this.elements.restartButton.addEventListener("click", () => {
+      RPMSendAsyncMessage("torconnect:restart");
     });
 
-    this.elements.advancedButton.textContent = TorStrings.torConnect.torConfigure;
-    this.elements.advancedButton.addEventListener("click", () => {
+    this.elements.configureButton.textContent =
+      TorStrings.torConnect.torConfigure;
+    this.elements.configureButton.addEventListener("click", () => {
       RPMSendAsyncMessage("torconnect:open-tor-preferences");
     });
 
@@ -247,6 +597,36 @@ class AboutTorConnect {
     this.elements.cancelButton.addEventListener("click", () => {
       this.cancelBootstrap();
     });
+
+    this.elements.connectButton.textContent =
+      TorStrings.torConnect.torConnectButton;
+    this.elements.connectButton.addEventListener("click", () => {
+      this.beginBootstrap();
+    });
+
+    this.populateLocations();
+    this.elements.locationDropdownSelect.addEventListener("change", () => {
+      this.validateLocation();
+    });
+
+    this.elements.tryBridgeButton.textContent = TorStrings.torConnect.tryBridge;
+    this.elements.tryBridgeButton.addEventListener("click", () => {
+      this.beginAutoBootstrap();
+    });
+
+    this.elements.locationDropdownLabel.textContent =
+      TorStrings.torConnect.yourLocation;
+
+    this.elements.tryAgainButton.textContent = TorStrings.torConnect.tryAgain;
+    this.elements.tryAgainButton.setAttribute("disabled", "disabled");
+    this.elements.tryAgainButton.addEventListener("click", () => {
+      let selectedIndex = this.elements.locationDropdownSelect.selectedIndex;
+      let selectedOption = this.elements.locationDropdownSelect.options[
+        selectedIndex
+      ];
+
+      this.beginAutoBootstrap(selectedOption.value);
+    });
   }
 
   initObservers() {
@@ -257,7 +637,7 @@ class AboutTorConnect {
   }
 
   initKeyboardShortcuts() {
-    document.onkeydown = (evt) => {
+    document.onkeydown = evt => {
       // unfortunately it looks like we still haven't standardized keycodes to
       // integers, so we must resort to a string compare here :(
       // see https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code for relevant documentation
@@ -284,6 +664,8 @@ class AboutTorConnect {
     // various constants
     TorStrings = Object.freeze(args.TorStrings);
     TorConnectState = Object.freeze(args.TorConnectState);
+    TorCensorshipLevel = Object.freeze(args.TorCensorshipLevel);
+    this.locations = args.CountryNames;
 
     this.initElements(args.Direction);
     this.initObservers();
diff --git a/browser/components/torconnect/content/aboutTorConnect.xhtml b/browser/components/torconnect/content/aboutTorConnect.xhtml
index 595bbdf9a70a1..a98af43e2d53f 100644
--- a/browser/components/torconnect/content/aboutTorConnect.xhtml
+++ b/browser/components/torconnect/content/aboutTorConnect.xhtml
@@ -9,22 +9,35 @@
   <body>
     <div id="progressBackground"></div>
     <div id="connectPageContainer" class="container">
+      <div id="breadcrumbs" class="hidden">
+        <span id="connection-assist" class="breadcrumb-item">
+          <span id="connection-assist-icon" class="breadcrumb-icon" />
+          <span class="breadcrumb-label"/>
+        </span>
+        <span class="breadcrumb-separator breadcrumb-icon" />
+        <span id="location-settings" class="breadcrumb-item">
+          <span id="location-settings-icon" class="breadcrumb-icon" />
+          <span class="breadcrumb-label"/>
+        </span>
+        <span class="breadcrumb-separator breadcrumb-icon" />
+        <span id="try-bridge" class="breadcrumb-item">
+          <span id="try-bridge-icon" class="breadcrumb-icon" />
+          <span class="breadcrumb-label"/>
+        </span>
+      </div>
       <div id="text-container">
         <div class="title">
           <h1 class="title-text"/>
         </div>
         <div id="connectLongContent">
-          <div id="connectShortDesc">
-            <p id="connectShortDescText" />
-          </div>
+          <p id="connectLongContentText" />
+        </div>
+        <div id="connectShortDesc">
+          <p id="connectShortDescText" />
         </div>
 
-        <div id="copyLogContainer">
-          <span id="copyLogLink" hidden="true">
-            <div id="copyLogTooltip">
-              <span id="copyLogTooltipText"/>
-            </div>
-          </span>
+        <div id="viewLogContainer">
+          <span id="viewLogLink" hidden="true"></span>
         </div>
 
         <div id="quickstartContainer">
@@ -33,9 +46,17 @@
         </div>
 
         <div id="connectButtonContainer" class="button-container">
-          <button id="advancedButton" hidden="true"></button>
+          <button id="restartButton" hidden="true"></button>
+          <button id="configureButton" hidden="true"></button>
           <button id="cancelButton" hidden="true"></button>
-          <button id="connectButton" class="primary try-again" hidden="true"></button>
+          <button id="connectButton" class="primary" hidden="true"></button>
+          <button id="tryBridgeButton" class="primary" hidden="true"></button>
+          <div id="locationDropdownLabel"/>
+          <form id="locationDropdown" hidden="true">
+            <select id="countries">
+            </select>
+          </form>
+          <button id="tryAgainButton" class="primary" hidden="true"></button>
         </div>
       </div>
     </div>
diff --git a/browser/components/torconnect/content/arrow-right.svg b/browser/components/torconnect/content/arrow-right.svg
new file mode 100644
index 0000000000000..3f6d8ded52bed
--- /dev/null
+++ b/browser/components/torconnect/content/arrow-right.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+    <path d="M10.9991 8.352L5.53406 13.818C5.41557 13.9303 5.25792 13.9918 5.09472 13.9895C4.93152 13.9872 4.77567 13.9212 4.66039 13.8057C4.54511 13.6902 4.47951 13.5342 4.47758 13.3709C4.47565 13.2077 4.53754 13.0502 4.65006 12.932L9.58506 7.998L4.65106 3.067C4.53868 2.94864 4.47697 2.79106 4.47909 2.62786C4.48121 2.46466 4.54698 2.30874 4.66239 2.19333C4.7778 2.07792 4.93372 2.01215 5.09692 2.01003C5.26012 2.00792 5.41769 2.06962 5.53606 2.182L11.0001 7.647L10.9991 8.352Z" fill="conte [...]
+</svg>
diff --git a/browser/components/torconnect/content/bridge.svg b/browser/components/torconnect/content/bridge.svg
new file mode 100644
index 0000000000000..5ae3f05dfd082
--- /dev/null
+++ b/browser/components/torconnect/content/bridge.svg
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+    <path d="M1 9.48528C1 9.48528 3.82843 9.48528 6.65685 6.65685C9.48528 3.82843 9.48528 1 9.48528 1" stroke="context-fill" stroke-width="1.25" stroke-linecap="round"/>
+    <path d="M6.65686 15.1421C6.65686 15.1421 6.65686 12.3137 9.48529 9.48529C12.3137 6.65686 15.1421 6.65686 15.1421 6.65686" stroke="context-fill" stroke-width="1.25" stroke-linecap="round"/>
+</svg>
diff --git a/browser/components/torconnect/content/connection-failure.svg b/browser/components/torconnect/content/connection-failure.svg
new file mode 100644
index 0000000000000..8f2005e360556
--- /dev/null
+++ b/browser/components/torconnect/content/connection-failure.svg
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg fill="none" height="60" viewBox="0 0 60 60" width="60" xmlns="http://www.w3.org/2000/svg">
+  <path fill="context-fill" d="M 30,1.875 C 14.467,1.875 1.875,14.467 1.875,30 c 0,6.725546 2.3647525,12.894963 6.3027344,17.734375 l -4.7636719,4.763672 c -0.7834743,0.783474 -0.7834743,2.044651 0,2.828125 0.7834743,0.783474 2.0446507,0.783474 2.828125,0 C 21.046044,40.52782 34.415343,27.146014 47.546875,14.023438 v -0.002 l 6.779297,-6.7792965 c 0.783474,-0.7834743 0.783474,-2.0446507 0,-2.828125 -0.783474,-0.7834743 -2.044651,-0.7834743 -2.828125,0 L 47.734375,8.1777344 C 42.894963,4. [...]
+  <path fill="#d70022" d="m59.5328 52.4973-10.261-18.5715c-.7112-1.2833-1.9917-1.9258-3.2722-1.9258-1.2806 0-2.5611.6425-3.2704 1.9258l-10.261 18.5715c-1.3701 2.4755.4312 5.5027 3.2704 5.5027h20.5238c2.8373 0 4.6387-3.0272 3.2704-5.5027zm-12.3666-.533-.4666.4642h-1.4l-.4667-.4642v-1.3929l.4667-.4643h1.4l.4666.4643zm0-4.992c0 .3078-.1229.603-.3417.8207s-.5155.34-.8249.34-.6062-.1223-.825-.34-.3417-.5129-.3417-.8207v-6.383c0-.3079.1229-.6031.3417-.8208s.5156-.34.825-.34.6061.1223.8249.34.3 [...]
+</svg>
diff --git a/browser/components/torconnect/content/connection-location.svg b/browser/components/torconnect/content/connection-location.svg
new file mode 100644
index 0000000000000..1e5c41ccf99a0
--- /dev/null
+++ b/browser/components/torconnect/content/connection-location.svg
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg fill="none" height="60" viewBox="0 0 60 60" width="60" xmlns="http://www.w3.org/2000/svg">
+  <path fill="context-fill" d="M 30,1.875 C 14.467,1.875 1.875,14.467 1.875,30 c 0,6.725546 2.3647429,12.894963 6.3027344,17.734375 l -4.7636719,4.763672 c -0.7834743,0.783474 -0.7834743,2.044651 0,2.828125 0.7834743,0.783474 2.0446507,0.783474 2.828125,0 C 21.049647,40.524244 34.416498,27.144859 47.546875,14.023438 v -0.002 l 6.779297,-6.7792965 c 0.783474,-0.7834743 0.783474,-2.0446507 0,-2.828125 -0.783474,-0.7834743 -2.044651,-0.7834743 -2.828125,0 L 47.734375,8.1777344 C 42.894963,4 [...]
+  <path fill="#ffa436" d="m45 30c-3.713 0-7.274 1.475-9.8995 4.1005s-4.1005 6.1865-4.1005 9.8995 1.475 7.274 4.1005 9.8995 6.1865 4.1005 9.8995 4.1005 7.274-1.475 9.8995-4.1005 4.1005-6.1865 4.1005-9.8995-1.475-7.274-4.1005-9.8995-6.1865-4.1005-9.8995-4.1005zm4.5677 3.2667c1.9167.8229 3.5778 2.1443 4.8108 3.8267 1.233 1.6825 1.9928 3.6644 2.2004 5.7399h-4.1608c-.2298-3.4759-1.4862-6.8054-3.6101-9.5666zm-3.8248 0c2.5257 2.5792 4.06 5.967 4.3326 9.5666h-10.151c.2726-3.5996 1.8069-6.9874 4. [...]
+</svg>
diff --git a/browser/components/torconnect/content/globe.svg b/browser/components/torconnect/content/globe.svg
new file mode 100644
index 0000000000000..f4d1f19b43ce8
--- /dev/null
+++ b/browser/components/torconnect/content/globe.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+    <path d="M8 0.5C6.01088 0.5 4.10322 1.29018 2.6967 2.6967C1.29018 4.10322 0.5 6.01088 0.5 8C0.5 9.98912 1.29018 11.8968 2.6967 13.3033C4.10322 14.7098 6.01088 15.5 8 15.5C9.98912 15.5 11.8968 14.7098 13.3033 13.3033C14.7098 11.8968 15.5 9.98912 15.5 8C15.5 6.01088 14.7098 4.10322 13.3033 2.6967C11.8968 1.29018 9.98912 0.5 8 0.5ZM10.447 2.25C11.4738 2.69088 12.3637 3.39877 13.0242 4.30006C13.6848 5.20135 14.0918 6.26313 14.203 7.375H11.974C11.8509 5.51288 11.1778 3.72922 10.04 2.25H10 [...]
+</svg>
diff --git a/browser/components/torconnect/content/onion-slash-fillable.svg b/browser/components/torconnect/content/onion-slash-fillable.svg
new file mode 100644
index 0000000000000..18f1c5a5520bd
--- /dev/null
+++ b/browser/components/torconnect/content/onion-slash-fillable.svg
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg viewBox="0 0 16 16" width="16" height="16" xmlns="http://www.w3.org/2000/svg">
+  <path d="m14.1161 15.6245c-.0821.0001-.1634-.016-.2393-.0474-.0758-.0314-.1447-.0775-.2027-.1356l-12.749984-12.749c-.109266-.11882-.168406-.27526-.165071-.43666.003335-.16139.068886-.31525.182967-.42946.114078-.11421.267868-.17994.429258-.18345.16139-.00352.3179.05544.43685.16457l12.74998 12.75c.1168.1176.1824.2767.1824.4425s-.0656.3249-.1824.4425c-.058.058-.1269.1039-.2028.1352-.0759.0312-.1571.0471-.2392.0468z" fill-opacity="context-fill-opacity" fill="context-fill" />
+  <path d="m 8,0.5000002 c -1.61963,0 -3.1197431,0.5137987 -4.3457031,1.3867188 l 0.84375,0.8417968 0.7792969,0.78125 0.8613281,0.8613282 0.8164062,0.8164062 0.9863281,0.984375 h 0.058594 c 1.00965,0 1.828125,0.818485 1.828125,1.828125 0,0.01968 6.2e-4,0.039074 0,0.058594 L 10.8125,9.0449221 C 10.9334,8.7195921 11,8.3674002 11,8.0000002 c 0,-1.65685 -1.34314,-3 -3,-3 v -1.078125 c 2.25231,0 4.078125,1.825845 4.078125,4.078125 0,0.67051 -0.162519,1.3033281 -0.449219,1.8613281 l 0.861328,0 [...]
+</svg>
diff --git a/browser/components/torconnect/jar.mn b/browser/components/torconnect/jar.mn
index ed8a4de299b2b..8ca0b0651523e 100644
--- a/browser/components/torconnect/jar.mn
+++ b/browser/components/torconnect/jar.mn
@@ -3,5 +3,11 @@ browser.jar:
     content/browser/torconnect/aboutTorConnect.css                 (content/aboutTorConnect.css)
 *   content/browser/torconnect/aboutTorConnect.xhtml               (content/aboutTorConnect.xhtml)
     content/browser/torconnect/aboutTorConnect.js                  (content/aboutTorConnect.js)
+    content/browser/torconnect/arrow-right.svg                     (content/arrow-right.svg)
+    content/browser/torconnect/bridge.svg                          (content/bridge.svg)
+    content/browser/torconnect/globe.svg                           (content/globe.svg)
+    content/browser/torconnect/connection-failure.svg              (content/connection-failure.svg)
+    content/browser/torconnect/connection-location.svg             (content/connection-location.svg)
     content/browser/torconnect/onion.svg                           (content/onion.svg)
     content/browser/torconnect/onion-slash.svg                     (content/onion-slash.svg)
+    content/browser/torconnect/onion-slash-fillable.svg            (content/onion-slash-fillable.svg)
diff --git a/toolkit/modules/RemotePageAccessManager.jsm b/toolkit/modules/RemotePageAccessManager.jsm
index c0ead80fb2f67..5ddf546ce12de 100644
--- a/toolkit/modules/RemotePageAccessManager.jsm
+++ b/toolkit/modules/RemotePageAccessManager.jsm
@@ -217,23 +217,22 @@ let RemotePageAccessManager = {
       RPMRemoveMessageListener: ["*"],
     },
     "about:tbupdate": {
-      RPMSendQuery: [
-        "FetchUpdateData",
-      ],
+      RPMSendQuery: ["FetchUpdateData"],
     },
     "about:torconnect": {
-      RPMAddMessageListener: [
-        "torconnect:state-change",
-      ],
+      RPMAddMessageListener: ["torconnect:state-change"],
       RPMSendAsyncMessage: [
         "torconnect:open-tor-preferences",
         "torconnect:begin-bootstrap",
+        "torconnect:begin-autobootstrap",
         "torconnect:cancel-bootstrap",
         "torconnect:set-quickstart",
+        "torconnect:view-tor-logs",
+        "torconnect:restart",
       ],
       RPMSendQuery: [
         "torconnect:get-init-args",
-        "torconnect:copy-tor-logs",
+        "torconnect:get-country-codes",
       ],
     },
   },

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the tor-commits mailing list