Bug 1477117 - Part 2 - Add new IPC method for passing the changing method information. r=baku
authorEden Chuang <echuang@mozilla.com>
Tue, 27 Nov 2018 14:05:00 +0100
changeset 505058 b3aea4b20b5360b16336a66863927a169f5fd025
parent 505057 4a5132e165d819305dfe96f42aa2f09aa47e124d
child 505059 ae31de05b602600e90d8e312d279ed5e58779311
push id10290
push userffxbld-merge
push dateMon, 03 Dec 2018 16:23:23 +0000
treeherdermozilla-beta@700bed2445e6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbaku
bugs1477117
milestone65.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 1477117 - Part 2 - Add new IPC method for passing the changing method information. r=baku 1. Add a new method nsIPaymentRequestService::ChangePaymentMethod for UI to inform merchant the payment method is changed by user. 2. Add a new method in PPaymentRequest.ipdl to passing the changing method information from chrome process to content process. 3. Add BasicCardChangeDetails dictionary in BasicCardPayment.webidl 4. Create ChangeDetails, GeneralDetails and BasicCardDetails for saving the the method information in content process.
dom/interfaces/payments/nsIPaymentRequestService.idl
dom/payments/PaymentRequest.cpp
dom/payments/PaymentRequest.h
dom/payments/PaymentRequestData.cpp
dom/payments/PaymentRequestManager.cpp
dom/payments/PaymentRequestManager.h
dom/payments/PaymentRequestService.cpp
dom/payments/ipc/PPaymentRequest.ipdl
dom/payments/ipc/PaymentRequestChild.cpp
dom/payments/ipc/PaymentRequestChild.h
dom/payments/ipc/PaymentRequestParent.cpp
dom/payments/ipc/PaymentRequestParent.h
dom/webidl/BasicCardPayment.webidl
dom/webidl/PaymentRequest.webidl
--- a/dom/interfaces/payments/nsIPaymentRequestService.idl
+++ b/dom/interfaces/payments/nsIPaymentRequestService.idl
@@ -62,16 +62,27 @@ interface nsIPaymentRequestService : nsI
    *  @param aPayerPhone - the changed payer's phone.
    */
   void changePayerDetail(in AString requestId,
                          in AString aPayerName,
                          in AString aPayerEmail,
                          in AString aPayerPhone);
 
   /**
+   *  Inform the merchant the payment method has changed.
+   *  @param requestId - the request identifier of the payment request.
+   *  @param aMethodName - the changed payment method's name.
+   *  @param aMethodDetails - the changed payment method's details.
+   */
+  void changePaymentMethod(in AString requestId,
+                           in AString aMethodName,
+                           in nsIMethodChangeDetails aMethodDetails);
+
+
+  /**
    *  Following APIs are for testing or platform code only. UI implementation
    *  should not use them.
    */
   /**
    *  Clean up the all managed payment requests.
    *  This API is for testing only.
    */
   void cleanup();
--- a/dom/payments/PaymentRequest.cpp
+++ b/dom/payments/PaymentRequest.cpp
@@ -1037,16 +1037,25 @@ PaymentRequest::DispatchMerchantValidati
   }
   event->SetTrusted(true);
   event->SetRequest(this);
 
   DispatchEvent(*event, rv);
   return rv.StealNSResult();
 }
 
+nsresult
+PaymentRequest::DispatchPaymentMethodChangeEvent(const nsAString& aMethodName,
+                                                 const ChangeDetails& aMethodDetails)
+{
+  MOZ_ASSERT(ReadyForUpdate());
+  // TODO: create and dispatch a PaymentMethodChangeEvent
+  return NS_OK;
+}
+
 already_AddRefed<PaymentAddress>
 PaymentRequest::GetShippingAddress() const
 {
   RefPtr<PaymentAddress> address = mShippingAddress;
   return address.forget();
 }
 
 nsresult
@@ -1093,16 +1102,23 @@ nsresult
 PaymentRequest::UpdateShippingOption(const nsAString& aShippingOption)
 {
   mShippingOption = aShippingOption;
 
   // Fire shippingaddresschange event
   return DispatchUpdateEvent(NS_LITERAL_STRING("shippingoptionchange"));
 }
 
