Bug 1513057 - P1: Start the new socket process basics (prefs, full xpcom init, logging, no sandboxing) r=mayhemer,dragana
☠☠ backed out by 3f76ed638d83 ☠ ☠
authorKershaw Chang <kershaw@mozilla.com>
Fri, 11 Jan 2019 14:07:47 +0000
changeset 513455 4e94bbb9031517d9953adc24b6ff5646600df028
parent 513454 70cf6c6c51b4e162394552140d92b7266603f47d
child 513456 c900ac2519f5007a28b201bbc00a95873ac38f9c
push id1953
push userffxbld-merge
push dateMon, 11 Mar 2019 12:10:20 +0000
treeherdermozilla-release@9c35dcbaa899 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmayhemer, dragana
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: Start the new socket process basics (prefs, full xpcom init, logging, no sandboxing) r=mayhemer,dragana Differential Revision: https://phabricator.services.mozilla.com/D14148
docshell/base/timeline/TimelineConsumers.cpp
dom/ipc/PContent.ipdl
dom/ipc/PrefsTypes.ipdlh
dom/ipc/moz.build
ipc/glue/GeckoChildProcessHost.cpp
modules/libpref/init/all.js
netwerk/base/nsIOService.cpp
netwerk/base/nsIOService.h
netwerk/ipc/PSocketProcess.ipdl
netwerk/ipc/SocketProcessChild.cpp
netwerk/ipc/SocketProcessChild.h
netwerk/ipc/SocketProcessHost.cpp
netwerk/ipc/SocketProcessHost.h
netwerk/ipc/SocketProcessImpl.cpp
netwerk/ipc/SocketProcessImpl.h
netwerk/ipc/SocketProcessLogging.h
netwerk/ipc/SocketProcessParent.cpp
netwerk/ipc/SocketProcessParent.h
netwerk/ipc/moz.build
netwerk/protocol/http/nsHttpHandler.cpp
toolkit/xre/nsAppRunner.cpp
toolkit/xre/nsEmbedFunctions.cpp
xpcom/build/GeckoProcessTypes.h
xpcom/build/nsXULAppAPI.h
xpcom/system/nsIXULRuntime.idl
--- a/docshell/base/timeline/TimelineConsumers.cpp
+++ b/docshell/base/timeline/TimelineConsumers.cpp
@@ -29,17 +29,20 @@ StaticRefPtr<TimelineConsumers> Timeline
 // in these cases.
 bool TimelineConsumers::sInShutdown = false;
 
 already_AddRefed<TimelineConsumers> TimelineConsumers::Get() {
   // Using this class is not supported yet for other processes other than
   // parent or content. To avoid accidental checks to methods like `IsEmpty`,
   // which would probably always be true in those cases, assert here.
   // Remember, there will be different singletons available to each process.
-  MOZ_ASSERT(XRE_IsContentProcess() || XRE_IsParentProcess());
+
+  // TODO: we have to avoid calling this function in socket process.
+  MOZ_ASSERT(XRE_IsContentProcess() || XRE_IsParentProcess() ||
+             XRE_IsSocketProcess());
 
   // If we are shutting down, don't bother doing anything. Note: we can only
   // know whether or not we're in shutdown if we're instantiated.
   if (sInShutdown) {
     return nullptr;
   }
 
   // Note: We don't simply check `sInstance` for null-ness here, since otherwise
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -55,32 +55,32 @@ include PluginTypes;
 include ProtocolTypes;
 include PBackgroundSharedTypes;
 include PContentPermission;
 include ServiceWorkerConfiguration;
 include GraphicsMessages;
 include MemoryReportTypes;
 include ClientIPCTypes;
 include HangTypes;
+include PrefsTypes;
 
 // Workaround to prevent error if PContentChild.cpp & PContentBridgeParent.cpp
 // are put into different UnifiedProtocolsXX.cpp files.
 // XXX Remove this once bug 1069073 is fixed
 include "mozilla/dom/PContentBridgeParent.h";
 
 using refcounted class nsIDOMGeoPosition from "nsGeoPositionIPCSerialiser.h";
 using refcounted class nsIAlertNotification from "mozilla/AlertNotificationIPCSerializer.h";
 
 using struct ChromePackage from "mozilla/chrome/RegistryMessageUtils.h";
 using struct SubstitutionMapping from "mozilla/chrome/RegistryMessageUtils.h";
 using struct OverrideMapping from "mozilla/chrome/RegistryMessageUtils.h";
 using base::ProcessId from "base/process.h";
 using struct IPC::Permission from "mozilla/net/NeckoMessageUtils.h";
 using class IPC::Principal from "mozilla/dom/PermissionMessageUtils.h";
-using struct mozilla::null_t from "ipc/IPCMessageUtils.h";
 using mozilla::a11y::IHandlerControlHolder from "mozilla/a11y/IPCTypes.h";
 using mozilla::dom::NativeThreadId from "mozilla/dom/TabMessageUtils.h";
 using mozilla::hal::ProcessPriority from "mozilla/HalTypes.h";
 using mozilla::gfx::IntSize from "mozilla/gfx/2D.h";
 using mozilla::dom::TabId from "mozilla/dom/ipc/IdType.h";
 using mozilla::dom::ContentParentId from "mozilla/dom/ipc/IdType.h";
 using mozilla::LayoutDeviceIntPoint from "Units.h";
 using struct LookAndFeelInt from "mozilla/widget/WidgetMessageUtils.h";
@@ -141,40 +141,16 @@ struct FontPatternListEntry {
 
 // Wrap the Font*ListEntry records in a union so the SetXPCOMProcessAttributes
 // message can pass an array of either type.
 union SystemFontListEntry {
     FontFamilyListEntry;
     FontPatternListEntry;
 };
 
-union PrefValue {
-  nsCString;
-  int32_t;
-  bool;
-};
-
-union MaybePrefValue {
-  PrefValue;
-  null_t;
-};
-
-// This serialization form mirrors that used in mozilla::Pref in
-// Preferences.cpp. The two should be kept in sync, e.g. if something is added
-// to one it should also be added to the other.
-//
-// Note: there is no need to pass the isSticky attribute because that's an
-// immutable attribute obtained from file at startup.
-struct Pref {
-  nsCString name;
-  bool isLocked;
-  MaybePrefValue defaultValue;
-  MaybePrefValue userValue;
-};
-
 struct DataStorageItem {
   nsCString key;
   nsCString value;
   DataStorageType type;
 };
 
 struct DataStorageEntry {
   DataStorageItem[] items;
new file mode 100644
--- /dev/null
+++ b/dom/ipc/PrefsTypes.ipdlh
@@ -0,0 +1,36 @@
+/* -*- 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/. */
+
+using struct mozilla::null_t from "ipc/IPCMessageUtils.h";
+
+namespace mozilla {
+namespace dom {
+
+union PrefValue {
+  nsCString;
+  int32_t;
+  bool;
+};
+
+union MaybePrefValue {
+  PrefValue;
+  null_t;
+};
+
+// This serialization form mirrors that used in mozilla::Pref in
+// Preferences.cpp. The two should be kept in sync, e.g. if something is added
+// to one it should also be added to the other.
+//
+// Note: there is no need to pass the isSticky attribute because that's an
+// immutable attribute obtained from file at startup.
+struct Pref {
+  nsCString name;
+  bool isLocked;
+  MaybePrefValue defaultValue;
+  MaybePrefValue userValue;
+};
+
+} // namespace dom
+} // namespace mozilla
--- a/dom/ipc/moz.build
+++ b/dom/ipc/moz.build
@@ -110,16 +110,17 @@ IPDL_SOURCES += [
     'PContentBridge.ipdl',
     'PContentPermission.ipdlh',
     'PContentPermissionRequest.ipdl',
     'PCycleCollectWithLogs.ipdl',
     'PFilePicker.ipdl',
     'PLoginReputation.ipdl',
     'PPluginWidget.ipdl',
     'PProcessHangMonitor.ipdl',
+    'PrefsTypes.ipdlh',
     'PTabContext.ipdlh',
     'PURLClassifier.ipdl',
     'PURLClassifierInfo.ipdlh',
     'PURLClassifierLocal.ipdl',
     'PWindowGlobal.ipdl',
     'ServiceWorkerConfiguration.ipdlh',
 ]
 
--- a/ipc/glue/GeckoChildProcessHost.cpp
+++ b/ipc/glue/GeckoChildProcessHost.cpp
@@ -149,17 +149,18 @@ GeckoChildProcessHost::~GeckoChildProces
 
 // static
 auto GeckoChildProcessHost::GetPathToBinary(FilePath& exePath,
                                             GeckoProcessType processType)
     -> BinaryPathType {
   if (sRunSelfAsContentProc && (processType == GeckoProcessType_Content ||
                                 processType == GeckoProcessType_GPU ||
                                 processType == GeckoProcessType_VR ||
-                                processType == GeckoProcessType_RDD)) {
+                                processType == GeckoProcessType_RDD ||
+                                processType == GeckoProcessType_Socket)) {
 #if defined(OS_WIN)
     wchar_t exePathBuf[MAXPATHLEN];
     if (!::GetModuleFileNameW(nullptr, exePathBuf, MAXPATHLEN)) {
       MOZ_CRASH("GetModuleFileNameW failed (FIXME)");
     }
 #if defined(MOZ_SANDBOX)
     // We need to start the child process using the real path, so that the
     // sandbox policy rules will match for DLLs loaded from the bin dir after
@@ -971,16 +972,19 @@ bool GeckoChildProcessHost::PerformAsync
     case GeckoProcessType_RDD:
       if (!PR_GetEnv("MOZ_DISABLE_RDD_SANDBOX")) {
         if (!mSandboxBroker.SetSecurityLevelForRDDProcess()) {
           return false;
         }
         shouldSandboxCurrentProcess = true;
       }
       break;
+    case GeckoProcessType_Socket:
+      // TODO - setup sandboxing for the socket process.
+      break;
     case GeckoProcessType_Default:
     default:
       MOZ_CRASH("Bad process type in GeckoChildProcessHost");
       break;
   };
 
   if (shouldSandboxCurrentProcess) {
     for (auto it = mAllowedFilesRead.begin(); it != mAllowedFilesRead.end();
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -1941,16 +1941,22 @@ pref("network.sts.poll_busy_wait_period_
 pref("network.sts.max_time_for_pr_close_during_shutdown", 5000);
 
 // When the polling socket pair we use to wake poll() up on demand doesn't
 // get signalled (is not readable) within this timeout, we try to repair it.
 // This timeout can be disabled by setting this pref to 0.
 // The value is expected in seconds.
 pref("network.sts.pollable_event_timeout", 6);
 
+// Start a separate socket process. Performing networking on the socket process
+// is control by a sepparate pref
+// ("network.http.network_access_on_socket_process.enabled").
+// Changing these prefs requires a restart.
+pref("network.process.enabled", false);
+
 // Enable/disable sni encryption.
 pref("network.security.esni.enabled", false);
 
 // 2147483647 == PR_INT32_MAX == ~2 GB
 pref("network.websocket.max-message-size", 2147483647);
 
 // Should we automatically follow http 3xx redirects during handshake
 pref("network.websocket.auto-follow-http-redirects", false);
--- a/netwerk/base/nsIOService.cpp
+++ b/netwerk/base/nsIOService.cpp
@@ -49,16 +49,18 @@
 #include "mozilla/ipc/URIUtils.h"
 #include "mozilla/net/NeckoChild.h"
 #include "mozilla/net/NeckoParent.h"
 #include "mozilla/dom/ClientInfo.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/ServiceWorkerDescriptor.h"
 #include "mozilla/net/CaptivePortalService.h"
 #include "mozilla/net/NetworkConnectivityService.h"
+#include "mozilla/net/SocketProcessHost.h"
+#include "mozilla/net/SocketProcessParent.h"
 #include "mozilla/Unused.h"
 #include "ReferrerPolicy.h"
 #include "nsContentSecurityManager.h"
 #include "nsContentUtils.h"
 
 namespace mozilla {
 namespace net {
 
@@ -73,16 +75,17 @@ using mozilla::dom::ServiceWorkerDescrip
 
 // Nb: these have been misnomers since bug 715770 removed the buffer cache.
 // "network.segment.count" and "network.segment.size" would be better names,
 // but the old names are still used to preserve backward compatibility.
 #define NECKO_BUFFER_CACHE_COUNT_PREF "network.buffer.cache.count"
 #define NECKO_BUFFER_CACHE_SIZE_PREF "network.buffer.cache.size"
 #define NETWORK_NOTIFY_CHANGED_PREF "network.notify.changed"
 #define NETWORK_CAPTIVE_PORTAL_PREF "network.captive-portal-service.enabled"
+#define WEBRTC_PREF_PREFIX "media.peerconnection."
 
 #define MAX_RECURSION_COUNT 50
 
 nsIOService *gIOService;
 static bool gHasWarnedUploadChannel2;
 static bool gCaptivePortalEnabled = false;
 static LazyLogModule gIOServiceLog("nsIOService");
 #undef LOG
@@ -195,28 +198,34 @@ nsIOService::nsIOService()
       mChannelEventSinks(NS_CHANNEL_EVENT_SINK_CATEGORY),
       mNetworkNotifyChanged(true),
       mTotalRequests(0),
       mCacheWon(0),
       mNetWon(0),
       mLastOfflineStateChange(PR_IntervalNow()),
       mLastConnectivityChange(PR_IntervalNow()),
       mLastNetworkLinkChange(PR_IntervalNow()),
-      mNetTearingDownStarted(0) {}
+      mNetTearingDownStarted(0),
+      mSocketProcess(nullptr) {}
 
 static const char *gCallbackPrefs[] = {
     PORT_PREF_PREFIX,
     MANAGE_OFFLINE_STATUS_PREF,
     NECKO_BUFFER_CACHE_COUNT_PREF,
     NECKO_BUFFER_CACHE_SIZE_PREF,
     NETWORK_NOTIFY_CHANGED_PREF,
     NETWORK_CAPTIVE_PORTAL_PREF,
     nullptr,
 };
 
+static const char *gCallbackPrefsForSocketProcess[] = {
+    WEBRTC_PREF_PREFIX,
+    nullptr,
+};
+
 nsresult nsIOService::Init() {
   // XXX hack until xpidl supports error info directly (bug 13423)
   nsCOMPtr<nsIErrorService> errorService = nsErrorService::GetOrCreate();
   MOZ_ALWAYS_TRUE(errorService);
   errorService->RegisterErrorStringBundle(NS_ERROR_MODULE_NETWORK,
                                           NECKO_MSGS_URL);
 
   InitializeCaptivePortalService();
@@ -234,16 +243,17 @@ nsresult nsIOService::Init() {
   nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
   if (observerService) {
     observerService->AddObserver(this, kProfileChangeNetTeardownTopic, true);
     observerService->AddObserver(this, kProfileChangeNetRestoreTopic, true);
     observerService->AddObserver(this, kProfileDoChange, true);
     observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, true);
     observerService->AddObserver(this, NS_NETWORK_LINK_TOPIC, true);
     observerService->AddObserver(this, NS_WIDGET_WAKE_OBSERVER_TOPIC, true);
+    observerService->AddObserver(this, NS_PREFSERVICE_READ_TOPIC_ID, true);
   } else
     NS_WARNING("failed to get observer service");
 
   Preferences::AddBoolVarCache(&sIsDataURIUniqueOpaqueOrigin,
                                "security.data_uri.unique_opaque_origin", false);
   Preferences::AddBoolVarCache(
       &sBlockToplevelDataUriNavigations,
       "security.data_uri.block_toplevel_data_uri_navigations", false);
@@ -351,16 +361,119 @@ already_AddRefed<nsIOService> nsIOServic
     if (NS_SUCCEEDED(ios->Init())) {
       MOZ_ASSERT(gIOService == ios.get());
       return ios.forget();
     }
   }
   return do_AddRef(gIOService);
 }
 
+class SocketProcessListenerProxy : public SocketProcessHost::Listener {
+ public:
+  SocketProcessListenerProxy() = default;
+  void OnProcessLaunchComplete(SocketProcessHost *aHost, bool aSucceeded) {
+    if (!gIOService) {
+      return;
+    }
+
+    gIOService->OnProcessLaunchComplete(aHost, aSucceeded);
+  }
+
+  void OnProcessUnexpectedShutdown(SocketProcessHost *aHost) {
+    if (!gIOService) {
+      return;
+    }
+
+    gIOService->OnProcessUnexpectedShutdown(aHost);
+  }
+};
+
+nsresult nsIOService::LaunchSocketProcess() {
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (XRE_GetProcessType() != GeckoProcessType_Default) {
+    return NS_OK;
+  }
+
+  if (mSocketProcess) {
+    return NS_OK;
+  }
+
+  if (!Preferences::GetBool("network.process.enabled", true)) {
+    LOG(("nsIOService skipping LaunchSocketProcess because of the pref"));
+    return NS_OK;
+  }
+
+  Preferences::RegisterPrefixCallbacks(
+      PREF_CHANGE_METHOD(nsIOService::NotifySocketProcessPrefsChanged),
+      gCallbackPrefsForSocketProcess, this);
+
+  // The subprocess is launched asynchronously, so we wait for a callback to
+  // acquire the IPDL actor.
+  mSocketProcess = new SocketProcessHost(new SocketProcessListenerProxy());
+  LOG(("nsIOService::LaunchSocketProcess"));
+  if (!mSocketProcess->Launch()) {
+    NS_WARNING("Failed to launch socket process!!");
+    DestroySocketProcess();
+    return NS_ERROR_FAILURE;
+  }
+
+  return NS_OK;
+}
+
+void nsIOService::DestroySocketProcess() {
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (XRE_GetProcessType() != GeckoProcessType_Default || !mSocketProcess) {
+    return;
+  }
+
+  Preferences::UnregisterPrefixCallbacks(
+      PREF_CHANGE_METHOD(nsIOService::NotifySocketProcessPrefsChanged),
+      gCallbackPrefsForSocketProcess, this);
+
+  mSocketProcess->Shutdown();
+  mSocketProcess = nullptr;
+}
+
+bool nsIOService::SocketProcessReady() {
+  return mSocketProcess && mSocketProcess->IsConnected();
+}
+
+void nsIOService::NotifySocketProcessPrefsChanged(const char *aName) {
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (!SocketProcessReady()) {
+    mQueuedPrefNames.AppendElement(aName);
+    return;
+  }
+
+  dom::Pref pref(nsCString(aName), /* isLocked */ false, null_t(), null_t());
+  Preferences::GetPreference(&pref);
+  Unused << mSocketProcess->GetActor()->SendPreferenceUpdate(pref);
+}
+
+void nsIOService::OnProcessLaunchComplete(SocketProcessHost *aHost,
+                                          bool aSucceeded) {
+  MOZ_ASSERT(NS_IsMainThread());
+
+  LOG(("nsIOService::OnProcessLaunchComplete aSucceeded=%d\n", aSucceeded));
+  for (auto name : mQueuedPrefNames) {
+    NotifySocketProcessPrefsChanged(name);
+  }
+  mQueuedPrefNames.Clear();
+}
+
+void nsIOService::OnProcessUnexpectedShutdown(SocketProcessHost *aHost) {
+  MOZ_ASSERT(NS_IsMainThread());
+
+  LOG(("nsIOService::OnProcessUnexpectedShutdown\n"));
+  DestroySocketProcess();
+}
+
 NS_IMPL_ISUPPORTS(nsIOService, nsIIOService, nsINetUtil, nsISpeculativeConnect,
                   nsIObserver, nsIIOServiceInternal, nsISupportsWeakReference)
 
 ////////////////////////////////////////////////////////////////////////////////
 
 nsresult nsIOService::RecheckCaptivePortal() {
   MOZ_ASSERT(NS_IsMainThread(), "Must be called on the main thread");
   if (!mCaptivePortalService) {
@@ -1288,24 +1401,29 @@ nsIOService::Observe(nsISupports *subjec
 
     SetOffline(true);
 
     if (mCaptivePortalService) {
       static_cast<CaptivePortalService *>(mCaptivePortalService.get())->Stop();
       mCaptivePortalService = nullptr;
     }
 
+    DestroySocketProcess();
   } else if (!strcmp(topic, NS_NETWORK_LINK_TOPIC)) {
     OnNetworkLinkEvent(NS_ConvertUTF16toUTF8(data).get());
   } else if (!strcmp(topic, NS_WIDGET_WAKE_OBSERVER_TOPIC)) {
     // coming back alive from sleep
     // this indirection brought to you by:
     // https://bugzilla.mozilla.org/show_bug.cgi?id=1152048#c19
     nsCOMPtr<nsIRunnable> wakeupNotifier = new nsWakeupNotifier(this);
     NS_DispatchToMainThread(wakeupNotifier);
+  } else if (!strcmp(topic, NS_PREFSERVICE_READ_TOPIC_ID)) {
+    // Launch socket process after we load user's pref. This is to make sure
+    // that socket process can get the latest prefs.
+    LaunchSocketProcess();
   }
 
   return NS_OK;
 }
 
 // nsINetUtil interface
 NS_IMETHODIMP
 nsIOService::ParseRequestContentType(const nsACString &aTypeHeader,
--- a/netwerk/base/nsIOService.h
+++ b/netwerk/base/nsIOService.h
@@ -43,16 +43,17 @@ class nsIPrefBranch;
 class nsIProtocolProxyService2;
 class nsIProxyInfo;
 class nsPISocketTransportService;
 
 namespace mozilla {
 namespace net {
 class NeckoChild;
 class nsAsyncRedirectVerifyHelper;
+class SocketProcessHost;
 
 class nsIOService final : public nsIIOService,
                           public nsIObserver,
                           public nsINetUtil,
                           public nsISpeculativeConnect,
                           public nsSupportsWeakReference,
                           public nsIIOServiceInternal {
  public:
@@ -108,16 +109,21 @@ class nsIOService final : public nsIIOSe
   void IncrementCacheWonRequestNumber() { mCacheWon++; }
   uint32_t GetCacheWonRequestNumber() { return mCacheWon; }
   void IncrementNetWonRequestNumber() { mNetWon++; }
   uint32_t GetNetWonRequestNumber() { return mNetWon; }
 
   // Used to trigger a recheck of the captive portal status
   nsresult RecheckCaptivePortal();
 
+  void OnProcessLaunchComplete(SocketProcessHost* aHost, bool aSucceeded);
+  void OnProcessUnexpectedShutdown(SocketProcessHost* aHost);
+  bool SocketProcessReady();
+  void NotifySocketProcessPrefsChanged(const char* aName);
+
  private:
   // These shouldn't be called directly:
   // - construct using GetInstance
   // - destroy using Release
   nsIOService();
   ~nsIOService();
   nsresult SetConnectivityInternal(bool aConnectivity);
 
@@ -157,16 +163,19 @@ class nsIOService final : public nsIIOSe
                                                    uint32_t aProxyFlags,
                                                    nsILoadInfo* aLoadInfo,
                                                    nsIChannel** result);
 
   nsresult SpeculativeConnectInternal(nsIURI* aURI, nsIPrincipal* aPrincipal,
                                       nsIInterfaceRequestor* aCallbacks,
                                       bool aAnonymous);
 
+  nsresult LaunchSocketProcess();
+  void DestroySocketProcess();
+
  private:
   bool mOffline;
   mozilla::Atomic<bool, mozilla::Relaxed> mOfflineForProfileChange;
   bool mManageLinkStatus;
   bool mConnectivity;
   // If true, the connectivity state will be mirrored by IOService.offline
   // meaning if !mConnectivity, GetOffline() will return true
   bool mOfflineMirrorsConnectivity;
@@ -209,16 +218,19 @@ class nsIOService final : public nsIIOSe
   // change has happened shortly before.
   mozilla::Atomic<PRIntervalTime> mLastOfflineStateChange;
   mozilla::Atomic<PRIntervalTime> mLastConnectivityChange;
   mozilla::Atomic<PRIntervalTime> mLastNetworkLinkChange;
 
   // Time a network tearing down started.
   mozilla::Atomic<PRIntervalTime> mNetTearingDownStarted;
 
+  SocketProcessHost* mSocketProcess;
+  nsTArray<const char*> mQueuedPrefNames;
+
  public:
   // Used for all default buffer sizes that necko allocates.
   static uint32_t gDefaultSegmentSize;
   static uint32_t gDefaultSegmentCount;
 };
 
 /**
  * Reference to the IO service singleton. May be null.
new file mode 100644
--- /dev/null
+++ b/netwerk/ipc/PSocketProcess.ipdl
@@ -0,0 +1,18 @@
+/* -*- 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 PrefsTypes;
+
+namespace mozilla {
+namespace net {
+
+protocol PSocketProcess
+{
+child:
+  async PreferenceUpdate(Pref pref);
+};
+
+} // namespace net
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/ipc/SocketProcessChild.cpp
@@ -0,0 +1,80 @@
+/* -*- 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 "SocketProcessChild.h"
+#include "SocketProcessLogging.h"
+
+#include "base/task.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/ipc/ProcessChild.h"
+#include "mozilla/Preferences.h"
+#include "nsDebugImpl.h"
+#include "nsThreadManager.h"
+#include "ProcessUtils.h"
+
+namespace mozilla {
+namespace net {
+
+using namespace ipc;
+
+SocketProcessChild::SocketProcessChild() {
+  LOG(("CONSTRUCT SocketProcessChild::SocketProcessChild\n"));
+  nsDebugImpl::SetMultiprocessMode("Socket");
+
+  MOZ_COUNT_CTOR(SocketProcessChild);
+}
+
+SocketProcessChild::~SocketProcessChild() {
+  LOG(("DESTRUCT SocketProcessChild::SocketProcessChild\n"));
+  MOZ_COUNT_DTOR(SocketProcessChild);
+}
+
+bool SocketProcessChild::Init(base::ProcessId aParentPid,
+                              const char* aParentBuildID, MessageLoop* aIOLoop,
+                              IPC::Channel* aChannel) {
+  if (NS_WARN_IF(NS_FAILED(nsThreadManager::get().Init()))) {
+    return false;
+  }
+  if (NS_WARN_IF(!Open(aChannel, aParentPid, aIOLoop))) {
+    return false;
+  }
+  // This must be sent before any IPDL message, which may hit sentinel
+  // errors due to parent and content processes having different
+  // versions.
+  MessageChannel* channel = GetIPCChannel();
+  if (channel && !channel->SendBuildIDsMatchMessage(aParentBuildID)) {
+    // We need to quit this process if the buildID doesn't match the parent's.
+    // This can occur when an update occurred in the background.
+    ProcessChild::QuickExit();
+  }
+
+  SetThisProcessName("Socket Process");
+  return true;
+}
+
+void SocketProcessChild::ActorDestroy(ActorDestroyReason aWhy) {
+  LOG(("SocketProcessChild::ActorDestroy\n"));
+
+  if (AbnormalShutdown == aWhy) {
+    NS_WARNING("Shutting down Socket process early due to a crash!");
+    ProcessChild::QuickExit();
+  }
+
+  XRE_ShutdownChildProcess();
+}
+
+void SocketProcessChild::CleanUp() {
+  LOG(("SocketProcessChild::CleanUp\n"));
+
+  NS_ShutdownXPCOM(nullptr);
+}
+
+IPCResult SocketProcessChild::RecvPreferenceUpdate(const Pref& aPref) {
+  Preferences::SetPreference(aPref);
+  return IPC_OK();
+}
+
+}  // namespace net
+}  // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/ipc/SocketProcessChild.h
@@ -0,0 +1,35 @@
+/* -*- 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/. */
+
+#ifndef mozilla_net_SocketProcessChild_h
+#define mozilla_net_SocketProcessChild_h
+
+#include "mozilla/net/PSocketProcessChild.h"
+
+namespace mozilla {
+namespace net {
+
+// The IPC actor implements PSocketProcessChild in child process.
+// This is allocated and kept alive by SocketProcessImpl.
+class SocketProcessChild final : public PSocketProcessChild {
+ public:
+  SocketProcessChild();
+  ~SocketProcessChild();
+
+  bool Init(base::ProcessId aParentPid, const char* aParentBuildID,
+            MessageLoop* aIOLoop, IPC::Channel* aChannel);
+
+  void ActorDestroy(ActorDestroyReason aWhy) override;
+
+  mozilla::ipc::IPCResult RecvPreferenceUpdate(const Pref& aPref) override;
+  void CleanUp();
+
+ private:
+};
+
+}  // namespace net
+}  // namespace mozilla
+
+#endif  // mozilla_net_SocketProcessChild_h
new file mode 100644
--- /dev/null
+++ b/netwerk/ipc/SocketProcessHost.cpp
@@ -0,0 +1,230 @@
+/* -*- 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)
+    : GeckoChildProcessHost(GeckoProcessType_Socket),
+      mListener(aListener),
+      mTaskFactory(this),
+      mLaunchPhase(LaunchPhase::Unlaunched),
+      mShutdownRequested(false),
+      mChannelClosed(false) {
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_COUNT_CTOR(SocketProcessHost);
+}
+
+SocketProcessHost::~SocketProcessHost() { MOZ_COUNT_DTOR(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();
+
+  // 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");
+    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();
+  AddHandleToShare(prefsHandle);
+  AddHandleToShare(prefMapHandle.get());
+  extraArgs.push_back("-prefsHandle");
+  extraArgs.push_back(formatPtrArg(prefsHandle).get());
+  extraArgs.push_back("-prefMapHandle");
+  extraArgs.push_back(formatPtrArg(prefMapHandle.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);
+#endif
+
+  // Pass the lengths via command line flags.
+  extraArgs.push_back("-prefsLen");
+  extraArgs.push_back(formatPtrArg(prefs.Length()).get());
+
+  extraArgs.push_back("-prefMapSize");
+  extraArgs.push_back(formatPtrArg(prefMapSize).get());
+
+  nsAutoCString parentBuildID(mozilla::PlatformBuildID());
+  extraArgs.push_back("-parentBuildID");
+  extraArgs.push_back(parentBuildID.get());
+
+  mLaunchPhase = LaunchPhase::Waiting;
+  if (!GeckoChildProcessHost::LaunchAndWaitForProcessHandle(extraArgs)) {
+    mLaunchPhase = LaunchPhase::Complete;
+    return false;
+  }
+
+  return true;
+}
+
+void SocketProcessHost::OnChannelConnected(int32_t peer_pid) {
+  MOZ_ASSERT(!NS_IsMainThread());
+
+  GeckoChildProcessHost::OnChannelConnected(peer_pid);
+
+  // Post a task to the main thread. Take the lock because mTaskFactory is not
+  // thread-safe.
+  RefPtr<Runnable> runnable;
+  {
+    MonitorAutoLock lock(mMonitor);
+    runnable = mTaskFactory.NewRunnableMethod(
+        &SocketProcessHost::OnChannelConnectedTask);
+  }
+  NS_DispatchToMainThread(runnable);
+}
+
+void SocketProcessHost::OnChannelError() {
+  MOZ_ASSERT(!NS_IsMainThread());
+
+  GeckoChildProcessHost::OnChannelError();
+
+  // Post a task to the main thread. Take the lock because mTaskFactory is not
+  // thread-safe.
+  RefPtr<Runnable> runnable;
+  {
+    MonitorAutoLock lock(mMonitor);
+    runnable =
+        mTaskFactory.NewRunnableMethod(&SocketProcessHost::OnChannelErrorTask);
+  }
+  NS_DispatchToMainThread(runnable);
+}
+
+void SocketProcessHost::OnChannelConnectedTask() {
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (mLaunchPhase == LaunchPhase::Waiting) {
+    InitAfterConnect(true);
+  }
+}
+
+void SocketProcessHost::OnChannelErrorTask() {
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (mLaunchPhase == LaunchPhase::Waiting) {
+    InitAfterConnect(false);
+  }
+}
+
+void SocketProcessHost::InitAfterConnect(bool aSucceeded) {
+  MOZ_ASSERT(mLaunchPhase == LaunchPhase::Waiting);
+  MOZ_ASSERT(!mSocketProcessParent);
+  MOZ_ASSERT(NS_IsMainThread());
+
+  mLaunchPhase = LaunchPhase::Complete;
+
+  if (aSucceeded) {
+    mSocketProcessParent = MakeUnique<SocketProcessParent>(this);
+    DebugOnly<bool> rv = mSocketProcessParent->Open(
+        GetChannel(), base::GetProcId(GetChildProcessHandle()));
+    MOZ_ASSERT(rv);
+  }
+
+  if (mListener) {
+    mListener->OnProcessLaunchComplete(this, aSucceeded);
+  }
+}
+
+void SocketProcessHost::Shutdown() {
+  MOZ_ASSERT(!mShutdownRequested);
+  MOZ_ASSERT(NS_IsMainThread());
+
+  mListener = nullptr;
+
+  if (mSocketProcessParent) {
+    // OnChannelClosed uses this to check if the shutdown was expected or
+    // unexpected.
+    mShutdownRequested = true;
+
+    // The channel might already be closed if we got here unexpectedly.
+    if (!mChannelClosed) {
+      mSocketProcessParent->Close();
+    }
+
+    return;
+  }
+
+  DestroyProcess();
+}
+
+void SocketProcessHost::OnChannelClosed() {
+  MOZ_ASSERT(NS_IsMainThread());
+
+  mChannelClosed = true;
+
+  if (!mShutdownRequested && mListener) {
+    // This is an unclean shutdown. Notify our listener that we're going away.
+    mListener->OnProcessUnexpectedShutdown(this);
+  } else {
+    DestroyProcess();
+  }
+
+  // Release the actor.
+  SocketProcessParent::Destroy(std::move(mSocketProcessParent));
+  MOZ_ASSERT(!mSocketProcessParent);
+}
+
+static void DelayedDeleteSubprocess(GeckoChildProcessHost* aSubprocess) {
+  XRE_GetIOMessageLoop()->PostTask(
+      MakeAndAddRef<DeleteTask<GeckoChildProcessHost>>(aSubprocess));
+}
+
+void SocketProcessHost::DestroyProcess() {
+  {
+    MonitorAutoLock lock(mMonitor);
+    mTaskFactory.RevokeAll();
+  }
+
+  MessageLoop::current()->PostTask(NewRunnableFunction(
+      "DestroySocketProcessRunnable", DelayedDeleteSubprocess, this));
+}
+
+}  // namespace net
+}  // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/ipc/SocketProcessHost.h
@@ -0,0 +1,108 @@
+/* -*- 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/. */
+
+#ifndef mozilla_net_SocketProcessHost_h
+#define mozilla_net_SocketProcessHost_h
+
+#include "mozilla/UniquePtr.h"
+#include "mozilla/ipc/GeckoChildProcessHost.h"
+#include "mozilla/ipc/TaskFactory.h"
+
+namespace mozilla {
+namespace net {
+
+class SocketProcessParent;
+
+// SocketProcessHost is the "parent process" container for a subprocess handle
+// and IPC connection. It owns the parent process IPDL actor, which in this
+// case, is a SocketProcessParent.
+// SocketProcessHost is allocated and managed by nsIOService in parent process.
+class SocketProcessHost final : public mozilla::ipc::GeckoChildProcessHost {
+  friend class SocketProcessParent;
+
+ public:
+  class Listener {
+   public:
+    NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Listener)
+
+    // Called when the process of launching the process is complete.
+    virtual void OnProcessLaunchComplete(SocketProcessHost* aHost,
+                                         bool aSucceeded) = 0;
+
+    // Called when the channel is closed but Shutdown() is not invoked.
+    virtual void OnProcessUnexpectedShutdown(SocketProcessHost* aHost) = 0;
+
+   protected:
+    virtual ~Listener() = default;
+  };
+
+ public:
+  explicit SocketProcessHost(Listener* listener);
+  ~SocketProcessHost();
+
+  // Launch the socket process asynchronously.
+  // The OnProcessLaunchComplete listener callback will be invoked
+  // either when a connection has been established, or if a connection
+  // could not be established due to an asynchronous error.
+  bool Launch();
+
+  // Inform the socket process that it should clean up its resources and shut
+  // down. This initiates an asynchronous shutdown sequence. After this method
+  // returns, it is safe for the caller to forget its pointer to the
+  // SocketProcessHost.
+  void Shutdown();
+
+  // Return the actor for the top-level actor of the process. Return null if
+  // the process is not connected.
+  SocketProcessParent* GetActor() const {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    return mSocketProcessParent.get();
+  }
+
+  bool IsConnected() const {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    return !!mSocketProcessParent;
+  }
+
+  // Called on the IO thread.
+  void OnChannelConnected(int32_t peer_pid) override;
+  void OnChannelError() override;
+
+ private:
+  // Called on the main thread.
+  void OnChannelConnectedTask();
+  void OnChannelErrorTask();
+
+  // Called on the main thread after a connection has been established.
+  void InitAfterConnect(bool aSucceeded);
+
+  // Called on the main thread when the mSocketParent actor is shutting down.
+  void OnChannelClosed();
+
+  void DestroyProcess();
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SocketProcessHost);
+
+  RefPtr<Listener> mListener;
+  mozilla::ipc::TaskFactory<SocketProcessHost> mTaskFactory;
+
+  enum class LaunchPhase { Unlaunched, Waiting, Complete };
+  LaunchPhase mLaunchPhase;
+
+  UniquePtr<SocketProcessParent> mSocketProcessParent;
+  // mShutdownRequested is set to true only when Shutdown() is called.
+  // If mShutdownRequested is false and the IPC channel is closed,
+  // OnProcessUnexpectedShutdown will be invoked.
+  bool mShutdownRequested;
+  bool mChannelClosed;
+};
+
+}  // namespace net
+}  // namespace mozilla
+
+#endif  // mozilla_net_SocketProcessHost_h
new file mode 100644
--- /dev/null
+++ b/netwerk/ipc/SocketProcessImpl.cpp
@@ -0,0 +1,155 @@
+/* -*- 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 "SocketProcessImpl.h"
+
+#include "base/command_line.h"
+#include "base/shared_memory.h"
+#include "base/string_util.h"
+#include "mozilla/ipc/IOThreadChild.h"
+#include "mozilla/BackgroundHangMonitor.h"
+#include "mozilla/Preferences.h"
+
+using mozilla::ipc::IOThreadChild;
+
+namespace mozilla {
+namespace net {
+
+LazyLogModule gSocketProcessLog("socketprocess");
+
+SocketProcessImpl::SocketProcessImpl(ProcessId aParentPid)
+    : ProcessChild(aParentPid) {}
+
+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
+
+  for (int i = 1; i < aArgc; i++) {
+    if (!aArgv[i]) {
+      continue;
+    }
+
+    if (strcmp(aArgv[i], "-parentBuildID") == 0) {
+      if (++i == aArgc) {
+        return false;
+      }
+
+      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;
+      }
+
+    } 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;
+      }
+#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;
+      }
+
+    } 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;
+      }
+    }
+  }
+
+#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");
+    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());
+}
+
+void SocketProcessImpl::CleanUp() { mSocketProcessChild.CleanUp(); }
+
+}  // namespace net
+}  // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/ipc/SocketProcessImpl.h
@@ -0,0 +1,36 @@
+/* -*- 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/. */
+
+#ifndef mozilla_net_SocketProcessImpl_h
+#define mozilla_net_SocketProcessImpl_h
+
+#include "mozilla/ipc/ProcessChild.h"
+#include "SocketProcessChild.h"
+
+namespace mozilla {
+namespace net {
+
+// This class owns the subprocess instance of socket child process.
+// It is instantiated as a singleton in XRE_InitChildProcess.
+class SocketProcessImpl final : public mozilla::ipc::ProcessChild {
+ protected:
+  typedef mozilla::ipc::ProcessChild ProcessChild;
+
+ public:
+  explicit SocketProcessImpl(ProcessId aParentPid);
+  ~SocketProcessImpl();
+
+  bool Init(int aArgc, char* aArgv[]) override;
+  void CleanUp() override;
+
+ private:
+  SocketProcessChild mSocketProcessChild;
+  DISALLOW_COPY_AND_ASSIGN(SocketProcessImpl);
+};
+
+}  // namespace net
+}  // namespace mozilla
+
+#endif  // mozilla_net_SocketProcessImpl_h
new file mode 100644
--- /dev/null
+++ b/netwerk/ipc/SocketProcessLogging.h
@@ -0,0 +1,20 @@
+/* -*- 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/. */
+
+#ifndef mozilla_SocketProcessLogging_h
+#define mozilla_SocketProcessLogging_h
+
+#include "mozilla/Logging.h"
+
+namespace mozilla {
+namespace net {
+extern LazyLogModule gSocketProcessLog;
+}
+}  // namespace mozilla
+
+#define LOG(msg) MOZ_LOG(gSocketProcessLog, mozilla::LogLevel::Debug, msg)
+#define LOG_ENABLED() MOZ_LOG_TEST(gSocketProcessLog, mozilla::LogLevel::Debug)
+
+#endif  // mozilla_SocketProcessLogging_h
new file mode 100644
--- /dev/null
+++ b/netwerk/ipc/SocketProcessParent.cpp
@@ -0,0 +1,51 @@
+/* -*- 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 "SocketProcessParent.h"
+#include "SocketProcessHost.h"
+#include "mozilla/Move.h"
+
+namespace mozilla {
+namespace net {
+
+SocketProcessParent::SocketProcessParent(SocketProcessHost* aHost)
+    : mHost(aHost) {
+  MOZ_ASSERT(mHost);
+
+  MOZ_COUNT_CTOR(SocketProcessParent);
+}
+
+SocketProcessParent::~SocketProcessParent() {
+  MOZ_COUNT_DTOR(SocketProcessParent);
+}
+
+void SocketProcessParent::ActorDestroy(ActorDestroyReason aWhy) {
+  if (mHost) {
+    mHost->OnChannelClosed();
+  }
+}
+
+// To ensure that IPDL is finished before SocketParent gets deleted.
+class DeferredDeleteSocketProcessParent : public Runnable {
+ public:
+  explicit DeferredDeleteSocketProcessParent(
+      UniquePtr<SocketProcessParent>&& aParent)
+      : Runnable("net::DeferredDeleteSocketProcessParent"),
+        mParent(std::move(aParent)) {}
+
+  NS_IMETHODIMP Run() override { return NS_OK; }
+
+ private:
+  UniquePtr<SocketProcessParent> mParent;
+};
+
+/* static */ void SocketProcessParent::Destroy(
+    UniquePtr<SocketProcessParent>&& aParent) {
+  NS_DispatchToMainThread(
+      new DeferredDeleteSocketProcessParent(std::move(aParent)));
+}
+
+}  // namespace net
+}  // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/ipc/SocketProcessParent.h
@@ -0,0 +1,37 @@
+/* -*- 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/. */
+
+#ifndef mozilla_net_SocketProcessParent_h
+#define mozilla_net_SocketProcessParent_h
+
+#include "mozilla/UniquePtr.h"
+#include "mozilla/net/PSocketProcessParent.h"
+
+namespace mozilla {
+namespace net {
+
+class SocketProcessHost;
+
+// IPC actor of socket process in parent process. This is allocated and managed
+// by SocketProcessHost.
+class SocketProcessParent final : public PSocketProcessParent {
+ public:
+  friend class SocketProcessHost;
+
+  explicit SocketProcessParent(SocketProcessHost* aHost);
+  ~SocketProcessParent();
+
+  void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ private:
+  SocketProcessHost* mHost;
+
+  static void Destroy(UniquePtr<SocketProcessParent>&& aParent);
+};
+
+}  // namespace net
+}  // namespace mozilla
+
+#endif  // mozilla_net_SocketProcessParent_h
--- a/netwerk/ipc/moz.build
+++ b/netwerk/ipc/moz.build
@@ -6,33 +6,42 @@
 
 EXPORTS.mozilla.net += [
     'ChannelEventQueue.h',
     'NeckoChild.h',
     'NeckoCommon.h',
     'NeckoMessageUtils.h',
     'NeckoParent.h',
     'NeckoTargetHolder.h',
+    'SocketProcessChild.h',
+    'SocketProcessHost.h',
+    'SocketProcessImpl.h',
+    'SocketProcessParent.h',
 ]
 
 UNIFIED_SOURCES += [
     'ChannelEventQueue.cpp',
     'NeckoChild.cpp',
     'NeckoCommon.cpp',
     'NeckoParent.cpp',
     'NeckoTargetHolder.cpp',
+    'SocketProcessChild.cpp',
+    'SocketProcessHost.cpp',
+    'SocketProcessImpl.cpp',
+    'SocketProcessParent.cpp',
 ]
 
 IPDL_SOURCES = [
     'NeckoChannelParams.ipdlh',
     'PChannelDiverter.ipdl',
     'PDataChannel.ipdl',
     'PFileChannel.ipdl',
     'PNecko.ipdl',
     'PSimpleChannel.ipdl',
+    'PSocketProcess.ipdl'
 ]
 
 # needed so --disable-webrtc builds work (yes, a bit messy)
 if not CONFIG['MOZ_WEBRTC']:
   IPDL_SOURCES += [
       '../../media/mtransport/ipc/PStunAddrsRequest.ipdl',
       '../../media/mtransport/ipc/PWebrtcProxyChannel.ipdl',
   ]
@@ -46,12 +55,13 @@ include('/ipc/chromium/chromium-config.m
 FINAL_LIBRARY = 'xul'
 
 LOCAL_INCLUDES += [
     '/caps',
     '/dom/base',
     '/modules/libjar',
     '/netwerk/base',
     '/netwerk/protocol/http',
+    '/xpcom/threads'
 ]
 
 # Add libFuzzer configuration directives
 include('/tools/fuzzing/libfuzzer-config.mozbuild')
--- a/netwerk/protocol/http/nsHttpHandler.cpp
+++ b/netwerk/protocol/http/nsHttpHandler.cpp
@@ -1112,16 +1112,20 @@ uint32_t nsHttpHandler::MaxSocketCount()
 }
 
 void nsHttpHandler::PrefsChanged(const char *pref) {
   nsresult rv = NS_OK;
   int32_t val;
 
   LOG(("nsHttpHandler::PrefsChanged [pref=%s]\n", pref));
 
+  if (pref) {
+    gIOService->NotifySocketProcessPrefsChanged(pref);
+  }
+
 #define PREF_CHANGED(p) ((pref == nullptr) || !PL_strcmp(pref, p))
 #define MULTI_PREF_CHANGED(p) \
   ((pref == nullptr) || !PL_strncmp(pref, p, sizeof(p) - 1))
 
   // If a security pref changed, lets clear our connection pool reuse
   if (MULTI_PREF_CHANGED(SECURITY_PREFIX)) {
     LOG(("nsHttpHandler::PrefsChanged Security Pref Changed %s\n", pref));
     if (mConnMgr) {
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -732,19 +732,20 @@ nsXULAppInfo::GetWidgetToolkit(nsACStrin
 SYNC_ENUMS(DEFAULT, Default)
 SYNC_ENUMS(PLUGIN, Plugin)
 SYNC_ENUMS(CONTENT, Content)
 SYNC_ENUMS(IPDLUNITTEST, IPDLUnitTest)
 SYNC_ENUMS(GMPLUGIN, GMPlugin)
 SYNC_ENUMS(GPU, GPU)
 SYNC_ENUMS(VR, VR)
 SYNC_ENUMS(RDD, RDD)
+SYNC_ENUMS(SOCKET, Socket)
 
 // .. and ensure that that is all of them:
-static_assert(GeckoProcessType_RDD + 1 == GeckoProcessType_End,
+static_assert(GeckoProcessType_Socket + 1 == GeckoProcessType_End,
               "Did not find the final GeckoProcessType");
 
 NS_IMETHODIMP
 nsXULAppInfo::GetProcessType(uint32_t* aResult) {
   NS_ENSURE_ARG_POINTER(aResult);
   *aResult = XRE_GetProcessType();
   return NS_OK;
 }
--- a/toolkit/xre/nsEmbedFunctions.cpp
+++ b/toolkit/xre/nsEmbedFunctions.cpp
@@ -76,16 +76,17 @@
 
 #include "mozilla/ipc/TestShellParent.h"
 #include "mozilla/ipc/XPCShellEnvironment.h"
 #include "mozilla/Scheduler.h"
 #include "mozilla/WindowsDllBlocklist.h"
 
 #include "GMPProcessChild.h"
 #include "mozilla/gfx/GPUProcessImpl.h"
+#include "mozilla/net/SocketProcessImpl.h"
 
 #include "GeckoProfiler.h"
 
 #if defined(MOZ_SANDBOX) && defined(XP_WIN)
 #include "mozilla/sandboxTarget.h"
 #include "mozilla/sandboxing/loggingCallbacks.h"
 #endif
 
@@ -629,16 +630,17 @@ nsresult XRE_InitChildProcess(int aArgc,
   }
 
   MessageLoop::Type uiLoopType;
   switch (XRE_GetProcessType()) {
     case GeckoProcessType_Content:
     case GeckoProcessType_GPU:
     case GeckoProcessType_VR:
     case GeckoProcessType_RDD:
+    case GeckoProcessType_Socket:
       // Content processes need the XPCOM/chromium frankenventloop
       uiLoopType = MessageLoop::TYPE_MOZILLA_CHILD;
       break;
     case GeckoProcessType_GMPlugin:
       uiLoopType = MessageLoop::TYPE_DEFAULT;
       break;
     default:
       uiLoopType = MessageLoop::TYPE_UI;
@@ -698,16 +700,20 @@ nsresult XRE_InitChildProcess(int aArgc,
         case GeckoProcessType_VR:
           process = new gfx::VRProcessChild(parentPID);
           break;
 
         case GeckoProcessType_RDD:
           process = new RDDProcessImpl(parentPID);
           break;
 
+        case GeckoProcessType_Socket:
+          process = new net::SocketProcessImpl(parentPID);
+          break;
+
         default:
           MOZ_CRASH("Unknown main thread class");
       }
 
       if (!process->Init(aArgc, aArgv)) {
         return NS_ERROR_FAILURE;
       }
 
--- a/xpcom/build/GeckoProcessTypes.h
+++ b/xpcom/build/GeckoProcessTypes.h
@@ -21,8 +21,10 @@ GECKO_PROCESS_TYPE(IPDLUnitTest, "ipdlun
 // Gecko Media Plugin process.
 GECKO_PROCESS_TYPE(GMPlugin, "gmplugin", GMPlugin)
 // GPU and compositor process.
 GECKO_PROCESS_TYPE(GPU, "gpu", GPU)
 // VR process.
 GECKO_PROCESS_TYPE(VR, "vr", VR)
 // Remote Data Decoder process.
 GECKO_PROCESS_TYPE(RDD, "rdd", RDD)
+// Socket process
+GECKO_PROCESS_TYPE(Socket, "socket", Socket)
--- a/xpcom/build/nsXULAppAPI.h
+++ b/xpcom/build/nsXULAppAPI.h
@@ -439,16 +439,18 @@ XRE_API(bool, XRE_IsE10sParentProcess, (
  * the e10s parent process or called in the main process when e10s is
  * disabled.
  */
 #define GECKO_PROCESS_TYPE(enum_name, string_name, xre_name) \
   XRE_API(bool, XRE_Is##xre_name##Process, ())
 #include "mozilla/GeckoProcessTypes.h"
 #undef GECKO_PROCESS_TYPE
 
+XRE_API(bool, XRE_IsSocketProcess, ())
+
 /**
  * Returns true if the appshell should run its own native event loop. Returns
  * false if we should rely solely on the Gecko event loop.
  */
 XRE_API(bool, XRE_UseNativeEventProcessing, ())
 
 typedef void (*MainFunction)(void* aData);
 
--- a/xpcom/system/nsIXULRuntime.idl
+++ b/xpcom/system/nsIXULRuntime.idl
@@ -73,16 +73,17 @@ interface nsIXULRuntime : nsISupports
   const unsigned long PROCESS_TYPE_DEFAULT = 0;
   const unsigned long PROCESS_TYPE_PLUGIN = 1;
   const unsigned long PROCESS_TYPE_CONTENT = 2;
   const unsigned long PROCESS_TYPE_IPDLUNITTEST = 3;
   const unsigned long PROCESS_TYPE_GMPLUGIN = 4;
   const unsigned long PROCESS_TYPE_GPU = 5;
   const unsigned long PROCESS_TYPE_VR = 6;
   const unsigned long PROCESS_TYPE_RDD = 7;
+  const unsigned long PROCESS_TYPE_SOCKET = 8;
 
   /**
    * The type of the caller's process.  Returns one of the values above.
    */
   readonly attribute unsigned long processType;
 
   /**
    * The system process ID of the caller's process.