morgan pushed to branch tor-browser-153.1.0esr-16.0-1 at The Tor Project / Applications / Tor Browser
Commits:
-
6a6cb5e8
by Henry Wilkes at 2026-08-26T12:08:36+00:00
-
9250f769
by Henry Wilkes at 2026-08-26T12:08:36+00:00
-
fe4fe5a2
by Henry Wilkes at 2026-08-26T12:08:36+00:00
-
747ca252
by Henry Wilkes at 2026-08-26T12:08:36+00:00
-
3cb7efed
by Henry Wilkes at 2026-08-26T12:08:36+00:00
-
f54fe85b
by Henry Wilkes at 2026-08-26T12:08:36+00:00
4 changed files:
- browser/components/torconnect/content/aboutTorConnect.css
- browser/components/torconnect/content/aboutTorConnect.html
- browser/components/torconnect/content/aboutTorConnect.js
- toolkit/modules/TorConnect.sys.mjs
Changes:
| ... | ... | @@ -25,42 +25,59 @@ body { |
| 25 | 25 | #breadcrumbs {
|
| 26 | 26 | grid-area: breadcrumbs;
|
| 27 | 27 | display: flex;
|
| 28 | - align-items: center;
|
|
| 28 | + gap: var(--space-large);
|
|
| 29 | 29 | margin: 0 0 var(--space-xlarge) 0;
|
| 30 | -}
|
|
| 31 | 30 | |
| 32 | -#breadcrumbs.hidden {
|
|
| 33 | - visibility: hidden;
|
|
| 31 | + &.hide-breadcrumbs {
|
|
| 32 | + visibility: hidden;
|
|
| 33 | + }
|
|
| 34 | + |
|
| 35 | + & :is(ol, li:not([hidden])) {
|
|
| 36 | + display: contents;
|
|
| 37 | + }
|
|
| 34 | 38 | }
|
| 35 | 39 | |
| 36 | 40 | .breadcrumb-item,
|
| 37 | 41 | .breadcrumb-separator {
|
| 38 | - display: flex;
|
|
| 39 | - margin: 0;
|
|
| 40 | - margin-inline-start: var(--space-large);
|
|
| 42 | + flex: 0 0 auto;
|
|
| 41 | 43 | padding: var(--space-small);
|
| 42 | 44 | }
|
| 43 | 45 | |
| 44 | 46 | .breadcrumb-item {
|
| 47 | + display: flex;
|
|
| 48 | + gap: var(--space-xsmall);
|
|
| 45 | 49 | align-items: center;
|
| 46 | 50 | cursor: pointer;
|
| 47 | 51 | color: var(--text-color);
|
| 48 | 52 | border-radius: var(--border-radius-small);
|
| 49 | -}
|
|
| 50 | 53 | |
| 51 | -.breadcrumb-item:hover {
|
|
| 52 | - color: var(--color-accent-primary);
|
|
| 53 | - background-color: var(--button-background-color-hover);
|
|
| 54 | -}
|
|
| 54 | + & > * {
|
|
| 55 | + flex: 0 0 auto;
|
|
| 56 | + }
|
|
| 57 | + |
|
| 58 | + &:not(.breadcrumb-disabled):hover {
|
|
| 59 | + color: var(--color-accent-primary);
|
|
| 60 | + background-color: var(--button-background-color-hover);
|
|
| 61 | + }
|
|
| 62 | + |
|
| 63 | + &:not(.breadcrumb-disabled):active {
|
|
| 64 | + color: var(--color-accent-primary-active);
|
|
| 65 | + background-color: var(--button-background-color-active);
|
|
| 66 | + }
|
|
| 67 | + |
|
| 68 | + &.breadcrumb-active {
|
|
| 69 | + color: var(--color-accent-primary);
|
|
| 70 | + }
|
|
| 55 | 71 | |
| 56 | -.breadcrumb-item:active {
|
|
| 57 | - color: var(--color-accent-primary-active);
|
|
| 58 | - background-color: var(--button-background-color-active);
|
|
| 72 | + &.breadcrumb-disabled {
|
|
| 73 | + color: var(--text-color);
|
|
| 74 | + opacity: 0.4;
|
|
| 75 | + cursor: unset;
|
|
| 76 | + }
|
|
| 59 | 77 | }
|
| 60 | 78 | |
| 61 | 79 | .breadcrumb-separator {
|
| 62 | - width: var(--icon-size);
|
|
| 63 | - list-style-image: url("chrome://browser/content/torconnect/arrow-right.svg");
|
|
| 80 | + content: url("chrome://browser/content/torconnect/arrow-right.svg");
|
|
| 64 | 81 | }
|
| 65 | 82 | |
| 66 | 83 | .breadcrumb-separator:dir(rtl) {
|
| ... | ... | @@ -68,61 +85,35 @@ body { |
| 68 | 85 | }
|
| 69 | 86 | |
| 70 | 87 | .breadcrumb-icon {
|
| 71 | - display: inline list-item;
|
|
| 88 | + width: var(--icon-size);
|
|
| 72 | 89 | height: var(--icon-size);
|
| 73 | - list-style-position: inside;
|
|
| 74 | 90 | -moz-context-properties: fill, stroke;
|
| 75 | 91 | fill: currentColor;
|
| 76 | 92 | stroke: currentColor;
|
| 77 | 93 | }
|
| 78 | 94 | |
| 79 | -.breadcrumb-item.active {
|
|
| 80 | - color: var(--color-accent-primary);
|
|
| 81 | -}
|
|
| 82 | - |
|
| 83 | -.breadcrumb-item.disabled,
|
|
| 84 | -.breadcrumb-item.disabled:hover,
|
|
| 85 | -.breadcrumb-item.disabled:active {
|
|
| 86 | - color: var(--text-color);
|
|
| 87 | - opacity: 0.4;
|
|
| 88 | - cursor: default;
|
|
| 89 | -}
|
|
| 90 | - |
|
| 91 | -.breadcrumb-item.hidden,
|
|
| 92 | -.breadcrumb-separator.hidden {
|
|
| 93 | - display: none;
|
|
| 94 | -}
|
|
| 95 | - |
|
| 96 | 95 | #connect-to-tor {
|
| 97 | 96 | margin-inline-start: 0;
|
| 98 | 97 | }
|
| 99 | 98 | |
| 100 | 99 | #connect-to-tor-icon {
|
| 101 | - list-style-image: url("chrome://browser/content/torconnect/tor-connect.svg");
|
|
| 100 | + content: url("chrome://browser/content/torconnect/tor-connect.svg");
|
|
| 102 | 101 | }
|
| 103 | 102 | |
| 104 | 103 | #connection-assist-icon {
|
| 105 | - list-style-image: url("chrome://browser/content/torconnect/tor-connect-broken.svg");
|
|
| 106 | -}
|
|
| 107 | - |
|
| 108 | -#try-bridge {
|
|
| 109 | - cursor: default;
|
|
| 104 | + content: url("chrome://browser/content/torconnect/tor-connect-broken.svg");
|
|
| 110 | 105 | }
|
| 111 | 106 | |
| 112 | 107 | #try-bridge-icon {
|
| 113 | - list-style-image: url("chrome://browser/content/torconnect/bridge.svg");
|
|
| 108 | + content: url("chrome://browser/content/torconnect/bridge.svg");
|
|
| 114 | 109 | }
|
| 115 | 110 | |
| 116 | 111 | #locationDropdownLabel.error {
|
| 117 | 112 | color: var(--text-color-error);
|
| 118 | 113 | }
|
| 119 | 114 | |
| 120 | -form#locationDropdown {
|
|
| 115 | +#regions-select {
|
|
| 121 | 116 | width: 240px;
|
| 122 | -}
|
|
| 123 | - |
|
| 124 | -form#locationDropdown select {
|
|
| 125 | - width: 100%;
|
|
| 126 | 117 | margin: 0;
|
| 127 | 118 | font-weight: var(--font-weight-bold);
|
| 128 | 119 | }
|
| ... | ... | @@ -33,33 +33,43 @@ |
| 33 | 33 | ></script>
|
| 34 | 34 | </head>
|
| 35 | 35 | <body class="onion-pattern-background">
|
| 36 | - <div id="progressBar" hidden="hidden">
|
|
| 36 | + <div
|
|
| 37 | + id="progressBar"
|
|
| 38 | + role="progressbar"
|
|
| 39 | + aria-valuenow="0"
|
|
| 40 | + aria-valuemin="0"
|
|
| 41 | + aria-valuemax="100"
|
|
| 42 | + aria-labelledby="tor-connect-heading"
|
|
| 43 | + hidden="hidden"
|
|
| 44 | + >
|
|
| 37 | 45 | <div id="progressSolid"></div>
|
| 38 | 46 | <div id="progressBackground"></div>
|
| 39 | 47 | <div id="progressBarBackground"></div>
|
| 40 | 48 | </div>
|
| 41 | - <div id="breadcrumbs" class="hidden">
|
|
| 42 | - <span id="connect-to-tor" class="breadcrumb-item">
|
|
| 43 | - <span id="connect-to-tor-icon" class="breadcrumb-icon"></span>
|
|
| 44 | - <span class="breadcrumb-label"></span>
|
|
| 45 | - </span>
|
|
| 46 | - <span
|
|
| 47 | - id="connection-assist-separator"
|
|
| 48 | - class="breadcrumb-separator breadcrumb-icon"
|
|
| 49 | - ></span>
|
|
| 50 | - <span id="connection-assist" class="breadcrumb-item">
|
|
| 51 | - <span id="connection-assist-icon" class="breadcrumb-icon"></span>
|
|
| 52 | - <span class="breadcrumb-label"></span>
|
|
| 53 | - </span>
|
|
| 54 | - <span
|
|
| 55 | - id="try-bridge-separator"
|
|
| 56 | - class="breadcrumb-separator breadcrumb-icon"
|
|
| 57 | - ></span>
|
|
| 58 | - <span id="try-bridge" class="breadcrumb-item">
|
|
| 59 | - <span id="try-bridge-icon" class="breadcrumb-icon"></span>
|
|
| 60 | - <span class="breadcrumb-label"></span>
|
|
| 61 | - </span>
|
|
| 62 | - </div>
|
|
| 49 | + <nav id="breadcrumbs">
|
|
| 50 | + <ol>
|
|
| 51 | + <li id="connect-to-tor">
|
|
| 52 | + <div class="breadcrumb-item" role="link" tabindex="0">
|
|
| 53 | + <span id="connect-to-tor-icon" class="breadcrumb-icon"></span>
|
|
| 54 | + <span class="breadcrumb-label"></span>
|
|
| 55 | + </div>
|
|
| 56 | + </li>
|
|
| 57 | + <li id="connection-assist">
|
|
| 58 | + <div class="breadcrumb-separator breadcrumb-icon"></div>
|
|
| 59 | + <div class="breadcrumb-item" role="link" tabindex="0">
|
|
| 60 | + <span id="connection-assist-icon" class="breadcrumb-icon"></span>
|
|
| 61 | + <span class="breadcrumb-label"></span>
|
|
| 62 | + </div>
|
|
| 63 | + </li>
|
|
| 64 | + <li id="try-bridge">
|
|
| 65 | + <div class="breadcrumb-separator breadcrumb-icon"></div>
|
|
| 66 | + <div class="breadcrumb-item" role="link" tabindex="0">
|
|
| 67 | + <span id="try-bridge-icon" class="breadcrumb-icon"></span>
|
|
| 68 | + <span class="breadcrumb-label"></span>
|
|
| 69 | + </div>
|
|
| 70 | + </li>
|
|
| 71 | + </ol>
|
|
| 72 | + </nav>
|
|
| 63 | 73 | <div class="torconnect-stage-content" data-stage-name="ProviderStopped">
|
| 64 | 74 | <img class="torconnect-icon assist-icon" alt="" />
|
| 65 | 75 | <h1
|
| ... | ... | @@ -166,14 +176,12 @@ |
| 166 | 176 | <button id="configureButton" hidden="true"></button>
|
| 167 | 177 | <button id="cancelButton" hidden="true"></button>
|
| 168 | 178 | <button id="connectButton" hidden="true" class="tor-button"></button>
|
| 169 | - <label id="locationDropdownLabel" for="">"countries"></label>
|
|
| 170 | - <form id="locationDropdown" hidden="true">
|
|
| 171 | - <select id="regions-select">
|
|
| 172 | - <option id="first-region-option"></option>
|
|
| 173 | - <optgroup id="frequent-regions-option-group"></optgroup>
|
|
| 174 | - <optgroup id="full-regions-option-group"></optgroup>
|
|
| 175 | - </select>
|
|
| 176 | - </form>
|
|
| 179 | + <label id="locationDropdownLabel" for="">"regions-select"></label>
|
|
| 180 | + <select id="regions-select" hidden="hidden">
|
|
| 181 | + <option id="first-region-option"></option>
|
|
| 182 | + <optgroup id="frequent-regions-option-group"></optgroup>
|
|
| 183 | + <optgroup id="full-regions-option-group"></optgroup>
|
|
| 184 | + </select>
|
|
| 177 | 185 | <button id="tryBridgeButton" hidden="true" class="tor-button"></button>
|
| 178 | 186 | </div>
|
| 179 | 187 | </div>
|
| ... | ... | @@ -42,12 +42,10 @@ class AboutTorConnect { |
| 42 | 42 | label: "#connect-to-tor .breadcrumb-label",
|
| 43 | 43 | },
|
| 44 | 44 | connectionAssist: {
|
| 45 | - separator: "#connection-assist-separator",
|
|
| 46 | 45 | link: "#connection-assist",
|
| 47 | 46 | label: "#connection-assist .breadcrumb-label",
|
| 48 | 47 | },
|
| 49 | 48 | tryBridge: {
|
| 50 | - separator: "#try-bridge-separator",
|
|
| 51 | 49 | link: "#try-bridge",
|
| 52 | 50 | label: "#try-bridge .breadcrumb-label",
|
| 53 | 51 | },
|
| ... | ... | @@ -66,7 +64,6 @@ class AboutTorConnect { |
| 66 | 64 | connect: "button#connectButton",
|
| 67 | 65 | tryBridge: "button#tryBridgeButton",
|
| 68 | 66 | locationDropdownLabel: "#locationDropdownLabel",
|
| 69 | - locationDropdown: "form#locationDropdown",
|
|
| 70 | 67 | locationDropdownSelect: "#regions-select",
|
| 71 | 68 | },
|
| 72 | 69 | });
|
| ... | ... | @@ -90,18 +87,12 @@ class AboutTorConnect { |
| 90 | 87 | connectToTorLabel: document.querySelector(
|
| 91 | 88 | this.selectors.breadcrumbs.connectToTor.label
|
| 92 | 89 | ),
|
| 93 | - connectionAssistSeparator: document.querySelector(
|
|
| 94 | - this.selectors.breadcrumbs.connectionAssist.separator
|
|
| 95 | - ),
|
|
| 96 | 90 | connectionAssistLink: document.querySelector(
|
| 97 | 91 | this.selectors.breadcrumbs.connectionAssist.link
|
| 98 | 92 | ),
|
| 99 | 93 | connectionAssistLabel: document.querySelector(
|
| 100 | 94 | this.selectors.breadcrumbs.connectionAssist.label
|
| 101 | 95 | ),
|
| 102 | - tryBridgeSeparator: document.querySelector(
|
|
| 103 | - this.selectors.breadcrumbs.tryBridge.separator
|
|
| 104 | - ),
|
|
| 105 | 96 | tryBridgeLink: document.querySelector(
|
| 106 | 97 | this.selectors.breadcrumbs.tryBridge.link
|
| 107 | 98 | ),
|
| ... | ... | @@ -120,9 +111,6 @@ class AboutTorConnect { |
| 120 | 111 | locationDropdownLabel: document.querySelector(
|
| 121 | 112 | this.selectors.buttons.locationDropdownLabel
|
| 122 | 113 | ),
|
| 123 | - locationDropdown: document.querySelector(
|
|
| 124 | - this.selectors.buttons.locationDropdown
|
|
| 125 | - ),
|
|
| 126 | 114 | locationDropdownSelect: document.querySelector(
|
| 127 | 115 | this.selectors.buttons.locationDropdownSelect
|
| 128 | 116 | ),
|
| ... | ... | @@ -221,7 +209,7 @@ class AboutTorConnect { |
| 221 | 209 | this.hide(this.elements.cancelButton);
|
| 222 | 210 | this.hide(this.elements.connectButton);
|
| 223 | 211 | this.hide(this.elements.locationDropdownLabel);
|
| 224 | - this.hide(this.elements.locationDropdown);
|
|
| 212 | + this.hide(this.elements.locationDropdownSelect);
|
|
| 225 | 213 | this.hide(this.elements.tryBridgeButton);
|
| 226 | 214 | }
|
| 227 | 215 | |
| ... | ... | @@ -239,33 +227,49 @@ class AboutTorConnect { |
| 239 | 227 | }
|
| 240 | 228 | |
| 241 | 229 | setBreadcrumbsStatus(connectToTor, connectionAssist, tryBridge) {
|
| 242 | - this.elements.breadcrumbContainer.classList.remove("hidden");
|
|
| 243 | - const elems = [
|
|
| 244 | - [this.elements.connectToTorLink, connectToTor, null],
|
|
| 245 | - [
|
|
| 246 | - this.elements.connectionAssistLink,
|
|
| 247 | - connectionAssist,
|
|
| 248 | - this.elements.connectionAssistSeparator,
|
|
| 249 | - ],
|
|
| 250 | - [
|
|
| 251 | - this.elements.tryBridgeLink,
|
|
| 252 | - tryBridge,
|
|
| 253 | - this.elements.tryBridgeSeparator,
|
|
| 254 | - ],
|
|
| 255 | - ];
|
|
| 256 | - elems.forEach(([elem, status, separator]) => {
|
|
| 257 | - elem.classList.remove(BreadcrumbStatus.Hidden);
|
|
| 258 | - elem.classList.remove(BreadcrumbStatus.Disabled);
|
|
| 259 | - elem.classList.remove(BreadcrumbStatus.Active);
|
|
| 260 | - if (status !== "") {
|
|
| 261 | - elem.classList.add(status);
|
|
| 230 | + this.elements.breadcrumbContainer.classList.remove("hide-breadcrumbs");
|
|
| 231 | + for (const { listEl, status } of [
|
|
| 232 | + { listEl: this.elements.connectToTorLink, status: connectToTor },
|
|
| 233 | + { listEl: this.elements.connectionAssistLink, status: connectionAssist },
|
|
| 234 | + { listEl: this.elements.tryBridgeLink, status: tryBridge },
|
|
| 235 | + ]) {
|
|
| 236 | + let hidden = false;
|
|
| 237 | + let disabled = false;
|
|
| 238 | + let active = false;
|
|
| 239 | + switch (status) {
|
|
| 240 | + case BreadcrumbStatus.Hidden:
|
|
| 241 | + hidden = true;
|
|
| 242 | + break;
|
|
| 243 | + case BreadcrumbStatus.Disabled:
|
|
| 244 | + disabled = true;
|
|
| 245 | + break;
|
|
| 246 | + case BreadcrumbStatus.Active:
|
|
| 247 | + active = true;
|
|
| 248 | + break;
|
|
| 262 | 249 | }
|
| 263 | - separator?.classList.toggle("hidden", status === BreadcrumbStatus.Hidden);
|
|
| 264 | - });
|
|
| 250 | + // NOTE: Hiding or disabling elements can cause them to loose focus.
|
|
| 251 | + // However, with each stage change the focus should already be moved.
|
|
| 252 | + listEl.hidden = hidden;
|
|
| 253 | + const linkEl = listEl.querySelector(".breadcrumb-item");
|
|
| 254 | + linkEl.classList.toggle("breadcrumb-disabled", disabled);
|
|
| 255 | + linkEl.classList.toggle("breadcrumb-active", active);
|
|
| 256 | + if (disabled) {
|
|
| 257 | + linkEl.removeAttribute("tabindex");
|
|
| 258 | + linkEl.setAttribute("aria-disabled", "true");
|
|
| 259 | + } else {
|
|
| 260 | + linkEl.setAttribute("tabindex", "0");
|
|
| 261 | + linkEl.removeAttribute("aria-disabled", "true");
|
|
| 262 | + }
|
|
| 263 | + if (active) {
|
|
| 264 | + linkEl.setAttribute("aria-current", "step");
|
|
| 265 | + } else {
|
|
| 266 | + linkEl.removeAttribute("aria-current");
|
|
| 267 | + }
|
|
| 268 | + }
|
|
| 265 | 269 | }
|
| 266 | 270 | |
| 267 | 271 | hideBreadcrumbs() {
|
| 268 | - this.elements.breadcrumbContainer.classList.add("hidden");
|
|
| 272 | + this.elements.breadcrumbContainer.classList.add("hide-breadcrumbs");
|
|
| 269 | 273 | }
|
| 270 | 274 | |
| 271 | 275 | getLocalizedStatus(status) {
|
| ... | ... | @@ -432,7 +436,11 @@ class AboutTorConnect { |
| 432 | 436 | break;
|
| 433 | 437 | case "Bootstrapping":
|
| 434 | 438 | showProgress = true;
|
| 435 | - this.showBootstrapping(stage.bootstrapTrigger, stage.tryAgain);
|
|
| 439 | + this.showBootstrapping(
|
|
| 440 | + stage.bootstrapTrigger,
|
|
| 441 | + stage.tryAgain,
|
|
| 442 | + stage.potentiallyBlocked
|
|
| 443 | + );
|
|
| 436 | 444 | // Always focus the cancel button.
|
| 437 | 445 | moveFocus = this.elements.cancelButton;
|
| 438 | 446 | break;
|
| ... | ... | @@ -637,10 +645,13 @@ class AboutTorConnect { |
| 637 | 645 | }
|
| 638 | 646 | |
| 639 | 647 | updateBootstrappingStatus(data) {
|
| 648 | + let { progress } = data;
|
|
| 649 | + progress = Math.max(0, Math.min(progress, 100));
|
|
| 640 | 650 | this.elements.progressMeter.style.setProperty(
|
| 641 | 651 | "--progress-percent",
|
| 642 | - `${data.progress}%`
|
|
| 652 | + `${progress}%`
|
|
| 643 | 653 | );
|
| 654 | + this.elements.progressMeter.setAttribute("aria-valuenow", String(progress));
|
|
| 644 | 655 | if (this.shownStage === "Bootstrapping" && data.hasWarning) {
|
| 645 | 656 | // When bootstrapping starts, we hide the log button, but we re-show it if
|
| 646 | 657 | // we get a warning.
|
| ... | ... | @@ -676,16 +687,18 @@ class AboutTorConnect { |
| 676 | 687 | BreadcrumbStatus.Default,
|
| 677 | 688 | BreadcrumbStatus.Disabled
|
| 678 | 689 | );
|
| 690 | + } else {
|
|
| 691 | + this.hideBreadcrumbs();
|
|
| 679 | 692 | }
|
| 680 | 693 | }
|
| 681 | 694 | |
| 682 | - showBootstrapping(trigger, tryAgain) {
|
|
| 695 | + showBootstrapping(trigger, tryAgain, potentiallyBlocked) {
|
|
| 683 | 696 | let title = "";
|
| 684 | 697 | let description = "";
|
| 685 | 698 | const breadcrumbs = [
|
| 686 | - BreadcrumbStatus.Disabled,
|
|
| 687 | - BreadcrumbStatus.Disabled,
|
|
| 688 | - BreadcrumbStatus.Disabled,
|
|
| 699 | + BreadcrumbStatus.Default,
|
|
| 700 | + potentiallyBlocked ? BreadcrumbStatus.Default : BreadcrumbStatus.Hidden,
|
|
| 701 | + potentiallyBlocked ? BreadcrumbStatus.Disabled : BreadcrumbStatus.Hidden,
|
|
| 689 | 702 | ];
|
| 690 | 703 | switch (trigger) {
|
| 691 | 704 | case "Start":
|
| ... | ... | @@ -718,7 +731,7 @@ class AboutTorConnect { |
| 718 | 731 | this.setTitle(title, "");
|
| 719 | 732 | this.showConfigureConnectionLink(description);
|
| 720 | 733 | this.elements.progressDescription.textContent = "";
|
| 721 | - if (tryAgain) {
|
|
| 734 | + if (tryAgain || potentiallyBlocked) {
|
|
| 722 | 735 | this.setBreadcrumbsStatus(...breadcrumbs);
|
| 723 | 736 | } else {
|
| 724 | 737 | this.hideBreadcrumbs();
|
| ... | ... | @@ -780,8 +793,8 @@ class AboutTorConnect { |
| 780 | 793 | this.getMaybeLocalizedError(error);
|
| 781 | 794 | this.setBreadcrumbsStatus(
|
| 782 | 795 | BreadcrumbStatus.Default,
|
| 783 | - BreadcrumbStatus.Default,
|
|
| 784 | - BreadcrumbStatus.Active
|
|
| 796 | + BreadcrumbStatus.Active,
|
|
| 797 | + BreadcrumbStatus.Disabled
|
|
| 785 | 798 | );
|
| 786 | 799 | this.showLocationForm(false, TorStrings.torConnect.tryAgain);
|
| 787 | 800 | }
|
| ... | ... | @@ -805,7 +818,8 @@ class AboutTorConnect { |
| 805 | 818 | const pieces = text.split("%S");
|
| 806 | 819 | const link = document.createElement("a");
|
| 807 | 820 | link.textContent = TorStrings.torConnect.configureConnection;
|
| 808 | - link.setAttribute("href", "#");
|
|
| 821 | + link.setAttribute("href", "about:preferences#connection");
|
|
| 822 | + link.setAttribute("target", "_blank");
|
|
| 809 | 823 | link.addEventListener("click", e => {
|
| 810 | 824 | e.preventDefault();
|
| 811 | 825 | RPMSendAsyncMessage("torconnect:open-tor-preferences");
|
| ... | ... | @@ -918,7 +932,7 @@ class AboutTorConnect { |
| 918 | 932 | this.populateDelayedRegionOptions();
|
| 919 | 933 | |
| 920 | 934 | this.show(this.elements.locationDropdownLabel);
|
| 921 | - this.show(this.elements.locationDropdown);
|
|
| 935 | + this.show(this.elements.locationDropdownSelect);
|
|
| 922 | 936 | this.elements.locationDropdownLabel.classList.toggle("error", !isChoose);
|
| 923 | 937 | this.show(this.elements.tryBridgeButton, true);
|
| 924 | 938 | if (buttonLabel !== undefined) {
|
| ... | ... | @@ -929,24 +943,38 @@ class AboutTorConnect { |
| 929 | 943 | initElements(direction) {
|
| 930 | 944 | document.documentElement.setAttribute("dir", direction);
|
| 931 | 945 | |
| 932 | - this.elements.connectToTorLink.addEventListener("click", () => {
|
|
| 933 | - RPMSendAsyncMessage("torconnect:start-again");
|
|
| 934 | - });
|
|
| 946 | + for (const [linkEl, command] of [
|
|
| 947 | + [
|
|
| 948 | + this.elements.connectToTorLink,
|
|
| 949 | + () => {
|
|
| 950 | + RPMSendAsyncMessage("torconnect:start-again");
|
|
| 951 | + },
|
|
| 952 | + ],
|
|
| 953 | + [
|
|
| 954 | + this.elements.connectionAssistLink,
|
|
| 955 | + () => {
|
|
| 956 | + RPMSendAsyncMessage("torconnect:choose-region");
|
|
| 957 | + },
|
|
| 958 | + ],
|
|
| 959 | + // Final link does nothing.
|
|
| 960 | + ]) {
|
|
| 961 | + // NOTE: These link should never be both visible and disabled.
|
|
| 962 | + // NOTE: We allow re-selecting this link when it is already the
|
|
| 963 | + // "aria-current" item, to allow the user to reset to the beginning of
|
|
| 964 | + // Connection Assist.
|
|
| 965 | + linkEl.addEventListener("click", () => {
|
|
| 966 | + command();
|
|
| 967 | + });
|
|
| 968 | + linkEl.addEventListener("keydown", event => {
|
|
| 969 | + if (event.key !== "Enter") {
|
|
| 970 | + return;
|
|
| 971 | + }
|
|
| 972 | + command();
|
|
| 973 | + });
|
|
| 974 | + }
|
|
| 975 | + |
|
| 935 | 976 | this.elements.connectToTorLabel.textContent =
|
| 936 | 977 | TorStrings.torConnect.torConnect;
|
| 937 | - this.elements.connectionAssistLink.addEventListener("click", () => {
|
|
| 938 | - if (
|
|
| 939 | - this.elements.connectionAssistLink.classList.contains(
|
|
| 940 | - BreadcrumbStatus.Active
|
|
| 941 | - ) ||
|
|
| 942 | - this.elements.connectionAssistLink.classList.contains(
|
|
| 943 | - BreadcrumbStatus.Disabled
|
|
| 944 | - )
|
|
| 945 | - ) {
|
|
| 946 | - return;
|
|
| 947 | - }
|
|
| 948 | - RPMSendAsyncMessage("torconnect:choose-region");
|
|
| 949 | - });
|
|
| 950 | 978 | this.elements.connectionAssistLabel.textContent =
|
| 951 | 979 | TorStrings.torConnect.breadcrumbAssist;
|
| 952 | 980 | this.elements.tryBridgeLabel.textContent =
|
| ... | ... | @@ -1073,7 +1101,7 @@ class AboutTorConnect { |
| 1073 | 1101 | RPMAddMessageListener("torconnect:region-names-change", () => {
|
| 1074 | 1102 | // Reset the regions list.
|
| 1075 | 1103 | this.regions = null;
|
| 1076 | - if (!this.elements.locationDropdown.hidden) {
|
|
| 1104 | + if (!this.elements.locationDropdownSelect.hidden) {
|
|
| 1077 | 1105 | // Re-populate immediately.
|
| 1078 | 1106 | this.populateDelayedRegionOptions();
|
| 1079 | 1107 | }
|
| ... | ... | @@ -2,8 +2,6 @@ |
| 2 | 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this
|
| 3 | 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
| 4 | 4 | |
| 5 | -import { setTimeout, clearTimeout } from "resource://gre/modules/Timer.sys.mjs";
|
|
| 6 | - |
|
| 7 | 5 | const lazy = {};
|
| 8 | 6 | |
| 9 | 7 | ChromeUtils.defineESModuleGetters(lazy, {
|
| ... | ... | @@ -24,6 +22,10 @@ ChromeUtils.defineESModuleGetters(lazy, { |
| 24 | 22 | "moz-src:///toolkit/components/tor-launcher/TorLauncherUtil.sys.mjs",
|
| 25 | 23 | TorSettings: "moz-src:///toolkit/modules/TorSettings.sys.mjs",
|
| 26 | 24 | TorSettingsTopics: "moz-src:///toolkit/modules/TorSettings.sys.mjs",
|
| 25 | + clearInterval: "resource://gre/modules/Timer.sys.mjs",
|
|
| 26 | + clearTimeout: "resource://gre/modules/Timer.sys.mjs",
|
|
| 27 | + setInterval: "resource://gre/modules/Timer.sys.mjs",
|
|
| 28 | + setTimeout: "resource://gre/modules/Timer.sys.mjs",
|
|
| 27 | 29 | });
|
| 28 | 30 | |
| 29 | 31 | ChromeUtils.defineLazyGetter(lazy, "NetworkLinkService", () => {
|
| ... | ... | @@ -122,12 +124,17 @@ export const TorConnectTopics = Object.freeze({ |
| 122 | 124 | * bootstrap.
|
| 123 | 125 | * @property {integer} [simulateDelay] - The delay in microseconds to apply to
|
| 124 | 126 | * simulated bootstraps.
|
| 127 | + * @property {number} [simulateProgress] - The progress percent to reach before
|
|
| 128 | + * the simulated bootstrap fails.
|
|
| 125 | 129 | * @property {MoatSettings} [simulateMoatResponse] - Simulate a Moat response
|
| 126 | 130 | * for circumvention settings. Should include a "bridgesList" property, and
|
| 127 | 131 | * optionally a "country" property. The "bridgesList" property should be an
|
| 128 | 132 | * Array of MoatBridges objects that match the bridge settings accepted by
|
| 129 | - * TorSettings.bridges, plus you may add a "simulateCensorship" property to
|
|
| 130 | - * make only their bootstrap attempts fail.
|
|
| 133 | + * TorSettings.bridges. For each MoatBridges object, you may also add a
|
|
| 134 | + * "simulateCensorship" property, with an optional "simulateProgress"
|
|
| 135 | + * property, to make the bootstrap attempt fail for this set of bridges
|
|
| 136 | + * (whilst still allowing it to pass for other sets of bridges in the
|
|
| 137 | + * simulated Moat response).
|
|
| 131 | 138 | * @property {string} [regionCode] - The region code to use to fetch
|
| 132 | 139 | * auto-bootstrap settings, or "automatic" to automatically choose the region.
|
| 133 | 140 | */
|
| ... | ... | @@ -232,7 +239,7 @@ class BootstrapAttempt { |
| 232 | 239 | this.#bootstrap = {
|
| 233 | 240 | _timeout: 0,
|
| 234 | 241 | bootstrap() {
|
| 235 | - this._timeout = setTimeout(() => {
|
|
| 242 | + this._timeout = lazy.setTimeout(() => {
|
|
| 236 | 243 | const err = new lazy.TorBootstrapError({
|
| 237 | 244 | summary: "Censorship simulation",
|
| 238 | 245 | phase: "conn",
|
| ... | ... | @@ -240,9 +247,32 @@ class BootstrapAttempt { |
| 240 | 247 | });
|
| 241 | 248 | this.onbootstraperror(err);
|
| 242 | 249 | }, options.simulateDelay || 0);
|
| 250 | + this.onbootstrapstatus(0);
|
|
| 251 | + if (options.simulateDelay && options.simulateProgress) {
|
|
| 252 | + let progress = 0;
|
|
| 253 | + const step = 5;
|
|
| 254 | + const delay = Math.floor(
|
|
| 255 | + // We want the last step to last for 10 delay periods to emulate a
|
|
| 256 | + // short "hang" at this stage.
|
|
| 257 | + (step * options.simulateDelay) /
|
|
| 258 | + (options.simulateProgress + 10 * step)
|
|
| 259 | + );
|
|
| 260 | + this._progress = lazy.setInterval(() => {
|
|
| 261 | + progress += step;
|
|
| 262 | + if (progress >= options.simulateProgress) {
|
|
| 263 | + progress = options.simulateProgress;
|
|
| 264 | + lazy.clearInterval(this._progress);
|
|
| 265 | + delete this._progress;
|
|
| 266 | + }
|
|
| 267 | + this.onbootstrapstatus(progress);
|
|
| 268 | + }, delay);
|
|
| 269 | + }
|
|
| 243 | 270 | },
|
| 244 | 271 | cancel() {
|
| 245 | - clearTimeout(this._timeout);
|
|
| 272 | + lazy.clearTimeout(this._timeout);
|
|
| 273 | + if (this._progress) {
|
|
| 274 | + lazy.clearInterval(this._progress);
|
|
| 275 | + }
|
|
| 246 | 276 | },
|
| 247 | 277 | };
|
| 248 | 278 | } else {
|
| ... | ... | @@ -458,6 +488,7 @@ class AutoBootstrapAttempt { |
| 458 | 488 | : TorConnectError.NoSettingsForCountry
|
| 459 | 489 | ),
|
| 460 | 490 | });
|
| 491 | + return;
|
|
| 461 | 492 | }
|
| 462 | 493 | |
| 463 | 494 | // Apply each of our settings and try to bootstrap with each.
|
| ... | ... | @@ -489,7 +520,7 @@ class AutoBootstrapAttempt { |
| 489 | 520 | async #fetchBridges(options) {
|
| 490 | 521 | if (options.simulateMoatResponse) {
|
| 491 | 522 | await Promise.race([
|
| 492 | - new Promise(res => setTimeout(res, options.simulateDelay || 0)),
|
|
| 523 | + new Promise(res => lazy.setTimeout(res, options.simulateDelay || 0)),
|
|
| 493 | 524 | this.#cancelledPromise,
|
| 494 | 525 | ]);
|
| 495 | 526 | |
| ... | ... | @@ -588,8 +619,13 @@ class AutoBootstrapAttempt { |
| 588 | 619 | // Move the simulateCensorship option to the options for the next
|
| 589 | 620 | // BootstrapAttempt.
|
| 590 | 621 | bridges = structuredClone(bridges);
|
| 622 | + const simulateProgress = bridges.simulateProgress;
|
|
| 591 | 623 | delete bridges.simulateCensorship;
|
| 624 | + delete bridges.simulateProgress;
|
|
| 592 | 625 | options = { ...options, simulateCensorship: true };
|
| 626 | + if (simulateProgress) {
|
|
| 627 | + options.simulateProgress = simulateProgress;
|
|
| 628 | + }
|
|
| 593 | 629 | }
|
| 594 | 630 | |
| 595 | 631 | // Send the new settings directly to the provider. We will save them only
|
| ... | ... | @@ -1271,6 +1307,10 @@ export const TorConnect = { |
| 1271 | 1307 | bootstrapOptions.simulateDelay =
|
| 1272 | 1308 | this.simulateBootstrapOptions.simulateDelay;
|
| 1273 | 1309 | }
|
| 1310 | + if (this.simulateBootstrapOptions.simulateProgress) {
|
|
| 1311 | + bootstrapOptions.simulateProgress =
|
|
| 1312 | + this.simulateBootstrapOptions.simulateProgress;
|
|
| 1313 | + }
|
|
| 1274 | 1314 | if (this.simulateBootstrapOptions.simulateMoatResponse) {
|
| 1275 | 1315 | bootstrapOptions.simulateMoatResponse =
|
| 1276 | 1316 | this.simulateBootstrapOptions.simulateMoatResponse;
|
| ... | ... | @@ -1280,8 +1320,9 @@ export const TorConnect = { |
| 1280 | 1320 | TorConnectPrefs.censorship_level,
|
| 1281 | 1321 | 0
|
| 1282 | 1322 | );
|
| 1283 | - if (censorshipLevel > 0 && !bootstrapOptions.simulateDelay) {
|
|
| 1284 | - bootstrapOptions.simulateDelay = 1500;
|
|
| 1323 | + if (censorshipLevel > 0) {
|
|
| 1324 | + bootstrapOptions.simulateDelay ||= 1500;
|
|
| 1325 | + bootstrapOptions.simulateProgress ||= 20;
|
|
| 1285 | 1326 | }
|
| 1286 | 1327 | if (censorshipLevel === 1) {
|
| 1287 | 1328 | // Bootstrap fails, but auto-bootstrap does not.
|