Bug 1416623 - Start the new socket process basics (prefs, full xpcom init, logging, no sandboxing), r=mayhemer
authorKershaw Chang <kershaw@mozilla.com>
Thu, 13 Sep 2018 13:05:42 +0200
changeset 436120 55a5826ef371574be5dbc56530107e4c82a31742
parent 436119 efccb758c78cc4c42170c886294bf335d90e4383
child 436121 da0f434ad0e94f0dc2c6d75a58cb52c460715612
push id3
push userhonzab.moz@firemni.cz
push dateThu, 13 Sep 2018 11:23:43 +0000
reviewersmayhemer
bugs1416623
milestone64.0a1
Bug 1416623 - Start the new socket process basics (prefs, full xpcom init, logging, no sandboxing), r=mayhemer
docshell/base/timeline/TimelineConsumers.cpp
dom/ipc/PContent.ipdl
dom/ipc/PrefsTypes.ipdlh
dom/ipc/moz.build
ipc/glue/GeckoChildProcessHost.cpp
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/nsXULAppAPI.h
xpcom/components/nsComponentManager.cpp
xpcom/system/nsIXULRuntime.idl
--- a/docshell/base/timeline/TimelineConsumers.cpp
+++ b/docshell/base/timeline/TimelineConsumers.cpp
@@ -31,17 +31,19 @@ bool TimelineConsumers::sInShutdown = fa
 
 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,16 +55,17 @@ 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";
