Bug 967674 - Port new Fxa sync options work to in-content prefs. r=markh, a=sledru
authorRichard Marti <richard.marti@gmail.com>
Sat, 29 Mar 2014 11:22:10 +0100
changeset 183668 c8bcfc32f855
parent 183667 7be150811dd8
child 183669 ee6aea5824b7
push id3441
push userryanvm@gmail.com
push date2014-04-08 16:59 +0000
treeherdermozilla-beta@30c45853f8cb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmarkh, sledru
bugs967674
milestone29.0
Bug 967674 - Port new Fxa sync options work to in-content prefs. r=markh, a=sledru
browser/components/preferences/in-content/sync.js
browser/components/preferences/in-content/sync.xul
--- a/browser/components/preferences/in-content/sync.js
+++ b/browser/components/preferences/in-content/sync.js
@@ -1,17 +1,33 @@
 /* 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/. */
 
 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", {});
+});
 
 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.
+// 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;
@@ -38,42 +54,46 @@ let gSyncPane = {
                                 .getService(Components.interfaces.nsISupports)
                                 .wrappedJSObject;
 
     if (xps.ready) {
       this._init();
       return;
     }
 
+    // it may take some time before we can determine what provider to use
+    // and the state of that provider, so show the "please wait" page.
+    this.page = PAGE_PLEASE_WAIT;
+
     let onUnload = function () {
       window.removeEventListener("unload", onUnload, false);
       try {
         Services.obs.removeObserver(onReady, "weave:service:ready");
       } catch (e) {}
     };
 
     let onReady = function () {
       Services.obs.removeObserver(onReady, "weave:service:ready");
       window.removeEventListener("unload", onUnload, false);
       this._init();
     }.bind(this);
 
-
     Services.obs.addObserver(onReady, "weave:service:ready", false);
     window.addEventListener("unload", onUnload, false);
 
     xps.ensureLoaded();
   },
 
   _init: function () {
     let topics = ["weave:service:login:error",
                   "weave:service:login:finish",
-                  "weave:service:start-over",
+                  "weave:service:start-over:finish",
                   "weave:service:setup-complete",
-                  "weave:service:logout:finish"];
+                  "weave:service:logout:finish",
+                  FxAccountsCommon.ONVERIFIED_NOTIFICATION];
 
     // 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);
     window.addEventListener("unload", function() {
@@ -83,26 +103,83 @@ let gSyncPane = {
     }, false);
 
     this._stringBundle =
       Services.strings.createBundle("chrome://browser/locale/preferences/preferences.properties");
     this.updateWeavePrefs();
   },
 
   updateWeavePrefs: function () {
-    if (Weave.Status.service == Weave.CLIENT_NOT_CONFIGURED ||
-        Weave.Svc.Prefs.get("firstSync", "") == "notReady") {
+    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)
+        let fxaLoginStatus = document.getElementById("fxaLoginStatus");
+        let enginesListDisabled;
+        // Not Verfied implies login error state, so check that first.
+        if (!data.verified) {
+          fxaLoginStatus.selectedIndex = FXA_LOGIN_UNVERIFIED;
+          enginesListDisabled = true;
+        // So we think we are logged in, so login problems are next.
+        // (Although if the Sync identity manager is still initializing, we
+        // ignore login errors and assume all will eventually be good.)
+        // LOGIN_FAILED_LOGIN_REJECTED explicitly means "you must log back in".
+        // All other login failures are assumed to be transient and should go
+        // away by themselves, so aren't reflected here.
+        } else if (Weave.Status.login == Weave.LOGIN_FAILED_LOGIN_REJECTED) {
+          fxaLoginStatus.selectedIndex = FXA_LOGIN_FAILED;
+          enginesListDisabled = true;
+        // Else we must be golden (or in an error state we expect to magically
+        // resolve itself)
+        } else {
+          fxaLoginStatus.selectedIndex = FXA_LOGIN_VERIFIED;
+          enginesListDisabled = false;
+        }
+        document.getElementById("fxaEmailAddress1").textContent = data.email;
+        document.getElementById("fxaEmailAddress2").textContent = data.email;
+        document.getElementById("fxaEmailAddress3").textContent = data.email;
+        document.getElementById("fxaSyncComputerName").value = Weave.Service.clientsEngine.localName;
+        let engines = document.getElementById("fxaSyncEngines")
+        for (let checkbox of engines.querySelectorAll("checkbox")) {
+          checkbox.disabled = enginesListDisabled;
+        }
+
+        let checkbox = document.getElementById("fxa-pweng-chk");
+        let help = document.getElementById("fxa-pweng-help");
+        let allowPasswordsEngine = service.allowPasswordsEngine;
+
+        if (!allowPasswordsEngine) {
+          checkbox.checked = false;
+        }
+
+        checkbox.disabled = !allowPasswordsEngine;
+        help.hidden = allowPasswordsEngine;
+      });
+    // If fxAccountEnabled is false and we are in a "not configured" state,
+    // then fxAccounts is probably fully disabled rather than just unconfigured,
+    // so handle this case.  This block can be removed once we remove support
+    // for fxAccounts being disabled.
+    } else if (Weave.Status.service == Weave.CLIENT_NOT_CONFIGURED ||
+               Weave.Svc.Prefs.get("firstSync", "") == "notReady") {
       this.page = PAGE_NO_ACCOUNT;
-      let service = Components.classes["@mozilla.org/weave/service;1"]
-                    .getService(Components.interfaces.nsISupports)
-                    .wrappedJSObject;
-      // no concept of "pair" in an fxAccounts world.
-      if (service.fxAccountsEnabled) {
-        document.getElementById("pairDevice").hidden = true;
-      }
+    // else: sync was previously configured for the legacy provider, so we
+    // make the "old" panels available.
     } else if (Weave.Status.login == Weave.LOGIN_FAILED_INVALID_PASSPHRASE ||
                Weave.Status.login == Weave.LOGIN_FAILED_LOGIN_REJECTED) {
       this.needsUpdate();
     } 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").hidden = this._usingCustomServer;
