| // Copyright 2017 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/browser/payments/payment_app_provider_impl.h" |
| |
| #include <map> |
| #include <sstream> |
| #include <string> |
| #include <vector> |
| |
| #include "base/base64.h" |
| #include "base/bind.h" |
| #include "base/callback_helpers.h" |
| #include "base/feature_list.h" |
| #include "base/memory/ref_counted_memory.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/string_util.h" |
| #include "base/token.h" |
| #include "content/browser/devtools/devtools_background_services_context_impl.h" |
| #include "content/browser/payments/payment_app_context_impl.h" |
| #include "content/browser/payments/payment_app_installer.h" |
| #include "content/browser/service_worker/service_worker_context_wrapper.h" |
| #include "content/browser/service_worker/service_worker_metrics.h" |
| #include "content/browser/service_worker/service_worker_version.h" |
| #include "content/browser/storage_partition_impl.h" |
| #include "content/public/browser/browser_context.h" |
| #include "content/public/browser/browser_task_traits.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/devtools_background_services_context.h" |
| #include "content/public/browser/render_frame_host.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/common/content_features.h" |
| #include "mojo/public/cpp/bindings/message.h" |
| #include "mojo/public/mojom/base/time.mojom.h" |
| #include "third_party/blink/public/common/service_worker/service_worker_status_code.h" |
| #include "third_party/blink/public/mojom/service_worker/service_worker_container_type.mojom.h" |
| #include "third_party/skia/include/core/SkBitmap.h" |
| #include "ui/gfx/image/image.h" |
| |
| namespace content { |
| namespace { |
| |
| using payments::mojom::CanMakePaymentEventDataPtr; |
| using payments::mojom::PaymentDetailsModifierPtr; |
| using payments::mojom::PaymentEventResponseType; |
| using payments::mojom::PaymentHandlerStatus; |
| using payments::mojom::PaymentMethodDataPtr; |
| using payments::mojom::PaymentRequestEventDataPtr; |
| |
| void AddMethodDataToMap(const std::vector<PaymentMethodDataPtr>& method_data, |
| std::map<std::string, std::string>* out) { |
| for (size_t i = 0; i < method_data.size(); ++i) { |
| std::string counter = |
| method_data.size() == 1 ? "" : " #" + base::NumberToString(i); |
| out->emplace("Method Name" + counter, method_data[i]->supported_method); |
| out->emplace("Method Data" + counter, method_data[i]->stringified_data); |
| } |
| } |
| |
| void AddModifiersToMap(const std::vector<PaymentDetailsModifierPtr>& modifiers, |
| std::map<std::string, std::string>* out) { |
| for (size_t i = 0; i < modifiers.size(); ++i) { |
| std::string prefix = |
| "Modifier" + |
| (modifiers.size() == 1 ? "" : " #" + base::NumberToString(i)); |
| out->emplace(prefix + " Method Name", |
| modifiers[i]->method_data->supported_method); |
| out->emplace(prefix + " Method Data", |
| modifiers[i]->method_data->stringified_data); |
| if (!modifiers[i]->total) |
| continue; |
| out->emplace(prefix + " Total Currency", |
| modifiers[i]->total->amount->currency); |
| out->emplace(prefix + " Total Value", modifiers[i]->total->amount->value); |
| } |
| } |
| |
| } // namespace |
| |
| // static |
| PaymentAppProvider* PaymentAppProvider::GetOrCreateForWebContents( |
| WebContents* payment_request_web_contents) { |
| return PaymentAppProviderImpl::GetOrCreateForWebContents( |
| payment_request_web_contents); |
| } |
| |
| // static |
| PaymentAppProviderImpl* PaymentAppProviderImpl::GetOrCreateForWebContents( |
| WebContents* payment_request_web_contents) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| auto* data = |
| PaymentAppProviderImpl::FromWebContents(payment_request_web_contents); |
| if (!data) |
| PaymentAppProviderImpl::CreateForWebContents(payment_request_web_contents); |
| |
| return PaymentAppProviderImpl::FromWebContents(payment_request_web_contents); |
| } |
| |
| void PaymentAppProviderImpl::InvokePaymentApp( |
| int64_t registration_id, |
| const url::Origin& sw_origin, |
| PaymentRequestEventDataPtr event_data, |
| InvokePaymentAppCallback callback) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| DCHECK(payment_request_web_contents_); |
| |
| scoped_refptr<DevToolsBackgroundServicesContextImpl> dev_tools = |
| GetDevTools(sw_origin); |
| if (dev_tools) { |
| std::map<std::string, std::string> data = { |
| {"Merchant Top Origin", event_data->top_origin.spec()}, |
| {"Merchant Payment Request Origin", |
| event_data->payment_request_origin.spec()}, |
| {"Total Currency", event_data->total->currency}, |
| {"Total Value", event_data->total->value}, |
| {"Instrument Key", event_data->instrument_key}, |
| }; |
| AddMethodDataToMap(event_data->method_data, &data); |
| AddModifiersToMap(event_data->modifiers, &data); |
| dev_tools->LogBackgroundServiceEvent( |
| registration_id, sw_origin, DevToolsBackgroundService::kPaymentHandler, |
| "Payment request", |
| /*instance_id=*/event_data->payment_request_id, data); |
| } |
| |
| StoragePartitionImpl* partition = static_cast<StoragePartitionImpl*>( |
| payment_request_web_contents_->GetBrowserContext() |
| ->GetDefaultStoragePartition()); |
| scoped_refptr<ServiceWorkerContextWrapper> service_worker_context = |
| partition->GetServiceWorkerContext(); |
| |
| event_dispatcher_->InvokePayment(registration_id, sw_origin, |
| std::move(dev_tools), |
| std::move(service_worker_context), |
| std::move(event_data), std::move(callback)); |
| } |
| |
| void PaymentAppProviderImpl::InstallAndInvokePaymentApp( |
| PaymentRequestEventDataPtr event_data, |
| const std::string& app_name, |
| const SkBitmap& app_icon, |
| const GURL& sw_js_url, |
| const GURL& sw_scope, |
| bool sw_use_cache, |
| const std::string& method, |
| const SupportedDelegations& supported_delegations, |
| RegistrationIdCallback registration_id_callback, |
| InvokePaymentAppCallback callback) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| DCHECK(payment_request_web_contents_); |
| |
| if (!sw_js_url.is_valid() || !sw_scope.is_valid() || method.empty()) { |
| GetUIThreadTaskRunner({})->PostTask( |
| FROM_HERE, |
| base::BindOnce( |
| std::move(callback), |
| PaymentAppProviderUtil::CreateBlankPaymentHandlerResponse( |
| PaymentEventResponseType::PAYMENT_HANDLER_INSTALL_FAILED))); |
| return; |
| } |
| |
| url::Origin sw_origin = url::Origin::Create(sw_scope); |
| scoped_refptr<DevToolsBackgroundServicesContextImpl> dev_tools = |
| GetDevTools(sw_origin); |
| if (dev_tools) { |
| std::map<std::string, std::string> data = { |
| {"Merchant Top Origin", event_data->top_origin.spec()}, |
| {"Merchant Payment Request Origin", |
| event_data->payment_request_origin.spec()}, |
| {"Method Name", method}, |
| {"Payment Handler Name", app_name}, |
| {"Service Worker JavaScript File URL", sw_js_url.spec()}, |
| {"Service Worker Scope", sw_scope.spec()}, |
| {"Service Worker Uses Cache", sw_use_cache ? "true" : "false"}, |
| }; |
| dev_tools->LogBackgroundServiceEvent( |
| /*service_worker_registration_id=*/-1, sw_origin, |
| DevToolsBackgroundService::kPaymentHandler, "Install payment handler", |
| /*instance_id=*/event_data->payment_request_id, data); |
| } |
| |
| std::string string_encoded_icon; |
| if (!app_icon.empty()) { |
| gfx::Image decoded_image = gfx::Image::CreateFrom1xBitmap(app_icon); |
| scoped_refptr<base::RefCountedMemory> raw_data = |
| decoded_image.As1xPNGBytes(); |
| base::Base64Encode( |
| base::StringPiece(raw_data->front_as<char>(), raw_data->size()), |
| &string_encoded_icon); |
| } |
| |
| PaymentAppInstaller::Install( |
| payment_request_web_contents_, app_name, string_encoded_icon, sw_js_url, |
| sw_scope, sw_use_cache, method, supported_delegations, |
| base::BindOnce(&PaymentAppProviderImpl::OnInstallPaymentApp, |
| weak_ptr_factory_.GetWeakPtr(), sw_origin, |
| std::move(event_data), std::move(registration_id_callback), |
| std::move(callback))); |
| } |
| |
| void PaymentAppProviderImpl::UpdatePaymentAppIcon( |
| int64_t registration_id, |
| const std::string& instrument_key, |
| const std::string& name, |
| const std::string& string_encoded_icon, |
| const std::string& method_name, |
| const SupportedDelegations& supported_delegations, |
| PaymentAppProvider::UpdatePaymentAppIconCallback callback) { |
| StoragePartitionImpl* partition = static_cast<StoragePartitionImpl*>( |
| payment_request_web_contents_->GetBrowserContext() |
| ->GetDefaultStoragePartition()); |
| scoped_refptr<PaymentAppContextImpl> payment_app_context = |
| partition->GetPaymentAppContext(); |
| |
| payment_app_context->payment_app_database() |
| ->SetPaymentAppInfoForRegisteredServiceWorker( |
| registration_id, instrument_key, name, string_encoded_icon, |
| method_name, supported_delegations, std::move(callback)); |
| } |
| |
| void PaymentAppProviderImpl::CanMakePayment( |
| int64_t registration_id, |
| const url::Origin& sw_origin, |
| const std::string& payment_request_id, |
| CanMakePaymentEventDataPtr event_data, |
| CanMakePaymentCallback callback) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| DCHECK(payment_request_web_contents_); |
| |
| scoped_refptr<DevToolsBackgroundServicesContextImpl> dev_tools = |
| GetDevTools(sw_origin); |
| if (dev_tools) { |
| std::map<std::string, std::string> data = { |
| {"Merchant Top Origin", event_data->top_origin.spec()}, |
| {"Merchant Payment Request Origin", |
| event_data->payment_request_origin.spec()}}; |
| AddMethodDataToMap(event_data->method_data, &data); |
| AddModifiersToMap(event_data->modifiers, &data); |
| dev_tools->LogBackgroundServiceEvent( |
| registration_id, sw_origin, DevToolsBackgroundService::kPaymentHandler, |
| "Can make payment", |
| /*instance_id=*/payment_request_id, data); |
| } |
| |
| StoragePartitionImpl* partition = static_cast<StoragePartitionImpl*>( |
| payment_request_web_contents_->GetBrowserContext() |
| ->GetDefaultStoragePartition()); |
| scoped_refptr<ServiceWorkerContextWrapper> service_worker_context = |
| partition->GetServiceWorkerContext(); |
| |
| event_dispatcher_->CanMakePayment(registration_id, sw_origin, |
| payment_request_id, std::move(dev_tools), |
| std::move(service_worker_context), |
| std::move(event_data), std::move(callback)); |
| } |
| |
| void PaymentAppProviderImpl::AbortPayment(int64_t registration_id, |
| const url::Origin& sw_origin, |
| const std::string& payment_request_id, |
| AbortCallback callback) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| DCHECK(payment_request_web_contents_); |
| |
| scoped_refptr<DevToolsBackgroundServicesContextImpl> dev_tools = |
| GetDevTools(sw_origin); |
| if (dev_tools) { |
| dev_tools->LogBackgroundServiceEvent( |
| registration_id, sw_origin, DevToolsBackgroundService::kPaymentHandler, |
| "Abort payment", |
| /*instance_id=*/payment_request_id, {}); |
| } |
| |
| StoragePartitionImpl* partition = static_cast<StoragePartitionImpl*>( |
| payment_request_web_contents_->GetBrowserContext() |
| ->GetDefaultStoragePartition()); |
| scoped_refptr<ServiceWorkerContextWrapper> service_worker_context = |
| partition->GetServiceWorkerContext(); |
| |
| event_dispatcher_->AbortPayment( |
| registration_id, sw_origin, payment_request_id, std::move(dev_tools), |
| std::move(service_worker_context), std::move(callback)); |
| } |
| |
| void PaymentAppProviderImpl::SetOpenedWindow( |
| WebContents* payment_handler_web_contents) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| DCHECK(payment_handler_web_contents); |
| |
| CloseOpenedWindow(); |
| DCHECK(!payment_handler_window_); |
| |
| payment_handler_window_ = payment_handler_web_contents->GetWeakPtr(); |
| } |
| |
| void PaymentAppProviderImpl::CloseOpenedWindow() { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| if (payment_handler_window_) { |
| payment_handler_window_->Close(); |
| } |
| |
| payment_handler_window_.reset(); |
| } |
| |
| void PaymentAppProviderImpl::OnClosingOpenedWindow( |
| PaymentEventResponseType reason) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| event_dispatcher_->OnClosingOpenedWindow(reason); |
| } |
| |
| scoped_refptr<DevToolsBackgroundServicesContextImpl> |
| PaymentAppProviderImpl::GetDevTools(const url::Origin& sw_origin) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| DCHECK(payment_request_web_contents_); |
| auto* storage_partition = |
| payment_request_web_contents_->GetBrowserContext() |
| ->GetStoragePartitionForUrl(sw_origin.GetURL(), |
| /*can_create=*/true); |
| if (!storage_partition) |
| return nullptr; |
| |
| scoped_refptr<DevToolsBackgroundServicesContextImpl> dev_tools = |
| static_cast<DevToolsBackgroundServicesContextImpl*>( |
| storage_partition->GetDevToolsBackgroundServicesContext()); |
| return dev_tools && dev_tools->IsRecording( |
| DevToolsBackgroundService::kPaymentHandler) |
| ? dev_tools |
| : nullptr; |
| } |
| |
| void PaymentAppProviderImpl::StartServiceWorkerForDispatch( |
| int64_t registration_id, |
| PaymentEventDispatcher::ServiceWorkerStartCallback callback) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| StoragePartitionImpl* partition = static_cast<StoragePartitionImpl*>( |
| payment_request_web_contents_->GetBrowserContext() |
| ->GetDefaultStoragePartition()); |
| scoped_refptr<ServiceWorkerContextWrapper> service_worker_context = |
| partition->GetServiceWorkerContext(); |
| |
| event_dispatcher_->FindRegistration(std::move(service_worker_context), |
| registration_id, std::move(callback)); |
| } |
| |
| void PaymentAppProviderImpl::OnInstallPaymentApp( |
| const url::Origin& sw_origin, |
| PaymentRequestEventDataPtr event_data, |
| RegistrationIdCallback registration_id_callback, |
| InvokePaymentAppCallback callback, |
| int64_t registration_id) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| DCHECK(payment_request_web_contents_); |
| |
| scoped_refptr<DevToolsBackgroundServicesContextImpl> dev_tools = |
| GetDevTools(sw_origin); |
| if (dev_tools) { |
| std::map<std::string, std::string> data = { |
| {"Payment Handler Install Success", |
| registration_id >= 0 ? "true" : "false"}, |
| }; |
| dev_tools->LogBackgroundServiceEvent( |
| registration_id, sw_origin, DevToolsBackgroundService::kPaymentHandler, |
| "Install payment handler result", |
| /*instance_id=*/event_data->payment_request_id, data); |
| } |
| |
| if (registration_id >= 0) { |
| std::move(registration_id_callback).Run(registration_id); |
| InvokePaymentApp(registration_id, sw_origin, std::move(event_data), |
| std::move(callback)); |
| } else { |
| std::move(callback).Run( |
| PaymentAppProviderUtil::CreateBlankPaymentHandlerResponse( |
| PaymentEventResponseType::PAYMENT_HANDLER_INSTALL_FAILED)); |
| } |
| } |
| |
| PaymentAppProviderImpl::PaymentAppProviderImpl( |
| WebContents* payment_request_web_contents) |
| : WebContentsUserData<PaymentAppProviderImpl>( |
| *payment_request_web_contents), |
| payment_request_web_contents_(payment_request_web_contents), |
| event_dispatcher_(std::make_unique<PaymentEventDispatcher>()) { |
| event_dispatcher_->set_payment_app_provider(weak_ptr_factory_.GetWeakPtr()); |
| } |
| |
| PaymentAppProviderImpl::~PaymentAppProviderImpl() = default; |
| |
| WEB_CONTENTS_USER_DATA_KEY_IMPL(PaymentAppProviderImpl); |
| } // namespace content |