+nsresult
+PaymentRequest::UpdatePaymentMethod(const nsAString& aMethodName,
+                                    const ChangeDetails& aMethodDetails)
+{
+  return DispatchPaymentMethodChangeEvent(aMethodName, aMethodDetails);
+}
+
 void
 PaymentRequest::SetShippingType(const Nullable<PaymentShippingType>& aShippingType)
 {
   mShippingType = aShippingType;
 }
 
 Nullable<PaymentShippingType>
 PaymentRequest::GetShippingType() const
--- a/dom/payments/PaymentRequest.h
+++ b/dom/payments/PaymentRequest.h
@@ -14,22 +14,93 @@
 #include "mozilla/ErrorResult.h"
 #include "nsIDocumentActivity.h"
 #include "nsWrapperCache.h"
 #include "PaymentRequestUpdateEvent.h"
 
 namespace mozilla {
 namespace dom {
 
-class EventHandlerNonNull;
 class PaymentAddress;
 class PaymentRequestChild;
 class PaymentResponse;
 class ResponseData;
 
+class GeneralDetails final
+{
+public:
+  GeneralDetails() = default;
+  ~GeneralDetails() = default;
+  nsString details;
+};
+
+class BasicCardDetails final
+{
+public:
+  struct Address {
+    nsString country;
+    nsTArray<nsString> addressLine;
+    nsString region;
+    nsString regionCode;
+    nsString city;
+    nsString dependentLocality;
+    nsString postalCode;
+    nsString sortingCode;
+    nsString organization;
+    nsString recipient;
+    nsString phone;
+  };
+  BasicCardDetails() = default;
+  ~BasicCardDetails() = default;
+
+  Address billingAddress;
+};
+
+class ChangeDetails final
+{
+public:
+  enum Type {
+    Unknown = 0,
+    GeneralMethodDetails = 1,
+    BasicCardMethodDetails
+  };
+  ChangeDetails()
+    : mType(ChangeDetails::Unknown)
+  {}
+  explicit ChangeDetails(const GeneralDetails& aGeneralDetails)
+    : mType(GeneralMethodDetails)
+    , mGeneralDetails(aGeneralDetails)
+  {}
+  explicit ChangeDetails(const BasicCardDetails& aBasicCardDetails)
+    : mType(BasicCardMethodDetails)
+    , mBasicCardDetails(aBasicCardDetails)
+  {}
+  ChangeDetails& operator = (const GeneralDetails& aGeneralDetails) {
+    mType = GeneralMethodDetails;
+    mGeneralDetails = aGeneralDetails;
+    mBasicCardDetails = BasicCardDetails();
+    return *this;
+  }
+  ChangeDetails& operator = (const BasicCardDetails& aBasicCardDetails) {
+    mType = BasicCardMethodDetails;
+    mGeneralDetails = GeneralDetails();
+    mBasicCardDetails = aBasicCardDetails;
+    return *this;
+  }
+  ~ChangeDetails() = default;
+
+  const Type& type() const { return mType; }
+  const GeneralDetails& generalDetails() const { return mGeneralDetails; }
+  const BasicCardDetails& basicCardDetails() const { return mBasicCardDetails;}
+private:
+  Type mType;
+  GeneralDetails mGeneralDetails;
+  BasicCardDetails mBasicCardDetails;
+};
+
 class PaymentRequest final
   : public DOMEventTargetHelper
   , public PromiseNativeHandler
   , public nsIDocumentActivity
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(PaymentRequest,
@@ -92,17 +163,17 @@ public:
 
   already_AddRefed<Promise> CanMakePayment(ErrorResult& aRv);
   void RespondCanMakePayment(bool aResult);
 
   already_AddRefed<Promise> Show(
     const Optional<OwningNonNull<Promise>>& detailsPromise,
     ErrorResult& aRv);
   void RespondShowPayment(const nsAString& aMethodName,
-                          const ResponseData& aDetails,
+                          const ResponseData& aData,
                           const nsAString& aPayerName,
                           const nsAString& aPayerEmail,
                           const nsAString& aPayerPhone,
                           nsresult aRv);
   void RejectShowPayment(nsresult aRejectReason);
   void RespondComplete();
 
   already_AddRefed<Promise> Abort(ErrorResult& aRv);
@@ -146,16 +217,19 @@ public:
                          const PaymentDetailsUpdate& aDetails);
   void AbortUpdate(nsresult aRv);
 
   void SetShippingType(const Nullable<PaymentShippingType>& aShippingType);
   Nullable<PaymentShippingType> GetShippingType() const;
 
   inline void ShippingWasRequested() { mRequestShipping = true; }
 
+  nsresult UpdatePaymentMethod(const nsAString& aMethodName,
+                               const ChangeDetails& aMethodDetails);
+
   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);
 
@@ -171,16 +245,19 @@ protected:
 
   void RegisterActivityObserver();
   void UnregisterActivityObserver();
 
   nsresult DispatchUpdateEvent(const nsAString& aType);
 
   nsresult DispatchMerchantValidationEvent(const nsAString& aType);
 
+  nsresult DispatchPaymentMethodChangeEvent(const nsAString& aMethodName,
+                                            const ChangeDetails& aMethodDatils);
+
   PaymentRequest(nsPIDOMWindowInner* aWindow, const nsAString& aInternalId);
 
   // Id for internal identification
   nsString mInternalId;
   // Id for communicating with merchant side
   // mId is initialized to details.id if it exists
   // otherwise, mId has the same value as mInternalId.
   nsString mId;
--- a/dom/payments/PaymentRequestData.cpp
+++ b/dom/payments/PaymentRequestData.cpp
@@ -622,17 +622,17 @@ PaymentOptions::Create(const IPCPaymentO
 {
   NS_ENSURE_ARG_POINTER(aOptions);
 
   nsCOMPtr<nsIPaymentOptions> options =
     new PaymentOptions(aIPCOptions.requestPayerName(),
                        aIPCOptions.requestPayerEmail(),
                        aIPCOptions.requestPayerPhone(),
                        aIPCOptions.requestShipping(),
-                       false, //aIPCOptions.requestBillingAddress(),
+                       aIPCOptions.requestBillingAddress(),
                        aIPCOptions.shippingType());
   options.forget(aOptions);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PaymentOptions::GetRequestPayerName(bool* aRequestPayerName)
 {
--- a/dom/payments/PaymentRequestManager.cpp
+++ b/dom/payments/PaymentRequestManager.cpp
@@ -250,16 +250,17 @@ ConvertOptions(const PaymentOptions& aOp
   if (shippingTypeIndex < ArrayLength(PaymentShippingTypeValues::strings)) {
     shippingType.AssignASCII(
       PaymentShippingTypeValues::strings[shippingTypeIndex].value);
   }
   aIPCOption = IPCPaymentOptions(aOptions.mRequestPayerName,
                                  aOptions.mRequestPayerEmail,
                                  aOptions.mRequestPayerPhone,
                                  aOptions.mRequestShipping,
+                                 aOptions.mRequestBillingAddress,
                                  shippingType);
 }
 
 void
 ConvertResponseData(const IPCPaymentResponseData& aIPCData,
                     ResponseData& aData)
 {
   switch (aIPCData.type()) {
@@ -293,16 +294,53 @@ ConvertResponseData(const IPCPaymentResp
       aData = bData;
       break;
     }
     default: {
       break;
     }
   }
 }
+
+void
+ConvertMethodChangeDetails(const IPCMethodChangeDetails& aIPCDetails,
+                           ChangeDetails& aDetails)
+{
+  switch (aIPCDetails.type()) {
+    case IPCMethodChangeDetails::TIPCGeneralChangeDetails : {
+      const IPCGeneralChangeDetails& details = aIPCDetails;
+      GeneralDetails gDetails;
+      gDetails.details = details.details();
+      aDetails = gDetails;
+      break;
+    }
+    case IPCMethodChangeDetails::TIPCBasicCardChangeDetails: {
+      const IPCBasicCardChangeDetails& details = aIPCDetails;
+      BasicCardDetails bDetails;
+      bDetails.billingAddress.country = details.billingAddress().country();
+      bDetails.billingAddress.addressLine = details.billingAddress().addressLine();
+      bDetails.billingAddress.region = details.billingAddress().region();
+      bDetails.billingAddress.regionCode = details.billingAddress().regionCode();
+      bDetails.billingAddress.city = details.billingAddress().city();
+      bDetails.billingAddress.dependentLocality =
+        details.billingAddress().dependentLocality();
+      bDetails.billingAddress.postalCode = details.billingAddress().postalCode();
+      bDetails.billingAddress.sortingCode = details.billingAddress().sortingCode();
+      bDetails.billingAddress.organization = details.billingAddress().organization();
+      bDetails.billingAddress.recipient = details.billingAddress().recipient();
+      bDetails.billingAddress.phone = details.billingAddress().phone();
+      aDetails = bDetails;
+      break;
+    }
+    default: {
+      break;
+    }
+  }
+
+}
 } // end of namespace
 
 /* PaymentRequestManager */
 
 StaticRefPtr<PaymentRequestManager> gPaymentManager;
 const char kSupportedRegionsPref[] = "dom.payments.request.supportedRegions";
 
 void
@@ -761,10 +799,21 @@ PaymentRequestManager::ChangePayerDetail
   RefPtr<PaymentResponse> response = aRequest->GetResponse();
   // ignoring the case call changePayerDetail during show().
   if (!response) {
     return NS_OK;
   }
   return response->UpdatePayerDetail(aPayerName, aPayerEmail, aPayerPhone);
 }
 
+nsresult
+PaymentRequestManager::ChangePaymentMethod(PaymentRequest* aRequest,
+                                           const nsAString& aMethodName,
+                                           const IPCMethodChangeDetails& aMethodDetails)
+{
+  NS_ENSURE_ARG_POINTER(aRequest);
+  ChangeDetails methodDetails;
+  ConvertMethodChangeDetails(aMethodDetails, methodDetails);
+  return aRequest->UpdatePaymentMethod(aMethodName, methodDetails);
+}
+
 } // end of namespace dom
 } // end of namespace mozilla
