mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-26 02:02:33 +00:00
03ffca06a1
This is the first use of IPCBlob: ClonedMessageData. ClonedMessageData is used for BroadcastChannel, MessagePort and any postMessage() communication. This patch changes StructuredCloneData in order to use IPCBlob instead of PBlob. BroadcastChannel has a custom way to manage Blobs because when the parent receives them from a content process, it must send them to any other BroadcastChild actor duplicating the serialization.
326 lines
12 KiB
C++
326 lines
12 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#ifndef mozilla_dom_ipc_StructuredCloneData_h
|
|
#define mozilla_dom_ipc_StructuredCloneData_h
|
|
|
|
#include <algorithm>
|
|
#include "mozilla/RefPtr.h"
|
|
#include "mozilla/dom/StructuredCloneHolder.h"
|
|
#include "nsISupportsImpl.h"
|
|
#include "nsIInputStream.h"
|
|
|
|
namespace IPC {
|
|
class Message;
|
|
}
|
|
class PickleIterator;
|
|
|
|
namespace mozilla {
|
|
namespace ipc {
|
|
|
|
class AutoIPCStream;
|
|
class PBackgroundChild;
|
|
class PBackgroundParent;
|
|
|
|
} // namespace ipc
|
|
|
|
namespace dom {
|
|
|
|
class nsIContentChild;
|
|
class nsIContentParent;
|
|
|
|
namespace ipc {
|
|
|
|
/**
|
|
* Wraps the non-reference-counted JSStructuredCloneData class to have a
|
|
* reference count so that multiple StructuredCloneData instances can reference
|
|
* a single underlying serialized representation.
|
|
*
|
|
* As used by StructuredCloneData, it is an invariant that our
|
|
* JSStructuredCloneData owns its buffers. (For the non-owning case,
|
|
* StructuredCloneData uses mExternalData which holds a BufferList::Borrow()ed
|
|
* read-only view of the data.)
|
|
*/
|
|
class SharedJSAllocatedData final
|
|
{
|
|
public:
|
|
explicit SharedJSAllocatedData(JSStructuredCloneData&& aData)
|
|
: mData(Move(aData))
|
|
{ }
|
|
|
|
static already_AddRefed<SharedJSAllocatedData>
|
|
CreateFromExternalData(const char* aData, size_t aDataLength)
|
|
{
|
|
JSStructuredCloneData buf;
|
|
buf.WriteBytes(aData, aDataLength);
|
|
RefPtr<SharedJSAllocatedData> sharedData =
|
|
new SharedJSAllocatedData(Move(buf));
|
|
return sharedData.forget();
|
|
}
|
|
|
|
static already_AddRefed<SharedJSAllocatedData>
|
|
CreateFromExternalData(const JSStructuredCloneData& aData)
|
|
{
|
|
JSStructuredCloneData buf;
|
|
auto iter = aData.Iter();
|
|
while (!iter.Done()) {
|
|
buf.WriteBytes(iter.Data(), iter.RemainingInSegment());
|
|
iter.Advance(aData, iter.RemainingInSegment());
|
|
}
|
|
RefPtr<SharedJSAllocatedData> sharedData =
|
|
new SharedJSAllocatedData(Move(buf));
|
|
return sharedData.forget();
|
|
}
|
|
|
|
NS_INLINE_DECL_REFCOUNTING(SharedJSAllocatedData)
|
|
|
|
JSStructuredCloneData& Data() { return mData; }
|
|
size_t DataLength() const { return mData.Size(); }
|
|
|
|
private:
|
|
~SharedJSAllocatedData() { }
|
|
|
|
JSStructuredCloneData mData;
|
|
};
|
|
|
|
/**
|
|
* IPC-aware StructuredCloneHolder subclass that serves as both a helper class
|
|
* for dealing with message data (blobs, transferables) and also an IPDL
|
|
* data-type in cases where message data is not needed. If your use-case does
|
|
* not (potentially) involve IPC, then you should use StructuredCloneHolder or
|
|
* one of its other subclasses instead.
|
|
*
|
|
* ## Usage ##
|
|
*
|
|
* The general recipe for using this class is:
|
|
* - In your IPDL definition, use the ClonedMessageData type whenever you want
|
|
* to send a structured clone that may include blobs or transferables such as
|
|
* message ports.
|
|
* - To send the data, instantiate a StructuredCloneData instance and Write()
|
|
* into it like a normal structure clone. When you are ready to send the
|
|
* ClonedMessageData-bearing IPC message, use the appropriate
|
|
* BuildClonedMessageDataFor{Parent,Child,BackgroundParent,BackgroundChild}
|
|
* method to populate the ClonedMessageData and then send it before your
|
|
* StructuredCloneData instance is destroyed. (Buffer borrowing is used
|
|
* under-the-hood to avoid duplicating the serialized data, requiring this.)
|
|
* - To receive the data, instantiate a StructuredCloneData and use the
|
|
* appropriate {Borrow,Copy,Steal}FromClonedMessageDataFor{Parent,Child,
|
|
* BackgroundParent,BackgroundChild} method. See the memory management
|
|
* section for more info.
|
|
*
|
|
* Variations:
|
|
* - If transferables are not allowed (ex: BroadcastChannel), then use the
|
|
* StructuredCloneDataNoTransfers subclass instead of StructuredCloneData.
|
|
*
|
|
* ## Memory Management ##
|
|
*
|
|
* Serialized structured clone representations can be quite large. So it's best
|
|
* to avoid wasteful duplication. When Write()ing into the StructuredCloneData,
|
|
* you don't need to worry about this[1], but when you already have serialized
|
|
* structured clone data you plan to Read(), you do need to. Similarly, if
|
|
* you're using StructuredCloneData as an IPDL type, it efficiently unmarshals.
|
|
*
|
|
* The from-ClonedMessageData memory management strategies available are:
|
|
* - Borrow: Create a JSStructuredCloneData that holds a non-owning, read-only
|
|
* BufferList::Borrow()ed copy of the source. Your StructuredCloneData needs
|
|
* to be destroyed before the source is. Commonly used when the
|
|
* StructuredCloneData instance is stack-allocated (and Read() is used before
|
|
* the function returns).
|
|
* - Copy: Makes a reference-counted copy of the source JSStructuredCloneData,
|
|
* making it safe for the StructuredCloneData to outlive the source data.
|
|
* - Steal: Steal the buffers from the underlying JSStructuredCloneData so that
|
|
* it's safe for the StructuredCloneData to outlive the source data. This is
|
|
* safe to use with IPC-provided ClonedMessageData instances because
|
|
* JSStructuredCloneData's IPC ParamTraits::Read method uses ExtractBuffers,
|
|
* returning a fatal false if unable to extract. (And
|
|
* SerializedStructuredCloneBuffer wraps/defers to it.) But if it's possible
|
|
* the ClonedMessageData came from a different source that might have borrowed
|
|
* the buffers itself, then things will crash. That would be a pretty crazy
|
|
* implementation; if you see one, change it to use SharedJSAllocatedData.
|
|
*
|
|
* 1: Specifically, in the Write() case an owning SharedJSAllocatedData is
|
|
* created efficiently (by stealing from StructuredCloneHolder). The
|
|
* BuildClonedMessageDataFor* method can be called at any time and it will
|
|
* borrow the underlying memory. While it would be even better if
|
|
* SerializedStructuredCloneBuffer could hold a SharedJSAllocatedData ref,
|
|
* there's no reason you can't wait to BuildClonedMessageDataFor* until you
|
|
* need to make the IPC Send* call.
|
|
*/
|
|
class StructuredCloneData : public StructuredCloneHolder
|
|
{
|
|
public:
|
|
StructuredCloneData();
|
|
|
|
StructuredCloneData(const StructuredCloneData&) = delete;
|
|
|
|
StructuredCloneData(StructuredCloneData&& aOther);
|
|
|
|
~StructuredCloneData();
|
|
|
|
StructuredCloneData&
|
|
operator=(const StructuredCloneData& aOther) = delete;
|
|
|
|
StructuredCloneData&
|
|
operator=(StructuredCloneData&& aOther);
|
|
|
|
const nsTArray<RefPtr<BlobImpl>>& BlobImpls() const
|
|
{
|
|
return mBlobImplArray;
|
|
}
|
|
|
|
nsTArray<RefPtr<BlobImpl>>& BlobImpls()
|
|
{
|
|
return mBlobImplArray;
|
|
}
|
|
|
|
const nsTArray<nsCOMPtr<nsIInputStream>>& InputStreams() const
|
|
{
|
|
return mInputStreamArray;
|
|
}
|
|
|
|
nsTArray<nsCOMPtr<nsIInputStream>>& InputStreams()
|
|
{
|
|
return mInputStreamArray;
|
|
}
|
|
|
|
bool Copy(const StructuredCloneData& aData);
|
|
|
|
void Read(JSContext* aCx,
|
|
JS::MutableHandle<JS::Value> aValue,
|
|
ErrorResult &aRv);
|
|
|
|
void Write(JSContext* aCx,
|
|
JS::Handle<JS::Value> aValue,
|
|
ErrorResult &aRv);
|
|
|
|
void Write(JSContext* aCx,
|
|
JS::Handle<JS::Value> aValue,
|
|
JS::Handle<JS::Value> aTransfers,
|
|
ErrorResult &aRv);
|
|
|
|
// Actor-varying methods to convert the structured clone stored in this holder
|
|
// by a previous call to Write() into ClonedMessageData IPC representation.
|
|
// (Blobs are represented in IPC by IPCBlob actors, so we need the parent to
|
|
// be able to create them.)
|
|
bool BuildClonedMessageDataForParent(nsIContentParent* aParent,
|
|
ClonedMessageData& aClonedData);
|
|
bool BuildClonedMessageDataForChild(nsIContentChild* aChild,
|
|
ClonedMessageData& aClonedData);
|
|
bool BuildClonedMessageDataForBackgroundParent(mozilla::ipc::PBackgroundParent* aParent,
|
|
ClonedMessageData& aClonedData);
|
|
bool BuildClonedMessageDataForBackgroundChild(mozilla::ipc::PBackgroundChild* aChild,
|
|
ClonedMessageData& aClonedData);
|
|
|
|
// Actor-varying and memory-management-strategy-varying methods to initialize
|
|
// this holder from a ClonedMessageData representation.
|
|
void BorrowFromClonedMessageDataForParent(const ClonedMessageData& aClonedData);
|
|
void BorrowFromClonedMessageDataForChild(const ClonedMessageData& aClonedData);
|
|
void BorrowFromClonedMessageDataForBackgroundParent(const ClonedMessageData& aClonedData);
|
|
void BorrowFromClonedMessageDataForBackgroundChild(const ClonedMessageData& aClonedData);
|
|
|
|
void CopyFromClonedMessageDataForParent(const ClonedMessageData& aClonedData);
|
|
void CopyFromClonedMessageDataForChild(const ClonedMessageData& aClonedData);
|
|
void CopyFromClonedMessageDataForBackgroundParent(const ClonedMessageData& aClonedData);
|
|
void CopyFromClonedMessageDataForBackgroundChild(const ClonedMessageData& aClonedData);
|
|
|
|
// The steal variants of course take a non-const ClonedMessageData.
|
|
void StealFromClonedMessageDataForParent(ClonedMessageData& aClonedData);
|
|
void StealFromClonedMessageDataForChild(ClonedMessageData& aClonedData);
|
|
void StealFromClonedMessageDataForBackgroundParent(ClonedMessageData& aClonedData);
|
|
void StealFromClonedMessageDataForBackgroundChild(ClonedMessageData& aClonedData);
|
|
|
|
|
|
// Initialize this instance, borrowing the contents of the given
|
|
// JSStructuredCloneData. You are responsible for ensuring that this
|
|
// StructuredCloneData instance is destroyed before aData is destroyed.
|
|
bool UseExternalData(const JSStructuredCloneData& aData)
|
|
{
|
|
auto iter = aData.Iter();
|
|
bool success = false;
|
|
mExternalData =
|
|
aData.Borrow<js::SystemAllocPolicy>(iter, aData.Size(), &success);
|
|
mInitialized = true;
|
|
return success;
|
|
}
|
|
|
|
// Initialize this instance by copying the given data that probably came from
|
|
// nsStructuredClone doing a base64 decode. Don't use this.
|
|
bool CopyExternalData(const char* aData, size_t aDataLength);
|
|
// Initialize this instance by copying the contents of an existing
|
|
// JSStructuredCloneData. Use when this StructuredCloneData instance may
|
|
// outlive aData.
|
|
bool CopyExternalData(const JSStructuredCloneData& aData);
|
|
|
|
// Initialize this instance by stealing the contents of aData via Move
|
|
// constructor, clearing the original aData as a side-effect. This is only
|
|
// safe if aData owns the underlying buffers. This is the case for instances
|
|
// provided by IPC to Recv calls.
|
|
bool StealExternalData(JSStructuredCloneData& aData);
|
|
|
|
JSStructuredCloneData& Data()
|
|
{
|
|
return mSharedData ? mSharedData->Data() : mExternalData;
|
|
}
|
|
|
|
const JSStructuredCloneData& Data() const
|
|
{
|
|
return mSharedData ? mSharedData->Data() : mExternalData;
|
|
}
|
|
|
|
size_t DataLength() const
|
|
{
|
|
return mSharedData ? mSharedData->DataLength() : mExternalData.Size();
|
|
}
|
|
|
|
SharedJSAllocatedData* SharedData() const
|
|
{
|
|
return mSharedData;
|
|
}
|
|
|
|
bool SupportsTransferring()
|
|
{
|
|
return mSupportsTransferring;
|
|
}
|
|
|
|
FallibleTArray<mozilla::ipc::AutoIPCStream>& IPCStreams()
|
|
{
|
|
return mIPCStreams;
|
|
}
|
|
|
|
// For IPC serialization
|
|
void WriteIPCParams(IPC::Message* aMessage) const;
|
|
bool ReadIPCParams(const IPC::Message* aMessage, PickleIterator* aIter);
|
|
|
|
protected:
|
|
explicit StructuredCloneData(TransferringSupport aSupportsTransferring);
|
|
|
|
private:
|
|
JSStructuredCloneData mExternalData;
|
|
RefPtr<SharedJSAllocatedData> mSharedData;
|
|
|
|
// This array is needed because AutoIPCStream DTOR must be executed after the
|
|
// sending of the data via IPC. This will be fixed by bug 1353475.
|
|
FallibleTArray<mozilla::ipc::AutoIPCStream> mIPCStreams;
|
|
bool mInitialized;
|
|
};
|
|
|
|
/**
|
|
* For use when transferring should not be supported.
|
|
*/
|
|
class StructuredCloneDataNoTransfers : public StructuredCloneData
|
|
{
|
|
public:
|
|
StructuredCloneDataNoTransfers()
|
|
: StructuredCloneData(StructuredCloneHolder::TransferringNotSupported)
|
|
{}
|
|
};
|
|
|
|
} // namespace ipc
|
|
} // namespace dom
|
|
} // namespace mozilla
|
|
|
|
#endif // mozilla_dom_ipc_StructuredCloneData_h
|