forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			1398 lines
		
	
	
	
		
			42 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1398 lines
		
	
	
	
		
			42 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | |
| /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 | |
| /* 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/. */
 | |
| 
 | |
| #include "StructuredCloneHolder.h"
 | |
| 
 | |
| #include "ImageContainer.h"
 | |
| #include "mozilla/AutoRestore.h"
 | |
| #include "mozilla/dom/BlobBinding.h"
 | |
| #include "mozilla/dom/CryptoKey.h"
 | |
| #include "mozilla/dom/StructuredCloneBlob.h"
 | |
| #include "mozilla/dom/Directory.h"
 | |
| #include "mozilla/dom/DirectoryBinding.h"
 | |
| #include "mozilla/dom/File.h"
 | |
| #include "mozilla/dom/FileList.h"
 | |
| #include "mozilla/dom/FileListBinding.h"
 | |
| #include "mozilla/dom/FormData.h"
 | |
| #include "mozilla/dom/ImageBitmap.h"
 | |
| #include "mozilla/dom/ImageBitmapBinding.h"
 | |
| #include "mozilla/dom/ImageData.h"
 | |
| #include "mozilla/dom/ImageDataBinding.h"
 | |
| #include "mozilla/dom/StructuredClone.h"
 | |
| #include "mozilla/dom/MessagePort.h"
 | |
| #include "mozilla/dom/MessagePortBinding.h"
 | |
| #include "mozilla/dom/OffscreenCanvas.h"
 | |
| #include "mozilla/dom/OffscreenCanvasBinding.h"
 | |
| #include "mozilla/dom/PMessagePort.h"
 | |
| #include "mozilla/dom/StructuredCloneTags.h"
 | |
| #include "mozilla/dom/SubtleCryptoBinding.h"
 | |
| #include "mozilla/dom/ToJSValue.h"
 | |
| #include "mozilla/dom/URLSearchParams.h"
 | |
| #include "mozilla/dom/URLSearchParamsBinding.h"
 | |
| #include "mozilla/dom/WebCryptoCommon.h"
 | |
| #include "mozilla/gfx/2D.h"
 | |
| #include "mozilla/ipc/BackgroundChild.h"
 | |
| #include "mozilla/ipc/BackgroundUtils.h"
 | |
| #include "mozilla/ipc/PBackgroundSharedTypes.h"
 | |
| #include "MultipartBlobImpl.h"
 | |
| #include "nsQueryObject.h"
 | |
| 
 | |
| #ifdef MOZ_WEBRTC
 | |
| #include "mozilla/dom/RTCCertificate.h"
 | |
| #include "mozilla/dom/RTCCertificateBinding.h"
 | |
| #endif
 | |
| 
 | |
| using namespace mozilla::ipc;
 | |
| 
 | |
