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