Bug 1110336 - Update sync old-dialog-based preferences UI to match FxA migration flows, part 2. r=markh
authorDrew Willcoxon <adw@mozilla.com>
Thu, 08 Jan 2015 15:03:25 -0800
changeset 248729 0d113455b05c375db4e77cb2bef6fc87a402316a
parent 248728 df2c68a00ffbcf52c5d1c2d86dbee5fb1fbe58c2
child 248730 be0ad231ea19afc28da6aa0cde8aa4f949b569db
push id4489
push userraliiev@mozilla.com
push dateMon, 23 Feb 2015 15:17:55 +0000
treeherdermozilla-beta@fd7c3dc24146 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmarkh
bugs1110336
milestone37.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1110336 - Update sync old-dialog-based preferences UI to match FxA migration flows, part 2. r=markh
browser/components/preferences/sync.js
browser/components/preferences/sync.xul
browser/themes/linux/preferences/preferences.css
browser/themes/osx/preferences/preferences.css
browser/themes/windows/preferences/preferences.css
services/sync/modules/FxaMigrator.jsm
--- a/browser/components/preferences/sync.js
+++ b/browser/components/preferences/sync.js
@@ -26,17 +26,16 @@ const FXA_PAGE_LOGGED_IN = 5;
 // We are in a successful verified state - everything should work!
 const FXA_LOGIN_VERIFIED = 0;
 // We have logged in to an unverified account.
 const FXA_LOGIN_UNVERIFIED = 1;
 // We are logged in locally, but the server rejected our credentials.
 const FXA_LOGIN_FAILED = 2;
 
 let gSyncPane = {
-  _stringBundle: null,
   prefArray: ["engine.bookmarks", "engine.passwords", "engine.prefs",
               "engine.tabs", "engine.history"],
 
   get page() {
     return document.getElementById("weavePrefsDeck").selectedIndex;
   },
 
   set page(val) {
@@ -102,34 +101,40 @@ let gSyncPane = {
     //        of `this`. Fix in a followup. (bug 583347)
     topics.forEach(function (topic) {
       Weave.Svc.Obs.add(topic, this.updateWeavePrefs, this);
     }, this);
     // The FxA migration observer is a special case.
     Weave.Svc.Obs.add(migrateTopic, this.updateMigrationState, this);
 
     window.addEventListener("unload", function() {
-      topics.forEach(function (topic) {
+      topics.forEach(topic => {
         Weave.Svc.Obs.remove(topic, this.updateWeavePrefs, this);
-      }, gSyncPane);
+      });
       Weave.Svc.Obs.remove(migrateTopic, this.updateMigrationState, this);
-    }, false);
+    }.bind(this), false);
+
+    XPCOMUtils.defineLazyGetter(this, '_stringBundle', () => {
+      return Services.strings.createBundle("chrome://browser/locale/preferences/preferences.properties");
+    });
 
+    XPCOMUtils.defineLazyGetter(this, '_accountsStringBundle', () => {
+      return Services.strings.createBundle("chrome://browser/locale/accounts.properties");
+    });
+
+    this.updateWeavePrefs();
+  },
+
+  updateWeavePrefs: function () {
     // ask the migration module to broadcast its current state (and nothing will
     // happen if it's not loaded - which is good, as that means no migration
     // is pending/necessary) - we don't want to suck that module in just to
     // find there's nothing to do.
     Services.obs.notifyObservers(null, "fxa-migration:state-request", null);
 
-    this._stringBundle =
-      Services.strings.createBundle("chrome://browser/locale/preferences/preferences.properties");
-    this.updateWeavePrefs();
-  },
-
-  updateWeavePrefs: function () {
     let service = Components.classes["@mozilla.org/weave/service;1"]
                   .getService(Components.interfaces.nsISupports)
                   .wrappedJSObject;
     // service.fxAccountsEnabled is false iff sync is already configured for
     // the legacy provider.
     if (service.fxAccountsEnabled) {
       // determine the fxa status...
       this.page = PAGE_PLEASE_WAIT;
@@ -188,37 +193,84 @@ let gSyncPane = {
       document.getElementById("accountName").value = Weave.Service.identity.account;
       document.getElementById("syncComputerName").value = Weave.Service.clientsEngine.localName;
       document.getElementById("tosPP-normal").hidden = this._usingCustomServer;
     }
   },
 
   updateMigrationState: function(subject, state) {
     let selIndex;
+    let container = document.getElementById("sync-migration");
     switch (state) {
-      case fxaMigrator.STATE_USER_FXA:
+      case fxaMigrator.STATE_USER_FXA: {
+        let sb = this._accountsStringBundle;
+        // There are 2 cases here - no email address means it is an offer on
+        // the first device (so the user is prompted to create an account).
+        // If there is an email address it is the "join the party" flow, so the
+        // user is prompted to sign in with the address they previously used.
+        let email = subject ? subject.QueryInterface(Components.interfaces.nsISupportsString).data : null;
+        let elt = document.getElementById("sync-migrate-upgrade-description");
+        elt.textContent = email ?
+                          sb.formatStringFromName("signInAfterUpgradeOnOtherDevice.description",
+                                                  [email], 1) :
+                          sb.GetStringFromName("needUserLong");
+
+        // The "upgrade" button.
+        let button = document.getElementById("sync-migrate-upgrade");
+        button.setAttribute("label",
+                            sb.GetStringFromName(email
+                                                 ? "signInAfterUpgradeOnOtherDevice.label"
+                                                 : "upgradeToFxA.label"));
+        button.setAttribute("accesskey",
+                            sb.GetStringFromName(email
+                                                 ? "signInAfterUpgradeOnOtherDevice.accessKey"
+                                                 : "upgradeToFxA.accessKey"));
+        // The "unlink" button - this is only shown for first migration
+        button = document.getElementById("sync-migrate-unlink");
+        if (email) {
+          button.hidden = true;
+        } else {
+          button.setAttribute("label", sb.GetStringFromName("unlinkMigration.label"));
+          button.setAttribute("accesskey", sb.GetStringFromName("unlinkMigration.accessKey"));
+        }
         selIndex = 0;
         break;
-      case fxaMigrator.STATE_USER_FXA_VERIFIED:
+      }
+      case fxaMigrator.STATE_USER_FXA_VERIFIED: {
+        let sb = this._accountsStringBundle;
         let email = subject.QueryInterface(Components.interfaces.nsISupportsString).data;
-        let fsfn = this._stringBundle.formatStringFromName;
-        let label = fsfn("fxaVerificationNeeded.label", [email], 1);
-        let elt = document.getElementById("sync-migrate-verify-label");
-        elt.setAttribute("value", label);
+        let label = sb.formatStringFromName("needVerifiedUserLong", [email], 1);
+        let elt = document.getElementById("sync-migrate-verify-description");
+        elt.textContent = label;
+        // The "resend" button.
+        let button = document.getElementById("sync-migrate-resend");
+        button.setAttribute("label", sb.GetStringFromName("resendVerificationEmail.label"));
+        button.setAttribute("accesskey", sb.GetStringFromName("resendVerificationEmail.accessKey"));
+        // The "forget" button.
+        button = document.getElementById("sync-migrate-forget");
+        button.setAttribute("label", sb.GetStringFromName("forgetMigration.label"));
+        button.setAttribute("accesskey", sb.GetStringFromName("forgetMigration.accessKey"));
         selIndex = 1;
         break;
+      }
       default:
         if (state) { // |null| is expected, but everything else is not.
           Cu.reportError("updateMigrationState has unknown state: " + state);
         }
-        document.getElementById("sync-migration").hidden = true;
+        if (!container.hidden) {
+          window.innerHeight -= container.clientHeight;
+          container.hidden = true;
+        }
         return;
     }
-    document.getElementById("sync-migration").hidden = false;
     document.getElementById("sync-migration-deck").selectedIndex = selIndex;
+    if (container.hidden) {
+      container.hidden = false;
+      window.innerHeight += container.clientHeight;
+    }
   },
 
   startOver: function (showDialog) {
     if (showDialog) {
       let flags = Services.prompt.BUTTON_POS_0 * Services.prompt.BUTTON_TITLE_IS_STRING +
                   Services.prompt.BUTTON_POS_1 * Services.prompt.BUTTON_TITLE_CANCEL + 
                   Services.prompt.BUTTON_POS_1_DEFAULT;
       let buttonChoice =
@@ -393,22 +445,45 @@ let gSyncPane = {
   },
 
   resetSync: function () {
     this.openSetup("reset");
   },
 
   // click handlers for the FxA migration.
   migrateUpgrade: function() {
-    fxaMigrator.getFxAccountOptions().then(({url, options}) => {
+    fxaMigrator.getFxAccountCreationOptions().then(({url, options}) => {
       this.openContentInBrowser(url, options);
     });
   },
 
   migrateForget: function() {
     fxaMigrator.forgetFxAccount();
   },
 
   migrateResend: function() {
     fxaMigrator.resendVerificationMail(window);
   },
+
+  // When the "Unlink" button in the migration header is selected we display
+  // a slightly different message.
+  startOverMigration: function () {
+    let flags = Services.prompt.BUTTON_POS_0 * Services.prompt.BUTTON_TITLE_IS_STRING +
+                Services.prompt.BUTTON_POS_1 * Services.prompt.BUTTON_TITLE_CANCEL +
+                Services.prompt.BUTTON_POS_1_DEFAULT;
+    let sb = this._accountsStringBundle;
+    let buttonChoice =
+      Services.prompt.confirmEx(window,
+                                sb.GetStringFromName("unlinkVerificationTitle"),
+                                sb.GetStringFromName("unlinkVerificationDescription"),
+                                flags,
+                                sb.GetStringFromName("unlinkVerificationConfirm"),
+                                null, null, null, {});
+
+    // If the user selects cancel, just bail
+    if (buttonChoice == 1)
+      return;
+
+    Weave.Service.startOver();
+    this.updateWeavePrefs();
+  },
 };
 
--- a/browser/components/preferences/sync.xul
+++ b/browser/components/preferences/sync.xul
@@ -36,33 +36,32 @@
     <script type="application/javascript"
             src="chrome://browser/content/sync/utils.js"/>
 
       <vbox id="sync-migration" flex="1" hidden="true">
 
         <deck id="sync-migration-deck">
           <!-- When we are in the "need FxA user" state -->
           <hbox align="center">
-            <label>&migrate.upgradeNeeded;</label>
+            <description id="sync-migrate-upgrade-description" flex="1"/>
             <spacer flex="1"/>
+            <button id="sync-migrate-unlink"
+                    onclick="event.stopPropagation(); gSyncPane.startOverMigration();"/>
             <button id="sync-migrate-upgrade"
-                    onclick="event.stopPropagation(); gSyncPane.migrateUpgrade();"
-                    label="&migrate.upgradeButton;"/>
+                    onclick="event.stopPropagation(); gSyncPane.migrateUpgrade();"/>
           </hbox>
 
           <!-- When we are in the "need the user to be verified" state -->
           <hbox align="center">
-            <label id="sync-migrate-verify-label"/>
+            <description id="sync-migrate-verify-description" flex="1"/>
             <spacer flex="1"/>
             <button id="sync-migrate-forget"
-                    onclick="event.stopPropagation(); gSyncPane.migrateForget();"
-                    label="&migrate.verifyForgetButton;"/>
+                    onclick="event.stopPropagation(); gSyncPane.migrateForget();"/>
             <button id="sync-migrate-resend"
-                    onclick="event.stopPropagation(); gSyncPane.migrateResend();"
-                    label="&migrate.verifyResendButton;"/>
+                    onclick="event.stopPropagation(); gSyncPane.migrateResend();"/>
           </hbox>
         </deck>
       </vbox>
 
       <deck id="weavePrefsDeck">
 
         <!-- These panels are for the "legacy" sync provider -->
         <vbox id="noAccount" align="center">
--- a/browser/themes/linux/preferences/preferences.css
+++ b/browser/themes/linux/preferences/preferences.css
@@ -196,14 +196,18 @@ label.small {
   border: 1px solid rgba(0, 0, 0, 0.32);
   background-color: InfoBackground;
   color: InfoText;
   text-shadow: none;
   margin: 5px 0 0 0;
   animation: fadein 3000ms;
 }
 
+#sync-migration description {
+  margin: 8px;
+}
+
 @keyframes fadein {
   from { opacity: 0; }
   to   { opacity: 1; }
 }
 
 %endif
--- a/browser/themes/osx/preferences/preferences.css
+++ b/browser/themes/osx/preferences/preferences.css
@@ -257,14 +257,18 @@ html|a.inline-link:-moz-focusring {
   border: 1px solid rgba(0, 0, 0, 0.32);
   background-color: InfoBackground;
   color: InfoText;
   text-shadow: none;
   margin: 5px 0 0 0;
   animation: fadein 3000ms;
 }
 
+#sync-migration description {
+  margin: 8px;
+}
+
 @keyframes fadein {
   from { opacity: 0; }
   to   { opacity: 1; }
 }
 
 %endif
--- a/browser/themes/windows/preferences/preferences.css
+++ b/browser/themes/windows/preferences/preferences.css
@@ -183,14 +183,18 @@ label.small {
   border: 1px solid rgba(0, 0, 0, 0.32);
   background-color: InfoBackground;
   color: InfoText;
   text-shadow: none;
   margin: 5px 0 0 0;
   animation: fadein 3000ms;
 }
 
+#sync-migration description {
+  margin: 8px;
+}
+
 @keyframes fadein {
   from { opacity: 0; }
   to   { opacity: 1; }
 }
 
 %endif
--- a/services/sync/modules/FxaMigrator.jsm
+++ b/services/sync/modules/FxaMigrator.jsm
@@ -365,36 +365,36 @@ Migrator.prototype = {
    */
 
   // Open a UI for the user to create a Firefox Account.  This should only be
   // called while we are in the STATE_USER_FXA state.  When the user completes
   // the creation we'll see an ONLOGIN_NOTIFICATION notification from FxA and
   // we'll move to either the STATE_USER_FXA_VERIFIED state or we'll just
   // complete the migration if they login as an already verified user.
   createFxAccount: Task.async(function* (win) {
-    let {url, options} = yield this.getFxAccountOptions();
+    let {url, options} = yield this.getFxAccountCreationOptions();
     win.switchToTabHavingURI(url, true, options);
     // An FxA observer will fire when the user completes this, which will
     // cause us to move to the next "user blocked" state and notify via our
     // observer notification.
   }),
 
   // Returns an object with properties "url" and "options", suitable for
   // opening FxAccounts to create/signin to FxA suitable for the migration
   // state.  The caller of this is responsible for the actual opening of the
   // page.
   // This should only be called while we are in the STATE_USER_FXA state.  When
   // the user completes the creation we'll see an ONLOGIN_NOTIFICATION
   // notification from FxA and we'll move to either the STATE_USER_FXA_VERIFIED
   // state or we'll just complete the migration if they login as an already
   // verified user.
-  getFxAccountOptions: Task.async(function* (win) {
+  getFxAccountCreationOptions: Task.async(function* (win) {
     // warn if we aren't in the expected state - but go ahead anyway!
     if (this._state != this.STATE_USER_FXA) {
-      this.log.warn("createFxAccount called in an unexpected state: ${}", this._state);
+      this.log.warn("getFxAccountCreationOptions called in an unexpected state: ${}", this._state);
     }
     // We need to obtain the sentinel and apply any prefs that might be
     // specified *before* attempting to setup FxA as the prefs might
     // specify custom servers etc.
     let sentinel = yield this._getSyncMigrationSentinel();
     if (sentinel && sentinel.prefs) {
       this._applySentinelPrefs(sentinel.prefs);
     }
@@ -420,17 +420,17 @@ Migrator.prototype = {
   // Ask the FxA servers to re-send a verification mail for the currently
   // logged in user. This should only be called while we are in the
   // STATE_USER_FXA_VERIFIED state.  When the user clicks on the link in
   // the mail we should see an ONVERIFIED_NOTIFICATION which will cause us
   // to complete the migration.
   resendVerificationMail: Task.async(function * (win) {
     // warn if we aren't in the expected state - but go ahead anyway!
     if (this._state != this.STATE_USER_FXA_VERIFIED) {
-      this.log.warn("createFxAccount called in an unexpected state: ${}", this._state);
+      this.log.warn("resendVerificationMail called in an unexpected state: ${}", this._state);
     }
     let ok = true;
     try {
       yield fxAccounts.resendVerificationEmail();
     } catch (ex) {
       this.log.error("Failed to resend verification mail: ${}", ex);
       ok = false;
     }
@@ -455,17 +455,17 @@ Migrator.prototype = {
 
   // "forget" about the current Firefox account. This should only be called
   // while we are in the STATE_USER_FXA_VERIFIED state.  After this we will
   // see an ONLOGOUT_NOTIFICATION, which will cause the migrator to return back
   // to the STATE_USER_FXA state, from where they can choose a different account.
   forgetFxAccount: Task.async(function * () {
     // warn if we aren't in the expected state - but go ahead anyway!
     if (this._state != this.STATE_USER_FXA_VERIFIED) {
-      this.log.warn("createFxAccount called in an unexpected state: ${}", this._state);
+      this.log.warn("forgetFxAccount called in an unexpected state: ${}", this._state);
     }
     return fxAccounts.signOut();
   }),
 
 }
 
 // We expose a singleton
 this.EXPORTED_SYMBOLS = ["fxaMigrator"];