Bug 1513057 - P1.1: Create a reusable class to pass prefs to child processes r=dragana
authorKershaw Chang <kershaw@mozilla.com>
Fri, 11 Jan 2019 18:39:22 +0000
changeset 453536 696250a73120
parent 453535 c083f11eb58d
child 453537 ba234cd89920
push id35360
push usernbeleuzu@mozilla.com
push dateSat, 12 Jan 2019 09:39:47 +0000
treeherdermozilla-central@cb35977ae7a4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdragana
bugs1513057
milestone66.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 1513057 - P1.1: Create a reusable class to pass prefs to child processes r=dragana Differential Revision: https://phabricator.services.mozilla.com/D14970
dom/ipc/ContentParent.cpp
dom/ipc/ContentProcess.cpp
dom/ipc/ContentProcess.h
ipc/glue/ProcessUtils.h
ipc/glue/ProcessUtils_common.cpp
ipc/glue/moz.build
netwerk/ipc/SocketProcessHost.cpp
netwerk/ipc/SocketProcessImpl.cpp
toolkit/xre/nsEmbedFunctions.cpp
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/DebugOnly.h"
 
 #include "base/basictypes.h"
 #include "base/shared_memory.h"
 
 #include "ContentParent.h"
+#include "ProcessUtils.h"
 #include "TabParent.h"
 
 #if defined(ANDROID) || defined(LINUX)
 #include <sys/time.h>
 #include <sys/resource.h>
 #endif
 
 #include "chrome/common/process_watcher.h"
@@ -2106,82 +2107,65 @@ void ContentParent::LaunchSubprocessInte
   char idStr[21];
   SprintfLiteral(idStr, "%" PRId64, static_cast<uint64_t>(mChildID));
   extraArgs.push_back(idStr);
   extraArgs.push_back(IsForBrowser() ? "-isForBrowser" : "-notForBrowser");
 
   // Prefs information is passed via anonymous shared memory to avoid bloating
   // the command line.
 
