forked from mirrors/gecko-dev
		
	This is the first use of IPCBlob: ClonedMessageData. ClonedMessageData is used for BroadcastChannel, MessagePort and any postMessage() communication. This patch changes StructuredCloneData in order to use IPCBlob instead of PBlob. BroadcastChannel has a custom way to manage Blobs because when the parent receives them from a content process, it must send them to any other BroadcastChild actor duplicating the serialization.
		
			
				
	
	
		
			468 lines
		
	
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			468 lines
		
	
	
	
		
			13 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 "StructuredCloneData.h"
 | 
						|
 | 
						|
#include "nsIDOMDOMException.h"
 | 
						|
#include "nsIMutable.h"
 | 
						|
#include "nsIXPConnect.h"
 | 
						|
 | 
						|
#include "ipc/IPCMessageUtils.h"
 | 
						|
#include "mozilla/dom/BindingUtils.h"
 | 
						|
#include "mozilla/dom/BlobBinding.h"
 | 
						|
#include "mozilla/dom/IPCBlobUtils.h"
 | 
						|
#include "mozilla/dom/File.h"
 | 
						|
#include "mozilla/ipc/IPCStreamUtils.h"
 | 
						|
#include "nsContentUtils.h"
 | 
						|
#include "nsJSEnvironment.h"
 | 
						|
#include "MainThreadUtils.h"
 | 
						|
#include "StructuredCloneTags.h"
 | 
						|
#include "jsapi.h"
 | 
						|
 | 
						|