| namespace mozilla {
 | |
| namespace dom {
 | |
| 
 | |
| namespace {
 | |
| 
 | |
| JSObject*
 | |
| StructuredCloneCallbacksRead(JSContext* aCx,
 | |
|                              JSStructuredCloneReader* aReader,
 | |
|                              uint32_t aTag, uint32_t aIndex,
 | |
|                              void* aClosure)
 | |
| {
 | |
|   StructuredCloneHolderBase* holder =
 | |
|     static_cast<StructuredCloneHolderBase*>(aClosure);
 | |
|   MOZ_ASSERT(holder);
 | |
|   return holder->CustomReadHandler(aCx, aReader, aTag, aIndex);
 | |
| }
 | |
| 
 | |
| bool
 | |
| StructuredCloneCallbacksWrite(JSContext* aCx,
 | |
|                               JSStructuredCloneWriter* aWriter,
 | |
|                               JS::Handle<JSObject*> aObj,
 | |
|                               void* aClosure)
 | |
| {
 | |
|   StructuredCloneHolderBase* holder =
 | |
|     static_cast<StructuredCloneHolderBase*>(aClosure);
 | |
|   MOZ_ASSERT(holder);
 | |
|   return holder->CustomWriteHandler(aCx, aWriter, aObj);
 | |
| }
 | |
| 
 | |
| bool
 | |
| StructuredCloneCallbacksReadTransfer(JSContext* aCx,
 | |
|                                      JSStructuredCloneReader* aReader,
 | |
|                                      uint32_t aTag,
 | |
|                                      void* aContent,
 | |
|                                      uint64_t aExtraData,
 | |
|                                      void* aClosure,
 | |
|                                      JS::MutableHandleObject aReturnObject)
 | |
| {
 | |
|   StructuredCloneHolderBase* holder =
 | |
|     static_cast<StructuredCloneHolderBase*>(aClosure);
 | |
|   MOZ_ASSERT(holder);
 | |
|   return holder->CustomReadTransferHandler(aCx, aReader, aTag, aContent,
 | |
|                                            aExtraData, aReturnObject);
 | |
| }
 | |
| 
 | |
| bool
 | |
| StructuredCloneCallbacksWriteTransfer(JSContext* aCx,
 | |
|                                       JS::Handle<JSObject*> aObj,
 | |
|                                       void* aClosure,
 | |
|                                       // Output:
 | |
|                                       uint32_t* aTag,
 | |
|                                       JS::TransferableOwnership* aOwnership,
 | |
|                                       void** aContent,
 | |
|                                       uint64_t* aExtraData)
 | |
| {
 | |
|   StructuredCloneHolderBase* holder =
 | |
|     static_cast<StructuredCloneHolderBase*>(aClosure);
 | |
|   MOZ_ASSERT(holder);
 | |
|   return holder->CustomWriteTransferHandler(aCx, aObj, aTag, aOwnership,
 | |
|                                             aContent, aExtraData);
 | |
| }
 | |
| 
 | |
| void
 | |
| StructuredCloneCallbacksFreeTransfer(uint32_t aTag,
 | |
|                                      JS::TransferableOwnership aOwnership,
 | |
|                                      void* aContent,
 | |
|                                      uint64_t aExtraData,
 | |
|                                      void* aClosure)
 | |
| {
 | |
|   StructuredCloneHolderBase* holder =
 | |
|     static_cast<StructuredCloneHolderBase*>(aClosure);
 | |
|   MOZ_ASSERT(holder);
 | |
|   return holder->CustomFreeTransferHandler(aTag, aOwnership, aContent,
 | |
|                                            aExtraData);
 | |
| }
 | |
| 
 | |
| bool
 | |
| StructuredCloneCallbacksCanTransfer(JSContext* aCx,
 | |
|                                     JS::Handle<JSObject*> aObject,
 | |
|                                     void* aClosure)
 | |
| {
 | |
|   StructuredCloneHolderBase* holder =
 | |
|     static_cast<StructuredCloneHolderBase*>(aClosure);
 | |
|   MOZ_ASSERT(holder);
 | |
|   return holder->CustomCanTransferHandler(aCx, aObject);
 | |
| }
 | |
| 
 | |
| void
 | |
| StructuredCloneCallbacksError(JSContext* aCx,
 | |
|                               uint32_t aErrorId)
 | |
| {
 | |
|   NS_WARNING("Failed to clone data.");
 | |
| }
 | |
| 
 | |
| } // anonymous namespace
 | |
| 
 | |
| const JSStructuredCloneCallbacks StructuredCloneHolder::sCallbacks = {
 | |
|   StructuredCloneCallbacksRead,
 | |
|   StructuredCloneCallbacksWrite,
 | |
|   StructuredCloneCallbacksError,
 | |
|   StructuredCloneCallbacksReadTransfer,
 | |
|   StructuredCloneCallbacksWriteTransfer,
 | |
|   StructuredCloneCallbacksFreeTransfer,
 | |
|   StructuredCloneCallbacksCanTransfer,
 | |
| };
 | |
| 
 | |
| // StructuredCloneHolderBase class
 | |
| 
 | |
| StructuredCloneHolderBase::StructuredCloneHolderBase(StructuredCloneScope aScope)
 | |
|   : mStructuredCloneScope(aScope)
 | |
| #ifdef DEBUG
 | |
|   , mClearCalled(false)
 | |
| #endif
 | |
| {}
 | |
| 
 | |
| StructuredCloneHolderBase::~StructuredCloneHolderBase()
 | |
| {
 | |
| #ifdef DEBUG
 | |
|   MOZ_ASSERT(mClearCalled);
 | |
| #endif
 | |
| }
 | |
| 
 | |
| void
 | |
| StructuredCloneHolderBase::Clear()
 | |
| {
 | |
| #ifdef DEBUG
 | |
|   mClearCalled = true;
 | |
| #endif
 | |
| 
 | |
|   mBuffer = nullptr;
 | |
| }
 | |
| 
 | |
| bool
 | |
| StructuredCloneHolderBase::Write(JSContext* aCx,
 | |
|                                  JS::Handle<JS::Value> aValue)
 | |
| {
 | |
|   return Write(aCx, aValue, JS::UndefinedHandleValue,
 | |
|                JS::CloneDataPolicy().denySharedArrayBuffer());
 | |
| }
 | |
| 
 | |
| bool
 | |
| StructuredCloneHolderBase::Write(JSContext* aCx,
 | |
|                                  JS::Handle<JS::Value> aValue,
 | |
|                                  JS::Handle<JS::Value> aTransfer,
 | |
|                                  JS::CloneDataPolicy cloneDataPolicy)
 | |
| {
 | |
|   MOZ_ASSERT(!mBuffer, "Double Write is not allowed");
 | |
|   MOZ_ASSERT(!mClearCalled, "This method cannot be called after Clear.");
 | |
| 
 | |
|   mBuffer = MakeUnique<JSAutoStructuredCloneBuffer>(mStructuredCloneScope, &StructuredCloneHolder::sCallbacks, this);
 | |
| 
 | |
|   if (!mBuffer->write(aCx, aValue, aTransfer, cloneDataPolicy,
 | |
|                       &StructuredCloneHolder::sCallbacks, this))
 | |
|   {
 | |
|     mBuffer = nullptr;
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| bool
 | |
| StructuredCloneHolderBase::Read(JSContext* aCx,
 | |
|                                 JS::MutableHandle<JS::Value> aValue)
 | |
| {
 | |
|   MOZ_ASSERT(mBuffer, "Read() without Write() is not allowed.");
 | |
|   MOZ_ASSERT(!mClearCalled, "This method cannot be called after Clear.");
 | |
| 
 | |
|   bool ok = mBuffer->read(aCx, aValue, &StructuredCloneHolder::sCallbacks, this);
 | |
|   return ok;
 | |
| }
 | |
| 
 | |
| bool
 | |
| StructuredCloneHolderBase::CustomReadTransferHandler(JSContext* aCx,
 | |
|                                                      JSStructuredCloneReader* aReader,
 | |
|                                                      uint32_t aTag,
 | |
|                                                      void* aContent,
 | |
|                                                      uint64_t aExtraData,
 | |
|                                                      JS::MutableHandleObject aReturnObject)
 | |
| {
 | |
|   MOZ_CRASH("Nothing to read.");
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| bool
 | |
| StructuredCloneHolderBase::CustomWriteTransferHandler(JSContext* aCx,
 | |
|                                                       JS::Handle<JSObject*> aObj,
 | |
|                                                       uint32_t* aTag,
 | |
|                                                       JS::TransferableOwnership* aOwnership,
 | |
|                                                       void** aContent,
 | |
|                                                       uint64_t* aExtraData)
 | |
| {
 | |
|   // No transfers are supported by default.
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| void
 | |
| StructuredCloneHolderBase::CustomFreeTransferHandler(uint32_t aTag,
 | |
|                                                      JS::TransferableOwnership aOwnership,
 | |
|                                                      void* aContent,
 | |
|                                                      uint64_t aExtraData)
 | |
| {
 | |
|   MOZ_CRASH("Nothing to free.");
 | |
| }
 | |
| 
 | |
| bool
 | |
| StructuredCloneHolderBase::CustomCanTransferHandler(JSContext* aCx,
 | |
|                                                     JS::Handle<JSObject*> aObj)
 | |
| {
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| // StructuredCloneHolder class
 | |
| 
 | |
| StructuredCloneHolder::StructuredCloneHolder(CloningSupport aSupportsCloning,
 | |
|                                              TransferringSupport aSupportsTransferring,
 | |
|                                              StructuredCloneScope aScope)
 | |
|   : StructuredCloneHolderBase(aScope)
 | |
|   , mSupportsCloning(aSupportsCloning == CloningSupported)
 | |
|   , mSupportsTransferring(aSupportsTransferring == TransferringSupported)
 | |
|   , mParent(nullptr)
 | |
| #ifdef DEBUG
 | |
|   , mCreationEventTarget(GetCurrentThreadEventTarget())
 | |
| #endif
 | |
| {}
 | |
| 
 | |
| StructuredCloneHolder::~StructuredCloneHolder()
 | |
| {
 | |
|   Clear();
 | |
|   MOZ_ASSERT(mTransferredPorts.IsEmpty());
 | |
| }
 | |
| 
 | |
| void
 | |
| StructuredCloneHolder::Write(JSContext* aCx,
 | |
|                              JS::Handle<JS::Value> aValue,
 | |
|                              ErrorResult& aRv)
 | |
| {
 | |
|   Write(aCx, aValue, JS::UndefinedHandleValue,
 | |
|         JS::CloneDataPolicy().denySharedArrayBuffer(), aRv);
 | |
| }
 | |
| 
 | |
| void
 | |
| StructuredCloneHolder::Write(JSContext* aCx,
 | |
|                              JS::Handle<JS::Value> aValue,
 | |
|                              JS::Handle<JS::Value> aTransfer,
 | |
|                              JS::CloneDataPolicy cloneDataPolicy,
 | |
|                              ErrorResult& aRv)
 | |
| {
 | |
|   MOZ_ASSERT_IF(mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread,
 | |
|                 mCreationEventTarget->IsOnCurrentThread());
 | |
| 
 | |
|   if (!StructuredCloneHolderBase::Write(aCx, aValue, aTransfer, cloneDataPolicy)) {
 | |
|     aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
 | |
|     return;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void
 | |
| StructuredCloneHolder::Read(nsISupports* aParent,
 | |
|                             JSContext* aCx,
 | |
|                             JS::MutableHandle<JS::Value> aValue,
 | |
|                             ErrorResult& aRv)
 | |
| {
 | |
|   MOZ_ASSERT_IF(mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread,
 | |
|                 mCreationEventTarget->IsOnCurrentThread());
 | |
|   MOZ_ASSERT(aParent);
 | |
| 
 | |
|   mozilla::AutoRestore<nsISupports*> guard(mParent);
 | |
|   mParent = aParent;
 | |
| 
 | |
|   if (!StructuredCloneHolderBase::Read(aCx, aValue)) {
 | |
|     JS_ClearPendingException(aCx);
 | |
|     aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   // If we are tranferring something, we cannot call 'Read()' more than once.
 | |
|   if (mSupportsTransferring) {
 | |
|     mBlobImplArray.Clear();
 | |
|     mWasmModuleArray.Clear();
 | |
|     mClonedSurfaces.Clear();
 | |
|     mInputStreamArray.Clear();
 | |
|     Clear();
 | |
|   }
 | |
| }
 | |
| 
 | |
| void
 | |
| StructuredCloneHolder::ReadFromBuffer(nsISupports* aParent,
 | |
|                                       JSContext* aCx,
 | |
|                                       JSStructuredCloneData& aBuffer,
 | |
|                                       JS::MutableHandle<JS::Value> aValue,
 | |
|                                       ErrorResult& aRv)
 | |
| {
 | |
|   ReadFromBuffer(aParent, aCx, aBuffer,
 | |
|                  JS_STRUCTURED_CLONE_VERSION, aValue, aRv);
 | |
| }
 | |
| 
 | |
| void
 | |
| StructuredCloneHolder::ReadFromBuffer(nsISupports* aParent,
 | |
|                                       JSContext* aCx,
 | |
|                                       JSStructuredCloneData& aBuffer,
 | |
|                                       uint32_t aAlgorithmVersion,
 | |
|                                       JS::MutableHandle<JS::Value> aValue,
 | |
|                                       ErrorResult& aRv)
 | |
| {
 | |
|   MOZ_ASSERT_IF(mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread,
 | |
|                 mCreationEventTarget->IsOnCurrentThread());
 | |
| 
 | |
|   MOZ_ASSERT(!mBuffer, "ReadFromBuffer() must be called without a Write().");
 | |
| 
 | |
|   mozilla::AutoRestore<nsISupports*> guard(mParent);
 | |
|   mParent = aParent;
 | |
| 
 | |
|   if (!JS_ReadStructuredClone(aCx, aBuffer, aAlgorithmVersion,
 | |
|                               mStructuredCloneScope, aValue, &sCallbacks,
 | |
|                               this)) {
 | |
|     JS_ClearPendingException(aCx);
 | |
|     aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
 | |
|   }
 | |
| }
 | |
| 
 | |
| /* static */ JSObject*
 | |
| StructuredCloneHolder::ReadFullySerializableObjects(JSContext* aCx,
 | |
|                                                     JSStructuredCloneReader* aReader,
 | |
|                                                     uint32_t aTag)
 | |
| {
 | |
|   if (aTag == SCTAG_DOM_IMAGEDATA) {
 | |
|     return ReadStructuredCloneImageData(aCx, aReader);
 | |
|   }
 | |
| 
 | |
|   if (aTag == SCTAG_DOM_WEBCRYPTO_KEY || aTag == SCTAG_DOM_URLSEARCHPARAMS) {
 | |
|     nsIGlobalObject* global = xpc::CurrentNativeGlobal(aCx);
 | |
|     if (!global) {
 | |
|       return nullptr;
 | |
|     }
 | |
| 
 | |
|     // Prevent the return value from being trashed by a GC during ~nsRefPtr.
 | |
|     JS::Rooted<JSObject*> result(aCx);
 | |
|     {
 | |
|       if (aTag == SCTAG_DOM_WEBCRYPTO_KEY) {
 | |
|         RefPtr<CryptoKey> key = new CryptoKey(global);
 | |
|         if (!key->ReadStructuredClone(aReader)) {
 | |
|          result = nullptr;
 | |
|         } else {
 | |
|           result = key->WrapObject(aCx, nullptr);
 | |
|         }
 | |
|       } else if (aTag == SCTAG_DOM_URLSEARCHPARAMS) {
 | |
|         RefPtr<URLSearchParams> usp = new URLSearchParams(global);
 | |
|        if (!usp->ReadStructuredClone(aReader)) {
 | |
|           result = nullptr;
 | |
|         } else {
 | |
|           result = usp->WrapObject(aCx, nullptr);
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   if (aTag == SCTAG_DOM_NULL_PRINCIPAL ||
 | |
|       aTag == SCTAG_DOM_SYSTEM_PRINCIPAL ||
 | |
|       aTag == SCTAG_DOM_CONTENT_PRINCIPAL ||
 | |
|       aTag == SCTAG_DOM_EXPANDED_PRINCIPAL) {
 | |
|     JSPrincipals* prin;
 | |
|     if (!nsJSPrincipals::ReadKnownPrincipalType(aCx, aReader, aTag, &prin)) {
 | |
|       return nullptr;
 | |
|     }
 | |
|     // nsJSPrincipals::ReadKnownPrincipalType addrefs for us, but because of the
 | |
|     // casting between JSPrincipals* and nsIPrincipal* we can't use
 | |
|     // getter_AddRefs above and have to already_AddRefed here.
 | |
|     nsCOMPtr<nsIPrincipal> principal = already_AddRefed<nsIPrincipal>(nsJSPrincipals::get(prin));
 | |
| 
 | |
|     JS::RootedValue result(aCx);
 | |
|     nsresult rv = nsContentUtils::WrapNative(aCx, principal,
 | |
|                                              &NS_GET_IID(nsIPrincipal),
 | |
|                                              &result);
 | |
|     if (NS_FAILED(rv)) {
 | |
|       xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
 | |
|       return nullptr;
 | |
|     }
 | |
| 
 | |
|     return result.toObjectOrNull();
 | |
|   }
 | |
| 
 | |
| #ifdef MOZ_WEBRTC
 | |
|   if (aTag == SCTAG_DOM_RTC_CERTIFICATE) {
 | |
|     if (!NS_IsMainThread()) {
 | |
|       return nullptr;
 | |
|     }
 | |
| 
 | |
|     nsIGlobalObject* global = xpc::CurrentNativeGlobal(aCx);
 | |
|     if (!global) {
 | |
|       return nullptr;
 | |
|     }
 | |
| 
 | |
|     // Prevent the return value from being trashed by a GC during ~nsRefPtr.
 | |
|     JS::Rooted<JSObject*> result(aCx);
 | |
|     {
 | |
|       RefPtr<RTCCertificate> cert = new RTCCertificate(global);
 | |
|       if (!cert->ReadStructuredClone(aReader)) {
 | |
|         result = nullptr;
 | |
|       } else {
 | |
|         result = cert->WrapObject(aCx, nullptr);
 | |
|       }
 | |
|     }
 | |
|     return result;
 | |
|   }
 | |
| #endif
 | |
| 
 | |
|   // Don't know what this is. Bail.
 | |
|   xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
 | |
|   return nullptr;
 | |
| }
 | |
| 
 | |
| /* static */ bool
 | |
| StructuredCloneHolder::WriteFullySerializableObjects(JSContext* aCx,
 | |
|                                                      JSStructuredCloneWriter* aWriter,
 | |
|                                                      JS::Handle<JSObject*> aObj)
 | |
| {
 | |
|   JS::Rooted<JSObject*> obj(aCx, aObj);
 | |
| 
 | |
|   // See if this is a ImageData object.
 | |
|   {
 | |
|     ImageData* imageData = nullptr;
 | |
|     if (NS_SUCCEEDED(UNWRAP_OBJECT(ImageData, &obj, imageData))) {
 | |
|       return WriteStructuredCloneImageData(aCx, aWriter, imageData);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // Handle URLSearchParams cloning
 | |
|   {
 | |
|     URLSearchParams* usp = nullptr;
 | |
|     if (NS_SUCCEEDED(UNWRAP_OBJECT(URLSearchParams, &obj, usp))) {
 | |
|       return JS_WriteUint32Pair(aWriter, SCTAG_DOM_URLSEARCHPARAMS, 0) &&
 | |
|              usp->WriteStructuredClone(aWriter);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // Handle Key cloning
 | |
|   {
 | |
|     CryptoKey* key = nullptr;
 | |
|     if (NS_SUCCEEDED(UNWRAP_OBJECT(CryptoKey, &obj, key))) {
 | |
|       return JS_WriteUint32Pair(aWriter, SCTAG_DOM_WEBCRYPTO_KEY, 0) &&
 | |
|              key->WriteStructuredClone(aWriter);
 | |
|     }
 | |
|   }
 | |
| 
 | |
| #ifdef MOZ_WEBRTC
 | |
|   {
 | |
|     // Handle WebRTC Certificate cloning
 | |
|     RTCCertificate* cert = nullptr;
 | |
|     if (NS_SUCCEEDED(UNWRAP_OBJECT(RTCCertificate, &obj, cert))) {
 | |
|       MOZ_ASSERT(NS_IsMainThread());
 | |
|       return JS_WriteUint32Pair(aWriter, SCTAG_DOM_RTC_CERTIFICATE, 0) &&
 | |
|              cert->WriteStructuredClone(aWriter);
 | |
|     }
 | |
|   }
 | |
| #endif
 | |
| 
 | |
|   if (NS_IsMainThread() && xpc::IsReflector(obj)) {
 | |
|     nsCOMPtr<nsISupports> base = xpc::UnwrapReflectorToISupports(obj);
 | |
|     nsCOMPtr<nsIPrincipal> principal = do_QueryInterface(base);
 | |
|     if (principal) {
 | |
|       auto nsjsprincipals = nsJSPrincipals::get(principal);
 | |
|       return nsjsprincipals->write(aCx, aWriter);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // Don't know what this is
 | |
|   xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| namespace {
 | |
| 
 | |
| JSObject*
 | |
| ReadBlob(JSContext* aCx,
 | |
|          uint32_t aIndex,
 | |
|          StructuredCloneHolder* aHolder)
 | |
| {
 | |
|   MOZ_ASSERT(aHolder);
 | |
| #ifdef FUZZING
 | |
|   if (aIndex >= aHolder->BlobImpls().Length()) {
 | |
|     return nullptr;
 | |
|   }
 | |
| #endif
 | |
|   MOZ_ASSERT(aIndex < aHolder->BlobImpls().Length());
 | |
|   RefPtr<BlobImpl> blobImpl = aHolder->BlobImpls()[aIndex];
 | |
| 
 | |
|   MOZ_ALWAYS_SUCCEEDS(blobImpl->SetMutable(false));
 | |
| 
 | |
|   // RefPtr<File> needs to go out of scope before toObject() is
 | |
|   // called because the static analysis thinks dereferencing XPCOM objects
 | |
|   // can GC (because in some cases it can!), and a return statement with a
 | |
|   // JSObject* type means that JSObject* is on the stack as a raw pointer
 | |
|   // while destructors are running.
 | |
|   JS::Rooted<JS::Value> val(aCx);
 | |
|   {
 | |
|     RefPtr<Blob> blob = Blob::Create(aHolder->ParentDuringRead(), blobImpl);
 | |
|     if (!ToJSValue(aCx, blob, &val)) {
 | |
|       return nullptr;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return &val.toObject();
 | |
| }
 | |
| 
 | |
| bool
 | |
| WriteBlob(JSStructuredCloneWriter* aWriter,
 | |
|           Blob* aBlob,
 | |
|           StructuredCloneHolder* aHolder)
 | |
| {
 | |
|   MOZ_ASSERT(aWriter);
 | |
|   MOZ_ASSERT(aBlob);
 | |
|   MOZ_ASSERT(aHolder);
 | |
| 
 | |
|   if (JS_GetStructuredCloneScope(aWriter) != JS::StructuredCloneScope::SameProcessSameThread &&
 | |
|       !aBlob->Impl()->MayBeClonedToOtherThreads()) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   RefPtr<BlobImpl> blobImpl = aBlob->Impl();
 | |
|   MOZ_ALWAYS_SUCCEEDS(blobImpl->SetMutable(false));
 | |
| 
 | |
|   // We store the position of the blobImpl in the array as index.
 | |
|   if (JS_WriteUint32Pair(aWriter, SCTAG_DOM_BLOB,
 | |
|                          aHolder->BlobImpls().Length())) {
 | |
|     aHolder->BlobImpls().AppendElement(blobImpl);
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| // A directory is serialized as:
 | |
| // - pair of ints: SCTAG_DOM_DIRECTORY, path length
 | |
| // - path as string
 | |
| bool
 | |
| WriteDirectory(JSStructuredCloneWriter* aWriter,
 | |
|                Directory* aDirectory)
 | |
| {
 | |
|   MOZ_ASSERT(aWriter);
 | |
|   MOZ_ASSERT(aDirectory);
 | |
| 
 | |
|   nsAutoString path;
 | |
|   aDirectory->GetFullRealPath(path);
 | |
| 
 | |
|   size_t charSize = sizeof(nsString::char_type);
 | |
|   return JS_WriteUint32Pair(aWriter, SCTAG_DOM_DIRECTORY, path.Length()) &&
 | |
|          JS_WriteBytes(aWriter, path.get(), path.Length() * charSize);
 | |
| }
 | |
| 
 | |
| already_AddRefed<Directory>
 | |
| ReadDirectoryInternal(JSStructuredCloneReader* aReader,
 | |
|                       uint32_t aPathLength,
 | |
|                       StructuredCloneHolder* aHolder)
 | |
| {
 | |
|   MOZ_ASSERT(aReader);
 | |
|   MOZ_ASSERT(aHolder);
 | |
| 
 | |
|   nsAutoString path;
 | |
|   if (NS_WARN_IF(!path.SetLength(aPathLength, fallible))) {
 | |
|     return nullptr;
 | |
|   }
 | |
|   size_t charSize = sizeof(nsString::char_type);
 | |
|   if (!JS_ReadBytes(aReader, (void*) path.BeginWriting(),
 | |
|                     aPathLength * charSize)) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   nsCOMPtr<nsIFile> file;
 | |
|   nsresult rv = NS_NewLocalFile(path, true, getter_AddRefs(file));
 | |
|   if (NS_WARN_IF(NS_FAILED(rv))) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   RefPtr<Directory> directory =
 | |
|     Directory::Create(aHolder->ParentDuringRead(), file);
 | |
|   return directory.forget();
 | |
| }
 | |
| 
 | |
| JSObject*
 | |
| ReadDirectory(JSContext* aCx,
 | |
|               JSStructuredCloneReader* aReader,
 | |
|               uint32_t aPathLength,
 | |
|               StructuredCloneHolder* aHolder)
 | |
| {
 | |
|   MOZ_ASSERT(aCx);
 | |
|   MOZ_ASSERT(aReader);
 | |
|   MOZ_ASSERT(aHolder);
 | |
| 
 | |
|   // RefPtr<Directory> needs to go out of scope before toObject() is
 | |
|   // called because the static analysis thinks dereferencing XPCOM objects
 | |
|   // can GC (because in some cases it can!), and a return statement with a
 | |
|   // JSObject* type means that JSObject* is on the stack as a raw pointer
 | |
|   // while destructors are running.
 | |
|   JS::Rooted<JS::Value> val(aCx);
 | |
|   {
 | |
|     RefPtr<Directory> directory =
 | |
|       ReadDirectoryInternal(aReader, aPathLength, aHolder);
 | |
|     if (!directory) {
 | |
|       return nullptr;
 | |
|     }
 | |
| 
 | |
|     if (!ToJSValue(aCx, directory, &val)) {
 | |
|       return nullptr;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return &val.toObject();
 | |
| }
 | |
| 
 | |
| // Read the WriteFileList for the format.
 | |
| JSObject*
 | |
| ReadFileList(JSContext* aCx,
 | |
|              JSStructuredCloneReader* aReader,
 | |
|              uint32_t aCount,
 | |
|              StructuredCloneHolder* aHolder)
 | |
| {
 | |
|   MOZ_ASSERT(aCx);
 | |
|   MOZ_ASSERT(aReader);
 | |
| 
 | |
|   JS::Rooted<JS::Value> val(aCx);
 | |
|   {
 | |
|     RefPtr<FileList> fileList = new FileList(aHolder->ParentDuringRead());
 | |
| 
 | |
|     uint32_t zero, index;
 | |
|     // |index| is the index of the first blobImpl.
 | |
|     if (!JS_ReadUint32Pair(aReader, &zero, &index)) {
 | |
|       return nullptr;
 | |
|     }
 | |
| 
 | |
|     MOZ_ASSERT(zero == 0);
 | |
| 
 | |
|     // |aCount| is the number of BlobImpls to use from the |index|.
 | |
|     for (uint32_t i = 0; i < aCount; ++i) {
 | |
|       uint32_t pos = index + i;
 | |
| #ifdef FUZZING
 | |
|       if (pos >= aHolder->BlobImpls().Length()) {
 | |
|         return nullptr;
 | |
|       }
 | |
| #endif
 | |
|       MOZ_ASSERT(pos < aHolder->BlobImpls().Length());
 | |
| 
 | |
|       RefPtr<BlobImpl> blobImpl = aHolder->BlobImpls()[pos];
 | |
|       MOZ_ASSERT(blobImpl->IsFile());
 | |
| 
 | |
|       MOZ_ALWAYS_SUCCEEDS(blobImpl->SetMutable(false));
 | |
| 
 | |
|       RefPtr<File> file = File::Create(aHolder->ParentDuringRead(), blobImpl);
 | |
|       if (!fileList->Append(file)) {
 | |
|         return nullptr;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     if (!ToJSValue(aCx, fileList, &val)) {
 | |
|       return nullptr;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return &val.toObject();
 | |
| }
 | |
| 
 | |
| // The format of the FileList serialization is:
 | |
| // - pair of ints: SCTAG_DOM_FILELIST, Length of the FileList
 | |
| // - pair of ints: 0, The offset of the BlobImpl array
 | |
| bool
 | |
| WriteFileList(JSStructuredCloneWriter* aWriter,
 | |
|               FileList* aFileList,
 | |
|               StructuredCloneHolder* aHolder)
 | |
| {
 | |
|   MOZ_ASSERT(aWriter);
 | |
|   MOZ_ASSERT(aFileList);
 | |
|   MOZ_ASSERT(aHolder);
 | |
| 
 | |
|   // A FileList is serialized writing the X number of elements and the offset
 | |
|   // from mBlobImplArray. The Read will take X elements from mBlobImplArray
 | |
|   // starting from the offset.
 | |
|   if (!JS_WriteUint32Pair(aWriter, SCTAG_DOM_FILELIST,
 | |
|                           aFileList->Length()) ||
 | |
|       !JS_WriteUint32Pair(aWriter, 0,
 | |
|                           aHolder->BlobImpls().Length())) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   nsTArray<RefPtr<BlobImpl>> blobImpls;
 | |
| 
 | |
|   for (uint32_t i = 0; i < aFileList->Length(); ++i) {
 | |
|     RefPtr<BlobImpl> blobImpl = aFileList->Item(i)->Impl();
 | |
|     MOZ_ALWAYS_SUCCEEDS(blobImpl->SetMutable(false));
 | |
|     blobImpls.AppendElement(blobImpl);
 | |
|   }
 | |
| 
 | |
|   aHolder->BlobImpls().AppendElements(blobImpls);
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| // Read the WriteFormData for the format.
 | |
| JSObject*
 | |
| ReadFormData(JSContext* aCx,
 | |
|              JSStructuredCloneReader* aReader,
 | |
|              uint32_t aCount,
 | |
|              StructuredCloneHolder* aHolder)
 | |
| {
 | |
|   MOZ_ASSERT(aCx);
 | |
|   MOZ_ASSERT(aReader);
 | |
|   MOZ_ASSERT(aHolder);
 | |
| 
 | |
|   // See the serialization of the FormData for the format.
 | |
|   JS::Rooted<JS::Value> val(aCx);
 | |
|   {
 | |
|     RefPtr<FormData> formData =
 | |
|       new FormData(aHolder->ParentDuringRead());
 | |
| 
 | |
|     Optional<nsAString> thirdArg;
 | |
|     for (uint32_t i = 0; i < aCount; ++i) {
 | |
|       nsAutoString name;
 | |
|       if (!ReadString(aReader, name)) {
 | |
|         return nullptr;
 | |
|       }
 | |
| 
 | |
|       uint32_t tag, indexOrLengthOfString;
 | |
|       if (!JS_ReadUint32Pair(aReader, &tag, &indexOrLengthOfString)) {
 | |
|         return nullptr;
 | |
|       }
 | |
| 
 | |
|       if (tag == SCTAG_DOM_BLOB) {
 | |
| #ifdef FUZZING
 | |
|         if (indexOrLengthOfString >= aHolder->BlobImpls().Length()) {
 | |
|           return nullptr;
 | |
|         }
 | |
| #endif
 | |
|         MOZ_ASSERT(indexOrLengthOfString < aHolder->BlobImpls().Length());
 | |
| 
 | |
|         RefPtr<BlobImpl> blobImpl =
 | |
|           aHolder->BlobImpls()[indexOrLengthOfString];
 | |
|         MOZ_ALWAYS_SUCCEEDS(blobImpl->SetMutable(false));
 | |
| 
 | |
|         RefPtr<Blob> blob =
 | |
|           Blob::Create(aHolder->ParentDuringRead(), blobImpl);
 | |
|         MOZ_ASSERT(blob);
 | |
| 
 | |
|         ErrorResult rv;
 | |
|         formData->Append(name, *blob, thirdArg, rv);
 | |
|         if (NS_WARN_IF(rv.Failed())) {
 | |
|           rv.SuppressException();
 | |
|           return nullptr;
 | |
|         }
 | |
| 
 | |
|       } else if (tag == SCTAG_DOM_DIRECTORY) {
 | |
|         RefPtr<Directory> directory =
 | |
|           ReadDirectoryInternal(aReader, indexOrLengthOfString, aHolder);
 | |
|         if (!directory) {
 | |
|           return nullptr;
 | |
|         }
 | |
| 
 | |
|         formData->Append(name, directory);
 | |
| 
 | |
|       } else {
 | |
|         MOZ_ASSERT(tag == 0);
 | |
| 
 | |
|         nsAutoString value;
 | |
|         if (NS_WARN_IF(!value.SetLength(indexOrLengthOfString, fallible))) {
 | |
|           return nullptr;
 | |
|         }
 | |
|         size_t charSize = sizeof(nsString::char_type);
 | |
|         if (!JS_ReadBytes(aReader, (void*) value.BeginWriting(),
 | |
|                           indexOrLengthOfString * charSize)) {
 | |
|           return nullptr;
 | |
|         }
 | |
| 
 | |
|         ErrorResult rv;
 | |
|         formData->Append(name, value, rv);
 | |
|         if (NS_WARN_IF(rv.Failed())) {
 | |
|           rv.SuppressException();
 | |
|           return nullptr;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     if (!ToJSValue(aCx, formData, &val)) {
 | |
|       return nullptr;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return &val.toObject();
 | |
| }
 | |
| 
 | |
| // The format of the FormData serialization is:
 | |
| // - pair of ints: SCTAG_DOM_FORMDATA, Length of the FormData elements
 | |
| // - for each Element element:
 | |
| //   - name string
 | |
| //   - if it's a blob:
 | |
| //     - pair of ints: SCTAG_DOM_BLOB, index of the BlobImpl in the array
 | |
| //       mBlobImplArray.
 | |
| //   - if it's a directory (See WriteDirectory):
 | |
| //     - pair of ints: SCTAG_DOM_DIRECTORY, path length
 | |
| //     - path as string
 | |
| //   - else:
 | |
| //     - pair of ints: 0, string length
 | |
| //     - value string
 | |
| bool
 | |
| WriteFormData(JSStructuredCloneWriter* aWriter,
 | |
|               FormData* aFormData,
 | |
|               StructuredCloneHolder* aHolder)
 | |
| {
 | |
|   MOZ_ASSERT(aWriter);
 | |
|   MOZ_ASSERT(aFormData);
 | |
|   MOZ_ASSERT(aHolder);
 | |
| 
 | |
|   if (!JS_WriteUint32Pair(aWriter, SCTAG_DOM_FORMDATA,
 | |
|                           aFormData->Length())) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   class MOZ_STACK_CLASS Closure final
 | |
|   {
 | |
|     JSStructuredCloneWriter* mWriter;
 | |
|     StructuredCloneHolder* mHolder;
 | |
| 
 | |
|   public:
 | |
|     Closure(JSStructuredCloneWriter* aWriter,
 | |
|             StructuredCloneHolder* aHolder)
 | |
|       : mWriter(aWriter),
 | |
|         mHolder(aHolder)
 | |
|     { }
 | |
| 
 | |
|     static bool
 | |
|     Write(const nsString& aName, const OwningBlobOrDirectoryOrUSVString& aValue,
 | |
|           void* aClosure)
 | |
|     {
 | |
|       Closure* closure = static_cast<Closure*>(aClosure);
 | |
|       if (!WriteString(closure->mWriter, aName)) {
 | |
|         return false;
 | |
|       }
 | |
| 
 | |
|       if (aValue.IsBlob()) {
 | |
|         if (!JS_WriteUint32Pair(closure->mWriter, SCTAG_DOM_BLOB,
 | |
|                                 closure->mHolder->BlobImpls().Length())) {
 | |
|           return false;
 | |
|         }
 | |
| 
 | |
|         RefPtr<BlobImpl> blobImpl = aValue.GetAsBlob()->Impl();
 | |
|         MOZ_ALWAYS_SUCCEEDS(blobImpl->SetMutable(false));
 | |
| 
 | |
|         closure->mHolder->BlobImpls().AppendElement(blobImpl);
 | |
|         return true;
 | |
|       }
 | |
| 
 | |
|       if (aValue.IsDirectory()) {
 | |
|         Directory* directory = aValue.GetAsDirectory();
 | |
|         return WriteDirectory(closure->mWriter, directory);
 | |
|       }
 | |
| 
 | |
|       size_t charSize = sizeof(nsString::char_type);
 | |
|       if (!JS_WriteUint32Pair(closure->mWriter, 0,
 | |
|                               aValue.GetAsUSVString().Length()) ||
 | |
|           !JS_WriteBytes(closure->mWriter, aValue.GetAsUSVString().get(),
 | |
|                          aValue.GetAsUSVString().Length() * charSize)) {
 | |
|         return false;
 | |
|       }
 | |
| 
 | |
|       return true;
 | |
|     }
 | |
|   };
 | |
|   Closure closure(aWriter, aHolder);
 | |
|   return aFormData->ForEach(Closure::Write, &closure);
 | |
| }
 | |
| 
 | |
| JSObject*
 | |
| ReadWasmModule(JSContext* aCx,
 | |
|                uint32_t aIndex,
 | |
|                StructuredCloneHolder* aHolder)
 | |
| {
 | |
|   MOZ_ASSERT(aHolder);
 | |
|   MOZ_ASSERT(aHolder->CloneScope() == StructuredCloneHolder::StructuredCloneScope::SameProcessSameThread ||
 | |
|              aHolder->CloneScope() == StructuredCloneHolder::StructuredCloneScope::SameProcessDifferentThread);
 | |
| #ifdef FUZZING
 | |
|   if (aIndex >= aHolder->WasmModules().Length()) {
 | |
|     return nullptr;
 | |
|   }
 | |
| #endif
 | |
|   MOZ_ASSERT(aIndex < aHolder->WasmModules().Length());
 | |
| 
 | |
|   return aHolder->WasmModules()[aIndex]->createObject(aCx);
 | |
| }
 | |
| 
 | |
| bool
 | |
| WriteWasmModule(JSStructuredCloneWriter* aWriter,
 | |
|                 JS::WasmModule* aWasmModule,
 | |
|                 StructuredCloneHolder* aHolder)
 | |
| {
 | |
|   MOZ_ASSERT(aWriter);
 | |
|   MOZ_ASSERT(aWasmModule);
 | |
|   MOZ_ASSERT(aHolder);
 | |
|   MOZ_ASSERT(aHolder->CloneScope() == StructuredCloneHolder::StructuredCloneScope::SameProcessSameThread ||
 | |
|              aHolder->CloneScope() == StructuredCloneHolder::StructuredCloneScope::SameProcessDifferentThread);
 | |
| 
 | |
|   // We store the position of the wasmModule in the array as index.
 | |
|   if (JS_WriteUint32Pair(aWriter, SCTAG_DOM_WASM,
 | |
|                          aHolder->WasmModules().Length())) {
 | |
|     aHolder->WasmModules().AppendElement(aWasmModule);
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| JSObject*
 | |
| ReadInputStream(JSContext* aCx,
 | |
|                 uint32_t aIndex,
 | |
|                 StructuredCloneHolder* aHolder)
 | |
| {
 | |
|   MOZ_ASSERT(aHolder);
 | |
| #ifdef FUZZING
 | |
|   if (aIndex >= aHolder->InputStreams().Length()) {
 | |
|     return nullptr;
 | |
|   }
 | |
| #endif
 | |
|   MOZ_ASSERT(aIndex < aHolder->InputStreams().Length());
 | |
|   nsCOMPtr<nsIInputStream> inputStream = aHolder->InputStreams()[aIndex];
 | |
| 
 | |
|   JS::RootedValue result(aCx);
 | |
|   nsresult rv = nsContentUtils::WrapNative(aCx, inputStream,
 | |
|                                            &NS_GET_IID(nsIInputStream),
 | |
|                                            &result);
 | |
|   if (NS_FAILED(rv)) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   return &result.toObject();
 | |
| }
 | |
| 
 | |
| bool
 | |
| WriteInputStream(JSStructuredCloneWriter* aWriter,
 | |
|                  nsIInputStream* aInputStream,
 | |
|                  StructuredCloneHolder* aHolder)
 | |
| {
 | |
|   MOZ_ASSERT(aWriter);
 | |
|   MOZ_ASSERT(aInputStream);
 | |
|   MOZ_ASSERT(aHolder);
 | |
| 
 | |
|   // We store the position of the inputStream in the array as index.
 | |
|   if (JS_WriteUint32Pair(aWriter, SCTAG_DOM_INPUTSTREAM,
 | |
|                          aHolder->InputStreams().Length())) {
 | |
|     aHolder->InputStreams().AppendElement(aInputStream);
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| } // anonymous namespace
 | |
| 
 | |
| JSObject*
 | |
| StructuredCloneHolder::CustomReadHandler(JSContext* aCx,
 | |
|                                          JSStructuredCloneReader* aReader,
 | |
|                                          uint32_t aTag,
 | |
|                                          uint32_t aIndex)
 | |
| {
 | |
|   MOZ_ASSERT(mSupportsCloning);
 | |
| 
 | |
|   if (aTag == SCTAG_DOM_BLOB) {
 | |
|     return ReadBlob(aCx, aIndex, this);
 | |
|   }
 | |
| 
 | |
|   if (aTag == SCTAG_DOM_DIRECTORY) {
 | |
|     return ReadDirectory(aCx, aReader, aIndex, this);
 | |
|   }
 | |
| 
 | |
|   if (aTag == SCTAG_DOM_FILELIST) {
 | |
|     return ReadFileList(aCx, aReader, aIndex, this);
 | |
|   }
 | |
| 
 | |
|   if (aTag == SCTAG_DOM_FORMDATA) {
 | |
|     return ReadFormData(aCx, aReader, aIndex, this);
 | |
|   }
 | |
| 
 | |
|   if (aTag == SCTAG_DOM_IMAGEBITMAP &&
 | |
|       (mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread ||
 | |
|        mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread)) {
 | |
| 
 | |
|     // Get the current global object.
 | |
|     // This can be null.
 | |
|     nsCOMPtr<nsIGlobalObject> parent = do_QueryInterface(mParent);
 | |
|     // aIndex is the index of the cloned image.
 | |
|     return ImageBitmap::ReadStructuredClone(aCx, aReader,
 | |
|                                             parent, GetSurfaces(), aIndex);
 | |
|   }
 | |
| 
 | |
|   if (aTag == SCTAG_DOM_STRUCTURED_CLONE_HOLDER) {
 | |
|     return StructuredCloneBlob::ReadStructuredClone(aCx, aReader, this);
 | |
|   }
 | |
| 
 | |
|   if (aTag == SCTAG_DOM_WASM &&
 | |
|       (mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread ||
 | |
|        mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread)) {
 | |
|     return ReadWasmModule(aCx, aIndex, this);
 | |
|   }
 | |
| 
 | |
|   if (aTag == SCTAG_DOM_INPUTSTREAM) {
 | |
|     return ReadInputStream(aCx, aIndex, this);
 | |
|   }
 | |
| 
 | |
|   return ReadFullySerializableObjects(aCx, aReader, aTag);
 | |
| }
 | |
| 
 | |
| bool
 | |
| StructuredCloneHolder::CustomWriteHandler(JSContext* aCx,
 | |
|                                           JSStructuredCloneWriter* aWriter,
 | |
|                                           JS::Handle<JSObject*> aObj)
 | |
| {
 | |
|   if (!mSupportsCloning) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   JS::Rooted<JSObject*> obj(aCx, aObj);
 | |
| 
 | |
|   // See if this is a File/Blob object.
 | |
|   {
 | |
|     Blob* blob = nullptr;
 | |
|     if (NS_SUCCEEDED(UNWRAP_OBJECT(Blob, &obj, blob))) {
 | |
|       return WriteBlob(aWriter, blob, this);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // See if this is a Directory object.
 | |
|   {
 | |
|     Directory* directory = nullptr;
 | |
|     if (NS_SUCCEEDED(UNWRAP_OBJECT(Directory, &obj, directory))) {
 | |
|       return WriteDirectory(aWriter, directory);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // See if this is a FileList object.
 | |
|   {
 | |
|     FileList* fileList = nullptr;
 | |
|     if (NS_SUCCEEDED(UNWRAP_OBJECT(FileList, &obj, fileList))) {
 | |
|       return WriteFileList(aWriter, fileList, this);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // See if this is a FormData object.
 | |
|   {
 | |
|     FormData* formData = nullptr;
 | |
|     if (NS_SUCCEEDED(UNWRAP_OBJECT(FormData, &obj, formData))) {
 | |
|       return WriteFormData(aWriter, formData, this);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // See if this is an ImageBitmap object.
 | |
|   if (mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread ||
 | |
|       mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread) {
 | |
|     ImageBitmap* imageBitmap = nullptr;
 | |
|     if (NS_SUCCEEDED(UNWRAP_OBJECT(ImageBitmap, &obj, imageBitmap))) {
 | |
|       return ImageBitmap::WriteStructuredClone(aWriter,
 | |
|                                                GetSurfaces(),
 | |
|                                                imageBitmap);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // See if this is a StructuredCloneBlob object.
 | |
|   {
 | |
|     StructuredCloneBlob* holder = nullptr;
 | |
|     if (NS_SUCCEEDED(UNWRAP_OBJECT(StructuredCloneHolder, &obj, holder))) {
 | |
|       return holder->WriteStructuredClone(aCx, aWriter, this);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // See if this is a WasmModule.
 | |
|   if ((mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread ||
 | |
|        mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread) &&
 | |
|       JS::IsWasmModuleObject(obj)) {
 | |
|     RefPtr<JS::WasmModule> module = JS::GetWasmModule(obj);
 | |
|     MOZ_ASSERT(module);
 | |
| 
 | |
|     return WriteWasmModule(aWriter, module, this);
 | |
|   }
 | |
| 
 | |
|   {
 | |
|     nsCOMPtr<nsISupports> base = xpc::UnwrapReflectorToISupports(aObj);
 | |
|     nsCOMPtr<nsIInputStream> inputStream = do_QueryInterface(base);
 | |
|     if (inputStream) {
 | |
|       return WriteInputStream(aWriter, inputStream, this);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return WriteFullySerializableObjects(aCx, aWriter, aObj);
 | |
| }
 | |
| 
 | |
| bool
 | |
| StructuredCloneHolder::CustomReadTransferHandler(JSContext* aCx,
 | |
|                                                  JSStructuredCloneReader* aReader,
 | |
|                                                  uint32_t aTag,
 | |
|                                                  void* aContent,
 | |
|                                                  uint64_t aExtraData,
 | |
|                                                  JS::MutableHandleObject aReturnObject)
 | |
| {
 | |
|   MOZ_ASSERT(mSupportsTransferring);
 | |
| 
 | |
|   if (aTag == SCTAG_DOM_MAP_MESSAGEPORT) {
 | |
| #ifdef FUZZING
 | |
|     if (aExtraData >= mPortIdentifiers.Length()) {
 | |
|       return false;
 | |
|     }
 | |
| #endif
 | |
|     MOZ_ASSERT(aExtraData < mPortIdentifiers.Length());
 | |
|     const MessagePortIdentifier& portIdentifier = mPortIdentifiers[aExtraData];
 | |
| 
 | |
|     nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(mParent);
 | |
| 
 | |
|     ErrorResult rv;
 | |
|     RefPtr<MessagePort> port =
 | |
|       MessagePort::Create(global, portIdentifier, rv);
 | |
|     if (NS_WARN_IF(rv.Failed())) {
 | |
|       rv.SuppressException();
 | |
|       return false;
 | |
|     }
 | |
| 
 | |
|     mTransferredPorts.AppendElement(port);
 | |
| 
 | |
|     JS::Rooted<JS::Value> value(aCx);
 | |
|     if (!GetOrCreateDOMReflector(aCx, port, &value)) {
 | |
|       JS_ClearPendingException(aCx);
 | |
|       return false;
 | |
|     }
 | |
| 
 | |
|     aReturnObject.set(&value.toObject());
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   if (aTag == SCTAG_DOM_CANVAS &&
 | |
|       (mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread ||
 | |
|        mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread)) {
 | |
|     MOZ_ASSERT(aContent);
 | |
|     OffscreenCanvasCloneData* data =
 | |
|       static_cast<OffscreenCanvasCloneData*>(aContent);
 | |
|     nsCOMPtr<nsIGlobalObject> parent = do_QueryInterface(mParent);
 | |
|     RefPtr<OffscreenCanvas> canvas = OffscreenCanvas::CreateFromCloneData(parent, data);
 | |
|     delete data;
 | |
| 
 | |
|     JS::Rooted<JS::Value> value(aCx);
 | |
|     if (!GetOrCreateDOMReflector(aCx, canvas, &value)) {
 | |
|       JS_ClearPendingException(aCx);
 | |
|       return false;
 | |
|     }
 | |
| 
 | |
|     aReturnObject.set(&value.toObject());
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   if (aTag == SCTAG_DOM_IMAGEBITMAP &&
 | |
|       (mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread ||
 | |
|        mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread)) {
 | |
|     MOZ_ASSERT(aContent);
 | |
|     ImageBitmapCloneData* data =
 | |
|       static_cast<ImageBitmapCloneData*>(aContent);
 | |
|     nsCOMPtr<nsIGlobalObject> parent = do_QueryInterface(mParent);
 | |
|     RefPtr<ImageBitmap> bitmap = ImageBitmap::CreateFromCloneData(parent, data);
 | |
|     delete data;
 | |
| 
 | |
|     JS::Rooted<JS::Value> value(aCx);
 | |
|     if (!GetOrCreateDOMReflector(aCx, bitmap, &value)) {
 | |
|       JS_ClearPendingException(aCx);
 | |
|       return false;
 | |
|     }
 | |
| 
 | |
|     aReturnObject.set(&value.toObject());
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| bool
 | |
| StructuredCloneHolder::CustomWriteTransferHandler(JSContext* aCx,
 | |
|                                                   JS::Handle<JSObject*> aObj,
 | |
|                                                   uint32_t* aTag,
 | |
|                                                   JS::TransferableOwnership* aOwnership,
 | |
|                                                   void** aContent,
 | |
|                                                   uint64_t* aExtraData)
 | |
| {
 | |
|   if (!mSupportsTransferring) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   JS::Rooted<JSObject*> obj(aCx, aObj);
 | |
| 
 | |
|   {
 | |
|     MessagePort* port = nullptr;
 | |
|     nsresult rv = UNWRAP_OBJECT(MessagePort, &obj, port);
 | |
|     if (NS_SUCCEEDED(rv)) {
 | |
|       // We use aExtraData to store the index of this new port identifier.
 | |
|       *aExtraData = mPortIdentifiers.Length();
 | |
|       MessagePortIdentifier* identifier = mPortIdentifiers.AppendElement();
 | |
| 
 | |
|       if (!port->CanBeCloned()) {
 | |
|         return false;
 | |
|       }
 | |
| 
 | |
|       port->CloneAndDisentangle(*identifier);
 | |
| 
 | |
|       *aTag = SCTAG_DOM_MAP_MESSAGEPORT;
 | |
|       *aOwnership = JS::SCTAG_TMO_CUSTOM;
 | |
|       *aContent = nullptr;
 | |
| 
 | |
|       return true;
 | |
|     }
 | |
| 
 | |
|     if (mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread ||
 | |
|         mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread) {
 | |
|       OffscreenCanvas* canvas = nullptr;
 | |
|       rv = UNWRAP_OBJECT(OffscreenCanvas, &obj, canvas);
 | |
|       if (NS_SUCCEEDED(rv)) {
 | |
|         MOZ_ASSERT(canvas);
 | |
| 
 | |
|         if (canvas->IsNeutered()) {
 | |
|           return false;
 | |
|         }
 | |
| 
 | |
|         *aExtraData = 0;
 | |
|         *aTag = SCTAG_DOM_CANVAS;
 | |
|         *aOwnership = JS::SCTAG_TMO_CUSTOM;
 | |
|         *aContent = canvas->ToCloneData();
 | |
|         MOZ_ASSERT(*aContent);
 | |
|         canvas->SetNeutered();
 | |
| 
 | |
|         return true;
 | |
|       }
 | |
| 
 | |
|       ImageBitmap* bitmap = nullptr;
 | |
|       rv = UNWRAP_OBJECT(ImageBitmap, &obj, bitmap);
 | |
|       if (NS_SUCCEEDED(rv)) {
 | |
|         MOZ_ASSERT(bitmap);
 | |
| 
 | |
|         *aExtraData = 0;
 | |
|         *aTag = SCTAG_DOM_IMAGEBITMAP;
 | |
|         *aOwnership = JS::SCTAG_TMO_CUSTOM;
 | |
| 
 | |
|         UniquePtr<ImageBitmapCloneData> clonedBitmap = bitmap->ToCloneData();
 | |
|         if (!clonedBitmap) {
 | |
|           return false;
 | |
|         }
 | |
| 
 | |
|         *aContent = clonedBitmap.release();
 | |
|         MOZ_ASSERT(*aContent);
 | |
|         bitmap->Close();
 | |
| 
 | |
|         return true;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| void
 | |
| StructuredCloneHolder::CustomFreeTransferHandler(uint32_t aTag,
 | |
|                                                  JS::TransferableOwnership aOwnership,
 | |
|                                                  void* aContent,
 | |
|                                                  uint64_t aExtraData)
 | |
| {
 | |
|   MOZ_ASSERT(mSupportsTransferring);
 | |
| 
 | |
|   if (aTag == SCTAG_DOM_MAP_MESSAGEPORT) {
 | |
|     MOZ_ASSERT(!aContent);
 | |
| #ifdef FUZZING
 | |
|     if (aExtraData >= mPortIdentifiers.Length()) {
 | |
|       return;
 | |
|     }
 | |
| #endif
 | |
|     MOZ_ASSERT(aExtraData < mPortIdentifiers.Length());
 | |
|     MessagePort::ForceClose(mPortIdentifiers[aExtraData]);
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   if (aTag == SCTAG_DOM_CANVAS &&
 | |
|       (mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread ||
 | |
|        mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread)) {
 | |
|     MOZ_ASSERT(aContent);
 | |
|     OffscreenCanvasCloneData* data =
 | |
|       static_cast<OffscreenCanvasCloneData*>(aContent);
 | |
|     delete data;
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   if (aTag == SCTAG_DOM_IMAGEBITMAP &&
 | |
|       (mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread ||
 | |
|        mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread)) {
 | |
|     MOZ_ASSERT(aContent);
 | |
|     ImageBitmapCloneData* data =
 | |
|       static_cast<ImageBitmapCloneData*>(aContent);
 | |
|     delete data;
 | |
|     return;
 | |
|   }
 | |
| }
 | |
| 
 | |
| bool
 | |
| StructuredCloneHolder::CustomCanTransferHandler(JSContext* aCx,
 | |
|                                                 JS::Handle<JSObject*> aObj)
 | |
| {
 | |
|   if (!mSupportsTransferring) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   JS::Rooted<JSObject*> obj(aCx, aObj);
 | |
| 
 | |
|   {
 | |
|     MessagePort* port = nullptr;
 | |
|     nsresult rv = UNWRAP_OBJECT(MessagePort, &obj, port);
 | |
|     if (NS_SUCCEEDED(rv)) {
 | |
|       return true;
 | |
|     }
 | |
| 
 | |
|     if (mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread ||
 | |
|         mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread) {
 | |
|       OffscreenCanvas* canvas = nullptr;
 | |
|       rv = UNWRAP_OBJECT(OffscreenCanvas, &obj, canvas);
 | |
|       if (NS_SUCCEEDED(rv)) {
 | |
|         return true;
 | |
|       }
 | |
| 
 | |
|       ImageBitmap* bitmap = nullptr;
 | |
|       rv = UNWRAP_OBJECT(ImageBitmap, &obj, bitmap);
 | |
|       if (NS_SUCCEEDED(rv)) {
 | |
|         return true;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| bool
 | |
| StructuredCloneHolder::TakeTransferredPortsAsSequence(Sequence<OwningNonNull<mozilla::dom::MessagePort>>& aPorts)
 | |
| {
 | |
|   nsTArray<RefPtr<MessagePort>> ports = TakeTransferredPorts();
 | |
| 
 | |
|   aPorts.Clear();
 | |
|   for (uint32_t i = 0, len = ports.Length(); i < len; ++i) {
 | |
|     if (!aPorts.AppendElement(ports[i].forget(), fallible)) {
 | |
|       return false;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| } // dom namespace
 | |
| } // mozilla namespace
 | 