-  size_t prefMapSize;
-  auto prefMapHandle =
-      Preferences::EnsureSnapshot(&prefMapSize).ClonePlatformHandle();
-
-  // Serialize the early prefs.
-  nsAutoCStringN<1024> prefs;
-  Preferences::SerializePreferences(prefs);
-
-  // Set up the shared memory.
-  base::SharedMemory shm;
-  if (!shm.Create(prefs.Length())) {
-    NS_ERROR("failed to create shared memory in the parent");
+  SharedPreferenceSerializer prefSerializer;
+  if (!prefSerializer.SerializeToSharedMemory()) {
     MarkAsDead();
     earlyReject();
     return;
   }
-  if (!shm.Map(prefs.Length())) {
-    NS_ERROR("failed to map shared memory in the parent");
-    MarkAsDead();
-    earlyReject();
-    return;
-  }
-
-  // Copy the serialized prefs into the shared memory.
-  memcpy(static_cast<char*>(shm.memory()), prefs.get(), prefs.Length());
 
   // Register ContentParent as an observer for changes to any pref
   // whose prefix matches the empty string, i.e. all of them.  The
   // observation starts here in order to capture pref updates that
   // happen during async launch.
   Preferences::AddStrongObserver(this, "");
 
   // Formats a pointer or pointer-sized-integer as a string suitable for passing
   // in an arguments list.
   auto formatPtrArg = [](auto arg) {
     return nsPrintfCString("%zu", uintptr_t(arg));
   };
 
 #if defined(XP_WIN)
   // Record the handle as to-be-shared, and pass it via a command flag. This
   // works because Windows handles are system-wide.
-  HANDLE prefsHandle = shm.handle();
+  HANDLE prefsHandle = prefSerializer.GetSharedMemoryHandle();
   mSubprocess->AddHandleToShare(prefsHandle);
-  mSubprocess->AddHandleToShare(prefMapHandle.get());
+  mSubprocess->AddHandleToShare(prefSerializer.GetPrefMapHandle().get());
   extraArgs.push_back("-prefsHandle");
   extraArgs.push_back(formatPtrArg(prefsHandle).get());
   extraArgs.push_back("-prefMapHandle");
-  extraArgs.push_back(formatPtrArg(prefMapHandle.get()).get());
+  extraArgs.push_back(
+      formatPtrArg(prefSerializer.GetPrefMapHandle().get()).get());
 #else
   // In contrast, Unix fds are per-process. So remap the fd to a fixed one that
   // will be used in the child.
   // XXX: bug 1440207 is about improving how fixed fds are used.
   //
   // Note: on Android, AddFdToRemap() sets up the fd to be passed via a Parcel,
   // and the fixed fd isn't used. However, we still need to mark it for
   // remapping so it doesn't get closed in the child.
-  mSubprocess->AddFdToRemap(shm.handle().fd, kPrefsFileDescriptor);
-  mSubprocess->AddFdToRemap(prefMapHandle.get(), kPrefMapFileDescriptor);
+  mSubprocess->AddFdToRemap(prefSerializer.GetSharedMemoryHandle().fd,
+                            kPrefsFileDescriptor);
+  mSubprocess->AddFdToRemap(prefSerializer.GetPrefMapHandle().get(),
+                            kPrefMapFileDescriptor);
 #endif
 
   // Pass the lengths via command line flags.
   extraArgs.push_back("-prefsLen");
-  extraArgs.push_back(formatPtrArg(prefs.Length()).get());
-
+  extraArgs.push_back(formatPtrArg(prefSerializer.GetPrefLength()).get());
   extraArgs.push_back("-prefMapSize");
-  extraArgs.push_back(formatPtrArg(prefMapSize).get());
+  extraArgs.push_back(formatPtrArg(prefSerializer.GetPrefMapSize()).get());
 
   // Scheduler prefs need to be handled differently because the scheduler needs
   // to start up in the content process before the normal preferences service.
   nsPrintfCString schedulerPrefs = Scheduler::GetPrefs();
   extraArgs.push_back("-schedulerPrefs");
   extraArgs.push_back(schedulerPrefs.get());
 
   if (gSafeMode) {
@@ -2231,19 +2215,18 @@ void ContentParent::LaunchSubprocessInte
   // ContentParent then owns the GeckoChildProcessHost (and that
   // ownership is not exposed to the cycle collector).  Therefore,
   // this all stays alive until the promise is resolved or rejected.
 
   auto resolve = [self, this, aInitialPriority, isSync,
                   // Transfer ownership of RAII file descriptor/handle
                   // holders so that they won't be closed before the
                   // child can inherit them.
-                  shm = std::move(shm),
-                  prefMapHandle =
-                      std::move(prefMapHandle)](base::ProcessHandle handle) {
+                  prefSerializer =
+                      std::move(prefSerializer)](base::ProcessHandle handle) {
     AUTO_PROFILER_LABEL("ContentParent::LaunchSubprocess::resolve", OTHER);
     const auto launchResumeTS = TimeStamp::Now();
 
     base::ProcessId procId = base::GetProcId(handle);
     Open(mSubprocess->GetChannel(), procId);
 #ifdef MOZ_CODE_COVERAGE
     Unused << SendShareCodeCoverageMutex(
         CodeCoverageHandler::Get()->GetMutexHandle(procId));
--- a/dom/ipc/ContentProcess.cpp
+++ b/dom/ipc/ContentProcess.cpp
@@ -73,52 +73,29 @@ static void SetUpSandboxEnvironment() {
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return;
   }
 
   SetTmpEnvironmentVariable(sandboxedContentTemp);
 }
 #endif
 
-#ifdef ANDROID
-static int gPrefsFd = -1;
-static int gPrefMapFd = -1;
-
-void SetPrefsFd(int aFd) { gPrefsFd = aFd; }
-
-void SetPrefMapFd(int aFd) { gPrefMapFd = aFd; }
-#endif
-
 bool ContentProcess::Init(int aArgc, char* aArgv[]) {
   Maybe<uint64_t> childID;
   Maybe<bool> isForBrowser;
-  Maybe<base::SharedMemoryHandle> prefsHandle;
-  Maybe<FileDescriptor> prefMapHandle;
-  Maybe<size_t> prefsLen;
-  Maybe<size_t> prefMapSize;
   Maybe<const char*> schedulerPrefs;
   Maybe<const char*> parentBuildID;
+  char* prefsHandle = nullptr;
+  char* prefMapHandle = nullptr;
+  char* prefsLen = nullptr;
+  char* prefMapSize = nullptr;
 #if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
   nsCOMPtr<nsIFile> profileDir;
 #endif
 
-  // Parses an arg containing a pointer-sized-integer.
-  auto parseUIntPtrArg = [](char*& aArg) {
-    // ContentParent uses %zu to print a word-sized unsigned integer. So
-    // even though strtoull() returns a long long int, it will fit in a
-    // uintptr_t.
-    return uintptr_t(strtoull(aArg, &aArg, 10));
-  };
-
-#ifdef XP_WIN
-  auto parseHandleArg = [&](char*& aArg) {
-    return HANDLE(parseUIntPtrArg(aArg));
-  };
-#endif
-
   for (int i = 1; i < aArgc; i++) {
     if (!aArgv[i]) {
       continue;
     }
 
     if (strcmp(aArgv[i], "-appdir") == 0) {
       if (++i == aArgc) {
         return false;
@@ -142,57 +119,34 @@ bool ContentProcess::Init(int aArgc, cha
     } else if (strcmp(aArgv[i], "-notForBrowser") == 0) {
       isForBrowser = Some(false);
 
 #ifdef XP_WIN
     } else if (strcmp(aArgv[i], "-prefsHandle") == 0) {
       if (++i == aArgc) {
         return false;
       }
-      char* str = aArgv[i];
-      prefsHandle = Some(parseHandleArg(str));
-      if (str[0] != '\0') {
-        return false;
-      }
-
+      prefsHandle = aArgv[i];
     } else if (strcmp(aArgv[i], "-prefMapHandle") == 0) {
       if (++i == aArgc) {
         return false;
       }
-      char* str = aArgv[i];
-      // The FileDescriptor constructor will clone this handle when constructed,
-      // so store it in a UniquePlatformHandle to make sure the original gets
-      // closed.
-      FileDescriptor::UniquePlatformHandle handle(parseHandleArg(str));
-      prefMapHandle.emplace(handle.get());
-      if (str[0] != '\0') {
-        return false;
-      }
+      prefMapHandle = aArgv[i];
 #endif
 
     } else if (strcmp(aArgv[i], "-prefsLen") == 0) {
       if (++i == aArgc) {
         return false;
       }
-      char* str = aArgv[i];
-      prefsLen = Some(parseUIntPtrArg(str));
-      if (str[0] != '\0') {
-        return false;
-      }
-
+      prefsLen = aArgv[i];
     } else if (strcmp(aArgv[i], "-prefMapSize") == 0) {
       if (++i == aArgc) {
         return false;
       }
-      char* str = aArgv[i];
-      prefMapSize = Some(parseUIntPtrArg(str));
-      if (str[0] != '\0') {
-        return false;
-      }
-
+      prefMapSize = aArgv[i];
     } else if (strcmp(aArgv[i], "-schedulerPrefs") == 0) {
       if (++i == aArgc) {
         return false;
       }
       schedulerPrefs = Some(aArgv[i]);
 
     } else if (strcmp(aArgv[i], "-safeMode") == 0) {
       gSafeMode = true;
@@ -213,64 +167,34 @@ bool ContentProcess::Init(int aArgc, cha
       if (NS_FAILED(rv) || NS_FAILED(profileDir->Exists(&flag)) || !flag) {
         NS_WARNING("Invalid profile directory passed to content process.");
         profileDir = nullptr;
       }
 #endif /* XP_MACOSX && MOZ_CONTENT_SANDBOX */
     }
   }
 
-#ifdef ANDROID
-  // Android is different; get the FD via gPrefsFd instead of a fixed fd.
-  MOZ_RELEASE_ASSERT(gPrefsFd != -1);
-  prefsHandle = Some(base::FileDescriptor(gPrefsFd, /* auto_close */ true));
-
-  FileDescriptor::UniquePlatformHandle handle(gPrefMapFd);
-  prefMapHandle.emplace(handle.get());
-#elif XP_UNIX
-  prefsHandle = Some(base::FileDescriptor(kPrefsFileDescriptor,
-                                          /* auto_close */ true));
-
-  // The FileDescriptor constructor will clone this handle when constructed,
-  // so store it in a UniquePlatformHandle to make sure the original gets
-  // closed.
-  FileDescriptor::UniquePlatformHandle handle(kPrefMapFileDescriptor);
-  prefMapHandle.emplace(handle.get());
-#endif
-
   // Did we find all the mandatory flags?
   if (childID.isNothing() || isForBrowser.isNothing() ||
-      prefsHandle.isNothing() || prefsLen.isNothing() ||
-      prefMapHandle.isNothing() || prefMapSize.isNothing() ||
       schedulerPrefs.isNothing() || parentBuildID.isNothing()) {
     return false;
   }
 
-  // Init the shared-memory base preference mapping first, so that only changed
-  // preferences wind up in heap memory.
-  Preferences::InitSnapshot(prefMapHandle.ref(), *prefMapSize);
-
-  // Set up early prefs from the shared memory.
-  base::SharedMemory shm;
-  if (!shm.SetHandle(*prefsHandle, /* read_only */ true)) {
-    NS_ERROR("failed to open shared memory in the child");
+  SharedPreferenceDeserializer deserializer;
+  if (!deserializer.DeserializeFromSharedMemory(prefsHandle, prefMapHandle,
+                                                prefsLen, prefMapSize)) {
     return false;
   }
-  if (!shm.Map(*prefsLen)) {
-    NS_ERROR("failed to map shared memory in the child");
-    return false;
-  }
-  Preferences::DeserializePreferences(static_cast<char*>(shm.memory()),
-                                      *prefsLen);
 
   Scheduler::SetPrefs(*schedulerPrefs);
 
   if (recordreplay::IsMiddleman()) {
     recordreplay::parent::InitializeMiddleman(aArgc, aArgv, ParentPid(),
-                                              *prefsHandle, *prefMapHandle);
+                                              deserializer.GetPrefsHandle(),
+                                              deserializer.GetPrefMapHandle());
   }
 
   mContent.Init(IOThreadChild::message_loop(), ParentPid(), *parentBuildID,
                 IOThreadChild::channel(), *childID, *isForBrowser);
 
   mXREEmbed.Start();
 #if (defined(XP_MACOSX)) && defined(MOZ_CONTENT_SANDBOX)
   mContent.SetProfileDir(profileDir);
--- a/dom/ipc/ContentProcess.h
+++ b/dom/ipc/ContentProcess.h
@@ -40,19 +40,12 @@ class ContentProcess : public mozilla::i
 #if defined(XP_WIN)
   // This object initializes and configures COM.
   mozilla::mscom::MainThreadRuntime mCOMRuntime;
 #endif
 
   DISALLOW_EVIL_CONSTRUCTORS(ContentProcess);
 };
 
-#ifdef ANDROID
-// Android doesn't use -prefsHandle or -prefMapHandle. It gets those FDs
-// another way.
-void SetPrefsFd(int aFd);
-void SetPrefMapFd(int aFd);
-#endif
-
 }  // namespace dom
 }  // namespace mozilla
 
 #endif  // ifndef dom_tabs_ContentThread_h
--- a/ipc/glue/ProcessUtils.h
+++ b/ipc/glue/ProcessUtils.h
@@ -2,19 +2,77 @@
 /* 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_ipc_ProcessUtils_h
 #define mozilla_ipc_ProcessUtils_h
 
+#include "FileDescriptor.h"
+#include "base/shared_memory.h"
+
 namespace mozilla {
 namespace ipc {
 
 // You probably should call ContentChild::SetProcessName instead of calling
 // this directly.
-void SetThisProcessName(const char *aName);
+void SetThisProcessName(const char* aName);
+
+class SharedPreferenceSerializer final {
+ public:
+  SharedPreferenceSerializer();
+  SharedPreferenceSerializer(SharedPreferenceSerializer&& aOther);
+  ~SharedPreferenceSerializer();
+
+  bool SerializeToSharedMemory();
+
+  base::SharedMemoryHandle GetSharedMemoryHandle() const {
+    return mShm.handle();
+  }
+
+  const FileDescriptor::UniquePlatformHandle& GetPrefMapHandle() const {
+    return mPrefMapHandle;
+  }
+
+  nsACString::size_type GetPrefLength() const { return mPrefs.Length(); }
+
+  size_t GetPrefMapSize() const { return mPrefMapSize; }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SharedPreferenceSerializer);
+  size_t mPrefMapSize;
+  FileDescriptor::UniquePlatformHandle mPrefMapHandle;
+  base::SharedMemory mShm;
+  nsAutoCStringN<1024> mPrefs;
+};
+
+class SharedPreferenceDeserializer final {
+ public:
+  SharedPreferenceDeserializer();
+  ~SharedPreferenceDeserializer();
+
+  bool DeserializeFromSharedMemory(char* aPrefsHandleStr,
+                                   char* aPrefMapHandleStr, char* aPrefsLenStr,
+                                   char* aPrefMapSizeStr);
+
+  const base::SharedMemoryHandle& GetPrefsHandle() const;
+  const FileDescriptor& GetPrefMapHandle() const;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SharedPreferenceDeserializer);
+  Maybe<base::SharedMemoryHandle> mPrefsHandle;
+  Maybe<FileDescriptor> mPrefMapHandle;
+  Maybe<size_t> mPrefsLen;
+  Maybe<size_t> mPrefMapSize;
+};
+
+#ifdef ANDROID
+// Android doesn't use -prefsHandle or -prefMapHandle. It gets those FDs
+// another way.
+void SetPrefsFd(int aFd);
+void SetPrefMapFd(int aFd);
+#endif
 
 }  // namespace ipc
 }  // namespace mozilla
 
 #endif  // ifndef mozilla_ipc_ProcessUtils_h
new file mode 100644
--- /dev/null
+++ b/ipc/glue/ProcessUtils_common.cpp
@@ -0,0 +1,171 @@
+/* -*- 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 "ProcessUtils.h"
+
+#include "mozilla/Preferences.h"
+
+namespace mozilla {
+namespace ipc {
+
+SharedPreferenceSerializer::SharedPreferenceSerializer() : mPrefMapSize(0) {
+  MOZ_COUNT_CTOR(SharedPreferenceSerializer);
+}
+
+SharedPreferenceSerializer::~SharedPreferenceSerializer() {
+  MOZ_COUNT_DTOR(SharedPreferenceSerializer);
+}
+
+SharedPreferenceSerializer::SharedPreferenceSerializer(
+    SharedPreferenceSerializer&& aOther)
+    : mPrefMapSize(aOther.mPrefMapSize),
+      mPrefMapHandle(std::move(aOther.mPrefMapHandle)),
+      mShm(std::move(aOther.mShm)),
+      mPrefs(std::move(aOther.mPrefs)) {
+  MOZ_COUNT_CTOR(SharedPreferenceSerializer);
+}
+
+bool SharedPreferenceSerializer::SerializeToSharedMemory() {
+  mPrefMapHandle =
+      Preferences::EnsureSnapshot(&mPrefMapSize).ClonePlatformHandle();
+
+  // Serialize the early prefs.
+  Preferences::SerializePreferences(mPrefs);
+
+  // Set up the shared memory.
+  if (!mShm.Create(mPrefs.Length())) {
+    NS_ERROR("failed to create shared memory in the parent");
+    return false;
+  }
+  if (!mShm.Map(mPrefs.Length())) {
+    NS_ERROR("failed to map shared memory in the parent");
+    return false;
+  }
+
+  // Copy the serialized prefs into the shared memory.
+  memcpy(static_cast<char*>(mShm.memory()), mPrefs.get(), mPrefs.Length());
+
+  return true;
+}
+
+#ifdef ANDROID
+static int gPrefsFd = -1;
+static int gPrefMapFd = -1;
+
+void SetPrefsFd(int aFd) { gPrefsFd = aFd; }
+
+void SetPrefMapFd(int aFd) { gPrefMapFd = aFd; }
+#endif
+
+SharedPreferenceDeserializer::SharedPreferenceDeserializer() {
+  MOZ_COUNT_CTOR(SharedPreferenceDeserializer);
+}
+
+SharedPreferenceDeserializer::~SharedPreferenceDeserializer() {
+  MOZ_COUNT_DTOR(SharedPreferenceDeserializer);
+}
+
+bool SharedPreferenceDeserializer::DeserializeFromSharedMemory(
+    char* aPrefsHandleStr, char* aPrefMapHandleStr, char* aPrefsLenStr,
+    char* aPrefMapSizeStr) {
+  // Parses an arg containing a pointer-sized-integer.
+  auto parseUIntPtrArg = [](char*& aArg) {
+    // ContentParent uses %zu to print a word-sized unsigned integer. So
+    // even though strtoull() returns a long long int, it will fit in a
+    // uintptr_t.
+    return uintptr_t(strtoull(aArg, &aArg, 10));
+  };
+
+#ifdef XP_WIN
+  auto parseHandleArg = [&](char*& aArg) {
+    return HANDLE(parseUIntPtrArg(aArg));
+  };
+
+  mPrefsHandle = Some(parseHandleArg(aPrefsHandleStr));
+  if (aPrefsHandleStr[0] != '\0') {
+    return false;
+  }
+
+  // The FileDescriptor constructor will clone this handle when constructed,
+  // so store it in a UniquePlatformHandle to make sure the original gets
+  // closed.
+  FileDescriptor::UniquePlatformHandle handle(
+      parseHandleArg(aPrefMapHandleStr));
+  if (aPrefMapHandleStr[0] != '\0') {
+    return false;
+  }
+
+  mPrefMapHandle.emplace(handle.get());
+#endif
+
+  mPrefsLen = Some(parseUIntPtrArg(aPrefsLenStr));
+  if (aPrefsLenStr[0] != '\0') {
+    return false;
+  }
+
+  mPrefMapSize = Some(parseUIntPtrArg(aPrefMapSizeStr));
+  if (aPrefMapSizeStr[0] != '\0') {
+    return false;
+  }
+
+#ifdef ANDROID
+  // Android is different; get the FD via gPrefsFd instead of a fixed fd.
+  MOZ_RELEASE_ASSERT(gPrefsFd != -1);
+  mPrefsHandle = Some(base::FileDescriptor(gPrefsFd, /* auto_close */ true));
+
+  FileDescriptor::UniquePlatformHandle handle(gPrefMapFd);
+  mPrefMapHandle.emplace(handle.get());
+#elif XP_UNIX
+  mPrefsHandle = Some(base::FileDescriptor(kPrefsFileDescriptor,
+                                           /* auto_close */ true));
+
+  // The FileDescriptor constructor will clone this handle when constructed,
+  // so store it in a UniquePlatformHandle to make sure the original gets
+  // closed.
+  FileDescriptor::UniquePlatformHandle handle(kPrefMapFileDescriptor);
+  mPrefMapHandle.emplace(handle.get());
+#endif
+
+  if (mPrefsHandle.isNothing() || mPrefsLen.isNothing() ||
+      mPrefMapHandle.isNothing() || mPrefMapSize.isNothing()) {
+    return false;
+  }
+
+  // Init the shared-memory base preference mapping first, so that only changed
+  // preferences wind up in heap memory.
+  Preferences::InitSnapshot(mPrefMapHandle.ref(), *mPrefMapSize);
+
+  // Set up early prefs from the shared memory.
+  base::SharedMemory shm;
+  if (!shm.SetHandle(*mPrefsHandle, /* read_only */ true)) {
+    NS_ERROR("failed to open shared memory in the child");
+    return false;
+  }
+  if (!shm.Map(*mPrefsLen)) {
+    NS_ERROR("failed to map shared memory in the child");
+    return false;
+  }
+  Preferences::DeserializePreferences(static_cast<char*>(shm.memory()),
+                                      *mPrefsLen);
+
+  return true;
+}
+
+const base::SharedMemoryHandle& SharedPreferenceDeserializer::GetPrefsHandle()
+    const {
+  MOZ_ASSERT(mPrefsHandle.isSome());
+
+  return mPrefsHandle.ref();
+}
+
+const FileDescriptor& SharedPreferenceDeserializer::GetPrefMapHandle() const {
+  MOZ_ASSERT(mPrefMapHandle.isSome());
+
+  return mPrefMapHandle.ref();
+}
+
+}  // namespace ipc
+}  // namespace mozilla
\ No newline at end of file
--- a/ipc/glue/moz.build
+++ b/ipc/glue/moz.build
@@ -158,16 +158,17 @@ UNIFIED_SOURCES += [
     'IPCStreamDestination.cpp',
     'IPCStreamParent.cpp',
     'IPCStreamSource.cpp',
     'IPCStreamUtils.cpp',
     'MessageChannel.cpp',
     'MessageLink.cpp',
     'MessagePump.cpp',
     'ProcessChild.cpp',
+    'ProcessUtils_common.cpp',
     'ProtocolUtils.cpp',
     'ScopedXREEmbed.cpp',
     'SharedMemory.cpp',
     'Shmem.cpp',
     'StringUtil.cpp',
     'URIUtils.cpp',
 ]
 
