Bug 815148 - r=fabrice
authorFernando Jiménez <ferjmoreno@gmail.com>
Mon, 03 Dec 2012 21:44:58 +0100
changeset 114837 c304d3db90e2d85c79e5b3cde746a2f563a50b41
parent 114836 ad1ee5f59a6ee28347d45358adbc90de66fdac2b
child 114838 0251c6a430553de12d55333c4f74b96f4d639e55
push id23947
push useremorley@mozilla.com
push dateTue, 04 Dec 2012 14:54:11 +0000
treeherdermozilla-central@0035f77045bc [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfabrice
bugs815148
milestone20.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 815148 - r=fabrice
b2g/chrome/content/payment.js
b2g/components/PaymentGlue.js
dom/payment/Payment.jsm
dom/payment/interfaces/nsIPaymentUIGlue.idl
--- a/b2g/chrome/content/payment.js
+++ b/b2g/chrome/content/payment.js
@@ -19,25 +19,35 @@ XPCOMUtils.defineLazyServiceGetter(this,
                                    "nsIMessageSender");
 
 XPCOMUtils.defineLazyServiceGetter(this, "uuidgen",
                                    "@mozilla.org/uuid-generator;1",
                                    "nsIUUIDGenerator");
 
 const kClosePaymentFlowEvent = "close-payment-flow-dialog";
 
+let requestId;
+
 function paymentSuccess(aResult) {
   closePaymentFlowDialog(function notifySuccess() {
-    cpmm.sendAsyncMessage("Payment:Success", { result: aResult });
+    if (!requestId) {
+      return;
+    }
+    cpmm.sendAsyncMessage("Payment:Success", { result: aResult,
+                                               requestId: requestId });
   });
 }
 
 function paymentFailed(aErrorMsg) {
   closePaymentFlowDialog(function notifyError() {
-    cpmm.sendAsyncMessage("Payment:Failed", { errorMsg: aErrorMsg });
+    if (!requestId) {
+      return;
+    }
+    cpmm.sendAsyncMessage("Payment:Failed", { errorMsg: aErrorMsg,
+                                              requestId: requestId });
   });
 }
 
 function closePaymentFlowDialog(aCallback) {
   // After receiving the payment provider confirmation about the
   // successful or failed payment flow, we notify the UI to close the
   // payment flow dialog and return to the caller application.
   let randomId = uuidgen.generateUUID().toString();
@@ -70,12 +80,18 @@ function closePaymentFlowDialog(aCallbac
     let glue = Cc["@mozilla.org/payment/ui-glue;1"]
                  .createInstance(Ci.nsIPaymentUIGlue);
     glue.cleanup();
   });
 
   browser.shell.sendChromeEvent(detail);
 }
 
+// We save the identifier of the DOM request, so we can dispatch the results
+// of the payment flow to the appropriate content process.
+addMessageListener("Payment:LoadShim", function receiveMessage(aMessage) {
+  requestId = aMessage.json.requestId;
+});
+
 addEventListener("DOMContentLoaded", function(e) {
   content.wrappedJSObject.paymentSuccess = paymentSuccess;
   content.wrappedJSObject.paymentFailed = paymentFailed;
 });
