Bug 1335536 - File.createFromNsIFile and File.createFromFileName should be async - part 2 - code, r=smaug draft
authorAndrea Marchesini <amarchesini@mozilla.com>
Wed, 08 Feb 2017 10:19:01 +0100
changeset 480538 35902d02315744816e9cf5ce7e035742b5fa6733
parent 480537 1e8f56d4e7d70ef91f20c4d35cea6877afd0d19c
child 480539 6e3acedf2759e43f5a549655fef79de45c2b010c
push id44574
push userbmo:jbeich@FreeBSD.org
push dateWed, 08 Feb 2017 13:34:21 +0000
reviewerssmaug
bugs1335536
milestone54.0a1
Bug 1335536 - File.createFromNsIFile and File.createFromFileName should be async - part 2 - code, r=smaug
dom/file/File.cpp
dom/file/File.h
dom/file/FileCreatorHelper.cpp
dom/file/FileCreatorHelper.h
dom/file/MultipartBlobImpl.cpp
dom/file/MultipartBlobImpl.h
dom/file/moz.build
dom/ipc/ContentChild.cpp
dom/ipc/ContentChild.h
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/PContent.ipdl
--- a/dom/file/File.cpp
+++ b/dom/file/File.cpp
@@ -31,16 +31,17 @@
 #include "nsPrintfCString.h"
 #include "mozilla/SHA1.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/BlobBinding.h"
 #include "mozilla/dom/DOMError.h"
 #include "mozilla/dom/FileBinding.h"
+#include "mozilla/dom/FileCreatorHelper.h"
 #include "mozilla/dom/FileSystemUtils.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/WorkerPrivate.h"
 #include "mozilla/dom/WorkerRunnable.h"
 #include "nsThreadUtils.h"
 #include "nsStreamUtils.h"
 #include "SlicedInputStream.h"
 
@@ -586,70 +587,36 @@ File::Constructor(const GlobalObject& aG
 File::CreateFromNsIFile(const GlobalObject& aGlobal,
                         nsIFile* aData,
                         const ChromeFilePropertyBag& aBag,
                         SystemCallerGuarantee aGuarantee,
                         ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
-  nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal.GetAsSupports());
-
-  RefPtr<MultipartBlobImpl> impl = new MultipartBlobImpl(EmptyString());
-  impl->InitializeChromeFile(window, aData, aBag, true, aGuarantee, aRv);
-  if (aRv.Failed()) {
-    return nullptr;
-  }
-  MOZ_ASSERT(impl->IsFile());
-
-  if (aBag.mLastModified.WasPassed()) {
-    impl->SetLastModified(aBag.mLastModified.Value());
-  }
-
-  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
-  RefPtr<Promise> promise = Promise::Create(global, aRv);
-  if (NS_WARN_IF(aRv.Failed())) {
-    return nullptr;
-  }
-
-  RefPtr<File> domFile = new File(aGlobal.GetAsSupports(), impl);
-  promise->MaybeResolve(domFile);
-
+  RefPtr<Promise> promise =
+    FileCreatorHelper::CreateFile(aGlobal, aData, aBag, true, aRv);
   return promise.forget();
 }
 
 /* static */ already_AddRefed<Promise>
 File::CreateFromFileName(const GlobalObject& aGlobal,
-                         const nsAString& aData,
+                         const nsAString& aPath,
                          const ChromeFilePropertyBag& aBag,
                          SystemCallerGuarantee aGuarantee,
                          ErrorResult& aRv)
 {
-  nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal.GetAsSupports());
-
-  RefPtr<MultipartBlobImpl> impl = new MultipartBlobImpl(EmptyString());
-  impl->InitializeChromeFile(window, aData, aBag, aGuarantee, aRv);
-  if (aRv.Failed()) {
-    return nullptr;
-  }
-  MOZ_ASSERT(impl->IsFile());
-
-  if (aBag.mLastModified.WasPassed()) {
-    impl->SetLastModified(aBag.mLastModified.Value());
-  }
-
-  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
-  RefPtr<Promise> promise = Promise::Create(global, aRv);
+  nsCOMPtr<nsIFile> file;
+  aRv = NS_NewLocalFile(aPath, false, getter_AddRefs(file));
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
-  RefPtr<File> domFile = new File(aGlobal.GetAsSupports(), impl);
-  promise->MaybeResolve(domFile);
-
+  RefPtr<Promise> promise =
+    FileCreatorHelper::CreateFile(aGlobal, file, aBag, false, aRv);
   return promise.forget();
 }
 
 ////////////////////////////////////////////////////////////////////////////
 // mozilla::dom::BlobImpl implementation
 
 already_AddRefed<BlobImpl>
 BlobImpl::Slice(const Optional<int64_t>& aStart,
--- a/dom/file/File.h
+++ b/dom/file/File.h
@@ -202,25 +202,25 @@ public:
               const Sequence<BlobPart>& aData,
               const nsAString& aName,
               const FilePropertyBag& aBag,
               ErrorResult& aRv);
 
   // ChromeOnly
   static already_AddRefed<Promise>
   CreateFromFileName(const GlobalObject& aGlobal,
-                     const nsAString& aData,
+                     const nsAString& aFilePath,
                      const ChromeFilePropertyBag& aBag,
                      SystemCallerGuarantee aGuarantee,
                      ErrorResult& aRv);
 
   // ChromeOnly
   static already_AddRefed<Promise>
   CreateFromNsIFile(const GlobalObject& aGlobal,
-                    nsIFile* aData,
+                    nsIFile* aFile,
                     const ChromeFilePropertyBag& aBag,
                     SystemCallerGuarantee aGuarantee,
                     ErrorResult& aRv);
 
   void GetName(nsAString& aName) const;
 
   int64_t GetLastModified(ErrorResult& aRv);
 
