Bug 1435161 - Part 2 supporting PaymentResponse.retry(). r=baku

1. Add PaymentValidationErrors and PayerErrorFields in PaymentRequest.webidl
       and PaymentResponse.retry() in PaymentResponse.webidl
    2. Implement PaymentRequest.retryPayment() and
       PaymentRequestManager.retryPayment() in content process.
    3. Add IPCPaymentRetryActionRequest in PPaymentRequest.ipdl to transfer the
       error fields to chrome process.
    4. Implement PaymentResponse.retry() by reusing the code of show() and
       updateWith() of PaymentRequestService in chrome process.
This commit is contained in:
Eden Chuang 2018-09-04 12:28:40 +02:00
Родитель 92d58a1b87
Коммит 7058aae68b
14 изменённых файлов: 367 добавлений и 11 удалений

Просмотреть файл

@ -63,6 +63,10 @@ interface nsIPaymentDetails : nsISupports
readonly attribute AString error;
[implicit_jscontext]
readonly attribute jsval shippingAddressErrors;
[implicit_jscontext]
readonly attribute jsval payer;
[implicit_jscontext]
readonly attribute jsval paymentMethod;
};
[scriptable, builtinclass, uuid(d53f9f20-138e-47cc-9fd5-db16a3f6d301)]

Просмотреть файл

