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

#include "third_party/blink/renderer/modules/credentialmanagement/credential_manager_proxy.h"

#include "third_party/blink/public/platform/browser_interface_broker_proxy.h"
#include "third_party/blink/renderer/core/execution_context/execution_context.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/platform/bindings/script_state.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"

namespace blink {

CredentialManagerProxy::CredentialManagerProxy(LocalDOMWindow& window)
    : Supplement<LocalDOMWindow>(window),
      authenticator_(window.GetExecutionContext()),
      credential_manager_(window.GetExecutionContext()),
      webotp_service_(window.GetExecutionContext()),
      spc_service_(window.GetExecutionContext()),
      federated_request_service_(window.GetExecutionContext()),
      digital_identity_request_(window.GetExecutionContext()) {}

CredentialManagerProxy::~CredentialManagerProxy() = default;

mojom::blink::CredentialManager* CredentialManagerProxy::CredentialManager() {
  if (!credential_manager_.is_bound()) {
    LocalFrame* frame = GetSupplementable()->GetFrame();
    DCHECK(frame);
    frame->GetBrowserInterfaceBroker().GetInterface(
        credential_manager_.BindNewPipeAndPassReceiver(
            frame->GetTaskRunner(TaskType::kUserInteraction)));
  }
  return credential_manager_.get();
}

mojom::blink::Authenticator* CredentialManagerProxy::Authenticator() {
  if (!authenticator_.is_bound()) {
    LocalFrame* frame = GetSupplementable()->GetFrame();
    DCHECK(frame);
    frame->GetBrowserInterfaceBroker().GetInterface(
        authenticator_.BindNewPipeAndPassReceiver(
            frame->GetTaskRunner(TaskType::kUserInteraction)));
  }
  return authenticator_.get();
}

mojom::blink::WebOTPService* CredentialManagerProxy::WebOTPService() {
  if (!webotp_service_.is_bound()) {
    LocalFrame* frame = GetSupplementable()->GetFrame();
    DCHECK(frame);
    frame->GetBrowserInterfaceBroker().GetInterface(
        webotp_service_.BindNewPipeAndPassReceiver(
            frame->GetTaskRunner(TaskType::kMiscPlatformAPI)));
  }
  return webotp_service_.get();
}

payments::mojom::blink::SecurePaymentConfirmationService*
CredentialManagerProxy::SecurePaymentConfirmationService() {
  if (!spc_service_.is_bound()) {
    LocalFrame* frame = GetSupplementable()->GetFrame();
    DCHECK(frame);
    frame->GetBrowserInterfaceBroker().GetInterface(
        spc_service_.BindNewPipeAndPassReceiver(
            frame->GetTaskRunner(TaskType::kMiscPlatformAPI)));
  }
  return spc_service_.get();
}

template <typename Interface>
void CredentialManagerProxy::BindRemoteForFedCm(
    HeapMojoRemote<Interface>& remote,
    base::OnceClosure disconnect_closure) {
  if (remote.is_bound())
    return;

  LocalFrame* frame = GetSupplementable()->GetFrame();
  // TODO(kenrb): Work out whether kUserInteraction is the best task type
  // here. It might be appropriate to create a new one.
  frame->GetBrowserInterfaceBroker().GetInterface(
      remote.BindNewPipeAndPassReceiver(
          frame->GetTaskRunner(TaskType::kUserInteraction)));
  remote.set_disconnect_handler(std::move(disconnect_closure));
}

mojom::blink::FederatedRequestService*
CredentialManagerProxy::FederatedRequestService() {
  BindRemoteForFedCm(
      federated_request_service_,
      BindOnce(
          &CredentialManagerProxy::OnFederatedRequestServiceConnectionError,
          WrapWeakPersistent(this)));
  return federated_request_service_.get();
}

void CredentialManagerProxy::OnFederatedRequestServiceConnectionError() {
  federated_request_service_.reset();
}

mojom::blink::DigitalIdentityRequest*
CredentialManagerProxy::DigitalIdentityRequest() {
  BindRemoteForFedCm(
      digital_identity_request_,
      BindOnce(&CredentialManagerProxy::OnDigitalIdentityRequestConnectionError,
               WrapWeakPersistent(this)));
  return digital_identity_request_.get();
}

void CredentialManagerProxy::OnDigitalIdentityRequestConnectionError() {
  digital_identity_request_.reset();
}

// TODO(crbug.com/1372275): Replace From(ScriptState*) with
// From(ExecutionContext*)
// static
CredentialManagerProxy* CredentialManagerProxy::From(
    ScriptState* script_state) {
  DCHECK(script_state->ContextIsValid());
  LocalDOMWindow& window = *LocalDOMWindow::From(script_state);
  return From(&window);
}

CredentialManagerProxy* CredentialManagerProxy::From(LocalDOMWindow* window) {
  auto* supplement =
      Supplement<LocalDOMWindow>::From<CredentialManagerProxy>(*window);
  if (!supplement) {
    supplement = MakeGarbageCollected<CredentialManagerProxy>(*window);
    ProvideTo(*window, supplement);
  }
  return supplement;
}

// static
CredentialManagerProxy* CredentialManagerProxy::From(
    ExecutionContext* execution_context) {
  // Since the FedCM API cannot be used by workers, the execution context is
  // always a window.
  LocalDOMWindow& window = *To<LocalDOMWindow>(execution_context);
  auto* supplement =
      Supplement<LocalDOMWindow>::From<CredentialManagerProxy>(window);
  if (!supplement) {
    supplement = MakeGarbageCollected<CredentialManagerProxy>(window);
    ProvideTo(window, supplement);
  }
  return supplement;
}

void CredentialManagerProxy::Trace(Visitor* visitor) const {
  visitor->Trace(authenticator_);
  visitor->Trace(credential_manager_);
  visitor->Trace(webotp_service_);
  visitor->Trace(spc_service_);
  visitor->Trace(federated_request_service_);
  visitor->Trace(digital_identity_request_);
  Supplement<LocalDOMWindow>::Trace(visitor);
}

// static
const char CredentialManagerProxy::kSupplementName[] = "CredentialManagerProxy";

}  // namespace blink
