Bug 1441683 - response.complete() should make the completeStatus available to the payment UI service. r?baku draft
authorJared Wein <jwein@mozilla.com>
Tue, 13 Mar 2018 10:30:08 -0400
changeset 766886 ec0ea38623067f1be806c039f48c77591f1f2661
parent 766824 8f1b2f872f0ea358a0412eb8b8687f08d47f6621
push id102431
push userbmo:jaws@mozilla.com
push dateTue, 13 Mar 2018 14:55:24 +0000
reviewersbaku
bugs1441683
milestone61.0a1
Bug 1441683 - response.complete() should make the completeStatus available to the payment UI service. r?baku MozReview-Commit-ID: TnszpuqAa
dom/interfaces/payments/nsIPaymentRequest.idl
dom/payments/PaymentRequestData.cpp
dom/payments/PaymentRequestData.h
dom/payments/PaymentRequestService.cpp
dom/payments/test/ShowPaymentChromeScript.js
dom/payments/test/test_showPayment.html
--- a/dom/interfaces/payments/nsIPaymentRequest.idl
+++ b/dom/interfaces/payments/nsIPaymentRequest.idl
@@ -76,14 +76,16 @@ interface nsIPaymentOptions : nsISupport
 };
 
 [scriptable, builtinclass, uuid(2fa36783-d684-4487-b7a8-9def6ae3128f)]
 interface nsIPaymentRequest : nsISupports
 {
   readonly attribute uint64_t tabId;
   readonly attribute nsIPrincipal topLevelPrincipal;
   readonly attribute AString requestId;
+  readonly attribute AString completeStatus;
   readonly attribute nsIArray paymentMethods;
   readonly attribute nsIPaymentDetails paymentDetails;
   readonly attribute nsIPaymentOptions paymentOptions;
 
+  [noscript] void setCompleteStatus(in AString aCompleteStatus);
   void updatePaymentDetails(in nsIPaymentDetails aDetails);
 };
--- a/dom/payments/PaymentRequestData.cpp
+++ b/dom/payments/PaymentRequestData.cpp
@@ -676,16 +676,30 @@ PaymentRequest::GetPaymentOptions(nsIPay
 
 NS_IMETHODIMP
 PaymentRequest::UpdatePaymentDetails(nsIPaymentDetails* aPaymentDetails)
 {
   MOZ_ASSERT(aPaymentDetails);
   return mPaymentDetails->Update(aPaymentDetails);
 }
 
+NS_IMETHODIMP
+PaymentRequest::SetCompleteStatus(const nsAString& aCompleteStatus)
+{
+  mCompleteStatus = aCompleteStatus;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+PaymentRequest::GetCompleteStatus(nsAString& aCompleteStatus)
+{
+  aCompleteStatus = mCompleteStatus;
+  return NS_OK;
+}
+
 /* PaymentAddress */
 
 NS_IMPL_ISUPPORTS(PaymentAddress, nsIPaymentAddress)
 
 NS_IMETHODIMP
 PaymentAddress::Init(const nsAString& aCountry,
                      nsIArray* aAddressLine,
                      const nsAString& aRegion,
--- a/dom/payments/PaymentRequestData.h
+++ b/dom/payments/PaymentRequestData.h
@@ -186,16 +186,17 @@ public:
                  nsIPaymentDetails* aPaymentDetails,
                  nsIPaymentOptions* aPaymentOptions);
 
 private:
   ~PaymentRequest() = default;
 
   uint64_t mTabId;
   nsString mRequestId;
+  nsString mCompleteStatus;
   nsCOMPtr<nsIPrincipal> mTopLevelPrincipal;
   nsCOMPtr<nsIArray> mPaymentMethods;
   nsCOMPtr<nsIPaymentDetails> mPaymentDetails;
   nsCOMPtr<nsIPaymentOptions> mPaymentOptions;
 };
 
 class PaymentAddress final : public nsIPaymentAddress
 {
--- a/dom/payments/PaymentRequestService.cpp
+++ b/dom/payments/PaymentRequestService.cpp
@@ -313,18 +313,41 @@ PaymentRequestService::RequestPayment(ns
         }
         rv = LaunchUIAction(requestId, type);
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return NS_ERROR_FAILURE;
         }
       }
       break;
     }
