Bug 1472026 - Implement PaymentResponse.prototype.onpayerdetailchange. r=edenchuang,baku

Implement PaymentResponse.prototype.onpayerdetailchange, per spec.

Differential Revision: https://phabricator.services.mozilla.com/D5841

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Marcos Cáceres 2018-09-19 08:41:53 +00:00
parent 2e78564a84
commit 9dd1b3aeae
18 changed files with 499 additions and 104 deletions

View File

@ -54,6 +54,18 @@ interface nsIPaymentRequestService : nsISupports
*/
void changeShippingOption(in AString requestId, in AString option);
/**
* Inform the merchant the payer's details changed in the PaymentResponse.
* @param requestId - the request identifier of the payment request.
* @param aPayerName - the changed payer's name.
* @param aPayerEmail - the changed payer's email.
* @param aPayerPhone - the changed payer's phone.
*/
void changePayerDetail(in AString requestId,
in AString aPayerName,
in AString aPayerEmail,
in AString aPayerPhone);
/**
* Following APIs are for testing or platform code only. UI implementation
* should not use them.

View File

@ -605,7 +605,6 @@ PaymentRequest::Constructor(const GlobalObject& aGlobal,
aRv.Throw(NS_ERROR_DOM_TYPE_ERR);
return nullptr;
}
return request.forget();
}
@ -965,6 +964,13 @@ PaymentRequest::SetUpdating(bool aUpdating)
mUpdating = aUpdating;
}
already_AddRefed<PaymentResponse>
PaymentRequest::GetResponse() const
{
RefPtr<PaymentResponse> response = mResponse;
return response.forget();
}
nsresult
PaymentRequest::DispatchUpdateEvent(const nsAString& aType)
{
@ -1072,6 +1078,16 @@ PaymentRequest::GetShippingType() const
return mShippingType;
}
void PaymentRequest::GetOptions(PaymentOptions& aRetVal) const
{
aRetVal = mOptions;
}
void PaymentRequest::SetOptions(const PaymentOptions& aOptions)
{
mOptions = aOptions;
}
void
PaymentRequest::ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue)
{

View File

@ -24,20 +24,23 @@ class PaymentAddress;
class PaymentRequestChild;
class PaymentResponse;
class PaymentRequest final : public DOMEventTargetHelper
, public PromiseNativeHandler
, public nsIDocumentActivity
class PaymentRequest final
: public DOMEventTargetHelper
, public PromiseNativeHandler
, public nsIDocumentActivity
{
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(PaymentRequest, DOMEventTargetHelper)
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(PaymentRequest,
DOMEventTargetHelper)
NS_DECL_NSIDOCUMENTACTIVITY
virtual JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override;
static already_AddRefed<PaymentRequest>
CreatePaymentRequest(nsPIDOMWindowInner* aWindow, nsresult& aRv);
static already_AddRefed<PaymentRequest> CreatePaymentRequest(
nsPIDOMWindowInner* aWindow,
nsresult& aRv);
static bool PrefEnabled(JSContext* aCx, JSObject* aObj);
@ -47,56 +50,51 @@ public:
static nsresult IsValidPaymentMethodIdentifier(const nsAString& aIdentifier,
nsAString& aErrorMsg);
static nsresult IsValidMethodData(JSContext* aCx,
const Sequence<PaymentMethodData>& aMethodData,
nsAString& aErrorMsg);
static nsresult IsValidMethodData(
JSContext* aCx,
const Sequence<PaymentMethodData>& aMethodData,
nsAString& aErrorMsg);
static nsresult
IsValidNumber(const nsAString& aItem,
const nsAString& aStr,
nsAString& aErrorMsg);
static nsresult
IsNonNegativeNumber(const nsAString& aItem,
const nsAString& aStr,
nsAString& aErrorMsg);
static nsresult IsValidNumber(const nsAString& aItem,
const nsAString& aStr,
nsAString& aErrorMsg);
static nsresult IsNonNegativeNumber(const nsAString& aItem,
const nsAString& aStr,
nsAString& aErrorMsg);
static nsresult
IsValidCurrencyAmount(const nsAString& aItem,
const PaymentCurrencyAmount& aAmount,
const bool aIsTotalItem,
nsAString& aErrorMsg);
static nsresult IsValidCurrencyAmount(const nsAString& aItem,
const PaymentCurrencyAmount& aAmount,
const bool aIsTotalItem,
nsAString& aErrorMsg);
static nsresult
IsValidCurrency(const nsAString& aItem,
const nsAString& aCurrency,
nsAString& aErrorMsg);
static nsresult IsValidCurrency(const nsAString& aItem,
const nsAString& aCurrency,
nsAString& aErrorMsg);
static nsresult
IsValidDetailsInit(const PaymentDetailsInit& aDetails,
const bool aRequestShipping,
nsAString& aErrorMsg);
static nsresult IsValidDetailsInit(const PaymentDetailsInit& aDetails,
const bool aRequestShipping,
nsAString& aErrorMsg);
static nsresult
IsValidDetailsUpdate(const PaymentDetailsUpdate& aDetails,
const bool aRequestShipping);
static nsresult IsValidDetailsUpdate(const PaymentDetailsUpdate& aDetails,
const bool aRequestShipping);
static nsresult
IsValidDetailsBase(const PaymentDetailsBase& aDetails,
const bool aRequestShipping,
nsAString& aErrorMsg);
static nsresult IsValidDetailsBase(const PaymentDetailsBase& aDetails,
const bool aRequestShipping,
nsAString& aErrorMsg);
static already_AddRefed<PaymentRequest>
Constructor(const GlobalObject& aGlobal,
const Sequence<PaymentMethodData>& aMethodData,
const PaymentDetailsInit& aDetails,
const PaymentOptions& aOptions,
ErrorResult& aRv);
static already_AddRefed<PaymentRequest> Constructor(
const GlobalObject& aGlobal,
const Sequence<PaymentMethodData>& aMethodData,
const PaymentDetailsInit& aDetails,
const PaymentOptions& aOptions,
ErrorResult& aRv);
already_AddRefed<Promise> CanMakePayment(ErrorResult& aRv);
void RespondCanMakePayment(bool aResult);
already_AddRefed<Promise> Show(const Optional<OwningNonNull<Promise>>& detailsPromise,
ErrorResult& aRv);
already_AddRefed<Promise> Show(
const Optional<OwningNonNull<Promise>>& detailsPromise,
ErrorResult& aRv);
void RespondShowPayment(const nsAString& aMethodName,
const nsAString& aDetails,
const nsAString& aPayerName,
@ -121,6 +119,8 @@ public:
bool IsUpdating() const { return mUpdating; }
void SetUpdating(bool aUpdating);
already_AddRefed<PaymentResponse> GetResponse() const;
already_AddRefed<PaymentAddress> GetShippingAddress() const;
// Update mShippingAddress and fire shippingaddresschange event
nsresult UpdateShippingAddress(const nsAString& aCountry,
@ -134,42 +134,36 @@ public:
const nsAString& aRecipient,
const nsAString& aPhone);
void SetShippingOption(const nsAString& aShippingOption);
void GetShippingOption(nsAString& aRetVal) const;
void GetOptions(PaymentOptions& aRetVal) const;
void SetOptions(const PaymentOptions& aOptions);
nsresult UpdateShippingOption(const nsAString& aShippingOption);
nsresult UpdatePayment(JSContext* aCx, const PaymentDetailsUpdate& aDetails,
nsresult UpdatePayment(JSContext* aCx,
const PaymentDetailsUpdate& aDetails,
bool aDeferredShow);
void AbortUpdate(nsresult aRv, bool aDeferredShow);
void SetShippingType(const Nullable<PaymentShippingType>& aShippingType);
Nullable<PaymentShippingType> GetShippingType() const;
inline void ShippingWasRequested()
{
mRequestShipping = true;
}
inline void ShippingWasRequested() { mRequestShipping = true; }
void
ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
void
RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
IMPL_EVENT_HANDLER(merchantvalidation);
IMPL_EVENT_HANDLER(shippingaddresschange);
IMPL_EVENT_HANDLER(shippingoptionchange);
IMPL_EVENT_HANDLER(paymentmethodchange);
void SetIPC(PaymentRequestChild* aChild)
{
mIPC = aChild;
}
void SetIPC(PaymentRequestChild* aChild) { mIPC = aChild; }
PaymentRequestChild* GetIPC()
{
return mIPC;
}
PaymentRequestChild* GetIPC() const { return mIPC; }
private:
PaymentOptions mOptions;
protected:
~PaymentRequest();
@ -206,8 +200,8 @@ protected:
Nullable<PaymentShippingType> mShippingType;
// "true" when there is a pending updateWith() call to update the payment request
// and "false" otherwise.
// "true" when there is a pending updateWith() call to update the payment
// request and "false" otherwise.
bool mUpdating;
// Whether shipping was requested. This models [[options]].requestShipping,
@ -221,7 +215,8 @@ protected:
// The error is set in AbortUpdate(). The value is NS_OK by default.
nsresult mUpdateError;
enum {
enum
{
eUnknown,
eCreated,
eInteractive,
@ -230,7 +225,6 @@ protected:
PaymentRequestChild* mIPC;
};
} // namespace dom
} // namespace mozilla

View File

@ -382,7 +382,7 @@ PaymentRequestManager::CreatePayment(JSContext* aCx,
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
request->SetOptions(aOptions);
/*
* Set request's |mId| to details.id if details.id exists.
* Otherwise, set |mId| to internal id.
@ -687,5 +687,20 @@ PaymentRequestManager::ChangeShippingOption(PaymentRequest* aRequest,
return aRequest->UpdateShippingOption(aOption);
}
nsresult
PaymentRequestManager::ChangePayerDetail(PaymentRequest* aRequest,
const nsAString& aPayerName,
const nsAString& aPayerEmail,
const nsAString& aPayerPhone)
{
MOZ_ASSERT(aRequest);
RefPtr<PaymentResponse> response = aRequest->GetResponse();
// ignoring the case call changePayerDetail during show().
if (!response) {
return NS_OK;
}
return response->UpdatePayerDetail(aPayerName, aPayerEmail, aPayerPhone);
}
} // end of namespace dom
} // end of namespace mozilla

View File

@ -69,6 +69,11 @@ public:
nsresult ChangeShippingOption(PaymentRequest* aRequest,
const nsAString& aOption);
nsresult ChangePayerDetail(PaymentRequest* aRequest,
const nsAString& aPayerName,
const nsAString& aPayerEmail,
const nsAString& aPayerPhone);
// Called to ensure that we don't "leak" aRequest if we shut down while it had
// an active request to the parent.
void RequestIPCOver(PaymentRequest* aRequest);

View File

@ -564,5 +564,31 @@ PaymentRequestService::IsBasicCardPayment(const nsAString& aRequestId)
return false;
}
NS_IMETHODIMP
PaymentRequestService::ChangePayerDetail(const nsAString& aRequestId,
const nsAString& aPayerName,
const nsAString& aPayerEmail,
const nsAString& aPayerPhone)
{
nsCOMPtr<nsIPaymentRequest> request;
nsresult rv = GetPaymentRequestById(aRequestId, getter_AddRefs(request));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
MOZ_ASSERT(request);
payments::PaymentRequest* rowRequest =
static_cast<payments::PaymentRequest*>(request.get());
if (!rowRequest->GetIPC()) {
return NS_ERROR_FAILURE;
}
rv = rowRequest->GetIPC()->ChangePayerDetail(
aRequestId, aPayerName, aPayerEmail, aPayerPhone);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
} // end of namespace dom
} // end of namespace mozilla

View File

@ -7,24 +7,43 @@
#include "mozilla/StaticPrefs.h"
#include "mozilla/dom/PaymentResponse.h"
#include "mozilla/dom/BasicCardPaymentBinding.h"
#include "mozilla/dom/PaymentRequestUpdateEvent.h"
#include "BasicCardPayment.h"
#include "PaymentAddress.h"
#include "PaymentRequestUtils.h"
#include "mozilla/EventStateManager.h"
namespace mozilla {
namespace dom {
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(PaymentResponse, mOwner,
mShippingAddress, mPromise)
NS_IMPL_CYCLE_COLLECTION_CLASS(PaymentResponse)
NS_IMPL_CYCLE_COLLECTING_ADDREF(PaymentResponse)
NS_IMPL_CYCLE_COLLECTING_RELEASE(PaymentResponse)
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(PaymentResponse,
DOMEventTargetHelper)
// Don't need NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER because
// DOMEventTargetHelper does it for us.
NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(PaymentResponse,
DOMEventTargetHelper)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mShippingAddress)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPromise)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTimer)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(PaymentResponse,
DOMEventTargetHelper)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mShippingAddress)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPromise)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mTimer)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
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
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
NS_IMPL_ADDREF_INHERITED(PaymentResponse, DOMEventTargetHelper)
NS_IMPL_RELEASE_INHERITED(PaymentResponse, DOMEventTargetHelper)
PaymentResponse::PaymentResponse(nsPIDOMWindowInner* aWindow,
PaymentRequest* aRequest,
@ -36,7 +55,7 @@ PaymentResponse::PaymentResponse(nsPIDOMWindowInner* aWindow,
const nsAString& aPayerName,
const nsAString& aPayerEmail,
const nsAString& aPayerPhone)
: mOwner(aWindow)
: DOMEventTargetHelper(aWindow)
, mCompleteCalled(false)
, mRequest(aRequest)
, mRequestId(aRequestId)
@ -58,9 +77,7 @@ PaymentResponse::PaymentResponse(nsPIDOMWindowInner* aWindow,
aWindow->EventTargetFor(TaskCategory::Other));
}
PaymentResponse::~PaymentResponse()
{
}
PaymentResponse::~PaymentResponse() = default;
JSObject*
PaymentResponse::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
@ -81,7 +98,8 @@ PaymentResponse::GetMethodName(nsString& aRetVal) const
}
void
PaymentResponse::GetDetails(JSContext* aCx, JS::MutableHandle<JSObject*> aRetVal) const
PaymentResponse::GetDetails(JSContext* aCx,
JS::MutableHandle<JSObject*> aRetVal) const
{
RefPtr<BasicCardService> service = BasicCardService::GetService();
MOZ_ASSERT(service);
@ -89,7 +107,7 @@ PaymentResponse::GetDetails(JSContext* aCx, JS::MutableHandle<JSObject*> aRetVal
DeserializeToJSObject(mDetails, aCx, aRetVal);
} else {
BasicCardResponse response;
nsresult rv = service->DecodeBasicCardData(mDetails, mOwner, response);
nsresult rv = service->DecodeBasicCardData(mDetails, GetOwner(), response);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
@ -115,12 +133,14 @@ PaymentResponse::GetPayerName(nsString& aRetVal) const
aRetVal = mPayerName;
}
void PaymentResponse::GetPayerEmail(nsString& aRetVal) const
void
PaymentResponse::GetPayerEmail(nsString& aRetVal) const
{
aRetVal = mPayerEmail;
}
void PaymentResponse::GetPayerPhone(nsString& aRetVal) const
void
PaymentResponse::GetPayerPhone(nsString& aRetVal) const
{
aRetVal = mPayerPhone;
}
@ -161,7 +181,12 @@ PaymentResponse::Complete(PaymentComplete result, ErrorResult& aRv)
return nullptr;
}
nsIGlobalObject* global = mOwner->AsGlobal();
if (NS_WARN_IF(!GetOwner())) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
nsIGlobalObject* global = GetOwner()->AsGlobal();
ErrorResult errResult;
RefPtr<Promise> promise = Promise::Create(global, errResult);
if (errResult.Failed()) {
@ -188,7 +213,7 @@ PaymentResponse::Retry(JSContext* aCx,
const PaymentValidationErrors& aErrors,
ErrorResult& aRv)
{
nsIGlobalObject* global = mOwner->AsGlobal();
nsIGlobalObject* global = GetOwner()->AsGlobal();
ErrorResult errResult;
RefPtr<Promise> promise = Promise::Create(global, errResult);
if (errResult.Failed()) {
@ -201,7 +226,12 @@ PaymentResponse::Retry(JSContext* aCx,
mTimer = nullptr;
}
nsIDocument* doc = mOwner->GetExtantDoc();
if (NS_WARN_IF(!GetOwner())) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
nsIDocument* doc = GetOwner()->GetExtantDoc();
if (!doc || !doc->IsCurrentActiveDocument()) {
promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
return promise.forget();
@ -246,11 +276,15 @@ PaymentResponse::RespondRetry(const nsAString& aMethodName,
mPayerEmail = aPayerEmail;
mPayerPhone = aPayerPhone;
if (NS_WARN_IF(!GetOwner())) {
return;
}
NS_NewTimerWithCallback(getter_AddRefs(mTimer),
this,
StaticPrefs::dom_payments_response_timeout(),
nsITimer::TYPE_ONE_SHOT,
mOwner->EventTargetFor(TaskCategory::Other));
GetOwner()->EventTargetFor(TaskCategory::Other));
MOZ_ASSERT(mRetryPromise);
mRetryPromise->MaybeResolve(JS::UndefinedHandleValue);
mRetryPromise = nullptr;
@ -265,7 +299,8 @@ PaymentResponse::RejectRetry(nsresult aRejectReason)
}
nsresult
PaymentResponse::ValidatePaymentValidationErrors(const PaymentValidationErrors& aErrors)
PaymentResponse::ValidatePaymentValidationErrors(
const PaymentValidationErrors& aErrors)
{
// Should not be empty errors
// check PaymentValidationErrors.error
@ -318,8 +353,7 @@ PaymentResponse::ValidatePaymentValidationErrors(const PaymentValidationErrors&
!addErrors.mRecipient.Value().IsEmpty()) {
return NS_OK;
}
if (addErrors.mRegion.WasPassed() &&
!addErrors.mRegion.Value().IsEmpty()) {
if (addErrors.mRegion.WasPassed() && !addErrors.mRegion.Value().IsEmpty()) {
return NS_OK;
}
if (addErrors.mRegionCode.WasPassed() &&
@ -334,7 +368,7 @@ PaymentResponse::ValidatePaymentValidationErrors(const PaymentValidationErrors&
}
NS_IMETHODIMP
PaymentResponse::Notify(nsITimer *timer)
PaymentResponse::Notify(nsITimer* timer)
{
mTimer = nullptr;
if (mCompleteCalled) {
@ -351,5 +385,39 @@ PaymentResponse::Notify(nsITimer *timer)
return manager->CompletePayment(mRequest, PaymentComplete::Unknown, true);
}
nsresult
PaymentResponse::UpdatePayerDetail(const nsAString& aPayerName,
const nsAString& aPayerEmail,
const nsAString& aPayerPhone)
{
MOZ_ASSERT(mRequest->ReadyForUpdate());
PaymentOptions options;
mRequest->GetOptions(options);
if (options.mRequestPayerName) {
mPayerName = aPayerName;
}
if (options.mRequestPayerEmail) {
mPayerEmail = aPayerEmail;
}
if (options.mRequestPayerPhone) {
mPayerPhone = aPayerPhone;
}
return DispatchUpdateEvent(NS_LITERAL_STRING("payerdetailchange"));
}
nsresult
PaymentResponse::DispatchUpdateEvent(const nsAString& aType)
{
PaymentRequestUpdateEventInit init;
RefPtr<PaymentRequestUpdateEvent> event =
PaymentRequestUpdateEvent::Constructor(this, aType, init);
event->SetTrusted(true);
event->SetRequest(mRequest);
ErrorResult rv;
DispatchEvent(*event, rv);
return rv.StealNSResult();
}
} // namespace dom
} // namespace mozilla

View File

@ -7,9 +7,9 @@
#ifndef mozilla_dom_PaymentResponse_h
#define mozilla_dom_PaymentResponse_h
#include "mozilla/DOMEventTargetHelper.h"
#include "mozilla/dom/PaymentResponseBinding.h" // PaymentComplete
#include "nsPIDOMWindow.h"
#include "nsWrapperCache.h"
#include "nsITimer.h"
namespace mozilla {
@ -19,12 +19,15 @@ class PaymentAddress;
class PaymentRequest;
class Promise;
class PaymentResponse final : public nsITimerCallback,
public nsWrapperCache
class PaymentResponse final
: public DOMEventTargetHelper
, public nsITimerCallback
{
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(PaymentResponse)
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(PaymentResponse,
DOMEventTargetHelper)
NS_IMETHOD Notify(nsITimer* aTimer) override;
@ -39,11 +42,6 @@ public:
const nsAString& aPayerEmail,
const nsAString& aPayerPhone);
nsPIDOMWindowInner* GetParentObject() const
{
return mOwner;
}
virtual JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override;
@ -69,6 +67,12 @@ public:
void RespondComplete();
IMPL_EVENT_HANDLER(payerdetailchange);
nsresult UpdatePayerDetail(const nsAString& aPayerName,
const nsAString& aPayerEmail,
const nsAString& aPayerPhone);
already_AddRefed<Promise> Retry(JSContext* aCx,
const PaymentValidationErrors& errorField,
ErrorResult& aRv);
@ -87,8 +91,9 @@ protected:
nsresult ValidatePaymentValidationErrors(const PaymentValidationErrors& aErrors);
nsresult DispatchUpdateEvent(const nsAString& aType);
private:
nsCOMPtr<nsPIDOMWindowInner> mOwner;
bool mCompleteCalled;
PaymentRequest* mRequest;
nsString mRequestId;

View File

@ -199,6 +199,10 @@ child:
IPCPaymentAddress aAddress);
async ChangeShippingOption(nsString aRequestId,
nsString aOption);
async ChangePayerDetail(nsString aRequestId,
nsString aPayerName,
nsString aPayerEmail,
nsString aPayerPhone);
};
} // end of namespace dom

View File

@ -82,6 +82,25 @@ PaymentRequestChild::RecvChangeShippingOption(const nsString& aRequestId,
return IPC_OK();
}
mozilla::ipc::IPCResult
PaymentRequestChild::RecvChangePayerDetail(const nsString& aRequestId,
const nsString& aPayerName,
const nsString& aPayerEmail,
const nsString& aPayerPhone)
{
if (!mRequest) {
return IPC_FAIL_NO_REASON(this);
}
RefPtr<PaymentRequestManager> manager = PaymentRequestManager::GetSingleton();
MOZ_ASSERT(manager);
RefPtr<PaymentRequest> request(mRequest);
nsresult rv = manager->ChangePayerDetail(request, aPayerName, aPayerEmail, aPayerPhone);
if (NS_WARN_IF(NS_FAILED(rv))) {
return IPC_FAIL_NO_REASON(this);
}
return IPC_OK();
}
void
PaymentRequestChild::ActorDestroy(ActorDestroyReason aWhy)
{

View File

@ -35,6 +35,12 @@ protected:
RecvChangeShippingOption(const nsString& aRequestId,
const nsString& aOption) override;
mozilla::ipc::IPCResult
RecvChangePayerDetail(const nsString& aRequestId,
const nsString& aPayerName,
const nsString& aPayerEmail,
const nsString& aPayerPhone) override;
void ActorDestroy(ActorDestroyReason aWhy) override;
private:

View File

@ -299,6 +299,34 @@ PaymentRequestParent::ChangeShippingOption(const nsAString& aRequestId,
return NS_OK;
}
nsresult
PaymentRequestParent::ChangePayerDetail(const nsAString& aRequestId,
const nsAString& aPayerName,
const nsAString& aPayerEmail,
const nsAString& aPayerPhone)
{
nsAutoString requestId(aRequestId);
nsAutoString payerName(aPayerName);
nsAutoString payerEmail(aPayerEmail);
nsAutoString payerPhone(aPayerPhone);
if (!NS_IsMainThread()) {
RefPtr<PaymentRequestParent> self = this;
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction("dom::PaymentRequestParent::ChangePayerDetail",
[self, requestId, payerName, payerEmail, payerPhone] ()
{
self->ChangePayerDetail(requestId, payerName, payerEmail, payerPhone);
});
return NS_DispatchToMainThread(r);
}
if (!mActorAlive) {
return NS_ERROR_FAILURE;
}
if (!SendChangePayerDetail(requestId, payerName, payerEmail, payerPhone)) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
mozilla::ipc::IPCResult
PaymentRequestParent::Recv__delete__()
{

View File

@ -26,6 +26,10 @@ public:
nsIPaymentAddress* aAddress);
nsresult ChangeShippingOption(const nsAString& aRequestId,
const nsAString& aOption);
nsresult ChangePayerDetail(const nsAString& aRequestId,
const nsAString& aPayerName,
const nsAString& aPayerEmail,
const nsAString& aPayerPhone);
protected:
mozilla::ipc::IPCResult

View File

@ -0,0 +1,80 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);
const paymentSrv = Cc[
"@mozilla.org/dom/payments/payment-request-service;1"
].getService(Ci.nsIPaymentRequestService);
const TestingUIService = {
showPayment(requestId, name = "", email = "", phone = "") {
const showResponseData = Cc[
"@mozilla.org/dom/payments/general-response-data;1"
].createInstance(Ci.nsIGeneralResponseData);
showResponseData.initData({});
const 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
name,
email,
phone
);
paymentSrv.respondPayment(
showResponse.QueryInterface(Ci.nsIPaymentActionResponse)
);
},
// .retry({ payer }) and .updateWith({payerErrors}) both get routed here:
updatePayment(requestId) {
// Let's echo what was sent in by the error...
const request = paymentSrv.getPaymentRequestById(requestId);
const { name, email, phone } = request.paymentDetails.payer;
const { error } = request.paymentDetails;
// Let's use the .error as the switch
switch (error) {
case "retry-fire-payerdetaichangeevent": {
paymentSrv.changePayerDetail(requestId, name, email, phone);
break;
}
case "update-with": {
this.showPayment(requestId, name, email, phone);
break;
}
default:
const msg = `Expect details.error value: '${error}'`;
sendAsyncMessage("test-fail", msg);
}
},
completePayment(requestId) {
const request = paymentSrv.getPaymentRequestById(requestId);
const 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)
);
},
get QueryInterface() {
return ChromeUtils.generateQI([Ci.nsIPaymentUIService]);
},
};
paymentSrv.setTestingUIService(
TestingUIService.QueryInterface(Ci.nsIPaymentUIService)
);
addMessageListener("teardown", () => {
paymentSrv.setTestingUIService(null);
sendAsyncMessage("teardown-complete");
});

View File

@ -13,6 +13,7 @@ support-files =
CurrencyAmountValidationChromeScript.js
DefaultData.js
GeneralChromeScript.js
PayerDetailsChromeScript.js
PMIValidationChromeScript.js
RequestShippingChromeScript.js
RetryPaymentChromeScript.js
@ -24,6 +25,7 @@ support-files =
run-if = nightly_build # Bug 1390018: Depends on the Nightly-only UI service
[test_basiccard.html]
[test_block_none10s.html]
[test_payerDetails.html]
skip-if = e10s # Bug 1408250: Don't expose PaymentRequest Constructor in non-e10s
[test_bug1490698.html]
[test_canMakePayment.html]

View File

@ -0,0 +1,108 @@
<!DOCTYPE HTML>
<meta charset="utf-8">
<title>Test for PaymentResponse.prototype.onpayerdetailchange</title>
<link rel="stylesheet" href="/tests/SimpleTest/test.css" />
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<script src="./DefaultData.js"></script>
<script>
SimpleTest.waitForExplicitFinish();
SimpleTest.requestLongerTimeout(1000);
const gUrl = SimpleTest.getTestFileURL("PayerDetailsChromeScript.js");
const gScript = SpecialPowers.loadChromeScript(gUrl);
function okTester(result) {
return message => ok(result, message);
}
const passListener = okTester(true);
const failListener = okTester(false);
gScript.addMessageListener("test-fail", failListener);
gScript.addMessageListener("test-pass", passListener);
function sendOnce(message) {
return data => {
return new Promise(resolve => {
const doneMsg = `${message}-complete`;
gScript.addMessageListener(doneMsg, function listener() {
gScript.removeMessageListener(doneMsg, listener);
resolve();
});
gScript.sendAsyncMessage(message, data);
});
};
}
const sendTearDown = sendOnce("teardown");
async function loopTest(iterations) {
const handler = SpecialPowers.getDOMWindowUtils(window).setHandlingUserInput(
true
);
const options = {
requestPayerName: true,
requestPayerEmail: true,
requestPayerPhone: true,
}
const request = new PaymentRequest(defaultMethods, defaultDetails, options);
const response = await request.show();
is(response.payerName, "", ".payerName must initially be ''");
is(response.payerEmail, "", ".payerEmail must initially be ''");
is(response.payerPhone, "", ".payerPhone must initially be ''");
for (let i = 0; i < iterations; i++) {
const payer = {
name: `test name ${i}`,
phone: `test phone ${i}`,
email: `test email ${i}`,
}
// Capture the event to firing
const eventPromise = new Promise(resolve => {
response.onpayerdetailchange = resolve;
});
const retryPromise = response.retry({
error: "retry-fire-payerdetaichangeevent",
payer
});
const event = await eventPromise;
// Check things got updated
is(response.payerName, payer.name, `.payerName must be "${payer.name}"`);
is(response.payerEmail, payer.email, `.payerEmail must be "${payer.email}"`);
is(response.payerPhone, payer.phone, `.payerPhone must be "${payer.phone}"`);
// Finally, let's do an updateWith()
event.updateWith({ error: "update-with", payerErrors: payer, ...defaultDetails });
await retryPromise;
}
await response.complete("success");
handler.destruct();
}
async function teardown() {
await sendTearDown();
gScript.removeMessageListener("test-fail", failListener);
gScript.removeMessageListener("test-pass", passListener);
gScript.destroy();
SimpleTest.finish();
}
async function runTests() {
try {
await loopTest(5); // lets go around 5 times
} catch (err) {
ok(false, `Unexpected error: ${err}.`);
} finally {
await teardown();
}
}
window.addEventListener("load", () => {
const prefs = [["dom.payments.request.enabled", true]];
SpecialPowers.pushPrefEnv({ set: prefs }, runTests);
});
</script>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1472026">Mozilla Bug 1472026</a>

View File

@ -18,7 +18,7 @@ enum PaymentComplete {
[SecureContext,
Func="mozilla::dom::PaymentRequest::PrefEnabled"]
interface PaymentResponse {
interface PaymentResponse : EventTarget {
[Default] object toJSON();
readonly attribute DOMString requestId;
@ -36,4 +36,6 @@ interface PaymentResponse {
// If the dictionary argument has no required members, it must be optional.
[NewObject]
Promise<void> retry(optional PaymentValidationErrors errorFields);
attribute EventHandler onpayerdetailchange;
};

View File

@ -802,6 +802,7 @@ STATIC_ATOMS = [
Atom("onpagehide", "onpagehide"),
Atom("onpageshow", "onpageshow"),
Atom("onpaste", "onpaste"),
Atom("onpayerdetailchange", "onpayerdetailchange"),
Atom("onpaymentmethodchange", "onpaymentmethodchange"),
Atom("onpointerlockchange", "onpointerlockchange"),
Atom("onpointerlockerror", "onpointerlockerror"),