blob: f6f802d750b88217841b6232f7f293947552f8c7 [file] [log] [blame]
// Copyright 2022 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_CORE_IPCZ_DRIVER_MOJO_MESSAGE_H_
#define MOJO_CORE_IPCZ_DRIVER_MOJO_MESSAGE_H_
#include <cstddef>
#include <cstdint>
#include <memory>
#include <vector>
#include "base/containers/span.h"
#include "base/memory/ptr_util.h"
#include "base/memory/raw_span.h"
#include "mojo/core/scoped_ipcz_handle.h"
#include "mojo/public/c/system/message_pipe.h"
#include "mojo/public/c/system/types.h"
#include "third_party/ipcz/include/ipcz/ipcz.h"
namespace mojo::core::ipcz_driver {
// The ipcz-based implementation of Mojo message objects. ipcz API exposes no
// notion of message objects, so this is merely heap storage for data and ipcz
// handles.
class MojoMessage {
public:
// Even with an input size of 0, MojoAppendMessageData is expected to allocate
// *some* storage for message data. This constant therefore sets a lower bound
// on payload allocation size. 32 bytes is chosen since it's the smallest
// possible Mojo bindings message size (v0 header + 8 byte payload)
static constexpr size_t kMinBufferSize = 32;
MojoMessage();
MojoMessage(std::vector<uint8_t> data, std::vector<IpczHandle> handles);
~MojoMessage();
static MojoMessage* FromHandle(MojoMessageHandle handle) {
return reinterpret_cast<MojoMessage*>(handle);
}
static std::unique_ptr<MojoMessage> TakeFromHandle(MojoMessageHandle handle) {
return base::WrapUnique(FromHandle(handle));
}
MojoMessageHandle handle() const {
return reinterpret_cast<MojoMessageHandle>(this);
}
base::span<const uint8_t> data() const { return data_; }
base::span<uint8_t> mutable_data() const { return data_; }
std::vector<IpczHandle>& handles() { return handles_; }
uintptr_t context() const { return context_; }
IpczHandle parcel() const { return parcel_.get(); }
// Sets the received parcel object backing this message.
void SetParcel(ScopedIpczHandle parcel);
// Reserves capacity within a new message object, effectively implementing
// MojoReserveMessageCapacity().
MojoResult ReserveCapacity(uint32_t payload_buffer_size,
uint32_t* buffer_size);
// Appends data to a new or partially serialized message, effectively
// implementing MojoAppendMessageData().
MojoResult AppendData(uint32_t additional_num_bytes,
const MojoHandle* handles,
uint32_t num_handles,
void** buffer,
uint32_t* buffer_size,
bool commit_size);
// Retrieves data from a serialized message, effectively implementing
// MojoGetMessageData();
IpczResult GetData(void** buffer,
uint32_t* num_bytes,
MojoHandle* handles,
uint32_t* num_handles,
bool consume_handles);
// Finalizes the Message by ensuring that any attached DataPipe objects also
// attach their portals alongside the existing attachments. This operation is
// balanced within SetParcel(), where DataPipes extract their portals from
// the tail end of the attached handles.
void AttachDataPipePortals();
// Sets an unserialized message context on this message, with an optional
// serializer and destructor.
MojoResult SetContext(uintptr_t context,
MojoMessageContextSerializer serializer,
MojoMessageContextDestructor destructor);
// Forcibly serializes this message if it holds an unserialized context.
MojoResult Serialize();
// Functions provided to ipcz when boxing MojoMessage objects for lazy
// serialization.
static IpczResult SerializeForIpcz(uintptr_t object,
uint32_t,
const void*,
volatile void* data,
size_t* num_bytes,
IpczHandle* handles,
size_t* num_handles);
static void DestroyForIpcz(uintptr_t object, uint32_t, const void*);
// Boxes a MojoMessage object for transmission within another message. This is
// used to support transmission of unserialized MojoMessages through ipcz,
// with support for lazy serialization if needed.
static ScopedIpczHandle Box(std::unique_ptr<MojoMessage> message);
// Constructs a new MojoMessage from `message`, if `message` contains a single
// box with an application object or subparcel inside of it. In that case the
// application object or subparcel is interpreted as an embedded MojoMessage,
// and that MojoMessage is reconstituted and returned. Otherwise this returns
// null to indicate that `message` is not a wrapper around another
// MojoMessage.
static std::unique_ptr<MojoMessage> UnwrapFrom(MojoMessage& message);
private:
IpczResult SerializeForIpczImpl(volatile void* data,
size_t* num_bytes,
IpczHandle* handles,
size_t* num_handles);
// The parcel backing this message, if any.
ScopedIpczHandle parcel_;
// A heap buffer of message data, used only when `parcel_` is null.
using DataPtr = std::unique_ptr<uint8_t[]>;
DataPtr data_storage_;
size_t data_storage_size_ = 0;
// A view into the message data, whether it's backed by `parcel_` or stored in
// `data_storage_`.
base::raw_span<uint8_t, DanglingUntriaged> data_;
std::vector<IpczHandle> handles_;
bool handles_consumed_ = false;
bool size_committed_ = false;
// Unserialized message state. These values are provided by the application
// calling MojoSetMessageContext() for lazy serialization. `context_` is an
// arbitrary opaque value. `serializer_` is invoked when the application must
// produce a serialized message, with `context_` as an input. `destructor_` is
// if non-null is called to clean up any application state associated with
// `context_`.
//
// If `context_` is zero, then no unserialized message context has been set by
// the application.
uintptr_t context_ = 0;
MojoMessageContextSerializer serializer_ = nullptr;
MojoMessageContextDestructor destructor_ = nullptr;
};
} // namespace mojo::core::ipcz_driver
#endif // MOJO_CORE_IPCZ_DRIVER_MOJO_MESSAGE_H_