Bug 1011084 - Implement navigator.mozId with WebIDL; r=bholley r=spenrose, sr=jst
authorJed Parsons <jedp@mozilla.com>
Fri, 30 May 2014 17:06:18 -0700
changeset 232128 f70e16de6f33742ae387b1f38d1aa960667b3cda
parent 232127 4de43ef387d279e6eadefcb39c34a828822a7911
child 232129 90f8728118e8ba7018cf9e80bf2f739ce64d0575
push id4187
push userbhearsum@mozilla.com
push dateFri, 28 Nov 2014 15:29:12 +0000
treeherdermozilla-beta@f23cc6a30c11 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbholley, spenrose, jst
bugs1011084
milestone35.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 1011084 - Implement navigator.mozId with WebIDL; r=bholley r=spenrose, sr=jst
dom/identity/Identity.manifest
dom/identity/nsDOMIdentity.js
dom/webidl/Identity.webidl
dom/webidl/moz.build
--- a/dom/identity/Identity.manifest
+++ b/dom/identity/Identity.manifest
@@ -1,9 +1,8 @@
 # nsDOMIdentity.js
 component {210853d9-2c97-4669-9761-b1ab9cbf57ef} nsDOMIdentity.js
-contract @mozilla.org/dom/identity;1 {210853d9-2c97-4669-9761-b1ab9cbf57ef}
-category JavaScript-navigator-property mozId @mozilla.org/dom/identity;1
+contract @mozilla.org/identity/manager;1 {210853d9-2c97-4669-9761-b1ab9cbf57ef}
 
 # nsIDService.js (initialization on startup)
 component {4e0a0e98-b1d3-4745-a1eb-f815199dd06b} nsIDService.js
 contract @mozilla.org/dom/identity/service;1 {4e0a0e98-b1d3-4745-a1eb-f815199dd06b}
 category app-startup IDService @mozilla.org/dom/identity/service;1