--- a/dom/payments/PaymentRequestManager.h
+++ b/dom/payments/PaymentRequestManager.h
@@ -62,21 +62,23 @@ public:
                         const PaymentValidationErrors& aErrors);
 
   nsresult RespondPayment(PaymentRequest* aRequest,
                           const IPCPaymentActionResponse& aResponse);
   nsresult ChangeShippingAddress(PaymentRequest* aRequest,
                                  const IPCPaymentAddress& aAddress);
   nsresult ChangeShippingOption(PaymentRequest* aRequest,
                                 const nsAString& aOption);
-
   nsresult ChangePayerDetail(PaymentRequest* aRequest,
                              const nsAString& aPayerName,
                              const nsAString& aPayerEmail,
                              const nsAString& aPayerPhone);
+  nsresult ChangePaymentMethod(PaymentRequest* aRequest,
+                               const nsAString& aMethodName,
+                               const IPCMethodChangeDetails& aMethodDetails);
 
   bool IsRegionSupported(const nsAString& region) const;
 
   // 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);
 
 private:
--- a/dom/payments/PaymentRequestService.cpp
+++ b/dom/payments/PaymentRequestService.cpp
@@ -1,19 +1,20 @@
 /* -*- 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 "BasicCardPayment.h"
 #include "mozilla/ClearOnShutdown.h"
+#include "mozilla/dom/BasicCardPaymentBinding.h"
 #include "mozilla/dom/PaymentRequestParent.h"
+#include "nsSimpleEnumerator.h"
 #include "PaymentRequestService.h"
-#include "BasicCardPayment.h"
-#include "nsSimpleEnumerator.h"
 
 namespace mozilla {
 namespace dom {
 
 StaticRefPtr<PaymentRequestService> gPaymentService;
 
 namespace {
 
@@ -498,16 +499,67 @@ PaymentRequestService::ChangeShippingOpt
   }
   rv = request->GetIPC()->ChangeShippingOption(aRequestId, aOption);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   return NS_OK;
 }
 
+NS_IMETHODIMP
+PaymentRequestService::ChangePayerDetail(const nsAString& aRequestId,
+                                         const nsAString& aPayerName,
+                                         const nsAString& aPayerEmail,
+                                         const nsAString& aPayerPhone)
+{
+  RefPtr<payments::PaymentRequest> request;
+  nsresult rv = GetPaymentRequestById(aRequestId, getter_AddRefs(request));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+  MOZ_ASSERT(request);
+  if (!request->GetIPC()) {
+    return NS_ERROR_FAILURE;
+  }
+  rv = request->GetIPC()->ChangePayerDetail(
+    aRequestId, aPayerName, aPayerEmail, aPayerPhone);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+PaymentRequestService::ChangePaymentMethod(const nsAString& aRequestId,
+                                           const nsAString& aMethodName,
+                                           nsIMethodChangeDetails* aMethodDetails)
+{
+  RefPtr<payments::PaymentRequest> request;
+  nsresult rv = GetPaymentRequestById(aRequestId, getter_AddRefs(request));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+  if (!request) {
+    return NS_ERROR_FAILURE;
+  }
+  if (request->GetState() != payments::PaymentRequest::eInteractive) {
+    return NS_ERROR_FAILURE;
+  }
+  if (!request->GetIPC()) {
+    return NS_ERROR_FAILURE;
+  }
+  rv = request->GetIPC()->ChangePaymentMethod(aRequestId,
+                                              aMethodName,
+                                              aMethodDetails);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+  return NS_OK;
+}
+
 bool
 PaymentRequestService::CanMakePayment(const nsAString& aRequestId)
 {
   /*
    *  TODO: Check third party payment app support by traversing all
    *        registered third party payment apps.
    */
   return IsBasicCardPayment(aRequestId);
