Bug 1110336 - Update sync old-dialog-based preferences UI to match FxA migration flows, part 1. r=adw
authorMark Hammond <mhammond@skippinet.com.au>
Thu, 08 Jan 2015 15:03:22 -0800
changeset 248728 df2c68a00ffbcf52c5d1c2d86dbee5fb1fbe58c2
parent 248727 14e715ddc5ec94271cca2364578f916a10cd84fe
child 248729 0d113455b05c375db4e77cb2bef6fc87a402316a
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)
reviewersadw
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 1. r=adw
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
@@ -4,16 +4,22 @@
 
 Components.utils.import("resource://services-sync/main.js");
 Components.utils.import("resource://gre/modules/Services.jsm");
 
 XPCOMUtils.defineLazyGetter(this, "FxAccountsCommon", function () {
   return Components.utils.import("resource://gre/modules/FxAccountsCommon.js", {});
 });
 
+XPCOMUtils.defineLazyModuleGetter(this, "fxAccounts",
+  "resource://gre/modules/FxAccounts.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "fxaMigrator",
+  "resource://services-sync/FxaMigrator.jsm");
+
 const PAGE_NO_ACCOUNT = 0;
 const PAGE_HAS_ACCOUNT = 1;
 const PAGE_NEEDS_UPDATE = 2;
 const PAGE_PLEASE_WAIT = 3;
 const FXA_PAGE_LOGGED_OUT = 4;
 const FXA_PAGE_LOGGED_IN = 5;
 
 // Indexes into the "login status" deck.
@@ -84,44 +90,54 @@ let gSyncPane = {
 
   _init: function () {
     let topics = ["weave:service:login:error",
                   "weave:service:login:finish",
                   "weave:service:start-over:finish",
                   "weave:service:setup-complete",
                   "weave:service:logout:finish",
                   FxAccountsCommon.ONVERIFIED_NOTIFICATION];
+    let migrateTopic = "fxa-migration:state-changed";
 
     // Add the observers now and remove them on unload
     //XXXzpao This should use Services.obs.* but Weave's Obs does nice handling
     //        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) {
         Weave.Svc.Obs.remove(topic, this.updateWeavePrefs, this);
       }, gSyncPane);
+      Weave.Svc.Obs.remove(migrateTopic, this.updateMigrationState, this);
     }, false);
 
