Bug 1475641 - Move PAProxyAutoConfig to socket process, r=necko-reviewers,dragana
authorKershaw Chang <kershaw@mozilla.com>
Mon, 11 Oct 2021 19:17:58 +0000
changeset 595410 21bb8cd199ba6ada31ec7b17de0cd7ae7cb46440
parent 595409 76dadf1dab5f0ccc294e901de725c1476f491439
child 595411 4a1898ec99d0f6920e6981d68655269c120418e9
push id151253
push userkjang@mozilla.com
push dateMon, 11 Oct 2021 19:20:29 +0000
treeherderautoland@21bb8cd199ba [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnecko-reviewers, dragana
bugs1475641
milestone95.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 1475641 - Move PAProxyAutoConfig to socket process, r=necko-reviewers,dragana Differential Revision: https://phabricator.services.mozilla.com/D125855
netwerk/base/ProxyAutoConfig.cpp
netwerk/base/ProxyAutoConfig.h
netwerk/base/nsIOService.cpp
netwerk/base/nsPACMan.cpp
netwerk/base/nsPACMan.h
netwerk/ipc/PProxyAutoConfig.ipdl
netwerk/ipc/PSocketProcess.ipdl
netwerk/ipc/ProxyAutoConfigChild.cpp
netwerk/ipc/ProxyAutoConfigChild.h
netwerk/ipc/ProxyAutoConfigParent.cpp
netwerk/ipc/ProxyAutoConfigParent.h
netwerk/ipc/SocketProcessChild.cpp
netwerk/ipc/SocketProcessChild.h
netwerk/ipc/SocketProcessParent.cpp
netwerk/ipc/SocketProcessParent.h
netwerk/ipc/moz.build
--- a/netwerk/base/ProxyAutoConfig.cpp
+++ b/netwerk/base/ProxyAutoConfig.cpp
@@ -23,17 +23,22 @@
 #include "js/PropertySpec.h"
 #include "js/SourceText.h"  // JS::Source{Ownership,Text}
 #include "js/Utility.h"
 #include "js/Warnings.h"  // JS::SetWarningReporter
 #include "prnetdb.h"
 #include "nsITimer.h"
 #include "mozilla/Atomics.h"
 #include "mozilla/SpinEventLoopUntil.h"
+#include "mozilla/ipc/Endpoint.h"
 #include "mozilla/net/DNS.h"
+#include "mozilla/net/SocketProcessChild.h"
+#include "mozilla/net/SocketProcessParent.h"
+#include "mozilla/net/ProxyAutoConfigChild.h"
+#include "mozilla/net/ProxyAutoConfigParent.h"
 #include "mozilla/Utf8.h"  // mozilla::Utf8Unit
 #include "nsServiceManagerUtils.h"
 #include "nsNetCID.h"
 
 #if defined(XP_MACOSX)
 #  include "nsMacUtilsImpl.h"
 #endif
 
@@ -361,16 +366,31 @@ class PACResolver final : public nsIDNSL
   Mutex mMutex;
 
  private:
   ~PACResolver() = default;
 };
 NS_IMPL_ISUPPORTS(PACResolver, nsIDNSListener, nsITimerCallback, nsINamed)
 
 static void PACLogToConsole(nsString& aMessage) {
+  if (XRE_IsSocketProcess()) {
+    auto task = [message(aMessage)]() {
+      SocketProcessChild* child = SocketProcessChild::GetSingleton();
+      if (child) {
+        Unused << child->SendOnConsoleMessage(message);
+      }
+    };
+    if (NS_IsMainThread()) {
+      task();
+    } else {
+      NS_DispatchToMainThread(NS_NewRunnableFunction("PACLogToConsole", task));
+    }
+    return;
+  }
+
   nsCOMPtr<nsIConsoleService> consoleService =
       do_GetService(NS_CONSOLESERVICE_CONTRACTID);
   if (!consoleService) return;
 
   consoleService->LogStringMessage(aMessage.get());
 }
 
 // Javascript errors and warnings are logged to the main error console
@@ -463,26 +483,29 @@ bool ProxyAutoConfig::ResolveAddress(con
     if (!mTimer) mTimer = NS_NewTimer();
     if (mTimer) {
       mTimer->SetTarget(mMainThreadEventTarget);
       mTimer->InitWithCallback(helper, aTimeout, nsITimer::TYPE_ONE_SHOT);
       helper->mTimer = mTimer;
     }
   }
 
+  mWaitingForDNSResolve = true;
   // Spin the event loop of the pac thread until lookup is complete.
   // nsPACman is responsible for keeping a queue and only allowing
   // one PAC execution at a time even when it is called re-entrantly.
   SpinEventLoopUntil("ProxyAutoConfig::ResolveAddress"_ns, [&, helper, this]() {
     if (!helper->mRequest) {
+      mWaitingForDNSResolve = false;
       return true;
     }
     if (this->mShutdown) {
       NS_WARNING("mShutdown set with PAC request not cancelled");
       MOZ_ASSERT(NS_FAILED(helper->mStatus));
+      mWaitingForDNSResolve = false;
       return true;
     }
     return false;
   });
 
   if (NS_FAILED(helper->mStatus)) {
     return false;
   }
