Bug 1353629 - PBlob refactoring - part 9 - PBlob should use IPCStream in case it is dealing with an IPCBlobInputStream, r=smaug
authorAndrea Marchesini <amarchesini@mozilla.com>
Mon, 24 Apr 2017 12:09:40 +0200
changeset 354624 04d9349c61846108e9b2b99b207adaecdd366042
parent 354623 cee81d4988a513ca5f98bd81d94214ea5697d70d
child 354625 1c786e61d5c88e404cc47a2a92cea86028235ba2
push id31707
push userkwierso@gmail.com
push dateMon, 24 Apr 2017 22:53:41 +0000
treeherdermozilla-central@abdcc8dfc283 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1353629
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1353629 - PBlob refactoring - part 9 - PBlob should use IPCStream in case it is dealing with an IPCBlobInputStream, r=smaug This patch will go away when I'll finishing the removing of PBlob. Currently, when a PBlob is sent from child to parent, we use PMemoryStream in order to recreate the inputStream on the parent side. PMemoryStream sends the data in chunks. But if PBlob is dealing with a IPCBlobInputStream, it doesn't have access to the real data. In this case, we must send data using IPCStream. In this way, Note that thisIPCBlobInputStream will send its ID, and the parent will take the real inputStream from the IPCBlobInputStreamStorage. Note that I check the size to be 1mb instead 0. No particular reasons, but better to avoid the use of PMemoryStream for nothing.
dom/file/ipc/Blob.cpp
dom/ipc/DOMTypes.ipdlh
--- a/dom/file/ipc/Blob.cpp
+++ b/dom/file/ipc/Blob.cpp
@@ -666,19 +666,22 @@ SerializeInputStreamInChunks(nsIInputStr
   }
 
   return child;
 }
 
 void
 DeleteStreamMemoryFromBlobDataStream(BlobDataStream& aStream)
 {
-  PMemoryStreamChild* actor = aStream.streamChild();
-  if (actor) {
-    actor->Send__delete__(actor);
+  if (aStream.type() == BlobDataStream::TMemoryBlobDataStream) {
+    PMemoryStreamChild* actor =
+      aStream.get_MemoryBlobDataStream().streamChild();
+    if (actor) {
+      actor->Send__delete__(actor);
+    }
   }
 }
 
 void
 DeleteStreamMemoryFromBlobData(BlobData& aBlobData)
 {
   switch (aBlobData.type()) {
     case BlobData::TBlobDataStream:
@@ -748,28 +751,47 @@ CreateBlobImpl(const nsID& aKnownBlobIDD
 }
 
 already_AddRefed<BlobImpl>
 CreateBlobImpl(const BlobDataStream& aStream,
                const CreateBlobImplMetadata& aMetadata)
 {
   MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
 
-  MemoryStreamParent* actor =
-    static_cast<MemoryStreamParent*>(aStream.streamParent());
-
   nsCOMPtr<nsIInputStream> inputStream;
-  actor->GetStream(getter_AddRefs(inputStream));
-  if (!inputStream) {
-    ASSERT_UNLESS_FUZZING();
-    return nullptr;
+  uint64_t length;
+
+  if (aStream.type() == BlobDataStream::TMemoryBlobDataStream) {
+    const MemoryBlobDataStream& memoryBlobDataStream =
+      aStream.get_MemoryBlobDataStream();
+
+    MemoryStreamParent* actor =
+      static_cast<MemoryStreamParent*>(memoryBlobDataStream.streamParent());
+
+    actor->GetStream(getter_AddRefs(inputStream));
+    if (!inputStream) {
+      ASSERT_UNLESS_FUZZING();
+      return nullptr;
+    }
+
+    length = memoryBlobDataStream.length();
+  } else {
+    MOZ_ASSERT(aStream.type() == BlobDataStream::TIPCStream);
+    inputStream = DeserializeIPCStream(aStream.get_IPCStream());
+    if (!inputStream) {
+      ASSERT_UNLESS_FUZZING();
+      return nullptr;
+    }
+
+    nsresult rv = inputStream->Available(&length);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return nullptr;
+    }
   }
 
-  uint64_t length = aStream.length();
-
   RefPtr<BlobImpl> blobImpl;
   if (!aMetadata.mHasRecursed && aMetadata.IsFile()) {
     if (length) {
       blobImpl =
         StreamBlobImpl::Create(inputStream,
                                aMetadata.mName,
                                aMetadata.mContentType,
                                aMetadata.mLastModifiedDate,
@@ -946,17 +968,18 @@ CreateBlobImpl(const ParentBlobConstruct
   RefPtr<BlobImpl> blobImpl =
     CreateBlobImplFromBlobData(aBlobData, metadata);
   return blobImpl.forget();
 }
 
 template <class ChildManagerType>
 bool
 BlobDataFromBlobImpl(ChildManagerType* aManager, BlobImpl* aBlobImpl,
-                     BlobData& aBlobData)
+                     BlobData& aBlobData,
+                     nsTArray<UniquePtr<AutoIPCStream>>& aIPCStreams)
 {
   MOZ_ASSERT(gProcessType != GeckoProcessType_Default);
   MOZ_ASSERT(aBlobImpl);
 
   const nsTArray<RefPtr<BlobImpl>>* subBlobs = aBlobImpl->GetSubBlobImpls();
 
   if (subBlobs) {
     MOZ_ASSERT(subBlobs->Length());
@@ -965,17 +988,17 @@ BlobDataFromBlobImpl(ChildManagerType* a
 
     nsTArray<BlobData>& subBlobDatas = aBlobData.get_ArrayOfBlobData();
     subBlobDatas.SetLength(subBlobs->Length());
 
     for (uint32_t count = subBlobs->Length(), index = 0;
          index < count;
          index++) {
       if (!BlobDataFromBlobImpl(aManager, subBlobs->ElementAt(index),
-                                subBlobDatas[index])) {
+                                subBlobDatas[index], aIPCStreams)) {
         return false;
       }
     }
 
     return true;
   }
 
   nsCOMPtr<nsIRemoteBlob> remoteBlob = do_QueryInterface(aBlobImpl);
@@ -990,23 +1013,43 @@ BlobDataFromBlobImpl(ChildManagerType* a
   ErrorResult rv;
   uint64_t length = aBlobImpl->GetSize(rv);
   MOZ_ALWAYS_TRUE(!rv.Failed());
 
   nsCOMPtr<nsIInputStream> inputStream;
   aBlobImpl->GetInternalStream(getter_AddRefs(inputStream), rv);
   MOZ_ALWAYS_TRUE(!rv.Failed());
 
+  nsCOMPtr<nsIIPCSerializableInputStream> serializable =
+    do_QueryInterface(inputStream);
+
+  // ExpectedSerializedLength() returns the length of the stream if serialized.
+  // This is useful to decide if we want to continue using the serialization
+  // directly, or if it's better to use IPCStream.
+  uint64_t expectedLength =
+    serializable ? serializable->ExpectedSerializedLength().valueOr(0) : 0;
+
+  // If a stream is known to be larger than 1MB, prefer sending it in chunks.
+  const uint64_t kTooLargeStream = 1024 * 1024;
+  if (serializable && expectedLength < kTooLargeStream) {
+    UniquePtr<AutoIPCStream> autoStream(new AutoIPCStream());
+    autoStream->Serialize(inputStream, aManager);
+    aBlobData = autoStream->TakeValue();
+
+    aIPCStreams.AppendElement(Move(autoStream));
+    return true;
+  }
+
   PMemoryStreamChild* streamActor =
     SerializeInputStreamInChunks(inputStream, length, aManager);
   if (!streamActor) {
     return false;
   }
 
-  aBlobData = BlobDataStream(nullptr, streamActor, length);
+  aBlobData = MemoryBlobDataStream(nullptr, streamActor, length);
   return true;
 }
 
 RemoteInputStream::RemoteInputStream(BlobImpl* aBlobImpl,
                                      uint64_t aStart,
                                      uint64_t aLength)
   : mMonitor("RemoteInputStream.mMonitor")
   , mActor(nullptr)
@@ -3592,28 +3635,30 @@ BlobChild::GetOrCreateFromImpl(ChildMana
   if (NS_WARN_IF(NS_FAILED(aBlobImpl->SetMutable(false)))) {
     return nullptr;
   }
 
   MOZ_ASSERT(!aBlobImpl->IsSizeUnknown());
   MOZ_ASSERT(!aBlobImpl->IsDateUnknown());
 
   AnyBlobConstructorParams blobParams;
+  nsTArray<UniquePtr<AutoIPCStream>> autoIPCStreams;
 
   if (gProcessType == GeckoProcessType_Default) {
     RefPtr<BlobImpl> sameProcessImpl = aBlobImpl;
     auto addRefedBlobImpl =
       reinterpret_cast<intptr_t>(sameProcessImpl.forget().take());
 
     blobParams = SameProcessBlobConstructorParams(addRefedBlobImpl);
   } else {
     // BlobData is going to be populate here and it _must_ be send via IPC in
     // order to avoid leaks.
     BlobData blobData;
-    if (NS_WARN_IF(!BlobDataFromBlobImpl(aManager, aBlobImpl, blobData))) {
+    if (NS_WARN_IF(!BlobDataFromBlobImpl(aManager, aBlobImpl, blobData,
+                                         autoIPCStreams))) {
       return nullptr;
     }
 
     nsString contentType;
     aBlobImpl->GetType(contentType);
 
     ErrorResult rv;
     uint64_t length = aBlobImpl->GetSize(rv);
@@ -3642,16 +3687,17 @@ BlobChild::GetOrCreateFromImpl(ChildMana
   ParentBlobConstructorParams params(blobParams);
 
   if (NS_WARN_IF(!aManager->SendPBlobConstructor(actor, params))) {
     return nullptr;
   }
 
   DeleteStreamMemory(params.blobParams());
 
+  autoIPCStreams.Clear();
   return actor;
 }
 
 // static
 template <class ChildManagerType>
 BlobChild*
 BlobChild::CreateFromParams(ChildManagerType* aManager,
                             const ChildBlobConstructorParams& aParams)
--- a/dom/ipc/DOMTypes.ipdlh
+++ b/dom/ipc/DOMTypes.ipdlh
@@ -42,22 +42,31 @@ struct MessagePortIdentifier
 struct ClonedMessageData
 {
   SerializedStructuredCloneBuffer data;
   IPCBlob[] blobs;
   IPCStream[] inputStreams;
   MessagePortIdentifier[] identfiers;
 };
 
-struct BlobDataStream
+struct MemoryBlobDataStream
 {
   PMemoryStream stream;
   uint64_t length;
 };
 
+union BlobDataStream
+{
+  MemoryBlobDataStream;
+
+  // InputStreamParams is used only when we are _sure_ that the serialized size
+  // is lower than 1 Mb. Otherwise we use MemoryBlobDataStream.
+  IPCStream;
+};
+
 union BlobData
 {
   // For remote blobs.
   nsID;
 
   // For memory-backed blobs.
   BlobDataStream;