+    // 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;
-      Components.utils.import("resource://gre/modules/FxAccounts.jsm");
       fxAccounts.getSignedInUser().then(data => {
         if (!data) {
           this.page = FXA_PAGE_LOGGED_OUT;
           return;
         }
         this.page = FXA_PAGE_LOGGED_IN;
         // We are logged in locally, but maybe we are in a state where the
         // server rejected our credentials (eg, password changed on the server)
@@ -170,16 +186,41 @@ let gSyncPane = {
     } else {
       this.page = PAGE_HAS_ACCOUNT;
       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;
+    switch (state) {
+      case fxaMigrator.STATE_USER_FXA:
+        selIndex = 0;
+        break;
+      case fxaMigrator.STATE_USER_FXA_VERIFIED:
+        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);
+        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;
+        return;
+    }
+    document.getElementById("sync-migration").hidden = false;
+    document.getElementById("sync-migration-deck").selectedIndex = selIndex;
+  },
+
   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 =
         Services.prompt.confirmEx(window,
                                   this._stringBundle.GetStringFromName("syncUnlink.title"),
@@ -277,17 +318,16 @@ let gSyncPane = {
   },
 
   manageFirefoxAccount: function() {
     let url = Services.prefs.getCharPref("identity.fxaccounts.settings.uri");
     this.openContentInBrowser(url);
   },
 
   verifyFirefoxAccount: function() {
-    Components.utils.import("resource://gre/modules/FxAccounts.jsm");
     fxAccounts.resendVerificationEmail().then(() => {
       fxAccounts.getSignedInUser().then(data => {
         let sb = Services.strings.createBundle("chrome://browser/locale/accounts.properties");
         let title = sb.GetStringFromName("verificationSentTitle");
         let heading = sb.formatStringFromName("verificationSentHeading",
                                               [data.email], 1);
         let description = sb.GetStringFromName("verificationSentDescription");
 
@@ -318,17 +358,16 @@ let gSyncPane = {
                         (ps.BUTTON_POS_1 * ps.BUTTON_TITLE_CANCEL) +
                         ps.BUTTON_POS_1_DEFAULT;
       let pressed = Services.prompt.confirmEx(window, title, body, buttonFlags,
                                               continueLabel, null, null, null, {});
       if (pressed != 0) { // 0 is the "continue" button
         return;
       }
     }
-    Components.utils.import('resource://gre/modules/FxAccounts.jsm');
     fxAccounts.signOut().then(() => {
       this.updateWeavePrefs();
     });
   },
 
   openQuotaDialog: function () {
     let win = Services.wm.getMostRecentWindow("Sync:ViewQuota");
     if (win) {
@@ -351,10 +390,25 @@ let gSyncPane = {
       window.openDialog("chrome://browser/content/sync/addDevice.xul",
                         "syncAddDevice", "centerscreen,chrome,resizable=no");
     }
   },
 
   resetSync: function () {
     this.openSetup("reset");
   },
+
+  // click handlers for the FxA migration.
+  migrateUpgrade: function() {
+    fxaMigrator.getFxAccountOptions().then(({url, options}) => {
+      this.openContentInBrowser(url, options);
+    });
+  },
+
+  migrateForget: function() {
+    fxaMigrator.forgetFxAccount();
+  },
+
+  migrateResend: function() {
+    fxaMigrator.resendVerificationMail(window);
+  },
 };
 
--- a/browser/components/preferences/sync.xul
+++ b/browser/components/preferences/sync.xul
@@ -31,16 +31,41 @@
     </preferences>
 
 
     <script type="application/javascript"
             src="chrome://browser/content/preferences/sync.js"/>
     <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>
+            <spacer flex="1"/>
+            <button id="sync-migrate-upgrade"
+                    onclick="event.stopPropagation(); gSyncPane.migrateUpgrade();"
+                    label="&migrate.upgradeButton;"/>
+          </hbox>
+
+          <!-- When we are in the "need the user to be verified" state -->
+          <hbox align="center">
+            <label id="sync-migrate-verify-label"/>
+            <spacer flex="1"/>
+            <button id="sync-migrate-forget"
+                    onclick="event.stopPropagation(); gSyncPane.migrateForget();"
+                    label="&migrate.verifyForgetButton;"/>
+            <button id="sync-migrate-resend"
+                    onclick="event.stopPropagation(); gSyncPane.migrateResend();"
+                    label="&migrate.verifyResendButton;"/>
+          </hbox>
+        </deck>
+      </vbox>
 
       <deck id="weavePrefsDeck">
 
         <!-- These panels are for the "legacy" sync provider -->
         <vbox id="noAccount" align="center">
           <spacer flex="1"/>
           <description id="syncDesc">
             &weaveDesc.label;
--- a/browser/themes/linux/preferences/preferences.css
+++ b/browser/themes/linux/preferences/preferences.css
@@ -184,9 +184,26 @@ label.small {
   margin: 5px;
   line-height: 1.2em;
 }
 
 #noFxaAccount > label:first-child {
   margin-bottom: 0.6em;
 }
 
+/**
+ * Sync migration
+ */
+#sync-migration {
+  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;
+}
+
+@keyframes fadein {
+  from { opacity: 0; }
+  to   { opacity: 1; }
+}
+
 %endif
--- a/browser/themes/osx/preferences/preferences.css
+++ b/browser/themes/osx/preferences/preferences.css
@@ -245,9 +245,26 @@ html|a.inline-link:-moz-focusring {
   margin: 12px 4px;
   line-height: 1.2em;
 }
 
 #noFxaAccount > label:first-child {
   margin-bottom: 0.6em;
 }
 
+/**
+ * Sync migration
+ */
+#sync-migration {
+  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;
+}
+
+@keyframes fadein {
+  from { opacity: 0; }
+  to   { opacity: 1; }
+}
+
 %endif
--- a/browser/themes/windows/preferences/preferences.css
+++ b/browser/themes/windows/preferences/preferences.css
@@ -171,9 +171,26 @@ label.small {
   margin: 6px;
   line-height: 1.2em;
 }
 
 #noFxaAccount > label:first-child {
   margin-bottom: 0.6em;
 }
 
+/**
+ * Sync migration
+ */
+#sync-migration {
+  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;
+}
+
+@keyframes fadein {
+  from { opacity: 0; }
+  to   { opacity: 1; }
+}
+
 %endif
--- a/services/sync/modules/FxaMigrator.jsm
+++ b/services/sync/modules/FxaMigrator.jsm
@@ -365,16 +365,33 @@ 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();
+    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) {
     // 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);
     }
     // 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();
@@ -389,21 +406,20 @@ Migrator.prototype = {
     let tail = email ? "&email=" + encodeURIComponent(email) : "";
     // A special flag so server-side metrics can tell this is part of migration.
     tail += "&migration=sync11";
     // We want to ask FxA to offer a "Customize Sync" checkbox iff any engines
     // are disabled.
     let customize = !this._allEnginesEnabled();
     tail += "&customizeSync=" + customize;
 
-    win.switchToTabHavingURI("about:accounts?action=" + action + tail, true,
-                             {ignoreFragment: true, replaceQueryString: true});
-    // 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.
+    return {
+      url: "about:accounts?action=" + action + tail,
+      options: {ignoreFragment: true, replaceQueryString: true}
+    };
   }),
 
   // 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) {