@@ -688,20 +711,21 @@ class JSContextWrapper {
 const JSClass JSContextWrapper::sGlobalClass = {"PACResolutionThreadGlobal",
                                                 JSCLASS_GLOBAL_FLAGS,
                                                 &JS::DefaultGlobalClassOps};
 
 void ProxyAutoConfig::SetThreadLocalIndex(uint32_t index) {
   RunningIndex() = index;
 }
 
-nsresult ProxyAutoConfig::Init(const nsCString& aPACURI,
-                               const nsCString& aPACScriptData,
-                               bool aIncludePath, uint32_t aExtraHeapSize,
-                               nsIEventTarget* aEventTarget) {
+nsresult ProxyAutoConfig::ConfigurePAC(const nsCString& aPACURI,
+                                       const nsCString& aPACScriptData,
+                                       bool aIncludePath,
+                                       uint32_t aExtraHeapSize,
+                                       nsIEventTarget* aEventTarget) {
   mShutdown = false;  // Shutdown needs to be called prior to destruction
 
   mPACURI = aPACURI;
 
   // The full PAC script data is the concatenation of 1) the various functions
   // exposed to PAC scripts in |sAsciiPacUtils| and 2) the user-provided PAC
   // script data.  Historically this was single-byte Latin-1 text (usually just
   // ASCII, but bug 296163 has a real-world Latin-1 example).  We now support
@@ -815,16 +839,25 @@ nsresult ProxyAutoConfig::SetupJS() {
 
   // we don't need these now
   mConcatenatedPACData.Truncate();
   mPACURI.Truncate();
 
   return NS_OK;
 }
 
+void ProxyAutoConfig::GetProxyForURIWithCallback(
+    const nsCString& aTestURI, const nsCString& aTestHost,
+    std::function<void(nsresult aStatus, const nsACString& aResult)>&&
+        aCallback) {
+  nsAutoCString result;
+  nsresult status = GetProxyForURI(aTestURI, aTestHost, result);
+  aCallback(status, result);
+}
+
 nsresult ProxyAutoConfig::GetProxyForURI(const nsCString& aTestURI,
                                          const nsCString& aTestHost,
                                          nsACString& result) {
   if (mJSNeedsSetup) SetupJS();
 
   if (!mJSContext || !mJSContext->IsOK()) return NS_ERROR_NOT_AVAILABLE;
 
   JSContext* cx = mJSContext->Context();
@@ -1054,10 +1087,77 @@ bool ProxyAutoConfig::MyIPAddress(const 
   if (!dottedDecimalString) {
     return false;
   }
 
   aArgs.rval().setString(dottedDecimalString);
   return true;
 }
 
+RemoteProxyAutoConfig::RemoteProxyAutoConfig() = default;
+
+RemoteProxyAutoConfig::~RemoteProxyAutoConfig() = default;
+
+nsresult RemoteProxyAutoConfig::Init(nsIThread* aPACThread) {
+  MOZ_ASSERT(NS_IsMainThread());
+
+  SocketProcessParent* socketProcessParent =
+      SocketProcessParent::GetSingleton();
+  if (!socketProcessParent) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  ipc::Endpoint<PProxyAutoConfigParent> parent;
+  ipc::Endpoint<PProxyAutoConfigChild> child;
+  nsresult rv = PProxyAutoConfig::CreateEndpoints(
+      base::GetCurrentProcId(), socketProcessParent->OtherPid(), &parent,
+      &child);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  Unused << socketProcessParent->SendInitProxyAutoConfigChild(std::move(child));
+  mProxyAutoConfigParent = new ProxyAutoConfigParent();
+  return aPACThread->Dispatch(
+      NS_NewRunnableFunction("ProxyAutoConfigParent::ProxyAutoConfigParent",
+                             [proxyAutoConfigParent(mProxyAutoConfigParent),
+                              endpoint{std::move(parent)}]() mutable {
+                               proxyAutoConfigParent->Init(std::move(endpoint));
+                             }));
+}
+
+nsresult RemoteProxyAutoConfig::ConfigurePAC(const nsCString& aPACURI,
+                                             const nsCString& aPACScriptData,
+                                             bool aIncludePath,
+                                             uint32_t aExtraHeapSize,
+                                             nsIEventTarget*) {
+  Unused << mProxyAutoConfigParent->SendConfigurePAC(
+      aPACURI, aPACScriptData, aIncludePath, aExtraHeapSize);
+  return NS_OK;
+}
+
+void RemoteProxyAutoConfig::Shutdown() { mProxyAutoConfigParent->Close(); }
+
+void RemoteProxyAutoConfig::GC() { Unused << mProxyAutoConfigParent->SendGC(); }
+
+void RemoteProxyAutoConfig::GetProxyForURIWithCallback(
+    const nsCString& aTestURI, const nsCString& aTestHost,
+    std::function<void(nsresult aStatus, const nsACString& aResult)>&&
+        aCallback) {
+  if (!mProxyAutoConfigParent->CanSend()) {
+    return;
+  }
+
+  mProxyAutoConfigParent->SendGetProxyForURI(
+      aTestURI, aTestHost,
+      [aCallback](Tuple<nsresult, nsCString>&& aResult) {
+        nsresult status;
+        nsCString result;
+        Tie(status, result) = aResult;
+        aCallback(status, result);
+      },
+      [aCallback](mozilla::ipc::ResponseRejectReason&& aReason) {
+        aCallback(NS_ERROR_FAILURE, ""_ns);
+      });
+}
+
 }  // namespace net
 }  // namespace mozilla
--- a/netwerk/base/ProxyAutoConfig.h
+++ b/netwerk/base/ProxyAutoConfig.h
@@ -2,46 +2,66 @@
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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 ProxyAutoConfig_h__
 #define ProxyAutoConfig_h__
 
+#include <functional>
 #include "nsString.h"
 #include "nsCOMPtr.h"
 
 class nsIEventTarget;
 class nsITimer;
 namespace JS {
 class CallArgs;
 }  // namespace JS
 
 namespace mozilla {
 namespace net {
 
 class JSContextWrapper;
+class ProxyAutoConfigParent;
 union NetAddr;
 
+class ProxyAutoConfigBase {
+ public:
+  virtual ~ProxyAutoConfigBase() = default;
+  virtual nsresult Init(nsIThread* aPACThread) { return NS_OK; }
+  virtual nsresult ConfigurePAC(const nsCString& aPACURI,
+                                const nsCString& aPACScriptData,
+                                bool aIncludePath, uint32_t aExtraHeapSize,
+                                nsIEventTarget* aEventTarget) = 0;
+  virtual void SetThreadLocalIndex(uint32_t index) {}
+  virtual void Shutdown() = 0;
+  virtual void GC() = 0;
+  virtual void GetProxyForURIWithCallback(
+      const nsCString& aTestURI, const nsCString& aTestHost,
+      std::function<void(nsresult aStatus, const nsACString& aResult)>&&
+          aCallback) = 0;
+};
+
 // The ProxyAutoConfig class is meant to be created and run on a
 // non main thread. It synchronously resolves PAC files by blocking that
 // thread and running nested event loops. GetProxyForURI is not re-entrant.
 
-class ProxyAutoConfig {
+class ProxyAutoConfig : public ProxyAutoConfigBase {
  public:
   ProxyAutoConfig();
-  ~ProxyAutoConfig();
+  virtual ~ProxyAutoConfig();
 
-  nsresult Init(const nsCString& aPACURI, const nsCString& aPACScriptData,
-                bool aIncludePath, uint32_t aExtraHeapSize,
-                nsIEventTarget* aEventTarget);
-  void SetThreadLocalIndex(uint32_t index);
-  void Shutdown();
-  void GC();
+  nsresult ConfigurePAC(const nsCString& aPACURI,
+                        const nsCString& aPACScriptData, bool aIncludePath,
+                        uint32_t aExtraHeapSize,
+                        nsIEventTarget* aEventTarget) override;
+  void SetThreadLocalIndex(uint32_t index) override;
+  void Shutdown() override;
+  void GC() override;
   bool MyIPAddress(const JS::CallArgs& aArgs);
   bool ResolveAddress(const nsCString& aHostName, NetAddr* aNetAddr,
                       unsigned int aTimeout);
 
   /**
    * Get the proxy string for the specified URI.  The proxy string is
    * given by the following:
    *
@@ -73,16 +93,23 @@ class ProxyAutoConfig {
    *        The ASCII hostname to test.
    *
    * @param result
    *        result string as defined above.
    */
   nsresult GetProxyForURI(const nsCString& aTestURI, const nsCString& aTestHost,
                           nsACString& result);
 
+  void GetProxyForURIWithCallback(
+      const nsCString& aTestURI, const nsCString& aTestHost,
+      std::function<void(nsresult aStatus, const nsACString& aResult)>&&
+          aCallback) override;
+
+  bool WaitingForDNSResolve() const { return mWaitingForDNSResolve; }
+
  private:
   // allow 665ms for myipaddress dns queries. That's 95th percentile.
   const static unsigned int kTimeout = 665;
 
   // used to compile the PAC file and setup the execution context
   nsresult SetupJS();
 
   bool SrcAddress(const NetAddr* remoteAddress, nsCString& localAddress);
@@ -90,18 +117,40 @@ class ProxyAutoConfig {
                           const JS::CallArgs& aArgs, bool* aResult);
 
   JSContextWrapper* mJSContext{nullptr};
   bool mJSNeedsSetup{false};
   bool mShutdown{true};
   nsCString mConcatenatedPACData;
   nsCString mPACURI;
   bool mIncludePath{false};
+  bool mWaitingForDNSResolve{false};
   uint32_t mExtraHeapSize{0};
   nsCString mRunningHost;
   nsCOMPtr<nsITimer> mTimer;
   nsCOMPtr<nsIEventTarget> mMainThreadEventTarget;
 };
 
+class RemoteProxyAutoConfig : public ProxyAutoConfigBase {
+ public:
+  RemoteProxyAutoConfig();
+  virtual ~RemoteProxyAutoConfig();
+
+  nsresult Init(nsIThread* aPACThread) override;
+  nsresult ConfigurePAC(const nsCString& aPACURI,
+                        const nsCString& aPACScriptData, bool aIncludePath,
+                        uint32_t aExtraHeapSize,
+                        nsIEventTarget* aEventTarget) override;
+  void Shutdown() override;
+  void GC() override;
+  void GetProxyForURIWithCallback(
+      const nsCString& aTestURI, const nsCString& aTestHost,
+      std::function<void(nsresult aStatus, const nsACString& aResult)>&&
+          aCallback) override;
+
+ private:
+  RefPtr<ProxyAutoConfigParent> mProxyAutoConfigParent;
+};
+
 }  // namespace net
 }  // namespace mozilla
 
 #endif  // ProxyAutoConfig_h__
--- a/netwerk/base/nsIOService.cpp
+++ b/netwerk/base/nsIOService.cpp
@@ -216,16 +216,17 @@ static const char* gCallbackPrefsForSock
     "network.ssl_tokens_cache_enabled",
     "network.send_ODA_to_content_directly",
     "network.trr.",
     "doh-rollout.",
     "network.dns.disableIPv6",
     "network.dns.skipTRR-when-parental-control-enabled",
     "network.offline-mirrors-connectivity",
     "network.disable-localhost-when-offline",
+    "network.proxy.parse_pac_on_socket_process",
     nullptr,
 };
 
 static const char* gCallbackSecurityPrefs[] = {
     // Note the prefs listed below should be in sync with the code in
     // HandleTLSPrefChange().
     "security.tls.version.min",
     "security.tls.version.max",
--- a/netwerk/base/nsPACMan.cpp
+++ b/netwerk/base/nsPACMan.cpp
@@ -12,20 +12,22 @@
 #include "nsIAsyncVerifyRedirectCallback.h"
 #include "nsIAuthPrompt.h"
 #include "nsIDHCPClient.h"
 #include "nsIHttpChannel.h"
 #include "nsIPrefBranch.h"
 #include "nsIPromptFactory.h"
 #include "nsIProtocolProxyService.h"
 #include "nsISystemProxySettings.h"
+#include "nsIOService.h"
 #include "nsNetUtil.h"
 #include "nsThreadUtils.h"
 #include "mozilla/Result.h"
 #include "mozilla/ResultExtensions.h"
+#include "mozilla/StaticPrefs_network.h"
 #include "mozilla/Telemetry.h"
 
 //-----------------------------------------------------------------------------
 
 namespace mozilla {
 namespace net {
 
 LazyLogModule gProxyLog("proxy");
@@ -265,18 +267,19 @@ class ExecutePACThreadAction final : pub
       mCancel = false;
       return NS_OK;
     }
 
     if (mSetupPAC) {
       mSetupPAC = false;
 
       nsCOMPtr<nsIEventTarget> target = mPACMan->GetNeckoTarget();
-      mPACMan->mPAC.Init(mSetupPACURI, mSetupPACData, mPACMan->mIncludePath,
-                         mExtraHeapSize, target);
+      mPACMan->mPAC->ConfigurePAC(mSetupPACURI, mSetupPACData,
+                                  mPACMan->mIncludePath, mExtraHeapSize,
+                                  target);
 
       RefPtr<PACLoadComplete> runnable = new PACLoadComplete(mPACMan);
       mPACMan->Dispatch(runnable.forget());
       return NS_OK;
     }
 
     if (mConfigureWPAD) {
       nsAutoCString spec;
@@ -367,22 +370,28 @@ nsPACMan::nsPACMan(nsISerialEventTarget*
       mLoadPending(false),
       mShutdown(false),
       mLoadFailureCount(0),
       mInProgress(false),
       mAutoDetect(false),
       mWPADOverDHCPEnabled(false),
       mProxyConfigType(0) {
   MOZ_ASSERT(NS_IsMainThread(), "pacman must be created on main thread");
-  if (!sThreadLocalSetup) {
-    sThreadLocalSetup = true;
-    PR_NewThreadPrivateIndex(&sThreadLocalIndex, nullptr);
+  mIncludePath = Preferences::GetBool(kPACIncludePath, false);
+  if (StaticPrefs::network_proxy_parse_pac_on_socket_process() &&
+      gIOService->SocketProcessReady()) {
+    mPAC = MakeUnique<RemoteProxyAutoConfig>();
+  } else {
+    mPAC = MakeUnique<ProxyAutoConfig>();
+    if (!sThreadLocalSetup) {
+      sThreadLocalSetup = true;
+      PR_NewThreadPrivateIndex(&sThreadLocalIndex, nullptr);
+    }
+    mPAC->SetThreadLocalIndex(sThreadLocalIndex);
   }
-  mPAC.SetThreadLocalIndex(sThreadLocalIndex);
-  mIncludePath = Preferences::GetBool(kPACIncludePath, false);
 }
 
 nsPACMan::~nsPACMan() {
   MOZ_ASSERT(mShutdown, "Shutdown must be called before dtor.");
 
   if (mPACThread) {
     if (NS_IsMainThread()) {
       mPACThread->Shutdown();
@@ -433,16 +442,20 @@ nsresult nsPACMan::DispatchToPAC(already
   if (mShutdown) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   // Lazily create the PAC thread. This method is main-thread only so we don't
   // have to worry about threading issues here.
   if (!mPACThread) {
     MOZ_TRY(NS_NewNamedThread("ProxyResolution", getter_AddRefs(mPACThread)));
+    nsresult rv = mPAC->Init(mPACThread);
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
   }
 
   return mPACThread->Dispatch(
       e.forget(),
       aSync ? nsIEventTarget::DISPATCH_SYNC : nsIEventTarget::DISPATCH_NORMAL);
 }
 
 nsresult nsPACMan::AsyncGetProxyForURI(nsIURI* uri, nsPACManCallback* callback,
@@ -755,30 +768,32 @@ void nsPACMan::CancelPendingQ(nsresult s
   MOZ_ASSERT(!NS_IsMainThread(), "wrong thread");
   RefPtr<PendingPACQuery> query;
 
   while (!mPendingQ.isEmpty()) {
     query = dont_AddRef(mPendingQ.popLast());
     query->Complete(status, ""_ns);
   }
 
-  if (aShutdown) mPAC.Shutdown();
+  if (aShutdown) {
+    mPAC->Shutdown();
+  }
 }
 
 void nsPACMan::ProcessPendingQ() {
   MOZ_ASSERT(!NS_IsMainThread(), "wrong thread");
   while (ProcessPending()) {
     ;
   }
 
   if (mShutdown) {
-    mPAC.Shutdown();
+    mPAC->Shutdown();
   } else {
     // do GC while the thread has nothing pending
-    mPAC.GC();
+    mPAC->GC();
   }
 }
 
 // returns true if progress was made by shortening the queue
 bool nsPACMan::ProcessPending() {
   if (mPendingQ.isEmpty()) return false;
 
   // queue during normal load, but if we are retrying a failed load then
@@ -826,20 +841,23 @@ bool nsPACMan::ProcessPending() {
     }
     LOG(("Use proxy from system settings: %s\n", pacString.get()));
     query->Complete(NS_OK, pacString);
     completed = true;
   }
 
   // the systemproxysettings didn't complete the resolution. try via PAC
   if (!completed) {
-    nsresult status =
-        mPAC.GetProxyForURI(query->mSpec, query->mHost, pacString);
-    LOG(("Use proxy from PAC: %s\n", pacString.get()));
-    query->Complete(status, pacString);
+    auto callback = [query(query)](nsresult aStatus,
+                                   const nsACString& aResult) {
+      LOG(("Use proxy from PAC: %s\n", PromiseFlatCString(aResult).get()));
+      query->Complete(aStatus, aResult);
+    };
+    mPAC->GetProxyForURIWithCallback(query->mSpec, query->mHost,
+                                     std::move(callback));
   }
 
   mInProgress = false;
   return true;
 }
 
 NS_IMPL_ISUPPORTS(nsPACMan, nsIStreamLoaderObserver, nsIInterfaceRequestor,
                   nsIChannelEventSink)
--- a/netwerk/base/nsPACMan.h
+++ b/netwerk/base/nsPACMan.h
@@ -255,17 +255,17 @@ class nsPACMan final : public nsIStreamL
    * instantiation of the thread.
    *
    * @param aEvent The event to disptach.
    * @param aSync Whether or not this should be synchronous dispatch.
    */
   nsresult DispatchToPAC(already_AddRefed<nsIRunnable> aEvent,
                          bool aSync = false);
 
-  ProxyAutoConfig mPAC;
+  UniquePtr<ProxyAutoConfigBase> mPAC;
   nsCOMPtr<nsIThread> mPACThread;
   nsCOMPtr<nsISystemProxySettings> mSystemProxySettings;
   nsCOMPtr<nsIDHCPClient> mDHCPClient;
 
   LinkedList<PendingPACQuery> mPendingQ; /* pac thread only */
 
   // These specs are not nsIURI so that they can be used off the main thread.
   // The non-normalized versions are directly from the configuration, the
new file mode 100644
--- /dev/null
+++ b/netwerk/ipc/PProxyAutoConfig.ipdl
@@ -0,0 +1,22 @@
+/* -*- 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/. */
+
+namespace mozilla {
+namespace net {
+
+sync protocol PProxyAutoConfig
+{
+child:
+  async ConfigurePAC(nsCString aPACURI, nsCString aPACScriptData,
+                     bool aIncludePath, uint32_t aExtraHeapSize);
+  async GetProxyForURI(nsCString aTestURI, nsCString aTestHost)
+    returns (nsresult aStatus, nsCString aResult);
+  async GC();
+
+};
+
+} // namespace net
+} // namespace mozilla
--- a/netwerk/ipc/PSocketProcess.ipdl
+++ b/netwerk/ipc/PSocketProcess.ipdl
@@ -15,16 +15,17 @@ include protocol PParentToChildStream;
 include protocol PInputChannelThrottleQueue;
 include protocol PBackground;
 include protocol PAltService;
 include protocol PAltSvcTransaction;
 include protocol PTRRService;
 include protocol PProxyConfigLookup;
 include protocol PNativeDNSResolverOverride;
 include protocol PRemoteLazyInputStream;
+include protocol PProxyAutoConfig;
 
 #if defined(MOZ_SANDBOX) && defined(MOZ_DEBUG) && defined(ENABLE_TESTS)
 include protocol PSandboxTesting;
 #endif
 
 include MemoryReportTypes;
 include NeckoChannelParams;
 include PrefsTypes;
@@ -131,16 +132,18 @@ parent:
   async CachePushCheck(nsIURI aPushedURL,
                        OriginAttributes aOriginAttributes,
                        nsCString aRequestString)
     returns (bool aAccepted);
   async ODoHServiceActivated(bool aActivated);
 
   async ExcludeHttp2OrHttp3(HttpConnectionInfoCloneArgs aArgs);
 
+  async OnConsoleMessage(nsString aMessage);
+
 child:
   async Init(SocketPorcessInitAttributes aAttributes);
   async PreferenceUpdate(Pref pref);
   async RequestMemoryReport(uint32_t generation,
                             bool anonymize,
                             bool minimizeMemoryUsage,
                             FileDescriptor? DMDFile)
       returns (uint32_t aGeneration);
@@ -176,16 +179,18 @@ child:
 
   async GetSocketData()
     returns (SocketDataArgs data);
   async GetDNSCacheEntries()
     returns (DNSCacheEntries[] entries);
    async GetHttpConnectionData()
     returns (HttpRetParams[] params);
 
+  async InitProxyAutoConfigChild(Endpoint<PProxyAutoConfigChild> endpoint);
+
 both:
   async PFileDescriptorSet(FileDescriptor fd);
   async PDNSRequest(nsCString hostName, nsCString trrServer, uint16_t type,
                     OriginAttributes originAttributes, uint32_t flags);
 };
 
 } // namespace net
 } // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/ipc/ProxyAutoConfigChild.cpp
@@ -0,0 +1,199 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "ProxyAutoConfigChild.h"
+
+#include "mozilla/ipc/Endpoint.h"
+#include "mozilla/net/SocketProcessChild.h"
+#include "mozilla/SpinEventLoopUntil.h"
+#include "nsIObserver.h"
+#include "nsIObserverService.h"
+#include "nsThreadUtils.h"
+#include "ProxyAutoConfig.h"
+
+namespace mozilla::net {
+
+static bool sThreadLocalSetup = false;
+static uint32_t sThreadLocalIndex = 0xdeadbeef;
+StaticRefPtr<nsIThread> ProxyAutoConfigChild::sPACThread;
+bool ProxyAutoConfigChild::sShutdownObserverRegistered = false;
+Atomic<uint32_t> ProxyAutoConfigChild::sLiveActorCount(0);
+
+namespace {
+
+class ShutdownObserver final : public nsIObserver {
+ public:
+  ShutdownObserver() = default;
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIOBSERVER
+
+ private:
+  ~ShutdownObserver() = default;
+};
+
+NS_IMPL_ISUPPORTS(ShutdownObserver, nsIObserver)
+
+NS_IMETHODIMP
+ShutdownObserver::Observe(nsISupports* aSubject, const char* aTopic,
+                          const char16_t* aData) {
+  ProxyAutoConfigChild::ShutdownPACThread();
+  return NS_OK;
+}
+
+}  // namespace
+
+// static
+bool ProxyAutoConfigChild::Create(Endpoint<PProxyAutoConfigChild>&& aEndpoint) {
+  if (!sPACThread && !CreatePACThread()) {
+    NS_WARNING("Failed to create pac thread!");
+    return false;
+  }
+
+  if (!sShutdownObserverRegistered) {
+    nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+    if (NS_WARN_IF(!obs)) {
+      return false;
+    }
+    nsCOMPtr<nsIObserver> observer = new ShutdownObserver();
+    nsresult rv = obs->AddObserver(observer, "xpcom-shutdown-threads", false);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return false;
+    }
+    sShutdownObserverRegistered = true;
+  }
+
+  RefPtr<ProxyAutoConfigChild> actor = new ProxyAutoConfigChild();
+  if (NS_FAILED(sPACThread->Dispatch(
+          NS_NewRunnableFunction("ProxyAutoConfigChild::ProxyAutoConfigChild",
+                                 [actor = std::move(actor),
+                                  endpoint = std::move(aEndpoint)]() mutable {
+                                   MOZ_ASSERT(endpoint.IsValid());
+
+                                   // Transfer ownership to PAC thread. If
+                                   // Bind() fails then we will release this
+                                   // reference in Destroy.
+                                   ProxyAutoConfigChild* actorTmp;
+                                   actor.forget(&actorTmp);
+
+                                   if (!endpoint.Bind(actorTmp)) {
+                                     actorTmp->Destroy();
+                                   }
+                                 })))) {
+    NS_WARNING("Failed to dispatch runnable!");
+    return false;
+  }
+
+  return true;
+}
+
+// static
+bool ProxyAutoConfigChild::CreatePACThread() {
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (SocketProcessChild::GetSingleton()->IsShuttingDown()) {
+    NS_WARNING("Trying to create pac thread after shutdown has already begun!");
+    return false;
+  }
+
+  nsCOMPtr<nsIThread> thread;
+  if (NS_FAILED(NS_NewNamedThread("ProxyResolution", getter_AddRefs(thread)))) {
+    NS_WARNING("NS_NewNamedThread failed!");
+    return false;
+  }
+
+  sPACThread = thread.forget();
+  return true;
+}
+
+// static
+void ProxyAutoConfigChild::ShutdownPACThread() {
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (sPACThread) {
+    // Wait until all actos are released.
+    SpinEventLoopUntil("ProxyAutoConfigChild::ShutdownPACThread"_ns,
+                       [&]() { return !sLiveActorCount; });
+
+    nsCOMPtr<nsIThread> thread = sPACThread.get();
+    sPACThread = nullptr;
+    MOZ_ALWAYS_SUCCEEDS(thread->Shutdown());
+  }
+}
+
+ProxyAutoConfigChild::ProxyAutoConfigChild()
+    : mPAC(MakeUnique<ProxyAutoConfig>()) {
+  if (!sThreadLocalSetup) {
+    sThreadLocalSetup = true;
+    PR_NewThreadPrivateIndex(&sThreadLocalIndex, nullptr);
+  }
+
+  mPAC->SetThreadLocalIndex(sThreadLocalIndex);
+  sLiveActorCount++;
+}
+
+ProxyAutoConfigChild::~ProxyAutoConfigChild() {
+  MOZ_ASSERT(NS_IsMainThread());
+  sLiveActorCount--;
+}
+
+mozilla::ipc::IPCResult ProxyAutoConfigChild::RecvConfigurePAC(
+    const nsCString& aPACURI, const nsCString& aPACScriptData,
+    const bool& aIncludePath, const uint32_t& aExtraHeapSize) {
+  mPAC->ConfigurePAC(aPACURI, aPACScriptData, aIncludePath, aExtraHeapSize,
+                     GetMainThreadSerialEventTarget());
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ProxyAutoConfigChild::RecvGetProxyForURI(
+    const nsCString& aTestURI, const nsCString& aTestHost,
+    GetProxyForURIResolver&& aResolver) {
+  // When PAC is waiting for DNS result, we need to wait.
+  if (mPAC->WaitingForDNSResolve()) {
+    RefPtr<ProxyAutoConfigChild> self = this;
+    NS_DispatchToCurrentThread(NS_NewRunnableFunction(
+        "ProxyAutoConfigChild::RecvGetProxyForURI",
+        [self, testURI(aTestURI), testHost(aTestHost),
+         resolver{std::move(aResolver)}]() mutable {
+          self->RecvGetProxyForURI(testURI, testHost, std::move(resolver));
+        }));
+    return IPC_OK();
+  }
+  nsCString result;
+  nsresult rv = mPAC->GetProxyForURI(aTestURI, aTestHost, result);
+  aResolver(Tuple<const nsresult&, const nsCString&>(rv, result));
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ProxyAutoConfigChild::RecvGC() {
+  mPAC->GC();
+  return IPC_OK();
+}
+
+void ProxyAutoConfigChild::ActorDestroy(ActorDestroyReason aWhy) {
+  UniquePtr<ProxyAutoConfig> pac(std::move(mPAC));
+  pac->Shutdown();
+
+  // To avoid racing with the main thread, we need to dispatch
+  // ProxyAutoConfigChild::Destroy again.
+  MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(NewNonOwningRunnableMethod(
+      "ProxyAutoConfigChild::Destroy", this, &ProxyAutoConfigChild::Destroy)));
+}
+
+void ProxyAutoConfigChild::Destroy() {
+  // May be called on any thread!
+  MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(NewNonOwningRunnableMethod(
+      "ProxyAutoConfigChild::MainThreadActorDestroy", this,
+      &ProxyAutoConfigChild::MainThreadActorDestroy)));
+}
+
+void ProxyAutoConfigChild::MainThreadActorDestroy() {
+  MOZ_ASSERT(NS_IsMainThread());
+
+  // This may be the last reference!
+  Release();
+}
+
+}  // namespace mozilla::net
new file mode 100644
--- /dev/null
+++ b/netwerk/ipc/ProxyAutoConfigChild.h
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 ProxyAutoConfigChild_h__
+#define ProxyAutoConfigChild_h__
+
+#include "mozilla/net/PProxyAutoConfigChild.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/UniquePtr.h"
+
+namespace mozilla {
+namespace net {
+
+class ProxyAutoConfig;
+
+class ProxyAutoConfigChild final : public PProxyAutoConfigChild {
+ public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ProxyAutoConfigChild)
+
+  static bool Create(Endpoint<PProxyAutoConfigChild>&& aEndpoint);
+  static bool CreatePACThread();
+  static void ShutdownPACThread();
+
+  ProxyAutoConfigChild();
+
+  void ActorDestroy(ActorDestroyReason aWhy) override;
+  mozilla::ipc::IPCResult RecvConfigurePAC(const nsCString& aPACURI,
+                                           const nsCString& aPACScriptData,
+                                           const bool& aIncludePath,
+                                           const uint32_t& aExtraHeapSize);
+  mozilla::ipc::IPCResult RecvGetProxyForURI(
+      const nsCString& aTestURI, const nsCString& aTestHost,
+      GetProxyForURIResolver&& aResolver);
+
+  mozilla::ipc::IPCResult RecvGC();
+
+  void Destroy();
+
+ private:
+  virtual ~ProxyAutoConfigChild();
+  void MainThreadActorDestroy();
+
+  UniquePtr<ProxyAutoConfig> mPAC;
+
+  static StaticRefPtr<nsIThread> sPACThread;
+  static bool sShutdownObserverRegistered;
+  static Atomic<uint32_t> sLiveActorCount;
+};
+
+}  // namespace net
+}  // namespace mozilla
+
+#endif  // ProxyAutoConfigChild_h__
new file mode 100644
--- /dev/null
+++ b/netwerk/ipc/ProxyAutoConfigParent.cpp
@@ -0,0 +1,22 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "ProxyAutoConfigParent.h"
+
+#include "mozilla/ipc/Endpoint.h"
+#include "nsIConsoleService.h"
+
+namespace mozilla::net {
+
+ProxyAutoConfigParent::ProxyAutoConfigParent() = default;
+
+ProxyAutoConfigParent::~ProxyAutoConfigParent() = default;
+
+void ProxyAutoConfigParent::Init(Endpoint<PProxyAutoConfigParent>&& aEndpoint) {
+  DebugOnly<bool> ok = aEndpoint.Bind(this);
+  MOZ_ASSERT(ok);
+}
+
+}  // namespace mozilla::net
new file mode 100644
--- /dev/null
+++ b/netwerk/ipc/ProxyAutoConfigParent.h
@@ -0,0 +1,28 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 ProxyAutoConfigParent_h__
+#define ProxyAutoConfigParent_h__
+
+#include "mozilla/net/PProxyAutoConfigParent.h"
+
+namespace mozilla {
+namespace net {
+
+class ProxyAutoConfigParent final : public PProxyAutoConfigParent {
+ public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ProxyAutoConfigParent)
+
+  ProxyAutoConfigParent();
+  void Init(Endpoint<PProxyAutoConfigParent>&& aEndpoint);
+
+ private:
+  virtual ~ProxyAutoConfigParent();
+};
+
+}  // namespace net
+}  // namespace mozilla
+
+#endif  // ProxyAutoConfigParent_h__
--- a/netwerk/ipc/SocketProcessChild.cpp
+++ b/netwerk/ipc/SocketProcessChild.cpp
@@ -20,33 +20,38 @@
 #include "mozilla/ipc/FileDescriptorSetChild.h"
 #include "mozilla/ipc/IPCStreamAlloc.h"
 #include "mozilla/ipc/ProcessChild.h"
 #include "mozilla/net/AltSvcTransactionChild.h"
 #include "mozilla/net/BackgroundDataBridgeParent.h"
 #include "mozilla/net/DNSRequestChild.h"
 #include "mozilla/net/DNSRequestParent.h"
 #include "mozilla/net/NativeDNSResolverOverrideChild.h"
+#include "mozilla/net/ProxyAutoConfigChild.h"
 #include "mozilla/net/TRRServiceChild.h"
 #include "mozilla/ipc/PChildToParentStreamChild.h"
 #include "mozilla/ipc/PParentToChildStreamChild.h"
 #include "mozilla/ipc/ProcessUtils.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/RemoteLazyInputStreamChild.h"
+#include "mozilla/StaticPrefs_network.h"
 #include "mozilla/Telemetry.h"
 #include "nsDebugImpl.h"
 #include "nsHttpConnectionInfo.h"
 #include "nsHttpHandler.h"
 #include "nsIDNSService.h"
 #include "nsIHttpActivityObserver.h"
 #include "nsNetUtil.h"
 #include "nsNSSComponent.h"
 #include "nsSocketTransportService2.h"
 #include "nsThreadManager.h"
 #include "SocketProcessBridgeParent.h"
+#include "jsapi.h"
+#include "js/Initialization.h"
+#include "XPCSelfHostedShmem.h"
 
 #if defined(XP_WIN)
 #  include <process.h>
 #else
 #  include <unistd.h>
 #endif
 
 #if defined(XP_LINUX) && defined(MOZ_SANDBOX)
@@ -63,16 +68,18 @@
 #  include "mozilla/SandboxTestingChild.h"
 #endif
 
 namespace mozilla {
 namespace net {
 
 using namespace ipc;
 
+static bool sInitializedJS = false;
+
 SocketProcessChild* sSocketProcessChild;
 
 SocketProcessChild::SocketProcessChild() {
   LOG(("CONSTRUCT SocketProcessChild::SocketProcessChild\n"));
   nsDebugImpl::SetMultiprocessMode("Socket");
 
   MOZ_COUNT_CTOR(SocketProcessChild);
   sSocketProcessChild = this;
@@ -116,16 +123,27 @@ bool SocketProcessChild::Init(base::Proc
 
   // Init crash reporter support.
   CrashReporterClient::InitSingleton(this);
 
   if (NS_FAILED(NS_InitMinimalXPCOM())) {
     return false;
   }
 
+  if (StaticPrefs::network_proxy_parse_pac_on_socket_process()) {
+    // For parsing PAC.
+    const char* jsInitFailureReason = JS_InitWithFailureDiagnostic();
+    if (jsInitFailureReason) {
+      MOZ_CRASH_UNSAFE(jsInitFailureReason);
+    }
+    sInitializedJS = true;
+
+    xpc::SelfHostedShmem::GetSingleton();
+  }
+
   BackgroundChild::Startup();
   SetThisProcessName("Socket Process");
 #if defined(XP_MACOSX)
   // Close all current connections to the WindowServer. This ensures that the
   // Activity Monitor will not label the socket process as "Not responding"
   // because it's not running a native event loop. See bug 1384336.
   CGSShutdownServerConnections();
 #endif  // XP_MACOSX
@@ -176,16 +194,20 @@ void SocketProcessChild::CleanUp() {
     }
   }
 
   {
     MutexAutoLock lock(mMutex);
     mBackgroundDataBridgeMap.Clear();
   }
   NS_ShutdownXPCOM(nullptr);
+
+  if (sInitializedJS) {
+    JS_ShutDown();
+  }
 }
 
 mozilla::ipc::IPCResult SocketProcessChild::RecvInit(
     const SocketPorcessInitAttributes& aAttributes) {
   Unused << RecvSetOffline(aAttributes.mOffline());
   Unused << RecvSetConnectivity(aAttributes.mConnectivity());
   if (aAttributes.mInitSandbox()) {
     Unused << RecvInitLinuxSandbox(aAttributes.mSandboxBroker());
@@ -613,10 +635,16 @@ mozilla::ipc::IPCResult SocketProcessChi
             nsTArray<HttpRetParams> data;
             HttpInfo::GetHttpConnectionData(&data);
             resolver->OnResolve(std::move(data));
           }),
       NS_DISPATCH_NORMAL);
   return IPC_OK();
 }
 
+mozilla::ipc::IPCResult SocketProcessChild::RecvInitProxyAutoConfigChild(
+    Endpoint<PProxyAutoConfigChild>&& aEndpoint) {
+  Unused << ProxyAutoConfigChild::Create(std::move(aEndpoint));
+  return IPC_OK();
+}
+
 }  // namespace net
 }  // namespace mozilla
--- a/netwerk/ipc/SocketProcessChild.h
+++ b/netwerk/ipc/SocketProcessChild.h
@@ -14,16 +14,17 @@
 
 namespace mozilla {
 class ChildProfilerController;
 }
 
 namespace mozilla {
 namespace net {
 
+class ProxyAutoConfigChild;
 class SocketProcessBridgeParent;
 class BackgroundDataBridgeParent;
 
 // The IPC actor implements PSocketProcessChild in child process.
 // This is allocated and kept alive by SocketProcessImpl.
 class SocketProcessChild final
     : public PSocketProcessChild,
       public mozilla::ipc::ChildToParentStreamActorManager {
@@ -136,16 +137,19 @@ class SocketProcessChild final
   AllocPRemoteLazyInputStreamChild(const nsID& aID, const uint64_t& aSize);
 
   mozilla::ipc::IPCResult RecvGetSocketData(GetSocketDataResolver&& aResolve);
   mozilla::ipc::IPCResult RecvGetDNSCacheEntries(
       GetDNSCacheEntriesResolver&& aResolve);
   mozilla::ipc::IPCResult RecvGetHttpConnectionData(
       GetHttpConnectionDataResolver&& aResolve);
 
+  mozilla::ipc::IPCResult RecvInitProxyAutoConfigChild(
+      Endpoint<PProxyAutoConfigChild>&& aEndpoint);
+
  protected:
   friend class SocketProcessImpl;
   ~SocketProcessChild();
 
  private:
   // Mapping of content process id and the SocketProcessBridgeParent.
   // This table keeps SocketProcessBridgeParent alive in socket process.
   nsRefPtrHashtable<nsUint32HashKey, SocketProcessBridgeParent>
--- a/netwerk/ipc/SocketProcessParent.cpp
+++ b/netwerk/ipc/SocketProcessParent.cpp
@@ -17,16 +17,17 @@
 #include "mozilla/ipc/PChildToParentStreamParent.h"
 #include "mozilla/ipc/PParentToChildStreamParent.h"
 #include "mozilla/net/DNSRequestParent.h"
 #include "mozilla/net/ProxyConfigLookupParent.h"
 #include "mozilla/RemoteLazyInputStreamParent.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/TelemetryIPC.h"
 #include "nsIAppStartup.h"
+#include "nsIConsoleService.h"
 #include "nsIHttpActivityObserver.h"
 #include "nsIObserverService.h"
 #include "nsNSSIOLayer.h"
 #include "PSMIPCCommon.h"
 #include "secerr.h"
 #ifdef MOZ_WEBRTC
 #  include "mozilla/dom/ContentProcessManager.h"
 #  include "mozilla/dom/BrowserParent.h"
@@ -451,10 +452,20 @@ mozilla::ipc::IPCResult SocketProcessPar
   if (cinfo->IsHttp3()) {
     gHttpHandler->ExcludeHttp3(cinfo);
   } else {
     gHttpHandler->ExcludeHttp2(cinfo);
   }
   return IPC_OK();
 }
 
+mozilla::ipc::IPCResult SocketProcessParent::RecvOnConsoleMessage(
+    const nsString& aMessage) {
+  nsCOMPtr<nsIConsoleService> consoleService =
+      do_GetService(NS_CONSOLESERVICE_CONTRACTID);
+  if (consoleService) {
+    consoleService->LogStringMessage(aMessage.get());
+  }
+  return IPC_OK();
+}
+
 }  // namespace net
 }  // namespace mozilla
--- a/netwerk/ipc/SocketProcessParent.h
+++ b/netwerk/ipc/SocketProcessParent.h
@@ -118,16 +118,17 @@ class SocketProcessParent final
   mozilla::ipc::IPCResult RecvPRemoteLazyInputStreamConstructor(
       PRemoteLazyInputStreamParent* aActor, const nsID& aID,
       const uint64_t& aSize);
 
   mozilla::ipc::IPCResult RecvODoHServiceActivated(const bool& aActivated);
 
   mozilla::ipc::IPCResult RecvExcludeHttp2OrHttp3(
       const HttpConnectionInfoCloneArgs& aArgs);
+  mozilla::ipc::IPCResult RecvOnConsoleMessage(const nsString& aMessage);
 
  private:
   SocketProcessHost* mHost;
   UniquePtr<dom::MemoryReportRequestHost> mMemoryReportRequest;
 
   static void Destroy(UniquePtr<SocketProcessParent>&& aParent);
 };
 
--- a/netwerk/ipc/moz.build
+++ b/netwerk/ipc/moz.build
@@ -14,16 +14,18 @@ EXPORTS.mozilla.net += [
     "InputChannelThrottleQueueParent.h",
     "NeckoChild.h",
     "NeckoCommon.h",
     "NeckoMessageUtils.h",
     "NeckoParent.h",
     "NeckoTargetHolder.h",
     "ParentChannelWrapper.h",
     "ParentProcessDocumentChannel.h",
+    "ProxyAutoConfigChild.h",
+    "ProxyAutoConfigParent.h",
     "ProxyConfigLookup.h",
     "ProxyConfigLookupChild.h",
     "ProxyConfigLookupParent.h",
     "SocketProcessBridgeChild.h",
     "SocketProcessBridgeParent.h",
     "SocketProcessChild.h",
     "SocketProcessHost.h",
     "SocketProcessImpl.h",
@@ -49,27 +51,34 @@ UNIFIED_SOURCES += [
     "SocketProcessBridgeChild.cpp",
     "SocketProcessBridgeParent.cpp",
     "SocketProcessChild.cpp",
     "SocketProcessHost.cpp",
     "SocketProcessImpl.cpp",
     "SocketProcessParent.cpp",
 ]
 
+SOURCES += [
+    "ProxyAutoConfigChild.cpp",
+    "ProxyAutoConfigParent.cpp",
+]
+
+
 PREPROCESSED_IPDL_SOURCES += [
     "PNecko.ipdl",
     "PSocketProcess.ipdl",
 ]
 
 IPDL_SOURCES = [
     "NeckoChannelParams.ipdlh",
     "PDataChannel.ipdl",
     "PDocumentChannel.ipdl",
     "PFileChannel.ipdl",
     "PInputChannelThrottleQueue.ipdl",
+    "PProxyAutoConfig.ipdl",
     "PProxyConfigLookup.ipdl",
     "PSimpleChannel.ipdl",
     "PSocketProcessBridge.ipdl",
 ]
 
 # needed so --disable-webrtc builds work (yes, a bit messy)
 if not CONFIG["MOZ_WEBRTC"]:
     IPDL_SOURCES += [