Bug 1447773 - Time out PaymentResponse after 5 seconds. draft
authorHenri Sivonen <hsivonen@hsivonen.fi>
Tue, 29 May 2018 12:31:22 +0300
changeset 810730 387370748fcb2aeec7d3244d3a7e997e874cb4a9
parent 809193 f18328bef6bb069e0b651c913281537b219b1dbd
push id114074
push userbmo:hsivonen@hsivonen.fi
push dateTue, 26 Jun 2018 10:22:35 +0000
bugs1447773
milestone62.0a1
Bug 1447773 - Time out PaymentResponse after 5 seconds. MozReview-Commit-ID: JnoKgLC6yL6
dom/payments/PaymentRequestManager.cpp
dom/payments/PaymentRequestManager.h
dom/payments/PaymentResponse.cpp
dom/payments/PaymentResponse.h
modules/libpref/init/StaticPrefList.h
--- a/dom/payments/PaymentRequestManager.cpp
+++ b/dom/payments/PaymentRequestManager.cpp
@@ -473,23 +473,28 @@ PaymentRequestManager::AbortPayment(Paym
 
   // If aDeferredShow is true, then show was called with a promise that was
   // rejected. In that case, we need to remember that we called show earlier.
   return SendRequestPayment(aRequest, action, aDeferredShow);
 }
 
 nsresult
 PaymentRequestManager::CompletePayment(PaymentRequest* aRequest,
-                                       const PaymentComplete& aComplete)
+                                       const PaymentComplete& aComplete,
+                                       bool aTimedOut)
 {
   nsString completeStatusString(NS_LITERAL_STRING("unknown"));
-  uint8_t completeIndex = static_cast<uint8_t>(aComplete);
-  if (completeIndex < ArrayLength(PaymentCompleteValues::strings)) {
-    completeStatusString.AssignASCII(
-      PaymentCompleteValues::strings[completeIndex].value);
+  if (aTimedOut) {
+    completeStatusString.AssignLiteral("timeout");
+  } else {
+    uint8_t completeIndex = static_cast<uint8_t>(aComplete);
+    if (completeIndex < ArrayLength(PaymentCompleteValues::strings)) {
+      completeStatusString.AssignASCII(
+        PaymentCompleteValues::strings[completeIndex].value);
+    }
   }
 
   nsAutoString requestId;
   aRequest->GetInternalId(requestId);
   IPCPaymentCompleteActionRequest action(requestId, completeStatusString);
 
   return SendRequestPayment(aRequest, action, false);
 }
--- a/dom/payments/PaymentRequestManager.h
+++ b/dom/payments/PaymentRequestManager.h
@@ -45,17 +45,18 @@ public:
                 const PaymentDetailsInit& aDetails,
                 const PaymentOptions& aOptions,
                 PaymentRequest** aRequest);
 
   nsresult CanMakePayment(PaymentRequest* aRequest);
   nsresult ShowPayment(PaymentRequest* aRequest);
   nsresult AbortPayment(PaymentRequest* aRequest, bool aDeferredShow);
   nsresult CompletePayment(PaymentRequest* aRequest,
-                           const PaymentComplete& aComplete);
+                           const PaymentComplete& aComplete,
+                           bool aTimedOut = false);
   nsresult UpdatePayment(JSContext* aCx,
                          PaymentRequest* aRequest,
                          const PaymentDetailsUpdate& aDetails,
                          bool aRequestShipping,
                          bool aDeferredShow);
 
   nsresult RespondPayment(PaymentRequest* aRequest,
                           const IPCPaymentActionResponse& aResponse);