--- a/netwerk/ipc/SocketProcessHost.cpp
+++ b/netwerk/ipc/SocketProcessHost.cpp
@@ -1,15 +1,13 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "base/shared_memory.h"
-
 #include "SocketProcessHost.h"
 #include "SocketProcessParent.h"
 #include "nsAppRunner.h"
 
 namespace mozilla {
 namespace net {
 
 SocketProcessHost::SocketProcessHost(Listener* aListener)
@@ -27,79 +25,59 @@ SocketProcessHost::~SocketProcessHost() 
 
 bool SocketProcessHost::Launch() {
   MOZ_ASSERT(mLaunchPhase == LaunchPhase::Unlaunched);
   MOZ_ASSERT(!mSocketProcessParent);
   MOZ_ASSERT(NS_IsMainThread());
 
   std::vector<std::string> extraArgs;
 
-  // TODO: reduce duplicate code about preference below. See bug 1484774.
-  // Prefs information is passed via anonymous shared memory to avoid bloating
-  // the command line.
-  size_t prefMapSize;
-  auto prefMapHandle =
-      Preferences::EnsureSnapshot(&prefMapSize).ClonePlatformHandle();
+  nsAutoCString parentBuildID(mozilla::PlatformBuildID());
+  extraArgs.push_back("-parentBuildID");
+  extraArgs.push_back(parentBuildID.get());
 
-  // Serialize the early prefs.
-  nsAutoCStringN<1024> prefs;
-  Preferences::SerializePreferences(prefs);
-
-  // Set up the shared memory.
-  base::SharedMemory shm;
-  if (!shm.Create(prefs.Length())) {
-    NS_ERROR("failed to create shared memory in the parent");
+  SharedPreferenceSerializer prefSerializer;
+  if (!prefSerializer.SerializeToSharedMemory()) {
     return false;
   }
-  if (!shm.Map(prefs.Length())) {
-    NS_ERROR("failed to map shared memory in the parent");
-    return false;
-  }
-
-  // Copy the serialized prefs into the shared memory.
-  memcpy(static_cast<char*>(shm.memory()), prefs.get(), prefs.Length());
 
   // Formats a pointer or pointer-sized-integer as a string suitable for passing
   // in an arguments list.
   auto formatPtrArg = [](auto arg) {
     return nsPrintfCString("%zu", uintptr_t(arg));
   };
 
 #if defined(XP_WIN)
   // Record the handle as to-be-shared, and pass it via a command flag. This
   // works because Windows handles are system-wide.
-  HANDLE prefsHandle = shm.handle();
+  HANDLE prefsHandle = prefSerializer.GetSharedMemoryHandle();
   AddHandleToShare(prefsHandle);
-  AddHandleToShare(prefMapHandle.get());
+  AddHandleToShare(prefSerializer.GetPrefMapHandle().get());
   extraArgs.push_back("-prefsHandle");
   extraArgs.push_back(formatPtrArg(prefsHandle).get());
   extraArgs.push_back("-prefMapHandle");
-  extraArgs.push_back(formatPtrArg(prefMapHandle.get()).get());
+  extraArgs.push_back(
+      formatPtrArg(prefSerializer.GetPrefMapHandle().get()).get());
 #else
   // In contrast, Unix fds are per-process. So remap the fd to a fixed one that
   // will be used in the child.
   // XXX: bug 1440207 is about improving how fixed fds are used.
   //
   // Note: on Android, AddFdToRemap() sets up the fd to be passed via a Parcel,
   // and the fixed fd isn't used. However, we still need to mark it for
   // remapping so it doesn't get closed in the child.
-  AddFdToRemap(shm.handle().fd, kPrefsFileDescriptor);
-  AddFdToRemap(prefMapHandle.get(), kPrefMapFileDescriptor);
+  AddFdToRemap(prefSerializer.GetSharedMemoryHandle().fd, kPrefsFileDescriptor);
+  AddFdToRemap(prefSerializer.GetPrefMapHandle().get(), kPrefMapFileDescriptor);
 #endif
 
   // Pass the lengths via command line flags.
   extraArgs.push_back("-prefsLen");
-  extraArgs.push_back(formatPtrArg(prefs.Length()).get());
-
+  extraArgs.push_back(formatPtrArg(prefSerializer.GetPrefLength()).get());
   extraArgs.push_back("-prefMapSize");
-  extraArgs.push_back(formatPtrArg(prefMapSize).get());
-
-  nsAutoCString parentBuildID(mozilla::PlatformBuildID());
-  extraArgs.push_back("-parentBuildID");
-  extraArgs.push_back(parentBuildID.get());
+  extraArgs.push_back(formatPtrArg(prefSerializer.GetPrefMapSize()).get());
 
   mLaunchPhase = LaunchPhase::Waiting;
   if (!GeckoChildProcessHost::LaunchAndWaitForProcessHandle(extraArgs)) {
     mLaunchPhase = LaunchPhase::Complete;
     return false;
   }
 
   return true;
--- a/netwerk/ipc/SocketProcessImpl.cpp
+++ b/netwerk/ipc/SocketProcessImpl.cpp
@@ -27,36 +27,21 @@ SocketProcessImpl::~SocketProcessImpl() 
 bool SocketProcessImpl::Init(int aArgc, char* aArgv[]) {
 #ifdef OS_POSIX
   if (PR_GetEnv("MOZ_DEBUG_SOCKET_PROCESS")) {
     printf_stderr("\n\nSOCKETPROCESSnSOCKETPROCESS\n  debug me @ %d\n\n",
                   base::GetCurrentProcId());
     sleep(30);
   }
 #endif
-  // TODO: reduce duplicate code about preference below. See bug 1484774.
-  Maybe<base::SharedMemoryHandle> prefsHandle;
-  Maybe<FileDescriptor> prefMapHandle;
-  Maybe<size_t> prefsLen;
-  Maybe<size_t> prefMapSize;
   char* parentBuildID = nullptr;
-
-  // Parses an arg containing a pointer-sized-integer.
-  auto parseUIntPtrArg = [](char*& aArg) {
-    // ContentParent uses %zu to print a word-sized unsigned integer. So
-    // even though strtoull() returns a long long int, it will fit in a
-    // uintptr_t.
-    return uintptr_t(strtoull(aArg, &aArg, 10));
-  };
-
-#ifdef XP_WIN
-  auto parseHandleArg = [&](char*& aArg) {
-    return HANDLE(parseUIntPtrArg(aArg));
-  };
-#endif
+  char* prefsHandle = nullptr;
+  char* prefMapHandle = nullptr;
+  char* prefsLen = nullptr;
+  char* prefMapSize = nullptr;
 
   for (int i = 1; i < aArgc; i++) {
     if (!aArgv[i]) {
       continue;
     }
 
     if (strcmp(aArgv[i], "-parentBuildID") == 0) {
       if (++i == aArgc) {
@@ -65,85 +50,41 @@ bool SocketProcessImpl::Init(int aArgc, 
 
       parentBuildID = aArgv[i];
 
 #ifdef XP_WIN
     } else if (strcmp(aArgv[i], "-prefsHandle") == 0) {
       if (++i == aArgc) {
         return false;
       }
-      char* str = aArgv[i];
-      prefsHandle = Some(parseHandleArg(str));
-      if (str[0] != '\0') {
-        return false;
-      }
-
+      prefsHandle = aArgv[i];
     } else if (strcmp(aArgv[i], "-prefMapHandle") == 0) {
       if (++i == aArgc) {
         return false;
       }
-      char* str = aArgv[i];
-      // The FileDescriptor constructor will clone this handle when constructed,
-      // so store it in a UniquePlatformHandle to make sure the original gets
-      // closed.
-      FileDescriptor::UniquePlatformHandle handle(parseHandleArg(str));
-      prefMapHandle.emplace(handle.get());
-      if (str[0] != '\0') {
-        return false;
-      }
+      prefMapHandle = aArgv[i];
 #endif
     } else if (strcmp(aArgv[i], "-prefsLen") == 0) {
       if (++i == aArgc) {
         return false;
       }
-      char* str = aArgv[i];
-      prefsLen = Some(parseUIntPtrArg(str));
-      if (str[0] != '\0') {
-        return false;
-      }
-
+      prefsLen = aArgv[i];
     } else if (strcmp(aArgv[i], "-prefMapSize") == 0) {
       if (++i == aArgc) {
         return false;
       }
-      char* str = aArgv[i];
-      prefMapSize = Some(parseUIntPtrArg(str));
-      if (str[0] != '\0') {
-        return false;
-      }
+      prefMapSize = aArgv[i];
     }
   }
 
-#ifdef XP_UNIX
-  prefsHandle = Some(base::FileDescriptor(kPrefsFileDescriptor,
-                                          /* auto_close */ true));
-
-  // The FileDescriptor constructor will clone this handle when constructed,
-  // so store it in a UniquePlatformHandle to make sure the original gets
-  // closed.
-  FileDescriptor::UniquePlatformHandle handle(kPrefMapFileDescriptor);
-  prefMapHandle.emplace(handle.get());
-#endif
-
-  // Init the shared-memory base preference mapping first, so that only changed
-  // preferences wind up in heap memory.
-  Preferences::InitSnapshot(prefMapHandle.ref(), *prefMapSize);
-
-  // Set up early prefs from the shared memory.
-  base::SharedMemory shm;
-  if (!shm.SetHandle(*prefsHandle, /* read_only */ true)) {
-    NS_ERROR("failed to open shared memory in the child");
+  SharedPreferenceDeserializer deserializer;
+  if (!deserializer.DeserializeFromSharedMemory(prefsHandle, prefMapHandle,
+                                                prefsLen, prefMapSize)) {
     return false;
   }
-  if (!shm.Map(*prefsLen)) {
-    NS_ERROR("failed to map shared memory in the child");
-    return false;
-  }
-  Preferences::DeserializePreferences(static_cast<char*>(shm.memory()),
-                                      *prefsLen);
 
   if (NS_FAILED(NS_InitXPCOM2(nullptr, nullptr, nullptr))) {
     return false;
   }
 
   return mSocketProcessChild.Init(ParentPid(), parentBuildID,
                                   IOThreadChild::message_loop(),
                                   IOThreadChild::channel());
--- a/toolkit/xre/nsEmbedFunctions.cpp
+++ b/toolkit/xre/nsEmbedFunctions.cpp
@@ -50,16 +50,17 @@
 #include "base/at_exit.h"
 #include "base/command_line.h"
 #include "base/message_loop.h"
 #include "base/process_util.h"
 #include "chrome/common/child_process.h"
 #if defined(MOZ_WIDGET_ANDROID)
 #include "chrome/common/ipc_channel.h"
 #include "mozilla/jni/Utils.h"
+#include "ProcessUtils.h"
 #endif  //  defined(MOZ_WIDGET_ANDROID)
 
 #include "mozilla/AbstractThread.h"
 #include "mozilla/FilePreferences.h"
 #include "mozilla/RDDProcessImpl.h"
 
 #include "mozilla/ipc/BrowserProcessSubThread.h"
 #include "mozilla/ipc/GeckoChildProcessHost.h"
@@ -221,18 +222,18 @@ namespace mozilla {
 namespace startup {
 GeckoProcessType sChildProcessType = GeckoProcessType_Default;
 }  // namespace startup
 }  // namespace mozilla
 
 #if defined(MOZ_WIDGET_ANDROID)
 void XRE_SetAndroidChildFds(JNIEnv* env, const XRE_AndroidChildFds& fds) {
   mozilla::jni::SetGeckoThreadEnv(env);
-  mozilla::dom::SetPrefsFd(fds.mPrefsFd);
-  mozilla::dom::SetPrefMapFd(fds.mPrefMapFd);
+  mozilla::ipc::SetPrefsFd(fds.mPrefsFd);
+  mozilla::ipc::SetPrefMapFd(fds.mPrefMapFd);
   IPC::Channel::SetClientChannelFd(fds.mIpcFd);
   CrashReporter::SetNotificationPipeForChild(fds.mCrashFd);
   CrashReporter::SetCrashAnnotationPipeForChild(fds.mCrashAnnotationFd);
 }
 #endif  // defined(MOZ_WIDGET_ANDROID)
 
 void XRE_SetProcessType(const char* aProcessTypeString) {
   static bool called = false;