new file mode 100644
--- /dev/null
+++ b/dom/file/FileCreatorHelper.cpp
@@ -0,0 +1,190 @@
+/* -*- 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 "FileCreatorHelper.h"
+
+#include "mozilla/dom/BindingDeclarations.h"
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/FileBinding.h"
+#include "mozilla/dom/File.h"
+#include "mozilla/dom/Promise.h"
+#include "nsContentUtils.h"
+#include "nsPIDOMWindow.h"
+#include "nsIFile.h"
+
+// Undefine the macro of CreateFile to avoid FileCreatorHelper#CreateFile being
+// replaced by FileCreatorHelper#CreateFileW.
+#ifdef CreateFile
+#undef CreateFile
+#endif
+
+namespace mozilla {
+namespace dom {
+
+/* static */ already_AddRefed<Promise>
+FileCreatorHelper::CreateFile(const GlobalObject& aGlobal,
+                              nsIFile* aFile,
+                              const ChromeFilePropertyBag& aBag,
+                              bool aIsFromNsIFile,
+                              ErrorResult& aRv)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(nsContentUtils::IsCallerChrome());
+
+  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
+  RefPtr<Promise> promise = Promise::Create(global, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  nsCOMPtr<nsPIDOMWindowInner> window =
+    do_QueryInterface(aGlobal.GetAsSupports());
+
+  // Parent process
+
+  if (XRE_IsParentProcess()) {
+    RefPtr<File> file =
+      CreateFileInternal(window, aFile, aBag, aIsFromNsIFile, aRv);
+    if (aRv.Failed()) {
+      return nullptr;
+    }
+    promise->MaybeResolve(file);
+    return promise.forget();
+  }
+
+  // Content process.
+
+  RefPtr<FileCreatorHelper> helper = new FileCreatorHelper(promise, window);
+
+  // The request is sent to the parent process and it's kept alive by
+  // ContentChild.
+  helper->SendRequest(aFile, aBag, aIsFromNsIFile, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  return promise.forget();
+}
+
+/* static */ already_AddRefed<File>
+FileCreatorHelper::CreateFileInternal(nsPIDOMWindowInner* aWindow,
+                                      nsIFile* aFile,
+                                      const ChromeFilePropertyBag& aBag,
+                                      bool aIsFromNsIFile,
+                                      ErrorResult& aRv)
+{
+  bool lastModifiedPassed = false;
+  int64_t lastModified = 0;
+  if (aBag.mLastModified.WasPassed()) {
+    lastModifiedPassed = true;
+    lastModified = aBag.mLastModified.Value();
+  }
+
+  RefPtr<BlobImpl> blobImpl;
+  aRv = CreateBlobImpl(aFile, aBag.mType, aBag.mName, lastModifiedPassed,
+                       lastModified, aIsFromNsIFile, getter_AddRefs(blobImpl));
+  if (aRv.Failed()) {
+     return nullptr;
+  }
+
+  RefPtr<File> file = File::Create(aWindow, blobImpl);
+  return file.forget();
+}
+
+FileCreatorHelper::FileCreatorHelper(Promise* aPromise,
+                                     nsPIDOMWindowInner* aWindow)
+  : mPromise(aPromise)
+  , mWindow(aWindow)
+{
+  MOZ_ASSERT(aPromise);
+}
+
+void
+FileCreatorHelper::SendRequest(nsIFile* aFile,
+                               const ChromeFilePropertyBag& aBag,
+                               bool aIsFromNsIFile,
+                               ErrorResult& aRv)
+{
+  MOZ_ASSERT(aFile);
+
+  ContentChild* cc = ContentChild::GetSingleton();
+  if (NS_WARN_IF(!cc)) {
+    aRv.Throw(NS_ERROR_FAILURE);
+    return;
+  }
+
+  nsID uuid;
+  aRv = nsContentUtils::GenerateUUIDInPlace(uuid);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return;
+  }
+
+  nsAutoString path;
+  aRv = aFile->GetPath(path);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return;
+  }
+
+  cc->FileCreationRequest(uuid, this, path, aBag.mType, aBag.mName,
+                          aBag.mLastModified, aIsFromNsIFile);
+}
+
+void
+FileCreatorHelper::ResponseReceived(BlobImpl* aBlobImpl, nsresult aRv)
+{
+  if (NS_FAILED(aRv)) {
+    mPromise->MaybeReject(aRv);
+    return;
+  }
+
+  RefPtr<File> file = File::Create(mWindow, aBlobImpl);
+  mPromise->MaybeResolve(file);
+}
+
+/* static */ nsresult
+FileCreatorHelper::CreateBlobImplForIPC(const nsAString& aPath,
+                                        const nsAString& aType,
+                                        const nsAString& aName,
+                                        bool aLastModifiedPassed,
+                                        int64_t aLastModified,
+                                        bool aIsFromNsIFile,
+                                        BlobImpl** aBlobImpl)
+{
+  nsCOMPtr<nsIFile> file;
+  nsresult rv = NS_NewLocalFile(aPath, true, getter_AddRefs(file));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return CreateBlobImpl(file, aType, aName, aLastModifiedPassed, aLastModified,
+                        aIsFromNsIFile, aBlobImpl);
+}
+
+/* static */ nsresult
+FileCreatorHelper::CreateBlobImpl(nsIFile* aFile,
+                                  const nsAString& aType,
+                                  const nsAString& aName,
+                                  bool aLastModifiedPassed,
+                                  int64_t aLastModified,
+                                  bool aIsFromNsIFile,
+                                  BlobImpl** aBlobImpl)
+{
+  RefPtr<MultipartBlobImpl> impl = new MultipartBlobImpl(EmptyString());
+  nsresult rv =
+    impl->InitializeChromeFile(aFile, aType, aName, aLastModifiedPassed,
+                               aLastModified, aIsFromNsIFile);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  MOZ_ASSERT(impl->IsFile());
+
+  impl.forget(aBlobImpl);
+  return NS_OK;
+}
+
+} // dom namespace
+} // mozilla namespace
new file mode 100644
--- /dev/null
+++ b/dom/file/FileCreatorHelper.h
@@ -0,0 +1,88 @@
+/* -*- 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/. */
+
+#ifndef mozilla_dom_FileCreatorHelper_h
+#define mozilla_dom_FileCreatorHelper_h
+
+#include "mozilla/Attributes.h"
+#include "mozilla/ErrorResult.h"
+#include "mozilla/RefPtr.h"
+#include "nsISupportsImpl.h"
+
+// Undefine the macro of CreateFile to avoid FileCreatorHelper#CreateFile being
+// replaced by FileCreatorHelper#CreateFileW.
+#ifdef CreateFile
+#undef CreateFile
+#endif
+
+class nsIFile;
+class nsPIDOMWindowInner;
+
+namespace mozilla {
+namespace dom {
+
+struct ChromeFilePropertyBag;
+class GlobalObject;
+class Promise;
+class File;
+
+class FileCreatorHelper final
+{
+public:
+  NS_INLINE_DECL_REFCOUNTING(FileCreatorHelper);
+
+  static already_AddRefed<Promise>
+  CreateFile(const GlobalObject& aGlobal,
+             nsIFile* aFile,
+             const ChromeFilePropertyBag& aBag,
+             bool aIsFromNsIFile,
+             ErrorResult& aRv);
+
+  void
+  ResponseReceived(BlobImpl* aBlobImpl, nsresult aRv);
+
+  // For IPC only
+  static nsresult
+  CreateBlobImplForIPC(const nsAString& aPath,
+                       const nsAString& aType,
+                       const nsAString& aName,
+                       bool aLastModifiedPassed,
+                       int64_t aLastModified,
+                       bool aIsFromNsIFile,
+                       BlobImpl** aBlobImpl);
+
+private:
+  static already_AddRefed<File>
+  CreateFileInternal(nsPIDOMWindowInner* aWindow,
+                     nsIFile* aFile,
+                     const ChromeFilePropertyBag& aBag,
+                     bool aIsFromNsIFile,
+                     ErrorResult& aRv);
+
+  static nsresult
+  CreateBlobImpl(nsIFile* aFile,
+                 const nsAString& aType,
+                 const nsAString& aName,
+                 bool aLastModifiedPassed,
+                 int64_t aLastModified,
+                 bool aIsFromNsIFile,
+                 BlobImpl** aBlobImpl);
+
+  FileCreatorHelper(Promise* aPromise, nsPIDOMWindowInner* aWindow);
+  ~FileCreatorHelper() = default;
+
+  void
+  SendRequest(nsIFile* aFile, const ChromeFilePropertyBag& aBag,
+              bool aIsFromNsIFile, ErrorResult& aRv);
+
+  RefPtr<Promise> mPromise;
+  nsCOMPtr<nsPIDOMWindowInner> mWindow;
+};
+
+} // dom namespace
+} // mozilla namespace
+
+#endif // mozilla_dom_FileCreatorHelper_h
--- a/dom/file/MultipartBlobImpl.cpp
+++ b/dom/file/MultipartBlobImpl.cpp
@@ -318,101 +318,91 @@ MultipartBlobImpl::SetMutable(bool aMuta
     return rv;
   }
 
   MOZ_ASSERT_IF(!aMutable, mImmutable);
 
   return NS_OK;
 }
 