-    case nsIPaymentActionRequest::ABORT_ACTION:
+    case nsIPaymentActionRequest::ABORT_ACTION: {
+      rv = LaunchUIAction(requestId, type);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return NS_ERROR_FAILURE;
+      }
+      break;
+    }
     case nsIPaymentActionRequest::COMPLETE_ACTION: {
+      nsCOMPtr<nsIPaymentCompleteActionRequest> request =
+        do_QueryInterface(aRequest);
+      MOZ_ASSERT(request);
+      nsAutoString completeStatus;
+      rv = request->GetCompleteStatus(completeStatus);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return NS_ERROR_FAILURE;
+      }
+      nsCOMPtr<nsIPaymentRequest> payment;
+      rv = GetPaymentRequestById(requestId, getter_AddRefs(payment));
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return NS_ERROR_FAILURE;
+      }
+      rv = payment->SetCompleteStatus(completeStatus);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return NS_ERROR_FAILURE;
+      }
       rv = LaunchUIAction(requestId, type);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return NS_ERROR_FAILURE;
       }
       break;
     }
     case nsIPaymentActionRequest::UPDATE_ACTION: {
       nsCOMPtr<nsIPaymentUpdateActionRequest> request = do_QueryInterface(aRequest);
--- a/dom/payments/test/ShowPaymentChromeScript.js
+++ b/dom/payments/test/ShowPaymentChromeScript.js
@@ -1,20 +1,24 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 "use strict";
 
 const { XPCOMUtils } = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 const paymentSrv = Cc["@mozilla.org/dom/payments/payment-request-service;1"].getService(Ci.nsIPaymentRequestService);
+let expectedCompleteStatus;
 
 function emitTestFail(message) {
   sendAsyncMessage("test-fail", message);
 }
+function emitTestPass(message) {
+  sendAsyncMessage("test-pass", message);
+}
 
 const shippingAddress = Cc["@mozilla.org/dom/payments/payment-address;1"].
                            createInstance(Ci.nsIPaymentAddress);
 const addressLine = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
 const address = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
 address.data = "Easton Ave";
 addressLine.appendElement(address);
 shippingAddress.init("USA",              // country
@@ -24,24 +28,73 @@ shippingAddress.init("USA",             
                      "Test locality",    // dependent locality
                      "94066",            // postal code
                      "123456",           // sorting code
                      "en",               // language code
                      "Testing Org",      // organization
                      "Bill A. Pacheco",  // recipient
                      "+1-434-441-3879"); // phone
 
+const DummyUIService = {
+  showPayment: function(requestId) {
+    const showResponseData = Cc["@mozilla.org/dom/payments/general-response-data;1"].
+                                createInstance(Ci.nsIGeneralResponseData);
+    try {
+      showResponseData.initData({ paymentToken: "6880281f-0df3-4b8e-916f-66575e2457c1",});
+    } catch (e) {
+      emitTestFail("Fail to initialize response data with { paymentToken: \"6880281f-0df3-4b8e-916f-66575e2457c1\",}");
+    }
+    let showResponse = Cc["@mozilla.org/dom/payments/payment-show-action-response;1"].
+                   createInstance(Ci.nsIPaymentShowActionResponse);
+    showResponse.init(requestId,
+                      Ci.nsIPaymentActionResponse.PAYMENT_ACCEPTED,
+                      "testing-payment-method",   // payment method
+                      showResponseData,           // payment method data
+                      "Bill A. Pacheco",          // payer name
+                      "",                         // payer email
+                      "");                        // payer phone
+    paymentSrv.respondPayment(showResponse.QueryInterface(Ci.nsIPaymentActionResponse));
+  },
+  abortPayment: function(requestId) {
+  },
+  completePayment: function(requestId) {
+    let payRequest = paymentSrv.getPaymentRequestById(requestId);
+    if (payRequest.completeStatus == expectedCompleteStatus) {
+      emitTestPass("request.completeStatus matches expectation of " + expectedCompleteStatus);
+    } else {
+      emitTestFail("request.completeStatus incorrect. Expected " +
+                   expectedCompleteStatus + ", got " + payRequest.completeStatus);
+    }
+
+    let completeResponse = Cc["@mozilla.org/dom/payments/payment-complete-action-response;1"].
+                           createInstance(Ci.nsIPaymentCompleteActionResponse);
+    completeResponse.init(requestId, Ci.nsIPaymentActionResponse.COMPLETE_SUCCEEDED);
+    paymentSrv.respondPayment(completeResponse.QueryInterface(Ci.nsIPaymentActionResponse));
+  },
+  updatePayment: function(requestId) {
+  },
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPaymentUIService]),
+};
+
 const NormalUIService = {
   shippingOptionChanged: false,
   showPayment: function(requestId) {
     paymentSrv.changeShippingAddress(requestId, shippingAddress);
   },
   abortPayment: function(requestId) {
   },
   completePayment: function(requestId) {
+    let payRequest = paymentSrv.getPaymentRequestById(requestId);
+    if (payRequest.completeStatus == expectedCompleteStatus) {
+      emitTestPass("request.completeStatus matches expectation of " + expectedCompleteStatus);
+    } else {
+      emitTestFail("request.completeStatus incorrect. Expected " +
+                   expectedCompleteStatus + ", got " + payRequest.completeStatus);
+    }
+
     let completeResponse = Cc["@mozilla.org/dom/payments/payment-complete-action-response;1"].
                            createInstance(Ci.nsIPaymentCompleteActionResponse);
     completeResponse.init(requestId, Ci.nsIPaymentActionResponse.COMPLETE_SUCCEEDED);
     paymentSrv.respondPayment(completeResponse.QueryInterface(Ci.nsIPaymentActionResponse));
   },
   updatePayment: function(requestId) {
     let showResponse = null;
     let payRequest = paymentSrv.getPaymentRequestById(requestId);
@@ -203,28 +256,43 @@ function testInitDataAndResponse() {
   } catch (e) {
     if (e.name != "NS_ERROR_ILLEGAL_VALUE") {
       emitTestFail("Expected 'NS_ERROR_ILLEGAL_VALUE', but got " + e.name + ".");
     }
   }
   sendAsyncMessage("test-init-data-and-response-complete");
 }
 
+addMessageListener("set-dummy-ui-service", function() {
+  paymentSrv.setTestingUIService(DummyUIService.QueryInterface(Ci.nsIPaymentUIService));
+});
+
 addMessageListener("set-normal-ui-service", function() {
   paymentSrv.setTestingUIService(NormalUIService.QueryInterface(Ci.nsIPaymentUIService));
 });
 
 addMessageListener("set-reject-ui-service", function() {
   paymentSrv.setTestingUIService(RejectUIService.QueryInterface(Ci.nsIPaymentUIService));
 });
 
 addMessageListener("set-update-with-error-ui-service", function() {
   paymentSrv.setTestingUIService(ErrorUIService.QueryInterface(Ci.nsIPaymentUIService));
 });
 
 addMessageListener("test-init-data-and-response", testInitDataAndResponse);
 
+addMessageListener("set-complete-status-success", function() {
+  expectedCompleteStatus = "success";
+});
+
+addMessageListener("set-complete-status-fail", function() {
+  expectedCompleteStatus = "fail";
+});
+
+addMessageListener("set-complete-status-unknown", function() {
+  expectedCompleteStatus = "unknown";
+});
 
 addMessageListener("teardown", function() {
   paymentSrv.cleanup();
   paymentSrv.setTestingUIService(null);
   sendAsyncMessage('teardown-complete');
 });
--- a/dom/payments/test/test_showPayment.html
+++ b/dom/payments/test/test_showPayment.html
@@ -14,17 +14,21 @@ https://bugzilla.mozilla.org/show_bug.cg
   SimpleTest.waitForExplicitFinish();
 
   var gUrl = SimpleTest.getTestFileURL('ShowPaymentChromeScript.js');
   var gScript = SpecialPowers.loadChromeScript(gUrl);
 
   function testFailHandler(message) {
     ok(false, message);
   }
+  function testPassHandler(message) {
+    ok(true, message);
+  }
   gScript.addMessageListener("test-fail", testFailHandler);
+  gScript.addMessageListener("test-pass", testPassHandler);
 
   const defaultMethods = [{
     supportedMethods: "basic-card",
     data: {
       supportedNetworks: ['unionpay', 'visa', 'mastercard', 'amex', 'discover',
                           'diners', 'jcb', 'mir',
       ],
       supportedTypes: ['prepaid', 'debit', 'credit'],
@@ -150,18 +154,20 @@ https://bugzilla.mozilla.org/show_bug.cg
   }
 
   function updateWithError() {
     return new Promise((resolve, reject) => {
       resolve(updatedErrorDetails);
     });
   }
 
-  function testShow() {
+  function testShowWithSuccess() {
+    info("starting testShowWithSuccess");
     gScript.sendAsyncMessage("set-normal-ui-service");
+    gScript.sendAsyncMessage("set-complete-status-success");
     return new Promise((resolve, reject) => {
       const payRequest = new PaymentRequest(defaultMethods, defaultDetails, defaultOptions);
       payRequest.addEventListener("shippingaddresschange", event => {
         is(payRequest.shippingAddress.country, "USA", "payRequest.shippingAddress.country should be 'USA' from event.");
         is(payRequest.shippingAddress.addressLine.length, 0, "payRequest.shippingAddress.addressLine.length should be 0 from event.");
         is(payRequest.shippingAddress.region, "CA", "payRequest.shippingAddress.region should be 'CA' from event.");
         is(payRequest.shippingAddress.city, "San Bruno", "payRequest.shippingAddress.city should be 'San Bruno' from event.");
         is(payRequest.shippingAddress.dependentLocality, "Test locality", "payRequest.shippingAddress.dependentLocality should be 'Test locality' from event.");
@@ -213,16 +219,76 @@ https://bugzilla.mozilla.org/show_bug.cg
         });
       }).catch( e => {
         ok(false, "Unexpected error: " + e.name);
         resolve();
       });
     });
   }
 
+  function testShowWithFail() {
+    info("starting testShowWithFail");
+    gScript.sendAsyncMessage("set-dummy-ui-service");
+    gScript.sendAsyncMessage("set-complete-status-fail");
+    return new Promise((resolve, reject) => {
+      const payRequest = new PaymentRequest(defaultMethods, defaultDetails, defaultOptions);
+      payRequest.show().then(response => {
+        response.complete("fail").then(() => {
+          resolve();
+        }).catch(e => {
+          ok(false, "Unexpected error: " + e.name);
+          resolve();
+        });
+      }).catch( e => {
+        ok(false, "Unexpected error: " + e.name);
+        resolve();
+      });
+    });
+  }
+
+  function testShowWithUnknown() {
+    info("starting testShowWithUnknown");
+    gScript.sendAsyncMessage("set-dummy-ui-service");
+    gScript.sendAsyncMessage("set-complete-status-unknown");
+    return new Promise((resolve, reject) => {
+      const payRequest = new PaymentRequest(defaultMethods, defaultDetails, defaultOptions);
+      payRequest.show().then(response => {
+        response.complete("unknown").then(() => {
+          resolve();
+        }).catch(e => {
+          ok(false, "Unexpected error: " + e.name);
+          resolve();
+        });
+      }).catch( e => {
+        ok(false, "Unexpected error: " + e.name);
+        resolve();
+      });
+    });
+  }
+
+  function testShowWithEmpty() {
+    info("starting testShowWithEmpty");
+    gScript.sendAsyncMessage("set-dummy-ui-service");
+    gScript.sendAsyncMessage("set-complete-status-unknown");
+    return new Promise((resolve, reject) => {
+      const payRequest = new PaymentRequest(defaultMethods, defaultDetails, defaultOptions);
+      payRequest.show().then(response => {
+        response.complete().then(() => {
+          resolve();
+        }).catch(e => {
+          ok(false, "Unexpected error: " + e.name);
+          resolve();
+        });
+      }).catch( e => {
+        ok(false, "Unexpected error: " + e.name);
+        resolve();
+      });
+    });
+  }
+
   function testCannotMakePaymentShow() {
     return new Promise((resolve, reject) => {
       const payRequest = new PaymentRequest(nonSupportedMethods, defaultDetails);
       payRequest.canMakePayment().then(result => {
         ok(!result, "canMakePayment() should return false, but got " + result + ".");
         payRequest.show().then( () => {
           ok(false, "Should be rejected with 'NotSupportedError', but got resolved");
           resolve();
@@ -284,27 +350,31 @@ https://bugzilla.mozilla.org/show_bug.cg
       });
       gScript.sendAsyncMessage("test-init-data-and-response");
     });
   }
 
   function teardown() {
     gScript.addMessageListener("teardown-complete", function teardownCompleteHandler() {
       gScript.removeMessageListener("teardown-complete", teardownCompleteHandler);
-      gScript.removeMessageListener("test-fail", testFailHandler)
+      gScript.removeMessageListener("test-fail", testFailHandler);
+      gScript.removeMessageListener("test-pass", testPassHandler);
       gScript.destroy();
       SimpleTest.finish();
     });
     gScript.sendAsyncMessage("teardown");
   }
 
   function runTests() {
     testCannotMakePaymentShow()
     .then(testRejectShow)
-    .then(testShow)
+    .then(testShowWithSuccess)
+    .then(testShowWithFail)
+    .then(testShowWithUnknown)
+    .then(testShowWithEmpty)
     .then(testUpdateWithError)
     .then(testNullDetailsResponse)
     .then(teardown)
     .catch( e => {
       ok(false, "Unexpected error: " + e.name);
       SimpleTest.finish();
     });
   }