@@ -582,33 +634,10 @@ PaymentRequestService::IsBasicCardPaymen
     NS_ENSURE_SUCCESS(rv, false);
     if (service->IsBasicCardPayment(supportedMethods)) {
       return true;
     }
   }
   return false;
 }
 
-NS_IMETHODIMP
-PaymentRequestService::ChangePayerDetail(const nsAString& aRequestId,
-                                         const nsAString& aPayerName,
-                                         const nsAString& aPayerEmail,
-                                         const nsAString& aPayerPhone)
-{
-  RefPtr<payments::PaymentRequest> request;
-  nsresult rv = GetPaymentRequestById(aRequestId, getter_AddRefs(request));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-  MOZ_ASSERT(request);
-  if (!request->GetIPC()) {
-    return NS_ERROR_FAILURE;
-  }
-  rv = request->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
--- a/dom/payments/ipc/PPaymentRequest.ipdl
+++ b/dom/payments/ipc/PPaymentRequest.ipdl
@@ -60,16 +60,17 @@ struct IPCPaymentDetails
 };
 
 struct IPCPaymentOptions
 {
   bool requestPayerName;
   bool requestPayerEmail;
   bool requestPayerPhone;
   bool requestShipping;
+  bool requestBillingAddress;
   nsString shippingType;
 };
 
 struct IPCPaymentCreateActionRequest
 {
   uint64_t topOuterWindowId;
   nsString requestId;
   Principal topLevelPrincipal;
@@ -202,16 +203,32 @@ struct IPCPaymentCompleteActionResponse
 union IPCPaymentActionResponse
 {
   IPCPaymentCanMakeActionResponse;
   IPCPaymentShowActionResponse;
   IPCPaymentAbortActionResponse;
   IPCPaymentCompleteActionResponse;
 };
 
+struct IPCGeneralChangeDetails
+{
+  nsString details;
+};
+
+struct IPCBasicCardChangeDetails
+{
+  IPCPaymentAddress billingAddress;
+};
+
+union IPCMethodChangeDetails
+{
+  IPCGeneralChangeDetails;
+  IPCBasicCardChangeDetails;
+};
+
 sync protocol PPaymentRequest
 {
   manager PBrowser;
 
 parent:
   async __delete__();
 
   async RequestPayment(IPCPaymentActionRequest aAction);
@@ -221,12 +238,15 @@ child:
   async ChangeShippingAddress(nsString aRequestId,
                               IPCPaymentAddress aAddress);
   async ChangeShippingOption(nsString aRequestId,
                              nsString aOption);
   async ChangePayerDetail(nsString aRequestId,
                           nsString aPayerName,
                           nsString aPayerEmail,
                           nsString aPayerPhone);
+  async ChangePaymentMethod(nsString aRequestId,
+                            nsString aMethodName,
+                            IPCMethodChangeDetails aMethodDetails);
 };
 
 } // end of namespace dom
 } // end of namespace mozilla