-void
-MultipartBlobImpl::InitializeChromeFile(nsPIDOMWindowInner* aWindow,
-                                        nsIFile* aFile,
-                                        const ChromeFilePropertyBag& aBag,
-                                        bool aIsFromNsIFile,
-                                        SystemCallerGuarantee /* unused */,
-                                        ErrorResult& aRv)
+nsresult
+MultipartBlobImpl::InitializeChromeFile(nsIFile* aFile,
+                                        const nsAString& aType,
+                                        const nsAString& aName,
+                                        bool aLastModifiedPassed,
+                                        int64_t aLastModified,
+                                        bool aIsFromNsIFile)
 {
   MOZ_ASSERT(!mImmutable, "Something went wrong ...");
   if (mImmutable) {
-    aRv.Throw(NS_ERROR_UNEXPECTED);
-    return;
+    return NS_ERROR_UNEXPECTED;
   }
 
-  mName = aBag.mName;
-  mContentType = aBag.mType;
+  mName = aName;
+  mContentType = aType;
   mIsFromNsIFile = aIsFromNsIFile;
 
   bool exists;
-  aRv = aFile->Exists(&exists);
-  if (NS_WARN_IF(aRv.Failed())) {
-    return;
+  nsresult rv= aFile->Exists(&exists);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
   }
 
   if (!exists) {
-    aRv.Throw(NS_ERROR_FILE_NOT_FOUND);
-    return;
+    return NS_ERROR_FILE_NOT_FOUND;
   }
 
   bool isDir;
-  aRv = aFile->IsDirectory(&isDir);
-  if (NS_WARN_IF(aRv.Failed())) {
-    return;
+  rv = aFile->IsDirectory(&isDir);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
   }
 
   if (isDir) {
-    aRv.Throw(NS_ERROR_FILE_IS_DIRECTORY);
-    return;
+    return NS_ERROR_FILE_IS_DIRECTORY;
   }
 
   if (mName.IsEmpty()) {
     aFile->GetLeafName(mName);
   }
 
-  RefPtr<File> blob = File::CreateFromFile(aWindow, aFile);
+  RefPtr<File> blob = File::CreateFromFile(nullptr, aFile);
 
   // Pre-cache size.
-  blob->GetSize(aRv);
-  if (NS_WARN_IF(aRv.Failed())) {
-    return;
+  ErrorResult error;
+  blob->GetSize(error);
+  if (NS_WARN_IF(error.Failed())) {
+    return error.StealNSResult();
   }
 
   // Pre-cache modified date.
-  blob->GetLastModified(aRv);
-  if (NS_WARN_IF(aRv.Failed())) {
-    return;
+  blob->GetLastModified(error);
+  if (NS_WARN_IF(error.Failed())) {
+    return error.StealNSResult();
   }
 
   // XXXkhuey this is terrible
   if (mContentType.IsEmpty()) {
     blob->GetType(mContentType);
   }
 
   BlobSet blobSet;
   blobSet.AppendBlobImpl(static_cast<File*>(blob.get())->Impl());
   mBlobImpls = blobSet.GetBlobImpls();
 
-  SetLengthAndModifiedDate(aRv);
-  NS_WARNING_ASSERTION(!aRv.Failed(), "SetLengthAndModifiedDate failed");
-}
-
-void
-MultipartBlobImpl::InitializeChromeFile(nsPIDOMWindowInner* aWindow,
-                                        const nsAString& aData,
-                                        const ChromeFilePropertyBag& aBag,
-                                        SystemCallerGuarantee aGuarantee,
-                                        ErrorResult& aRv)
-{
-  nsCOMPtr<nsIFile> file;
-  aRv = NS_NewLocalFile(aData, false, getter_AddRefs(file));
-  if (NS_WARN_IF(aRv.Failed())) {
-    return;
+  SetLengthAndModifiedDate(error);
+  if (NS_WARN_IF(error.Failed())) {
+    return error.StealNSResult();
   }
 
-  InitializeChromeFile(aWindow, file, aBag, false, aGuarantee, aRv);
+  if (aLastModifiedPassed) {
+    SetLastModified(aLastModified);
+  }
+
+  return NS_OK;
 }
 
 bool
 MultipartBlobImpl::MayBeClonedToOtherThreads() const
 {
   for (uint32_t i = 0; i < mBlobImpls.Length(); ++i) {
     if (!mBlobImpls[i]->MayBeClonedToOtherThreads()) {
       return false;
--- a/dom/file/MultipartBlobImpl.h
+++ b/dom/file/MultipartBlobImpl.h
@@ -56,28 +56,22 @@ public:
   void InitializeBlob(ErrorResult& aRv);
 
   void InitializeBlob(JSContext* aCx,
                       const Sequence<Blob::BlobPart>& aData,
                       const nsAString& aContentType,
                       bool aNativeEOL,
                       ErrorResult& aRv);
 
-  void InitializeChromeFile(nsPIDOMWindowInner* aWindow,
-                            const nsAString& aData,
-                            const ChromeFilePropertyBag& aBag,
-                            SystemCallerGuarantee aGuarantee,
-                            ErrorResult& aRv);
-
-  void InitializeChromeFile(nsPIDOMWindowInner* aWindow,
-                            nsIFile* aData,
-                            const ChromeFilePropertyBag& aBag,
-                            bool aIsFromNsIFile,
-                            SystemCallerGuarantee /* unused */,
-                            ErrorResult& aRv);
+  nsresult InitializeChromeFile(nsIFile* aData,
+                                const nsAString& aType,
+                                const nsAString& aName,
+                                bool aLastModifiedPassed,
+                                int64_t aLastModified,
+                                bool aIsFromNsIFile);
 
   virtual already_AddRefed<BlobImpl>
   CreateSlice(uint64_t aStart, uint64_t aLength,
               const nsAString& aContentType,
               ErrorResult& aRv) override;
 
   virtual uint64_t GetSize(ErrorResult& aRv) override
   {
--- a/dom/file/moz.build
+++ b/dom/file/moz.build
@@ -16,26 +16,28 @@ XPIDL_MODULE = 'dom_file'
 EXPORTS += [
     'nsHostObjectProtocolHandler.h',
     'nsHostObjectURI.h',
 ]
 
 EXPORTS.mozilla.dom += [
     'BlobSet.h',
     'File.h',
+    'FileCreatorHelper.h',
     'FileList.h',
     'FileReader.h',
     'MultipartBlobImpl.h',
     'MutableBlobStorage.h',
     'MutableBlobStreamListener.h',
 ]
 
 UNIFIED_SOURCES += [
     'BlobSet.cpp',
     'File.cpp',
+    'FileCreatorHelper.cpp',
     'FileList.cpp',
     'FileReader.cpp',
     'MultipartBlobImpl.cpp',
     'MutableBlobStorage.cpp',
     'MutableBlobStreamListener.cpp',
     'nsHostObjectProtocolHandler.cpp',
     'nsHostObjectURI.cpp',
 ]
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -24,16 +24,17 @@
 #include "mozilla/docshell/OfflineCacheUpdateChild.h"
 #include "mozilla/dom/ContentBridgeChild.h"
 #include "mozilla/dom/ContentBridgeParent.h"
 #include "mozilla/dom/VideoDecoderManagerChild.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/DataTransfer.h"
 #include "mozilla/dom/DocGroup.h"
 #include "mozilla/dom/ExternalHelperAppChild.h"
+#include "mozilla/dom/FileCreatorHelper.h"
 #include "mozilla/dom/FlyWebPublishedServerIPC.h"
 #include "mozilla/dom/GetFilesHelper.h"
 #include "mozilla/dom/MemoryReportRequest.h"
 #include "mozilla/dom/PCrashReporterChild.h"
 #include "mozilla/dom/ProcessGlobal.h"
 #include "mozilla/dom/PushNotifier.h"
 #include "mozilla/dom/StorageIPC.h"
 #include "mozilla/dom/TabGroup.h"
@@ -3170,10 +3171,60 @@ ContentChild::GetConstructedEventTarget(
 
   // If the request for a new TabChild is coming from the parent process, then
   // there is no opener. Therefore, we create a fresh TabGroup.
   RefPtr<TabGroup> tabGroup = new TabGroup();
   nsCOMPtr<nsIEventTarget> target = tabGroup->EventTargetFor(TaskCategory::Other);
   return target.forget();
 }
 
+void
+ContentChild::FileCreationRequest(nsID& aUUID, FileCreatorHelper* aHelper,
+                                  const nsAString& aFullPath,
+                                  const nsAString& aType,
+                                  const nsAString& aName,
+                                  const Optional<int64_t>& aLastModified,
+                                  bool aIsFromNsIFile)
+{
+  MOZ_ASSERT(aHelper);
+
+  bool lastModifiedPassed = false;
+  int64_t lastModified = 0;
+  if (aLastModified.WasPassed()) {
+    lastModifiedPassed = true;
+    lastModified = aLastModified.Value();
+  }
+
+  Unused << SendFileCreationRequest(aUUID, nsString(aFullPath), nsString(aType),
+                                    nsString(aName), lastModifiedPassed,
+                                    lastModified, aIsFromNsIFile);
+  mFileCreationPending.Put(aUUID, aHelper);
+}
+
+mozilla::ipc::IPCResult
+ContentChild::RecvFileCreationResponse(const nsID& aUUID,
+                                       const FileCreationResult& aResult)
+{
+  FileCreatorHelper* helper = mFileCreationPending.GetWeak(aUUID);
+  if (!helper) {
+    return IPC_FAIL_NO_REASON(this);
+  }
+
+  if (aResult.type() == FileCreationResult::TFileCreationErrorResult) {
+    helper->ResponseReceived(nullptr,
+                             aResult.get_FileCreationErrorResult().errorCode());
+  } else {
+    MOZ_ASSERT(aResult.type() == FileCreationResult::TFileCreationSuccessResult);
+
+    PBlobChild* blobChild =
+      aResult.get_FileCreationSuccessResult().blobChild();
+    MOZ_ASSERT(blobChild);
+
+    RefPtr<BlobImpl> impl = static_cast<BlobChild*>(blobChild)->GetBlobImpl();
+    helper->ResponseReceived(impl, NS_OK);
+  }
+
+  mFileCreationPending.Remove(aUUID);
+  return IPC_OK();
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -44,16 +44,17 @@ class URIParams;
 namespace dom {
 
 class AlertObserver;
 class ConsoleListener;
 class PStorageChild;
 class ClonedMessageData;
 class TabChild;
 class GetFilesHelperChild;
+class FileCreatorHelper;
 
 class ContentChild final : public PContentChild
                          , public nsIWindowProvider
                          , public nsIContentChild
 {
   typedef mozilla::dom::ClonedMessageData ClonedMessageData;
   typedef mozilla::ipc::FileDescriptor FileDescriptor;
   typedef mozilla::ipc::OptionalURIParams OptionalURIParams;
@@ -564,16 +565,20 @@ public:
 
   virtual mozilla::ipc::IPCResult
   RecvBlobURLRegistration(const nsCString& aURI, PBlobChild* aBlobChild,
                           const IPC::Principal& aPrincipal) override;
 
   virtual mozilla::ipc::IPCResult
   RecvBlobURLUnregistration(const nsCString& aURI) override;
 
+  virtual mozilla::ipc::IPCResult
+  RecvFileCreationResponse(const nsID& aUUID,
+                           const FileCreationResult& aResult) override;
+
   mozilla::ipc::IPCResult
   RecvRequestMemoryReport(
           const uint32_t& generation,
           const bool& anonymize,
           const bool& minimizeMemoryUsage,
           const MaybeFileDesc& DMDFile) override;
 
   virtual mozilla::ipc::IPCResult
@@ -610,16 +615,24 @@ public:
    * Overrides FatalError to just be a warning when communicating with the
    * GPU process since we don't want to crash the content process when the
    * GPU process crashes.
    */
   static void FatalErrorIfNotUsingGPUProcess(const char* const aProtocolName,
                                              const char* const aErrorMsg,
                                              base::ProcessId aOtherPid);
 
+  // This method is used by FileCreatorHelper for the creation of a BlobImpl.
+  void
+  FileCreationRequest(nsID& aUUID, FileCreatorHelper* aHelper,
+                      const nsAString& aFullPath, const nsAString& aType,
+                      const nsAString& aName,
+                      const Optional<int64_t>& aLastModified,
+                      bool aIsFromNsIFile);
+
 private:
   static void ForceKillTimerCallback(nsITimer* aTimer, void* aClosure);
   void StartForceKillTimer();
 
   virtual void ActorDestroy(ActorDestroyReason why) override;
 
   virtual void ProcessingError(Result aCode, const char* aReason) override;
 
@@ -674,16 +687,20 @@ private:
   nsCOMPtr<nsIFile> mProfileDir;
 #endif
 
   // Hashtable to keep track of the pending GetFilesHelper objects.
   // This GetFilesHelperChild objects are removed when RecvGetFilesResponse is
   // received.
   nsRefPtrHashtable<nsIDHashKey, GetFilesHelperChild> mGetFilesPendingRequests;
 
+  // Hashtable to keep track of the pending file creation.
+  // These items are removed when RecvFileCreationResponse is received.
+  nsRefPtrHashtable<nsIDHashKey, FileCreatorHelper> mFileCreationPending;
+
   bool mShuttingDown;
 
   DISALLOW_EVIL_CONSTRUCTORS(ContentChild);
 };
 
 uint64_t
 NextWindowID();
 
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -37,16 +37,17 @@
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/StyleSheetInlines.h"
 #include "mozilla/DataStorage.h"
 #include "mozilla/devtools/HeapSnapshotTempFileHelperParent.h"
 #include "mozilla/docshell/OfflineCacheUpdateParent.h"
 #include "mozilla/dom/DataTransfer.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/File.h"
+#include "mozilla/dom/FileCreatorHelper.h"
 #include "mozilla/dom/ExternalHelperAppParent.h"
 #include "mozilla/dom/GetFilesHelper.h"
 #include "mozilla/dom/GeolocationBinding.h"
 #include "mozilla/dom/MemoryReportRequest.h"
 #include "mozilla/dom/Notification.h"
 #include "mozilla/dom/PContentBridgeParent.h"
 #include "mozilla/dom/PContentPermissionRequestParent.h"
 #include "mozilla/dom/PCycleCollectWithLogsParent.h"
@@ -4956,8 +4957,42 @@ ContentParent::RecvClassifyLocal(const U
   nsCOMPtr<nsIURIClassifier> uriClassifier =
     do_GetService(NS_URICLASSIFIERSERVICE_CONTRACTID);
   if (!uriClassifier) {
     return IPC_FAIL_NO_REASON(this);
   }
   *aRv = uriClassifier->ClassifyLocalWithTables(uri, aTables, *aResults);
   return IPC_OK();
 }
+
+mozilla::ipc::IPCResult
+ContentParent::RecvFileCreationRequest(const nsID& aID,
+                                       const nsString& aFullPath,
+                                       const nsString& aType,
+                                       const nsString& aName,
+                                       const bool& aLastModifiedPassed,
+                                       const int64_t& aLastModified,
+                                       const bool& aIsFromNsIFile)
+{
+  RefPtr<BlobImpl> blobImpl;
+  nsresult rv =
+    FileCreatorHelper::CreateBlobImplForIPC(aFullPath, aType, aName,
+                                            aLastModifiedPassed,
+                                            aLastModified, aIsFromNsIFile,
+                                            getter_AddRefs(blobImpl));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    if (!SendFileCreationResponse(aID, FileCreationErrorResult(rv))) {
+      return IPC_FAIL_NO_REASON(this);
+    }
+
+    return IPC_OK();
+  }
+
+  MOZ_ASSERT(blobImpl);
+
+  BlobParent* blobParent = BlobParent::GetOrCreate(this, blobImpl);
+  if (!SendFileCreationResponse(aID,
+                                FileCreationSuccessResult(blobParent, nullptr))) {
+    return IPC_FAIL_NO_REASON(this);
+  }
+
+  return IPC_OK();
+}
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -1083,16 +1083,23 @@ private:
   virtual mozilla::ipc::IPCResult RecvNotifyLowMemory() override;
 
   virtual mozilla::ipc::IPCResult RecvGetFilesRequest(const nsID& aID,
                                                       const nsString& aDirectoryPath,
                                                       const bool& aRecursiveFlag) override;
 
   virtual mozilla::ipc::IPCResult RecvDeleteGetFilesRequest(const nsID& aID) override;
 
+  virtual mozilla::ipc::IPCResult
+  RecvFileCreationRequest(const nsID& aID, const nsString& aFullPath,
+                          const nsString& aType, const nsString& aName,
+                          const bool& aLastModifiedPassed,
+                          const int64_t& aLastModified,
+                          const bool& aIsFromNsIFile) override;
+
   virtual mozilla::ipc::IPCResult RecvAccumulateChildHistogram(
     InfallibleTArray<Accumulation>&& aAccumulations) override;
   virtual mozilla::ipc::IPCResult RecvAccumulateChildKeyedHistogram(
     InfallibleTArray<KeyedAccumulation>&& aAccumulations) override;
   virtual mozilla::ipc::IPCResult RecvUpdateChildScalars(
     InfallibleTArray<ScalarAction>&& aScalarActions) override;
   virtual mozilla::ipc::IPCResult RecvUpdateChildKeyedScalars(
     InfallibleTArray<KeyedScalarAction>&& aScalarActions) override;
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -309,16 +309,32 @@ struct GetFilesResponseFailure
 };
 
 union GetFilesResponseResult
 {
   GetFilesResponseSuccess;
   GetFilesResponseFailure;
 };
 
+struct FileCreationSuccessResult
+{
+  PBlob blob;
+};
+
+struct FileCreationErrorResult
+{
+  nsresult errorCode;
+};
+
+union FileCreationResult
+{
+  FileCreationSuccessResult;
+  FileCreationErrorResult;
+};
+
 struct BlobURLRegistrationData
 {
     nsCString url;
     PBlob blob;
     Principal principal;
 };
 
 struct GMPAPITags
@@ -660,16 +676,18 @@ child:
     async BlobURLRegistration(nsCString aURI, PBlob aBlob,
                               Principal aPrincipal);
 
     async BlobURLUnregistration(nsCString aURI);
 
 
     async GMPsChanged(GMPCapabilityData[] capabilities);
 
+    async FileCreationResponse(nsID aID, FileCreationResult aResult);
+
 parent:
     async InitBackground(Endpoint<PBackgroundParent> aEndpoint);
 
     sync CreateChildProcess(IPCTabContext context,
                             ProcessPriority priority,
                             TabId openerTabId)
         returns (ContentParentId cpId, bool isForBrowser, TabId tabId);
     sync BridgeToChildProcess(ContentParentId cpId)
@@ -1136,16 +1154,20 @@ parent:
      * allows the parent process to save a memory report that can potentially be
      * sent with a crash report from the content process.
      */
      async NotifyLowMemory();
 
      async GetFilesRequest(nsID aID, nsString aDirectory, bool aRecursiveFlag);
      async DeleteGetFilesRequest(nsID aID);
 
+     async FileCreationRequest(nsID aID, nsString aFullPath, nsString aType,
+                               nsString aName, bool lastModifiedPassed,
+                               int64_t lastModified, bool aIsFromNsIFile);
+
      async StoreAndBroadcastBlobURLRegistration(nsCString url, PBlob blob,
                                                 Principal principal);
 
      async UnstoreAndBroadcastBlobURLUnregistration(nsCString url);
 
     /**
      * Messages for communicating child Telemetry to the parent process
      */