dom/payments/PaymentRequestUpdateEvent.cpp
author Eden Chuang <echuang@mozilla.com>
Wed, 05 Dec 2018 15:01:52 -0500
changeset 451033 07ddae62b81220bf8ea11341a1cc48d8d953f7c2
parent 450775 2738bb9f6d54d34aa1742c50da7f6f120d9577a9
child 451036 87126fc8d1e9a781066739fba9d15448b9c51b12
permissions -rw-r--r--
bug 1498310 - Setting nsIPaymentRequest.completeStatus when Updating timeout or no event handler. r=baku 1. Set nsIPaymentRequest.completeStatus as 'updating' when calling nsIPaymentRequestService::Change*. After event handling done, set nsIPaymentRequest.completeStatus as '' and call nsIPaymentUIService::UpdatePayment() 2. When the page handles PaymentRequestUpdateEvent or the Promise of PaymentRequest::Show() over the dom.payments.response.timeout, set nsIPaymentRequest.completeStatus as 'timeout' and call PaymentUIService::UpdatePayment() automatically 3. Set nsIPaymentRequest.completeStatus as 'nohandler' when dispatching PaymentRequestUpdateEvent and no event handler for it. 4. Set the initial valud of dom.payments.response.timeout to 20000 ms.

/* -*- 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/dom/PaymentRequestUpdateEvent.h"

namespace mozilla {
namespace dom {

NS_IMPL_CYCLE_COLLECTION_INHERITED(PaymentRequestUpdateEvent, Event, mRequest, mTimer)

NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(PaymentRequestUpdateEvent, Event)
NS_IMPL_CYCLE_COLLECTION_TRACE_END

NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PaymentRequestUpdateEvent)
  NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
NS_INTERFACE_MAP_END_INHERITING(Event)

NS_IMPL_ADDREF_INHERITED(PaymentRequestUpdateEvent, Event)
NS_IMPL_RELEASE_INHERITED(PaymentRequestUpdateEvent, Event)

already_AddRefed<PaymentRequestUpdateEvent>
PaymentRequestUpdateEvent::Constructor(
    mozilla::dom::EventTarget* aOwner, const nsAString& aType,
    const PaymentRequestUpdateEventInit& aEventInitDict) {
  RefPtr<PaymentRequestUpdateEvent> e = new PaymentRequestUpdateEvent(aOwner);
  bool trusted = e->Init(aOwner);
  e->InitEvent(aType, aEventInitDict.mBubbles, aEventInitDict.mCancelable);
  e->SetTrusted(trusted);
  e->SetComposed(aEventInitDict.mComposed);
  return e.forget();
}

already_AddRefed<PaymentRequestUpdateEvent>
PaymentRequestUpdateEvent::Constructor(
    const GlobalObject& aGlobal, const nsAString& aType,
    const PaymentRequestUpdateEventInit& aEventInitDict, ErrorResult& aRv) {
  nsCOMPtr<mozilla::dom::EventTarget> owner =
      do_QueryInterface(aGlobal.GetAsSupports());
  return Constructor(owner, aType, aEventInitDict);
}

PaymentRequestUpdateEvent::PaymentRequestUpdateEvent(EventTarget* aOwner)
    : Event(aOwner, nullptr, nullptr),
      mWaitForUpdate(false),
      mRequest(nullptr) {
  MOZ_ASSERT(aOwner);
  nsCOMPtr<nsPIDOMWindowInner> win = do_QueryInterface(aOwner->GetOwnerGlobal());
  NS_NewTimerWithCallback(getter_AddRefs(mTimer),
                          this,
                          StaticPrefs::dom_payments_response_timeout(),
                          nsITimer::TYPE_ONE_SHOT,
                          win->EventTargetFor(TaskCategory::Other));
}

void PaymentRequestUpdateEvent::ResolvedCallback(JSContext* aCx,
                                                 JS::Handle<JS::Value> aValue) {
  MOZ_ASSERT(aCx);
  MOZ_ASSERT(mRequest);
  if (!mRequest->InFullyActiveDocument()) {
    return;
  }

  if (NS_WARN_IF(!aValue.isObject()) || !mWaitForUpdate) {
    return;
  }

  if (mTimer) {
    mTimer->Cancel();
    mTimer = nullptr;
  }

  // Converting value to a PaymentDetailsUpdate dictionary
  PaymentDetailsUpdate details;
  if (!details.Init(aCx, aValue)) {
    mRequest->AbortUpdate(NS_ERROR_TYPE_ERR);
    JS_ClearPendingException(aCx);
    return;
  }

  // Validate and canonicalize the details
  // requestShipping must be true here. PaymentRequestUpdateEvent is only
  // dispatched when shippingAddress/shippingOption is changed, and it also
  // means Options.RequestShipping must be true while creating the corresponding
  // PaymentRequest.
  nsresult rv =
      mRequest->IsValidDetailsUpdate(details, true /*aRequestShipping*/);
  if (NS_FAILED(rv)) {
    mRequest->AbortUpdate(rv);
    return;
  }

  // Update the PaymentRequest with the new details
  if (NS_FAILED(mRequest->UpdatePayment(aCx, details))) {
    mRequest->AbortUpdate(NS_ERROR_DOM_ABORT_ERR);
    return;
  }
  mWaitForUpdate = false;
  mRequest->SetUpdating(false);
}

void PaymentRequestUpdateEvent::RejectedCallback(JSContext* aCx,
                                                 JS::Handle<JS::Value> aValue) {
  MOZ_ASSERT(mRequest);
  if (!mRequest->InFullyActiveDocument()) {
    return;
  }

  mRequest->AbortUpdate(NS_ERROR_DOM_ABORT_ERR);
  mWaitForUpdate = false;
  mRequest->SetUpdating(false);
}

void PaymentRequestUpdateEvent::UpdateWith(Promise& aPromise,
                                           ErrorResult& aRv) {
  if (!IsTrusted()) {
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
    return;
  }

  MOZ_ASSERT(mRequest);
  if (!mRequest->InFullyActiveDocument()) {
    return;
  }

  if (mWaitForUpdate || !mRequest->ReadyForUpdate()) {
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
    return;
  }

  aPromise.AppendNativeHandler(this);

  StopPropagation();
  StopImmediatePropagation();
  mWaitForUpdate = true;
  mRequest->SetUpdating(true);
}

void PaymentRequestUpdateEvent::SetRequest(PaymentRequest* aRequest) {
  MOZ_ASSERT(IsTrusted());
  MOZ_ASSERT(!mRequest);
  MOZ_ASSERT(aRequest);

  mRequest = aRequest;
}

NS_IMETHODIMP PaymentRequestUpdateEvent::Notify(nsITimer* aTimer) {
  mTimer = nullptr;
  if (!mRequest) {
    return NS_OK;
  }
  nsresult rv = mRequest->UpdatePayment(nullptr, PaymentDetailsUpdate(), true);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }
  mWaitForUpdate = false;
  mRequest->SetUpdating(false);
  return NS_OK;
}

PaymentRequestUpdateEvent::~PaymentRequestUpdateEvent() {}

JSObject* PaymentRequestUpdateEvent::WrapObjectInternal(
    JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
  return PaymentRequestUpdateEvent_Binding::Wrap(aCx, this, aGivenProto);
}

}  // namespace dom
}  // namespace mozilla