--- a/dom/payments/ipc/PaymentRequestChild.cpp
+++ b/dom/payments/ipc/PaymentRequestChild.cpp
@@ -96,16 +96,34 @@ PaymentRequestChild::RecvChangePayerDeta
   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();
 }
 
+mozilla::ipc::IPCResult
+PaymentRequestChild::RecvChangePaymentMethod(const nsString& aRequestId,
+                                             const nsString& aMethodName,
+                                             const IPCMethodChangeDetails& aMethodDetails)
+{
+  if (!mRequest) {
+    return IPC_FAIL_NO_REASON(this);
+  }
+  RefPtr<PaymentRequestManager> manager = PaymentRequestManager::GetSingleton();
+  MOZ_ASSERT(manager);
+  RefPtr<PaymentRequest> request(mRequest);
+  nsresult rv = manager->ChangePaymentMethod(request, aMethodName, aMethodDetails);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return IPC_FAIL_NO_REASON(this);
+  }
+  return IPC_OK();
+}
+
 void
 PaymentRequestChild::ActorDestroy(ActorDestroyReason aWhy)
 {
   if (mRequest) {
     DetachFromRequest(true);
   }
 }
 
--- a/dom/payments/ipc/PaymentRequestChild.h
+++ b/dom/payments/ipc/PaymentRequestChild.h
@@ -36,16 +36,21 @@ protected:
                            const nsString& aOption) override;
 
   mozilla::ipc::IPCResult
   RecvChangePayerDetail(const nsString& aRequestId,
                         const nsString& aPayerName,
                         const nsString& aPayerEmail,
                         const nsString& aPayerPhone) override;
 
