Line data Source code
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/. */
6 :
7 : #ifndef mozilla_dom_ipc_StructuredCloneData_h
8 : #define mozilla_dom_ipc_StructuredCloneData_h
9 :
10 : #include <algorithm>
11 : #include "mozilla/RefPtr.h"
12 : #include "mozilla/dom/StructuredCloneHolder.h"
13 : #include "nsISupportsImpl.h"
14 : #include "nsIInputStream.h"
15 :
16 : namespace IPC {
17 : class Message;
18 : }
19 : class PickleIterator;
20 :
21 : namespace mozilla {
22 : namespace ipc {
23 :
24 : class AutoIPCStream;
25 : class PBackgroundChild;
26 : class PBackgroundParent;
27 :
28 : } // namespace ipc
29 :
30 : namespace dom {
31 :
32 : class nsIContentChild;
33 : class nsIContentParent;
34 :
35 : namespace ipc {
36 :
37 : /**
38 : * Wraps the non-reference-counted JSStructuredCloneData class to have a
39 : * reference count so that multiple StructuredCloneData instances can reference
40 : * a single underlying serialized representation.
41 : *
42 : * As used by StructuredCloneData, it is an invariant that our
43 : * JSStructuredCloneData owns its buffers. (For the non-owning case,
44 : * StructuredCloneData uses mExternalData which holds a BufferList::Borrow()ed
45 : * read-only view of the data.)
46 : */
47 : class SharedJSAllocatedData final
48 : {
49 : public:
50 46 : explicit SharedJSAllocatedData(JSStructuredCloneData&& aData)
51 46 : : mData(Move(aData))
52 46 : { }
53 :
54 : static already_AddRefed<SharedJSAllocatedData>
55 0 : CreateFromExternalData(const char* aData, size_t aDataLength)
56 : {
57 0 : JSStructuredCloneData buf;
58 0 : buf.WriteBytes(aData, aDataLength);
59 : RefPtr<SharedJSAllocatedData> sharedData =
60 0 : new SharedJSAllocatedData(Move(buf));
61 0 : return sharedData.forget();
62 : }
63 :
64 : static already_AddRefed<SharedJSAllocatedData>
65 0 : CreateFromExternalData(const JSStructuredCloneData& aData)
66 : {
67 0 : JSStructuredCloneData buf;
68 0 : auto iter = aData.Iter();
69 0 : while (!iter.Done()) {
70 0 : buf.WriteBytes(iter.Data(), iter.RemainingInSegment());
71 0 : iter.Advance(aData, iter.RemainingInSegment());
72 : }
73 : RefPtr<SharedJSAllocatedData> sharedData =
74 0 : new SharedJSAllocatedData(Move(buf));
75 0 : return sharedData.forget();
76 : }
77 :
78 150 : NS_INLINE_DECL_REFCOUNTING(SharedJSAllocatedData)
79 :
80 125 : JSStructuredCloneData& Data() { return mData; }
81 46 : size_t DataLength() const { return mData.Size(); }
82 :
83 : private:
84 46 : ~SharedJSAllocatedData() { }
85 :
86 : JSStructuredCloneData mData;
87 : };
88 :
89 : /**
90 : * IPC-aware StructuredCloneHolder subclass that serves as both a helper class
91 : * for dealing with message data (blobs, transferables) and also an IPDL
92 : * data-type in cases where message data is not needed. If your use-case does
93 : * not (potentially) involve IPC, then you should use StructuredCloneHolder or
94 : * one of its other subclasses instead.
95 : *
96 : * ## Usage ##
97 : *
98 : * The general recipe for using this class is:
99 : * - In your IPDL definition, use the ClonedMessageData type whenever you want
100 : * to send a structured clone that may include blobs or transferables such as
101 : * message ports.
102 : * - To send the data, instantiate a StructuredCloneData instance and Write()
103 : * into it like a normal structure clone. When you are ready to send the
104 : * ClonedMessageData-bearing IPC message, use the appropriate
105 : * BuildClonedMessageDataFor{Parent,Child,BackgroundParent,BackgroundChild}
106 : * method to populate the ClonedMessageData and then send it before your
107 : * StructuredCloneData instance is destroyed. (Buffer borrowing is used
108 : * under-the-hood to avoid duplicating the serialized data, requiring this.)
109 : * - To receive the data, instantiate a StructuredCloneData and use the
110 : * appropriate {Borrow,Copy,Steal}FromClonedMessageDataFor{Parent,Child,
111 : * BackgroundParent,BackgroundChild} method. See the memory management
112 : * section for more info.
113 : *
114 : * Variations:
115 : * - If transferables are not allowed (ex: BroadcastChannel), then use the
116 : * StructuredCloneDataNoTransfers subclass instead of StructuredCloneData.
117 : *
118 : * ## Memory Management ##
119 : *
120 : * Serialized structured clone representations can be quite large. So it's best
121 : * to avoid wasteful duplication. When Write()ing into the StructuredCloneData,
122 : * you don't need to worry about this[1], but when you already have serialized
123 : * structured clone data you plan to Read(), you do need to. Similarly, if
124 : * you're using StructuredCloneData as an IPDL type, it efficiently unmarshals.
125 : *
126 : * The from-ClonedMessageData memory management strategies available are:
127 : * - Borrow: Create a JSStructuredCloneData that holds a non-owning, read-only
128 : * BufferList::Borrow()ed copy of the source. Your StructuredCloneData needs
129 : * to be destroyed before the source is. Commonly used when the
130 : * StructuredCloneData instance is stack-allocated (and Read() is used before
131 : * the function returns).
132 : * - Copy: Makes a reference-counted copy of the source JSStructuredCloneData,
133 : * making it safe for the StructuredCloneData to outlive the source data.
134 : * - Steal: Steal the buffers from the underlying JSStructuredCloneData so that
135 : * it's safe for the StructuredCloneData to outlive the source data. This is
136 : * safe to use with IPC-provided ClonedMessageData instances because
137 : * JSStructuredCloneData's IPC ParamTraits::Read method uses ExtractBuffers,
138 : * returning a fatal false if unable to extract. (And
139 : * SerializedStructuredCloneBuffer wraps/defers to it.) But if it's possible
140 : * the ClonedMessageData came from a different source that might have borrowed
141 : * the buffers itself, then things will crash. That would be a pretty crazy
142 : * implementation; if you see one, change it to use SharedJSAllocatedData.
143 : *
144 : * 1: Specifically, in the Write() case an owning SharedJSAllocatedData is
145 : * created efficiently (by stealing from StructuredCloneHolder). The
146 : * BuildClonedMessageDataFor* method can be called at any time and it will
147 : * borrow the underlying memory. While it would be even better if
148 : * SerializedStructuredCloneBuffer could hold a SharedJSAllocatedData ref,
149 : * there's no reason you can't wait to BuildClonedMessageDataFor* until you
150 : * need to make the IPC Send* call.
151 : */
152 : class StructuredCloneData : public StructuredCloneHolder
153 : {
154 : public:
155 : StructuredCloneData();
156 :
157 : StructuredCloneData(const StructuredCloneData&) = delete;
158 :
159 : StructuredCloneData(StructuredCloneData&& aOther);
160 :
161 : ~StructuredCloneData();
162 :
163 : StructuredCloneData&
164 : operator=(const StructuredCloneData& aOther) = delete;
165 :
166 : StructuredCloneData&
167 : operator=(StructuredCloneData&& aOther);
168 :
169 6 : const nsTArray<RefPtr<BlobImpl>>& BlobImpls() const
170 : {
171 6 : return mBlobImplArray;
172 : }
173 :
174 56 : nsTArray<RefPtr<BlobImpl>>& BlobImpls()
175 : {
176 56 : return mBlobImplArray;
177 : }
178 :
179 6 : const nsTArray<nsCOMPtr<nsIInputStream>>& InputStreams() const
180 : {
181 6 : return mInputStreamArray;
182 : }
183 :
184 56 : nsTArray<nsCOMPtr<nsIInputStream>>& InputStreams()
185 : {
186 56 : return mInputStreamArray;
187 : }
188 :
189 : bool Copy(const StructuredCloneData& aData);
190 :
191 : void Read(JSContext* aCx,
192 : JS::MutableHandle<JS::Value> aValue,
193 : ErrorResult &aRv);
194 :
195 : void Write(JSContext* aCx,
196 : JS::Handle<JS::Value> aValue,
197 : ErrorResult &aRv);
198 :
199 : void Write(JSContext* aCx,
200 : JS::Handle<JS::Value> aValue,
201 : JS::Handle<JS::Value> aTransfers,
202 : ErrorResult &aRv);
203 :
204 : // Actor-varying methods to convert the structured clone stored in this holder
205 : // by a previous call to Write() into ClonedMessageData IPC representation.
206 : // (Blobs are represented in IPC by IPCBlob actors, so we need the parent to
207 : // be able to create them.)
208 : bool BuildClonedMessageDataForParent(nsIContentParent* aParent,
209 : ClonedMessageData& aClonedData);
210 : bool BuildClonedMessageDataForChild(nsIContentChild* aChild,
211 : ClonedMessageData& aClonedData);
212 : bool BuildClonedMessageDataForBackgroundParent(mozilla::ipc::PBackgroundParent* aParent,
213 : ClonedMessageData& aClonedData);
214 : bool BuildClonedMessageDataForBackgroundChild(mozilla::ipc::PBackgroundChild* aChild,
215 : ClonedMessageData& aClonedData);
216 :
217 : // Actor-varying and memory-management-strategy-varying methods to initialize
218 : // this holder from a ClonedMessageData representation.
219 : void BorrowFromClonedMessageDataForParent(const ClonedMessageData& aClonedData);
220 : void BorrowFromClonedMessageDataForChild(const ClonedMessageData& aClonedData);
221 : void BorrowFromClonedMessageDataForBackgroundParent(const ClonedMessageData& aClonedData);
222 : void BorrowFromClonedMessageDataForBackgroundChild(const ClonedMessageData& aClonedData);
223 :
224 : void CopyFromClonedMessageDataForParent(const ClonedMessageData& aClonedData);
225 : void CopyFromClonedMessageDataForChild(const ClonedMessageData& aClonedData);
226 : void CopyFromClonedMessageDataForBackgroundParent(const ClonedMessageData& aClonedData);
227 : void CopyFromClonedMessageDataForBackgroundChild(const ClonedMessageData& aClonedData);
228 :
229 : // The steal variants of course take a non-const ClonedMessageData.
230 : void StealFromClonedMessageDataForParent(ClonedMessageData& aClonedData);
231 : void StealFromClonedMessageDataForChild(ClonedMessageData& aClonedData);
232 : void StealFromClonedMessageDataForBackgroundParent(ClonedMessageData& aClonedData);
233 : void StealFromClonedMessageDataForBackgroundChild(ClonedMessageData& aClonedData);
234 :
235 :
236 : // Initialize this instance, borrowing the contents of the given
237 : // JSStructuredCloneData. You are responsible for ensuring that this
238 : // StructuredCloneData instance is destroyed before aData is destroyed.
239 42 : bool UseExternalData(const JSStructuredCloneData& aData)
240 : {
241 42 : auto iter = aData.Iter();
242 42 : bool success = false;
243 : mExternalData =
244 42 : aData.Borrow<js::SystemAllocPolicy>(iter, aData.Size(), &success);
245 42 : mInitialized = true;
246 42 : return success;
247 : }
248 :
249 : // Initialize this instance by copying the given data that probably came from
250 : // nsStructuredClone doing a base64 decode. Don't use this.
251 : bool CopyExternalData(const char* aData, size_t aDataLength);
252 : // Initialize this instance by copying the contents of an existing
253 : // JSStructuredCloneData. Use when this StructuredCloneData instance may
254 : // outlive aData.
255 : bool CopyExternalData(const JSStructuredCloneData& aData);
256 :
257 : // Initialize this instance by stealing the contents of aData via Move
258 : // constructor, clearing the original aData as a side-effect. This is only
259 : // safe if aData owns the underlying buffers. This is the case for instances
260 : // provided by IPC to Recv calls.
261 : bool StealExternalData(JSStructuredCloneData& aData);
262 :
263 175 : JSStructuredCloneData& Data()
264 : {
265 175 : return mSharedData ? mSharedData->Data() : mExternalData;
266 : }
267 :
268 2 : const JSStructuredCloneData& Data() const
269 : {
270 2 : return mSharedData ? mSharedData->Data() : mExternalData;
271 : }
272 :
273 94 : size_t DataLength() const
274 : {
275 94 : return mSharedData ? mSharedData->DataLength() : mExternalData.Size();
276 : }
277 :
278 12 : SharedJSAllocatedData* SharedData() const
279 : {
280 12 : return mSharedData;
281 : }
282 :
283 86 : bool SupportsTransferring()
284 : {
285 86 : return mSupportsTransferring;
286 : }
287 :
288 0 : FallibleTArray<mozilla::ipc::AutoIPCStream>& IPCStreams()
289 : {
290 0 : return mIPCStreams;
291 : }
292 :
293 : // For IPC serialization
294 : void WriteIPCParams(IPC::Message* aMessage) const;
295 : bool ReadIPCParams(const IPC::Message* aMessage, PickleIterator* aIter);
296 :
297 : protected:
298 : explicit StructuredCloneData(TransferringSupport aSupportsTransferring);
299 :
300 : private:
301 : JSStructuredCloneData mExternalData;
302 : RefPtr<SharedJSAllocatedData> mSharedData;
303 :
304 : // This array is needed because AutoIPCStream DTOR must be executed after the
305 : // sending of the data via IPC. This will be fixed by bug 1353475.
306 : FallibleTArray<mozilla::ipc::AutoIPCStream> mIPCStreams;
307 : bool mInitialized;
308 : };
309 :
310 : /**
311 : * For use when transferring should not be supported.
312 : */
313 0 : class StructuredCloneDataNoTransfers : public StructuredCloneData
314 : {
315 : public:
316 0 : StructuredCloneDataNoTransfers()
317 0 : : StructuredCloneData(StructuredCloneHolder::TransferringNotSupported)
318 0 : {}
319 : };
320 :
321 : } // namespace ipc
322 : } // namespace dom
323 : } // namespace mozilla
324 :
325 : #endif // mozilla_dom_ipc_StructuredCloneData_h
|