910e62b5创建于 1月15日历史提交
// 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 MOJO_PUBLIC_CPP_BINDINGS_BINDER_MAP_H_
#define MOJO_PUBLIC_CPP_BINDINGS_BINDER_MAP_H_

#include <map>
#include <string>
#include <type_traits>
#include <vector>

#include "base/component_export.h"
#include "base/containers/contains.h"
#include "base/containers/variant_map.h"
#include "base/functional/callback.h"
#include "base/memory/scoped_refptr.h"
#include "base/task/sequenced_task_runner.h"
#include "base/types/pass_key.h"
#include "build/chromecast_buildflags.h"
#include "mojo/public/cpp/bindings/generic_pending_receiver.h"
#include "mojo/public/cpp/bindings/lib/binder_map_internal.h"

namespace mojo {

// BinderMapWithContext is a helper class that maintains a registry of
// callbacks that bind receivers for arbitrary Mojo interfaces. By default the
// map is empty and cannot bind any interfaces.
//
// Call |Add()| to register a new binder for a specific interface.
// Call |TryBind()| to attempt to run a registered binder on the generic
// input receiver. If a suitable binder is found, it will take ownership of the
// receiver.
//
// If a non-void ContextType is specified, registered callbacks must accept an
// additional ContextType argument, and each invocation of |TryBind()| must
// provide such a value.
//
// NOTE: Most common uses of BinderMapWithContext do not require a context value
// per bind request. Use the BinderMap alias defined below this class in such
// cases.
template <typename ContextType>
class BinderMapWithContext {
 public:
  using PassKey = base::PassKey<BinderMapWithContext>;

  using Traits = internal::BinderContextTraits<ContextType>;
  using ContextValueType = typename Traits::ValueType;
  using GenericBinderType = typename Traits::GenericBinderType;

  template <typename Interface>
  using BinderType = typename Traits::template BinderType<Interface>;

  template <typename Interface>
  using FuncType = typename Traits::template FuncType<Interface>;

  BinderMapWithContext() : binders_(PassKey()) {}

  BinderMapWithContext(const BinderMapWithContext&) = default;
  BinderMapWithContext(BinderMapWithContext&&) = default;
  ~BinderMapWithContext() = default;

  BinderMapWithContext& operator=(const BinderMapWithContext&) = default;
  BinderMapWithContext& operator=(BinderMapWithContext&&) = default;

  // Adds a new binder specifically for Interface receivers. This exists for the
  // convenience of being able to register strongly-typed binding methods like:
  //
  //   void OnBindFoo(mojo::PendingReceiver<Foo> receiver) { ... }
  //
  // more easily.
  //
  // If Add() is called multiple times for the same interface, the most recent
  // one replaces any existing binder.
  template <typename Interface>
  void Add(std::type_identity_t<BinderType<Interface>> binder,
           scoped_refptr<base::SequencedTaskRunner> task_runner = nullptr) {
    Add(internal::StaticString(Interface::Name_),
        internal::GenericCallbackBinderWithContext<ContextType>(
            Traits::MakeGenericBinder(std::move(binder)),
            std::move(task_runner)));
  }

  // Adds a new binder specifically for Interface functors. This exists for the
  // convenience of being able to register strongly-typed functors like:
  //
  //   void OnBindFoo(mojo::PendingReceiver<Foo> receiver) { ... }
  //
  // more easily.
  //
  // If Add() is called multiple times for the same interface, the most recent
  // one replaces any existing binder.
  template <typename Interface>
  void Add(std::type_identity_t<FuncType<Interface>>* func,
           scoped_refptr<base::SequencedTaskRunner> task_runner = nullptr) {
    Add(internal::StaticString(Interface::Name_),
        internal::GenericCallbackBinderWithContext<ContextType>(
            Traits::MakeGenericBinder(func), std::move(task_runner)));
  }

