/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim:set ts=2 sw=2 sts=2 et cindent: */ /* 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/ClearOnShutdown.h" #include "PaymentRequestData.h" #include "PaymentRequestService.h" #include "BasicCardPayment.h" namespace mozilla { namespace dom { StaticRefPtr gPaymentService; namespace { class PaymentRequestEnumerator final : public nsISimpleEnumerator { public: NS_DECL_ISUPPORTS NS_DECL_NSISIMPLEENUMERATOR PaymentRequestEnumerator() : mIndex(0) {} private: ~PaymentRequestEnumerator() = default; uint32_t mIndex; }; NS_IMPL_ISUPPORTS(PaymentRequestEnumerator, nsISimpleEnumerator) NS_IMETHODIMP PaymentRequestEnumerator::HasMoreElements(bool* aReturn) { NS_ENSURE_ARG_POINTER(aReturn); *aReturn = false; if (NS_WARN_IF(!gPaymentService)) { return NS_ERROR_FAILURE; } RefPtr service = gPaymentService; *aReturn = mIndex < service->NumPayments(); return NS_OK; } NS_IMETHODIMP PaymentRequestEnumerator::GetNext(nsISupports** aItem) { NS_ENSURE_ARG_POINTER(aItem); if (NS_WARN_IF(!gPaymentService)) { return NS_ERROR_FAILURE; } nsCOMPtr request = gPaymentService->GetPaymentRequestByIndex(mIndex); if (NS_WARN_IF(!request)) { return NS_ERROR_FAILURE; } nsCOMPtr item = do_QueryInterface(request); if (NS_WARN_IF(!item)) { return NS_ERROR_FAILURE; } mIndex++; item.forget(aItem); return NS_OK; } } // end of anonymous namespace /* PaymentRequestService */ NS_IMPL_ISUPPORTS(PaymentRequestService, nsIPaymentRequestService) already_AddRefed PaymentRequestService::GetSingleton() { MOZ_ASSERT(NS_IsMainThread()); if (!gPaymentService) { gPaymentService = new PaymentRequestService(); ClearOnShutdown(&gPaymentService); } RefPtr service = gPaymentService; return service.forget(); } uint32_t PaymentRequestService::NumPayments() const { return mRequestQueue.Length(); } already_AddRefed PaymentRequestService::GetPaymentRequestByIndex(const uint32_t aIndex) { if (aIndex >= mRequestQueue.Length()) { return nullptr; } nsCOMPtr request = mRequestQueue[aIndex]; MOZ_ASSERT(request); return request.forget(); } NS_IMETHODIMP PaymentRequestService::GetPaymentRequestById(const nsAString& aRequestId, nsIPaymentRequest** aRequest) { NS_ENSURE_ARG_POINTER(aRequest); *aRequest = nullptr; uint32_t numRequests = mRequestQueue.Length(); for (uint32_t index = 0; index < numRequests; ++index) { nsCOMPtr request = mRequestQueue[index]; MOZ_ASSERT(request); nsAutoString requestId; nsresult rv = request->GetRequestId(requestId); NS_ENSURE_SUCCESS(rv, rv); if (requestId == aRequestId) { request.forget(aRequest); break; } } return NS_OK; } NS_IMETHODIMP PaymentRequestService::Enumerate(nsISimpleEnumerator** aEnumerator) { NS_ENSURE_ARG_POINTER(aEnumerator); nsCOMPtr enumerator = new PaymentRequestEnumerator(); enumerator.forget(aEnumerator); return NS_OK; } NS_IMETHODIMP PaymentRequestService::Cleanup() { mRequestQueue.Clear(); return NS_OK; } NS_IMETHODIMP PaymentRequestService::SetTestingUIService(nsIPaymentUIService* aUIService) { // aUIService can be nullptr mTestingUIService = aUIService; return NS_OK; } nsresult PaymentRequestService::LaunchUIAction(const nsAString& aRequestId, uint32_t aActionType) { nsCOMPtr uiService; nsresult rv; if (mTestingUIService) { uiService = mTestingUIService; } else { uiService = do_GetService(NS_PAYMENT_UI_SERVICE_CONTRACT_ID, &rv); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } } switch (aActionType) { case nsIPaymentActionRequest::SHOW_ACTION: { rv = uiService->ShowPayment(aRequestId); break; } case nsIPaymentActionRequest::ABORT_ACTION: { rv = uiService->AbortPayment(aRequestId); break; } case nsIPaymentActionRequest::COMPLETE_ACTION: { rv = uiService->CompletePayment(aRequestId); break; } case nsIPaymentActionRequest::UPDATE_ACTION: { rv = uiService->UpdatePayment(aRequestId); break; } default : { return NS_ERROR_FAILURE; } } if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } return NS_OK; } NS_IMETHODIMP PaymentRequestService::RemoveActionCallback(nsIPaymentActionCallback* aCallback) { NS_ENSURE_ARG_POINTER(aCallback); for (auto iter = mCallbackHashtable.Iter(); !iter.Done(); iter.Next()) { nsCOMPtr callback = iter.Data(); MOZ_ASSERT(callback); if (callback == aCallback) { iter.Remove(); return NS_OK; } } return NS_OK; } NS_IMETHODIMP PaymentRequestService::RequestPayment(nsIPaymentActionRequest* aRequest) { NS_ENSURE_ARG_POINTER(aRequest); nsAutoString requestId; nsresult rv = aRequest->GetRequestId(requestId); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } nsCOMPtr callback; rv = aRequest->GetCallback(getter_AddRefs(callback)); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } rv = SetActionCallback(requestId, callback); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } uint32_t type; rv = aRequest->GetType(&type); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } switch (type) { case nsIPaymentActionRequest::CREATE_ACTION: { nsCOMPtr request = do_QueryInterface(aRequest); MOZ_ASSERT(request); uint64_t tabId; rv = request->GetTabId(&tabId); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr methodData; rv = request->GetMethodData(getter_AddRefs(methodData)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr details; rv = request->GetDetails(getter_AddRefs(details)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr options; rv = request->GetOptions(getter_AddRefs(options)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr payment = new payments::PaymentRequest(tabId, requestId, methodData, details, options); if (!mRequestQueue.AppendElement(payment, mozilla::fallible)) { return NS_ERROR_OUT_OF_MEMORY; } break; } /* * TODO: 1. Check third party payment app support by traversing all * registered third party payment apps. */ case nsIPaymentActionRequest::CANMAKE_ACTION: { nsCOMPtr canMakeResponse = do_CreateInstance(NS_PAYMENT_CANMAKE_ACTION_RESPONSE_CONTRACT_ID); MOZ_ASSERT(canMakeResponse); if (IsBasicCardPayment(requestId)) { rv = canMakeResponse->Init(requestId, true); } else { rv = canMakeResponse->Init(requestId, false); } if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } nsCOMPtr response = do_QueryInterface(canMakeResponse); MOZ_ASSERT(response); rv = RespondPayment(response); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } break; } case nsIPaymentActionRequest::SHOW_ACTION: { if (mShowingRequest) { nsCOMPtr responseData = do_CreateInstance(NS_GENERAL_RESPONSE_DATA_CONTRACT_ID); MOZ_ASSERT(responseData); nsCOMPtr showResponse = do_CreateInstance(NS_PAYMENT_SHOW_ACTION_RESPONSE_CONTRACT_ID); MOZ_ASSERT(showResponse); rv = showResponse->Init(requestId, nsIPaymentActionResponse::PAYMENT_REJECTED, EmptyString(), responseData, EmptyString(), EmptyString(), EmptyString()); nsCOMPtr response = do_QueryInterface(showResponse); MOZ_ASSERT(response); rv = RespondPayment(response); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } } else { rv = GetPaymentRequestById(requestId, getter_AddRefs(mShowingRequest)); if (NS_WARN_IF(NS_FAILED(rv))) { return NS_ERROR_FAILURE; } rv = LaunchUIAction(requestId, type); if (NS_WARN_IF(NS_FAILED(rv))) { return NS_ERROR_FAILURE; } } break; } case nsIPaymentActionRequest::ABORT_ACTION: case nsIPaymentActionRequest::COMPLETE_ACTION: { rv = LaunchUIAction(requestId, type); if (NS_WARN_IF(NS_FAILED(rv))) { return NS_ERROR_FAILURE; } break; } case nsIPaymentActionRequest::UPDATE_ACTION: { nsCOMPtr request = do_QueryInterface(aRequest); MOZ_ASSERT(request); nsCOMPtr details; rv = request->GetDetails(getter_AddRefs(details)); NS_ENSURE_SUCCESS(rv, rv); rv = request->GetRequestId(requestId); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr payment; rv = GetPaymentRequestById(requestId, getter_AddRefs(payment)); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } rv = payment->UpdatePaymentDetails(details); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } rv = LaunchUIAction(requestId, type); if (NS_WARN_IF(NS_FAILED(rv))) { return NS_ERROR_FAILURE; } break; } default: { return NS_ERROR_FAILURE; } } return NS_OK; } NS_IMETHODIMP PaymentRequestService::RespondPayment(nsIPaymentActionResponse* aResponse) { NS_ENSURE_ARG_POINTER(aResponse); nsAutoString requestId; nsresult rv = aResponse->GetRequestId(requestId); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr request; rv = GetPaymentRequestById(requestId, getter_AddRefs(request)); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } nsCOMPtr callback; if (!mCallbackHashtable.Get(requestId, getter_AddRefs(callback))) { return NS_ERROR_FAILURE; } if (NS_WARN_IF(!callback)) { return NS_ERROR_FAILURE; } rv = callback->RespondPayment(aResponse); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } // Remove nsIPaymentRequest from mRequestQueue while receive succeeded abort // response or complete response uint32_t type; rv = aResponse->GetType(&type); NS_ENSURE_SUCCESS(rv, rv); switch (type) { case nsIPaymentActionResponse::ABORT_ACTION: { nsCOMPtr response = do_QueryInterface(aResponse); MOZ_ASSERT(response); bool isSucceeded; rv = response->IsSucceeded(&isSucceeded); NS_ENSURE_SUCCESS(rv, rv); if (isSucceeded) { mShowingRequest = nullptr; mRequestQueue.RemoveElement(request); } break; } case nsIPaymentActionResponse::SHOW_ACTION: { nsCOMPtr response = do_QueryInterface(aResponse); MOZ_ASSERT(response); bool isAccepted; rv = response->IsAccepted(&isAccepted); NS_ENSURE_SUCCESS(rv, rv); if (!isAccepted) { mShowingRequest = nullptr; mRequestQueue.RemoveElement(request); } break; } case nsIPaymentActionResponse::COMPLETE_ACTION: { mShowingRequest = nullptr; mRequestQueue.RemoveElement(request); break; } default: { break; } } return NS_OK; } NS_IMETHODIMP PaymentRequestService::ChangeShippingAddress(const nsAString& aRequestId, nsIPaymentAddress* aAddress) { nsCOMPtr callback; if (!mCallbackHashtable.Get(aRequestId, getter_AddRefs(callback))) { return NS_ERROR_FAILURE; } if (NS_WARN_IF(!callback)) { return NS_ERROR_FAILURE; } nsresult rv = callback->ChangeShippingAddress(aRequestId, aAddress); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } return NS_OK; } NS_IMETHODIMP PaymentRequestService::ChangeShippingOption(const nsAString& aRequestId, const nsAString& aOption) { nsCOMPtr callback; if (!mCallbackHashtable.Get(aRequestId, getter_AddRefs(callback))) { return NS_ERROR_FAILURE; } if (NS_WARN_IF(!callback)) { return NS_ERROR_FAILURE; } nsresult rv = callback->ChangeShippingOption(aRequestId, aOption); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } return NS_OK; } nsresult PaymentRequestService::SetActionCallback(const nsAString& aRequestId, nsIPaymentActionCallback* aCallback) { NS_ENSURE_ARG_POINTER(aCallback); nsCOMPtr callback; if (mCallbackHashtable.Get(aRequestId, getter_AddRefs(callback))) { mCallbackHashtable.Remove(aRequestId); } mCallbackHashtable.Put(aRequestId, aCallback); return NS_OK; } nsresult PaymentRequestService::RemoveActionCallback(const nsAString& aRequestId) { nsCOMPtr callback; if (!mCallbackHashtable.Get(aRequestId, getter_AddRefs(callback))) { return NS_ERROR_FAILURE; } mCallbackHashtable.Remove(aRequestId); return NS_OK; } bool PaymentRequestService::IsBasicCardPayment(const nsAString& aRequestId) { nsCOMPtr payment; nsresult rv = GetPaymentRequestById(aRequestId, getter_AddRefs(payment)); NS_ENSURE_SUCCESS(rv, false); nsCOMPtr methods; rv = payment->GetPaymentMethods(getter_AddRefs(methods)); NS_ENSURE_SUCCESS(rv, false); uint32_t length; rv = methods->GetLength(&length); NS_ENSURE_SUCCESS(rv, false); RefPtr service = BasicCardService::GetService(); MOZ_ASSERT(service); for (uint32_t index = 0; index < length; ++index) { nsCOMPtr method = do_QueryElementAt(methods, index); MOZ_ASSERT(method); nsAutoString supportedMethods; rv = method->GetSupportedMethods(supportedMethods); NS_ENSURE_SUCCESS(rv, false); if (service->IsBasicCardPayment(supportedMethods)) { return true; } } return false; } } // end of namespace dom } // end of namespace mozilla