michael@0: // -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*- michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: "use strict"; michael@0: michael@0: Components.utils.import("resource://gre/modules/Services.jsm"); michael@0: michael@0: let SyncFlyoutPanel = { michael@0: init: function() { michael@0: if (this._isInitialized) { michael@0: Cu.reportError("Attempted to initialize SyncFlyoutPanel more than once"); michael@0: return; michael@0: } michael@0: michael@0: this._isInitialized = true; michael@0: this._bundle = Services.strings.createBundle("chrome://browser/locale/sync.properties"); michael@0: Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); michael@0: let self = this; michael@0: michael@0: this._elements = {}; michael@0: [ michael@0: ['outer', 'sync-flyoutpanel'], michael@0: ['preSetup', 'sync-presetup-container'], michael@0: ['easySetup', 'sync-setup-container'], michael@0: ['manualSetup', 'sync-manualsetup-container'], michael@0: ['setupSuccess', 'sync-setupsuccess-container'], michael@0: ['setupFailure', 'sync-setupfailure-container'], michael@0: ['connected', 'sync-connected-container'], michael@0: ['pairNewDevice', 'sync-pair-container'], michael@0: ['pairSuccess', 'sync-pair-success-container'], michael@0: ['setupCode1', 'sync-setup-code1'], michael@0: ['setupCode2', 'sync-setup-code2'], michael@0: ['setupCode3', 'sync-setup-code3'], michael@0: ['setupThrobber', 'sync-setup-throbber'], michael@0: ['account', 'sync-manualsetup-account'], michael@0: ['password', 'sync-manualsetup-password'], michael@0: ['syncKey', 'sync-manualsetup-syncKey'], michael@0: ['manualSetupConnect', 'sync-manualsetup-connect'], michael@0: ['manualSetupFailure', 'sync-manualsetup-failure'], michael@0: ['connectedAccount', 'sync-connected-account'], michael@0: ['deviceName', 'sync-connected-device'], michael@0: ['lastSync', 'sync-connected-lastSynced'], michael@0: ['connectedThrobber', 'sync-connected-throbber'], michael@0: ['disconnectLink', 'sync-disconnect-label'], michael@0: ['disconnectWarning', 'sync-disconnect-warning'], michael@0: ['pairCode1', 'sync-pair-entry1'], michael@0: ['pairCode2', 'sync-pair-entry2'], michael@0: ['pairCode3', 'sync-pair-entry3'], michael@0: ['pairButton', 'sync-pair-button'], michael@0: ['pairFailureMessage', 'sync-pair-failure'], michael@0: ].forEach(function (aContainer) { michael@0: let [name, id] = aContainer; michael@0: XPCOMUtils.defineLazyGetter(self._elements, name, function() { michael@0: return document.getElementById(id); michael@0: }); michael@0: }); michael@0: michael@0: this._topmostElement = this._elements.outer; michael@0: michael@0: let xps = Components.classes["@mozilla.org/weave/service;1"] michael@0: .getService(Components.interfaces.nsISupports) michael@0: .wrappedJSObject; michael@0: michael@0: if (xps.ready) { michael@0: this._onServiceReady(); michael@0: } else { michael@0: Services.obs.addObserver(this._onServiceReady.bind(this), michael@0: "weave:service:ready", michael@0: false); michael@0: xps.ensureLoaded(); michael@0: } michael@0: }, michael@0: michael@0: _hide: function() { michael@0: this._elements.outer.hide(); michael@0: this.showInitialScreen(); michael@0: }, michael@0: michael@0: _hideVisibleContainer: function() { michael@0: if (this._currentlyVisibleContainer) { michael@0: this._currentlyVisibleContainer.collapsed = true; michael@0: delete this._currentlyVisibleContainer; michael@0: delete this._onBackButton; michael@0: } michael@0: }, michael@0: michael@0: _onServiceReady: function(aEvent) { michael@0: if (aEvent) { michael@0: Services.obs.removeObserver(this._onServiceReady, "weave:service:ready"); michael@0: } michael@0: michael@0: this.showInitialScreen(); michael@0: Services.obs.addObserver(this._onSyncStart.bind(this), "weave:service:sync:start", false); michael@0: Services.obs.addObserver(this._onSyncEnd.bind(this), "weave:ui:sync:finish", false); michael@0: Services.obs.addObserver(this._onSyncEnd.bind(this), "weave:ui:sync:error", false); michael@0: Weave.Service.scheduler.scheduleNextSync(10*1000); michael@0: }, michael@0: michael@0: _onSyncStart: function() { michael@0: this._isSyncing = true; michael@0: this._updateConnectedPage(); michael@0: }, michael@0: michael@0: _onSyncEnd: function() { michael@0: this._isSyncing = false; michael@0: this._updateConnectedPage(); michael@0: }, michael@0: michael@0: showInitialScreen: function() { michael@0: if (Weave.Status.login == Weave.LOGIN_SUCCEEDED) { michael@0: this.showConnected(); michael@0: } else { michael@0: this.showPreSetup(); michael@0: } michael@0: }, michael@0: michael@0: abortEasySetup: function() { michael@0: if (this._setupJpake) { michael@0: this._setupJpake.abort(); michael@0: } michael@0: this._cleanUpEasySetup(); michael@0: }, michael@0: michael@0: _cleanUpEasySetup: function() { michael@0: this._elements.setupCode1.value = ""; michael@0: this._elements.setupCode2.value = ""; michael@0: this._elements.setupCode3.value = ""; michael@0: delete this._setupJpake; michael@0: this._elements.setupThrobber.collapsed = true; michael@0: this._elements.setupThrobber.enabled = false; michael@0: }, michael@0: michael@0: _updateConnectedPage: function() { michael@0: // Show the day-of-week and time (HH:MM) of last sync michael@0: let lastSync = Weave.Svc.Prefs.get("lastSync"); michael@0: let syncDate = ''; michael@0: if (lastSync != null) { michael@0: syncDate = new Date(lastSync).toLocaleFormat("%A %I:%M %p"); michael@0: } michael@0: michael@0: let device = Weave.Service.clientsEngine.localName; michael@0: let account = Weave.Service.identity.account; michael@0: this._elements.deviceName.textContent = michael@0: this._bundle.formatStringFromName("sync.flyout.connected.device", michael@0: [device], 1); michael@0: this._elements.connectedAccount.textContent = michael@0: this._bundle.formatStringFromName("sync.flyout.connected.account", michael@0: [account], 1); michael@0: this._elements.lastSync.textContent = michael@0: this._bundle.formatStringFromName("sync.flyout.connected.lastSynced", michael@0: [syncDate], 1); michael@0: michael@0: if (this._currentlyVisibleContainer == this._elements.connected michael@0: && this._isSyncing) { michael@0: this._elements.connectedThrobber.collapsed = false; michael@0: this._elements.connectedThrobber.enabled = true; michael@0: } else { michael@0: this._elements.connectedThrobber.collapsed = true; michael@0: this._elements.connectedThrobber.enabled = false; michael@0: } michael@0: }, michael@0: michael@0: showConnected: function() { michael@0: // Reset state of the connected screen michael@0: this._elements.disconnectWarning.collapsed = true; michael@0: this._elements.disconnectLink.collapsed = false; michael@0: michael@0: this._updateConnectedPage(); michael@0: this._showContainer(this._elements.connected); michael@0: }, michael@0: michael@0: startEasySetup: function() { michael@0: let self = this; michael@0: michael@0: this._showContainer(this._elements.easySetup); michael@0: michael@0: // Set up our back button to do the appropriate action michael@0: this._onBackButton = function() { michael@0: self.abortEasySetup(); michael@0: self.showInitialScreen(); michael@0: }; michael@0: michael@0: this._setupJpake = new Weave.JPAKEClient({ michael@0: displayPIN: function displayPIN(aPin) { michael@0: self._elements.setupCode1.value = aPin.slice(0, 4); michael@0: self._elements.setupCode2.value = aPin.slice(4, 8); michael@0: self._elements.setupCode3.value = aPin.slice(8); michael@0: }, michael@0: michael@0: onPairingStart: function onPairingStart() { michael@0: self._elements.setupThrobber.collapsed = false; michael@0: self._elements.setupThrobber.enabled = true; michael@0: }, michael@0: michael@0: onComplete: function onComplete(aCredentials) { michael@0: Weave.Service.identity.account = aCredentials.account; michael@0: Weave.Service.identity.basicPassword = aCredentials.password; michael@0: Weave.Service.identity.syncKey = aCredentials.synckey; michael@0: Weave.Service.serverURL = aCredentials.serverURL; michael@0: Weave.Service.persistLogin(); michael@0: Weave.Service.scheduler.scheduleNextSync(0); michael@0: michael@0: if (self._currentlyVisibleContainer == self._elements.easySetup) { michael@0: self.showSetupSuccess(); michael@0: } michael@0: self._cleanUpEasySetup(); michael@0: }, michael@0: michael@0: onAbort: function onAbort(aError) { michael@0: if (aError == "jpake.error.userabort") { michael@0: Services.obs.notifyObservers(null, "browser:sync:setup:userabort", ""); michael@0: self._cleanUpEasySetup(); michael@0: return; michael@0: } else if (aError == "jpake.error.network") { michael@0: Services.obs.notifyObservers(null, "browser:sync:setup:networkerror", ""); michael@0: } michael@0: michael@0: if (self._currentlyVisibleContainer == self._elements.easySetup) { michael@0: self.showSetupFailure(); michael@0: self._cleanUpEasySetup(); michael@0: } michael@0: } michael@0: }); michael@0: michael@0: this._setupJpake.receiveNoPIN(); michael@0: }, michael@0: michael@0: _showContainer: function(aContainer) { michael@0: this._hideVisibleContainer(); michael@0: this._currentlyVisibleContainer = aContainer; michael@0: this._currentlyVisibleContainer.collapsed = false; michael@0: }, michael@0: michael@0: showSetupSuccess: function() { michael@0: this._showContainer(this._elements.setupSuccess); michael@0: this._onBackButton = this.showInitialScreen; michael@0: }, michael@0: michael@0: showSetupFailure: function() { michael@0: this._showContainer(this._elements.setupFailure); michael@0: this._onBackButton = this.showInitialScreen; michael@0: }, michael@0: michael@0: showPreSetup: function() { michael@0: this._showContainer(this._elements.preSetup); michael@0: delete this._onBackButton; michael@0: }, michael@0: michael@0: showManualSetup: function() { michael@0: this._showContainer(this._elements.manualSetup); michael@0: this._onBackButton = this.showInitialScreen; michael@0: michael@0: this._elements.account.value = Weave.Service.identity.account; michael@0: this._elements.password.value = Weave.Service.identity.basicPassword; michael@0: this._elements.syncKey.value = michael@0: Weave.Utils.hyphenatePassphrase(Weave.Service.identity.syncKey); michael@0: this.updateManualSetupConnectButtonState(); michael@0: }, michael@0: michael@0: updateManualSetupConnectButtonState: function() { michael@0: this._elements.manualSetupConnect.disabled = !this._elements.account.value michael@0: || !this._elements.password.value michael@0: || !this._elements.syncKey.value; michael@0: }, michael@0: michael@0: manualSetupConnect: function() { michael@0: delete this._onBackButton; michael@0: this._elements.manualSetupConnect.disabled = true; michael@0: Weave.Service.identity.account = this._elements.account.value; michael@0: Weave.Service.identity.basicPassword = this._elements.password.value; michael@0: Weave.Service.identity.syncKey = Weave.Utils.normalizePassphrase(this._elements.syncKey.value); michael@0: if (Weave.Service.login()) { michael@0: Weave.Service.persistLogin(); michael@0: if (this._currentlyVisibleContainer == this._elements.manualSetup) { michael@0: this.showSetupSuccess(); michael@0: } michael@0: Weave.Service.scheduler.scheduleNextSync(0); michael@0: } else { michael@0: this._elements.manualSetupFailure.textContent = Weave.Utils.getErrorString(Weave.Status.login); michael@0: this._elements.manualSetupFailure.collapsed = false; michael@0: this.updateManualSetupConnectButtonState(); michael@0: } michael@0: }, michael@0: michael@0: onDisconnectLink: function() { michael@0: this._elements.disconnectWarning.collapsed = false; michael@0: this._elements.disconnectLink.collapsed = true; michael@0: }, michael@0: michael@0: onDisconnectCancel: function() { michael@0: this._elements.disconnectWarning.collapsed = true; michael@0: this._elements.disconnectLink.collapsed = false; michael@0: }, michael@0: michael@0: onDisconnectButton: function() { michael@0: Weave.Service.startOver(); michael@0: this.showInitialScreen(); michael@0: }, michael@0: michael@0: onPairDeviceLink: function() { michael@0: // Reset state michael@0: this._elements.pairCode1.value = ""; michael@0: this._elements.pairCode2.value = ""; michael@0: this._elements.pairCode3.value = ""; michael@0: this.updatePairButtonState(); michael@0: this._elements.pairFailureMessage.collapsed = true; michael@0: this._elements.pairNewDevice.collapsed = false; michael@0: michael@0: this._showContainer(this._elements.pairNewDevice); michael@0: this._onBackButton = this.showInitialScreen; michael@0: }, michael@0: michael@0: updatePairButtonState: function () { michael@0: this._elements.pairButton.disabled = !this._elements.pairCode1.value michael@0: || !this._elements.pairCode2.value michael@0: || !this._elements.pairCode3.value; michael@0: }, michael@0: michael@0: onCancelButton: function() { michael@0: this.showInitialContainer(); michael@0: }, michael@0: michael@0: onTryAgainButton: function() { michael@0: this.startEasySetup(); michael@0: }, michael@0: michael@0: onPairButton: function() { michael@0: this._elements.pairButton.disabled = true; michael@0: this._elements.pairFailureMessage.collapsed = true; michael@0: let self = this; michael@0: this._pairJpake = new Weave.JPAKEClient({ michael@0: onPaired: function() { michael@0: self._pairJpake.sendAndComplete({ michael@0: account: Weave.Service.identity.account, michael@0: password: Weave.Service.identity.basicPassword, michael@0: synckey: Weave.Service.identity.syncKey, michael@0: serverURL: Weave.Service.serverURL michael@0: }); michael@0: }, michael@0: michael@0: onComplete: function() { michael@0: delete self._pairJpake; michael@0: Weave.Service.persistLogin(); michael@0: if (self._currentlyVisibleContainer == self._elements.pairNewDevice) { michael@0: self._showContainer(self._elements.pairSuccess); michael@0: } michael@0: Weave.Service.scheduler.scheduleNextSync(Weave.Service.scheduler.activeInterval); michael@0: }, michael@0: michael@0: onAbort: function(error) { michael@0: delete self._pairJpake; michael@0: if (error == Weave.JPAKE_ERROR_USERABORT) { michael@0: return; michael@0: } michael@0: michael@0: self._elements.pairFailureMessage.collapsed = false; michael@0: self.updatePairButtonState(); michael@0: } michael@0: }); michael@0: michael@0: this._pairJpake.pairWithPIN(this._elements.pairCode1.value michael@0: + this._elements.pairCode2.value michael@0: + this._elements.pairCode3.value, michael@0: false); michael@0: }, michael@0: };