Bug 943998 - Need debug and uri pref for FirefoxAccounts. r=markh
authorFernando Jiménez <ferjmoreno@gmail.com>
Fri, 13 Dec 2013 12:37:55 +0100
changeset 160325 b3d70eda988d109045abba665677f1e279112841
parent 160324 da3dcd40f826241e1244323c4504aa7be90bb969
child 160326 44aefcd6f082521dc1e79421e5435e48bb976b5e
push idunknown
push userunknown
push dateunknown
reviewersmarkh
bugs943998
milestone29.0a1
Bug 943998 - Need debug and uri pref for FirefoxAccounts. r=markh
b2g/components/FxAccountsMgmtService.jsm
b2g/components/FxAccountsUIGlue.js
services/fxaccounts/FxAccounts.jsm
services/fxaccounts/FxAccountsClient.jsm
services/fxaccounts/FxAccountsCommon.js
services/fxaccounts/FxAccountsConsts.js
services/fxaccounts/FxAccountsManager.jsm
services/fxaccounts/moz.build
services/fxaccounts/tests/xpcshell/test_manager.js
--- a/b2g/components/FxAccountsMgmtService.jsm
+++ b/b2g/components/FxAccountsMgmtService.jsm
@@ -20,16 +20,17 @@
 
 this.EXPORTED_SYMBOLS = ["FxAccountsMgmtService"];
 
 const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/ObjectWrapper.jsm");
+Cu.import("resource://gre/modules/FxAccountsCommon.js");
 
 XPCOMUtils.defineLazyModuleGetter(this, "FxAccountsManager",
   "resource://gre/modules/FxAccountsManager.jsm");
 
 this.FxAccountsMgmtService = {
 
   _sendChromeEvent: function(aMsg) {
     if (!this._shell) {
@@ -61,16 +62,17 @@ this.FxAccountsMgmtService = {
     let content = this._shell.contentBrowser.contentWindow;
     content.addEventListener("mozFxAccountsContentEvent",
                              FxAccountsMgmtService);
     Services.obs.removeObserver(this, "content-start");
   },
 
   handleEvent: function(aEvent) {
     let msg = aEvent.detail;
+    log.debug("Got content msg " + JSON.stringify(msg));
     let self = FxAccountsMgmtService;
 
     if (!msg.id) {
       return;
     }
 
     let data = msg.data;
     if (!data) {
--- a/b2g/components/FxAccountsUIGlue.js
+++ b/b2g/components/FxAccountsUIGlue.js
@@ -5,16 +5,17 @@
 "use strict"
 
 const { interfaces: Ci, utils: Cu } = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/ObjectWrapper.jsm");
 Cu.import("resource://gre/modules/Promise.jsm");
+Cu.import("resource://gre/modules/FxAccountsCommon.js");
 
 XPCOMUtils.defineLazyServiceGetter(this, "uuidgen",
                                    "@mozilla.org/uuid-generator;1",
                                    "nsIUUIDGenerator");
 
 function FxAccountsUIGlue() {
 }
 
@@ -38,29 +39,33 @@ FxAccountsUIGlue.prototype = {
       let msg = result.detail;
       if (!msg || !msg.id || msg.id != id) {
         deferred.reject("InternalErrorWrongContentEvent");
         content.removeEventListener("mozFxAccountsRPContentEvent",
                                     onContentEvent);
         return;
       }
 
+      log.debug("Got content event " + JSON.stringify(msg));
+
       if (msg.error) {
         deferred.reject(msg);
       } else {
         deferred.resolve(msg.result);
       }
       content.removeEventListener("mozFxAccountsRPContentEvent",
                                   onContentEvent);
     });
 
-    this._browser.shell.sendCustomEvent("mozFxAccountsRPChromeEvent", {
-      method: "openFlow",
-      id: id
-    });
+    let detail = {
+       method: "openFlow",
+       id: id
+    };
+    log.debug("Send chrome event " + JSON.stringify(detail));
+    this._browser.shell.sendCustomEvent("mozFxAccountsRPChromeEvent", detail);
 
     return deferred.promise;
   },
 
   classID: Components.ID("{51875c14-91d7-4b8c-b65d-3549e101228c}"),
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIFxAccountsUIGlue])
 };
