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