Bug 1374501 - Allow extra sync engines to be activited using FxA Web Channels. r?markh draft
authorEdouard Oger <eoger@fastmail.com>
Wed, 21 Jun 2017 13:55:05 -0400
changeset 599037 44045b531bcaebb1da353b8573d9cc55f1efa3dd
parent 598150 2b07ef4f3381eafab2183a46704bed50800bede3
child 634662 d3ba0a101d1842bc0e6872c75684526cf26a8e8c
push id65406
push userbmo:eoger@fastmail.com
push dateThu, 22 Jun 2017 15:39:09 +0000
reviewersmarkh
bugs1374501
milestone56.0a1
Bug 1374501 - Allow extra sync engines to be activited using FxA Web Channels. r?markh MozReview-Commit-ID: HykDad81Ifh
services/fxaccounts/FxAccountsWebChannel.jsm
services/fxaccounts/tests/xpcshell/test_web_channel.js
--- a/services/fxaccounts/FxAccountsWebChannel.jsm
+++ b/services/fxaccounts/FxAccountsWebChannel.jsm
@@ -33,16 +33,21 @@ const COMMAND_LOGIN                = "fx
 const COMMAND_LOGOUT               = "fxaccounts:logout";
 const COMMAND_DELETE               = "fxaccounts:delete";
 const COMMAND_SYNC_PREFERENCES     = "fxaccounts:sync_preferences";
 const COMMAND_CHANGE_PASSWORD      = "fxaccounts:change_password";
 const COMMAND_FXA_STATUS           = "fxaccounts:fxa_status";
 
 const PREF_LAST_FXA_USER           = "identity.fxaccounts.lastSignedInUserHash";
 
+// These engines were added years after Sync had been introduced, they need
+// special handling since they are system add-ons and are un-available on
+// older versions of Firefox.
+const EXTRA_ENGINES = ["addresses", "creditcards"];
+
 /**
  * A helper function that extracts the message and stack from an error object.
  * Returns a `{ message, stack }` tuple. `stack` will be null if the error
  * doesn't have a stack trace.
  */
 function getErrorDetails(error) {
   let details = { message: String(error), stack: null };
 
@@ -275,16 +280,27 @@ this.FxAccountsWebChannelHelpers.prototy
    * @param accountData the user's account data and credentials
    */
   login(accountData) {
 
     // We don't act on customizeSync anymore, it used to open a dialog inside
     // the browser to selecte the engines to sync but we do it on the web now.
     delete accountData.customizeSync;
 
+    if (accountData.offeredSyncEngines) {
+      EXTRA_ENGINES.forEach(engine => {
+        if (accountData.offeredSyncEngines.includes(engine) &&
+            !accountData.declinedSyncEngines.includes(engine)) {
+          // These extra engines are disabled by default.
+          Services.prefs.setBoolPref(`services.sync.engine.${engine}`, true);
+        }
+      });
+      delete accountData.offeredSyncEngines;
+    }
+
     if (accountData.declinedSyncEngines) {
       let declinedSyncEngines = accountData.declinedSyncEngines;
       log.debug("Received declined engines", declinedSyncEngines);
       Weave.Service.engineManager.setDeclined(declinedSyncEngines);
       declinedSyncEngines.forEach(engine => {
         Services.prefs.setBoolPref("services.sync.engine." + engine, false);
       });
       delete accountData.declinedSyncEngines;
@@ -379,20 +395,33 @@ this.FxAccountsWebChannelHelpers.prototy
           sessionToken: userData.sessionToken,
           uid: userData.uid,
           verified: userData.verified
         };
       }
     }
 
     return {
-      signedInUser
+      signedInUser,
+      capabilities: {
+        engines: this._getAvailableExtraEngines()
+      }
     };
   },
 