--- a/services/fxaccounts/FxAccounts.jsm
+++ b/services/fxaccounts/FxAccounts.jsm
@@ -11,37 +11,21 @@ Cu.import("resource://gre/modules/Promis
 Cu.import("resource://gre/modules/osfile.jsm");
 Cu.import("resource://services-common/utils.js");
 Cu.import("resource://services-crypto/utils.js");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Timer.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
 Cu.import("resource://gre/modules/FxAccountsClient.jsm");
-Cu.import("resource://gre/modules/FxAccountsConsts.js");
+Cu.import("resource://gre/modules/FxAccountsCommon.js");
 
 XPCOMUtils.defineLazyModuleGetter(this, "jwcrypto",
                                   "resource://gre/modules/identity/jwcrypto.jsm");
 
-// loglevel preference should be one of: "FATAL", "ERROR", "WARN", "INFO",
-// "CONFIG", "DEBUG", "TRACE" or "ALL". We will be logging error messages by
-// default.
-const PREF_LOG_LEVEL = "identity.fxaccounts.loglevel";
-try {
-  this.LOG_LEVEL =
-    Services.prefs.getPrefType(PREF_LOG_LEVEL) == Ci.nsIPrefBranch.PREF_STRING
-    && Services.prefs.getCharPref(PREF_LOG_LEVEL);
-} catch (e) {
-  this.LOG_LEVEL = Log.Level.Error;
-}
-
-let log = Log.repository.getLogger("Services.FxAccounts");
-log.level = LOG_LEVEL;
-log.addAppender(new Log.ConsoleAppender(new Log.BasicFormatter()));
-
 InternalMethods = function(mock) {
   this.cert = null;
   this.keyPair = null;
   this.signedInUser = null;
   this.version = DATA_FORMAT_VERSION;
 
   // Make a local copy of these constants so we can mock it in testing
   this.POLL_STEP = POLL_STEP;
--- a/services/fxaccounts/FxAccountsClient.jsm
+++ b/services/fxaccounts/FxAccountsClient.jsm
@@ -5,16 +5,17 @@
 this.EXPORTED_SYMBOLS = ["FxAccountsClient"];
 
 const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 
 Cu.import("resource://gre/modules/Promise.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://services-common/utils.js");
 Cu.import("resource://services-crypto/utils.js");
+Cu.import("resource://gre/modules/FxAccountsCommon.js");
 
 // Default can be changed by the preference 'identity.fxaccounts.auth.uri'
 let _host = "https://api-accounts.dev.lcip.org/v1";
 try {
   _host = Services.prefs.getCharPref("identity.fxaccounts.auth.uri");
 } catch(keepDefault) {}
 
 const HOST = _host;
@@ -278,16 +279,19 @@ this.FxAccountsClient.prototype = {
     let payload;
 
     xhr.mozBackgroundRequest = true;
 
     if (jsonPayload) {
       payload = JSON.stringify(jsonPayload);
     }
 
+    log.debug("(HAWK request) - Path: " + path + " - Method: " + method +
+              " - Payload: " + payload);
+
     xhr.open(method, URI);
     xhr.channel.loadFlags = Ci.nsIChannel.LOAD_BYPASS_CACHE |
                             Ci.nsIChannel.INHIBIT_CACHING;
 
     // When things really blow up, reconstruct an error object that follows the general format
     // of the server on error responses.
     function constructError(err) {
       return { error: err, message: xhr.statusText, code: xhr.status, errno: xhr.status };
@@ -295,22 +299,26 @@ this.FxAccountsClient.prototype = {
 
     xhr.onerror = function() {
       deferred.reject(constructError('Request failed'));
     };
 
     xhr.onload = function onload() {
       try {
         let response = JSON.parse(xhr.responseText);
+        log.debug("(Response) Code: " + xhr.status + " - Status text: " +
+                  xhr.statusText + " - Response text: " + xhr.responseText);
         if (xhr.status !== 200 || response.error) {
           // In this case, the response is an object with error information.
           return deferred.reject(response);
         }
         deferred.resolve(response);
       } catch (e) {
+        log.error("(Response) Code: " + xhr.status + " - Status text: " +
+                  xhr.statusText);
         deferred.reject(constructError(e));
       }
     };
 
     let uri = Services.io.newURI(URI, null, null);
 
     if (credentials) {
       let header = CryptoUtils.computeHAWK(uri, method, {
rename from services/fxaccounts/FxAccountsConsts.js
rename to services/fxaccounts/FxAccountsCommon.js
--- a/services/fxaccounts/FxAccountsConsts.js
+++ b/services/fxaccounts/FxAccountsCommon.js
@@ -1,12 +1,39 @@
 /* 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/. */
 
+const { interfaces: Ci, utils: Cu } = Components;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/Log.jsm");
+
+// loglevel should be one of "Fatal", "Error", "Warn", "Info", "Config",
+// "Debug", "Trace" or "All". If none is specified, "Error" will be used by
+// default.
+const PREF_LOG_LEVEL = "identity.fxaccounts.loglevel";
+
+XPCOMUtils.defineLazyGetter(this, 'log', function() {
+  let log = Log.repository.getLogger("FirefoxAccounts");
+  log.addAppender(new Log.DumpAppender());
+  log.level = Log.Level.Error;
+  try {
+    let level =
+      Services.prefs.getPrefType(PREF_LOG_LEVEL) == Ci.nsIPrefBranch.PREF_STRING
+      && Services.prefs.getCharPref(PREF_LOG_LEVEL);
+    log.level = Log.Level[level] || Log.Level.Error;
+  } catch (e) {
+    log.error(e);
+  }
+
+  return log;
+});
+
 this.DATA_FORMAT_VERSION = 1;
 this.DEFAULT_STORAGE_FILENAME = "signedInUser.json";
 
 // Token life times.
 this.ASSERTION_LIFETIME = 1000 * 60 * 5;    // 5 minutes
 this.CERT_LIFETIME      = 1000 * 3600 * 6;  // 6 hours
 this.KEY_LIFETIME       = 1000 * 3600 * 12; // 12 hours
 
--- a/services/fxaccounts/FxAccountsManager.jsm
+++ b/services/fxaccounts/FxAccountsManager.jsm
@@ -14,17 +14,17 @@
 this.EXPORTED_SYMBOLS = ["FxAccountsManager"];
 
 const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/FxAccounts.jsm");
 Cu.import("resource://gre/modules/Promise.jsm");
-Cu.import("resource://gre/modules/FxAccountsConsts.js");
+Cu.import("resource://gre/modules/FxAccountsCommon.js");
 
 XPCOMUtils.defineLazyModuleGetter(this, "FxAccountsClient",
   "resource://gre/modules/FxAccountsClient.jsm");
 
 this.FxAccountsManager = {
 
   // We don't really need to save fxAccounts instance but this way we allow
   // to mock FxAccounts from tests.
@@ -45,17 +45,19 @@ this.FxAccountsManager = {
       verified: this._activeSession.verified
     }
   },
 
   _getError: function(aServerResponse) {
     if (!aServerResponse || !aServerResponse.error || !aServerResponse.error.errno) {
       return;
     }
-    return SERVER_ERRNO_TO_ERROR[aServerResponse.error.errno];
+    let error = SERVER_ERRNO_TO_ERROR[aServerResponse.error.errno];
+    log.error(error);
+    return error;
   },
 
   _serverError: function(aServerResponse) {
     let error = this._getError({ error: aServerResponse });
     return Promise.reject({
       error: error ? error : ERROR_SERVER_ERROR,
       details: aServerResponse
     });
@@ -64,73 +66,81 @@ this.FxAccountsManager = {
   // As we do with _fxAccounts, we don't really need this factory, but this way
   // we allow tests to mock FxAccountsClient.
   _createFxAccountsClient: function() {
     return new FxAccountsClient();
   },
 
   _signInSignUp: function(aMethod, aAccountId, aPassword) {
     if (Services.io.offline) {
+      log.error(ERROR_OFFLINE);
       return Promise.reject({
         error: ERROR_OFFLINE
       });
     }
 
     if (!aAccountId) {
+      log.error(ERROR_INVALID_ACCOUNTID);
       return Promise.reject({
         error: ERROR_INVALID_ACCOUNTID
       });
     }
 
     if (!aPassword) {
+      log.error(ERROR_INVALID_PASSWORD);
       return Promise.reject({
         error: ERROR_INVALID_PASSWORD
       });
     }
 
     // Check that there is no signed in account first.
     if (this._activeSession) {
+      log.error(ERROR_ALREADY_SIGNED_IN_USER);
       return Promise.reject({
         error: ERROR_ALREADY_SIGNED_IN_USER,
         details: {
           user: this._user
         }
       });
     }
 
     let client = this._createFxAccountsClient();
     return this._fxAccounts.getSignedInUser().then(
       user => {
         if (user) {
+          log.error(ERROR_ALREADY_SIGNED_IN_USER);
           return Promise.reject({
             error: ERROR_ALREADY_SIGNED_IN_USER,
             details: {
               user: user
             }
           });
         }
         return client[aMethod](aAccountId, aPassword);
       }
     ).then(
       user => {
         let error = this._getError(user);
         if (!user || !user.uid || !user.sessionToken || error) {
+          log.error(error ? error : ERROR_INTERNAL_INVALID_USER);
           return Promise.reject({
             error: error ? error : ERROR_INTERNAL_INVALID_USER,
             details: {
               user: user
             }
           });
         }
 
         // Save the credentials of the signed in user.
         user.email = aAccountId;
         return this._fxAccounts.setSignedInUser(user, false).then(
           () => {
             this._activeSession = user;
+            log.debug("User signed in: " + JSON.stringify(this._user) +
+                      " - Account created " + (aMethod == "signUp"));
             return Promise.resolve({
               accountCreated: aMethod === "signUp",
               user: this._user
             });
           }
         );
       },
       reason => { return this._serverError(reason); }
@@ -164,16 +174,17 @@ this.FxAccountsManager = {
             this._activeSession = null;
             let error = this._getError(result);
             if (error) {
               return Promise.reject({
                 error: error,
                 details: result
               });
             }
+            log.debug("Signed out");
             return Promise.resolve();
           },
           reason => {
             // Even if there is a remote server error, we remove the local
             // session.
             this._activeSession = null;
             return this._serverError(reason);
           }
@@ -213,57 +224,65 @@ this.FxAccountsManager = {
     if (this._activeSession) {
       // If our cache says that the account is not yet verified, we check that
       // this information is correct, and update the cached data if not.
       if (this._activeSession && !this._activeSession.verified &&
           !Services.io.offline) {
         return this.verificationStatus(this._activeSession);
       }
 
+      log.debug("Account " + JSON.stringify(this._user));
       return Promise.resolve(this._user);
     }
 
     // If no cached information, we try to get it from the persistent storage.
     return this._fxAccounts.getSignedInUser().then(
       user => {
         if (!user || !user.email) {
+          log.debug("No signed in account");
           return Promise.resolve(null);
         }
 
         this._activeSession = user;
         // If we get a stored information of a not yet verified account,
         // we check this information with the server, update the stored
         // data if needed and finally return the account details.
         if (!user.verified && !Services.io.offline) {
+          log.debug("Unverified account");
           return this.verificationStatus(user);
         }
 
+        log.debug("Account " + JSON.stringify(this._user));
         return Promise.resolve(this._user);
       }
     );
   },
 
   queryAccount: function(aAccountId) {
+    log.debug("queryAccount " + aAccountId);
     if (Services.io.offline) {
+      log.error(ERROR_OFFLINE);
       return Promise.reject({
         error: ERROR_OFFLINE
       });
     }
 
     let deferred = Promise.defer();
 
     if (!aAccountId) {
+      log.error(ERROR_INVALID_ACCOUNTID);
       return Promise.reject({
         error: ERROR_INVALID_ACCOUNTID
       });
     }
 
     let client = this._createFxAccountsClient();
     return client.accountExists(aAccountId).then(
       result => {
+        log.debug("Account " + result ? "" : "does not" + " exists");
         let error = this._getError(result);
         if (error) {
           return Promise.reject({
             error: error,
             details: result
           });
         }
 
@@ -271,29 +290,33 @@ this.FxAccountsManager = {
           registered: result
         });
       },
       reason => { this._serverError(reason); }
     );
   },
 
   verificationStatus: function() {
+    log.debug("verificationStatus");
     if (!this._activeSession || !this._activeSession.sessionToken) {
+      log.error(ERROR_NO_TOKEN_SESSION);
       return Promise.reject({
         error: ERROR_NO_TOKEN_SESSION
       });
     }
 
     // There is no way to unverify an already verified account, so we just
     // return the account details of a verified account
     if (this._activeSession.verified) {
+      log.debug("Account already verified");
       return Promise.resolve(this._user);
     }
 
     if (Services.io.offline) {
+      log.error(ERROR_OFFLINE);
       return Promise.reject({
         error: ERROR_OFFLINE
       });
     }
 
     let client = this._createFxAccountsClient();
     return client.recoveryEmailStatus(this._activeSession.sessionToken).then(
       data => {
@@ -307,74 +330,84 @@ this.FxAccountsManager = {
 
         // If the verification status is different from the one that we have
         // stored, we update it and return the session data. If not, we simply
         // return the session data.
         if (this._activeSession.verified != data.verified) {
           this._activeSession.verified = data.verified;
           return this._fxAccounts.setSignedInUser(this._activeSession).then(
             () => {
+              log.debug(JSON.stringify(this._user));
               return Promise.resolve(this._user);
             }
           );
         }
+        log.debug(JSON.stringify(this._user));
         return Promise.resolve(this._user);
       },
       reason => { return this._serverError(reason); }
     );
   },
 
   getAssertion: function(aAudience) {
+    log.debug("getAssertion " + aAudience);
     if (!aAudience) {
+      log.error(ERROR_INVALID_AUDIENCE);
       return Promise.reject({
         error: ERROR_INVALID_AUDIENCE
       });
     }
 
     if (Services.io.offline) {
+      log.error(ERROR_OFFLINE);
       return Promise.reject({
         error: ERROR_OFFLINE
       });
     }
 
     return this.getAccount().then(
       user => {
         if (user) {
           // We cannot get assertions for unverified accounts.
           if (user.verified) {
             return this._getAssertion(aAudience);
           }
 
+          log.error(ERROR_UNVERIFIED_ACCOUNT);
           return Promise.reject({
             error: ERROR_UNVERIFIED_ACCOUNT,
             details: {
               user: user
             }
           });
         }
 
+        log.debug("No signed in user");
         // If there is no currently signed in user, we trigger the signIn UI
         // flow.
         let ui = Cc["@mozilla.org/fxaccounts/fxaccounts-ui-glue;1"]
                    .createInstance(Ci.nsIFxAccountsUIGlue);
         return ui.signInFlow().then(
           result => {
             // Even if we get a successful result from the UI, the account will
             // most likely be unverified, so we cannot get an assertion.
             if (result && result.verified) {
               return this._getAssertion(aAudience);
             }
+
+            log.error(ERROR_UNVERIFIED_ACCOUNT);
             return Promise.reject({
               error: ERROR_UNVERIFIED_ACCOUNT,
               details: {
                 user: result
               }
             });
           },
           error => {
+            log.error(ERROR_UI_ERROR + " " + error);
             return Promise.reject({
               error: ERROR_UI_ERROR,
               details: error
             });
           }
         );
       }
     );
--- a/services/fxaccounts/moz.build
+++ b/services/fxaccounts/moz.build
@@ -6,14 +6,14 @@
 
 PARALLEL_DIRS += ['interfaces']
 
 TEST_DIRS += ['tests']
 
 EXTRA_JS_MODULES += [
   'FxAccounts.jsm',
   'FxAccountsClient.jsm',
-  'FxAccountsConsts.js'
+  'FxAccountsCommon.js'
 ]
 
 # For now, we will only be using the FxA manager in B2G.
 if CONFIG['MOZ_B2G']:
   EXTRA_JS_MODULES += ['FxAccountsManager.jsm']
--- a/services/fxaccounts/tests/xpcshell/test_manager.js
+++ b/services/fxaccounts/tests/xpcshell/test_manager.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 const Cm = Components.manager;
 
 Cu.import("resource://gre/modules/FxAccounts.jsm");
-Cu.import("resource://gre/modules/FxAccountsConsts.js");
+Cu.import("resource://gre/modules/FxAccountsCommon.js");
 Cu.import("resource://gre/modules/FxAccountsManager.jsm");
 Cu.import("resource://gre/modules/Promise.jsm");
 
 // === Mocks ===
 
 // Override FxAccountsUIGlue.
 const kFxAccountsUIGlueUUID = "{8f6d5d87-41ed-4bb5-aa28-625de57564c5}";
 const kFxAccountsUIGlueContractID =