| ... |
... |
@@ -32,7 +32,6 @@ class AboutTorConnect { |
|
32
|
32
|
selectors = Object.freeze({
|
|
33
|
33
|
textContainer: {
|
|
34
|
34
|
title: "div.title",
|
|
35
|
|
- titleText: "h1.title-text",
|
|
36
|
35
|
longContentText: "#connectLongContentText",
|
|
37
|
36
|
},
|
|
38
|
37
|
progress: {
|
| ... |
... |
@@ -77,7 +76,7 @@ class AboutTorConnect { |
|
77
|
76
|
|
|
78
|
77
|
elements = Object.freeze({
|
|
79
|
78
|
title: document.querySelector(this.selectors.textContainer.title),
|
|
80
|
|
- titleText: document.querySelector(this.selectors.textContainer.titleText),
|
|
|
79
|
+ heading: document.getElementById("tor-connect-heading"),
|
|
81
|
80
|
longContentText: document.querySelector(
|
|
82
|
81
|
this.selectors.textContainer.longContentText
|
|
83
|
82
|
),
|
| ... |
... |
@@ -138,18 +137,44 @@ class AboutTorConnect { |
|
138
|
137
|
|
|
139
|
138
|
locations = {};
|
|
140
|
139
|
|
|
141
|
|
- beginBootstrapping() {
|
|
142
|
|
- RPMSendAsyncMessage("torconnect:begin-bootstrapping", {});
|
|
|
140
|
+ /**
|
|
|
141
|
+ * Whether the user requested a cancellation of the bootstrap from *this*
|
|
|
142
|
+ * page.
|
|
|
143
|
+ *
|
|
|
144
|
+ * @type {boolean}
|
|
|
145
|
+ */
|
|
|
146
|
+ userCancelled = false;
|
|
|
147
|
+
|
|
|
148
|
+ /**
|
|
|
149
|
+ * Start a normal bootstrap attempt.
|
|
|
150
|
+ *
|
|
|
151
|
+ * @param {boolean} userClickedConnect - Whether this request was triggered by
|
|
|
152
|
+ * the user clicking the "Connect" button on the "Start" page.
|
|
|
153
|
+ */
|
|
|
154
|
+ beginBootstrapping(userClickedConnect) {
|
|
|
155
|
+ RPMSendAsyncMessage("torconnect:begin-bootstrapping", {
|
|
|
156
|
+ userClickedConnect,
|
|
|
157
|
+ });
|
|
143
|
158
|
}
|
|
144
|
159
|
|
|
|
160
|
+ /**
|
|
|
161
|
+ * Start an auto bootstrap attempt.
|
|
|
162
|
+ *
|
|
|
163
|
+ * @param {string} regionCode - The region code to use for the bootstrap, or
|
|
|
164
|
+ * "automatic".
|
|
|
165
|
+ */
|
|
145
|
166
|
beginAutoBootstrapping(regionCode) {
|
|
146
|
167
|
RPMSendAsyncMessage("torconnect:begin-bootstrapping", {
|
|
147
|
168
|
regionCode,
|
|
148
|
169
|
});
|
|
149
|
170
|
}
|
|
150
|
171
|
|
|
|
172
|
+ /**
|
|
|
173
|
+ * Try and cancel the current bootstrap attempt.
|
|
|
174
|
+ */
|
|
151
|
175
|
cancelBootstrapping() {
|
|
152
|
176
|
RPMSendAsyncMessage("torconnect:cancel-bootstrapping");
|
|
|
177
|
+ this.userCancelled = true;
|
|
153
|
178
|
}
|
|
154
|
179
|
|
|
155
|
180
|
/*
|
| ... |
... |
@@ -260,7 +285,7 @@ class AboutTorConnect { |
|
260
|
285
|
}
|
|
261
|
286
|
|
|
262
|
287
|
setTitle(title, className) {
|
|
263
|
|
- this.elements.titleText.textContent = title;
|
|
|
288
|
+ this.elements.heading.textContent = title;
|
|
264
|
289
|
this.elements.title.className = "title";
|
|
265
|
290
|
if (className) {
|
|
266
|
291
|
this.elements.title.classList.add(className);
|
| ... |
... |
@@ -349,18 +374,88 @@ class AboutTorConnect { |
|
349
|
374
|
}
|
|
350
|
375
|
}
|
|
351
|
376
|
|
|
|
377
|
+ /**
|
|
|
378
|
+ * The connect button that was focused just prior to a bootstrap attempt, if
|
|
|
379
|
+ * any.
|
|
|
380
|
+ *
|
|
|
381
|
+ * @type {?Element}
|
|
|
382
|
+ */
|
|
|
383
|
+ preBootstrappingFocus = null;
|
|
|
384
|
+
|
|
|
385
|
+ /**
|
|
|
386
|
+ * The stage that was shown on this page just prior to a bootstrap attempt.
|
|
|
387
|
+ *
|
|
|
388
|
+ * @type {?string}
|
|
|
389
|
+ */
|
|
|
390
|
+ preBootstrappingStage = null;
|
|
|
391
|
+
|
|
352
|
392
|
/*
|
|
353
|
393
|
These methods update the UI based on the current TorConnect state
|
|
354
|
394
|
*/
|
|
355
|
395
|
|
|
356
|
|
- updateStage(stage) {
|
|
|
396
|
+ /**
|
|
|
397
|
+ * Update the shown stage.
|
|
|
398
|
+ *
|
|
|
399
|
+ * @param {ConnectStage} stage - The new stage to show.
|
|
|
400
|
+ * @param {boolean} [focusConnect=false] - Whether to try and focus the
|
|
|
401
|
+ * connect button, if we are in the Start stage.
|
|
|
402
|
+ */
|
|
|
403
|
+ updateStage(stage, focusConnect = false) {
|
|
357
|
404
|
if (stage.name === this.shownStage) {
|
|
358
|
405
|
return;
|
|
359
|
406
|
}
|
|
360
|
407
|
|
|
|
408
|
+ const prevStage = this.shownStage;
|
|
361
|
409
|
this.shownStage = stage.name;
|
|
362
|
410
|
this.selectedLocation = stage.defaultRegion;
|
|
363
|
411
|
|
|
|
412
|
+ // By default we want to reset the focus to the top of the page when
|
|
|
413
|
+ // changing the displayed page since we want a user to read the new page
|
|
|
414
|
+ // before activating a control.
|
|
|
415
|
+ let moveFocus = this.elements.heading;
|
|
|
416
|
+
|
|
|
417
|
+ if (stage.name === "Bootstrapping") {
|
|
|
418
|
+ this.preBootstrappingStage = prevStage;
|
|
|
419
|
+ this.preBootstrappingFocus = null;
|
|
|
420
|
+ if (focusConnect && stage.isQuickstart) {
|
|
|
421
|
+ // If this is the initial automatic bootstrap triggered by the
|
|
|
422
|
+ // quickstart preference, treat as if the previous shown stage was
|
|
|
423
|
+ // "Start" and the user clicked the "Connect" button.
|
|
|
424
|
+ // Then, if the user cancels, the focus should still move to the
|
|
|
425
|
+ // "Connect" button.
|
|
|
426
|
+ this.preBootstrappingStage = "Start";
|
|
|
427
|
+ this.preBootstrappingFocus = this.elements.connectButton;
|
|
|
428
|
+ } else if (this.elements.connectButton.contains(document.activeElement)) {
|
|
|
429
|
+ this.preBootstrappingFocus = this.elements.connectButton;
|
|
|
430
|
+ } else if (
|
|
|
431
|
+ this.elements.tryBridgeButton.contains(document.activeElement)
|
|
|
432
|
+ ) {
|
|
|
433
|
+ this.preBootstrappingFocus = this.elements.tryBridgeButton;
|
|
|
434
|
+ }
|
|
|
435
|
+ } else {
|
|
|
436
|
+ if (
|
|
|
437
|
+ this.userCancelled &&
|
|
|
438
|
+ prevStage === "Bootstrapping" &&
|
|
|
439
|
+ stage.name === this.preBootstrappingStage &&
|
|
|
440
|
+ this.preBootstrappingFocus &&
|
|
|
441
|
+ this.elements.cancelButton.contains(document.activeElement)
|
|
|
442
|
+ ) {
|
|
|
443
|
+ // If returning back to the same stage after the user tried to cancel
|
|
|
444
|
+ // bootstrapping from within this page, then we restore the focus to the
|
|
|
445
|
+ // connect button to allow the user to quickly re-try.
|
|
|
446
|
+ // If the bootstrap was cancelled for any other reason, we reset the
|
|
|
447
|
+ // focus as usual.
|
|
|
448
|
+ moveFocus = this.preBootstrappingFocus;
|
|
|
449
|
+ }
|
|
|
450
|
+ // Clear the Bootstrapping variables.
|
|
|
451
|
+ this.preBootstrappingStage = null;
|
|
|
452
|
+ this.preBootstrappingFocus = null;
|
|
|
453
|
+ }
|
|
|
454
|
+
|
|
|
455
|
+ // Clear the recording of the cancellation request.
|
|
|
456
|
+ this.userCancelled = false;
|
|
|
457
|
+
|
|
|
458
|
+ let isLoaded = true;
|
|
364
|
459
|
let showProgress = false;
|
|
365
|
460
|
let showLog = false;
|
|
366
|
461
|
switch (stage.name) {
|
| ... |
... |
@@ -368,14 +463,21 @@ class AboutTorConnect { |
|
368
|
463
|
console.error("Should not be open when TorConnect is disabled");
|
|
369
|
464
|
break;
|
|
370
|
465
|
case "Loading":
|
|
|
466
|
+ // Unexpected for this page to open so early.
|
|
|
467
|
+ console.warn("Page opened whilst loading");
|
|
|
468
|
+ isLoaded = false;
|
|
|
469
|
+ break;
|
|
371
|
470
|
case "Start":
|
|
372
|
|
- // Loading is not currnetly handled, treat the same as "Start", but UI
|
|
373
|
|
- // will be unresponsive.
|
|
374
|
471
|
this.showStart(stage.tryAgain, stage.potentiallyBlocked);
|
|
|
472
|
+ if (focusConnect) {
|
|
|
473
|
+ moveFocus = this.elements.connectButton;
|
|
|
474
|
+ }
|
|
375
|
475
|
break;
|
|
376
|
476
|
case "Bootstrapping":
|
|
377
|
477
|
showProgress = true;
|
|
378
|
478
|
this.showBootstrapping(stage.bootstrapTrigger, stage.tryAgain);
|
|
|
479
|
+ // Always focus the cancel button.
|
|
|
480
|
+ moveFocus = this.elements.cancelButton;
|
|
379
|
481
|
break;
|
|
380
|
482
|
case "Offline":
|
|
381
|
483
|
showLog = true;
|
| ... |
... |
@@ -419,6 +521,9 @@ class AboutTorConnect { |
|
419
|
521
|
} else {
|
|
420
|
522
|
this.hide(this.elements.viewLogButton);
|
|
421
|
523
|
}
|
|
|
524
|
+
|
|
|
525
|
+ document.body.classList.toggle("loaded", isLoaded);
|
|
|
526
|
+ moveFocus.focus();
|
|
422
|
527
|
}
|
|
423
|
528
|
|
|
424
|
529
|
updateBootstrappingStatus(data) {
|
| ... |
... |
@@ -452,10 +557,9 @@ class AboutTorConnect { |
|
452
|
557
|
this.show(this.elements.quickstartContainer);
|
|
453
|
558
|
this.show(this.elements.configureButton);
|
|
454
|
559
|
this.show(this.elements.connectButton, true);
|
|
455
|
|
- this.elements.connectButton.focus();
|
|
456
|
|
- if (tryAgain) {
|
|
457
|
|
- this.elements.connectButton.textContent = TorStrings.torConnect.tryAgain;
|
|
458
|
|
- }
|
|
|
560
|
+ this.elements.connectButton.textContent = tryAgain
|
|
|
561
|
+ ? TorStrings.torConnect.tryAgain
|
|
|
562
|
+ : TorStrings.torConnect.torConnectButton;
|
|
459
|
563
|
if (potentiallyBlocked) {
|
|
460
|
564
|
this.setBreadcrumbsStatus(
|
|
461
|
565
|
BreadcrumbStatus.Active,
|
| ... |
... |
@@ -511,7 +615,6 @@ class AboutTorConnect { |
|
511
|
615
|
}
|
|
512
|
616
|
this.hideButtons();
|
|
513
|
617
|
this.show(this.elements.cancelButton);
|
|
514
|
|
- this.elements.cancelButton.focus();
|
|
515
|
618
|
}
|
|
516
|
619
|
|
|
517
|
620
|
showOffline() {
|
| ... |
... |
@@ -541,7 +644,6 @@ class AboutTorConnect { |
|
541
|
644
|
BreadcrumbStatus.Disabled
|
|
542
|
645
|
);
|
|
543
|
646
|
this.showLocationForm(true, TorStrings.torConnect.tryBridge);
|
|
544
|
|
- this.elements.tryBridgeButton.focus();
|
|
545
|
647
|
}
|
|
546
|
648
|
|
|
547
|
649
|
showRegionNotFound() {
|
| ... |
... |
@@ -557,7 +659,6 @@ class AboutTorConnect { |
|
557
|
659
|
BreadcrumbStatus.Disabled
|
|
558
|
660
|
);
|
|
559
|
661
|
this.showLocationForm(false, TorStrings.torConnect.tryBridge);
|
|
560
|
|
- this.elements.tryBridgeButton.focus();
|
|
561
|
662
|
}
|
|
562
|
663
|
|
|
563
|
664
|
showConfirmRegion(error) {
|
| ... |
... |
@@ -573,7 +674,6 @@ class AboutTorConnect { |
|
573
|
674
|
BreadcrumbStatus.Active
|
|
574
|
675
|
);
|
|
575
|
676
|
this.showLocationForm(false, TorStrings.torConnect.tryAgain);
|
|
576
|
|
- this.elements.tryBridgeButton.focus();
|
|
577
|
677
|
}
|
|
578
|
678
|
|
|
579
|
679
|
showFinalError(error) {
|
| ... |
... |
@@ -722,7 +822,8 @@ class AboutTorConnect { |
|
722
|
822
|
this.elements.connectButton.textContent =
|
|
723
|
823
|
TorStrings.torConnect.torConnectButton;
|
|
724
|
824
|
this.elements.connectButton.addEventListener("click", () => {
|
|
725
|
|
- this.beginBootstrapping();
|
|
|
825
|
+ // Record as userClickedConnect if we are in the Start stage.
|
|
|
826
|
+ this.beginBootstrapping(this.shownStage === "Start");
|
|
726
|
827
|
});
|
|
727
|
828
|
|
|
728
|
829
|
this.populateLocations();
|
| ... |
... |
@@ -802,7 +903,13 @@ class AboutTorConnect { |
|
802
|
903
|
this.initObservers();
|
|
803
|
904
|
this.initKeyboardShortcuts();
|
|
804
|
905
|
|
|
805
|
|
- this.updateStage(args.stage);
|
|
|
906
|
+ // If we have previously opened about:torconnect and the user tried the
|
|
|
907
|
+ // "Connect" button we want to focus the "Connect" button for easy
|
|
|
908
|
+ // activation.
|
|
|
909
|
+ // Otherwise, we do not want to focus it for first time users so they can
|
|
|
910
|
+ // read the full page first.
|
|
|
911
|
+ const focusConnect = args.userHasEverClickedConnect;
|
|
|
912
|
+ this.updateStage(args.stage, focusConnect);
|
|
806
|
913
|
this.updateQuickstart(args.quickstartEnabled);
|
|
807
|
914
|
}
|
|
808
|
915
|
}
|