summaryrefslogtreecommitdiffstats
path: root/browser/components/preferences/fxaPairDevice.js
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /browser/components/preferences/fxaPairDevice.js
parentInitial commit. (diff)
downloadfirefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz
firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'browser/components/preferences/fxaPairDevice.js')
-rw-r--r--browser/components/preferences/fxaPairDevice.js124
1 files changed, 124 insertions, 0 deletions
diff --git a/browser/components/preferences/fxaPairDevice.js b/browser/components/preferences/fxaPairDevice.js
new file mode 100644
index 0000000000..42fad9f8e3
--- /dev/null
+++ b/browser/components/preferences/fxaPairDevice.js
@@ -0,0 +1,124 @@
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+const { XPCOMUtils } = ChromeUtils.import(
+ "resource://gre/modules/XPCOMUtils.jsm"
+);
+const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+const { FxAccounts } = ChromeUtils.import(
+ "resource://gre/modules/FxAccounts.jsm"
+);
+const { Weave } = ChromeUtils.import("resource://services-sync/main.js");
+
+XPCOMUtils.defineLazyModuleGetters(this, {
+ EventEmitter: "resource://gre/modules/EventEmitter.jsm",
+ FxAccountsPairingFlow: "resource://gre/modules/FxAccountsPairing.jsm",
+});
+const { require } = ChromeUtils.import(
+ "resource://devtools/shared/Loader.jsm",
+ {}
+);
+const QR = require("devtools/shared/qrcode/index");
+
+// This is only for "labor illusion", see
+// https://www.fastcompany.com/3061519/the-ux-secret-that-will-ruin-apps-for-you
+const MIN_PAIRING_LOADING_TIME_MS = 1000;
+
+/**
+ * Communication between FxAccountsPairingFlow and gFxaPairDeviceDialog
+ * is done using an emitter via the following messages:
+ * <- [view:SwitchToWebContent] - Notifies the view to navigate to a specific URL.
+ * <- [view:Error] - Notifies the view something went wrong during the pairing process.
+ * -> [view:Closed] - Notifies the pairing module the view was closed.
+ */
+var gFxaPairDeviceDialog = {
+ init() {
+ this._resetBackgroundQR();
+ FxAccounts.config
+ .promiseConnectDeviceURI("pairing-modal")
+ .then(connectURI => {
+ document
+ .getElementById("connect-another-device-link")
+ .setAttribute("href", connectURI);
+ });
+ // We let the modal show itself before eventually showing a master-password dialog later.
+ Services.tm.dispatchToMainThread(() => this.startPairingFlow());
+ },
+
+ uninit() {
+ this.teardownListeners();
+ this._emitter.emit("view:Closed");
+ },
+
+ async startPairingFlow() {
+ this._resetBackgroundQR();
+ document
+ .getElementById("qrWrapper")
+ .setAttribute("pairing-status", "loading");
+ this._emitter = new EventEmitter();
+ this.setupListeners();
+ try {
+ if (!Weave.Utils.ensureMPUnlocked()) {
+ throw new Error("Master-password locked.");
+ }
+ const [, uri] = await Promise.all([
+ new Promise(res => setTimeout(res, MIN_PAIRING_LOADING_TIME_MS)),
+ FxAccountsPairingFlow.start({ emitter: this._emitter }),
+ ]);
+ const imgData = QR.encodeToDataURI(uri, "L");
+ document.getElementById(
+ "qrContainer"
+ ).style.backgroundImage = `url("${imgData.src}")`;
+ document
+ .getElementById("qrWrapper")
+ .setAttribute("pairing-status", "ready");
+ } catch (e) {
+ this.onError(e);
+ }
+ },
+
+ _resetBackgroundQR() {
+ // The text we encode doesn't really matter as it is un-scannable (blurry and very transparent).
+ const imgData = QR.encodeToDataURI(
+ "https://accounts.firefox.com/pair",
+ "L"
+ );
+ document.getElementById(
+ "qrContainer"
+ ).style.backgroundImage = `url("${imgData.src}")`;
+ },
+
+ onError(err) {
+ Cu.reportError(err);
+ this.teardownListeners();
+ document
+ .getElementById("qrWrapper")
+ .setAttribute("pairing-status", "error");
+ },
+
+ _switchToUrl(url) {
+ const browser = window.docShell.chromeEventHandler;
+ browser.loadURI(url, {
+ triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal(
+ {}
+ ),
+ });
+ },
+
+ setupListeners() {
+ this._switchToWebContent = (_, url) => this._switchToUrl(url);
+ this._onError = (_, error) => this.onError(error);
+ this._emitter.once("view:SwitchToWebContent", this._switchToWebContent);
+ this._emitter.on("view:Error", this._onError);
+ },
+
+ teardownListeners() {
+ try {
+ this._emitter.off("view:SwitchToWebContent", this._switchToWebContent);
+ this._emitter.off("view:Error", this._onError);
+ } catch (e) {
+ console.warn("Error while tearing down listeners.", e);
+ }
+ },
+};