Bug 823751 - When persona login in the Trusty UI is canceled, fire the RP's oncancel() callback; r=benadida, a=bb+
authorJed Parsons <jparsons@mozilla.com>
Thu, 10 Jan 2013 09:56:10 +0100
changeset 118343 973188627480b64c0c0e1f753594ed2115128438
parent 118342 25ce76f1d0cf92f0ee9a26d7b97cc38a905e960a
child 118344 4d70f7fa681a18f049acadb6d56b8003fdde5571
push id24162
push useremorley@mozilla.com
push dateThu, 10 Jan 2013 16:00:43 +0000
treeherdermozilla-central@fc3ed72129d9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbenadida, bb
bugs823751
milestone21.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 823751 - When persona login in the Trusty UI is canceled, fire the RP's oncancel() callback; r=benadida, a=bb+
b2g/components/SignInToWebsite.jsm
dom/identity/DOMIdentity.jsm
dom/identity/nsDOMIdentity.js
toolkit/identity/MinimalIdentity.jsm
--- a/b2g/components/SignInToWebsite.jsm
+++ b/b2g/components/SignInToWebsite.jsm
@@ -153,90 +153,109 @@ let Pipe = {
    */
   communicate: function(aRpOptions, aGaiaOptions, aMessageCallback) {
     log("open gaia dialog with options:", aGaiaOptions);
 
     // This content variable is injected into the scope of
     // kIdentityShimFile, where it is used to access the BrowserID object
     // and its internal API.
     let content = GaiaInterface.getContent();
+    let mm = null;
+    let uuid = getRandomId();
 
     if (!content) {
       log("ERROR: what the what? no content window?");
       // aErrorCb.onresult("NO_CONTENT_WINDOW");
       return;
     }
 
-    // Prepare a message for gaia.  The parameter showUI signals
-    // whether user interaction is needed.  If it is, gaia will open a
-    // dialog; if not, a hidden iframe.  In each case, BrowserID is
-    // available in the context.
-    let id = kOpenIdentityDialog + "-" + getRandomId();
-    let detail = {
-      type: kOpenIdentityDialog,
-      showUI: aGaiaOptions.showUI || false,
-      id: id
-    };
+    function removeMessageListeners() {
+      if (mm) {
+        mm.removeMessageListener(kIdentityDelegateFinished, identityDelegateFinished);
+        mm.removeMessageListener(kIdentityControllerDoMethod, aMessageCallback);
+      }
+    }
+
+    function identityDelegateFinished() {
+      removeMessageListeners();
 
-    // When gaia signals back with a mozContentEvent containing the
-    // unique id we created, we know the window is ready.  We then inject
-    // the magic javascript (kIdentityShimFile) that will give the content
-    // the superpowers it needs to communicate back with this code.
+      let detail = {
+        type: kReceivedIdentityAssertion,
+        showUI: aGaiaOptions.showUI || false,
+        id: kReceivedIdentityAssertion + "-" + uuid
+      };
+      log('telling gaia to close the dialog');
+      // tell gaia to close the dialog
+      GaiaInterface.sendChromeEvent(detail);
+    }
+
     content.addEventListener("mozContentEvent", function getAssertion(evt) {
-
-      // Make sure the message is really for us
       let msg = evt.detail;
-      if (msg.id != id) {
+      if (!msg.id.match(uuid)) {
         return;
       }
 
-      // We only need to catch the first mozContentEvent from the
-      // iframe or popup, so we remove the listener right away.
-      content.removeEventListener("mozContentEvent", getAssertion);
+      switch (msg.id) {
+        case kOpenIdentityDialog + '-' + uuid:
+          if (msg.type === 'cancel') {
+            // The user closed the dialog.  Clean up and call cancel.
+            content.removeEventListener("mozContentEvent", getAssertion);
+            removeMessageListeners();
+            aMessageCallback({json: {method: "cancel"}});
+          } else {
+            // The window has opened.  Inject the identity shim file containing
+            // the callbacks in the content script.  This could be either the
+            // visible popup that the user interacts with, or it could be an
+            // invisible frame.
+            let frame = evt.detail.frame;
+            let frameLoader = frame.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader;
+            mm = frameLoader.messageManager;
+            try {
+              mm.loadFrameScript(kIdentityShimFile, true);
+              log("Loaded shim " + kIdentityShimFile + "\n");
+            } catch (e) {
+              log("Error loading ", kIdentityShimFile, " as a frame script: ", e);
+            }
 
-      // Try to load the identity shim file containing the callbacks
-      // in the content script.  This could be either the visible
-      // popup that the user interacts with, or it could be an invisible
-      // frame.
-      let frame = evt.detail.frame;
-      let frameLoader = frame.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader;
-      let mm = frameLoader.messageManager;
-      try {
-        mm.loadFrameScript(kIdentityShimFile, true);
-        log("Loaded shim " + kIdentityShimFile + "\n");
-      } catch (e) {
-        log("Error loading ", kIdentityShimFile, " as a frame script: ", e);
+            // There are two messages that the delegate can send back: a "do
+            // method" event, and a "finished" event.  We pass the do-method
+            // events straight to the caller for interpretation and handling.
+            // If we receive a "finished" event, then the delegate is done, so
+            // we shut down the pipe and clean up.
+            mm.addMessageListener(kIdentityControllerDoMethod, aMessageCallback);
+            mm.addMessageListener(kIdentityDelegateFinished, identityDelegateFinished);
+
+            mm.sendAsyncMessage(aGaiaOptions.message, aRpOptions);
+          }
+          break;
+
+        case kReceivedIdentityAssertion + '-' + uuid:
+          // Received our assertion.  The message manager callbacks will handle
+          // communicating back to the IDService.  All we have to do is remove
+          // this listener.
+          content.removeEventListener("mozContentEvent", getAssertion);
+          break;
+
+        default:
+          log("ERROR - Unexpected message: id=" + msg.id + ", type=" + msg.type + ", errorMsg=" + msg.errorMsg);
+          break;
       }
 
-      // There are two messages that the delegate can send back: a "do
-      // method" event, and a "finished" event.  We pass the do-method
-      // events straight to the caller for interpretation and handling.
-      // If we receive a "finished" event, then the delegate is done, so
-      // we shut down the pipe and clean up.
-      mm.addMessageListener(kIdentityControllerDoMethod, aMessageCallback);
-      mm.addMessageListener(kIdentityDelegateFinished, function identityDelegateFinished() {
-        // clean up listeners
-        mm.removeMessageListener(kIdentityDelegateFinished, identityDelegateFinished);
-        mm.removeMessageListener(kIdentityControllerDoMethod, aMessageCallback);
-
-        let id = kReceivedIdentityAssertion + "-" + getRandomId();
-        let detail = {
-          type: kReceivedIdentityAssertion,
-          showUI: aGaiaOptions.showUI || false,
-          id: id
-        };
-        log('telling gaia to close the dialog');
-        // tell gaia to close the dialog
-        GaiaInterface.sendChromeEvent(detail);
-      });
-
-      mm.sendAsyncMessage(aGaiaOptions.message, aRpOptions);
     });
 
-    // Tell gaia to open the identity iframe or trusty popup
+    // Tell gaia to open the identity iframe or trusty popup. The parameter
+    // showUI signals whether user interaction is needed.  If it is, gaia will
+    // open a dialog; if not, a hidden iframe.  In each case, BrowserID is
+    // available in the context.
+    let detail = {
+      type: kOpenIdentityDialog,
+      showUI: aGaiaOptions.showUI || false,
+      id: kOpenIdentityDialog + "-" + uuid
+    };
+
     GaiaInterface.sendChromeEvent(detail);
   }
 
 };
 
 /*
  * The controller sits between the IdentityService used by DOMIdentity
  * and a gaia process launches an (invisible) iframe or (visible)
@@ -311,16 +330,20 @@ this.SignInToWebsiteController = {
              IdentityService.doLogin(aRpId, message.assertion);
            }
           break;
 
         case "logout":
           IdentityService.doLogout(aRpId);
           break;
 
+        case "cancel":
+          IdentityService.doCancel(aRpId);
+          break;
+
         default:
           log("WARNING: wonky method call:", message.method);
           break;
       }
     };
   },
 
   doWatch: function SignInToWebsiteController_doWatch(aRpOptions) {
--- a/dom/identity/DOMIdentity.jsm
+++ b/dom/identity/DOMIdentity.jsm
@@ -122,16 +122,22 @@ RPWatchContext.prototype = {
   },
 
   doReady: function RPWatchContext_onready() {
     log("doReady: " + this.id);
     let message = new IDDOMMessage({id: this.id});
     this._mm.sendAsyncMessage("Identity:RP:Watch:OnReady", message);
   },
 
+  doCancel: function RPWatchContext_oncancel() {
+    log("doCancel: " + this.id);
+    let message = new IDDOMMessage({id: this.id});
+    this._mm.sendAsyncMessage("Identity:RP:Watch:OnCancel", message);
+  },
+
   doError: function RPWatchContext_onerror(aMessage) {
     log("doError: " + aMessage);
   }
 };
 
 this.DOMIdentity = {
   // nsIMessageListener
   receiveMessage: function DOMIdentity_receiveMessage(aMessage) {
--- a/dom/identity/nsDOMIdentity.js
+++ b/dom/identity/nsDOMIdentity.js
@@ -434,17 +434,17 @@ nsDOMIdentity.prototype = {
           dump("WARNING: Received OnReady message, but there is no RP watcher\n");
           return;
         }
 
         if (this._rpWatcher.onready) {
           this._rpWatcher.onready();
         }
         break;
-      case "Identity:RP:Request:OnCancel":
+      case "Identity:RP:Watch:OnCancel":
         // Do we have a watcher?
         if (!this._rpWatcher) {
           dump("WARNING: Received OnCancel message, but there is no RP watcher\n");
           return;
         }
 
         if (this._onCancelRequestCallback) {
           this._onCancelRequestCallback();
@@ -593,17 +593,17 @@ nsDOMIdentityInternal.prototype = {
     this._mm = cpmm;
 
     // Setup listeners for messages from parent process.
     this._messages = [
       "Identity:ResetState",
       "Identity:RP:Watch:OnLogin",
       "Identity:RP:Watch:OnLogout",
       "Identity:RP:Watch:OnReady",
-      "Identity:RP:Request:OnCancel",
+      "Identity:RP:Watch:OnCancel",
       "Identity:IDP:CallBeginProvisioningCallback",
       "Identity:IDP:CallGenKeyPairCallback",
       "Identity:IDP:CallBeginAuthenticationCallback",
     ];
     this._messages.forEach((function(msgName) {
       this._mm.addMessageListener(msgName, this);
     }).bind(this));
 
--- a/toolkit/identity/MinimalIdentity.jsm
+++ b/toolkit/identity/MinimalIdentity.jsm
@@ -203,16 +203,26 @@ IDService.prototype = {
     if (!rp) {
       dump("WARNING: doReady found no rp to go with callerId " + aRpCallerId + "\n");
       return;
     }
 
     rp.doReady();
   },
 
+  doCancel: function doCancel(aRpCallerId) {
+    let rp = this._rpFlows[aRpCallerId];
+    if (!rp) {
+      dump("WARNING: doCancel found no rp to go with callerId " + aRpCallerId + "\n");
+      return;
+    }
+
+    rp.doCancel();
+  },
+
 
   /*
    * XXX Bug 804229: Implement Identity Provider Functions
    *
    * Stubs for Identity Provider functions follow
    */
 
   /**