|  | // Copyright 2023 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_DIRECT_RECEIVER_H_ | 
|  | #define MOJO_PUBLIC_CPP_BINDINGS_DIRECT_RECEIVER_H_ | 
|  |  | 
|  | #include <cstdint> | 
|  | #include <map> | 
|  | #include <memory> | 
|  | #include <string_view> | 
|  | #include <utility> | 
|  |  | 
|  | #include "base/component_export.h" | 
|  | #include "base/containers/flat_map.h" | 
|  | #include "base/memory/ref_counted.h" | 
|  | #include "base/memory/scoped_refptr.h" | 
|  | #include "base/memory/weak_ptr.h" | 
|  | #include "base/task/single_thread_task_runner.h" | 
|  | #include "base/threading/thread_checker.h" | 
|  | #include "base/types/pass_key.h" | 
|  | #include "build/build_config.h" | 
|  | #include "mojo/public/cpp/bindings/pending_receiver.h" | 
|  | #include "mojo/public/cpp/bindings/receiver.h" | 
|  | #include "mojo/public/cpp/system/handle.h" | 
|  | #include "mojo/public/cpp/system/message_pipe.h" | 
|  | #include "third_party/ipcz/include/ipcz/ipcz.h" | 
|  |  | 
|  | namespace blink { | 
|  | class WidgetInputHandlerImpl; | 
|  | } | 
|  |  | 
|  | namespace cc::mojo_embedder { | 
|  | class AsyncLayerTreeFrameSink; | 
|  | } | 
|  |  | 
|  | namespace cc::slim { | 
|  | class FrameSinkImpl; | 
|  | } | 
|  |  | 
|  | namespace viz { | 
|  | class CompositorFrameSinkImpl; | 
|  | class FrameSinkManagerImpl; | 
|  | } | 
|  |  | 
|  | namespace mojo { | 
|  |  | 
|  | namespace internal { | 
|  |  | 
|  | // Encapsulates a thread-local ipcz node which is brought up lazily by any | 
|  | // DirectReceiver when binding a pipe on a specific thread. The underlying node | 
|  | // is ref-counted such that a thread's node is automatically torn down when its | 
|  | // last DirectReceiver goes away. (Except in sandboxed processes on Windows, | 
|  | // which only allow a fixed number of ThreadLocalNodes, so once created they're | 
|  | // never deleted.) | 
|  | // TODO(crbug.com/446199357): Remove the refcounting completely. It's unneeded | 
|  | // complexity. | 
|  | class COMPONENT_EXPORT(MOJO_CPP_BINDINGS) ThreadLocalNode | 
|  | : public base::RefCounted<ThreadLocalNode> { | 
|  | public: | 
|  | // Construction requires a PassKey private to this class. In practice, | 
|  | // constructed only within the static `Get()` method as needed. | 
|  | explicit ThreadLocalNode(base::PassKey<ThreadLocalNode>); | 
|  |  | 
|  | // Gets the current thread's ThreadLocalNode instance, initializing a new one | 
|  | // if one doesn't already exist. | 
|  | static scoped_refptr<ThreadLocalNode> Get(); | 
|  |  | 
|  | // Indicates whether a ThreadLocalNode instance exists for the current thread. | 
|  | static bool CurrentThreadHasInstance(); | 
|  |  | 
|  | IpczHandle node() const { | 
|  | DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); | 
|  | return node_->value(); | 
|  | } | 
|  |  | 
|  | // Transfers `pipe` from the process's global node to the thread-local ipcz | 
|  | // node owned by this object and returns a new pipe handle for it. | 
|  | ScopedMessagePipeHandle AdoptPipe(ScopedMessagePipeHandle pipe); | 
|  |  | 
|  | private: | 
|  | friend class base::RefCounted<ThreadLocalNode>; | 
|  |  | 
|  | ~ThreadLocalNode(); | 
|  |  | 
|  | void WatchForIncomingTransfers(); | 
|  | static void OnTrapEvent(const IpczTrapEvent* event); | 
|  | void OnTransferredPortalAvailable(); | 
|  |  | 
|  | THREAD_CHECKER(thread_checker_); | 
|  |  | 
|  | // A dedicated node created for this object. | 
|  | ScopedHandle node_ GUARDED_BY_CONTEXT(thread_checker_); | 
|  |  | 
|  | // A portal on the local node which is connected to `global_portal_`. Used to | 
|  | // receive pipes from the global node. | 
|  | ScopedHandle local_portal_ GUARDED_BY_CONTEXT(thread_checker_); | 
|  |  | 
|  | // A portal on the global node which is connected to `local_portal_`. Used to | 
|  | // transfer pipes from the global node to the local one. | 
|  | ScopedHandle global_portal_ GUARDED_BY_CONTEXT(thread_checker_); | 
|  |  | 
|  | // Tracks pending portal merges. See AdoptPortal() implementation for gritty | 
|  | // details. | 
|  | uint64_t next_merge_id_ GUARDED_BY_CONTEXT(thread_checker_) = 0; | 
|  | std::map<uint64_t, ScopedHandle> pending_merges_ | 
|  | GUARDED_BY_CONTEXT(thread_checker_); | 
|  |  | 
|  | base::WeakPtrFactory<ThreadLocalNode> weak_ptr_factory_ | 
|  | GUARDED_BY_CONTEXT(thread_checker_){this}; | 
|  | }; | 
|  |  | 
|  | }  // namespace internal | 
|  |  | 
|  | namespace test::direct_receiver_unittest { | 
|  | class ServiceImpl; | 
|  | }  // namespace test::direct_receiver_unittest | 
|  |  | 
|  | // Key object that must be provided to construct a DirectReceiver instance. | 
|  | // See notes on DirectReceiver below to understand why this is guarded. | 
|  | class DirectReceiverKey { | 
|  | private: | 
|  | DirectReceiverKey() = default; | 
|  |  | 
|  | // Update this list and get a mojo/OWNERS approval in order to gain access to | 
|  | // DirectReceiver construction. | 
|  | friend class cc::mojo_embedder::AsyncLayerTreeFrameSink; | 
|  | friend class cc::slim::FrameSinkImpl; | 
|  | friend class mojo::test::direct_receiver_unittest::ServiceImpl; | 
|  | friend class blink::WidgetInputHandlerImpl; | 
|  | friend class viz::CompositorFrameSinkImpl; | 
|  | friend class viz::FrameSinkManagerImpl; | 
|  | }; | 
|  |  | 
|  | // DirectReceiver is a wrapper around the standard Receiver<T> type that always | 
|  | // receives its messages directly from the sender without an IO-thread hop. To | 
|  | // enable this safely DirectReceiver is constrained in a few ways: | 
|  | // | 
|  | //   - It cannot be unbound and moved once bound | 
|  | //   - It's always bound on the current default task runner | 
|  | //   - It must be bound on a thread whose MessagePump exposes an IOWatcher | 
|  | // | 
|  | // As long as any DirectReceiver exists on a thread, there is a thread-local | 
|  | // ThreadLocalNode instance which lives on that thread to receive IPC directly | 
|  | // from out-of-process peers. When one of these DirectReceivers is bound to a | 
|  | // pipe, it indicates that the pipe will be receiving messages on the thread. | 
|  | // For that to happen the pipe is 'transferred' to the ThreadLocalNode. | 
|  | // | 
|  | // TODO(crbug.com/40266729): Find a way to transfer without creating another | 
|  | // ipcz pipe. | 
|  | // | 
|  | // SUBTLE: DirectReceiver internally allocates a LIMITED SYSTEM RESOURCE on many | 
|  | // systems (including Android and Chrome OS) and must therefore be used | 
|  | // sparingly. All usage must be approved by Mojo OWNERS, with access controlled | 
|  | // by the friend list in DirectReceiverKey above. | 
|  | // | 
|  | // EVEN MORE SUBTLE: Any Mojo interface endpoints received in messages to a | 
|  | // DirectReceiver will also permanently receive I/O on the DirectReceiver's | 
|  | // thread. While they may be bound on any thread and otherwise behave like any | 
|  | // other Receiver, their incoming messages will hop through the DirectReceiver's | 
|  | // thread just as messages to other Receivers normally hop through the global IO | 
|  | // thread. Unless you're going to bind them all to the same thread as the | 
|  | // DirectReceiver, passing pipes to your DirectReceiver is likely a BAD IDEA. | 
|  | template <typename T> | 
|  | class DirectReceiver { | 
|  | static_assert( | 
|  | T::kSupportsDirectReceiver, | 
|  | "This interface must be marked with the [DirectReceiver] attribute."); | 
|  |  | 
|  | public: | 
|  | // Creates a DirectReceiver bound to the current thread. | 
|  | DirectReceiver(DirectReceiverKey, T* impl) : receiver_(impl) {} | 
|  | ~DirectReceiver() = default; | 
|  |  | 
|  | void set_disconnect_handler(base::OnceClosure handler) { | 
|  | receiver_.set_disconnect_handler(std::move(handler)); | 
|  | } | 
|  |  | 
|  | bool is_bound() const { return receiver_.is_bound(); } | 
|  |  | 
|  | void ReportBadMessage(std::string_view error) { | 
|  | receiver_.ReportBadMessage(error); | 
|  | } | 
|  |  | 
|  | // Binds this object to `receiver` to receive IPC directly on the calling | 
|  | // thread, which must be the same thread the DirectReceiver was created on. | 
|  | void Bind(PendingReceiver<T> receiver) { | 
|  | receiver_.Bind(receiver.is_valid() ? PendingReceiver<T>(node_->AdoptPipe( | 
|  | receiver.PassPipe())) | 
|  | : std::move(receiver)); | 
|  | } | 
|  |  | 
|  | void ResetWithReason(uint32_t custom_reason_code, | 
|  | std::string_view description) { | 
|  | receiver_.ResetWithReason(custom_reason_code, description); | 
|  | } | 
|  |  | 
|  | internal::ThreadLocalNode& node_for_testing() { return *node_; } | 
|  | Receiver<T>& receiver_for_testing() { return receiver_; } | 
|  |  | 
|  | private: | 
|  | const scoped_refptr<internal::ThreadLocalNode> node_{ | 
|  | internal::ThreadLocalNode::Get()}; | 
|  | Receiver<T> receiver_; | 
|  | }; | 
|  |  | 
|  | // Indicates whether DirectReceiver can be supported in the calling process. | 
|  | COMPONENT_EXPORT(MOJO_CPP_BINDINGS) bool IsDirectReceiverSupported(); | 
|  |  | 
|  | #if BUILDFLAG(IS_WIN) | 
|  |  | 
|  | // The Windows sandbox blocks named pipe creation, so in a sandboxed process | 
|  | // this must be called before the sandbox is locked down. This is safe to call | 
|  | // in an unsandboxed process but is not required. | 
|  | // | 
|  | // This restricts DirectReceiver to a single thread, although the number of | 
|  | // threads could be increased by creating more transports if needed. | 
|  | COMPONENT_EXPORT(MOJO_CPP_BINDINGS) | 
|  | void CreateDirectReceiverTransportBeforeSandbox(); | 
|  |  | 
|  | #endif  // BUILDFLAG(IS_WIN) | 
|  |  | 
|  | }  // namespace mojo | 
|  |  | 
|  | #endif  // MOJO_PUBLIC_CPP_BINDINGS_DIRECT_RECEIVER_H_ |