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

#ifndef CONTENT_BROWSER_BROWSER_INTERFACE_BROKER_IMPL_H_
#define CONTENT_BROWSER_BROWSER_INTERFACE_BROKER_IMPL_H_

#include "base/memory/raw_ptr.h"
#include "content/browser/browser_interface_binders.h"
#include "content/browser/mojo_binder_policy_applier.h"
#include "mojo/public/cpp/bindings/binder_map.h"
#include "mojo/public/cpp/bindings/generic_pending_receiver.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/mojom/browser_interface_broker.mojom.h"

namespace content {

// content's implementation of the BrowserInterfaceBroker interface that binds
// interfaces requested by the renderer. Every execution context type (frame,
// worker etc) owns an instance and registers appropriate handlers, called
// "binders" (see internal::PopulateBinderMap and
// internal::PopulateBinderMapWithContext).
//
// By default, BrowserInterfaceBrokerImpl runs the binder that was registered
// for a given interface when the interface is requested. However, in some cases
// such as prerendering pages, it may be desirable to defer running the binder,
// or take another action. Setting a non-null `MojoBinderPolicyApplier` enables
// this behavior.
//
// Note: BrowserInterfaceBrokerImpl will eventually replace the usage of
// InterfaceProvider and browser manifests, as well as DocumentInterfaceBroker.
template <typename ExecutionContextHost, typename InterfaceBinderContext>
class BrowserInterfaceBrokerImpl : public blink::mojom::BrowserInterfaceBroker {
 public:
  explicit BrowserInterfaceBrokerImpl(ExecutionContextHost* host)
      : host_(host) {
    // The populate functions here define all the interfaces that will be
    // exposed through the broker.
    //
    // The `host` is a templated type (one of RenderFrameHostImpl,
    // ServiceWorkerHost, etc.). which allows the populate steps here to call a
    // set of overloaded functions based on that type. Thus each type of `host`
    // can expose a different set of interfaces, which is determined statically
    // at compile time.
    internal::PopulateBinderMap(host, &binder_map_);
    internal::PopulateBinderMapWithContext(host, &binder_map_with_context_);
  }

  // Disallows copy and move operations.
  BrowserInterfaceBrokerImpl(const BrowserInterfaceBrokerImpl& other) = delete;
  BrowserInterfaceBrokerImpl& operator=(
      const BrowserInterfaceBrokerImpl& other) = delete;
  BrowserInterfaceBrokerImpl(BrowserInterfaceBrokerImpl&&) = delete;
  BrowserInterfaceBrokerImpl& operator=(BrowserInterfaceBrokerImpl&&) = delete;

  // blink::mojom::BrowserInterfaceBroker
  void GetInterface(mojo::GenericPendingReceiver receiver) override {
    DCHECK(receiver.interface_name().has_value());
    if (!policy_applier_) {
      BindInterface(std::move(receiver));
    } else {
      std::string interface_name = receiver.interface_name().value();
      // base::Unretained is safe because `this` outlives `policy_applier_`.
      policy_applier_->ApplyPolicyToNonAssociatedBinder(
          interface_name,
          base::BindOnce(&BrowserInterfaceBrokerImpl::BindInterface,
                         base::Unretained(this), std::move(receiver)));
    }
  }

  // Sets MojoBinderPolicyApplier to control when to bind interfaces.
  void ApplyMojoBinderPolicies(MojoBinderPolicyApplier* policy_applier) {
    DCHECK(policy_applier);
    DCHECK(!policy_applier_);
    policy_applier_ = policy_applier;
  }

  // Stops applying policies to binding requests.
  void ReleaseMojoBinderPolicies() {
    DCHECK(policy_applier_);
    // Reset `policy_applier_` to disable capability control.
    policy_applier_ = nullptr;
  }

  void GetBinderMapInterfacesForTesting(std::vector<std::string>& out) {
    binder_map_.GetInterfacesForTesting(out);
    binder_map_with_context_.GetInterfacesForTesting(out);
  }

 private:
  void BindInterface(mojo::GenericPendingReceiver receiver) {
    if (!binder_map_.TryBind(&receiver)) {
      if (!binder_map_with_context_.TryBind(internal::GetContextForHost(host_),
                                            &receiver)) {
        host_->ReportNoBinderForInterface("No binder found for interface " +
                                          *receiver.interface_name());
      }
    }
  }

  const raw_ptr<ExecutionContextHost> host_;
  mojo::BinderMap binder_map_;
  mojo::BinderMapWithContext<InterfaceBinderContext> binder_map_with_context_;

  // The lifetime of `policy_applier_` is managed by the owner of this instance.
  // The owner should call `ReleaseMojoBinderPolicies()` when it destroys the
  // applier.
  raw_ptr<MojoBinderPolicyApplier> policy_applier_ = nullptr;
};

}  // namespace content

#endif  // CONTENT_BROWSER_BROWSER_INTERFACE_BROKER_IMPL_H_