blob: 1583b89092ae6139b40a081890538faa472f633d [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef THIRD_PARTY_BLINK_PUBLIC_COMMON_MESSAGING_MESSAGE_PORT_DESCRIPTOR_H_
#define THIRD_PARTY_BLINK_PUBLIC_COMMON_MESSAGING_MESSAGE_PORT_DESCRIPTOR_H_
#include "base/unguessable_token.h"
#include "mojo/public/cpp/system/message_pipe.h"
#include "third_party/blink/public/common/common_export.h"
namespace blink {
class ExecutionContext;
// Defines a message port descriptor, which is a mojo::MessagePipeHandle and
// some associated state which follows the handle around as it is passed from
// one execution context to another. This can be serialized into a
// mojom::MessagePortDescriptor using MessagePortDescriptorStructTraits. Since
// this class uses only POD and Mojo types the same serialization logic is fine
// for use both inside and outside of Blink. This type is move-only so that only
// a single representation of an endpoint can exist at any moment in time.
// This class is not thread-safe, but a MessagePortDescriptor is only ever owned
// by a single thread at a time.
//
// A MessagePortDescriptor should never be created in isolation, but rather they
// should only be created in pairs via MessagePortDescriptorPair.
//
// To enforce that a Mojo pipe isn't left dangling, MessagePortDescriptors
// enforce that they are only destroyed while holding onto their pipe.
//
// This class is intended to be used as follows:
//
// MessagePortDescriptorPair port_pair;
// MessagePortDescriptor port0 = port_pair.TakePort0();
// MessagePortDescriptor port1 = port_pair.TakePort1();
//
// ... pass around the port descriptors in TransferableMessages as desired ...
//
// // Pass this into a MessagePortChannel for reference-counted safe-keeping.
// MessagePortChannel channel0(port0);
//
// // Entangle into a MessagePort for use in sending messages.
// MessagePort message_port;
// message_port.Entangle(channel0)
// message_port.postMessage(...);
// channel0 = message_port.Disentangle();
//
// Note that there is a Java wrapper to this class implemented by
// org.chromium.content.browser.AppWebMessagePortDescriptor.
class BLINK_COMMON_EXPORT MessagePortDescriptor {
public:
// Delegate used to provide information about the state of message ports.
// See full class declaration below.
class InstrumentationDelegate;
// Allows setting a singleton instrumentation delegate. This is not
// thread-safe and should be set in early Blink initialization.
static void SetInstrumentationDelegate(InstrumentationDelegate* delegate);
MessagePortDescriptor();
// Disallow copying, and enforce move-only semantics.
MessagePortDescriptor(const MessagePortDescriptor& message_port) = delete;
MessagePortDescriptor(MessagePortDescriptor&& message_port);
MessagePortDescriptor& operator=(const MessagePortDescriptor& message_port) =
delete;
MessagePortDescriptor& operator=(MessagePortDescriptor&& message_port);
~MessagePortDescriptor();
// Simple accessors.
const mojo::ScopedMessagePipeHandle& handle() const { return handle_; }
const base::UnguessableToken& id() const { return id_; }
uint64_t sequence_number() const { return sequence_number_; }
// Helper accessor for getting the underlying Mojo handle. Makes tests a
// little easier to write.
MojoHandle GetMojoHandleForTesting() const;
// Returns true if this is a valid descriptor.
bool IsValid() const;
// Returns true if this descriptor is currently entangled (meaning that the
// handle has been vended out via "TakeHandleToEntangle*").
bool IsEntangled() const;
// Returns true if this is a default initialized descriptor.
bool IsDefault() const;
// Resets the descriptor, closing the pipe if this is a valid descriptor.
// After calling this "IsDefault" will return true. It is not valid to call
// this on a descriptor whose handle is currently entangled (taken via
// "TakeHandleToEntangle*" but not yet returned) or in the process of being
// serialized.
void Reset();
// These are only meant to be used for serialization, and as such the values
// should always be non-default initialized when they are called. These should
// only be called for descriptors that actually host non-default values. If
// you start serializing an object by calling any of the
// "TakeFooForSerialization" functions it is expected (and enforced by
// DCHECKs) that you will call all of them. Don't use these unless you really
// need to!
void InitializeFromSerializedValues(mojo::ScopedMessagePipeHandle handle,
const base::UnguessableToken& id,
uint64_t sequence_number);
mojo::ScopedMessagePipeHandle TakeHandleForSerialization();
base::UnguessableToken TakeIdForSerialization();
uint64_t TakeSequenceNumberForSerialization();
// The following functions are only intended to be used by classes that
// implemented message port endpoints, like blink::MessagePort (for internal
// use from content and blink), blink::WebMessagePort (for embedder use from
// C++ code) and org.chromium.content.browser.AppWebMessagePort
// (implementation of org.chromium.content_public.browser.MessagePort, which
// is intended for embedder use in Java code).
// Intended for use by MessagePort, for binding/unbinding the handle to/from a
// mojo::Connector. The handle must be bound directly to a mojo::Connector in
// order for messages to be sent or received. MessagePort::Entangle is passed
// a MessagePortDescriptor, and takes the handle from the descriptor in order
// to bind it to the mojo::Connector. Similarly, MessagePort::Disentangle
// takes the handle back from the mojo::Connector, gives it back to the
// MessagePortDescriptor, and releases the MessagePortDescriptor to the
// caller. See MessagePort::Entangle and MessagePort::Disentangle.
mojo::ScopedMessagePipeHandle TakeHandleToEntangle(
ExecutionContext* execution_context);
// Intended for use by WebMessagePort and the corresponding
// org.chromium.content.browser.AppWebMessagePort, which are the interfaces
// that embedders use for communicating with hosted content.
mojo::ScopedMessagePipeHandle TakeHandleToEntangleWithEmbedder();
// Returns a handle that was previously taken for entangling via
// "TakeHandleToEntangle*". Passing an invalid handle indicates that the
// handle was forcibly closed due to error while vended out by the descriptor.
// TODO(chrisha): Close the loop and move the connector inside of the
// descriptor, by making TakeHandleToEntangle vend a bound Connector.
void GiveDisentangledHandle(mojo::ScopedMessagePipeHandle handle);
private:
// For access to NotifyPeer and the following constructor.
friend class MessagePortDescriptorPair;
// Creates a new MessagePortDescriptor that wraps the provided brand new
// handle. A unique id and starting sequence number will be generated. Only
// meant to be called from MessagePortDescriptorPair.
explicit MessagePortDescriptor(mojo::ScopedMessagePipeHandle handle);
// Helper functions for forwarding notifications to the
// InstrumentationDelegate if it exists.
void NotifyAttached(ExecutionContext* execution_context);
void NotifyAttachedToEmbedder();
void NotifyDetached();
void NotifyDestroyed();
// Checks that the serialization state of the object is valid. Only
// meaningful in DCHECK builds.
void EnsureNotSerialized() const;
void EnsureValidSerializationState() const;
static constexpr size_t kInvalidSequenceNumber = 0;
static constexpr size_t kFirstValidSequenceNumber = 1;
// The handle to the underlying pipe.
mojo::ScopedMessagePipeHandle handle_;
#if DCHECK_IS_ON()
// Keeps track of serialization status. An object will explode if
// serialization is only ever half-completed.
struct SerializationState {
bool took_handle_for_serialization_ : 1;
bool took_id_for_serialization_ : 1;
bool took_sequence_number_for_serialization_ : 1;
} serialization_state_ = {};
#endif
// The randomly generated ID of this message handle. The ID follows this
// MessagePortDescriptor around as it is passed between execution contexts,
// and allows for bookkeeping across the various contexts. When an execution
// context entangles itself with a handle, it will report back to the browser
// the ID of the handle and the execution context as well. This allows for the
// browser to know who is talking to who.
base::UnguessableToken id_;
// The sequence number of the instrumentation message related to this handle.
// Since these messages can arrive out of order in the browser process, this
// is used to reorder them so that consistent state can be maintained. This
// will never be zero for a valid port descriptor.
uint64_t sequence_number_ = kInvalidSequenceNumber;
};
// Defines a wrapped mojo::MessagePipe, containing 2 MessagePortDescriptors that
// are peers of each other.
class BLINK_COMMON_EXPORT MessagePortDescriptorPair {
public:
MessagePortDescriptorPair();
~MessagePortDescriptorPair();
const MessagePortDescriptor& port0() const { return port0_; }
const MessagePortDescriptor& port1() const { return port1_; }
MessagePortDescriptor TakePort0() { return std::move(port0_); }
MessagePortDescriptor TakePort1() { return std::move(port1_); }
private:
MessagePortDescriptor port0_;
MessagePortDescriptor port1_;
};
// A delegate used for instrumenting operations on message handle descriptors.
// These messages allow the observing entity to follow the handle endpoints as
// they travel from one execution context to another, and to know when they are
// bound. If no instrumentation delegate is provided the instrumentation is
// disabled. The implementation needs to be thread-safe.
//
// Note that the sequence numbers associated with each port will never be
// reused, and increment by exactly one for each message received. This allows
// the receiver to order incoming messages and know if a message is missing.
// NotifyMessagePortsCreated is special in that it introduces new ports and
// starts new sequence numbers. The next message received (either a PortAttached
// or PortDestroyed message) will use the subsequent sequence number.
class BLINK_COMMON_EXPORT MessagePortDescriptor::InstrumentationDelegate {
public:
virtual ~InstrumentationDelegate() = default;
// Notifies the instrumentation that a pair of matching ports was created.
virtual void NotifyMessagePortPairCreated(
const base::UnguessableToken& port0_id,
const base::UnguessableToken& port1_id) = 0;
// Notifies the instrumentation that a handle has been attached to an
// execution context. Note that |execution_context| should never be null, but
// it is valid for "execution_context->IsContextDestroyed()" to return true.
// Further note that this should only ever be called by blink::MessagePort.
// All other contexts should be calling NotifyMessagePortAttachedToEmbedder.
virtual void NotifyMessagePortAttached(
const base::UnguessableToken& port_id,
uint64_t sequence_number,
ExecutionContext* execution_context) = 0;
// Notifies the instrumentation that a handle has been attached to an
// embedder.
virtual void NotifyMessagePortAttachedToEmbedder(
const base::UnguessableToken& port_id,
uint64_t sequence_number) = 0;
// Notifies the instrumentation that a handle has been detached from an
// execution context.
virtual void NotifyMessagePortDetached(const base::UnguessableToken& port_id,
uint64_t sequence_number) = 0;
// Notifies the instrumentation that a handle has been destroyed.
virtual void NotifyMessagePortDestroyed(const base::UnguessableToken& port_id,
uint64_t sequence_number) = 0;
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_PUBLIC_COMMON_MESSAGING_MESSAGE_PORT_DESCRIPTOR_H_