diff options
Diffstat (limited to 'dom/base/StructuredCloneHolder.h')
-rw-r--r-- | dom/base/StructuredCloneHolder.h | 332 |
1 files changed, 332 insertions, 0 deletions
diff --git a/dom/base/StructuredCloneHolder.h b/dom/base/StructuredCloneHolder.h new file mode 100644 index 000000000..e6953d0f3 --- /dev/null +++ b/dom/base/StructuredCloneHolder.h @@ -0,0 +1,332 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_StructuredCloneHolder_h +#define mozilla_dom_StructuredCloneHolder_h + +#include "jsapi.h" +#include "js/StructuredClone.h" +#include "mozilla/Move.h" +#include "mozilla/UniquePtr.h" +#include "mozilla/dom/BindingDeclarations.h" +#include "nsISupports.h" +#include "nsTArray.h" + +#ifdef DEBUG +#include "nsIThread.h" +#endif + +namespace mozilla { +class ErrorResult; +namespace layers { +class Image; +} + +namespace gfx { +class DataSourceSurface; +} + +namespace dom { + +class StructuredCloneHolderBase +{ +public: + typedef JS::StructuredCloneScope StructuredCloneScope; + + StructuredCloneHolderBase(StructuredCloneScope aScope = StructuredCloneScope::SameProcessSameThread); + virtual ~StructuredCloneHolderBase(); + + StructuredCloneHolderBase(StructuredCloneHolderBase&& aOther) = default; + + // These methods should be implemented in order to clone data. + // Read more documentation in js/public/StructuredClone.h. + + virtual JSObject* CustomReadHandler(JSContext* aCx, + JSStructuredCloneReader* aReader, + uint32_t aTag, + uint32_t aIndex) = 0; + + virtual bool CustomWriteHandler(JSContext* aCx, + JSStructuredCloneWriter* aWriter, + JS::Handle<JSObject*> aObj) = 0; + + // This method has to be called when this object is not needed anymore. + // It will free memory and the buffer. This has to be called because + // otherwise the buffer will be freed in the DTOR of this class and at that + // point we cannot use the overridden methods. + void Clear(); + + // If these 3 methods are not implement, transfering objects will not be + // allowed. Otherwise only arrayBuffers will be transferred. + + virtual bool + CustomReadTransferHandler(JSContext* aCx, + JSStructuredCloneReader* aReader, + uint32_t aTag, + void* aContent, + uint64_t aExtraData, + JS::MutableHandleObject aReturnObject); + + virtual bool + CustomWriteTransferHandler(JSContext* aCx, + JS::Handle<JSObject*> aObj, + // Output: + uint32_t* aTag, + JS::TransferableOwnership* aOwnership, + void** aContent, + uint64_t* aExtraData); + + virtual void + CustomFreeTransferHandler(uint32_t aTag, + JS::TransferableOwnership aOwnership, + void* aContent, + uint64_t aExtraData); + + // These methods are what you should use to read/write data. + + // Execute the serialization of aValue using the Structured Clone Algorithm. + // The data can read back using Read(). + bool Write(JSContext* aCx, + JS::Handle<JS::Value> aValue); + + // Like Write() but it supports the transferring of objects and handling + // of cloning policy. + bool Write(JSContext* aCx, + JS::Handle<JS::Value> aValue, + JS::Handle<JS::Value> aTransfer, + JS::CloneDataPolicy cloneDataPolicy); + + // If Write() has been called, this method retrieves data and stores it into + // aValue. + bool Read(JSContext* aCx, + JS::MutableHandle<JS::Value> aValue); + + bool HasData() const + { + return !!mBuffer; + } + + JSStructuredCloneData& BufferData() const + { + MOZ_ASSERT(mBuffer, "Write() has never been called."); + return mBuffer->data(); + } + +protected: + UniquePtr<JSAutoStructuredCloneBuffer> mBuffer; + + StructuredCloneScope mStructuredCloneScope; + +#ifdef DEBUG + bool mClearCalled; +#endif +}; + +class BlobImpl; +class MessagePort; +class MessagePortIdentifier; + +class StructuredCloneHolder : public StructuredCloneHolderBase +{ +public: + enum CloningSupport + { + CloningSupported, + CloningNotSupported + }; + + enum TransferringSupport + { + TransferringSupported, + TransferringNotSupported + }; + + // If cloning is supported, this object will clone objects such as Blobs, + // FileList, ImageData, etc. + // If transferring is supported, we will transfer MessagePorts and in the + // future other transferrable objects. + // The StructuredCloneScope is useful to know where the cloned/transferred + // data can be read and written. Additional checks about the nature of the + // objects will be done based on this scope value because not all the + // objects can be sent between threads or processes. + explicit StructuredCloneHolder(CloningSupport aSupportsCloning, + TransferringSupport aSupportsTransferring, + StructuredCloneScope aStructuredCloneScope); + virtual ~StructuredCloneHolder(); + + StructuredCloneHolder(StructuredCloneHolder&& aOther) = default; + + // Normally you should just use Write() and Read(). + + void Write(JSContext* aCx, + JS::Handle<JS::Value> aValue, + ErrorResult &aRv); + + void Write(JSContext* aCx, + JS::Handle<JS::Value> aValue, + JS::Handle<JS::Value> aTransfer, + JS::CloneDataPolicy cloneDataPolicy, + ErrorResult &aRv); + + void Read(nsISupports* aParent, + JSContext* aCx, + JS::MutableHandle<JS::Value> aValue, + ErrorResult &aRv); + + // Call this method to know if this object is keeping some DOM object alive. + bool HasClonedDOMObjects() const + { + return !mBlobImplArray.IsEmpty() || + !mWasmModuleArray.IsEmpty() || + !mClonedSurfaces.IsEmpty(); + } + + nsTArray<RefPtr<BlobImpl>>& BlobImpls() + { + MOZ_ASSERT(mSupportsCloning, "Blobs cannot be taken/set if cloning is not supported."); + return mBlobImplArray; + } + + nsTArray<RefPtr<JS::WasmModule>>& WasmModules() + { + MOZ_ASSERT(mSupportsCloning, "WasmModules cannot be taken/set if cloning is not supported."); + return mWasmModuleArray; + } + + StructuredCloneScope CloneScope() const + { + return mStructuredCloneScope; + } + + // The parent object is set internally just during the Read(). This method + // can be used by read functions to retrieve it. + nsISupports* ParentDuringRead() const + { + return mParent; + } + + // This must be called if the transferring has ports generated by Read(). + // MessagePorts are not thread-safe and they must be retrieved in the thread + // where they are created. + nsTArray<RefPtr<MessagePort>>&& TakeTransferredPorts() + { + MOZ_ASSERT(mSupportsTransferring); + return Move(mTransferredPorts); + } + + // This method uses TakeTransferredPorts() to populate a sequence of + // MessagePorts for WebIDL binding classes. + bool + TakeTransferredPortsAsSequence(Sequence<OwningNonNull<mozilla::dom::MessagePort>>& aPorts); + + nsTArray<MessagePortIdentifier>& PortIdentifiers() const + { + MOZ_ASSERT(mSupportsTransferring); + return mPortIdentifiers; + } + + nsTArray<RefPtr<gfx::DataSourceSurface>>& GetSurfaces() + { + return mClonedSurfaces; + } + + // Implementations of the virtual methods to allow cloning of objects which + // JS engine itself doesn't clone. + + virtual JSObject* CustomReadHandler(JSContext* aCx, + JSStructuredCloneReader* aReader, + uint32_t aTag, + uint32_t aIndex) override; + + virtual bool CustomWriteHandler(JSContext* aCx, + JSStructuredCloneWriter* aWriter, + JS::Handle<JSObject*> aObj) override; + + virtual bool CustomReadTransferHandler(JSContext* aCx, + JSStructuredCloneReader* aReader, + uint32_t aTag, + void* aContent, + uint64_t aExtraData, + JS::MutableHandleObject aReturnObject) override; + + virtual bool CustomWriteTransferHandler(JSContext* aCx, + JS::Handle<JSObject*> aObj, + uint32_t* aTag, + JS::TransferableOwnership* aOwnership, + void** aContent, + uint64_t* aExtraData) override; + + virtual void CustomFreeTransferHandler(uint32_t aTag, + JS::TransferableOwnership aOwnership, + void* aContent, + uint64_t aExtraData) override; + + // These 2 static methods are useful to read/write fully serializable objects. + // They can be used by custom StructuredCloneHolderBase classes to + // serialize objects such as ImageData, CryptoKey, RTCCertificate, etc. + + static JSObject* ReadFullySerializableObjects(JSContext* aCx, + JSStructuredCloneReader* aReader, + uint32_t aTag); + + static bool WriteFullySerializableObjects(JSContext* aCx, + JSStructuredCloneWriter* aWriter, + JS::Handle<JSObject*> aObj); + + static const JSStructuredCloneCallbacks sCallbacks; + +protected: + // If you receive a buffer from IPC, you can use this method to retrieve a + // JS::Value. It can happen that you want to pre-populate the array of Blobs + // and/or the PortIdentifiers. + void ReadFromBuffer(nsISupports* aParent, + JSContext* aCx, + JSStructuredCloneData& aBuffer, + JS::MutableHandle<JS::Value> aValue, + ErrorResult &aRv); + + void ReadFromBuffer(nsISupports* aParent, + JSContext* aCx, + JSStructuredCloneData& aBuffer, + uint32_t aAlgorithmVersion, + JS::MutableHandle<JS::Value> aValue, + ErrorResult &aRv); + + bool mSupportsCloning; + bool mSupportsTransferring; + + // Used for cloning blobs in the structured cloning algorithm. + nsTArray<RefPtr<BlobImpl>> mBlobImplArray; + + // Used for cloning JS::WasmModules in the structured cloning algorithm. + nsTArray<RefPtr<JS::WasmModule>> mWasmModuleArray; + + // This is used for sharing the backend of ImageBitmaps. + // The DataSourceSurface object must be thread-safely reference-counted. + // The DataSourceSurface object will not be written ever via any ImageBitmap + // instance, so no race condition will occur. + nsTArray<RefPtr<gfx::DataSourceSurface>> mClonedSurfaces; + + // This raw pointer is only set within ::Read() and is unset by the end. + nsISupports* MOZ_NON_OWNING_REF mParent; + + // This array contains the ports once we've finished the reading. It's + // generated from the mPortIdentifiers array. + nsTArray<RefPtr<MessagePort>> mTransferredPorts; + + // This array contains the identifiers of the MessagePorts. Based on these we + // are able to reconnect the new transferred ports with the other + // MessageChannel ports. + mutable nsTArray<MessagePortIdentifier> mPortIdentifiers; + +#ifdef DEBUG + nsCOMPtr<nsIThread> mCreationThread; +#endif +}; + +} // dom namespace +} // mozilla namespace + +#endif // mozilla_dom_StructuredCloneHolder_h |