+  mozilla::ipc::IPCResult
+  RecvChangePaymentMethod(const nsString& aRequestId,
+                          const nsString& aMethodName,
+                          const IPCMethodChangeDetails& aMethodDetails) override;
+
   void ActorDestroy(ActorDestroyReason aWhy) override;
 
 private:
   ~PaymentRequestChild() = default;
 
   void DetachFromRequest(bool aCanBeInManager);
 
   PaymentRequest* MOZ_NON_OWNING_REF mRequest;
--- a/dom/payments/ipc/PaymentRequestParent.cpp
+++ b/dom/payments/ipc/PaymentRequestParent.cpp
@@ -272,16 +272,77 @@ PaymentRequestParent::ChangePayerDetail(
     return NS_ERROR_FAILURE;
   }
   if (!SendChangePayerDetail(requestId, payerName, payerEmail, payerPhone)) {
     return NS_ERROR_FAILURE;
   }
   return NS_OK;
 }
 
+nsresult
+PaymentRequestParent::ChangePaymentMethod(const nsAString& aRequestId,
+                                          const nsAString& aMethodName,
+                                          nsIMethodChangeDetails* aMethodDetails)
+{
+  nsAutoString requestId(aRequestId);
+  nsAutoString methodName(aMethodName);
+  nsCOMPtr<nsIMethodChangeDetails> methodDetails(aMethodDetails);
+  if (!NS_IsMainThread()) {
+    RefPtr<PaymentRequestParent> self = this;
+    nsCOMPtr<nsIRunnable> r =
+      NS_NewRunnableFunction("dom::PaymentRequestParent::ChangePaymentMethod",
+                             [self, requestId, methodName, methodDetails] ()
+    {
+      self->ChangePaymentMethod(requestId, methodName, methodDetails);
+    });
+    return NS_DispatchToMainThread(r);
+  }
+  if (!mActorAlive) {
+    return NS_ERROR_FAILURE;
+  }
+
+  // Convert nsIMethodChangeDetails to IPCMethodChangeDetails
+  // aMethodChangeDetails can be null
+  IPCMethodChangeDetails ipcChangeDetails;
+  if (aMethodDetails) {
+    uint32_t dataType;
+    NS_ENSURE_SUCCESS(aMethodDetails->GetType(&dataType), NS_ERROR_FAILURE);
+    switch(dataType) {
+      case nsIMethodChangeDetails::GENERAL_DETAILS: {
+        nsCOMPtr<nsIGeneralChangeDetails> details = do_QueryInterface(methodDetails);
+        MOZ_ASSERT(details);
+        IPCGeneralChangeDetails ipcGeneralDetails;
+        NS_ENSURE_SUCCESS(details->GetDetails(ipcGeneralDetails.details()), NS_ERROR_FAILURE);
+        ipcChangeDetails = ipcGeneralDetails;
+        break;
+      }
+      case nsIMethodChangeDetails::BASICCARD_DETAILS: {
+        nsCOMPtr<nsIBasicCardChangeDetails> details = do_QueryInterface(methodDetails);
+        MOZ_ASSERT(details);
+        IPCBasicCardChangeDetails ipcBasicCardDetails;
+        nsCOMPtr<nsIPaymentAddress> address;
+        NS_ENSURE_SUCCESS(details->GetBillingAddress(getter_AddRefs(address)),
+                          NS_ERROR_FAILURE);
+        IPCPaymentAddress ipcAddress;
+        NS_ENSURE_SUCCESS(SerializeAddress(ipcAddress, address), NS_ERROR_FAILURE);
+        ipcBasicCardDetails.billingAddress() = ipcAddress;
+        ipcChangeDetails = ipcBasicCardDetails;
+        break;
+      }
+      default: {
+        return NS_ERROR_FAILURE;
+      }
+    }
+  }
+  if (!SendChangePaymentMethod(requestId, methodName, ipcChangeDetails)) {
+    return NS_ERROR_FAILURE;
+  }
+  return NS_OK;
+}
+
 mozilla::ipc::IPCResult
 PaymentRequestParent::Recv__delete__()
 {
   mActorAlive = false;
   return IPC_OK();
 }
 
 void