--- a/dom/identity/nsDOMIdentity.js
+++ b/dom/identity/nsDOMIdentity.js
@@ -41,39 +41,20 @@ XPCOMUtils.defineLazyServiceGetter(this,
 
 const ERRORS = {
   "ERROR_INVALID_ASSERTION_AUDIENCE":
     "Assertion audience may not differ from origin",
   "ERROR_REQUEST_WHILE_NOT_HANDLING_USER_INPUT":
     "The request() method may only be invoked when handling user input",
 };
 
-function nsDOMIdentity(aIdentityInternal) {
-  this._identityInternal = aIdentityInternal;
+function nsDOMIdentity() {
 }
-nsDOMIdentity.prototype = {
-  __exposedProps__: {
-    // Relying Party (RP)
-    watch: 'r',
-    request: 'r',
-    logout: 'r',
-    get: 'r',
-    getVerifiedEmail: 'r',
 
-    // Provisioning
-    beginProvisioning: 'r',
-    genKeyPair: 'r',
-    registerCertificate: 'r',
-    raiseProvisioningFailure: 'r',
-
-    // Authentication
-    beginAuthentication: 'r',
-    completeAuthentication: 'r',
-    raiseAuthenticationFailure: 'r'
-  },
+nsDOMIdentity.prototype = {
 
   // require native events unless syntheticEventsOk is set
   get nativeEventsRequired() {
     if (Services.prefs.prefHasUserValue(PREF_SYNTHETIC_EVENTS_OK) &&
         (Services.prefs.getPrefType(PREF_SYNTHETIC_EVENTS_OK) ===
          Ci.nsIPrefBranch.PREF_BOOL)) {
       return !Services.prefs.getBoolPref(PREF_SYNTHETIC_EVENTS_OK);
     }
@@ -143,17 +124,17 @@ nsDOMIdentity.prototype = {
     this._rpWatcher.audience = message.audience;
 
     if (message.errors.length) {
       this.reportErrors(message);
       // We don't delete the rpWatcher object, because we don't want the
       // broken client to be able to call watch() any more.  It's broken.
       return;
     }
-    this._identityInternal._mm.sendAsyncMessage(
+    this._mm.sendAsyncMessage(
       "Identity:RP:Watch",
       message,
       null,
       this._window.document.nodePrincipal
     );
   },
 
   request: function nsDOMIdentity_request(aOptions = {}) {
@@ -219,17 +200,17 @@ nsDOMIdentity.prototype = {
         throw new Error("oncancel is not a function");
       } else {
         // Store optional cancel callback for later.
         this._onCancelRequestCallback = aOptions.oncancel;
       }
     }
 
     this._rpCalls++;
-    this._identityInternal._mm.sendAsyncMessage(
+    this._mm.sendAsyncMessage(
       "Identity:RP:Request",
       message,
       null,
       this._window.document.nodePrincipal
     );
   },
 
   logout: function nsDOMIdentity_logout() {
@@ -244,17 +225,17 @@ nsDOMIdentity.prototype = {
     let message = this.DOMIdentityMessage();
 
     // Report and fail hard on any errors.
     if (message.errors.length) {
       this.reportErrors(message);
       return;
     }
 
-    this._identityInternal._mm.sendAsyncMessage(
+    this._mm.sendAsyncMessage(
       "Identity:RP:Logout",
       message,
       null,
       this._window.document.nodePrincipal
     );
   },
 
   /*
@@ -332,17 +313,17 @@ nsDOMIdentity.prototype = {
     if (this._beginProvisioningCallback) {
       throw new Error("navigator.id.beginProvisioning already called.");
     }
     if (!aCallback || typeof(aCallback) !== "function") {
       throw new Error("beginProvisioning callback is required.");
     }
 
     this._beginProvisioningCallback = aCallback;
-    this._identityInternal._mm.sendAsyncMessage(
+    this._mm.sendAsyncMessage(
       "Identity:IDP:BeginProvisioning",
       this.DOMIdentityMessage(),
       null,
       this._window.document.nodePrincipal
     );
   },
 
   genKeyPair: function nsDOMIdentity_genKeyPair(aCallback) {
@@ -353,17 +334,17 @@ nsDOMIdentity.prototype = {
     if (this._genKeyPairCallback) {
       throw new Error("navigator.id.genKeyPair already called.");
     }
     if (!aCallback || typeof(aCallback) !== "function") {
       throw new Error("genKeyPair callback is required.");
     }
 
     this._genKeyPairCallback = aCallback;
-    this._identityInternal._mm.sendAsyncMessage(
+    this._mm.sendAsyncMessage(
       "Identity:IDP:GenKeyPair",
       this.DOMIdentityMessage(),
       null,
       this._window.document.nodePrincipal
     );
   },
 
   registerCertificate: function nsDOMIdentity_registerCertificate(aCertificate) {
@@ -373,17 +354,17 @@ nsDOMIdentity.prototype = {
     }
     if (this._provisioningEnded) {
       throw new Error("Provisioning already ended");
     }
     this._provisioningEnded = true;
 
     let message = this.DOMIdentityMessage();
     message.cert = aCertificate;
-    this._identityInternal._mm.sendAsyncMessage(
+    this._mm.sendAsyncMessage(
       "Identity:IDP:RegisterCertificate",
       message,
       null,
       this._window.document.nodePrincipal
     );
   },
 
   raiseProvisioningFailure: function nsDOMIdentity_raiseProvisioningFailure(aReason) {
@@ -393,17 +374,17 @@ nsDOMIdentity.prototype = {
     }
     if (!aReason || typeof(aReason) != "string") {
       throw new Error("raiseProvisioningFailure reason is required");
     }
     this._provisioningEnded = true;
 
     let message = this.DOMIdentityMessage();
     message.reason = aReason;
-    this._identityInternal._mm.sendAsyncMessage(
+    this._mm.sendAsyncMessage(
       "Identity:IDP:ProvisioningFailure",
       message,
       null,
       this._window.document.nodePrincipal
     );
   },
 
   /**
@@ -418,34 +399,34 @@ nsDOMIdentity.prototype = {
     if (typeof(aCallback) !== "function") {
       throw new Error("beginAuthentication callback is required.");
     }
     if (!aCallback || typeof(aCallback) !== "function") {
       throw new Error("beginAuthentication callback is required.");
     }
 
     this._beginAuthenticationCallback = aCallback;
-    this._identityInternal._mm.sendAsyncMessage(
+    this._mm.sendAsyncMessage(
       "Identity:IDP:BeginAuthentication",
       this.DOMIdentityMessage(),
       null,
       this._window.document.nodePrincipal
     );
   },
 
   completeAuthentication: function nsDOMIdentity_completeAuthentication() {
     if (this._authenticationEnded) {
       throw new Error("Authentication already ended");
     }
     if (!this._beginAuthenticationCallback) {
       throw new Error("navigator.id.completeAuthentication called outside of authentication");
     }
     this._authenticationEnded = true;
 
-    this._identityInternal._mm.sendAsyncMessage(
+    this._mm.sendAsyncMessage(
       "Identity:IDP:CompleteAuthentication",
       this.DOMIdentityMessage(),
       null,
       this._window.document.nodePrincipal
     );
   },
 
   raiseAuthenticationFailure: function nsDOMIdentity_raiseAuthenticationFailure(aReason) {
@@ -453,44 +434,24 @@ nsDOMIdentity.prototype = {
       throw new Error("Authentication already ended");
     }
     if (!aReason || typeof(aReason) != "string") {
       throw new Error("raiseProvisioningFailure reason is required");
     }
 
     let message = this.DOMIdentityMessage();
     message.reason = aReason;
-    this._identityInternal._mm.sendAsyncMessage(
+    this._mm.sendAsyncMessage(
       "Identity:IDP:AuthenticationFailure",
       message,
       null,
       this._window.document.nodePrincipal
     );
   },
 
-  // Private.
-  _init: function nsDOMIdentity__init(aWindow) {
-
-    this._initializeState();
-
-    // Store window and origin URI.
-    this._window = aWindow;
-    this._origin = aWindow.document.nodePrincipal.origin;
-    this._appStatus = aWindow.document.nodePrincipal.appStatus;
-    this._appId = aWindow.document.nodePrincipal.appId;
-
-    // Setup identifiers for current window.
-    let util = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
-                      .getInterface(Ci.nsIDOMWindowUtils);
-
-    // We need to inherit the id from the internalIdentity service.
-    // See comments below in that service's init.
-    this._id = this._identityInternal._id;
-  },
-
   /**
    * Called during init and shutdown.
    */
   _initializeState: function nsDOMIdentity__initializeState() {
     // Some state to prevent abuse
     // Limit the number of calls to .request
     this._rpCalls = 0;
     this._provisioningEnded = false;
@@ -498,22 +459,28 @@ nsDOMIdentity.prototype = {
 
     this._rpWatcher = null;
     this._onCancelRequestCallback = null;
     this._beginProvisioningCallback = null;
     this._genKeyPairCallback = null;
     this._beginAuthenticationCallback = null;
   },
 
-  _receiveMessage: function nsDOMIdentity_receiveMessage(aMessage) {
+  // nsIMessageListener
+  receiveMessage: function nsDOMIdentity_receiveMessage(aMessage) {
     let msg = aMessage.json;
 
+    // Is this message intended for this window?
+    if (msg.id != this._id) {
+      return;
+    }
+
     switch (aMessage.name) {
       case "Identity:ResetState":
-        if (!this._identityInternal._debug) {
+        if (!this._debug) {
           return;
         }
         this._initializeState();
         Services.obs.notifyObservers(null, "identity-DOM-state-reset", this._id);
         break;
       case "Identity:RP:Watch:OnLogin":
         // Do we have a watcher?
         if (!this._rpWatcher) {
@@ -581,20 +548,16 @@ nsDOMIdentity.prototype = {
         this._callGenKeyPairCallback(msg);
         break;
       case "Identity:IDP:CallBeginAuthenticationCallback":
         this._callBeginAuthenticationCallback(msg);
         break;
     }
   },
 
-  _log: function nsDOMIdentity__log(msg) {
-    this._identityInternal._log(msg);
-  },
-
   _callGenKeyPairCallback: function nsDOMIdentity__callGenKeyPairCallback(message) {
     // create a pubkey object that works
     let chrome_pubkey = JSON.parse(message.publicKey);
 
     // bunch of stuff to create a proper object in window context
     function genPropDesc(value) {
       return {
         enumerable: true, configurable: true, writable: true, value: value
@@ -671,98 +634,79 @@ nsDOMIdentity.prototype = {
     // Replace any audience supplied by the RP with one that has been sanitised
     message.audience = _audience;
 
     this._log("DOMIdentityMessage: " + JSON.stringify(message));
 
     return message;
   },
 
-  uninit: function DOMIdentity_uninit() {
-    this._log("nsDOMIdentity uninit() " + this._id);
-    this._identityInternal._mm.sendAsyncMessage(
-      "Identity:RP:Unwatch",
-      { id: this._id },
-      null,
-      this._window.document.nodePrincipal
-    );
-  }
-
-};
-
-/**
- * Internal functions that shouldn't be exposed to content.
- */
-function nsDOMIdentityInternal() {
-}
-nsDOMIdentityInternal.prototype = {
-
-  // nsIMessageListener
-  receiveMessage: function nsDOMIdentityInternal_receiveMessage(aMessage) {
-    let msg = aMessage.json;
-    // Is this message intended for this window?
-    if (msg.id != this._id) {
-      return;
-    }
-    this._identity._receiveMessage(aMessage);
-  },
-
+ /**
+  * Internal methods that are not exposed to content.
+  * See dom/webidl/Identity.webidl for the public interface.
+  */
   // nsIObserver
   observe: function nsDOMIdentityInternal_observe(aSubject, aTopic, aData) {
     let window = aSubject.QueryInterface(Ci.nsIDOMWindow);
     if (window != this._window) {
       return;
     }
 
-    this._identity.uninit();
+    this.uninit();
 
     Services.obs.removeObserver(this, "dom-window-destroyed");
-    this._identity._initializeState();
-    this._identity = null;
+    this._initializeState();
 
     // TODO: Also send message to DOMIdentity notifiying window is no longer valid
     // ie. in the case that the user closes the auth. window and we need to know.
 
     try {
       for (let msgName of this._messages) {
         this._mm.removeMessageListener(msgName, this);
       }
     } catch (ex) {
       // Avoid errors when removing more than once.
     }
 
     this._mm = null;
   },
 
-  // nsIDOMGlobalPropertyInitializer
+  //  Because we implement nsIDOMGlobalPropertyInitializer, our init() method
+  //  is invoked with content window as its single argument.
   init: function nsDOMIdentityInternal_init(aWindow) {
     if (Services.prefs.getPrefType(PREF_ENABLED) != Ci.nsIPrefBranch.PREF_BOOL
         || !Services.prefs.getBoolPref(PREF_ENABLED)) {
       return null;
     }
 
     this._debug =
       Services.prefs.getPrefType(PREF_DEBUG) == Ci.nsIPrefBranch.PREF_BOOL
       && Services.prefs.getBoolPref(PREF_DEBUG);
 
+    // Setup identifiers for current window.
     let util = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                       .getInterface(Ci.nsIDOMWindowUtils);
 
     // To avoid cross-process windowId collisions, use a uuid as an
     // almost certainly unique identifier.
     //
     // XXX Bug 869182 - use a combination of child process id and
     // innerwindow id to construct the unique id.
     this._id = uuidgen.generateUUID().toString();
     this._window = aWindow;
 
     // nsDOMIdentity needs to know our _id, so this goes after
     // its creation.
-    this._identity = new nsDOMIdentity(this);
-    this._identity._init(aWindow);
+    this._initializeState();
+
+    // Store window and origin URI.
+    this._window = aWindow;
+    this._origin = aWindow.document.nodePrincipal.origin;
+    this._appStatus = aWindow.document.nodePrincipal.appStatus;
+    this._appId = aWindow.document.nodePrincipal.appId;
 
     this._log("init was called from " + aWindow.document.location);
 
     this._mm = cpmm;
 
     // Setup listeners for messages from parent process.
     this._messages = [
       "Identity:ResetState",
@@ -776,34 +720,42 @@ nsDOMIdentityInternal.prototype = {
       "Identity:IDP:CallBeginAuthenticationCallback"
     ];
     this._messages.forEach(function(msgName) {
       this._mm.addMessageListener(msgName, this);
     }, this);
 
     // Setup observers so we can remove message listeners.
     Services.obs.addObserver(this, "dom-window-destroyed", false);
+  },
 
-    return this._identity;
-  },
+  uninit: function DOMIdentity_uninit() {
+    this._log("nsDOMIdentity uninit() " + this._id);
+    this._mm.sendAsyncMessage(
+      "Identity:RP:Unwatch",
+      { id: this._id }
+    );
+   },
 
   // Private.
   _log: function nsDOMIdentityInternal__log(msg) {
     if (!this._debug) {
       return;
     }
     dump("nsDOMIdentity (" + this._id + "): " + msg + "\n");
   },
 
   // Component setup.
   classID: Components.ID("{210853d9-2c97-4669-9761-b1ab9cbf57ef}"),
 
-  QueryInterface: XPCOMUtils.generateQI(
-    [Ci.nsIDOMGlobalPropertyInitializer, Ci.nsIMessageListener]
-  ),
+  QueryInterface: XPCOMUtils.generateQI([
+      Ci.nsIMessageListener,
+      Ci.nsIObserver,
+      Ci.nsIDOMGlobalPropertyInitializer
+  ]),
 
   classInfo: XPCOMUtils.generateCI({
     classID: Components.ID("{210853d9-2c97-4669-9761-b1ab9cbf57ef}"),
     contractID: "@mozilla.org/dom/identity;1",
     interfaces: [],
     classDescription: "Identity DOM Implementation"
   })
 };
@@ -835,9 +787,9 @@ function assertCorrectCallbacks(aOptions
 
   for (let cbName of optionalCallbacks) {
     if (aOptions[cbName] && typeof(aOptions[cbName]) != "function") {
       throw new Error(cbName + " must be a function");
     }
   }
 }
 
-this.NSGetFactory = XPCOMUtils.generateNSGetFactory([nsDOMIdentityInternal]);
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([nsDOMIdentity]);
new file mode 100644
--- /dev/null
+++ b/dom/webidl/Identity.webidl
@@ -0,0 +1,70 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/.
+ */
+
+callback IdentityOnReadyCallback = void();
+callback IdentityOnLoginCallback = void(DOMString identityAssertion);
+callback IdentityOnLogoutCallback = void();
+callback IdentityOnCancelCallback = void(DOMString? error);
+callback IdentityOnErrorCallback = void(DOMString error);
+
+dictionary IdentityWatchOptions {
+  // Required callback
+  IdentityOnLoginCallback onlogin;
+
+  // Optional parameters
+  DOMString wantIssuer;
+  DOMString loggedInUser;
+
+  // Optional callbacks
+  IdentityOnReadyCallback onready;
+  IdentityOnLogoutCallback onlogout;
+  IdentityOnErrorCallback onerror;
+
+  // Certified apps can specify this
+  DOMString audience;
+};
+
+dictionary IdentityRequestOptions {
+  // Optional parameters
+  long refreshAuthentication;
+  DOMString termsOfService;
+  DOMString privacyPolicy;
+  DOMString backgroundColor;
+  DOMString siteLogo;
+  DOMString siteName;
+  DOMString returnTo;
+
+  IdentityOnCancelCallback oncancel;
+
+  // Certified apps can specify this
+  DOMString origin;
+};
+
+dictionary IdentityGetOptions {
+  DOMString privacyPolicy;
+  DOMString termsOfService;
+  DOMString privacyURL;
+  DOMString tosURL;
+  DOMString siteName;
+  DOMString siteLogo;
+};
+
+[JSImplementation="@mozilla.org/identity/manager;1",
+ NoInterfaceObject,
+ NavigatorProperty="mozId",
+ Pref="dom.identity.enabled"]
+interface IdentityManager {
+  void watch(optional IdentityWatchOptions options);
+  void request(optional IdentityRequestOptions options);
+  void logout();
+
+  [Pref="dom.identity.exposeLegacyGetAPI"]
+  void get(IdentityOnLoginCallback callback, optional IdentityGetOptions options);
+
+  [Pref="dom.identity.exposeLegacyGetVerifiedEmailAPI"]
+  void getVerifiedEmail(IdentityOnLoginCallback callback);
+};
+
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -227,16 +227,17 @@ WEBIDL_FILES = [
     'IDBIndex.webidl',
     'IDBKeyRange.webidl',
     'IDBMutableFile.webidl',
     'IDBObjectStore.webidl',
     'IDBOpenDBRequest.webidl',
     'IDBRequest.webidl',
     'IDBTransaction.webidl',
     'IDBVersionChangeEvent.webidl',
+    'Identity.webidl',
     'ImageCapture.webidl',
     'ImageData.webidl',
     'ImageDocument.webidl',
     'InputEvent.webidl',
     'InputMethod.webidl',
     'InspectorUtils.webidl',
     'InstallEvent.webidl',
     'InstallPhaseEvent.webidl',