@@ -142,43 +219,121 @@ let gSyncPane = {
     if (Weave.Status.login == Weave.LOGIN_FAILED_LOGIN_REJECTED)
       gSyncUtils.resetPassword();
     else
       gSyncUtils.resetPassphrase();
   },
 
   /**
    * Invoke the Sync setup wizard.
-   * 
+   *
    * @param wizardType
    *        Indicates type of wizard to launch:
    *          null    -- regular set up wizard
    *          "pair"  -- pair a device first
    *          "reset" -- reset sync
    */
   openSetup: function (wizardType) {
     let service = Components.classes["@mozilla.org/weave/service;1"]
                   .getService(Components.interfaces.nsISupports)
                   .wrappedJSObject;
 
     if (service.fxAccountsEnabled) {
-      let win = Services.wm.getMostRecentWindow("navigator:browser");
-      win.switchToTabHavingURI("about:accounts", true);
+      this.openContentInBrowser("about:accounts");
     } else {
       let win = Services.wm.getMostRecentWindow("Weave:AccountSetup");
       if (win)
         win.focus();
       else {
         window.openDialog("chrome://browser/content/sync/setup.xul",
                           "weaveSetup", "centerscreen,chrome,resizable=no",
                           wizardType);
       }
     }
   },
 
+  openContentInBrowser: function(url) {
+    let win = Services.wm.getMostRecentWindow("navigator:browser");
+    if (!win) {
+      // no window to use, so use _openLink to create a new one.  We don't
+      // always use that as it prefers to open a new window rather than use
+      // an existing one.
+      gSyncUtils._openLink(url);
+      return;
+    }
+    win.switchToTabHavingURI(url, true);
+    // seeing as we are doing this in a tab we close the prefs dialog.
+    window.close();
+  },
+
+  signUp: function() {
+    this.openContentInBrowser("about:accounts?action=signup");
+  },
+
+  signIn: function() {
+    this.openContentInBrowser("about:accounts?action=signin");
+  },
+
+  reSignIn: function() {
+    this.openContentInBrowser("about:accounts?action=reauth");
+  },
+
+  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 = this._stringBundle;
+        let title = sb.GetStringFromName("firefoxAccountsVerificationSentTitle");
+        let heading = sb.formatStringFromName("firefoxAccountsVerificationSentHeading",
+                                              [data.email], 1);
+        let description = sb.GetStringFromName("firefoxAccountVerificationSentDescription");
+
+        Services.prompt.alert(window, title, heading + "\n\n" + description);
+      });
+    });
+  },
+
+  openOldSyncSupportPage: function() {
+    let url = Services.urlFormatter.formatURLPref('app.support.baseURL') + "old-sync"
+    this.openContentInBrowser(url);
+  },
+
+  unlinkFirefoxAccount: function(confirm) {
+    if (confirm) {
+      // We use a string bundle shared with aboutAccounts.
+      let sb = Services.strings.createBundle("chrome://browser/locale/syncSetup.properties");
+      let continueLabel = sb.GetStringFromName("continue.label");
+      let title = sb.GetStringFromName("disconnect.verify.title");
+      let brandBundle = Services.strings.createBundle("chrome://branding/locale/brand.properties");
+      let brandShortName = brandBundle.GetStringFromName("brandShortName");
+      let body = sb.GetStringFromName("disconnect.verify.heading") +
+                 "\n\n" +
+                 sb.formatStringFromName("disconnect.verify.description",
+                                         [brandShortName], 1);
+      let ps = Services.prompt;
+      let buttonFlags = (ps.BUTTON_POS_0 * ps.BUTTON_TITLE_IS_STRING) +
+                        (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)
       win.focus();
     else 
       window.openDialog("chrome://browser/content/sync/quota.xul", "",
                         "centerscreen,chrome,dialog,modal");
   },
--- a/browser/components/preferences/in-content/sync.xul
+++ b/browser/components/preferences/in-content/sync.xul
@@ -24,16 +24,17 @@
 </preferences>
 
 <script type="application/javascript"
         src="chrome://browser/content/preferences/in-content/sync.js"/>
 <script type="application/javascript"
         src="chrome://browser/content/sync/utils.js"/>
 
 <deck id="weavePrefsDeck" data-category="paneSync" hidden="true">
+  <!-- These panels are for the "legacy" sync provider -->
   <vbox id="noAccount" align="center">
     <spacer flex="1"/>
     <description id="syncDesc">
       &weaveDesc.label;
     </description>
     <separator/>
     <label class="text-link"
            onclick="event.stopPropagation(); gSyncPane.openSetup(null);"
@@ -162,9 +163,146 @@
              onclick="gSyncPane.resetPass(); return false;"
              value="&resetPass.label;"/>
     </hbox>
     <label class="text-link"
            onclick="gSyncPane.startOver(true); return false;"
            value="&unlinkDevice.label;"/>
   </vbox>
 
+  <!-- These panels are for the Firefox Accounts identity provider -->
+  <vbox id="fxaDeterminingStatus" align="center">
+    <spacer flex="1"/>
+    <label value="&determiningAcctStatus.label;"/>
+    <spacer flex="1"/>
+  </vbox>
+
+  <vbox id="noFxaAccount">
+    <label>&welcome.description;</label>
+    <label class="text-link"
+            onclick="gSyncPane.signUp(); return false;"
+            value="&welcome.createAccount.label;"/>
+    <label class="text-link"
+            onclick="gSyncPane.signIn(); return false;"
+            value="&welcome.signIn.label;"/>
+    <separator/>
+    <label class="text-link"
+            onclick="gSyncPane.openOldSyncSupportPage(); return false;"
+            value="&welcome.useOldSync.label;"/>
+  </vbox>
+
+  <vbox id="hasFxaAccount">
+    <groupbox id="fxaGroup">
+      <caption label="&syncBrand.fxAccount.label;"/>
+
+      <deck id="fxaLoginStatus">
+
+        <!-- logged in and verified and all is good -->
+        <hbox>
+          <label id="fxaEmailAddress1"/>
+          <vbox>
+            <label class="text-link"
+                    onclick="gSyncPane.manageFirefoxAccount();"
+                    value="&manage.label;"/>
+          </vbox>
+          <spacer flex="1"/>
+          <vbox>
+            <button id="fxaUnlinkButton"
+                    oncommand="gSyncPane.unlinkFirefoxAccount(true);"
+                    label="&disconnect.label;"/>
+          </vbox>
+        </hbox>
+
+        <!-- logged in to an unverified account -->
+        <hbox flex="1">
+          <description>
+            &signedInUnverified.beforename.label;
+            <label id="fxaEmailAddress2"/>
+            &signedInUnverified.aftername.label;
+          </description>
+          <spacer flex="1"/>
+          <vbox align="end">
+            <button oncommand="gSyncPane.verifyFirefoxAccount();"
+                    label="&verify.label;"/>
+            <label class="text-link"
+                    onclick="/* no warning as account can't have previously synced */ gSyncPane.unlinkFirefoxAccount(false);"
+                    value="&forget.label;"/>
+          </vbox>
+        </hbox>
+
+        <!-- logged in locally but server rejected credentials -->
+        <hbox flex="1">
+          <description>
+            &signedInLoginFailure.beforename.label;
+            <label id="fxaEmailAddress3"/>
+            &signedInLoginFailure.aftername.label;
+          </description>
+          <spacer flex="1"/>
+          <vbox align="end">
+            <button oncommand="gSyncPane.reSignIn();"
+                    label="&signIn.label;"/>
+            <label class="text-link"
+                    onclick="gSyncPane.unlinkFirefoxAccount(true);"
+                    value="&forget.label;"/>
+          </vbox>
+        </hbox>
+      </deck>
+    </groupbox>
+
+    <groupbox id="syncOptions">
+      <caption label="&syncBrand.shortName.label;"/>
+      <hbox id="fxaSyncEngines">
+        <vbox>
+          <checkbox label="&engine.tabs.label;"
+                    accesskey="&engine.tabs.accesskey;"
+                    preference="engine.tabs"/>
+          <checkbox label="&engine.bookmarks.label;"
+                    accesskey="&engine.bookmarks.accesskey;"
+                    preference="engine.bookmarks"/>
+          <hbox>
+            <checkbox id="fxa-pweng-chk"
+                      label="&engine.passwords.label;"
+                      accesskey="&engine.passwords.accesskey;"
+                      preference="engine.passwords"/>
+
+            <vbox id="fxa-pweng-help">
+              <spacer flex="1"/>
+              <hbox id="fxa-pweng-help-link">
+                <label value=" ["/>
+                <label class="text-link" value="?"
+                        onclick="gSyncUtils.openMPInfoPage(event);"/>
+                <label value="]"/>
+              </hbox>
+              <spacer flex="1"/>
+            </vbox>
+          </hbox>
+          <checkbox label="&engine.history.label;"
+                    accesskey="&engine.history.accesskey;"
+                    preference="engine.history"/>
+          <checkbox label="&engine.addons.label;"
+                    accesskey="&engine.addons.accesskey;"
+                    preference="engine.addons"/>
+          <checkbox label="&engine.prefs.label;"
+                    accesskey="&engine.prefs.accesskey;"
+                    preference="engine.prefs"/>
+        </vbox>
+        <spacer/>
+      </hbox>
+    </groupbox>
+    <hbox align="center">
+      <label value="&syncDeviceName.label;"
+              accesskey="&syncDeviceName.accesskey;"
+              control="syncComputerName"/>
+      <textbox id="fxaSyncComputerName"
+                flex="1"
+                onchange="gSyncUtils.changeName(this)"/>
+    </hbox>
+    <spacer flex="1"/>
+    <hbox id="tosPP" pack="center">
+      <label class="text-link small"
+              onclick="event.stopPropagation();gSyncUtils.openToS();"
+              value="&prefs.tosLink.label;"/>
+      <label class="text-link small"
+              onclick="event.stopPropagation();gSyncUtils.openPrivacyPolicy();"
+              value="&fxaPrivacyNotice.link.label;"/>
+    </hbox>
+  </vbox>
 </deck>