namespace mozilla {
 | 
						|
namespace dom {
 | 
						|
namespace ipc {
 | 
						|
 | 
						|
StructuredCloneData::StructuredCloneData()
 | 
						|
  : StructuredCloneData(StructuredCloneHolder::TransferringSupported)
 | 
						|
{}
 | 
						|
 | 
						|
StructuredCloneData::StructuredCloneData(StructuredCloneData&& aOther)
 | 
						|
  : StructuredCloneData(StructuredCloneHolder::TransferringSupported)
 | 
						|
{
 | 
						|
  *this = Move(aOther);
 | 
						|
}
 | 
						|
 | 
						|
StructuredCloneData::StructuredCloneData(TransferringSupport aSupportsTransferring)
 | 
						|
  : StructuredCloneHolder(StructuredCloneHolder::CloningSupported,
 | 
						|
                          aSupportsTransferring,
 | 
						|
                          StructuredCloneHolder::StructuredCloneScope::DifferentProcess)
 | 
						|
  , mInitialized(false)
 | 
						|
{}
 | 
						|
 | 
						|
StructuredCloneData::~StructuredCloneData()
 | 
						|
{}
 | 
						|
 | 
						|
StructuredCloneData&
 | 
						|
StructuredCloneData::operator=(StructuredCloneData&& aOther)
 | 
						|
{
 | 
						|
  mExternalData = Move(aOther.mExternalData);
 | 
						|
  mSharedData = Move(aOther.mSharedData);
 | 
						|
  mIPCStreams = Move(aOther.mIPCStreams);
 | 
						|
  mInitialized = aOther.mInitialized;
 | 
						|
 | 
						|
  return *this;
 | 
						|
}
 | 
						|
 | 
						|
bool
 | 
						|
StructuredCloneData::Copy(const StructuredCloneData& aData)
 | 
						|
{
 | 
						|
  if (!aData.mInitialized) {
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
 | 
						|
  if (aData.SharedData()) {
 | 
						|
    mSharedData = aData.SharedData();
 | 
						|
  } else {
 | 
						|
    mSharedData =
 | 
						|
      SharedJSAllocatedData::CreateFromExternalData(aData.Data());
 | 
						|
    NS_ENSURE_TRUE(mSharedData, false);
 | 
						|
  }
 | 
						|
 | 
						|
  if (mSupportsTransferring) {
 | 
						|
    PortIdentifiers().AppendElements(aData.PortIdentifiers());
 | 
						|
  }
 | 
						|
 | 
						|
  MOZ_ASSERT(BlobImpls().IsEmpty());
 | 
						|
  BlobImpls().AppendElements(aData.BlobImpls());
 | 
						|
 | 
						|
  MOZ_ASSERT(GetSurfaces().IsEmpty());
 | 
						|
  MOZ_ASSERT(WasmModules().IsEmpty());
 | 
						|
 | 
						|
  MOZ_ASSERT(InputStreams().IsEmpty());
 | 
						|
  InputStreams().AppendElements(aData.InputStreams());
 | 
						|
 | 
						|
  mInitialized = true;
 | 
						|
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
StructuredCloneData::Read(JSContext* aCx,
 | 
						|
                          JS::MutableHandle<JS::Value> aValue,
 | 
						|
                          ErrorResult &aRv)
 | 
						|
{
 | 
						|
  MOZ_ASSERT(mInitialized);
 | 
						|
 | 
						|
  nsIGlobalObject *global = xpc::NativeGlobal(JS::CurrentGlobalOrNull(aCx));
 | 
						|
  MOZ_ASSERT(global);
 | 
						|
 | 
						|
  ReadFromBuffer(global, aCx, Data(), aValue, aRv);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
StructuredCloneData::Write(JSContext* aCx,
 | 
						|
                           JS::Handle<JS::Value> aValue,
 | 
						|
                           ErrorResult &aRv)
 | 
						|
{
 | 
						|
  Write(aCx, aValue, JS::UndefinedHandleValue, aRv);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
StructuredCloneData::Write(JSContext* aCx,
 | 
						|
                           JS::Handle<JS::Value> aValue,
 | 
						|
                           JS::Handle<JS::Value> aTransfer,
 | 
						|
                           ErrorResult &aRv)
 | 
						|
{
 | 
						|
  MOZ_ASSERT(!mInitialized);
 | 
						|
 | 
						|
  StructuredCloneHolder::Write(aCx, aValue, aTransfer,
 | 
						|
                               JS::CloneDataPolicy().denySharedArrayBuffer(), aRv);
 | 
						|
  if (NS_WARN_IF(aRv.Failed())) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  JSStructuredCloneData data;
 | 
						|
  mBuffer->abandon();
 | 
						|
  mBuffer->steal(&data);
 | 
						|
  mBuffer = nullptr;
 | 
						|
  mSharedData = new SharedJSAllocatedData(Move(data));
 | 
						|
  mInitialized = true;
 | 
						|
}
 | 
						|
 | 
						|
enum ActorFlavorEnum {
 | 
						|
  Parent = 0,
 | 
						|
  Child,
 | 
						|
};
 | 
						|
 | 
						|
enum ManagerFlavorEnum {
 | 
						|
  ContentProtocol = 0,
 | 
						|
  BackgroundProtocol
 | 
						|
};
 | 
						|
 | 
						|
template<typename M>
 | 
						|
bool
 | 
						|
BuildClonedMessageData(M* aManager, StructuredCloneData& aData,
 | 
						|
                       ClonedMessageData& aClonedData)
 | 
						|
{
 | 
						|
  SerializedStructuredCloneBuffer& buffer = aClonedData.data();
 | 
						|
  auto iter = aData.Data().Iter();
 | 
						|
  size_t size = aData.Data().Size();
 | 
						|
  bool success;
 | 
						|
  buffer.data = aData.Data().Borrow<js::SystemAllocPolicy>(iter, size, &success);
 | 
						|
  if (NS_WARN_IF(!success)) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
  if (aData.SupportsTransferring()) {
 | 
						|
    aClonedData.identfiers().AppendElements(aData.PortIdentifiers());
 | 
						|
  }
 | 
						|
 | 
						|
  const nsTArray<RefPtr<BlobImpl>>& blobImpls = aData.BlobImpls();
 | 
						|
 | 
						|
  if (!blobImpls.IsEmpty()) {
 | 
						|
    if (NS_WARN_IF(!aClonedData.blobs().SetLength(blobImpls.Length(), fallible))) {
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
 | 
						|
    for (uint32_t i = 0; i < blobImpls.Length(); ++i) {
 | 
						|
      nsresult rv = IPCBlobUtils::Serialize(blobImpls[i], aManager,
 | 
						|
                                            aClonedData.blobs()[i]);
 | 
						|
      if (NS_WARN_IF(NS_FAILED(rv))) {
 | 
						|
        return false;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  const nsTArray<nsCOMPtr<nsIInputStream>>& inputStreams = aData.InputStreams();
 | 
						|
  if (!inputStreams.IsEmpty()) {
 | 
						|
    if (NS_WARN_IF(!aData.IPCStreams().SetCapacity(inputStreams.Length(),
 | 
						|
                                                   fallible))) {
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
 | 
						|
    InfallibleTArray<IPCStream>& streams = aClonedData.inputStreams();
 | 
						|
    uint32_t length = inputStreams.Length();
 | 
						|
    streams.SetCapacity(length);
 | 
						|
    for (uint32_t i = 0; i < length; ++i) {
 | 
						|
      AutoIPCStream* stream = aData.IPCStreams().AppendElement(fallible);
 | 
						|
      if (NS_WARN_IF(!stream)) {
 | 
						|
        return false;
 | 
						|
      }
 | 
						|
 | 
						|
      if (!stream->Serialize(inputStreams[i], aManager)) {
 | 
						|
        return false;
 | 
						|
      }
 | 
						|
      streams.AppendElement(stream->TakeValue());
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
bool
 | 
						|
StructuredCloneData::BuildClonedMessageDataForParent(
 | 
						|
  nsIContentParent* aParent,
 | 
						|
  ClonedMessageData& aClonedData)
 | 
						|
{
 | 
						|
  return BuildClonedMessageData(aParent, *this, aClonedData);
 | 
						|
}
 | 
						|
 | 
						|
bool
 | 
						|
StructuredCloneData::BuildClonedMessageDataForChild(
 | 
						|
  nsIContentChild* aChild,
 | 
						|
  ClonedMessageData& aClonedData)
 | 
						|
{
 | 
						|
  return BuildClonedMessageData(aChild, *this, aClonedData);
 | 
						|
}
 | 
						|
 | 
						|
bool
 | 
						|
StructuredCloneData::BuildClonedMessageDataForBackgroundParent(
 | 
						|
  PBackgroundParent* aParent,
 | 
						|
  ClonedMessageData& aClonedData)
 | 
						|
{
 | 
						|
  return BuildClonedMessageData(aParent, *this, aClonedData);
 | 
						|
}
 | 
						|
 | 
						|
bool
 | 
						|
StructuredCloneData::BuildClonedMessageDataForBackgroundChild(
 | 
						|
  PBackgroundChild* aChild,
 | 
						|
  ClonedMessageData& aClonedData)
 | 
						|
{
 | 
						|
  return BuildClonedMessageData(aChild, *this, aClonedData);
 | 
						|
}
 | 
						|
 | 
						|
// See the StructuredCloneData class block comment for the meanings of each val.
 | 
						|
enum MemoryFlavorEnum {
 | 
						|
  BorrowMemory = 0,
 | 
						|
  CopyMemory,
 | 
						|
  StealMemory
 | 
						|
};
 | 
						|
 | 
						|
template<MemoryFlavorEnum>
 | 
						|
struct MemoryTraits
 | 
						|
{ };
 | 
						|
 | 
						|
template<>
 | 
						|
struct MemoryTraits<BorrowMemory>
 | 
						|
{
 | 
						|
  typedef const mozilla::dom::ClonedMessageData ClonedMessageType;
 | 
						|
 | 
						|
  static void ProvideBuffer(const ClonedMessageData& aClonedData,
 | 
						|
                            StructuredCloneData& aData)
 | 
						|
  {
 | 
						|
    const SerializedStructuredCloneBuffer& buffer = aClonedData.data();
 | 
						|
    aData.UseExternalData(buffer.data);
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
template<>
 | 
						|
struct MemoryTraits<CopyMemory>
 | 
						|
{
 | 
						|
  typedef const mozilla::dom::ClonedMessageData ClonedMessageType;
 | 
						|
 | 
						|
  static void ProvideBuffer(const ClonedMessageData& aClonedData,
 | 
						|
                            StructuredCloneData& aData)
 | 
						|
  {
 | 
						|
    const SerializedStructuredCloneBuffer& buffer = aClonedData.data();
 | 
						|
    aData.CopyExternalData(buffer.data);
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
template<>
 | 
						|
struct MemoryTraits<StealMemory>
 | 
						|
{
 | 
						|
  // note: not const!
 | 
						|
  typedef mozilla::dom::ClonedMessageData ClonedMessageType;
 | 
						|
 | 
						|
  static void ProvideBuffer(ClonedMessageData& aClonedData,
 | 
						|
                            StructuredCloneData& aData)
 | 
						|
  {
 | 
						|
    SerializedStructuredCloneBuffer& buffer = aClonedData.data();
 | 
						|
    aData.StealExternalData(buffer.data);
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
// Note that there isn't actually a difference between Parent/BackgroundParent
 | 
						|
// and Child/BackgroundChild in this implementation.  The calling methods,
 | 
						|
// however, do maintain the distinction for code-reading purposes and are backed
 | 
						|
// by assertions to enforce there is no misuse.
 | 
						|
template<MemoryFlavorEnum MemoryFlavor, ActorFlavorEnum Flavor>
 | 
						|
static void
 | 
						|
UnpackClonedMessageData(typename MemoryTraits<MemoryFlavor>::ClonedMessageType& aClonedData,
 | 
						|
                        StructuredCloneData& aData)
 | 
						|
{
 | 
						|
  const InfallibleTArray<MessagePortIdentifier>& identifiers = aClonedData.identfiers();
 | 
						|
 | 
						|
  MemoryTraits<MemoryFlavor>::ProvideBuffer(aClonedData, aData);
 | 
						|
 | 
						|
  if (aData.SupportsTransferring()) {
 | 
						|
    aData.PortIdentifiers().AppendElements(identifiers);
 | 
						|
  }
 | 
						|
 | 
						|
  const nsTArray<IPCBlob>& blobs = aClonedData.blobs();
 | 
						|
  if (!blobs.IsEmpty()) {
 | 
						|
    uint32_t length = blobs.Length();
 | 
						|
    aData.BlobImpls().SetCapacity(length);
 | 
						|
    for (uint32_t i = 0; i < length; ++i) {
 | 
						|
      RefPtr<BlobImpl> blobImpl = IPCBlobUtils::Deserialize(blobs[i]);
 | 
						|
      MOZ_ASSERT(blobImpl);
 | 
						|
 | 
						|
      aData.BlobImpls().AppendElement(blobImpl);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  const InfallibleTArray<IPCStream>& streams = aClonedData.inputStreams();
 | 
						|
  if (!streams.IsEmpty()) {
 | 
						|
    uint32_t length = streams.Length();
 | 
						|
    aData.InputStreams().SetCapacity(length);
 | 
						|
    for (uint32_t i = 0; i < length; ++i) {
 | 
						|
      nsCOMPtr<nsIInputStream> stream = DeserializeIPCStream(streams[i]);
 | 
						|
      aData.InputStreams().AppendElement(stream);
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
StructuredCloneData::BorrowFromClonedMessageDataForParent(const ClonedMessageData& aClonedData)
 | 
						|
{
 | 
						|
  // PContent parent is always main thread and actor constraints demand we're
 | 
						|
  // likewise on that thread.
 | 
						|
  MOZ_ASSERT(NS_IsMainThread());
 | 
						|
  UnpackClonedMessageData<BorrowMemory, Parent>(aClonedData, *this);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
StructuredCloneData::BorrowFromClonedMessageDataForChild(const ClonedMessageData& aClonedData)
 | 
						|
{
 | 
						|
  // PContent child is always main thread and actor constraints demand we're
 | 
						|
  // likewise on that thread.
 | 
						|
  MOZ_ASSERT(NS_IsMainThread());
 | 
						|
  UnpackClonedMessageData<BorrowMemory, Child>(aClonedData, *this);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
StructuredCloneData::BorrowFromClonedMessageDataForBackgroundParent(const ClonedMessageData& aClonedData)
 | 
						|
{
 | 
						|
  MOZ_ASSERT(IsOnBackgroundThread());
 | 
						|
  UnpackClonedMessageData<BorrowMemory, Parent>(aClonedData, *this);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
StructuredCloneData::BorrowFromClonedMessageDataForBackgroundChild(const ClonedMessageData& aClonedData)
 | 
						|
{
 | 
						|
  // No thread assertion; BackgroundChild can happen on any thread.
 | 
						|
  UnpackClonedMessageData<BorrowMemory, Child>(aClonedData, *this);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
StructuredCloneData::CopyFromClonedMessageDataForParent(const ClonedMessageData& aClonedData)
 | 
						|
{
 | 
						|
  MOZ_ASSERT(NS_IsMainThread());
 | 
						|
  UnpackClonedMessageData<CopyMemory, Parent>(aClonedData, *this);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
StructuredCloneData::CopyFromClonedMessageDataForChild(const ClonedMessageData& aClonedData)
 | 
						|
{
 | 
						|
  MOZ_ASSERT(NS_IsMainThread());
 | 
						|
  UnpackClonedMessageData<CopyMemory, Child>(aClonedData, *this);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
StructuredCloneData::CopyFromClonedMessageDataForBackgroundParent(const ClonedMessageData& aClonedData)
 | 
						|
{
 | 
						|
  MOZ_ASSERT(IsOnBackgroundThread());
 | 
						|
  UnpackClonedMessageData<BorrowMemory, Parent>(aClonedData, *this);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
StructuredCloneData::CopyFromClonedMessageDataForBackgroundChild(const ClonedMessageData& aClonedData)
 | 
						|
{
 | 
						|
  UnpackClonedMessageData<CopyMemory, Child>(aClonedData, *this);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
StructuredCloneData::StealFromClonedMessageDataForParent(ClonedMessageData& aClonedData)
 | 
						|
{
 | 
						|
  MOZ_ASSERT(NS_IsMainThread());
 | 
						|
  UnpackClonedMessageData<StealMemory, Parent>(aClonedData, *this);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
StructuredCloneData::StealFromClonedMessageDataForChild(ClonedMessageData& aClonedData)
 | 
						|
{
 | 
						|
  MOZ_ASSERT(NS_IsMainThread());
 | 
						|
  UnpackClonedMessageData<StealMemory, Child>(aClonedData, *this);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
StructuredCloneData::StealFromClonedMessageDataForBackgroundParent(ClonedMessageData& aClonedData)
 | 
						|
{
 | 
						|
  MOZ_ASSERT(IsOnBackgroundThread());
 | 
						|
  UnpackClonedMessageData<StealMemory, Parent>(aClonedData, *this);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
StructuredCloneData::StealFromClonedMessageDataForBackgroundChild(ClonedMessageData& aClonedData)
 | 
						|
{
 | 
						|
  UnpackClonedMessageData<StealMemory, Child>(aClonedData, *this);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void
 | 
						|
StructuredCloneData::WriteIPCParams(IPC::Message* aMsg) const
 | 
						|
{
 | 
						|
  WriteParam(aMsg, Data());
 | 
						|
}
 | 
						|
 | 
						|
bool
 | 
						|
StructuredCloneData::ReadIPCParams(const IPC::Message* aMsg,
 | 
						|
                                   PickleIterator* aIter)
 | 
						|
{
 | 
						|
  MOZ_ASSERT(!mInitialized);
 | 
						|
  JSStructuredCloneData data;
 | 
						|
  if (!ReadParam(aMsg, aIter, &data)) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
  mSharedData = new SharedJSAllocatedData(Move(data));
 | 
						|
  mInitialized = true;
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
bool
 | 
						|
StructuredCloneData::CopyExternalData(const char* aData,
 | 
						|
                                      size_t aDataLength)
 | 
						|
{
 | 
						|
  MOZ_ASSERT(!mInitialized);
 | 
						|
  mSharedData = SharedJSAllocatedData::CreateFromExternalData(aData,
 | 
						|
                                                              aDataLength);
 | 
						|
  NS_ENSURE_TRUE(mSharedData, false);
 | 
						|
  mInitialized = true;
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
bool
 | 
						|
StructuredCloneData::CopyExternalData(const JSStructuredCloneData& aData)
 | 
						|
{
 | 
						|
  MOZ_ASSERT(!mInitialized);
 | 
						|
  mSharedData = SharedJSAllocatedData::CreateFromExternalData(aData);
 | 
						|
  NS_ENSURE_TRUE(mSharedData, false);
 | 
						|
  mInitialized = true;
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
bool
 | 
						|
StructuredCloneData::StealExternalData(JSStructuredCloneData& aData)
 | 
						|
{
 | 
						|
  MOZ_ASSERT(!mInitialized);
 | 
						|
  mSharedData = new SharedJSAllocatedData(Move(aData));
 | 
						|
  mInitialized = true;
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
} // namespace ipc
 | 
						|
} // namespace dom
 | 
						|
} // namespace mozilla
 |