1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef mozilla_dom_ipc_StructuredCloneData_h
8 #define mozilla_dom_ipc_StructuredCloneData_h
11 #include "mozilla/RefPtr.h"
12 #include "mozilla/dom/StructuredCloneHolder.h"
13 #include "nsISupportsImpl.h"
14 #include "nsIInputStream.h"
26 class PBackgroundChild
;
27 class PBackgroundParent
;
39 * Wraps the non-reference-counted JSStructuredCloneData class to have a
40 * reference count so that multiple StructuredCloneData instances can reference
41 * a single underlying serialized representation.
43 * As used by StructuredCloneData, it is an invariant that our
44 * JSStructuredCloneData owns its buffers. (For the non-owning case,
45 * StructuredCloneData uses mExternalData which holds a BufferList::Borrow()ed
46 * read-only view of the data.)
48 class SharedJSAllocatedData final
{
50 explicit SharedJSAllocatedData(JSStructuredCloneData
&& aData
)
51 : mData(std::move(aData
)) {}
53 static already_AddRefed
<SharedJSAllocatedData
> CreateFromExternalData(
54 const char* aData
, size_t aDataLength
) {
55 JSStructuredCloneData
buf(JS::StructuredCloneScope::DifferentProcess
);
56 NS_ENSURE_TRUE(buf
.AppendBytes(aData
, aDataLength
), nullptr);
57 RefPtr
<SharedJSAllocatedData
> sharedData
=
58 new SharedJSAllocatedData(std::move(buf
));
59 return sharedData
.forget();
62 static already_AddRefed
<SharedJSAllocatedData
> CreateFromExternalData(
63 const JSStructuredCloneData
& aData
) {
64 JSStructuredCloneData
buf(aData
.scope());
65 NS_ENSURE_TRUE(buf
.Append(aData
), nullptr);
66 RefPtr
<SharedJSAllocatedData
> sharedData
=
67 new SharedJSAllocatedData(std::move(buf
));
68 return sharedData
.forget();
71 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SharedJSAllocatedData
)
73 JSStructuredCloneData
& Data() { return mData
; }
74 size_t DataLength() const { return mData
.Size(); }
77 ~SharedJSAllocatedData() = default;
79 JSStructuredCloneData mData
;
83 * IPC-aware StructuredCloneHolder subclass that serves as both a helper class
84 * for dealing with message data (blobs, transferables) and also an IPDL
85 * data-type in cases where message data is not needed. If your use-case does
86 * not (potentially) involve IPC, then you should use StructuredCloneHolder or
87 * one of its other subclasses instead.
91 * The general recipe for using this class is:
92 * - In your IPDL definition, use the ClonedMessageData type whenever you want
93 * to send a structured clone that may include blobs or transferables such as
95 * - To send the data, instantiate a StructuredCloneData instance and Write()
96 * into it like a normal structured clone. When you are ready to send the
97 * ClonedMessageData-bearing IPC message, call the BuildClonedMessageData
98 * method to populate the ClonedMessageData and then send it before your
99 * StructuredCloneData instance is destroyed. (Buffer borrowing is used
100 * under-the-hood to avoid duplicating the serialized data, requiring this.)
101 * - To receive the data, instantiate a StructuredCloneData and then call
102 * the BorrowFromClonedMessageData method. See the memory management
103 * section for more information.
105 * ## Memory Management ##
107 * Serialized structured clone representations can be quite large. So it's best
108 * to avoid wasteful duplication. When Write()ing into the StructuredCloneData,
109 * you don't need to worry about this[1], but when you already have serialized
110 * structured clone data you plan to Read(), you do need to. Similarly, if
111 * you're using StructuredCloneData as an IPDL type, it efficiently unmarshals.
113 * The from-ClonedMessageData memory management strategies available are:
114 * - Borrow: Create a JSStructuredCloneData that holds a non-owning, read-only
115 * BufferList::Borrow()ed copy of the source. Your StructuredCloneData needs
116 * to be destroyed before the source is. Commonly used when the
117 * StructuredCloneData instance is stack-allocated (and Read() is used before
118 * the function returns).
119 * - Copy: Makes a reference-counted copy of the source JSStructuredCloneData,
120 * making it safe for the StructuredCloneData to outlive the source data.
121 * - Steal: Steal the buffers from the underlying JSStructuredCloneData so that
122 * it's safe for the StructuredCloneData to outlive the source data. This is
123 * safe to use with IPC-provided ClonedMessageData instances because
124 * JSStructuredCloneData's IPC ParamTraits::Read method copies the relevant
125 * data into owned buffers. But if it's possible the ClonedMessageData came
126 * from a different source that might have borrowed the buffers itself, then
127 * things will crash. That would be a pretty strange implementation; if you
128 * see one, change it to use SharedJSAllocatedData.
130 * 1: Specifically, in the Write() case an owning SharedJSAllocatedData is
131 * created efficiently (by stealing from StructuredCloneHolder). The
132 * BuildClonedMessageData method can be called at any time and it will
133 * borrow the underlying memory. While it would be even better if
134 * SerializedStructuredCloneBuffer could hold a SharedJSAllocatedData ref,
135 * there's no reason you can't wait to call the BuildClonedMessageData
136 * method until you need to make the IPC Send* call.
138 class StructuredCloneData
: public StructuredCloneHolder
{
140 StructuredCloneData();
142 StructuredCloneData(const StructuredCloneData
&) = delete;
144 StructuredCloneData(StructuredCloneData
&& aOther
);
146 // Only DifferentProcess and UnknownDestination scopes are supported.
147 StructuredCloneData(StructuredCloneScope aScope
,
148 TransferringSupport aSupportsTransferring
);
150 ~StructuredCloneData();
152 StructuredCloneData
& operator=(const StructuredCloneData
& aOther
) = delete;
154 StructuredCloneData
& operator=(StructuredCloneData
&& aOther
);
156 const nsTArray
<RefPtr
<BlobImpl
>>& BlobImpls() const { return mBlobImplArray
; }
158 nsTArray
<RefPtr
<BlobImpl
>>& BlobImpls() { return mBlobImplArray
; }
160 const nsTArray
<nsCOMPtr
<nsIInputStream
>>& InputStreams() const {
161 return mInputStreamArray
;
164 nsTArray
<nsCOMPtr
<nsIInputStream
>>& InputStreams() {
165 return mInputStreamArray
;
168 bool Copy(const StructuredCloneData
& aData
);
170 void Read(JSContext
* aCx
, JS::MutableHandle
<JS::Value
> aValue
,
173 void Read(JSContext
* aCx
, JS::MutableHandle
<JS::Value
> aValue
,
174 const JS::CloneDataPolicy
& aCloneDataPolicy
, ErrorResult
& aRv
);
176 // Write with no transfer objects and with the default CloneDataPolicy. With
177 // a default CloneDataPolicy, read and write will not be considered as part of
178 // the same agent cluster and shared memory objects will not be supported.
179 void Write(JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
,
180 ErrorResult
& aRv
) override
;
182 // The most generic Write method, with tansfers and CloneDataPolicy.
183 void Write(JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
,
184 JS::Handle
<JS::Value
> aTransfers
,
185 const JS::CloneDataPolicy
& aCloneDataPolicy
,
186 ErrorResult
& aRv
) override
;
188 // Method to convert the structured clone stored in this holder by a previous
189 // call to Write() into ClonedMessageData IPC representation.
190 bool BuildClonedMessageData(ClonedMessageData
& aClonedData
);
192 // Memory-management-strategy-varying methods to initialize this holder from a
193 // ClonedMessageData representation.
194 void BorrowFromClonedMessageData(const ClonedMessageData
& aClonedData
);
196 void CopyFromClonedMessageData(const ClonedMessageData
& aClonedData
);
198 // The steal variant of course takes a non-const ClonedMessageData.
199 void StealFromClonedMessageData(ClonedMessageData
& aClonedData
);
201 // Initialize this instance, borrowing the contents of the given
202 // JSStructuredCloneData. You are responsible for ensuring that this
203 // StructuredCloneData instance is destroyed before aData is destroyed.
204 bool UseExternalData(const JSStructuredCloneData
& aData
) {
205 auto iter
= aData
.Start();
206 bool success
= false;
207 mExternalData
= aData
.Borrow(iter
, aData
.Size(), &success
);
212 // Initialize this instance by copying the given data that probably came from
213 // nsStructuredClone doing a base64 decode. Don't use this.
214 bool CopyExternalData(const char* aData
, size_t aDataLength
);
215 // Initialize this instance by copying the contents of an existing
216 // JSStructuredCloneData. Use when this StructuredCloneData instance may
218 bool CopyExternalData(const JSStructuredCloneData
& aData
);
220 // Initialize this instance by stealing the contents of aData via Move
221 // constructor, clearing the original aData as a side-effect. This is only
222 // safe if aData owns the underlying buffers. This is the case for instances
223 // provided by IPC to Recv calls.
224 bool StealExternalData(JSStructuredCloneData
& aData
);
226 JSStructuredCloneData
& Data() {
227 return mSharedData
? mSharedData
->Data() : mExternalData
;
230 const JSStructuredCloneData
& Data() const {
231 return mSharedData
? mSharedData
->Data() : mExternalData
;
234 void InitScope(JS::StructuredCloneScope aScope
) { Data().initScope(aScope
); }
236 size_t DataLength() const {
237 return mSharedData
? mSharedData
->DataLength() : mExternalData
.Size();
240 SharedJSAllocatedData
* SharedData() const { return mSharedData
; }
242 bool SupportsTransferring() { return mSupportsTransferring
; }
244 // For IPC serialization
245 void WriteIPCParams(IPC::MessageWriter
* aWriter
) const;
246 bool ReadIPCParams(IPC::MessageReader
* aReader
);
249 already_AddRefed
<SharedJSAllocatedData
> TakeSharedData();
252 JSStructuredCloneData mExternalData
;
253 RefPtr
<SharedJSAllocatedData
> mSharedData
;
260 } // namespace mozilla
262 #endif // mozilla_dom_ipc_StructuredCloneData_h