--- a/dom/payments/ipc/PaymentRequestParent.h
+++ b/dom/payments/ipc/PaymentRequestParent.h
@@ -24,16 +24,19 @@ public:
   nsresult ChangeShippingAddress(const nsAString& aRequestId,
                                  nsIPaymentAddress* aAddress);
   nsresult ChangeShippingOption(const nsAString& aRequestId,
                                 const nsAString& aOption);
   nsresult ChangePayerDetail(const nsAString& aRequestId,
                              const nsAString& aPayerName,
                              const nsAString& aPayerEmail,
                              const nsAString& aPayerPhone);
+  nsresult ChangePaymentMethod(const nsAString& aRequestId,
+                               const nsAString& aMethodName,
+                               nsIMethodChangeDetails* aMethodDetails);
 
 protected:
   mozilla::ipc::IPCResult
   RecvRequestPayment(const IPCPaymentActionRequest& aRequest) override;
 
   mozilla::ipc::IPCResult Recv__delete__() override;
 
   void ActorDestroy(ActorDestroyReason aWhy) override;
--- a/dom/webidl/BasicCardPayment.webidl
+++ b/dom/webidl/BasicCardPayment.webidl
@@ -15,16 +15,20 @@ dictionary BasicCardResponse {
            DOMString cardholderName;
   required DOMString cardNumber;
            DOMString expiryMonth;
            DOMString expiryYear;
            DOMString cardSecurityCode;
            PaymentAddress? billingAddress;
 };
 
+dictionary BasicCardChangeDetails {
+  PaymentAddress? billingAddress;
+};
+
 dictionary BasicCardErrors {
   DOMString cardNumber;
   DOMString cardholderName;
   DOMString cardSecurityCode;
   DOMString expiryMonth;
   DOMString expiryYear;
   AddressErrors billingAddress;
 };
--- a/dom/webidl/PaymentRequest.webidl
+++ b/dom/webidl/PaymentRequest.webidl
@@ -93,16 +93,17 @@ enum PaymentShippingType {
   "pickup"
 };
 
 dictionary PaymentOptions {
   boolean             requestPayerName = false;
   boolean             requestPayerEmail = false;
   boolean             requestPayerPhone = false;
   boolean             requestShipping = false;
+  boolean             requestBillingAddress = false;
   PaymentShippingType shippingType = "shipping";
 };
 
 [Constructor(sequence<PaymentMethodData> methodData, PaymentDetailsInit details,
              optional PaymentOptions options),
  SecureContext,
  Func="mozilla::dom::PaymentRequest::PrefEnabled"]
 interface PaymentRequest : EventTarget {