chromium/components/payments/content/android/payment_app_service_bridge.cc

// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "components/payments/content/android/payment_app_service_bridge.h"

#include <string>
#include <utility>
#include <vector>

#include "base/android/jni_array.h"
#include "base/android/jni_string.h"
#include "base/android/scoped_java_ref.h"
#include "base/check_op.h"
#include "base/functional/bind.h"
#include "base/location.h"
#include "base/memory/singleton.h"
#include "base/notreached.h"
#include "base/task/single_thread_task_runner.h"
#include "components/payments/content/android/byte_buffer_helper.h"
#include "components/payments/content/android/csp_checker_android.h"
#include "components/payments/content/android/jni_payment_app.h"
#include "components/payments/content/android/payment_request_spec.h"
#include "components/payments/content/payment_app_service.h"
#include "components/payments/content/payment_manifest_web_data_service.h"
#include "components/payments/content/payment_request_spec.h"
#include "components/url_formatter/elide_url.h"
#include "components/webauthn/android/internal_authenticator_android.h"
#include "components/webdata_services/web_data_service_wrapper_factory.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/web_contents.h"
#include "third_party/blink/public/mojom/payments/payment_app.mojom.h"
#include "third_party/blink/public/mojom/payments/payment_request.mojom.h"
#include "ui/gfx/android/java_bitmap.h"
#include "url/android/gurl_android.h"
#include "url/origin.h"

// Must come after all headers that specialize FromJniType() / ToJniType().
#include "components/payments/content/android/jni_headers/PaymentAppServiceBridge_jni.h"

namespace {
using ::base::android::AttachCurrentThread;
using ::base::android::ConvertJavaStringToUTF8;
using ::base::android::ConvertUTF8ToJavaString;
using ::base::android::JavaParamRef;
using ::base::android::JavaRef;
using ::base::android::ScopedJavaGlobalRef;
using ::payments::mojom::PaymentMethodDataPtr;

void OnCanMakePaymentCalculated(const JavaRef<jobject>& jcallback,
                                bool can_make_payment) {
  Java_PaymentAppServiceCallback_onCanMakePaymentCalculated(
      AttachCurrentThread(), jcallback, can_make_payment);
}

void OnPaymentAppCreated(const JavaRef<jobject>& jcallback,
                         std::unique_ptr<payments::PaymentApp> payment_app) {
  JNIEnv* env = AttachCurrentThread();
  Java_PaymentAppServiceCallback_onPaymentAppCreated(
      env, jcallback,
      payments::JniPaymentApp::Create(env, std::move(payment_app)));
}

void OnPaymentAppCreationError(
    const JavaRef<jobject>& jcallback,
    const std::string& error_message,
    payments::AppCreationFailureReason error_reason) {
  JNIEnv* env = AttachCurrentThread();
  Java_PaymentAppServiceCallback_onPaymentAppCreationError(
      env, jcallback, ConvertUTF8ToJavaString(env, error_message),
      static_cast<jint>(error_reason));
}

void OnDoneCreatingPaymentApps(const JavaRef<jobject>& jcallback) {
  JNIEnv* env = AttachCurrentThread();
  Java_PaymentAppServiceCallback_onDoneCreatingPaymentApps(env, jcallback);
}

void SetCanMakePaymentEvenWithoutApps(const JavaRef<jobject>& jcallback) {
  JNIEnv* env = AttachCurrentThread();
  if (!env)
    return;
  Java_PaymentAppServiceCallback_setCanMakePaymentEvenWithoutApps(env,
                                                                  jcallback);
}

void SetOptOutOffered(const JavaRef<jobject>& jcallback) {
  JNIEnv* env = AttachCurrentThread();
  if (!env)
    return;
  Java_PaymentAppServiceCallback_setOptOutOffered(env, jcallback);
}

}  // namespace