--- a/b2g/components/PaymentGlue.js
+++ b/b2g/components/PaymentGlue.js
@@ -25,22 +25,23 @@ function debug (s) {
   //dump("-*- PaymentGlue: " + s + "\n");
 };
 
 function PaymentUI() {
 }
 
 PaymentUI.prototype = {
 
-  confirmPaymentRequest: function confirmPaymentRequest(aRequests,
+  confirmPaymentRequest: function confirmPaymentRequest(aRequestId,
+                                                        aRequests,
                                                         aSuccessCb,
                                                         aErrorCb) {
     let _error = function _error(errorMsg) {
       if (aErrorCb) {
-        aErrorCb.onresult(errorMsg);
+        aErrorCb.onresult(aRequestId, errorMsg);
       }
     };
 
     let browser = Services.wm.getMostRecentWindow("navigator:browser");
     let content = browser.getContentWindow();
     if (!content) {
       _error("NO_CONTENT_WINDOW");
       return;
@@ -61,33 +62,33 @@ PaymentUI.prototype = {
     // based on the selected payment provider.
     content.addEventListener("mozContentEvent", function handleSelection(evt) {
       let msg = evt.detail;
       if (msg.id != id) {
         return;
       }
 
       if (msg.userSelection && aSuccessCb) {
-        aSuccessCb.onresult(msg.userSelection);
+        aSuccessCb.onresult(aRequestId, msg.userSelection);
       } else if (msg.errorMsg) {
         _error(msg.errorMsg);
       }
 
       content.removeEventListener("mozContentEvent", handleSelection);
     });
 
     browser.shell.sendChromeEvent(detail);
   },
 
-  showPaymentFlow: function showPaymentFlow(aPaymentFlowInfo, aErrorCb) {
-    debug("showPaymentFlow. uri " + aPaymentFlowInfo.uri);
-
+  showPaymentFlow: function showPaymentFlow(aRequestId,
+                                            aPaymentFlowInfo,
+                                            aErrorCb) {
     let _error = function _error(errorMsg) {
       if (aErrorCb) {
-        aErrorCb.onresult(errorMsg);
+        aErrorCb.onresult(aRequestId, errorMsg);
       }
     };
 
     // We ask the UI to browse to the selected payment flow.
     let browser = Services.wm.getMostRecentWindow("navigator:browser");
     let content = browser.getContentWindow();
     if (!content) {
       _error("NO_CONTENT_WINDOW");
@@ -119,16 +120,17 @@ PaymentUI.prototype = {
         return;
       }
       let frame = evt.detail.frame;
       let frameLoader = frame.QueryInterface(Ci.nsIFrameLoaderOwner)
                         .frameLoader;
       let mm = frameLoader.messageManager;
       try {
         mm.loadFrameScript(kPaymentShimFile, true);
+        mm.sendAsyncMessage("Payment:LoadShim", { requestId: aRequestId });
       } catch (e) {
         debug("Error loading " + kPaymentShimFile + " as a frame script: " + e);
         _error("ERROR_LOADING_PAYMENT_SHIM");
       } finally {
         content.removeEventListener("mozContentEvent", loadPaymentShim);
       }
     }).bind(this));
 
--- a/dom/payment/Payment.jsm
+++ b/dom/payment/Payment.jsm
@@ -26,18 +26,16 @@ XPCOMUtils.defineLazyServiceGetter(this,
                                    "nsIPrefService");
 
 function debug (s) {
   //dump("-*- PaymentManager: " + s + "\n");
 };
 
 let PaymentManager =  {
   init: function init() {
-    this.requestId = null;
-
     // Payment providers data are stored as a preference.
     this.registeredProviders = null;
 
     this.messageManagers = {};
 
     for each (let msgname in PAYMENT_IPC_MSG_NAMES) {
       ppmm.addMessageListener(msgname, this);
     }
@@ -48,109 +46,113 @@ let PaymentManager =  {
   /**
    * Process a message from the content process.
    */
   receiveMessage: function receiveMessage(aMessage) {
     let name = aMessage.name;
     let msg = aMessage.json;
     debug("Received '" + name + "' message from content process");
 
-    if (msg.requestId) {
-      this.requestId = msg.requestId;
-    }
-
     switch (name) {
       case "Payment:Pay": {
         // First of all, we register the payment providers.
         if (!this.registeredProviders) {
           this.registeredProviders = {};
           this.registerPaymentProviders();
         }
 
         // We save the message target message manager so we can later dispatch
         // back messages without broadcasting to all child processes.
-        this.messageManagers[this.requestId] = aMessage.target;
+        let requestId = msg.requestId;
+        this.messageManagers[requestId] = aMessage.target;
 
         // We check the jwt type and look for a match within the
         // registered payment providers to get the correct payment request
         // information.
         let paymentRequests = [];
         let jwtTypes = [];
         for (let i in msg.jwts) {
-          let pr = this.getPaymentRequestInfo(msg.jwts[i]);
+          let pr = this.getPaymentRequestInfo(requestId, msg.jwts[i]);
           if (!pr) {
             continue;
           }
           if (!(pr instanceof Ci.nsIDOMPaymentRequestInfo)) {
             return;
           }
           // We consider jwt type repetition an error.
           if (jwtTypes[pr.type]) {
-            this.paymentFailed("PAY_REQUEST_ERROR_DUPLICATED_JWT_TYPE");
+            this.paymentFailed(requestId,
+                               "PAY_REQUEST_ERROR_DUPLICATED_JWT_TYPE");
             return;
           }
           jwtTypes[pr.type] = true;
           paymentRequests.push(pr);
         }
 
         if (!paymentRequests.length) {
-          this.paymentFailed("PAY_REQUEST_ERROR_NO_VALID_REQUEST_FOUND");
+          this.paymentFailed(requestId,
+                             "PAY_REQUEST_ERROR_NO_VALID_REQUEST_FOUND");
           return;
         }
 
         // After getting the list of valid payment requests, we ask the user
         // for confirmation before sending any request to any payment provider.
         // If there is more than one choice, we also let the user select the one
         // that he prefers.
         let glue = Cc["@mozilla.org/payment/ui-glue;1"]
                    .createInstance(Ci.nsIPaymentUIGlue);
         if (!glue) {
           debug("Could not create nsIPaymentUIGlue instance");
-          this.paymentFailed("INTERNAL_ERROR_CREATE_PAYMENT_GLUE_FAILED");
+          this.paymentFailed(requestId,
+                             "INTERNAL_ERROR_CREATE_PAYMENT_GLUE_FAILED");
           return;
         }
 
-        let confirmPaymentSuccessCb = function successCb(aResult) {
+        let confirmPaymentSuccessCb = function successCb(aRequestId,
+                                                         aResult) {
           // Get the appropriate payment provider data based on user's choice.
           let selectedProvider = this.registeredProviders[aResult];
           if (!selectedProvider || !selectedProvider.uri) {
             debug("Could not retrieve a valid provider based on user's " +
                   "selection");
-            this.paymentFailed("INTERNAL_ERROR_NO_VALID_SELECTED_PROVIDER");
+            this.paymentFailed(aRequestId,
+                               "INTERNAL_ERROR_NO_VALID_SELECTED_PROVIDER");
             return;
           }
 
           let jwt;
           for (let i in paymentRequests) {
             if (paymentRequests[i].type == aResult) {
               jwt = paymentRequests[i].jwt;
               break;
             }
           }
           if (!jwt) {
             debug("The selected request has no JWT information associated");
-            this.paymentFailed("INTERNAL_ERROR_NO_JWT_ASSOCIATED_TO_REQUEST");
+            this.paymentFailed(aRequestId,
+                               "INTERNAL_ERROR_NO_JWT_ASSOCIATED_TO_REQUEST");
             return;
           }
 
-          this.showPaymentFlow(selectedProvider, jwt);
+          this.showPaymentFlow(aRequestId, selectedProvider, jwt);
         };
 
         let confirmPaymentErrorCb = this.paymentFailed;
 
-        glue.confirmPaymentRequest(paymentRequests,
+        glue.confirmPaymentRequest(requestId,
+                                   paymentRequests,
                                    confirmPaymentSuccessCb.bind(this),
                                    confirmPaymentErrorCb.bind(this));
         break;
       }
       case "Payment:Success":
       case "Payment:Failed": {
-        let mm = this.messageManagers[this.requestId];
+        let mm = this.messageManagers[msg.requestId];
         mm.sendAsyncMessage(name, {
-          requestId: this.requestId,
+          requestId: msg.requestId,
           result: msg.result,
           errorMsg: msg.errorMsg
         });
         break;
       }
     }
   },
 
@@ -198,86 +200,91 @@ let PaymentManager =  {
         debug("An error ocurred registering a payment provider. " + ex);
       }
     }
   },
 
   /**
    * Helper for sending a Payment:Failed message to the parent process.
    */
-  paymentFailed: function paymentFailed(aErrorMsg) {
-    let mm = this.messageManagers[this.requestId];
+  paymentFailed: function paymentFailed(aRequestId, aErrorMsg) {
+    let mm = this.messageManagers[aRequestId];
     mm.sendAsyncMessage("Payment:Failed", {
-      requestId: this.requestId,
+      requestId: aRequestId,
       errorMsg: aErrorMsg
     });
   },
 
   /**
    * Helper function to get the payment request info according to the jwt
    * type. Payment provider's data is stored as a preference.
    */
-  getPaymentRequestInfo: function getPaymentRequestInfo(aJwt) {
+  getPaymentRequestInfo: function getPaymentRequestInfo(aRequestId, aJwt) {
     if (!aJwt) {
-      this.paymentFailed("INTERNAL_ERROR_CALL_WITH_MISSING_JWT");
+      this.paymentFailed(aRequestId, "INTERNAL_ERROR_CALL_WITH_MISSING_JWT");
       return true;
     }
 
     // First thing, we check that the jwt type is an allowed type and has a
     // payment provider flow information associated.
 
     // A jwt string consists in three parts separated by period ('.'): header,
     // payload and signature.
     let segments = aJwt.split('.');
     if (segments.length !== 3) {
       debug("Error getting payment provider's uri. " +
             "Not enough or too many segments");
-      this.paymentFailed("PAY_REQUEST_ERROR_WRONG_SEGMENTS_COUNT");
+      this.paymentFailed(aRequestId,
+                         "PAY_REQUEST_ERROR_WRONG_SEGMENTS_COUNT");
       return true;
     }
 
     let payloadObject;
     try {
       // We only care about the payload segment, which contains the jwt type
       // that should match with any of the stored payment provider's data and
       // the payment request information to be shown to the user.
       let payload = atob(segments[1]);
       debug("Payload " + payload);
       if (!payload.length) {
-        this.paymentFailed("PAY_REQUEST_ERROR_EMPTY_PAYLOAD");
+        this.paymentFailed(aRequestId, "PAY_REQUEST_ERROR_EMPTY_PAYLOAD");
         return true;
       }
 
       // We get rid off the quotes and backslashes so we can parse the JSON
       // object.
       if (payload.charAt(0) === '"') {
         payload = payload.substr(1);
       }
       if (payload.charAt(payload.length - 1) === '"') {
         payload = payload.slice(0, -1);
       }
       payload = payload.replace(/\\/g, '');
 
       payloadObject = JSON.parse(payload);
       if (!payloadObject) {
-        this.paymentFailed("PAY_REQUEST_ERROR_ERROR_PARSING_JWT_PAYLOAD");
+        this.paymentFailed(aRequestId,
+                           "PAY_REQUEST_ERROR_ERROR_PARSING_JWT_PAYLOAD");
         return true;
       }
     } catch (e) {
-      this.paymentFailed("PAY_REQUEST_ERROR_ERROR_DECODING_JWT");
+      this.paymentFailed(aRequestId,
+                         "PAY_REQUEST_ERROR_ERROR_DECODING_JWT");
       return true;
     }
 
     if (!payloadObject.typ) {
-      this.paymentFailed("PAY_REQUEST_ERROR_NO_TYP_PARAMETER");
+      this.paymentFailed(aRequestId,
+                         "PAY_REQUEST_ERROR_NO_TYP_PARAMETER");
       return true;
     }
 
     if (!payloadObject.request) {
-      this.paymentFailed("PAY_REQUEST_ERROR_NO_REQUEST_PARAMETER");
+      this.paymentFailed(aRequestId,
+                         "PAY_REQUEST_ERROR_NO_REQUEST_PARAMETER");
       return true;
     }
 
     // Once we got the jwt 'typ' value we look for a match within the payment
     // providers stored preferences. If the jwt 'typ' is not recognized as one
     // of the allowed values for registered payment providers, we skip the jwt
     // validation but we don't fire any error. This way developers might have
     // a default set of well formed JWTs that might be used in different B2G
@@ -285,56 +292,64 @@ let PaymentManager =  {
     let provider = this.registeredProviders[payloadObject.typ];
     if (!provider) {
       debug("Not registered payment provider for jwt type: " +
             payloadObject.typ);
       return false;
     }
 
     if (!provider.uri || !provider.name) {
-      this.paymentFailed("INTERNAL_ERROR_WRONG_REGISTERED_PAY_PROVIDER");
+      this.paymentFailed(aRequestId,
+                         "INTERNAL_ERROR_WRONG_REGISTERED_PAY_PROVIDER");
       return true;
     }
 
     // We only allow https for payment providers uris.
     if (!/^https/.exec(provider.uri.toLowerCase())) {
       // We should never get this far.
       debug("Payment provider uris must be https: " + provider.uri);
-      this.paymentFailed("INTERNAL_ERROR_NON_HTTPS_PROVIDER_URI");
+      this.paymentFailed(aRequestId,
+                         "INTERNAL_ERROR_NON_HTTPS_PROVIDER_URI");
       return true;
     }
 
     let pldRequest = payloadObject.request;
     let request = Cc["@mozilla.org/payment/request-info;1"]
                   .createInstance(Ci.nsIDOMPaymentRequestInfo);
     if (!request) {
-      this.paymentFailed("INTERNAL_ERROR_ERROR_CREATING_PAY_REQUEST");
+      this.paymentFailed(aRequestId,
+                         "INTERNAL_ERROR_ERROR_CREATING_PAY_REQUEST");
       return true;
     }
     request.wrappedJSObject.init(aJwt,
                                  payloadObject.typ,
                                  provider.name);
     return request;
   },
 
-  showPaymentFlow: function showPaymentFlow(aPaymentProvider, aJwt) {
+  showPaymentFlow: function showPaymentFlow(aRequestId,
+                                            aPaymentProvider,
+                                            aJwt) {
     let paymentFlowInfo = Cc["@mozilla.org/payment/flow-info;1"]
                           .createInstance(Ci.nsIPaymentFlowInfo);
     paymentFlowInfo.uri = aPaymentProvider.uri;
     paymentFlowInfo.requestMethod = aPaymentProvider.requestMethod;
     paymentFlowInfo.jwt = aJwt;
 
     let glue = Cc["@mozilla.org/payment/ui-glue;1"]
                .createInstance(Ci.nsIPaymentUIGlue);
     if (!glue) {
       debug("Could not create nsIPaymentUIGlue instance");
-      this.paymentFailed("INTERNAL_ERROR_CREATE_PAYMENT_GLUE_FAILED");
+      this.paymentFailed(aRequestId,
+                         "INTERNAL_ERROR_CREATE_PAYMENT_GLUE_FAILED");
       return false;
     }
-    glue.showPaymentFlow(paymentFlowInfo, this.paymentFailed.bind(this));
+    glue.showPaymentFlow(aRequestId,
+                         paymentFlowInfo,
+                         this.paymentFailed.bind(this));
   },
 
   // nsIObserver
 
   observe: function observe(subject, topic, data) {
     if (topic == "xpcom-shutdown") {
       for each (let msgname in PAYMENT_IPC_MSG_NAMES) {
         ppmm.removeMessageListener(msgname, this);
--- a/dom/payment/interfaces/nsIPaymentUIGlue.idl
+++ b/dom/payment/interfaces/nsIPaymentUIGlue.idl
@@ -1,28 +1,30 @@
 /* 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/. */
 
 #include "nsISupports.idl"
 
 interface nsIPaymentFlowInfo;
 
-[scriptable, function, uuid(ca475754-6852-49a2-97e8-8a94cc7a453f)]
+[scriptable, function, uuid(b9afa678-71a5-4975-bcdb-0c4098730eff)]
 interface nsIPaymentUIGlueCallback : nsISupports
 {
-    void onresult(in DOMString result);
+    void onresult(in DOMString requestId, in DOMString result);
 };
 
-[scriptable, uuid(344e5442-7cc2-4636-bc42-e2249cb67813)]
+[scriptable, uuid(4dda9aa0-df88-4dcd-a583-199e516fa438)]
 interface nsIPaymentUIGlue : nsISupports
 {
     // The 'paymentRequestsInfo' contains the payment request information
     // for each JWT provided via navigator.mozPay call.
-    void confirmPaymentRequest(in jsval paymentRequestsInfo,
+    void confirmPaymentRequest(in DOMString requestId,
+                               in jsval paymentRequestsInfo,
                                in nsIPaymentUIGlueCallback successCb,
                                in nsIPaymentUIGlueCallback errorCb);
 
-    void showPaymentFlow(in nsIPaymentFlowInfo paymentFlowInfo,
+    void showPaymentFlow(in DOMString requestId,
+                         in nsIPaymentFlowInfo paymentFlowInfo,
                          in nsIPaymentUIGlueCallback errorCb);
 
     void cleanup();
 };