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_StructuredCloneHolder_h
8 #define mozilla_dom_StructuredCloneHolder_h
9 
10 #include <utility>
11 
12 #include "js/StructuredClone.h"
13 #include "jsapi.h"
14 #include "mozilla/MemoryReporting.h"
15 #include "mozilla/UniquePtr.h"
16 #include "mozilla/dom/BindingDeclarations.h"
17 #include "nsTArray.h"
18 
19 #ifdef DEBUG
20 #  include "nsIThread.h"
21 #endif
22 
23 class nsIGlobalObject;
24 class nsIInputStream;
25 
26 namespace mozilla {
27 class ErrorResult;
28 namespace layers {
29 class Image;
30 }
31 
32 namespace gfx {
33 class DataSourceSurface;
34 }
35 
36 namespace dom {
37 
38 class StructuredCloneHolderBase {
39  public:
40   typedef JS::StructuredCloneScope StructuredCloneScope;
41 
42   StructuredCloneHolderBase(
43       StructuredCloneScope aScope = StructuredCloneScope::SameProcess);
44   virtual ~StructuredCloneHolderBase();
45 
46   // Note, it is unsafe to std::move() a StructuredCloneHolderBase since a raw
47   // this pointer is passed to mBuffer as a callback closure.  That must
48   // be fixed if you want to implement a move constructor here.
49   StructuredCloneHolderBase(StructuredCloneHolderBase&& aOther) = delete;
50 
51   // These methods should be implemented in order to clone data.
52   // Read more documentation in js/public/StructuredClone.h.
53 
54   virtual JSObject* CustomReadHandler(
55       JSContext* aCx, JSStructuredCloneReader* aReader,
56       const JS::CloneDataPolicy& aCloneDataPolicy, uint32_t aTag,
57       uint32_t aIndex) = 0;
58 
59   virtual bool CustomWriteHandler(JSContext* aCx,
60                                   JSStructuredCloneWriter* aWriter,
61                                   JS::Handle<JSObject*> aObj,
62                                   bool* aSameProcessScopeRequired) = 0;
63 
64   // This method has to be called when this object is not needed anymore.
65   // It will free memory and the buffer. This has to be called because
66   // otherwise the buffer will be freed in the DTOR of this class and at that
67   // point we cannot use the overridden methods.
68   void Clear();
69 
70   // If these 3 methods are not implement, transfering objects will not be
71   // allowed. Otherwise only arrayBuffers will be transferred.
72 
73   virtual bool CustomReadTransferHandler(JSContext* aCx,
74                                          JSStructuredCloneReader* aReader,
75                                          uint32_t aTag, void* aContent,
76                                          uint64_t aExtraData,
77                                          JS::MutableHandleObject aReturnObject);
78 
79   virtual bool CustomWriteTransferHandler(JSContext* aCx,
80                                           JS::Handle<JSObject*> aObj,
81                                           // Output:
82                                           uint32_t* aTag,
83                                           JS::TransferableOwnership* aOwnership,
84                                           void** aContent,
85                                           uint64_t* aExtraData);
86 
87   virtual void CustomFreeTransferHandler(uint32_t aTag,
88                                          JS::TransferableOwnership aOwnership,
89                                          void* aContent, uint64_t aExtraData);
90 
91   virtual bool CustomCanTransferHandler(JSContext* aCx,
92                                         JS::Handle<JSObject*> aObj,
93                                         bool* aSameProcessScopeRequired);
94 
95   // These methods are what you should use to read/write data.
96 
97   // Execute the serialization of aValue using the Structured Clone Algorithm.
98   // The data can read back using Read().
99   bool Write(JSContext* aCx, JS::Handle<JS::Value> aValue);
100 
101   // Like Write() but it supports the transferring of objects and handling
102   // of cloning policy.
103   bool Write(JSContext* aCx, JS::Handle<JS::Value> aValue,
104              JS::Handle<JS::Value> aTransfer,
105              const JS::CloneDataPolicy& aCloneDataPolicy);
106 
107   // If Write() has been called, this method retrieves data and stores it into
108   // aValue.
109   bool Read(JSContext* aCx, JS::MutableHandle<JS::Value> aValue);
110 
111   // Like Read() but it supports handling of clone policy.
112   bool Read(JSContext* aCx, JS::MutableHandle<JS::Value> aValue,
113             const JS::CloneDataPolicy& aCloneDataPolicy);
114 
HasData()115   bool HasData() const { return !!mBuffer; }
116 
BufferData()117   JSStructuredCloneData& BufferData() const {
118     MOZ_ASSERT(mBuffer, "Write() has never been called.");
119     return mBuffer->data();
120   }
121 
SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf)122   size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) {
123     size_t size = 0;
124     if (HasData()) {
125       size += mBuffer->sizeOfIncludingThis(aMallocSizeOf);
126     }
127     return size;
128   }
129 
SetErrorMessage(const char * aErrorMessage)130   void SetErrorMessage(const char* aErrorMessage) {
131     mErrorMessage.Assign(aErrorMessage);
132   }
133 
134  protected:
135   UniquePtr<JSAutoStructuredCloneBuffer> mBuffer;
136 
137   StructuredCloneScope mStructuredCloneScope;
138 
139   // Error message when a data clone error is about to throw. It's held while
140   // the error callback is fired and it will be throw with a data clone error
141   // later.
142   nsCString mErrorMessage;
143 
144 #ifdef DEBUG
145   bool mClearCalled;
146 #endif
147 };
148 
149 class BlobImpl;
150 class MessagePort;
151 class MessagePortIdentifier;
152 
153 class StructuredCloneHolder : public StructuredCloneHolderBase {
154  public:
155   enum CloningSupport { CloningSupported, CloningNotSupported };
156 
157   enum TransferringSupport { TransferringSupported, TransferringNotSupported };
158 
159   // If cloning is supported, this object will clone objects such as Blobs,
160   // FileList, ImageData, etc.
161   // If transferring is supported, we will transfer MessagePorts and in the
162   // future other transferrable objects.
163   // The StructuredCloneScope is useful to know where the cloned/transferred
164   // data can be read and written. Additional checks about the nature of the
165   // objects will be done based on this scope value because not all the
166   // objects can be sent between threads or processes.
167   explicit StructuredCloneHolder(CloningSupport aSupportsCloning,
168                                  TransferringSupport aSupportsTransferring,
169                                  StructuredCloneScope aStructuredCloneScope);
170   virtual ~StructuredCloneHolder();
171 
172   StructuredCloneHolder(StructuredCloneHolder&& aOther) = delete;
173 
174   // Normally you should just use Write() and Read().
175 
176   virtual void Write(JSContext* aCx, JS::Handle<JS::Value> aValue,
177                      ErrorResult& aRv);
178 
179   virtual void Write(JSContext* aCx, JS::Handle<JS::Value> aValue,
180                      JS::Handle<JS::Value> aTransfer,
181                      const JS::CloneDataPolicy& aCloneDataPolicy,
182                      ErrorResult& aRv);
183 
184   void Read(nsIGlobalObject* aGlobal, JSContext* aCx,
185             JS::MutableHandle<JS::Value> aValue, ErrorResult& aRv);
186 
187   void Read(nsIGlobalObject* aGlobal, JSContext* aCx,
188             JS::MutableHandle<JS::Value> aValue,
189             const JS::CloneDataPolicy& aCloneDataPolicy, ErrorResult& aRv);
190 
191   // Call this method to know if this object is keeping some DOM object alive.
HasClonedDOMObjects()192   bool HasClonedDOMObjects() const {
193     return !mBlobImplArray.IsEmpty() || !mWasmModuleArray.IsEmpty() ||
194            !mClonedSurfaces.IsEmpty() || !mInputStreamArray.IsEmpty();
195   }
196 
BlobImpls()197   nsTArray<RefPtr<BlobImpl>>& BlobImpls() {
198     MOZ_ASSERT(mSupportsCloning,
199                "Blobs cannot be taken/set if cloning is not supported.");
200     return mBlobImplArray;
201   }
202 
WasmModules()203   nsTArray<RefPtr<JS::WasmModule>>& WasmModules() {
204     MOZ_ASSERT(mSupportsCloning,
205                "WasmModules cannot be taken/set if cloning is not supported.");
206     return mWasmModuleArray;
207   }
208 
InputStreams()209   nsTArray<nsCOMPtr<nsIInputStream>>& InputStreams() {
210     MOZ_ASSERT(mSupportsCloning,
211                "InputStreams cannot be taken/set if cloning is not supported.");
212     return mInputStreamArray;
213   }
214 
215   // This method returns the final scope. If the final scope is unknown,
216   // DifferentProcess is returned because it's the most restrictive one.
CloneScope()217   StructuredCloneScope CloneScope() const {
218     if (mStructuredCloneScope == StructuredCloneScope::UnknownDestination) {
219       return StructuredCloneScope::DifferentProcess;
220     }
221     return mStructuredCloneScope;
222   }
223 
224   // The global object is set internally just during the Read(). This method
225   // can be used by read functions to retrieve it.
GlobalDuringRead()226   nsIGlobalObject* GlobalDuringRead() const { return mGlobal; }
227 
228   // This must be called if the transferring has ports generated by Read().
229   // MessagePorts are not thread-safe and they must be retrieved in the thread
230   // where they are created.
TakeTransferredPorts()231   nsTArray<RefPtr<MessagePort>>&& TakeTransferredPorts() {
232     MOZ_ASSERT(mSupportsTransferring);
233     return std::move(mTransferredPorts);
234   }
235 
236   // This method uses TakeTransferredPorts() to populate a sequence of
237   // MessagePorts for WebIDL binding classes.
238   bool TakeTransferredPortsAsSequence(
239       Sequence<OwningNonNull<mozilla::dom::MessagePort>>& aPorts);
240 
PortIdentifiers()241   nsTArray<MessagePortIdentifier>& PortIdentifiers() const {
242     MOZ_ASSERT(mSupportsTransferring);
243     return mPortIdentifiers;
244   }
245 
GetSurfaces()246   nsTArray<RefPtr<gfx::DataSourceSurface>>& GetSurfaces() {
247     return mClonedSurfaces;
248   }
249 
250   // Implementations of the virtual methods to allow cloning of objects which
251   // JS engine itself doesn't clone.
252 
253   virtual JSObject* CustomReadHandler(
254       JSContext* aCx, JSStructuredCloneReader* aReader,
255       const JS::CloneDataPolicy& aCloneDataPolicy, uint32_t aTag,
256       uint32_t aIndex) override;
257 
258   virtual bool CustomWriteHandler(JSContext* aCx,
259                                   JSStructuredCloneWriter* aWriter,
260                                   JS::Handle<JSObject*> aObj,
261                                   bool* aSameProcessScopeRequired) override;
262 
263   virtual bool CustomReadTransferHandler(
264       JSContext* aCx, JSStructuredCloneReader* aReader, uint32_t aTag,
265       void* aContent, uint64_t aExtraData,
266       JS::MutableHandleObject aReturnObject) override;
267 
268   virtual bool CustomWriteTransferHandler(JSContext* aCx,
269                                           JS::Handle<JSObject*> aObj,
270                                           uint32_t* aTag,
271                                           JS::TransferableOwnership* aOwnership,
272                                           void** aContent,
273                                           uint64_t* aExtraData) override;
274 
275   virtual void CustomFreeTransferHandler(uint32_t aTag,
276                                          JS::TransferableOwnership aOwnership,
277                                          void* aContent,
278                                          uint64_t aExtraData) override;
279 
280   virtual bool CustomCanTransferHandler(
281       JSContext* aCx, JS::Handle<JSObject*> aObj,
282       bool* aSameProcessScopeRequired) override;
283 
284   // These 2 static methods are useful to read/write fully serializable objects.
285   // They can be used by custom StructuredCloneHolderBase classes to
286   // serialize objects such as ImageData, CryptoKey, RTCCertificate, etc.
287 
288   static JSObject* ReadFullySerializableObjects(
289       JSContext* aCx, JSStructuredCloneReader* aReader, uint32_t aTag);
290 
291   static bool WriteFullySerializableObjects(JSContext* aCx,
292                                             JSStructuredCloneWriter* aWriter,
293                                             JS::Handle<JSObject*> aObj);
294 
295   // Helper functions for reading and writing strings.
296   static bool ReadString(JSStructuredCloneReader* aReader, nsString& aString);
297   static bool WriteString(JSStructuredCloneWriter* aWriter,
298                           const nsString& aString);
299 
300   static const JSStructuredCloneCallbacks sCallbacks;
301 
302  protected:
303   // If you receive a buffer from IPC, you can use this method to retrieve a
304   // JS::Value. It can happen that you want to pre-populate the array of Blobs
305   // and/or the PortIdentifiers.
306   void ReadFromBuffer(nsIGlobalObject* aGlobal, JSContext* aCx,
307                       JSStructuredCloneData& aBuffer,
308                       JS::MutableHandle<JS::Value> aValue,
309                       const JS::CloneDataPolicy& aCloneDataPolicy,
310                       ErrorResult& aRv);
311 
312   void ReadFromBuffer(nsIGlobalObject* aGlobal, JSContext* aCx,
313                       JSStructuredCloneData& aBuffer,
314                       uint32_t aAlgorithmVersion,
315                       JS::MutableHandle<JS::Value> aValue,
316                       const JS::CloneDataPolicy& aCloneDataPolicy,
317                       ErrorResult& aRv);
318 
319   void SameProcessScopeRequired(bool* aSameProcessScopeRequired);
320 
321   bool mSupportsCloning;
322   bool mSupportsTransferring;
323 
324   // SizeOfExcludingThis is inherited from StructuredCloneHolderBase. It doesn't
325   // account for objects in the following arrays because a) they're not expected
326   // to be stored in long-lived StructuredCloneHolder objects, and b) in the
327   // case of BlobImpl objects, MemoryBlobImpls have their own memory reporters,
328   // and the other types do not hold significant amounts of memory alive.
329 
330   // Used for cloning blobs in the structured cloning algorithm.
331   nsTArray<RefPtr<BlobImpl>> mBlobImplArray;
332 
333   // Used for cloning JS::WasmModules in the structured cloning algorithm.
334   nsTArray<RefPtr<JS::WasmModule>> mWasmModuleArray;
335 
336   // Used for cloning InputStream in the structured cloning algorithm.
337   nsTArray<nsCOMPtr<nsIInputStream>> mInputStreamArray;
338 
339   // This is used for sharing the backend of ImageBitmaps.
340   // The DataSourceSurface object must be thread-safely reference-counted.
341   // The DataSourceSurface object will not be written ever via any ImageBitmap
342   // instance, so no race condition will occur.
343   nsTArray<RefPtr<gfx::DataSourceSurface>> mClonedSurfaces;
344 
345   // This raw pointer is only set within ::Read() and is unset by the end.
346   nsIGlobalObject* MOZ_NON_OWNING_REF mGlobal;
347 
348   // This array contains the ports once we've finished the reading. It's
349   // generated from the mPortIdentifiers array.
350   nsTArray<RefPtr<MessagePort>> mTransferredPorts;
351 
352   // This array contains the identifiers of the MessagePorts. Based on these we
353   // are able to reconnect the new transferred ports with the other
354   // MessageChannel ports.
355   mutable nsTArray<MessagePortIdentifier> mPortIdentifiers;
356 
357 #ifdef DEBUG
358   nsCOMPtr<nsIEventTarget> mCreationEventTarget;
359 #endif
360 };
361 
362 }  // namespace dom
363 }  // namespace mozilla
364 
365 #endif  // mozilla_dom_StructuredCloneHolder_h
366