/* static */
void JNI_PaymentAppServiceBridge_Create(
    JNIEnv* env,
    const JavaParamRef<jobject>& jrender_frame_host,
    const JavaParamRef<jstring>& jtop_origin,
    const JavaParamRef<jobject>& jpayment_request_spec,
    const JavaParamRef<jstring>& jtwa_package_name,
    // TODO(crbug.com/40182225): Remove jmay_crawl_for_installable_payment_apps,
    // as it is no longer used.
    jboolean jmay_crawl_for_installable_payment_apps,
    jboolean jis_off_the_record,
    jlong native_csp_checker_android,
    const JavaParamRef<jobject>& jcallback) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);

  auto* render_frame_host =
      content::RenderFrameHost::FromJavaRenderFrameHost(jrender_frame_host);
  if (!render_frame_host)  // The frame is being unloaded.
    return;

  std::string top_origin = ConvertJavaStringToUTF8(jtop_origin);

  scoped_refptr<payments::PaymentManifestWebDataService> web_data_service =
      webdata_services::WebDataServiceWrapperFactory::
          GetPaymentManifestWebDataServiceForBrowserContext(
              render_frame_host->GetBrowserContext(),
              ServiceAccessType::EXPLICIT_ACCESS);

  auto* bridge = payments::PaymentAppServiceBridge::Create(
      std::make_unique<payments::PaymentAppService>(
          render_frame_host->GetBrowserContext()),
      render_frame_host, GURL(top_origin),
      payments::android::PaymentRequestSpec::FromJavaPaymentRequestSpec(
          env, jpayment_request_spec),
      jtwa_package_name ? ConvertJavaStringToUTF8(env, jtwa_package_name) : "",
      web_data_service, jis_off_the_record,
      payments::CSPCheckerAndroid::GetWeakPtr(native_csp_checker_android),
      base::BindOnce(&OnCanMakePaymentCalculated,
                     ScopedJavaGlobalRef<jobject>(env, jcallback)),
      base::BindRepeating(&OnPaymentAppCreated,
                          ScopedJavaGlobalRef<jobject>(env, jcallback)),
      base::BindRepeating(&OnPaymentAppCreationError,
                          ScopedJavaGlobalRef<jobject>(env, jcallback)),
      base::BindOnce(&OnDoneCreatingPaymentApps,
                     ScopedJavaGlobalRef<jobject>(env, jcallback)),
      base::BindRepeating(&SetCanMakePaymentEvenWithoutApps,
                          ScopedJavaGlobalRef<jobject>(env, jcallback)),
      base::BindRepeating(&SetOptOutOffered,
                          ScopedJavaGlobalRef<jobject>(env, jcallback)));

  bridge->CreatePaymentApps();
}

namespace payments {
namespace {

// A singleton class to maintain  ownership of PaymentAppServiceBridge objects
// until Remove() is called.
class PaymentAppServiceBridgeStorage {
 public:
  static PaymentAppServiceBridgeStorage* GetInstance() {
    return base::Singleton<PaymentAppServiceBridgeStorage>::get();
  }

  PaymentAppServiceBridge* Add(std::unique_ptr<PaymentAppServiceBridge> owned) {
    DCHECK(owned);
    PaymentAppServiceBridge* key = owned.get();
    owner_[key] = std::move(owned);
    return key;
  }

  void Remove(PaymentAppServiceBridge* owned) {
    size_t number_of_deleted_objects = owner_.erase(owned);
    DCHECK_EQ(1U, number_of_deleted_objects);
  }

 private:
  friend struct base::DefaultSingletonTraits<PaymentAppServiceBridgeStorage>;
  PaymentAppServiceBridgeStorage() = default;
  ~PaymentAppServiceBridgeStorage() = default;