  // Returns true if this map contains a binder for `Interface` receivers.
  template <typename Interface>
  bool Contains() {
    return base::Contains(binders_, Interface::Name_);
  }

  // Attempts to bind the |receiver| using one of the registered binders in
  // this map. If a matching binder is found, ownership of the |receiver|'s
  // MessagePipe will be transferred and this will return |true|. If the binder
  // was registered with a SequencedTaskRunner, the binder will be dispatched
  // asynchronously on it; otherwise, it will be called directly.
  //
  // If no matching binder is found, this returns |false| and the |receiver|
  // will be left intact for the caller.
  //
  // This method is only usable when ContextType is void.
  [[nodiscard]] bool TryBind(mojo::GenericPendingReceiver* receiver) {
    static_assert(IsVoidContext::value,
                  "TryBind() must be called with a context value when "
                  "ContextType is non-void.");
    auto it = binders_.find(*receiver->interface_name());
    if (it == binders_.end()) {
      return false;
    }

    it->second.BindInterface(receiver->PassPipe());
    return true;
  }

  // Like above, but passes |context| to the binder if one exists. Only usable
  // when ContextType is non-void.
  [[nodiscard]] bool TryBind(ContextValueType context,
                             mojo::GenericPendingReceiver* receiver) {
    static_assert(!IsVoidContext::value,
                  "TryBind() must be called without a context value when "
                  "ContextType is void.");
    auto it = binders_.find(*receiver->interface_name());
    if (it == binders_.end()) {
#if BUILDFLAG(IS_CASTOS) || BUILDFLAG(IS_CAST_ANDROID)
      return default_binder_ && default_binder_.Run(context, *receiver);
#else
      return false;
#endif
    }

    it->second.BindInterface(std::move(context), receiver->PassPipe());
    return true;
  }

#if BUILDFLAG(IS_CASTOS) || BUILDFLAG(IS_CAST_ANDROID)
  // DO NOT USE. This sets a generic default handler for any receiver that
  // doesn't match a registered binder. It's a transitional API to help migrate
  // some older code to BinderMap. Reliance on this mechanism makes security
  // auditing more difficult. Note that this intentionally only supports use
  // with a non-void ContextType, since that's the only existing use case.
  using DefaultBinder =
      base::RepeatingCallback<bool(ContextValueType context,
                                   mojo::GenericPendingReceiver&)>;
  void SetDefaultBinderDeprecated(DefaultBinder binder) {
    default_binder_ = std::move(binder);
  }
#endif

  void GetInterfacesForTesting(std::vector<std::string>& out) {
    for (const auto& [key, _] : binders_) {
      out.push_back(std::string(key));
    }
  }

 private:
  using IsVoidContext = std::is_same<ContextType, void>;

  void Add(internal::StaticString name,
           internal::GenericCallbackBinderWithContext<ContextType>&& binder) {
    // This is not a public method because it is not safe to use with a
    // non-static `name`. The map key is a `string_view` which would result in
    // a dangling pointer if the underlying string were to be freed.
    // While it may be possible to make this safe by using `std::string` as the
    // key, this is explicitly not supported, as we want to avoid the overhead
    // of copying strings at runtime.
    auto key = std::string_view(name);
    binders_.erase(key);
    binders_.try_emplace(key, std::move(binder));
  }

  base::VariantMap<std::string_view,
                   internal::GenericCallbackBinderWithContext<ContextType>>
      binders_;

#if BUILDFLAG(IS_CASTOS) || BUILDFLAG(IS_CAST_ANDROID)
  DefaultBinder default_binder_;
#endif
};

// Common alias for BinderMapWithContext that has no context. Binders added to
// this type of map will only take a single PendingReceiver<T> argument (or a
// GenericPendingReceiver).
class BinderMap : public BinderMapWithContext<void> {};

}  // namespace mojo

#endif  // MOJO_PUBLIC_CPP_BINDINGS_BINDER_MAP_H_