blob: 02f43ea2c75566985f867565befee50e6828b000 [file] [log] [blame]
// 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/functional/callback.h"
#include "base/memory/scoped_refptr.h"
#include "base/task/sequenced_task_runner.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 Traits = internal::BinderContextTraits<ContextType>;
using ContextValueType = typename Traits::ValueType;
using GenericBinderType = typename Traits::GenericBinderType;
template <typename Interface>
using BinderType = typename Traits::template BinderType<Interface>;
BinderMapWithContext() = default;
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::common_type_t<BinderType<Interface>> binder,
scoped_refptr<base::SequencedTaskRunner> task_runner = nullptr) {
binders_[Interface::Name_] = std::make_unique<
internal::GenericCallbackBinderWithContext<ContextType>>(
Traits::MakeGenericBinder(std::move(binder)), 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())
return default_binder_ && default_binder_.Run(context, *receiver);
it->second->BindInterface(std::move(context), receiver->PassPipe());
return true;
}
// 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);
}
void GetInterfacesForTesting(std::vector<std::string>& out) {
for (const auto& [key, _] : binders_) {
out.push_back(key);
}
}
private:
using IsVoidContext = std::is_same<ContextType, void>;
std::map<
std::string,
std::unique_ptr<internal::GenericCallbackBinderWithContext<ContextType>>>
binders_;
DefaultBinder default_binder_;
};
// 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_