@ -745,10 +745,14 @@ PaymentRequest::Show(const Optional<OwningNonNull<Promise>>& aDetailsPromise,
void
PaymentRequest::RejectShowPayment(nsresult aRejectReason)
{
MOZ_ASSERT(mAcceptPromise);
MOZ_ASSERT(mAcceptPromise || mResponse);
MOZ_ASSERT(mState == eInteractive);
mAcceptPromise->MaybeReject(aRejectReason);
if (mResponse) {
mResponse->RejectRetry(aRejectReason);
} else {
mAcceptPromise->MaybeReject(aRejectReason);
}
mState = eClosed;
mAcceptPromise = nullptr;
}
@ -761,7 +765,7 @@ PaymentRequest::RespondShowPayment(const nsAString& aMethodName,
const nsAString& aPayerPhone,
nsresult aRv)
{
MOZ_ASSERT(mAcceptPromise);
MOZ_ASSERT(mAcceptPromise || mResponse);
MOZ_ASSERT(mState == eInteractive);
if (NS_FAILED(aRv)) {
@ -773,12 +777,17 @@ PaymentRequest::RespondShowPayment(const nsAString& aMethodName,
mShippingAddress.swap(mFullShippingAddress);
mFullShippingAddress = nullptr;
RefPtr<PaymentResponse> paymentResponse =
new PaymentResponse(GetOwner(), this, mId, aMethodName,
mShippingOption, mShippingAddress, aDetails,
aPayerName, aPayerEmail, aPayerPhone);
mResponse = paymentResponse;
mAcceptPromise->MaybeResolve(paymentResponse);
if (mResponse) {
mResponse->RespondRetry(aMethodName, mShippingOption, mShippingAddress,
aDetails, aPayerName, aPayerEmail, aPayerPhone);
} else {
RefPtr<PaymentResponse> paymentResponse =
new PaymentResponse(GetOwner(), this, mId, aMethodName,
mShippingOption, mShippingAddress, aDetails,
aPayerName, aPayerEmail, aPayerPhone);
mResponse = paymentResponse;
mAcceptPromise->MaybeResolve(paymentResponse);
}
mState = eClosed;
mAcceptPromise = nullptr;
@ -903,6 +912,22 @@ PaymentRequest::AbortUpdate(nsresult aRv, bool aDeferredShow)
mUpdateError = aRv;
}
nsresult
PaymentRequest::RetryPayment(JSContext* aCx, const PaymentValidationErrors& aErrors)
{
if (mState == eInteractive) {
return NS_ERROR_DOM_INVALID_STATE_ERR;
}
RefPtr<PaymentRequestManager> manager = PaymentRequestManager::GetSingleton();
MOZ_ASSERT(manager);
nsresult rv = manager->RetryPayment(aCx, this, aErrors);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
mState = eInteractive;
return NS_OK;
}
void
PaymentRequest::GetId(nsAString& aRetVal) const
{

Просмотреть файл

@ -109,6 +109,8 @@ public:
already_AddRefed<Promise> Abort(ErrorResult& aRv);
void RespondAbortPayment(bool aResult);
nsresult RetryPayment(JSContext* aCx, const PaymentValidationErrors& aErrors);
void GetId(nsAString& aRetVal) const;
void GetInternalId(nsAString& aRetVal);
void SetId(const nsAString& aId);

Просмотреть файл

@ -483,6 +483,31 @@ PaymentDetails::GetShippingAddressErrors(JSContext* aCx, JS::MutableHandleValue
return NS_OK;
}
NS_IMETHODIMP
PaymentDetails::GetPayer(JSContext* aCx, JS::MutableHandleValue aErrors)
{
PayerErrorFields errors;
errors.Init(mPayerErrors);
if (!ToJSValue(aCx, errors, aErrors)) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
NS_IMETHODIMP
PaymentDetails::GetPaymentMethod(JSContext* aCx, JS::MutableHandleValue aErrors)
{
if (mPaymentMethodErrors.IsEmpty()) {
aErrors.set(JS::NullValue());
return NS_OK;
}
nsresult rv = DeserializeToJSValue(mPaymentMethodErrors, aCx ,aErrors);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
nsresult
PaymentDetails::Update(nsIPaymentDetails* aDetails, const bool aRequestShipping)
{
@ -547,6 +572,19 @@ PaymentDetails::GetShippingAddressErrors() const
return mShippingAddressErrors;
}
nsresult
PaymentDetails::UpdateErrors(const nsAString& aError,
const nsAString& aPayerErrors,
const nsAString& aPaymentMethodErrors,
const nsAString& aShippingAddressErrors)
{
mError = aError;
mPayerErrors = aPayerErrors;
mPaymentMethodErrors = aPaymentMethodErrors;
mShippingAddressErrors = aShippingAddressErrors;
return NS_OK;
}
/* PaymentOptions */
NS_IMPL_ISUPPORTS(PaymentOptions,
@ -727,6 +765,20 @@ PaymentRequest::SetCompleteStatus(const nsAString& aCompleteStatus)
mCompleteStatus = aCompleteStatus;
}
nsresult
PaymentRequest::UpdateErrors(const nsAString& aError,
const nsAString& aPayerErrors,
const nsAString& aPaymentMethodErrors,
const nsAString& aShippingAddressErrors)
{
PaymentDetails* rowDetails = static_cast<PaymentDetails*>(mPaymentDetails.get());
MOZ_ASSERT(rowDetails);
return rowDetails->UpdateErrors(aError,
aPayerErrors,
aPaymentMethodErrors,
aShippingAddressErrors);
}
NS_IMETHODIMP
PaymentRequest::GetCompleteStatus(nsAString& aCompleteStatus)
{

Просмотреть файл

@ -133,6 +133,11 @@ public:
nsIPaymentDetails** aDetails);
nsresult Update(nsIPaymentDetails* aDetails, const bool aRequestShipping);
nsString GetShippingAddressErrors() const;
nsresult UpdateErrors(const nsAString& aError,
const nsAString& aPayerErrors,
const nsAString& aPaymentMethodErrors,
const nsAString& aShippingAddressErrors);
private:
PaymentDetails(const nsAString& aId,
nsIPaymentItem* aTotalItem,
@ -151,6 +156,8 @@ private:
nsCOMPtr<nsIArray> mModifiers;
nsString mError;
nsString mShippingAddressErrors;
nsString mPayerErrors;
nsString mPaymentMethodErrors;
};
class PaymentOptions final : public nsIPaymentOptions
@ -208,6 +215,12 @@ public:
void
SetCompleteStatus(const nsAString& aCompleteStatus);
nsresult
UpdateErrors(const nsAString& aError,
const nsAString& aPayerErrors,
const nsAString& aPaymentMethodErrors,
const nsAString& aShippingAddressErrors);
private:
~PaymentRequest() = default;

Просмотреть файл

@ -539,6 +539,44 @@ PaymentRequestManager::ClosePayment(PaymentRequest* aRequest)
return SendRequestPayment(aRequest, action, false);
}
nsresult
PaymentRequestManager::RetryPayment(JSContext* aCx,
PaymentRequest* aRequest,
const PaymentValidationErrors& aErrors)
{
NS_ENSURE_ARG_POINTER(aCx);
NS_ENSURE_ARG_POINTER(aRequest);
nsAutoString requestId;
aRequest->GetInternalId(requestId);
nsAutoString error(EmptyString());
if (aErrors.mError.WasPassed()) {
error = aErrors.mError.Value();
}
nsAutoString shippingAddressErrors(EmptyString());
aErrors.mShippingAddress.ToJSON(shippingAddressErrors);
nsAutoString payerErrors(EmptyString());
aErrors.mPayer.ToJSON(payerErrors);
nsAutoString paymentMethodErrors(EmptyString());
if (aErrors.mPaymentMethod.WasPassed()) {
JS::RootedObject object(aCx, aErrors.mPaymentMethod.Value());
nsresult rv = SerializeFromJSObject(aCx, object, paymentMethodErrors);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
IPCPaymentRetryActionRequest action(requestId,
error,
payerErrors,
paymentMethodErrors,
shippingAddressErrors);
return SendRequestPayment(aRequest, action);
}
nsresult
PaymentRequestManager::RespondPayment(PaymentRequest* aRequest,
const IPCPaymentActionResponse& aResponse)

Просмотреть файл

@ -58,6 +58,9 @@ public:
bool aRequestShipping,
bool aDeferredShow);
nsresult ClosePayment(PaymentRequest* aRequest);
nsresult RetryPayment(JSContext* aCx,
PaymentRequest* aRequest,
const PaymentValidationErrors& aErrors);
nsresult RespondPayment(PaymentRequest* aRequest,
const IPCPaymentActionResponse& aResponse);

Просмотреть файл

@ -378,6 +378,26 @@ PaymentRequestService::RequestPayment(const nsAString& aRequestId,
mRequestQueue.RemoveElement(payment);
break;
}
case IPCPaymentActionRequest::TIPCPaymentRetryActionRequest: {
const IPCPaymentRetryActionRequest& action = aAction;
nsCOMPtr<nsIPaymentRequest> payment;
rv = GetPaymentRequestById(aRequestId, getter_AddRefs(payment));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
MOZ_ASSERT(payment);
payments::PaymentRequest* rowPayment =
static_cast<payments::PaymentRequest*>(payment.get());
MOZ_ASSERT(rowPayment);
rowPayment->UpdateErrors(action.error(),
action.payerErrors(),
action.paymentMethodErrors(),
action.shippingAddressErrors());
MOZ_ASSERT(mShowingRequest == payment);
rv = LaunchUIAction(aRequestId,
IPCPaymentActionRequest::TIPCPaymentUpdateActionRequest);
break;
}
default: {
return NS_ERROR_FAILURE;
}

Просмотреть файл

@ -31,7 +31,7 @@ PaymentResponse::PaymentResponse(nsPIDOMWindowInner* aWindow,
const nsAString& aRequestId,
const nsAString& aMethodName,
const nsAString& aShippingOption,
RefPtr<PaymentAddress> aShippingAddress,
PaymentAddress* aShippingAddress,
const nsAString& aDetails,
const nsAString& aPayerName,
const nsAString& aPayerEmail,
@ -183,6 +183,156 @@ PaymentResponse::RespondComplete()
}
}
already_AddRefed<Promise>
PaymentResponse::Retry(JSContext* aCx,
const PaymentValidationErrors& aErrors,
ErrorResult& aRv)
{
nsIGlobalObject* global = mOwner->AsGlobal();
ErrorResult errResult;
RefPtr<Promise> promise = Promise::Create(global, errResult);
if (errResult.Failed()) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
if (mTimer) {
mTimer->Cancel();
mTimer = nullptr;
}
nsIDocument* doc = mOwner->GetExtantDoc();
if (!doc || !doc->IsCurrentActiveDocument()) {
promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
return promise.forget();
}
if (mCompleteCalled || mRetryPromise) {
promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
return promise.forget();
}
nsresult rv = ValidatePaymentValidationErrors(aErrors);
if (NS_WARN_IF(NS_FAILED(rv))) {
promise->MaybeReject(rv);
return promise.forget();
}
MOZ_ASSERT(mRequest);
rv = mRequest->RetryPayment(aCx, aErrors);
if (NS_WARN_IF(NS_FAILED(rv))) {
promise->MaybeReject(rv);
return promise.forget();
}
mRetryPromise = promise;
return promise.forget();
}
void
PaymentResponse::RespondRetry(const nsAString& aMethodName,
const nsAString& aShippingOption,
PaymentAddress* aShippingAddress,
const nsAString& aDetails,
const nsAString& aPayerName,
const nsAString& aPayerEmail,
const nsAString& aPayerPhone)
{
mMethodName = aMethodName;
mShippingOption = aShippingOption;
mShippingAddress = aShippingAddress;
mDetails = aDetails;
mPayerName = aPayerName;
mPayerEmail = aPayerEmail;
mPayerPhone = aPayerPhone;
NS_NewTimerWithCallback(getter_AddRefs(mTimer),
this,
StaticPrefs::dom_payments_response_timeout(),
nsITimer::TYPE_ONE_SHOT,
mOwner->EventTargetFor(TaskCategory::Other));
MOZ_ASSERT(mRetryPromise);
mRetryPromise->MaybeResolve(JS::UndefinedHandleValue);
mRetryPromise = nullptr;
}
void
PaymentResponse::RejectRetry(nsresult aRejectReason)
{
MOZ_ASSERT(mRetryPromise);
mRetryPromise->MaybeReject(aRejectReason);
mRetryPromise = nullptr;
}
nsresult
PaymentResponse::ValidatePaymentValidationErrors(const PaymentValidationErrors& aErrors)
{
// Should not be empty errors
// check PaymentValidationErrors.error
if (aErrors.mError.WasPassed() && !aErrors.mError.Value().IsEmpty()) {
return NS_OK;
}
// check PaymentValidationErrors.payer
PayerErrorFields payerErrors(aErrors.mPayer);
if (payerErrors.mName.WasPassed() && !payerErrors.mName.Value().IsEmpty()) {
return NS_OK;
}
if (payerErrors.mEmail.WasPassed() && !payerErrors.mEmail.Value().IsEmpty()) {
return NS_OK;
}
if (payerErrors.mPhone.WasPassed() && !payerErrors.mPhone.Value().IsEmpty()) {
return NS_OK;
}
// check PaymentValidationErrors.paymentMethod
if (aErrors.mPaymentMethod.WasPassed()) {
return NS_OK;
}
// check PaymentValidationErrors.shippingAddress
AddressErrors addErrors(aErrors.mShippingAddress);
if (addErrors.mAddressLine.WasPassed() &&
!addErrors.mAddressLine.Value().IsEmpty()) {
return NS_OK;
}
if (addErrors.mCity.WasPassed() && !addErrors.mCity.Value().IsEmpty()) {
return NS_OK;
}
if (addErrors.mCountry.WasPassed() && !addErrors.mCountry.Value().IsEmpty()) {
return NS_OK;
}
if (addErrors.mDependentLocality.WasPassed() &&
!addErrors.mDependentLocality.Value().IsEmpty()) {
return NS_OK;
}
if (addErrors.mOrganization.WasPassed() &&
!addErrors.mOrganization.Value().IsEmpty()) {
return NS_OK;
}
if (addErrors.mPhone.WasPassed() && !addErrors.mPhone.Value().IsEmpty()) {
return NS_OK;
}
if (addErrors.mPostalCode.WasPassed() &&
!addErrors.mPostalCode.Value().IsEmpty()) {
return NS_OK;
}
if (addErrors.mRecipient.WasPassed() &&
!addErrors.mRecipient.Value().IsEmpty()) {
return NS_OK;
}
if (addErrors.mRegion.WasPassed() &&
!addErrors.mRegion.Value().IsEmpty()) {
return NS_OK;
}
if (addErrors.mRegionCode.WasPassed() &&
!addErrors.mRegionCode.Value().IsEmpty()) {
return NS_OK;
}
if (addErrors.mSortingCode.WasPassed() &&
!addErrors.mSortingCode.Value().IsEmpty()) {
return NS_OK;
}
return NS_ERROR_DOM_ABORT_ERR;
}
NS_IMETHODIMP
PaymentResponse::Notify(nsITimer *timer)
{

Просмотреть файл

@ -33,7 +33,7 @@ public:
const nsAString& aRequestId,
const nsAString& aMethodName,
const nsAString& aShippingOption,
RefPtr<PaymentAddress> aShippingAddress,
PaymentAddress* aShippingAddress,
const nsAString& aDetails,
const nsAString& aPayerName,
const nsAString& aPayerEmail,
@ -69,9 +69,24 @@ public:
void RespondComplete();
already_AddRefed<Promise> Retry(JSContext* aCx,
const PaymentValidationErrors& errorField,
ErrorResult& aRv);
void RespondRetry(const nsAString& aMethodName,
const nsAString& aShippingOption,
PaymentAddress* aShippingAddress,
const nsAString& aDetails,
const nsAString& aPayerName,
const nsAString& aPayerEmail,
const nsAString& aPayerPhone);
void RejectRetry(nsresult aRejectReason);
protected:
~PaymentResponse();
nsresult ValidatePaymentValidationErrors(const PaymentValidationErrors& aErrors);
private:
nsCOMPtr<nsPIDOMWindowInner> mOwner;
bool mCompleteCalled;
@ -89,6 +104,8 @@ private:
// Timer for timing out if the page doesn't call
// complete()
nsCOMPtr<nsITimer> mTimer;
// Promise for "PaymentResponse::Retry"
RefPtr<Promise> mRetryPromise;
};
} // namespace dom

Просмотреть файл

@ -110,6 +110,15 @@ struct IPCPaymentCloseActionRequest
nsString requestId;
};
struct IPCPaymentRetryActionRequest
{
nsString requestId;
nsString error;
nsString payerErrors;
nsString paymentMethodErrors;
nsString shippingAddressErrors;
};
union IPCPaymentActionRequest
{
IPCPaymentCreateActionRequest;
@ -119,6 +128,7 @@ union IPCPaymentActionRequest
IPCPaymentCompleteActionRequest;
IPCPaymentUpdateActionRequest;
IPCPaymentCloseActionRequest;
IPCPaymentRetryActionRequest;
};
struct IPCPaymentCanMakeActionResponse

Просмотреть файл

@ -66,6 +66,11 @@ PaymentRequestParent::RecvRequestPayment(const IPCPaymentActionRequest& aRequest
mRequestId = request.requestId();
break;
}
case IPCPaymentActionRequest::TIPCPaymentRetryActionRequest: {
const IPCPaymentRetryActionRequest& request = aRequest;
mRequestId = request.requestId();
break;
}
default : {
return IPC_FAIL(this, "Unknown PaymentRequest action type");
}

Просмотреть файл

@ -68,6 +68,19 @@ dictionary AddressErrors {
DOMString sortingCode;
};
dictionary PaymentValidationErrors {
PayerErrorFields payer;
AddressErrors shippingAddress;
DOMString error;
object paymentMethod;
};
dictionary PayerErrorFields {
DOMString email;
DOMString name;
DOMString phone;
};
dictionary PaymentDetailsUpdate : PaymentDetailsBase {
DOMString error;
AddressErrors shippingAddressErrors;

Просмотреть файл

@ -29,4 +29,8 @@ interface PaymentResponse {
[NewObject]
Promise<void> complete(optional PaymentComplete result = "unknown");
// If the dictionary argument has no required members, it must be optional.
[NewObject]
Promise<void> retry(optional PaymentValidationErrors errorFields);
};