diff options
Diffstat (limited to 'mobile/android/chrome/content/aboutAccounts.js')
-rw-r--r-- | mobile/android/chrome/content/aboutAccounts.js | 351 |
1 files changed, 0 insertions, 351 deletions
diff --git a/mobile/android/chrome/content/aboutAccounts.js b/mobile/android/chrome/content/aboutAccounts.js deleted file mode 100644 index 4801a76a1..000000000 --- a/mobile/android/chrome/content/aboutAccounts.js +++ /dev/null @@ -1,351 +0,0 @@ -// -*- indent-tabs-mode: nil; js-indent-level: 2 -*- -/* 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/. */ - -/** - * Wrap a remote fxa-content-server. - * - * An about:accounts tab loads and displays an fxa-content-server page, - * depending on the current Android Account status and an optional 'action' - * parameter. - * - * We show a spinner while the remote iframe is loading. We expect the - * WebChannel message listening to the fxa-content-server to send this tab's - * <browser>'s messageManager a LOADED message when the remote iframe provides - * the WebChannel LOADED message. See the messageManager registration and the - * |loadedDeferred| promise. This loosely couples the WebChannel implementation - * and about:accounts! (We need this coupling in order to distinguish - * WebChannel LOADED messages produced by multiple about:accounts tabs.) - * - * We capture error conditions by accessing the inner nsIWebNavigation of the - * iframe directly. - */ - -"use strict"; - -var {classes: Cc, interfaces: Ci, utils: Cu} = Components; /*global Components */ - -Cu.import("resource://gre/modules/Accounts.jsm"); /*global Accounts */ -Cu.import("resource://gre/modules/PromiseUtils.jsm"); /*global PromiseUtils */ -Cu.import("resource://gre/modules/Services.jsm"); /*global Services */ -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); /*global XPCOMUtils */ - -const ACTION_URL_PARAM = "action"; - -const COMMAND_LOADED = "fxaccounts:loaded"; - -const log = Cu.import("resource://gre/modules/AndroidLog.jsm", {}).AndroidLog.bind("FxAccounts"); - -XPCOMUtils.defineLazyServiceGetter(this, "ParentalControls", - "@mozilla.org/parental-controls-service;1", "nsIParentalControlsService"); - -// Shows the toplevel element with |id| to be shown - all other top-level -// elements are hidden. -// If |id| is 'spinner', then 'remote' is also shown, with opacity 0. -function show(id) { - let allTop = document.querySelectorAll(".toplevel"); - for (let elt of allTop) { - if (elt.getAttribute("id") == id) { - elt.style.display = 'block'; - } else { - elt.style.display = 'none'; - } - } - if (id == 'spinner') { - document.getElementById('remote').style.display = 'block'; - document.getElementById('remote').style.opacity = 0; - } -} - -// Each time we try to load the remote <iframe>, loadedDeferred is replaced. It -// is resolved by a LOADED message, and rejected by a failure to load. -var loadedDeferred = null; - -// We have a new load starting. Replace the existing promise with a new one, -// and queue up the transition to remote content. -function deferTransitionToRemoteAfterLoaded() { - log.d('Waiting for LOADED message.'); - - loadedDeferred = PromiseUtils.defer(); - loadedDeferred.promise.then(() => { - log.d('Got LOADED message!'); - document.getElementById("remote").style.opacity = 0; - show("remote"); - document.getElementById("remote").style.opacity = 1; - }) - .catch((e) => { - log.w('Did not get LOADED message: ' + e.toString()); - }); -} - -function handleLoadedMessage(message) { - loadedDeferred.resolve(); -}; - -var wrapper = { - iframe: null, - - url: null, - - init: function (url) { - this.url = url; - deferTransitionToRemoteAfterLoaded(); - - let iframe = document.getElementById("remote"); - this.iframe = iframe; - this.iframe.QueryInterface(Ci.nsIFrameLoaderOwner); - let docShell = this.iframe.frameLoader.docShell; - docShell.QueryInterface(Ci.nsIWebProgress); - docShell.addProgressListener(this.iframeListener, Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT); - - // Set the iframe's location with loadURI/LOAD_FLAGS_BYPASS_HISTORY to - // avoid having a new history entry being added. - let webNav = iframe.frameLoader.docShell.QueryInterface(Ci.nsIWebNavigation); - webNav.loadURI(url, Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_HISTORY, null, null, null); - }, - - retry: function () { - deferTransitionToRemoteAfterLoaded(); - - let webNav = this.iframe.frameLoader.docShell.QueryInterface(Ci.nsIWebNavigation); - webNav.loadURI(this.url, Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_HISTORY, null, null, null); - }, - - iframeListener: { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener, - Ci.nsISupportsWeakReference, - Ci.nsISupports]), - - onStateChange: function(aWebProgress, aRequest, aState, aStatus) { - let failure = false; - - // Captive portals sometimes redirect users - if ((aState & Ci.nsIWebProgressListener.STATE_REDIRECTING)) { - failure = true; - } else if ((aState & Ci.nsIWebProgressListener.STATE_STOP)) { - if (aRequest instanceof Ci.nsIHttpChannel) { - try { - failure = aRequest.responseStatus != 200; - } catch (e) { - failure = aStatus != Components.results.NS_OK; - } - } - } - - // Calling cancel() will raise some OnStateChange notifications by itself, - // so avoid doing that more than once - if (failure && aStatus != Components.results.NS_BINDING_ABORTED) { - aRequest.cancel(Components.results.NS_BINDING_ABORTED); - // Since after a promise is fulfilled, subsequent fulfillments are - // treated as no-ops, we don't care that we might see multiple failures - // due to multiple listener callbacks. (It's not easy to extract this - // from the Promises spec, but it is widely quoted. Start with - // http://stackoverflow.com/a/18218542.) - loadedDeferred.reject(new Error("Failed in onStateChange!")); - show("networkError"); - } - }, - - onLocationChange: function(aWebProgress, aRequest, aLocation, aFlags) { - if (aRequest && aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_ERROR_PAGE) { - aRequest.cancel(Components.results.NS_BINDING_ABORTED); - // As above, we're not concerned by multiple listener callbacks. - loadedDeferred.reject(new Error("Failed in onLocationChange!")); - show("networkError"); - } - }, - - onProgressChange: function() {}, - onStatusChange: function() {}, - onSecurityChange: function() {}, - }, -}; - - -function retry() { - log.i("Retrying."); - show("spinner"); - wrapper.retry(); -} - -function openPrefs() { - log.i("Opening Sync preferences."); - // If an Android Account exists, this will open the Status Activity. - // Otherwise, it will begin the Get Started flow. This should only be shown - // when an Account actually exists. - Accounts.launchSetup(); -} - -function getURLForAction(action, urlParams) { - let url = Services.urlFormatter.formatURLPref("identity.fxaccounts.remote.webchannel.uri"); - url = url + (url.endsWith("/") ? "" : "/") + action; - const CONTEXT = "fx_fennec_v1"; - // The only service managed by Fennec, to date, is Firefox Sync. - const SERVICE = "sync"; - urlParams = urlParams || new URLSearchParams(""); - urlParams.set('service', SERVICE); - urlParams.set('context', CONTEXT); - // Ideally we'd just merge urlParams with new URL(url).searchParams, but our - // URLSearchParams implementation doesn't support iteration (bug 1085284). - let urlParamStr = urlParams.toString(); - if (urlParamStr) { - url += (url.includes("?") ? "&" : "?") + urlParamStr; - } - return url; -} - -function updateDisplayedEmail(user) { - let emailDiv = document.getElementById("email"); - if (emailDiv && user) { - emailDiv.textContent = user.email; - } -} - -function init() { - // Test for restrictions before getFirefoxAccount(), since that will fail if - // we are restricted. - if (!ParentalControls.isAllowed(ParentalControls.MODIFY_ACCOUNTS)) { - // It's better to log and show an error message than to invite user - // confusion by removing about:accounts entirely. That is, if the user is - // restricted, this way they'll discover as much and may be able to get - // out of their restricted profile. If we remove about:accounts entirely, - // it will look like Fennec is buggy, and the user will be very confused. - log.e("This profile cannot connect to Firefox Accounts: showing restricted error."); - show("restrictedError"); - return; - } - - Accounts.getFirefoxAccount().then(user => { - // It's possible for the window to start closing before getting the user - // completes. Tests in particular can cause this. - if (window.closed) { - return; - } - - updateDisplayedEmail(user); - - // Ideally we'd use new URL(document.URL).searchParams, but for about: URIs, - // searchParams is empty. - let urlParams = new URLSearchParams(document.URL.split("?")[1] || ""); - let action = urlParams.get(ACTION_URL_PARAM); - urlParams.delete(ACTION_URL_PARAM); - - switch (action) { - case "signup": - if (user) { - // Asking to sign-up when already signed in just shows prefs. - show("prefs"); - } else { - show("spinner"); - wrapper.init(getURLForAction("signup", urlParams)); - } - break; - case "signin": - if (user) { - // Asking to sign-in when already signed in just shows prefs. - show("prefs"); - } else { - show("spinner"); - wrapper.init(getURLForAction("signin", urlParams)); - } - break; - case "force_auth": - if (user) { - show("spinner"); - urlParams.set("email", user.email); // In future, pin using the UID. - wrapper.init(getURLForAction("force_auth", urlParams)); - } else { - show("spinner"); - wrapper.init(getURLForAction("signup", urlParams)); - } - break; - case "manage": - if (user) { - show("spinner"); - urlParams.set("email", user.email); // In future, pin using the UID. - wrapper.init(getURLForAction("settings", urlParams)); - } else { - show("spinner"); - wrapper.init(getURLForAction("signup", urlParams)); - } - break; - case "avatar": - if (user) { - show("spinner"); - urlParams.set("email", user.email); // In future, pin using the UID. - wrapper.init(getURLForAction("settings/avatar/change", urlParams)); - } else { - show("spinner"); - wrapper.init(getURLForAction("signup", urlParams)); - } - break; - default: - // Unrecognized or no action specified. - if (action) { - log.w("Ignoring unrecognized action: " + action); - } - if (user) { - show("prefs"); - } else { - show("spinner"); - wrapper.init(getURLForAction("signup", urlParams)); - } - break; - } - }).catch(e => { - log.e("Failed to get the signed in user: " + e.toString()); - }); -} - -document.addEventListener("DOMContentLoaded", function onload() { - document.removeEventListener("DOMContentLoaded", onload, true); - init(); - var buttonRetry = document.getElementById('buttonRetry'); - buttonRetry.addEventListener('click', retry); - - var buttonOpenPrefs = document.getElementById('buttonOpenPrefs'); - buttonOpenPrefs.addEventListener('click', openPrefs); -}, true); - -// This window is contained in a XUL <browser> element. Return the -// messageManager of that <browser> element, or null. -function getBrowserMessageManager() { - let browser = window - .QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIWebNavigation) - .QueryInterface(Ci.nsIDocShellTreeItem) - .rootTreeItem - .QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindow) - .QueryInterface(Ci.nsIDOMChromeWindow) - .BrowserApp - .getBrowserForDocument(document); - if (browser) { - return browser.messageManager; - } - return null; -} - -// Add a single listener for 'loaded' messages from the iframe in this -// <browser>. These 'loaded' messages are ferried from the WebChannel to just -// this <browser>. -var mm = getBrowserMessageManager(); -if (mm) { - mm.addMessageListener(COMMAND_LOADED, handleLoadedMessage); -} else { - log.e('No messageManager, not listening for LOADED message!'); -} - -window.addEventListener("unload", function(event) { - try { - let mm = getBrowserMessageManager(); - if (mm) { - mm.removeMessageListener(COMMAND_LOADED, handleLoadedMessage); - } - } catch (e) { - // This could fail if the page is being torn down, the tab is being - // destroyed, etc. - log.w('Not removing listener for LOADED message: ' + e.toString()); - } -}); |