+  _getAvailableExtraEngines() {
+    return EXTRA_ENGINES.filter(engineName => {
+      try {
+        return Services.prefs.getBoolPref(`services.sync.engine.${engineName}.available`);
+      } catch (e) {
+        return false;
+      }
+    });
+  },
+
   changePassword(credentials) {
     // If |credentials| has fields that aren't handled by accounts storage,
     // updateUserAccountData will throw - mainly to prevent errors in code
     // that hard-codes field names.
     // However, in this case the field names aren't really in our control.
     // We *could* still insist the server know what fields names are valid,
     // but that makes life difficult for the server when Firefox adds new
     // features (ie, new fields) - forcing the server to track a map of
--- a/services/fxaccounts/tests/xpcshell/test_web_channel.js
+++ b/services/fxaccounts/tests/xpcshell/test_web_channel.js
@@ -254,16 +254,19 @@ add_test(function test_fxa_status_messag
     helpers: {
       async getFxaStatus() {
         return {
           signedInUser: {
             email: "testuser@testuser.com",
             sessionToken: "session-token",
             uid: "uid",
             verified: true
+          },
+          capabilities: {
+            engines: ["creditcards", "addresses"]
           }
         };
       }
     }
   });
 
   channel._channel = {
     send(response, sendingContext) {
@@ -272,16 +275,18 @@ add_test(function test_fxa_status_messag
 
       let signedInUser = response.data.signedInUser;
       do_check_true(!!signedInUser);
       do_check_eq(signedInUser.email, "testuser@testuser.com");
       do_check_eq(signedInUser.sessionToken, "session-token");
       do_check_eq(signedInUser.uid, "uid");
       do_check_eq(signedInUser.verified, true);
 
+      deepEqual(response.data.capabilities.engines, ["creditcards", "addresses"]);
+
       run_next_test();
     }
   };
 
   channel._channelCallback(WEBCHANNEL_ID, mockMessage, mockSendingContext);
 });
 
 add_test(function test_unrecognized_message() {
@@ -412,32 +417,93 @@ add_task(function* test_helpers_login_wi
   yield helpers.login({
     email: "testuser@testuser.com",
     verifiedCanLinkAccount: true,
     customizeSync: true,
     declinedSyncEngines: ["addons", "prefs"]
   });
 });
 
+add_task(async function test_helpers_login_with_offered_sync_engines() {
+  let helpers;
+  const setSignedInUserCalled = new Promise(resolve => {
+    helpers = new FxAccountsWebChannelHelpers({
+      fxAccounts: {
+        async setSignedInUser(accountData) {
+          resolve(accountData);
+        }
+      }
+    });
+  });
+
+  Services.prefs.setBoolPref("services.sync.engine.creditcards", false);
+  Services.prefs.setBoolPref("services.sync.engine.addresses", false);
+
+  await helpers.login({
+    email: "testuser@testuser.com",
+    verifiedCanLinkAccount: true,
+    customizeSync: true,
+    declinedSyncEngines: ["addresses"],
+    offeredSyncEngines: ["creditcards", "addresses"]
+  });
+
+  const accountData = await setSignedInUserCalled;
+
+  // ensure fxAccounts is informed of the new user being signed in.
+  equal(accountData.email, "testuser@testuser.com");
+
+  // offeredSyncEngines should be stripped in the data.
+  ok(!("offeredSyncEngines" in accountData));
+  // credit cards was offered but not declined.
+  equal(Services.prefs.getBoolPref("services.sync.engine.creditcards"), true);
+  // addresses was offered and explicitely declined.
+  equal(Services.prefs.getBoolPref("services.sync.engine.addresses"), false);
+});
+
 add_test(function test_helpers_open_sync_preferences() {
   let helpers = new FxAccountsWebChannelHelpers({
     fxAccounts: {
     }
   });
 
   let mockBrowser = {
     loadURI(uri) {
       do_check_eq(uri, "about:preferences?entrypoint=fxa%3Averification_complete#sync");
       run_next_test();
     }
   };
 
   helpers.openSyncPreferences(mockBrowser, "fxa:verification_complete");
 });
 
+add_task(async function test_helpers_getFxAStatus_extra_engines() {
+  let helpers = new FxAccountsWebChannelHelpers({
+    fxAccounts: {
+      getSignedInUser() {
+        return Promise.resolve({
+          email: "testuser@testuser.com",
+          kA: "kA",
+          kb: "kB",
+          sessionToken: "sessionToken",
+          uid: "uid",
+          verified: true
+        });
+      }
+    }
+  });
+
+  Services.prefs.setBoolPref("services.sync.engine.creditcards.available", true);
+  // Not defining "services.sync.engine.addresses.available" on purpose.
+
+  let fxaStatus = await helpers.getFxaStatus("sync", mockSendingContext);
+  ok(!!fxaStatus);
+  ok(!!fxaStatus.signedInUser);
+  deepEqual(fxaStatus.capabilities.engines, ["creditcards"]);
+});
+
 
 add_task(function* test_helpers_getFxaStatus_allowed_signedInUser() {
   let wasCalled = {
     getSignedInUser: false,
     shouldAllowFxaStatus: false
   };
 
   let helpers = new FxAccountsWebChannelHelpers({