summaryrefslogtreecommitdiffstats
path: root/mobile/android/modules/FxAccountsWebChannel.jsm
diff options
context:
space:
mode:
Diffstat (limited to 'mobile/android/modules/FxAccountsWebChannel.jsm')
-rw-r--r--mobile/android/modules/FxAccountsWebChannel.jsm394
1 files changed, 0 insertions, 394 deletions
diff --git a/mobile/android/modules/FxAccountsWebChannel.jsm b/mobile/android/modules/FxAccountsWebChannel.jsm
deleted file mode 100644
index 6ee8fd07f..000000000
--- a/mobile/android/modules/FxAccountsWebChannel.jsm
+++ /dev/null
@@ -1,394 +0,0 @@
-// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
-/* 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/. */
-
-/**
- * Firefox Accounts Web Channel.
- *
- * Use the WebChannel component to receive messages about account
- * state changes.
- */
-this.EXPORTED_SYMBOLS = ["EnsureFxAccountsWebChannel"];
-
-const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; /*global Components */
-
-Cu.import("resource://gre/modules/Accounts.jsm"); /*global Accounts */
-Cu.import("resource://gre/modules/Services.jsm"); /*global Services */
-Cu.import("resource://gre/modules/WebChannel.jsm"); /*global WebChannel */
-Cu.import("resource://gre/modules/XPCOMUtils.jsm"); /*global XPCOMUtils */
-
-const log = Cu.import("resource://gre/modules/AndroidLog.jsm", {}).AndroidLog.bind("FxAccounts");
-
-const WEBCHANNEL_ID = "account_updates";
-
-const COMMAND_LOADED = "fxaccounts:loaded";
-const COMMAND_CAN_LINK_ACCOUNT = "fxaccounts:can_link_account";
-const COMMAND_LOGIN = "fxaccounts:login";
-const COMMAND_CHANGE_PASSWORD = "fxaccounts:change_password";
-const COMMAND_DELETE_ACCOUNT = "fxaccounts:delete_account";
-const COMMAND_PROFILE_CHANGE = "profile:change";
-const COMMAND_SYNC_PREFERENCES = "fxaccounts:sync_preferences";
-
-const PREF_LAST_FXA_USER = "identity.fxaccounts.lastSignedInUserHash";
-
-XPCOMUtils.defineLazyGetter(this, "strings",
- () => Services.strings.createBundle("chrome://browser/locale/aboutAccounts.properties")); /*global strings */
-
-XPCOMUtils.defineLazyModuleGetter(this, "Snackbars", "resource://gre/modules/Snackbars.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "Prompt", "resource://gre/modules/Prompt.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "UITelemetry", "resource://gre/modules/UITelemetry.jsm");
-
-this.FxAccountsWebChannelHelpers = function() {
-};
-
-this.FxAccountsWebChannelHelpers.prototype = {
- /**
- * Get the hash of account name of the previously signed in account.
- */
- getPreviousAccountNameHashPref() {
- try {
- return Services.prefs.getComplexValue(PREF_LAST_FXA_USER, Ci.nsISupportsString).data;
- } catch (_) {
- return "";
- }
- },
-
- /**
- * Given an account name, set the hash of the previously signed in account.
- *
- * @param acctName the account name of the user's account.
- */
- setPreviousAccountNameHashPref(acctName) {
- let string = Cc["@mozilla.org/supports-string;1"]
- .createInstance(Ci.nsISupportsString);
- string.data = this.sha256(acctName);
- Services.prefs.setComplexValue(PREF_LAST_FXA_USER, Ci.nsISupportsString, string);
- },
-
- /**
- * Given a string, returns the SHA265 hash in base64.
- */
- sha256(str) {
- let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
- .createInstance(Ci.nsIScriptableUnicodeConverter);
- converter.charset = "UTF-8";
- // Data is an array of bytes.
- let data = converter.convertToByteArray(str, {});
- let hasher = Cc["@mozilla.org/security/hash;1"]
- .createInstance(Ci.nsICryptoHash);
- hasher.init(hasher.SHA256);
- hasher.update(data, data.length);
-
- return hasher.finish(true);
- },
-};
-
-/**
- * Create a new FxAccountsWebChannel to listen for account updates.
- *
- * @param {Object} options Options
- * @param {Object} options
- * @param {String} options.content_uri
- * The FxA Content server uri
- * @param {String} options.channel_id
- * The ID of the WebChannel
- * @param {String} options.helpers
- * Helpers functions. Should only be passed in for testing.
- * @constructor
- */
-this.FxAccountsWebChannel = function(options) {
- if (!options) {
- throw new Error("Missing configuration options");
- }
- if (!options["content_uri"]) {
- throw new Error("Missing 'content_uri' option");
- }
- this._contentUri = options.content_uri;
-
- if (!options["channel_id"]) {
- throw new Error("Missing 'channel_id' option");
- }
- this._webChannelId = options.channel_id;
-
- // options.helpers is only specified by tests.
- this._helpers = options.helpers || new FxAccountsWebChannelHelpers(options);
-
- this._setupChannel();
-};
-
-this.FxAccountsWebChannel.prototype = {
- /**
- * WebChannel that is used to communicate with content page
- */
- _channel: null,
-
- /**
- * WebChannel ID.
- */
- _webChannelId: null,
- /**
- * WebChannel origin, used to validate origin of messages
- */
- _webChannelOrigin: null,
-
- /**
- * Release all resources that are in use.
- */
- tearDown() {
- this._channel.stopListening();
- this._channel = null;
- this._channelCallback = null;
- },
-
- /**
- * Configures and registers a new WebChannel
- *
- * @private
- */
- _setupChannel() {
- // if this.contentUri is present but not a valid URI, then this will throw an error.
- try {
- this._webChannelOrigin = Services.io.newURI(this._contentUri, null, null);
- this._registerChannel();
- } catch (e) {
- log.e(e.toString());
- throw e;
- }
- },
-
- /**
- * Create a new channel with the WebChannelBroker, setup a callback listener
- * @private
- */
- _registerChannel() {
- /**
- * Processes messages that are called back from the FxAccountsChannel
- *
- * @param webChannelId {String}
- * Command webChannelId
- * @param message {Object}
- * Command message
- * @param sendingContext {Object}
- * Message sending context.
- * @param sendingContext.browser {browser}
- * The <browser> object that captured the
- * WebChannelMessageToChrome.
- * @param sendingContext.eventTarget {EventTarget}
- * The <EventTarget> where the message was sent.
- * @param sendingContext.principal {Principal}
- * The <Principal> of the EventTarget where the message was sent.
- * @private
- *
- */
- let listener = (webChannelId, message, sendingContext) => {
- if (message) {
- let command = message.command;
- let data = message.data;
- log.d("FxAccountsWebChannel message received, command: " + command);
-
- // Respond to the message with true or false.
- let respond = (data) => {
- let response = {
- command: command,
- messageId: message.messageId,
- data: data
- };
- log.d("Sending response to command: " + command);
- this._channel.send(response, sendingContext);
- };
-
- switch (command) {
- case COMMAND_LOADED:
- let mm = sendingContext.browser.docShell
- .QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIContentFrameMessageManager);
- mm.sendAsyncMessage(COMMAND_LOADED);
- break;
-
- case COMMAND_CAN_LINK_ACCOUNT:
- Accounts.getFirefoxAccount().then(account => {
- if (account) {
- // If we /have/ an Android Account, we never allow the user to
- // login to a different account. They need to manually delete
- // the first Android Account and then create a new one.
- if (account.email == data.email) {
- // In future, we should use a UID for this comparison.
- log.d("Relinking existing Android Account: email addresses agree.");
- respond({ok: true});
- } else {
- log.w("Not relinking existing Android Account: email addresses disagree!");
- let message = strings.GetStringFromName("relinkDenied.message");
- let buttonLabel = strings.GetStringFromName("relinkDenied.openPrefs");
- Snackbars.show(message, Snackbars.LENGTH_LONG, {
- action: {
- label: buttonLabel,
- callback: () => {
- // We have an account, so this opens Sync native preferences.
- Accounts.launchSetup();
- },
- }
- });
- respond({ok: false});
- }
- } else {
- // If we /don't have/ an Android Account, we warn if we're
- // connecting to a new Account. This is to minimize surprise;
- // we never did this when changing accounts via the native UI.
- let prevAcctHash = this._helpers.getPreviousAccountNameHashPref();
- let shouldShowWarning = prevAcctHash && (prevAcctHash != this._helpers.sha256(data.email));
-
- if (shouldShowWarning) {
- log.w("Warning about creating a new Android Account: previously linked to different email address!");
- let message = strings.formatStringFromName("relinkVerify.message", [data.email], 1);
- new Prompt({
- title: strings.GetStringFromName("relinkVerify.title"),
- message: message,
- buttons: [
- // This puts Cancel on the right.
- strings.GetStringFromName("relinkVerify.cancel"),
- strings.GetStringFromName("relinkVerify.continue"),
- ],
- }).show(result => respond({ok: result && result.button == 1}));
- } else {
- log.d("Not warning about creating a new Android Account: no previously linked email address.");
- respond({ok: true});
- }
- }
- }).catch(e => {
- log.e(e.toString());
- respond({ok: false});
- });
- break;
-
- case COMMAND_LOGIN:
- // Either create a new Android Account or re-connect an existing
- // Android Account here. There's not much to be done if we don't
- // succeed or get an error.
- Accounts.getFirefoxAccount().then(account => {
- if (!account) {
- return Accounts.createFirefoxAccountFromJSON(data).then(success => {
- if (!success) {
- throw new Error("Could not create Firefox Account!");
- }
- UITelemetry.addEvent("action.1", "content", null, "fxaccount-create");
- return success;
- });
- } else {
- return Accounts.updateFirefoxAccountFromJSON(data).then(success => {
- if (!success) {
- throw new Error("Could not update Firefox Account!");
- }
- UITelemetry.addEvent("action.1", "content", null, "fxaccount-login");
- return success;
- });
- }
- })
- .then(success => {
- if (!success) {
- throw new Error("Could not create or update Firefox Account!");
- }
-
- // Remember who it is so we can show a relink warning when appropriate.
- this._helpers.setPreviousAccountNameHashPref(data.email);
-
- log.i("Created or updated Firefox Account.");
- })
- .catch(e => {
- log.e(e.toString());
- });
- break;
-
- case COMMAND_CHANGE_PASSWORD:
- // Only update an existing Android Account.
- Accounts.getFirefoxAccount().then(account => {
- if (!account) {
- throw new Error("Can't change password of non-existent Firefox Account!");
- }
- return Accounts.updateFirefoxAccountFromJSON(data);
- })
- .then(success => {
- if (!success) {
- throw new Error("Could not change Firefox Account password!");
- }
- UITelemetry.addEvent("action.1", "content", null, "fxaccount-changepassword");
- log.i("Changed Firefox Account password.");
- })
- .catch(e => {
- log.e(e.toString());
- });
- break;
-
- case COMMAND_DELETE_ACCOUNT:
- // The fxa-content-server has already confirmed the user's intent.
- // Bombs away. There's no recovery from failure, and not even a
- // real need to check an account exists (although we do, for error
- // messaging only).
- Accounts.getFirefoxAccount().then(account => {
- if (!account) {
- throw new Error("Can't delete non-existent Firefox Account!");
- }
- return Accounts.deleteFirefoxAccount().then(success => {
- if (!success) {
- throw new Error("Could not delete Firefox Account!");
- }
- UITelemetry.addEvent("action.1", "content", null, "fxaccount-delete");
- log.i("Firefox Account deleted.");
- });
- }).catch(e => {
- log.e(e.toString());
- });
- break;
-
- case COMMAND_PROFILE_CHANGE:
- // Only update an existing Android Account.
- Accounts.getFirefoxAccount().then(account => {
- if (!account) {
- throw new Error("Can't change profile of non-existent Firefox Account!");
- }
- UITelemetry.addEvent("action.1", "content", null, "fxaccount-changeprofile");
- return Accounts.notifyFirefoxAccountProfileChanged();
- })
- .catch(e => {
- log.e(e.toString());
- });
- break;
-
- case COMMAND_SYNC_PREFERENCES:
- UITelemetry.addEvent("action.1", "content", null, "fxaccount-syncprefs");
- Accounts.showSyncPreferences()
- .catch(e => {
- log.e(e.toString());
- });
- break;
-
- default:
- log.w("Ignoring unrecognized FxAccountsWebChannel command: " + JSON.stringify(command));
- break;
- }
- }
- };
-
- this._channelCallback = listener;
- this._channel = new WebChannel(this._webChannelId, this._webChannelOrigin);
- this._channel.listen(listener);
-
- log.d("FxAccountsWebChannel registered: " + this._webChannelId + " with origin " + this._webChannelOrigin.prePath);
- }
-};
-
-var singleton;
-// The entry-point for this module, which ensures only one of our channels is
-// ever created - we require this because the WebChannel is global in scope and
-// allowing multiple channels would cause such notifications to be sent multiple
-// times.
-this.EnsureFxAccountsWebChannel = function() {
- if (!singleton) {
- let contentUri = Services.urlFormatter.formatURLPref("identity.fxaccounts.remote.webchannel.uri");
- // The FxAccountsWebChannel listens for events and updates the Java layer.
- singleton = new this.FxAccountsWebChannel({
- content_uri: contentUri,
- channel_id: WEBCHANNEL_ID,
- });
- }
-};