#ifndef MOJO_PUBLIC_CPP_BINDINGS_REMOTE_SET_H_
#define MOJO_PUBLIC_CPP_BINDINGS_REMOTE_SET_H_
#include <iterator>
#include <optional>
#include <set>
#include <utility>
#include "base/containers/contains.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/memory/scoped_refptr.h"
#include "base/task/sequenced_task_runner.h"
#include "base/types/id_type.h"
#include "mojo/public/cpp/bindings/associated_remote.h"
#include "mojo/public/cpp/bindings/pending_associated_remote.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/bindings/runtime_features.h"
namespace mojo {
namespace internal {
struct RemoteSetElementIdTypeTag {};
}
using RemoteSetElementId = base::IdTypeU32<internal::RemoteSetElementIdTypeTag>;
template <typename Interface,
template <typename>
class RemoteType,
template <typename>
class PendingRemoteType>
class RemoteSetImpl {
public:
using Storage = std::map<RemoteSetElementId, RemoteType<Interface>>;
struct Iterator {
using self_type = Iterator;
using value_type = RemoteType<Interface>;
using reference = const value_type&;
using pointer = const value_type*;
using iterator_category = std::bidirectional_iterator_tag;
using difference_type = std::ptrdiff_t;
explicit Iterator(typename Storage::const_iterator it) : it_(it) {}
self_type& operator++() {
++it_;
return *this;
}
self_type operator++(int) {
self_type result(*this);
++(*this);
return result;
}
self_type& operator--() {
--it_;
return *this;
}
self_type operator--(int) {
self_type result(*this);
--(*this);
return result;
}
RemoteSetElementId id() const { return it_->first; }
reference operator*() const { return it_->second; }
pointer operator->() const { return &it_->second; }
bool operator==(const self_type& rhs) const { return it_ == rhs.it_; }
bool operator!=(const self_type& rhs) const { return it_ != rhs.it_; }
private:
typename Storage::const_iterator it_;
};
RemoteSetImpl() = default;
RemoteSetImpl(const RemoteSetImpl&) = delete;
RemoteSetImpl& operator=(const RemoteSetImpl&) = delete;
~RemoteSetImpl() = default;
RemoteSetElementId Add(RemoteType<Interface> remote)
requires(!internal::kIsRuntimeFeatureGuarded<Interface>)
{
return AddImpl(std::move(remote));
}
RemoteSetElementId Add(
PendingRemoteType<Interface> remote,
scoped_refptr<base::SequencedTaskRunner> task_runner = nullptr)
requires(!internal::kIsRuntimeFeatureGuarded<Interface>)
{
return AddImpl(std::move(remote), std::move(task_runner));
}
std::optional<RemoteSetElementId> Add(RemoteType<Interface> remote)
requires(internal::kIsRuntimeFeatureGuarded<Interface>)
{
if (!internal::GetRuntimeFeature_ExpectEnabled<Interface>()) {
return std::nullopt;
}
return AddImpl(std::move(remote));
}
std::optional<RemoteSetElementId> Add(
PendingRemoteType<Interface> remote,
scoped_refptr<base::SequencedTaskRunner> task_runner = nullptr)
requires(internal::kIsRuntimeFeatureGuarded<Interface>)
{
if (!internal::GetRuntimeFeature_ExpectEnabled<Interface>()) {
return std::nullopt;
}
return AddImpl(std::move(remote), std::move(task_runner));
}
void Remove(RemoteSetElementId id) { storage_.erase(id); }
void RemoveWithReason(RemoteSetElementId id,
uint32_t custom_reason_code,
const std::string& description) {
auto it = storage_.find(id);
if (it == storage_.end()) {
return;
}
it->second.ResetWithReason(custom_reason_code, description);
storage_.erase(it);
}
bool Contains(RemoteSetElementId id) { return base::Contains(storage_, id); }
Interface* Get(RemoteSetElementId id) {
auto it = storage_.find(id);
if (it == storage_.end())
return nullptr;
return it->second.get();
}
using DisconnectHandler = base::RepeatingCallback<void(RemoteSetElementId)>;
using DisconnectWithReasonHandler =
base::RepeatingCallback<void(RemoteSetElementId,
uint32_t ,
const std::string& )>;
void set_disconnect_handler(DisconnectHandler handler) {
disconnect_handler_ = std::move(handler);
disconnect_with_reason_handler_.Reset();
}
void set_disconnect_with_reason_handler(DisconnectWithReasonHandler handler) {
disconnect_with_reason_handler_ = std::move(handler);
disconnect_handler_.Reset();
}
void Clear() { storage_.clear(); }
void ClearWithReason(uint32_t custom_reason_code,
const std::string& description) {
for (auto& [_, remote] : storage_) {
remote.ResetWithReason(custom_reason_code, description);
}
Clear();
}
bool empty() const { return storage_.empty(); }
size_t size() const { return storage_.size(); }
Iterator begin() { return Iterator(storage_.begin()); }
Iterator begin() const { return Iterator(storage_.begin()); }
Iterator end() { return Iterator(storage_.end()); }
Iterator end() const { return Iterator(storage_.end()); }
void FlushForTesting() {
std::vector<RemoteSetElementId> ids;
for (const auto& entry : storage_) {
ids.push_back(entry.first);
}
for (auto& id : ids) {
auto it = storage_.find(id);
if (it != storage_.end()) {
it->second.FlushForTesting();
}
}
}
private:
RemoteSetElementId AddImpl(RemoteType<Interface> remote) {
DCHECK(remote.is_bound());
auto id = GenerateNextElementId();
remote.set_disconnect_with_reason_handler(base::BindOnce(
&RemoteSetImpl::OnDisconnect, base::Unretained(this), id));
auto result = storage_.emplace(id, std::move(remote));
DCHECK(result.second);
return id;
}
RemoteSetElementId AddImpl(
PendingRemoteType<Interface> remote,
scoped_refptr<base::SequencedTaskRunner> task_runner = nullptr) {
DCHECK(remote.is_valid());
return AddImpl(
RemoteType<Interface>(std::move(remote), std::move(task_runner)));
}
RemoteSetElementId GenerateNextElementId() {
return remote_set_element_id_generator_.GenerateNextId();
}
void OnDisconnect(RemoteSetElementId id,
uint32_t custom_reason_code,
const std::string& description) {
Remove(id);
if (disconnect_handler_)
disconnect_handler_.Run(id);
else if (disconnect_with_reason_handler_) {
disconnect_with_reason_handler_.Run(id, custom_reason_code, description);
}
}
RemoteSetElementId::Generator remote_set_element_id_generator_;
Storage storage_;
DisconnectHandler disconnect_handler_;
DisconnectWithReasonHandler disconnect_with_reason_handler_;
};
template <typename Interface>
using RemoteSet = RemoteSetImpl<Interface, Remote, PendingRemote>;
template <typename Interface>
using AssociatedRemoteSet =
RemoteSetImpl<Interface, AssociatedRemote, PendingAssociatedRemote>;
}
#endif