  std::map<PaymentAppServiceBridge*, std::unique_ptr<PaymentAppServiceBridge>>
      owner_;
};

}  // namespace

/* static */
PaymentAppServiceBridge* PaymentAppServiceBridge::Create(
    std::unique_ptr<PaymentAppService> payment_app_service,
    content::RenderFrameHost* render_frame_host,
    const GURL& top_origin,
    base::WeakPtr<PaymentRequestSpec> spec,
    const std::string& twa_package_name,
    scoped_refptr<PaymentManifestWebDataService> web_data_service,
    bool is_off_the_record,
    base::WeakPtr<CSPChecker> csp_checker,
    CanMakePaymentCalculatedCallback can_make_payment_calculated_callback,
    PaymentAppCreatedCallback payment_app_created_callback,
    PaymentAppCreationErrorCallback payment_app_creation_error_callback,
    base::OnceClosure done_creating_payment_apps_callback,
    base::RepeatingClosure set_can_make_payment_even_without_apps_callback,
    base::RepeatingClosure set_opt_out_offered_callback) {
  DCHECK(render_frame_host);
  // Not using std::make_unique, because that requires a public constructor.
  std::unique_ptr<PaymentAppServiceBridge> bridge(new PaymentAppServiceBridge(
      std::move(payment_app_service), render_frame_host, top_origin, spec,
      twa_package_name, std::move(web_data_service), is_off_the_record,
      csp_checker, std::move(can_make_payment_calculated_callback),
      std::move(payment_app_created_callback),
      std::move(payment_app_creation_error_callback),
      std::move(done_creating_payment_apps_callback),
      std::move(set_can_make_payment_even_without_apps_callback),
      std::move(set_opt_out_offered_callback)));
  return PaymentAppServiceBridgeStorage::GetInstance()->Add(std::move(bridge));
}

PaymentAppServiceBridge::~PaymentAppServiceBridge() = default;

void PaymentAppServiceBridge::CreatePaymentApps() {
  payment_app_service_->Create(weak_ptr_factory_.GetWeakPtr());
}

base::WeakPtr<PaymentAppServiceBridge>
PaymentAppServiceBridge::GetWeakPtrForTest() {
  return weak_ptr_factory_.GetWeakPtr();
}

content::WebContents* PaymentAppServiceBridge::GetWebContents() {
  auto* rfh = content::RenderFrameHost::FromID(frame_routing_id_);
  return rfh && rfh->IsActive() ? content::WebContents::FromRenderFrameHost(rfh)
                                : nullptr;
}
const GURL& PaymentAppServiceBridge::GetTopOrigin() {
  return top_origin_;
}

const GURL& PaymentAppServiceBridge::GetFrameOrigin() {
  return frame_origin_;
}

const url::Origin& PaymentAppServiceBridge::GetFrameSecurityOrigin() {
  return frame_security_origin_;
}

content::RenderFrameHost* PaymentAppServiceBridge::GetInitiatorRenderFrameHost()
    const {
  return content::RenderFrameHost::FromID(frame_routing_id_);
}

content::GlobalRenderFrameHostId
PaymentAppServiceBridge::GetInitiatorRenderFrameHostId() const {
  return frame_routing_id_;
}

const std::vector<PaymentMethodDataPtr>&
PaymentAppServiceBridge::GetMethodData() const {
  DCHECK(spec_);
  return spec_->method_data();
}

std::unique_ptr<webauthn::InternalAuthenticator>
PaymentAppServiceBridge::CreateInternalAuthenticator() const {
  // This authenticator can be used in a cross-origin iframe only if the
  // top-level frame allowed it with Permissions Policy, e.g., with
  // allow="payment" iframe attribute. The secure payment confirmation dialog
  // displays the top-level origin in its UI before the user can click on the
  // [Verify] button to invoke this authenticator.
  auto* rfh = content::RenderFrameHost::FromID(frame_routing_id_);
  // Lifetime of the created authenticator is externally managed by the
  // authenticator factory, but is generally tied to the RenderFrame by
  // listening for `RenderFrameDeleted()`. Check `IsRenderFrameLive()` as a
  // safety precaution to ensure that `RenderFrameDeleted()` will be called at
  // some point.
  return rfh && rfh->IsActive() && rfh->IsRenderFrameLive()
             ? std::make_unique<webauthn::InternalAuthenticatorAndroid>(rfh)
             : nullptr;
}

scoped_refptr<PaymentManifestWebDataService>
PaymentAppServiceBridge::GetPaymentManifestWebDataService() const {
  return payment_manifest_web_data_service_;
}

bool PaymentAppServiceBridge::IsOffTheRecord() const {
  return is_off_the_record_;
}

base::WeakPtr<ContentPaymentRequestDelegate>
PaymentAppServiceBridge::GetPaymentRequestDelegate() const {
  // PaymentAppService flow should have short-circuited before this point.
  NOTREACHED_IN_MIGRATION();
  return nullptr;
}

void PaymentAppServiceBridge::ShowProcessingSpinner() {
  // Java UI determines when the show a spinner itself.
}

base::WeakPtr<PaymentRequestSpec> PaymentAppServiceBridge::GetSpec() const {
  return spec_;
}

void PaymentAppServiceBridge::GetTwaPackageName(
    GetTwaPackageNameCallback callback) {
  base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
      FROM_HERE, base::BindOnce(std::move(callback), twa_package_name_));
}