--- a/dom/payments/PaymentResponse.cpp
+++ b/dom/payments/PaymentResponse.cpp
@@ -1,14 +1,15 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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 "mozilla/StaticPrefs.h"
 #include "mozilla/dom/PaymentResponse.h"
 #include "mozilla/dom/BasicCardPaymentBinding.h"
 #include "BasicCardPayment.h"
 #include "PaymentAddress.h"
 #include "PaymentRequestUtils.h"
 
 namespace mozilla {
 namespace dom {
@@ -17,16 +18,17 @@ NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Pa
                                       mShippingAddress, mPromise)
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(PaymentResponse)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(PaymentResponse)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PaymentResponse)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
+  NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
 NS_INTERFACE_MAP_END
 
 PaymentResponse::PaymentResponse(nsPIDOMWindowInner* aWindow,
                                  PaymentRequest* aRequest,
                                  const nsAString& aRequestId,
                                  const nsAString& aMethodName,
                                  const nsAString& aShippingOption,
                                  RefPtr<PaymentAddress> aShippingAddress,
@@ -44,16 +46,21 @@ PaymentResponse::PaymentResponse(nsPIDOM
   , mPayerName(aPayerName)
   , mPayerEmail(aPayerEmail)
   , mPayerPhone(aPayerPhone)
   , mShippingAddress(aShippingAddress)
 {
 
   // TODO: from https://github.com/w3c/browser-payment-api/issues/480
   // Add payerGivenName + payerFamilyName to PaymentAddress
+  NS_NewTimerWithCallback(getter_AddRefs(mTimer),
+                          this,
+                          StaticPrefs::dom_payments_response_timeout(),
+                          nsITimer::TYPE_ONE_SHOT,
+                          aWindow->EventTargetFor(TaskCategory::Other));
 }
 
 PaymentResponse::~PaymentResponse()
 {
 }
 
 JSObject*
 PaymentResponse::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
@@ -131,44 +138,68 @@ PaymentResponse::GetShippingAddress() co
 already_AddRefed<Promise>
 PaymentResponse::Complete(PaymentComplete result, ErrorResult& aRv)
 {
   if (mCompleteCalled) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return nullptr;
   }
 
-  nsIGlobalObject* global = mOwner->AsGlobal();
-  ErrorResult errResult;
-  RefPtr<Promise> promise = Promise::Create(global, errResult);
-  if (errResult.Failed()) {
-    aRv.Throw(NS_ERROR_FAILURE);
-    return nullptr;
+  mCompleteCalled = true;
+
+  if (mTimer) {
+    mTimer->Cancel();
+    mTimer = nullptr;
   }
 
-  mCompleteCalled = true;
-
   RefPtr<PaymentRequestManager> manager = PaymentRequestManager::GetSingleton();
   if (NS_WARN_IF(!manager)) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
   nsresult rv = manager->CompletePayment(mRequest, result);
   if (NS_WARN_IF(NS_FAILED(rv))) {
-    promise->MaybeReject(NS_ERROR_FAILURE);
-    return promise.forget();
+    aRv.Throw(NS_ERROR_FAILURE);
+    return nullptr;
+  }
+
+  nsIGlobalObject* global = mOwner->AsGlobal();
+  ErrorResult errResult;
+  RefPtr<Promise> promise = Promise::Create(global, errResult);
+  if (errResult.Failed()) {
+    aRv.Throw(NS_ERROR_FAILURE);
+    return nullptr;
   }
 
   mPromise = promise;
   return promise.forget();
 }
 
 void
 PaymentResponse::RespondComplete()
 {
-  MOZ_ASSERT(mPromise);
+  // mPromise may be null when timing out
+  if (mPromise) {
+    mPromise->MaybeResolve(JS::UndefinedHandleValue);
+    mPromise = nullptr;
+  }
+}
 
-  mPromise->MaybeResolve(JS::UndefinedHandleValue);
-  mPromise = nullptr;
+NS_IMETHODIMP
+PaymentResponse::Notify(nsITimer *timer)
+{
+  mTimer = nullptr;
+  if (mCompleteCalled) {
+    return NS_OK;
+  }
+
+  mCompleteCalled = true;
+
+  RefPtr<PaymentRequestManager> manager = PaymentRequestManager::GetSingleton();
+  if (NS_WARN_IF(!manager)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  return manager->CompletePayment(mRequest, PaymentComplete::Unknown, true);
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/payments/PaymentResponse.h
+++ b/dom/payments/PaymentResponse.h
@@ -5,31 +5,34 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_PaymentResponse_h
 #define mozilla_dom_PaymentResponse_h
 
 #include "mozilla/dom/PaymentResponseBinding.h" // PaymentComplete
 #include "nsPIDOMWindow.h"
 #include "nsWrapperCache.h"
+#include "nsITimer.h"
 
 namespace mozilla {
 namespace dom {
 
 class PaymentAddress;
 class PaymentRequest;
 class Promise;
 
-class PaymentResponse final : public nsISupports,
+class PaymentResponse final : public nsITimerCallback,
                               public nsWrapperCache
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(PaymentResponse)
 
+  NS_IMETHOD Notify(nsITimer* aTimer) override;
+
   PaymentResponse(nsPIDOMWindowInner* aWindow,
                   PaymentRequest* aRequest,
                   const nsAString& aRequestId,
                   const nsAString& aMethodName,
                   const nsAString& aShippingOption,
                   RefPtr<PaymentAddress> aShippingAddress,
                   const nsAString& aDetails,
                   const nsAString& aPayerName,
@@ -78,14 +81,17 @@ private:
   nsString mDetails;
   nsString mShippingOption;
   nsString mPayerName;
   nsString mPayerEmail;
   nsString mPayerPhone;
   RefPtr<PaymentAddress> mShippingAddress;
   // Promise for "PaymentResponse::Complete"
   RefPtr<Promise> mPromise;
+  // Timer for timing out if the page doesn't call
+  // complete()
+  nsCOMPtr<nsITimer> mTimer;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_PaymentResponse_h
--- a/modules/libpref/init/StaticPrefList.h
+++ b/modules/libpref/init/StaticPrefList.h
@@ -111,16 +111,24 @@ VARCACHE_PREF(
   RelaxedAtomicBool, false
 )
 
 // If true. then the service worker interception and the ServiceWorkerManager
 // will live in the parent process.  This only takes effect on browser start.
 // Note, this is not currently safe to use for normal browsing yet.
 PREF("dom.serviceWorkers.parent_intercept", bool, false)
 
+// Time in milliseconds for PaymentResponse to wait for
+// the Web page to call complete().
+VARCACHE_PREF(
+  "dom.payments.response.timeout",
+   dom_payments_response_timeout,
+  uint32_t, 5000
+)
+
 //---------------------------------------------------------------------------
 // Clear-Site-Data prefs
 //---------------------------------------------------------------------------
 
 #ifdef NIGHTLY
 # define PREF_VALUE true
 #else
 # define PREF_VALUE false