@@ -141,40 +142,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,38 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
+
+/* 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
\ No newline at end of file
--- a/dom/ipc/moz.build
+++ b/dom/ipc/moz.build
@@ -103,16 +103,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',
     'ServiceWorkerConfiguration.ipdlh',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
--- a/ipc/glue/GeckoChildProcessHost.cpp
+++ b/ipc/glue/GeckoChildProcessHost.cpp
@@ -139,17 +139,17 @@ GeckoChildProcessHost::~GeckoChildProces
 }
 
 //static
 auto
 GeckoChildProcessHost::GetPathToBinary(FilePath& exePath, GeckoProcessType processType) -> BinaryPathType
 {
   if (sRunSelfAsContentProc &&
       (processType == GeckoProcessType_Content || processType == GeckoProcessType_GPU ||
-       processType == GeckoProcessType_VR)) {
+       processType == GeckoProcessType_VR || 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
@@ -1001,16 +1001,19 @@ GeckoChildProcessHost::PerformAsyncLaunc
         shouldSandboxCurrentProcess = true;
       }
       break;
     case GeckoProcessType_VR:
       if (mSandboxLevel > 0 && !PR_GetEnv("MOZ_DISABLE_VR_SANDBOX")) {
         // TODO: Implement sandbox for VR process, Bug 1430043.
       }
       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();
--- a/netwerk/base/nsIOService.cpp
+++ b/netwerk/base/nsIOService.cpp
@@ -48,16 +48,18 @@
 #include "mozilla/net/DNS.h"
 #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/SocketProcessHost.h"
+#include "mozilla/net/SocketProcessParent.h"
 #include "mozilla/Unused.h"
 #include "ReferrerPolicy.h"
 #include "nsContentSecurityManager.h"
 #include "nsContentUtils.h"
 
 namespace mozilla {
 namespace net {
 
@@ -193,16 +195,17 @@ nsIOService::nsIOService()
     , mNetworkNotifyChanged(true)
     , mTotalRequests(0)
     , mCacheWon(0)
     , mNetWon(0)
     , mLastOfflineStateChange(PR_IntervalNow())
     , mLastConnectivityChange(PR_IntervalNow())
     , mLastNetworkLinkChange(PR_IntervalNow())
     , 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,
@@ -238,16 +241,17 @@ 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);
@@ -348,16 +352,117 @@ nsIOService::InitializeProtocolProxyServ
     if (XRE_IsParentProcess()) {
         // for early-initialization
         Unused << do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID, &rv);
     }
 
     return rv;
 }
 
+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;
+    }
+
+    char *val = PR_GetEnv("NECKO_DEDICATED_PROCESS");
+    if (!val || *val != '1') {
+        LOG(("nsIOService skipping LaunchSocketProcess env-var is missing"));
+        return NS_OK;
+    }
+    // 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!!");
+        return NS_ERROR_FAILURE;
+    }
+
+    return NS_OK;
+}
+
+void
+nsIOService::DestroySocketProcess()
+{
+    MOZ_ASSERT(NS_IsMainThread());
+
+    if (XRE_GetProcessType() != GeckoProcessType_Default || !mSocketProcess) {
+        return;
+    }
+
+    mSocketProcess->Shutdown();
+    mSocketProcess = nullptr;
+}
+
+void
+nsIOService::NotifySocketProcessPrefsChanged(const char* aName)
+{
+    MOZ_ASSERT(NS_IsMainThread());
+
+    if (!mSocketProcess || !mSocketProcess->IsConnected()) {
+        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));
+
+    if (aSucceeded) {
+        Unused << mSocketProcess->GetActor()->SendTest();
+    }
+}
+
+void
+nsIOService::OnProcessUnexpectedShutdown(SocketProcessHost* aHost)
+{
+    MOZ_ASSERT(NS_IsMainThread());
+
+    LOG(("nsIOService::OnProcessUnexpectedShutdown\n"));
+    DestroySocketProcess();
+}
+
 already_AddRefed<nsIOService>
 nsIOService::GetInstance() {
     if (!gIOService) {
         RefPtr<nsIOService> ios = new nsIOService();
         if (NS_SUCCEEDED(ios->Init())) {
             MOZ_ASSERT(gIOService == ios.get());
             return ios.forget();
         }
@@ -1418,24 +1523,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
 {
@@ -107,16 +108,22 @@ public:
     // Used to keep "race cache with network" stats
     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);
+    void NotifySocketProcessPrefsChanged(const char* aName);
+
 private:
     // These shouldn't be called directly:
     // - construct using GetInstance
     // - destroy using Release
     nsIOService();
     ~nsIOService();
     nsresult SetConnectivityInternal(bool aConnectivity);
 
@@ -162,16 +169,19 @@ private:
                                                      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;
@@ -213,16 +223,18 @@ private:
     // time in any of these functions we want to know if and what network
     // 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;
 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,21 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
+
+/* 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 Test();
+  async PreferenceUpdate(Pref pref);
+};
+
+} // namespace net
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/ipc/SocketProcessChild.cpp
@@ -0,0 +1,117 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=99: */
+
+/* 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);
+}
+
+static const char* gCallbackPrefs[] = {
+  "network.http.",
+  nullptr,
+};
+
+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");
+
+  Preferences::RegisterPrefixCallbacks(
+    PREF_CHANGE_METHOD(SocketProcessChild::PrefsChanged),
+    gCallbackPrefs, this);
+
+  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::RecvTest()
+{
+  LOG(("SocketProcessChild::RecvTest\n"));
+
+  return IPC_OK();
+}
+
+IPCResult
+SocketProcessChild::RecvPreferenceUpdate(const Pref& aPref)
+{
+  Preferences::SetPreference(aPref);
+  return IPC_OK();
+}
+
+void
+SocketProcessChild::PrefsChanged(const char *pref)
+{
+  LOG(("SocketProcessChild::PrefsChanged pref=%s\n", pref));
+}
+
+} // namespace net
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/ipc/SocketProcessChild.h
@@ -0,0 +1,43 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et 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_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 RecvTest() override;
+  mozilla::ipc::IPCResult RecvPreferenceUpdate(const Pref& aPref) override;
+  void CleanUp();
+
+private:
+  void PrefsChanged(const char *pref);
+
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozilla_net_SocketProcessChild_h
new file mode 100644
--- /dev/null
+++ b/netwerk/ipc/SocketProcessHost.cpp
@@ -0,0 +1,248 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=99: */
+
+/* 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_COUNT_CTOR(SocketProcessHost);
+}
+
+SocketProcessHost::~SocketProcessHost()
+{
+  MOZ_COUNT_DTOR(SocketProcessHost);
+}
+
+bool
+SocketProcessHost::Launch()
+{
+  MOZ_ASSERT(mLaunchPhase == LaunchPhase::Unlaunched);
+  MOZ_ASSERT(!mSocketProcessParent);
+
+  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()
+{
+  if (mLaunchPhase == LaunchPhase::Waiting) {
+    InitAfterConnect(true);
+  }
+}
+
+void
+SocketProcessHost::OnChannelErrorTask()
+{
+  if (mLaunchPhase == LaunchPhase::Waiting) {
+    InitAfterConnect(false);
+  }
+}
+
+void
+SocketProcessHost::InitAfterConnect(bool aSucceeded)
+{
+  MOZ_ASSERT(mLaunchPhase == LaunchPhase::Waiting);
+  MOZ_ASSERT(!mSocketProcessParent);
+
+  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);
+
+  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,115 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=99: */
+
+/* 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,162 @@
+/* -*- 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 (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,21 @@
+/* -*- 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;
+}
+}
+
+#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,63 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et 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 "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,40 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et 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_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;
+
+  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',
   ]
   EXPORTS.mozilla.net += [
@@ -45,12 +54,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
@@ -1168,16 +1168,20 @@ 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
@@ -780,19 +780,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(PDFIUM, PDFium)
 SYNC_ENUMS(VR, VR)
+SYNC_ENUMS(SOCKET, Socket)
 
 // .. and ensure that that is all of them:
-static_assert(GeckoProcessType_VR + 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;
@@ -5194,16 +5195,22 @@ XRE_IsContentProcess()
 
 bool
 XRE_IsPluginProcess()
 {
   return XRE_GetProcessType() == GeckoProcessType_Plugin;
 }
 
 bool
+XRE_IsSocketProcess()
+{
+  return XRE_GetProcessType() == GeckoProcessType_Socket;
+}
+
+bool
 XRE_UseNativeEventProcessing()
 {
   if (XRE_IsContentProcess()) {
     static bool sInited = false;
     static bool sUseNativeEventProcessing = false;
     if (!sInited) {
       Preferences::AddBoolVarCache(&sUseNativeEventProcessing,
                                    "dom.ipc.useNativeEventProcessing.content");
--- a/toolkit/xre/nsEmbedFunctions.cpp
+++ b/toolkit/xre/nsEmbedFunctions.cpp
@@ -75,16 +75,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
 
@@ -660,16 +661,17 @@ XRE_InitChildProcess(int aArgc,
   if (NS_FAILED(rv)) {
     return NS_ERROR_FAILURE;
   }
 
   MessageLoop::Type uiLoopType;
   switch (XRE_GetProcessType()) {
   case GeckoProcessType_Content:
   case GeckoProcessType_GPU:
+  case GeckoProcessType_Socket:
       // Content processes need the XPCOM/chromium frankenventloop
       uiLoopType = MessageLoop::TYPE_MOZILLA_CHILD;
       break;
   case GeckoProcessType_GMPlugin:
   case GeckoProcessType_PDFium:
   case GeckoProcessType_VR:
       uiLoopType = MessageLoop::TYPE_DEFAULT;
       break;
@@ -732,16 +734,20 @@ XRE_InitChildProcess(int aArgc,
       case GeckoProcessType_GPU:
         process = new gfx::GPUProcessImpl(parentPID);
         break;
 
       case GeckoProcessType_VR:
         process = new gfx::VRProcessChild(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/nsXULAppAPI.h
+++ b/xpcom/build/nsXULAppAPI.h
@@ -371,29 +371,31 @@ enum GeckoProcessType
 
   GeckoProcessType_IPDLUnitTest,
 
   GeckoProcessType_GMPlugin, // Gecko Media Plugin
 
   GeckoProcessType_GPU,      // GPU and compositor process
   GeckoProcessType_PDFium,   // Gecko PDFium process
   GeckoProcessType_VR,       // VR process
+  GeckoProcessType_Socket,   // Socket process
   GeckoProcessType_End,
   GeckoProcessType_Invalid = GeckoProcessType_End
 };
 
 static const char* const kGeckoProcessTypeString[] = {
   "default",
   "plugin",
   "tab",
   "ipdlunittest",
   "geckomediaplugin",
   "gpu",
   "pdfium",
-  "vr"
+  "vr",
+  "socket"
 };
 
 static_assert(MOZ_ARRAY_LENGTH(kGeckoProcessTypeString) ==
               GeckoProcessType_End,
               "Array length mismatch");
 
 XRE_API(const char*,
         XRE_ChildProcessTypeToString, (GeckoProcessType aProcessType))
@@ -467,16 +469,19 @@ XRE_API(bool,
         XRE_IsGPUProcess, ())
 
 XRE_API(bool,
         XRE_IsVRProcess, ())
 
 XRE_API(bool,
         XRE_IsPluginProcess, ())
 
+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/components/nsComponentManager.cpp
+++ b/xpcom/components/nsComponentManager.cpp
@@ -365,17 +365,18 @@ nsComponentManagerImpl::Init()
       RegisterModule(module, nullptr);
     }
   }
 
   for (uint32_t i = 0; i < sExtraStaticModules->Length(); ++i) {
     RegisterModule((*sExtraStaticModules)[i], nullptr);
   }
 
-  bool loadChromeManifests = (XRE_GetProcessType() != GeckoProcessType_GPU);
+  bool loadChromeManifests = (XRE_GetProcessType() != GeckoProcessType_GPU &&
+                              XRE_GetProcessType() != GeckoProcessType_Socket);
   if (loadChromeManifests) {
     // The overall order in which chrome.manifests are expected to be treated
     // is the following:
     // - greDir
     // - greDir's omni.ja
     // - appDir
     // - appDir's omni.ja
 
--- 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_PDFIUM = 6;
   const unsigned long PROCESS_TYPE_VR = 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.