void PaymentAppServiceBridge::OnPaymentAppCreated(
    std::unique_ptr<PaymentApp> app) {
  if (can_make_payment_calculated_callback_)
    std::move(can_make_payment_calculated_callback_).Run(true);

  payment_app_created_callback_.Run(std::move(app));
}

void PaymentAppServiceBridge::OnPaymentAppCreationError(
    const std::string& error_message,
    AppCreationFailureReason error_reason) {
  payment_app_creation_error_callback_.Run(error_message, error_reason);
}

void PaymentAppServiceBridge::OnDoneCreatingPaymentApps() {
  if (number_of_pending_factories_ > 1U) {
    number_of_pending_factories_--;
    return;
  }

  DCHECK_EQ(1U, number_of_pending_factories_);

  if (can_make_payment_calculated_callback_)
    std::move(can_make_payment_calculated_callback_).Run(false);

  std::move(done_creating_payment_apps_callback_).Run();
  PaymentAppServiceBridgeStorage::GetInstance()->Remove(this);
}

void PaymentAppServiceBridge::SetCanMakePaymentEvenWithoutApps() {
  set_can_make_payment_even_without_apps_callback_.Run();
}

base::WeakPtr<CSPChecker> PaymentAppServiceBridge::GetCSPChecker() {
  return csp_checker_;
}

void PaymentAppServiceBridge::SetOptOutOffered() {
  set_opt_out_offered_callback_.Run();
}

std::optional<base::UnguessableToken>
PaymentAppServiceBridge::GetChromeOSTWAInstanceId() const {
  return std::nullopt;
}

PaymentAppServiceBridge::PaymentAppServiceBridge(
    std::unique_ptr<PaymentAppService> payment_app_service,
    content::RenderFrameHost* render_frame_host,
    const GURL& top_origin,
    base::WeakPtr<PaymentRequestSpec> spec,
    const std::string& twa_package_name,
    scoped_refptr<PaymentManifestWebDataService> web_data_service,
    bool is_off_the_record,
    base::WeakPtr<CSPChecker> csp_checker,
    CanMakePaymentCalculatedCallback can_make_payment_calculated_callback,
    PaymentAppCreatedCallback payment_app_created_callback,
    PaymentAppCreationErrorCallback payment_app_creation_error_callback,
    base::OnceClosure done_creating_payment_apps_callback,
    base::RepeatingClosure set_can_make_payment_even_without_apps_callback,
    base::RepeatingClosure set_opt_out_offered_callback)
    : payment_app_service_(std::move(payment_app_service)),
      number_of_pending_factories_(
          payment_app_service_->GetNumberOfFactories()),
      frame_routing_id_(render_frame_host->GetGlobalId()),
      top_origin_(top_origin),
      frame_origin_(url_formatter::FormatUrlForSecurityDisplay(
          render_frame_host->GetLastCommittedURL())),
      frame_security_origin_(render_frame_host->GetLastCommittedOrigin()),
      spec_(spec),
      twa_package_name_(twa_package_name),
      payment_manifest_web_data_service_(web_data_service),
      is_off_the_record_(is_off_the_record),
      csp_checker_(csp_checker),
      can_make_payment_calculated_callback_(
          std::move(can_make_payment_calculated_callback)),
      payment_app_created_callback_(std::move(payment_app_created_callback)),
      payment_app_creation_error_callback_(
          std::move(payment_app_creation_error_callback)),
      done_creating_payment_apps_callback_(
          std::move(done_creating_payment_apps_callback)),
      set_can_make_payment_even_without_apps_callback_(
          std::move(set_can_make_payment_even_without_apps_callback)),
      set_opt_out_offered_callback_(std::move(set_opt_out_offered_callback)) {}

}  // namespace payments