summaryrefslogtreecommitdiffstats
path: root/netwerk/ipc
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /netwerk/ipc
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'netwerk/ipc')
-rw-r--r--netwerk/ipc/ChannelEventQueue.cpp227
-rw-r--r--netwerk/ipc/ChannelEventQueue.h383
-rw-r--r--netwerk/ipc/DocumentChannel.cpp484
-rw-r--r--netwerk/ipc/DocumentChannel.h120
-rw-r--r--netwerk/ipc/DocumentChannelChild.cpp483
-rw-r--r--netwerk/ipc/DocumentChannelChild.h76
-rw-r--r--netwerk/ipc/DocumentChannelParent.cpp160
-rw-r--r--netwerk/ipc/DocumentChannelParent.h68
-rw-r--r--netwerk/ipc/DocumentLoadListener.cpp3027
-rw-r--r--netwerk/ipc/DocumentLoadListener.h630
-rw-r--r--netwerk/ipc/InputChannelThrottleQueueChild.cpp30
-rw-r--r--netwerk/ipc/InputChannelThrottleQueueChild.h33
-rw-r--r--netwerk/ipc/InputChannelThrottleQueueParent.cpp125
-rw-r--r--netwerk/ipc/InputChannelThrottleQueueParent.h51
-rw-r--r--netwerk/ipc/NeckoChannelParams.ipdlh623
-rw-r--r--netwerk/ipc/NeckoChild.cpp337
-rw-r--r--netwerk/ipc/NeckoChild.h93
-rw-r--r--netwerk/ipc/NeckoCommon.cpp68
-rw-r--r--netwerk/ipc/NeckoCommon.h144
-rw-r--r--netwerk/ipc/NeckoMessageUtils.h190
-rw-r--r--netwerk/ipc/NeckoParent.cpp889
-rw-r--r--netwerk/ipc/NeckoParent.h217
-rw-r--r--netwerk/ipc/NeckoTargetHolder.cpp38
-rw-r--r--netwerk/ipc/NeckoTargetHolder.h40
-rw-r--r--netwerk/ipc/PDataChannel.ipdl25
-rw-r--r--netwerk/ipc/PDocumentChannel.ipdl66
-rw-r--r--netwerk/ipc/PFileChannel.ipdl28
-rw-r--r--netwerk/ipc/PInputChannelThrottleQueue.ipdl25
-rw-r--r--netwerk/ipc/PNecko.ipdl178
-rw-r--r--netwerk/ipc/PProxyAutoConfig.ipdl21
-rw-r--r--netwerk/ipc/PProxyConfigLookup.ipdl21
-rw-r--r--netwerk/ipc/PSimpleChannel.ipdl25
-rw-r--r--netwerk/ipc/PSocketProcess.ipdl222
-rw-r--r--netwerk/ipc/PSocketProcessBackground.ipdl59
-rw-r--r--netwerk/ipc/PSocketProcessBridge.ipdl48
-rw-r--r--netwerk/ipc/ParentChannelWrapper.cpp101
-rw-r--r--netwerk/ipc/ParentChannelWrapper.h39
-rw-r--r--netwerk/ipc/ParentProcessDocumentChannel.cpp314
-rw-r--r--netwerk/ipc/ParentProcessDocumentChannel.h62
-rw-r--r--netwerk/ipc/ProxyAutoConfigChild.cpp225
-rw-r--r--netwerk/ipc/ProxyAutoConfigChild.h84
-rw-r--r--netwerk/ipc/ProxyAutoConfigParent.cpp21
-rw-r--r--netwerk/ipc/ProxyAutoConfigParent.h28
-rw-r--r--netwerk/ipc/ProxyConfigLookup.cpp99
-rw-r--r--netwerk/ipc/ProxyConfigLookup.h44
-rw-r--r--netwerk/ipc/ProxyConfigLookupChild.cpp43
-rw-r--r--netwerk/ipc/ProxyConfigLookupChild.h39
-rw-r--r--netwerk/ipc/ProxyConfigLookupParent.cpp43
-rw-r--r--netwerk/ipc/ProxyConfigLookupParent.h35
-rw-r--r--netwerk/ipc/SocketProcessBackgroundChild.cpp113
-rw-r--r--netwerk/ipc/SocketProcessBackgroundChild.h45
-rw-r--r--netwerk/ipc/SocketProcessBackgroundParent.cpp169
-rw-r--r--netwerk/ipc/SocketProcessBackgroundParent.h53
-rw-r--r--netwerk/ipc/SocketProcessBridgeChild.cpp186
-rw-r--r--netwerk/ipc/SocketProcessBridgeChild.h50
-rw-r--r--netwerk/ipc/SocketProcessBridgeParent.cpp97
-rw-r--r--netwerk/ipc/SocketProcessBridgeParent.h44
-rw-r--r--netwerk/ipc/SocketProcessChild.cpp833
-rw-r--r--netwerk/ipc/SocketProcessChild.h188
-rw-r--r--netwerk/ipc/SocketProcessHost.cpp292
-rw-r--r--netwerk/ipc/SocketProcessHost.h150
-rw-r--r--netwerk/ipc/SocketProcessImpl.cpp76
-rw-r--r--netwerk/ipc/SocketProcessImpl.h35
-rw-r--r--netwerk/ipc/SocketProcessLogging.h20
-rw-r--r--netwerk/ipc/SocketProcessParent.cpp364
-rw-r--r--netwerk/ipc/SocketProcessParent.h117
-rw-r--r--netwerk/ipc/moz.build117
67 files changed, 13380 insertions, 0 deletions
diff --git a/netwerk/ipc/ChannelEventQueue.cpp b/netwerk/ipc/ChannelEventQueue.cpp
new file mode 100644
index 0000000000..911deaa7d9
--- /dev/null
+++ b/netwerk/ipc/ChannelEventQueue.cpp
@@ -0,0 +1,227 @@
+/* -*- 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 "ChannelEventQueue.h"
+
+#include "mozilla/Assertions.h"
+#include "mozilla/Unused.h"
+#include "nsIChannel.h"
+#include "mozilla/dom/Document.h"
+#include "nsThreadUtils.h"
+
+namespace mozilla {
+namespace net {
+
+ChannelEvent* ChannelEventQueue::TakeEvent() {
+ mMutex.AssertCurrentThreadOwns();
+ MOZ_ASSERT(mFlushing);
+
+ if (mSuspended || mEventQueue.IsEmpty()) {
+ return nullptr;
+ }
+
+ UniquePtr<ChannelEvent> event(std::move(mEventQueue[0]));
+ mEventQueue.RemoveElementAt(0);
+
+ return event.release();
+}
+
+void ChannelEventQueue::FlushQueue() {
+ // Events flushed could include destruction of channel (and our own
+ // destructor) unless we make sure its refcount doesn't drop to 0 while this
+ // method is running.
+ nsCOMPtr<nsISupports> kungFuDeathGrip;
+ {
+ MutexAutoLock lock(mMutex);
+ kungFuDeathGrip = mOwner;
+ }
+ mozilla::Unused << kungFuDeathGrip; // Not used in this function
+
+#ifdef DEBUG
+ {
+ MutexAutoLock lock(mMutex);
+ MOZ_ASSERT(mFlushing);
+ }
+#endif // DEBUG
+
+ bool needResumeOnOtherThread = false;
+
+ while (true) {
+ UniquePtr<ChannelEvent> event;
+ {
+ MutexAutoLock lock(mMutex);
+ event.reset(TakeEvent());
+ if (!event) {
+ MOZ_ASSERT(mFlushing);
+ mFlushing = false;
+ MOZ_ASSERT(mEventQueue.IsEmpty() || (mSuspended || !!mForcedCount));
+ break;
+ }
+ }
+
+ nsCOMPtr<nsIEventTarget> target = event->GetEventTarget();
+ MOZ_ASSERT(target);
+
+ bool isCurrentThread = false;
+ nsresult rv = target->IsOnCurrentThread(&isCurrentThread);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ // Simply run this event on current thread if we are not sure about it
+ // in release channel, or assert in Aurora/Nightly channel.
+ MOZ_DIAGNOSTIC_ASSERT(false);
+ isCurrentThread = true;
+ }
+
+ if (!isCurrentThread) {
+ // Next event needs to run on another thread. Put it back to
+ // the front of the queue can try resume on that thread.
+ Suspend();
+ PrependEvent(std::move(event));
+
+ needResumeOnOtherThread = true;
+ {
+ MutexAutoLock lock(mMutex);
+ MOZ_ASSERT(mFlushing);
+ mFlushing = false;
+ MOZ_ASSERT(!mEventQueue.IsEmpty());
+ }
+ break;
+ }
+
+ event->Run();
+ } // end of while(true)
+
+ // The flush procedure is aborted because next event cannot be run on current
+ // thread. We need to resume the event processing right after flush procedure
+ // is finished.
+ // Note: we cannot call Resume() while "mFlushing == true" because
+ // CompleteResume will not trigger FlushQueue while there is an ongoing flush.
+ if (needResumeOnOtherThread) {
+ Resume();
+ }
+}
+
+void ChannelEventQueue::Suspend() {
+ MutexAutoLock lock(mMutex);
+ SuspendInternal();
+}
+
+void ChannelEventQueue::SuspendInternal() {
+ mMutex.AssertCurrentThreadOwns();
+
+ mSuspended = true;
+ mSuspendCount++;
+}
+
+void ChannelEventQueue::Resume() {
+ MutexAutoLock lock(mMutex);
+ ResumeInternal();
+}
+
+void ChannelEventQueue::ResumeInternal() {
+ mMutex.AssertCurrentThreadOwns();
+
+ // Resuming w/o suspend: error in debug mode, ignore in build
+ MOZ_ASSERT(mSuspendCount > 0);
+ if (mSuspendCount <= 0) {
+ return;
+ }
+
+ if (!--mSuspendCount) {
+ if (mEventQueue.IsEmpty() || !!mForcedCount) {
+ // Nothing in queue to flush or waiting for AutoEventEnqueuer to
+ // finish the force enqueue period, simply clear the flag.
+ mSuspended = false;
+ return;
+ }
+
+ // Hold a strong reference of mOwner to avoid the channel release
+ // before CompleteResume was executed.
+ class CompleteResumeRunnable : public Runnable {
+ public:
+ explicit CompleteResumeRunnable(ChannelEventQueue* aQueue,
+ nsISupports* aOwner)
+ : Runnable("CompleteResumeRunnable"),
+ mQueue(aQueue),
+ mOwner(aOwner) {}
+
+ NS_IMETHOD Run() override {
+ mQueue->CompleteResume();
+ return NS_OK;
+ }
+
+ private:
+ virtual ~CompleteResumeRunnable() = default;
+
+ RefPtr<ChannelEventQueue> mQueue;
+ nsCOMPtr<nsISupports> mOwner;
+ };
+
+ if (!mOwner) {
+ return;
+ }
+
+ // Worker thread requires a CancelableRunnable.
+ RefPtr<Runnable> event = new CompleteResumeRunnable(this, mOwner);
+
+ nsCOMPtr<nsIEventTarget> target;
+ target = mEventQueue[0]->GetEventTarget();
+ MOZ_ASSERT(target);
+
+ Unused << NS_WARN_IF(
+ NS_FAILED(target->Dispatch(event.forget(), NS_DISPATCH_NORMAL)));
+ }
+}
+
+bool ChannelEventQueue::MaybeSuspendIfEventsAreSuppressed() {
+ // We only ever need to suppress events on the main thread, since this is
+ // where content scripts can run.
+ if (!NS_IsMainThread()) {
+ return false;
+ }
+
+ // Only suppress events for queues associated with XHRs, as these can cause
+ // content scripts to run.
+ if (mHasCheckedForXMLHttpRequest && !mForXMLHttpRequest) {
+ return false;
+ }
+
+ mMutex.AssertCurrentThreadOwns();
+ nsCOMPtr<nsIChannel> channel(do_QueryInterface(mOwner));
+ if (!channel) {
+ return false;
+ }
+
+ nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
+ // Figure out if this is for an XHR, if we haven't done so already.
+ if (!mHasCheckedForXMLHttpRequest) {
+ nsContentPolicyType contentType = loadInfo->InternalContentPolicyType();
+ mForXMLHttpRequest =
+ (contentType == nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST);
+ mHasCheckedForXMLHttpRequest = true;
+
+ if (!mForXMLHttpRequest) {
+ return false;
+ }
+ }
+
+ // Suspend the queue if the associated document has suppressed event handling,
+ // *and* it is not in the middle of a synchronous operation that might require
+ // XHR events to be processed (such as a synchronous XHR).
+ RefPtr<dom::Document> document;
+ loadInfo->GetLoadingDocument(getter_AddRefs(document));
+ if (document && document->EventHandlingSuppressed() &&
+ !document->IsInSyncOperation()) {
+ document->AddSuspendedChannelEventQueue(this);
+ SuspendInternal();
+ return true;
+ }
+
+ return false;
+}
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/ipc/ChannelEventQueue.h b/netwerk/ipc/ChannelEventQueue.h
new file mode 100644
index 0000000000..1b61088e91
--- /dev/null
+++ b/netwerk/ipc/ChannelEventQueue.h
@@ -0,0 +1,383 @@
+/* -*- 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_ChannelEventQueue_h
+#define mozilla_net_ChannelEventQueue_h
+
+#include "nsTArray.h"
+#include "nsIEventTarget.h"
+#include "nsThreadUtils.h"
+#include "nsXULAppAPI.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/RecursiveMutex.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Unused.h"
+
+class nsISupports;
+
+namespace mozilla {
+namespace net {
+
+class ChannelEvent {
+ public:
+ MOZ_COUNTED_DEFAULT_CTOR(ChannelEvent)
+ MOZ_COUNTED_DTOR_VIRTUAL(ChannelEvent) virtual void Run() = 0;
+ virtual already_AddRefed<nsIEventTarget> GetEventTarget() = 0;
+};
+
+// Note that MainThreadChannelEvent should not be used in child process since
+// GetEventTarget() directly returns an unlabeled event target.
+class MainThreadChannelEvent : public ChannelEvent {
+ public:
+ MOZ_COUNTED_DEFAULT_CTOR(MainThreadChannelEvent)
+ MOZ_COUNTED_DTOR_OVERRIDE(MainThreadChannelEvent)
+
+ already_AddRefed<nsIEventTarget> GetEventTarget() override {
+ MOZ_ASSERT(XRE_IsParentProcess());
+
+ return do_AddRef(GetMainThreadSerialEventTarget());
+ }
+};
+
+class ChannelFunctionEvent : public ChannelEvent {
+ public:
+ ChannelFunctionEvent(
+ std::function<already_AddRefed<nsIEventTarget>()>&& aGetEventTarget,
+ std::function<void()>&& aCallback)
+ : mGetEventTarget(std::move(aGetEventTarget)),
+ mCallback(std::move(aCallback)) {}
+
+ void Run() override { mCallback(); }
+ already_AddRefed<nsIEventTarget> GetEventTarget() override {
+ return mGetEventTarget();
+ }
+
+ private:
+ const std::function<already_AddRefed<nsIEventTarget>()> mGetEventTarget;
+ const std::function<void()> mCallback;
+};
+
+// UnsafePtr is a work-around our static analyzer that requires all
+// ref-counted objects to be captured in lambda via a RefPtr
+// The ChannelEventQueue makes it safe to capture "this" by pointer only.
+// This is required as work-around to prevent cycles until bug 1596295
+// is resolved.
+template <typename T>
+class UnsafePtr {
+ public:
+ explicit UnsafePtr(T* aPtr) : mPtr(aPtr) {}
+
+ T& operator*() const { return *mPtr; }
+ T* operator->() const {
+ MOZ_ASSERT(mPtr, "dereferencing a null pointer");
+ return mPtr;
+ }
+ operator T*() const& { return mPtr; }
+ explicit operator bool() const { return mPtr != nullptr; }
+
+ private:
+ T* const mPtr;
+};
+
+class NeckoTargetChannelFunctionEvent : public ChannelFunctionEvent {
+ public:
+ template <typename T>
+ NeckoTargetChannelFunctionEvent(T* aChild, std::function<void()>&& aCallback)
+ : ChannelFunctionEvent(
+ [child = UnsafePtr<T>(aChild)]() {
+ MOZ_ASSERT(child);
+ return child->GetNeckoTarget();
+ },
+ std::move(aCallback)) {}
+};
+
+// Workaround for Necko re-entrancy dangers. We buffer IPDL messages in a
+// queue if still dispatching previous one(s) to listeners/observers.
+// Otherwise synchronous XMLHttpRequests and/or other code that spins the
+// event loop (ex: IPDL rpc) could cause listener->OnDataAvailable (for
+// instance) to be dispatched and called before mListener->OnStartRequest has
+// completed.
+// The ChannelEventQueue implementation ensures strict ordering of
+// event execution across target threads.
+
+class ChannelEventQueue final {
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ChannelEventQueue)
+
+ public:
+ explicit ChannelEventQueue(nsISupports* owner)
+ : mSuspendCount(0),
+ mSuspended(false),
+ mForcedCount(0),
+ mFlushing(false),
+ mHasCheckedForXMLHttpRequest(false),
+ mForXMLHttpRequest(false),
+ mOwner(owner),
+ mMutex("ChannelEventQueue::mMutex"),
+ mRunningMutex("ChannelEventQueue::mRunningMutex") {}
+
+ // Puts IPDL-generated channel event into queue, to be run later
+ // automatically when EndForcedQueueing and/or Resume is called.
+ //
+ // @param aCallback - the ChannelEvent
+ // @param aAssertionWhenNotQueued - this optional param will be used in an
+ // assertion when the event is executed directly.
+ inline void RunOrEnqueue(ChannelEvent* aCallback,
+ bool aAssertionWhenNotQueued = false);
+
+ // Append ChannelEvent in front of the event queue.
+ inline void PrependEvent(UniquePtr<ChannelEvent>&& aEvent);
+ inline void PrependEvents(nsTArray<UniquePtr<ChannelEvent>>& aEvents);
+
+ // After StartForcedQueueing is called, RunOrEnqueue() will start enqueuing
+ // events that will be run/flushed when EndForcedQueueing is called.
+ // - Note: queueing may still be required after EndForcedQueueing() (if the
+ // queue is suspended, etc): always call RunOrEnqueue() to avoid race
+ // conditions.
+ inline void StartForcedQueueing();
+ inline void EndForcedQueueing();
+
+ // Suspend/resume event queue. RunOrEnqueue() will start enqueuing
+ // events and they will be run/flushed when resume is called. These should be
+ // called when the channel owning the event queue is suspended/resumed.
+ void Suspend();
+ // Resume flushes the queue asynchronously, i.e. items in queue will be
+ // dispatched in a new event on the current thread.
+ void Resume();
+
+ void NotifyReleasingOwner() {
+ MutexAutoLock lock(mMutex);
+ mOwner = nullptr;
+ }
+
+#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
+ bool IsEmpty() {
+ MutexAutoLock lock(mMutex);
+ return mEventQueue.IsEmpty();
+ }
+#endif
+
+ private:
+ // Private destructor, to discourage deletion outside of Release():
+ ~ChannelEventQueue() = default;
+
+ void SuspendInternal();
+ void ResumeInternal();
+
+ bool MaybeSuspendIfEventsAreSuppressed() MOZ_REQUIRES(mMutex);
+
+ inline void MaybeFlushQueue();
+ void FlushQueue();
+ inline void CompleteResume();
+
+ ChannelEvent* TakeEvent();
+
+ nsTArray<UniquePtr<ChannelEvent>> mEventQueue MOZ_GUARDED_BY(mMutex);
+
+ uint32_t mSuspendCount MOZ_GUARDED_BY(mMutex);
+ bool mSuspended MOZ_GUARDED_BY(mMutex);
+ uint32_t mForcedCount // Support ForcedQueueing on multiple thread.
+ MOZ_GUARDED_BY(mMutex);
+ bool mFlushing MOZ_GUARDED_BY(mMutex);
+
+ // Whether the queue is associated with an XHR. This is lazily instantiated
+ // the first time it is needed. These are MainThread-only.
+ bool mHasCheckedForXMLHttpRequest;
+ bool mForXMLHttpRequest;
+
+ // Keep ptr to avoid refcount cycle: only grab ref during flushing.
+ nsISupports* mOwner MOZ_GUARDED_BY(mMutex);
+
+ // For atomic mEventQueue operation and state update
+ Mutex mMutex;
+
+ // To guarantee event execution order among threads
+ RecursiveMutex mRunningMutex MOZ_ACQUIRED_BEFORE(mMutex);
+
+ friend class AutoEventEnqueuer;
+};
+
+inline void ChannelEventQueue::RunOrEnqueue(ChannelEvent* aCallback,
+ bool aAssertionWhenNotQueued) {
+ MOZ_ASSERT(aCallback);
+ // Events execution could be a destruction of the channel (and our own
+ // destructor) unless we make sure its refcount doesn't drop to 0 while this
+ // method is running.
+ nsCOMPtr<nsISupports> kungFuDeathGrip;
+
+ // To avoid leaks.
+ UniquePtr<ChannelEvent> event(aCallback);
+
+ // To guarantee that the running event and all the events generated within
+ // it will be finished before events on other threads.
+ RecursiveMutexAutoLock lock(mRunningMutex);
+ {
+ MutexAutoLock lock(mMutex);
+ kungFuDeathGrip = mOwner; // must be under the lock
+
+ bool enqueue = !!mForcedCount || mSuspended || mFlushing ||
+ !mEventQueue.IsEmpty() ||
+ MaybeSuspendIfEventsAreSuppressed();
+ // To ensure strict ordering of events across multiple threads we buffer the
+ // events for the below cases:
+ // a. event queuing is forced by AutoEventEnqueuer
+ // b. event queue is suspended
+ // c. an event is currently flushed/executed from the queue
+ // d. queue is non-empty (pending events on remote thread targets)
+ if (enqueue) {
+ mEventQueue.AppendElement(std::move(event));
+ return;
+ }
+
+ nsCOMPtr<nsIEventTarget> target = event->GetEventTarget();
+ MOZ_ASSERT(target);
+
+ bool isCurrentThread = false;
+ DebugOnly<nsresult> rv = target->IsOnCurrentThread(&isCurrentThread);
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+
+ if (!isCurrentThread) {
+ // Leverage Suspend/Resume mechanism to trigger flush procedure without
+ // creating a new one.
+ // The execution of further events in the queue is blocked until the
+ // target thread completes the execution of this event.
+ // A callback is dispatched to the target thread to flush events from the
+ // queue. This is done
+ // by ResumeInternal which dispatches a runnable
+ // (CompleteResumeRunnable) to the target thread. The target thread will
+ // call CompleteResume to flush the queue. All the events are run
+ // synchronously in their respective target threads.
+ SuspendInternal();
+ mEventQueue.AppendElement(std::move(event));
+ ResumeInternal();
+ return;
+ }
+ }
+
+ MOZ_RELEASE_ASSERT(!aAssertionWhenNotQueued);
+ // execute the event synchronously if we are not queuing it and
+ // the target thread is the current thread
+ event->Run();
+}
+
+inline void ChannelEventQueue::StartForcedQueueing() {
+ MutexAutoLock lock(mMutex);
+ ++mForcedCount;
+}
+
+inline void ChannelEventQueue::EndForcedQueueing() {
+ bool tryFlush = false;
+ {
+ MutexAutoLock lock(mMutex);
+ MOZ_ASSERT(mForcedCount > 0);
+ if (!--mForcedCount) {
+ tryFlush = true;
+ }
+ }
+
+ if (tryFlush) {
+ MaybeFlushQueue();
+ }
+}
+
+inline void ChannelEventQueue::PrependEvent(UniquePtr<ChannelEvent>&& aEvent) {
+ MutexAutoLock lock(mMutex);
+
+ // Prepending event while no queue flush foreseen might cause the following
+ // channel events not run. This assertion here guarantee there must be a
+ // queue flush, either triggered by Resume or EndForcedQueueing, to execute
+ // the added event.
+ MOZ_ASSERT(mSuspended || !!mForcedCount);
+
+ mEventQueue.InsertElementAt(0, std::move(aEvent));
+}
+
+inline void ChannelEventQueue::PrependEvents(
+ nsTArray<UniquePtr<ChannelEvent>>& aEvents) {
+ MutexAutoLock lock(mMutex);
+
+ // Prepending event while no queue flush foreseen might cause the following
+ // channel events not run. This assertion here guarantee there must be a
+ // queue flush, either triggered by Resume or EndForcedQueueing, to execute
+ // the added events.
+ MOZ_ASSERT(mSuspended || !!mForcedCount);
+
+ mEventQueue.InsertElementsAt(0, aEvents.Length());
+
+ for (uint32_t i = 0; i < aEvents.Length(); i++) {
+ mEventQueue[i] = std::move(aEvents[i]);
+ }
+}
+
+inline void ChannelEventQueue::CompleteResume() {
+ bool tryFlush = false;
+ {
+ MutexAutoLock lock(mMutex);
+
+ // channel may have been suspended again since Resume fired event to call
+ // this.
+ if (!mSuspendCount) {
+ // we need to remain logically suspended (for purposes of queuing incoming
+ // messages) until this point, else new incoming messages could run before
+ // queued ones.
+ mSuspended = false;
+ tryFlush = true;
+ }
+ }
+
+ if (tryFlush) {
+ MaybeFlushQueue();
+ }
+}
+
+inline void ChannelEventQueue::MaybeFlushQueue() {
+ // Don't flush if forced queuing on, we're already being flushed, or
+ // suspended, or there's nothing to flush
+ bool flushQueue = false;
+
+ {
+ MutexAutoLock lock(mMutex);
+ flushQueue = !mForcedCount && !mFlushing && !mSuspended &&
+ !mEventQueue.IsEmpty() && !MaybeSuspendIfEventsAreSuppressed();
+
+ // Only one thread is allowed to run FlushQueue at a time.
+ if (flushQueue) {
+ mFlushing = true;
+ }
+ }
+
+ if (flushQueue) {
+ FlushQueue();
+ }
+}
+
+// Ensures that RunOrEnqueue() will be collecting events during its lifetime
+// (letting caller know incoming IPDL msgs should be queued). Flushes the queue
+// when it goes out of scope.
+class MOZ_STACK_CLASS AutoEventEnqueuer {
+ public:
+ explicit AutoEventEnqueuer(ChannelEventQueue* queue) : mEventQueue(queue) {
+ {
+ // Probably not actually needed, since NotifyReleasingOwner should
+ // only happen after this, but safer to take it in case things change
+ MutexAutoLock lock(queue->mMutex);
+ mOwner = queue->mOwner;
+ }
+ mEventQueue->StartForcedQueueing();
+ }
+ ~AutoEventEnqueuer() { mEventQueue->EndForcedQueueing(); }
+
+ private:
+ RefPtr<ChannelEventQueue> mEventQueue;
+ // Ensure channel object lives longer than ChannelEventQueue.
+ nsCOMPtr<nsISupports> mOwner;
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif
diff --git a/netwerk/ipc/DocumentChannel.cpp b/netwerk/ipc/DocumentChannel.cpp
new file mode 100644
index 0000000000..a0d6a0c1f9
--- /dev/null
+++ b/netwerk/ipc/DocumentChannel.cpp
@@ -0,0 +1,484 @@
+/* -*- 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 "mozilla/net/DocumentChannel.h"
+
+#include <inttypes.h>
+#include <utility>
+#include "mozIDOMWindow.h"
+#include "mozilla/AlreadyAddRefed.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/LoadInfo.h"
+#include "mozilla/Logging.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/TimeStamp.h"
+#include "mozilla/Unused.h"
+#include "mozilla/dom/Document.h"
+#include "mozilla/net/DocumentChannelChild.h"
+#include "mozilla/net/ParentProcessDocumentChannel.h"
+#include "nsCOMPtr.h"
+#include "nsDebug.h"
+#include "nsDocShell.h"
+#include "nsDocShellLoadState.h"
+#include "nsHttpHandler.h"
+#include "nsIContentPolicy.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsILoadContext.h"
+#include "nsILoadGroup.h"
+#include "nsILoadInfo.h"
+#include "nsIStreamListener.h"
+#include "nsIURI.h"
+#include "nsMimeTypes.h"
+#include "nsNetUtil.h"
+#include "nsPIDOMWindow.h"
+#include "nsPIDOMWindowInlines.h"
+#include "nsStringFwd.h"
+#include "nsThreadUtils.h"
+#include "nsXULAppAPI.h"
+#include "nscore.h"
+
+using namespace mozilla::dom;
+using namespace mozilla::ipc;
+
+extern mozilla::LazyLogModule gDocumentChannelLog;
+#define LOG(fmt) MOZ_LOG(gDocumentChannelLog, mozilla::LogLevel::Verbose, fmt)
+
+namespace mozilla {
+namespace net {
+
+//-----------------------------------------------------------------------------
+// DocumentChannel::nsISupports
+
+NS_IMPL_ADDREF(DocumentChannel)
+NS_IMPL_RELEASE(DocumentChannel)
+
+NS_INTERFACE_MAP_BEGIN(DocumentChannel)
+ NS_INTERFACE_MAP_ENTRY(nsIRequest)
+ NS_INTERFACE_MAP_ENTRY(nsIChannel)
+ NS_INTERFACE_MAP_ENTRY(nsIIdentChannel)
+ NS_INTERFACE_MAP_ENTRY_CONCRETE(DocumentChannel)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIRequest)
+NS_INTERFACE_MAP_END
+
+DocumentChannel::DocumentChannel(nsDocShellLoadState* aLoadState,
+ net::LoadInfo* aLoadInfo,
+ nsLoadFlags aLoadFlags, uint32_t aCacheKey,
+ bool aUriModified,
+ bool aIsEmbeddingBlockedError)
+ : mLoadState(aLoadState),
+ mCacheKey(aCacheKey),
+ mLoadFlags(aLoadFlags),
+ mURI(aLoadState->URI()),
+ mLoadInfo(aLoadInfo),
+ mUriModified(aUriModified),
+ mIsEmbeddingBlockedError(aIsEmbeddingBlockedError) {
+ LOG(("DocumentChannel ctor [this=%p, uri=%s]", this,
+ aLoadState->URI()->GetSpecOrDefault().get()));
+ RefPtr<nsHttpHandler> handler = nsHttpHandler::GetInstance();
+ uint64_t channelId;
+ Unused << handler->NewChannelId(channelId);
+ mChannelId = channelId;
+}
+
+NS_IMETHODIMP
+DocumentChannel::AsyncOpen(nsIStreamListener* aListener) {
+ MOZ_CRASH("If we get here, something is broken");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+void DocumentChannel::ShutdownListeners(nsresult aStatusCode) {
+ LOG(("DocumentChannel ShutdownListeners [this=%p, status=%" PRIx32 "]", this,
+ static_cast<uint32_t>(aStatusCode)));
+ mStatus = aStatusCode;
+
+ nsCOMPtr<nsIStreamListener> listener = mListener;
+ if (listener) {
+ listener->OnStartRequest(this);
+ }
+
+ mIsPending = false;
+
+ listener = mListener; // it might have changed!
+ nsCOMPtr<nsILoadGroup> loadGroup = mLoadGroup;
+
+ mListener = nullptr;
+ mLoadGroup = nullptr;
+ mCallbacks = nullptr;
+
+ NS_DispatchToMainThread(NS_NewRunnableFunction(
+ "DocumentChannel::ShutdownListeners", [=, self = RefPtr{this}] {
+ if (listener) {
+ listener->OnStopRequest(self, aStatusCode);
+ }
+
+ if (loadGroup) {
+ loadGroup->RemoveRequest(self, nullptr, aStatusCode);
+ }
+ }));
+
+ DeleteIPDL();
+}
+
+void DocumentChannel::DisconnectChildListeners(
+ const nsresult& aStatus, const nsresult& aLoadGroupStatus) {
+ MOZ_ASSERT(NS_FAILED(aStatus));
+ mStatus = aLoadGroupStatus;
+ // Make sure we remove from the load group before
+ // setting mStatus, as existing tests expect the
+ // status to be successful when we disconnect.
+ if (mLoadGroup) {
+ mLoadGroup->RemoveRequest(this, nullptr, aStatus);
+ mLoadGroup = nullptr;
+ }
+
+ ShutdownListeners(aStatus);
+}
+
+nsDocShell* DocumentChannel::GetDocShell() {
+ nsCOMPtr<nsILoadContext> loadContext;
+ NS_QueryNotificationCallbacks(this, loadContext);
+ if (!loadContext) {
+ return nullptr;
+ }
+ nsCOMPtr<mozIDOMWindowProxy> domWindow;
+ loadContext->GetAssociatedWindow(getter_AddRefs(domWindow));
+ if (!domWindow) {
+ return nullptr;
+ }
+ auto* pDomWindow = nsPIDOMWindowOuter::From(domWindow);
+ nsIDocShell* docshell = pDomWindow->GetDocShell();
+ return nsDocShell::Cast(docshell);
+}
+
+static bool URIUsesDocChannel(nsIURI* aURI) {
+ if (SchemeIsJavascript(aURI)) {
+ return false;
+ }
+
+ nsCString spec = aURI->GetSpecOrDefault();
+ return !spec.EqualsLiteral("about:crashcontent");
+}
+
+bool DocumentChannel::CanUseDocumentChannel(nsIURI* aURI) {
+ // We want to use DocumentChannel if we're using a supported scheme.
+ return URIUsesDocChannel(aURI);
+}
+
+/* static */
+already_AddRefed<DocumentChannel> DocumentChannel::CreateForDocument(
+ nsDocShellLoadState* aLoadState, class LoadInfo* aLoadInfo,
+ nsLoadFlags aLoadFlags, nsIInterfaceRequestor* aNotificationCallbacks,
+ uint32_t aCacheKey, bool aUriModified, bool aIsEmbeddingBlockedError) {
+ RefPtr<DocumentChannel> channel;
+ if (XRE_IsContentProcess()) {
+ channel =
+ new DocumentChannelChild(aLoadState, aLoadInfo, aLoadFlags, aCacheKey,
+ aUriModified, aIsEmbeddingBlockedError);
+ } else {
+ channel = new ParentProcessDocumentChannel(
+ aLoadState, aLoadInfo, aLoadFlags, aCacheKey, aUriModified,
+ aIsEmbeddingBlockedError);
+ }
+ channel->SetNotificationCallbacks(aNotificationCallbacks);
+ return channel.forget();
+}
+
+/* static */
+already_AddRefed<DocumentChannel> DocumentChannel::CreateForObject(
+ nsDocShellLoadState* aLoadState, class LoadInfo* aLoadInfo,
+ nsLoadFlags aLoadFlags, nsIInterfaceRequestor* aNotificationCallbacks) {
+ return CreateForDocument(aLoadState, aLoadInfo, aLoadFlags,
+ aNotificationCallbacks, 0, false, false);
+}
+
+NS_IMETHODIMP DocumentChannel::SetCanceledReason(const nsACString& aReason) {
+ return SetCanceledReasonImpl(aReason);
+}
+
+NS_IMETHODIMP DocumentChannel::GetCanceledReason(nsACString& aReason) {
+ return GetCanceledReasonImpl(aReason);
+}
+
+NS_IMETHODIMP DocumentChannel::CancelWithReason(nsresult aStatus,
+ const nsACString& aReason) {
+ return CancelWithReasonImpl(aStatus, aReason);
+}
+
+NS_IMETHODIMP
+DocumentChannel::Cancel(nsresult aStatusCode) {
+ MOZ_CRASH("If we get here, something is broken");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+DocumentChannel::Suspend() {
+ MOZ_CRASH("If we get here, something is broken");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+DocumentChannel::Resume() {
+ MOZ_CRASH("If we get here, something is broken");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+//-----------------------------------------------------------------------------
+// Remainder of nsIRequest/nsIChannel.
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP DocumentChannel::GetNotificationCallbacks(
+ nsIInterfaceRequestor** aCallbacks) {
+ nsCOMPtr<nsIInterfaceRequestor> callbacks(mCallbacks);
+ callbacks.forget(aCallbacks);
+ return NS_OK;
+}
+
+NS_IMETHODIMP DocumentChannel::SetNotificationCallbacks(
+ nsIInterfaceRequestor* aNotificationCallbacks) {
+ mCallbacks = aNotificationCallbacks;
+ return NS_OK;
+}
+
+NS_IMETHODIMP DocumentChannel::GetLoadGroup(nsILoadGroup** aLoadGroup) {
+ nsCOMPtr<nsILoadGroup> loadGroup(mLoadGroup);
+ loadGroup.forget(aLoadGroup);
+ return NS_OK;
+}
+
+NS_IMETHODIMP DocumentChannel::SetLoadGroup(nsILoadGroup* aLoadGroup) {
+ mLoadGroup = aLoadGroup;
+ return NS_OK;
+}
+
+NS_IMETHODIMP DocumentChannel::GetStatus(nsresult* aStatus) {
+ *aStatus = mStatus;
+ return NS_OK;
+}
+
+NS_IMETHODIMP DocumentChannel::GetName(nsACString& aResult) {
+ if (!mURI) {
+ aResult.Truncate();
+ return NS_OK;
+ }
+ nsCString spec;
+ nsresult rv = mURI->GetSpec(spec);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ aResult.AssignLiteral("documentchannel:");
+ aResult.Append(spec);
+ return NS_OK;
+}
+
+NS_IMETHODIMP DocumentChannel::IsPending(bool* aResult) {
+ *aResult = mIsPending;
+ return NS_OK;
+}
+
+NS_IMETHODIMP DocumentChannel::GetLoadFlags(nsLoadFlags* aLoadFlags) {
+ *aLoadFlags = mLoadFlags;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+DocumentChannel::GetTRRMode(nsIRequest::TRRMode* aTRRMode) {
+ return GetTRRModeImpl(aTRRMode);
+}
+
+NS_IMETHODIMP
+DocumentChannel::SetTRRMode(nsIRequest::TRRMode aTRRMode) {
+ return SetTRRModeImpl(aTRRMode);
+}
+
+NS_IMETHODIMP DocumentChannel::SetLoadFlags(nsLoadFlags aLoadFlags) {
+ // Setting load flags for TYPE_OBJECT is OK, so long as the channel to parent
+ // isn't opened yet, or we're only setting the `LOAD_DOCUMENT_URI` flag.
+ auto contentPolicy = mLoadInfo->GetExternalContentPolicyType();
+ if (contentPolicy == ExtContentPolicy::TYPE_OBJECT) {
+ if (mWasOpened) {
+ MOZ_DIAGNOSTIC_ASSERT(
+ aLoadFlags == (mLoadFlags | nsIChannel::LOAD_DOCUMENT_URI),
+ "After the channel has been opened, can only set the "
+ "`LOAD_DOCUMENT_URI` flag.");
+ }
+ mLoadFlags = aLoadFlags;
+ return NS_OK;
+ }
+
+ MOZ_CRASH_UNSAFE_PRINTF(
+ "DocumentChannel::SetLoadFlags: Don't set flags after creation "
+ "(differing flags %x != %x)",
+ (mLoadFlags ^ aLoadFlags) & mLoadFlags,
+ (mLoadFlags ^ aLoadFlags) & aLoadFlags);
+}
+
+NS_IMETHODIMP DocumentChannel::GetOriginalURI(nsIURI** aOriginalURI) {
+ nsCOMPtr<nsIURI> originalURI =
+ mLoadState->OriginalURI() ? mLoadState->OriginalURI() : mLoadState->URI();
+ originalURI.forget(aOriginalURI);
+ return NS_OK;
+}
+
+NS_IMETHODIMP DocumentChannel::SetOriginalURI(nsIURI* aOriginalURI) {
+ MOZ_CRASH("If we get here, something is broken");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP DocumentChannel::GetURI(nsIURI** aURI) {
+ nsCOMPtr<nsIURI> uri(mURI);
+ uri.forget(aURI);
+ return NS_OK;
+}
+
+NS_IMETHODIMP DocumentChannel::GetOwner(nsISupports** aOwner) {
+ nsCOMPtr<nsISupports> owner(mOwner);
+ owner.forget(aOwner);
+ return NS_OK;
+}
+
+NS_IMETHODIMP DocumentChannel::SetOwner(nsISupports* aOwner) {
+ mOwner = aOwner;
+ return NS_OK;
+}
+
+NS_IMETHODIMP DocumentChannel::GetSecurityInfo(
+ nsITransportSecurityInfo** aSecurityInfo) {
+ *aSecurityInfo = nullptr;
+ return NS_OK;
+}
+
+NS_IMETHODIMP DocumentChannel::GetContentType(nsACString& aContentType) {
+ // We may be trying to load HTML object data, and have determined that we're
+ // going to be performing a document load. In that case, fake the "text/html"
+ // content type for nsObjectLoadingContent.
+ if ((mLoadFlags & nsIRequest::LOAD_HTML_OBJECT_DATA) &&
+ (mLoadFlags & nsIChannel::LOAD_DOCUMENT_URI)) {
+ aContentType = TEXT_HTML;
+ return NS_OK;
+ }
+
+ NS_ERROR("If we get here, something is broken");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP DocumentChannel::SetContentType(const nsACString& aContentType) {
+ MOZ_CRASH("If we get here, something is broken");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP DocumentChannel::GetContentCharset(nsACString& aContentCharset) {
+ MOZ_CRASH("If we get here, something is broken");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP DocumentChannel::SetContentCharset(
+ const nsACString& aContentCharset) {
+ MOZ_CRASH("If we get here, something is broken");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP DocumentChannel::GetContentLength(int64_t* aContentLength) {
+ MOZ_CRASH("If we get here, something is broken");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP DocumentChannel::SetContentLength(int64_t aContentLength) {
+ MOZ_CRASH("If we get here, something is broken");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP DocumentChannel::Open(nsIInputStream** aStream) {
+ MOZ_CRASH("If we get here, something is broken");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP DocumentChannel::GetContentDisposition(
+ uint32_t* aContentDisposition) {
+ MOZ_CRASH("If we get here, something is broken");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP DocumentChannel::SetContentDisposition(
+ uint32_t aContentDisposition) {
+ MOZ_CRASH("If we get here, something is broken");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP DocumentChannel::GetContentDispositionFilename(
+ nsAString& aContentDispositionFilename) {
+ MOZ_CRASH("If we get here, something will be broken");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP DocumentChannel::SetContentDispositionFilename(
+ const nsAString& aContentDispositionFilename) {
+ MOZ_CRASH("If we get here, something will be broken");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP DocumentChannel::GetContentDispositionHeader(
+ nsACString& aContentDispositionHeader) {
+ MOZ_CRASH("If we get here, something is broken");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP DocumentChannel::GetLoadInfo(nsILoadInfo** aLoadInfo) {
+ nsCOMPtr<nsILoadInfo> loadInfo(mLoadInfo);
+ loadInfo.forget(aLoadInfo);
+ return NS_OK;
+}
+
+NS_IMETHODIMP DocumentChannel::SetLoadInfo(nsILoadInfo* aLoadInfo) {
+ MOZ_CRASH("If we get here, something is broken");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP DocumentChannel::GetIsDocument(bool* aIsDocument) {
+ return NS_GetIsDocumentChannel(this, aIsDocument);
+}
+
+NS_IMETHODIMP DocumentChannel::GetCanceled(bool* aCanceled) {
+ *aCanceled = mCanceled;
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// nsIIdentChannel
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+DocumentChannel::GetChannelId(uint64_t* aChannelId) {
+ *aChannelId = mChannelId;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+DocumentChannel::SetChannelId(uint64_t aChannelId) {
+ mChannelId = aChannelId;
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// Helpers
+//-----------------------------------------------------------------------------
+
+uint64_t InnerWindowIDForExtantDoc(nsDocShell* docShell) {
+ if (!docShell) {
+ return 0;
+ }
+
+ Document* doc = docShell->GetExtantDocument();
+ if (!doc) {
+ return 0;
+ }
+
+ return doc->InnerWindowID();
+}
+
+} // namespace net
+} // namespace mozilla
+
+#undef LOG
diff --git a/netwerk/ipc/DocumentChannel.h b/netwerk/ipc/DocumentChannel.h
new file mode 100644
index 0000000000..f6a75d4f1b
--- /dev/null
+++ b/netwerk/ipc/DocumentChannel.h
@@ -0,0 +1,120 @@
+/* -*- 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_DocumentChannel_h
+#define mozilla_net_DocumentChannel_h
+
+#include "mozilla/dom/ClientInfo.h"
+#include "mozilla/net/NeckoChannelParams.h"
+#include "nsDOMNavigationTiming.h"
+#include "nsIChannel.h"
+#include "nsIChildChannel.h"
+
+class nsDocShell;
+
+#define DOCUMENT_CHANNEL_IID \
+ { \
+ 0x6977bc44, 0xb1db, 0x41b7, { \
+ 0xb5, 0xc5, 0xe2, 0x13, 0x68, 0x22, 0xc9, 0x8f \
+ } \
+ }
+
+namespace mozilla {
+namespace net {
+
+uint64_t InnerWindowIDForExtantDoc(nsDocShell* docShell);
+
+/**
+ * DocumentChannel is a protocol agnostic placeholder nsIChannel implementation
+ * that we use so that nsDocShell knows about a connecting load. It transfers
+ * all data into a DocumentLoadListener (running in the parent process), which
+ * will create the real channel for the connection, and decide which process to
+ * load the resulting document in. If the document is to be loaded in the
+ * current process, then we'll synthesize a redirect replacing this placeholder
+ * channel with the real one, otherwise the originating docshell will be removed
+ * during the process switch.
+ */
+class DocumentChannel : public nsIIdentChannel {
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIREQUEST
+ NS_DECL_NSICHANNEL
+ NS_DECL_NSIIDENTCHANNEL
+
+ NS_DECLARE_STATIC_IID_ACCESSOR(DOCUMENT_CHANNEL_IID)
+
+ void SetNavigationTiming(nsDOMNavigationTiming* aTiming) {
+ mTiming = aTiming;
+ }
+
+ void SetInitialClientInfo(const Maybe<dom::ClientInfo>& aInfo) {
+ mInitialClientInfo = aInfo;
+ }
+
+ void DisconnectChildListeners(const nsresult& aStatus,
+ const nsresult& aLoadGroupStatus);
+
+ /**
+ * Will create the appropriate document channel:
+ * Either a DocumentChannelChild if called from the content process or
+ * a ParentProcessDocumentChannel if called from the parent process.
+ * This operation is infallible.
+ */
+ static already_AddRefed<DocumentChannel> CreateForDocument(
+ nsDocShellLoadState* aLoadState, class LoadInfo* aLoadInfo,
+ nsLoadFlags aLoadFlags, nsIInterfaceRequestor* aNotificationCallbacks,
+ uint32_t aCacheKey, bool aUriModified, bool aIsEmbeddingBlockedError);
+ static already_AddRefed<DocumentChannel> CreateForObject(
+ nsDocShellLoadState* aLoadState, class LoadInfo* aLoadInfo,
+ nsLoadFlags aLoadFlags, nsIInterfaceRequestor* aNotificationCallbacks);
+
+ static bool CanUseDocumentChannel(nsIURI* aURI);
+
+ protected:
+ DocumentChannel(nsDocShellLoadState* aLoadState, class LoadInfo* aLoadInfo,
+ nsLoadFlags aLoadFlags, uint32_t aCacheKey, bool aUriModified,
+ bool aIsEmbeddingBlockedError);
+
+ void ShutdownListeners(nsresult aStatusCode);
+ virtual void DeleteIPDL() {}
+
+ nsDocShell* GetDocShell();
+
+ virtual ~DocumentChannel() = default;
+
+ const RefPtr<nsDocShellLoadState> mLoadState;
+ const uint32_t mCacheKey;
+
+ nsresult mStatus = NS_OK;
+ bool mCanceled = false;
+ bool mIsPending = false;
+ bool mWasOpened = false;
+ uint64_t mChannelId;
+ uint32_t mLoadFlags = LOAD_NORMAL;
+ const nsCOMPtr<nsIURI> mURI;
+ nsCOMPtr<nsILoadGroup> mLoadGroup;
+ nsCOMPtr<nsILoadInfo> mLoadInfo;
+ nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
+ nsCOMPtr<nsIStreamListener> mListener;
+ nsCOMPtr<nsISupports> mOwner;
+ RefPtr<nsDOMNavigationTiming> mTiming;
+ Maybe<dom::ClientInfo> mInitialClientInfo;
+ // mUriModified is true if we're doing a history load and the URI of the
+ // session history had been modified by pushState/replaceState.
+ bool mUriModified = false;
+ // mIsEmbeddingBlockedError is true if we're handling a load error and the
+ // status of the failed channel is NS_ERROR_XFO_VIOLATION or
+ // NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION.
+ bool mIsEmbeddingBlockedError = false;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(DocumentChannel, DOCUMENT_CHANNEL_IID)
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozilla_net_DocumentChannel_h
diff --git a/netwerk/ipc/DocumentChannelChild.cpp b/netwerk/ipc/DocumentChannelChild.cpp
new file mode 100644
index 0000000000..ead243eaa2
--- /dev/null
+++ b/netwerk/ipc/DocumentChannelChild.cpp
@@ -0,0 +1,483 @@
+/* -*- 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 "DocumentChannelChild.h"
+
+#include "mozilla/dom/Document.h"
+#include "mozilla/dom/RemoteType.h"
+#include "mozilla/extensions/StreamFilterParent.h"
+#include "mozilla/ipc/Endpoint.h"
+#include "mozilla/net/HttpBaseChannel.h"
+#include "mozilla/net/NeckoChild.h"
+#include "mozilla/ScopeExit.h"
+#include "mozilla/StaticPrefs_fission.h"
+#include "nsHashPropertyBag.h"
+#include "nsIHttpChannelInternal.h"
+#include "nsIObjectLoadingContent.h"
+#include "nsIXULRuntime.h"
+#include "nsIWritablePropertyBag.h"
+#include "nsFrameLoader.h"
+#include "nsFrameLoaderOwner.h"
+#include "nsQueryObject.h"
+#include "nsDocShellLoadState.h"
+
+using namespace mozilla::dom;
+using namespace mozilla::ipc;
+
+extern mozilla::LazyLogModule gDocumentChannelLog;
+#define LOG(fmt) MOZ_LOG(gDocumentChannelLog, mozilla::LogLevel::Verbose, fmt)
+
+namespace mozilla {
+namespace net {
+
+//-----------------------------------------------------------------------------
+// DocumentChannelChild::nsISupports
+
+NS_INTERFACE_MAP_BEGIN(DocumentChannelChild)
+ NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectCallback)
+NS_INTERFACE_MAP_END_INHERITING(DocumentChannel)
+
+NS_IMPL_ADDREF_INHERITED(DocumentChannelChild, DocumentChannel)
+NS_IMPL_RELEASE_INHERITED(DocumentChannelChild, DocumentChannel)
+
+DocumentChannelChild::DocumentChannelChild(nsDocShellLoadState* aLoadState,
+ net::LoadInfo* aLoadInfo,
+ nsLoadFlags aLoadFlags,
+ uint32_t aCacheKey,
+ bool aUriModified,
+ bool aIsEmbeddingBlockedError)
+ : DocumentChannel(aLoadState, aLoadInfo, aLoadFlags, aCacheKey,
+ aUriModified, aIsEmbeddingBlockedError) {
+ mLoadingContext = nullptr;
+ LOG(("DocumentChannelChild ctor [this=%p, uri=%s]", this,
+ aLoadState->URI()->GetSpecOrDefault().get()));
+}
+
+DocumentChannelChild::~DocumentChannelChild() {
+ LOG(("DocumentChannelChild dtor [this=%p]", this));
+}
+
+NS_IMETHODIMP
+DocumentChannelChild::AsyncOpen(nsIStreamListener* aListener) {
+ nsresult rv = NS_OK;
+
+ nsCOMPtr<nsIStreamListener> listener = aListener;
+
+ NS_ENSURE_TRUE(gNeckoChild, NS_ERROR_FAILURE);
+ NS_ENSURE_ARG_POINTER(listener);
+ NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
+ NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);
+
+ // Port checked in parent, but duplicate here so we can return with error
+ // immediately, as we've done since before e10s.
+ rv = NS_CheckPortSafety(mURI);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool isNotDownload = mLoadState->FileName().IsVoid();
+
+ // If not a download, add ourselves to the load group
+ if (isNotDownload && mLoadGroup) {
+ // During this call, we can re-enter back into the DocumentChannelChild to
+ // call SetNavigationTiming.
+ mLoadGroup->AddRequest(this, nullptr);
+ }
+
+ if (mCanceled) {
+ // We may have been canceled already, either by on-modify-request
+ // listeners or by load group observers; in that case, don't create IPDL
+ // connection. See nsHttpChannel::AsyncOpen().
+ return mStatus;
+ }
+
+ gHttpHandler->OnOpeningDocumentRequest(this);
+
+ RefPtr<nsDocShell> docShell = GetDocShell();
+ if (!docShell) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // `loadingContext` is the BC that is initiating the resource load.
+ // For normal subdocument loads, the BC is the one that the subdoc will load
+ // into. For <object>/<embed> it's the embedder doc's BC.
+ RefPtr<BrowsingContext> loadingContext = docShell->GetBrowsingContext();
+ if (!loadingContext || loadingContext->IsDiscarded()) {
+ return NS_ERROR_FAILURE;
+ }
+ mLoadingContext = loadingContext;
+
+ Maybe<IPCClientInfo> ipcClientInfo;
+ if (mInitialClientInfo.isSome()) {
+ ipcClientInfo.emplace(mInitialClientInfo.ref().ToIPC());
+ }
+
+ DocumentChannelElementCreationArgs ipcElementCreationArgs;
+ switch (mLoadInfo->GetExternalContentPolicyType()) {
+ case ExtContentPolicy::TYPE_DOCUMENT:
+ case ExtContentPolicy::TYPE_SUBDOCUMENT: {
+ DocumentCreationArgs docArgs;
+ docArgs.uriModified() = mUriModified;
+ docArgs.isEmbeddingBlockedError() = mIsEmbeddingBlockedError;
+
+ ipcElementCreationArgs = docArgs;
+ break;
+ }
+
+ case ExtContentPolicy::TYPE_OBJECT: {
+ ObjectCreationArgs objectArgs;
+ objectArgs.embedderInnerWindowId() = InnerWindowIDForExtantDoc(docShell);
+ objectArgs.loadFlags() = mLoadFlags;
+ objectArgs.contentPolicyType() = mLoadInfo->InternalContentPolicyType();
+ objectArgs.isUrgentStart() = UserActivation::IsHandlingUserInput();
+
+ ipcElementCreationArgs = objectArgs;
+ break;
+ }
+
+ default:
+ MOZ_ASSERT_UNREACHABLE("unsupported content policy type");
+ return NS_ERROR_FAILURE;
+ }
+
+ switch (mLoadInfo->GetExternalContentPolicyType()) {
+ case ExtContentPolicy::TYPE_DOCUMENT:
+ case ExtContentPolicy::TYPE_SUBDOCUMENT:
+ MOZ_ALWAYS_SUCCEEDS(loadingContext->SetCurrentLoadIdentifier(
+ Some(mLoadState->GetLoadIdentifier())));
+ break;
+
+ default:
+ break;
+ }
+
+ mLoadState->AssertProcessCouldTriggerLoadIfSystem();
+
+ DocumentChannelCreationArgs args(
+ mozilla::WrapNotNull(mLoadState), TimeStamp::Now(), mChannelId, mCacheKey,
+ mTiming, ipcClientInfo, ipcElementCreationArgs,
+ loadingContext->GetParentInitiatedNavigationEpoch());
+
+ gNeckoChild->SendPDocumentChannelConstructor(this, loadingContext, args);
+
+ mIsPending = true;
+ mWasOpened = true;
+ mListener = listener;
+
+ return NS_OK;
+}
+
+IPCResult DocumentChannelChild::RecvFailedAsyncOpen(
+ const nsresult& aStatusCode) {
+ if (aStatusCode == NS_ERROR_RECURSIVE_DOCUMENT_LOAD) {
+ // This exists so that we are able to fire an error event
+ // for when there are too many recursive iframe or object loads.
+ // This is an incomplete solution, because right now we don't have a unified
+ // way of firing error events due to errors in document channel.
+ // This should be fixed in bug 1629201.
+ MOZ_DIAGNOSTIC_ASSERT(mLoadingContext);
+ if (RefPtr<Element> embedder = mLoadingContext->GetEmbedderElement()) {
+ if (RefPtr<nsFrameLoaderOwner> flo = do_QueryObject(embedder)) {
+ if (RefPtr<nsFrameLoader> fl = flo->GetFrameLoader()) {
+ fl->FireErrorEvent();
+ }
+ }
+ }
+ }
+ ShutdownListeners(aStatusCode);
+ return IPC_OK();
+}
+
+IPCResult DocumentChannelChild::RecvDisconnectChildListeners(
+ const nsresult& aStatus, const nsresult& aLoadGroupStatus,
+ bool aContinueNavigating) {
+ // If this disconnect is not due to a process switch, perform the disconnect
+ // immediately.
+ if (!aContinueNavigating) {
+ DisconnectChildListeners(aStatus, aLoadGroupStatus);
+ return IPC_OK();
+ }
+
+ // Otherwise, the disconnect will occur later using some other mechanism,
+ // depending on what's happening to the loading DocShell. If this is a
+ // toplevel navigation, and this BrowsingContext enters the BFCache, we will
+ // cancel this channel when the PageHide event is firing, whereas if it does
+ // not enter BFCache (e.g. due to being an object, subframe or non-bfcached
+ // toplevel navigation), we will cancel this channel when the DocShell is
+ // destroyed.
+ nsDocShell* shell = GetDocShell();
+ if (mLoadInfo->GetExternalContentPolicyType() ==
+ ExtContentPolicy::TYPE_DOCUMENT &&
+ shell) {
+ MOZ_ASSERT(shell->GetBrowsingContext()->IsTop());
+ if (mozilla::SessionHistoryInParent() &&
+ shell->GetBrowsingContext()->IsInBFCache()) {
+ DisconnectChildListeners(aStatus, aLoadGroupStatus);
+ } else {
+ // Tell the DocShell which channel to cancel if it enters the BFCache.
+ shell->SetChannelToDisconnectOnPageHide(mChannelId);
+ }
+ }
+
+ return IPC_OK();
+}
+
+IPCResult DocumentChannelChild::RecvRedirectToRealChannel(
+ RedirectToRealChannelArgs&& aArgs,
+ nsTArray<Endpoint<extensions::PStreamFilterParent>>&& aEndpoints,
+ RedirectToRealChannelResolver&& aResolve) {
+ LOG(("DocumentChannelChild RecvRedirectToRealChannel [this=%p, uri=%s]", this,
+ aArgs.uri()->GetSpecOrDefault().get()));
+
+ // The document that created the cspToInherit.
+ // This is used when deserializing LoadInfo from the parent
+ // process, since we can't serialize Documents directly.
+ // TODO: For a fission OOP iframe this will be unavailable,
+ // as will the loadingContext computed in LoadInfoArgsToLoadInfo.
+ // Figure out if we need these for cross-origin subdocs.
+ RefPtr<dom::Document> cspToInheritLoadingDocument;
+ nsCOMPtr<nsIContentSecurityPolicy> policy = mLoadState->Csp();
+ if (policy) {
+ nsWeakPtr ctx =
+ static_cast<nsCSPContext*>(policy.get())->GetLoadingContext();
+ cspToInheritLoadingDocument = do_QueryReferent(ctx);
+ }
+ nsCOMPtr<nsILoadInfo> loadInfo;
+ MOZ_ALWAYS_SUCCEEDS(LoadInfoArgsToLoadInfo(aArgs.loadInfo(), NOT_REMOTE_TYPE,
+ cspToInheritLoadingDocument,
+ getter_AddRefs(loadInfo)));
+
+ mRedirectResolver = std::move(aResolve);
+
+ nsCOMPtr<nsIChannel> newChannel;
+ MOZ_ASSERT((aArgs.loadStateInternalLoadFlags() &
+ nsDocShell::InternalLoad::INTERNAL_LOAD_FLAGS_IS_SRCDOC) ||
+ aArgs.srcdocData().IsVoid());
+ nsresult rv = nsDocShell::CreateRealChannelForDocument(
+ getter_AddRefs(newChannel), aArgs.uri(), loadInfo, nullptr,
+ aArgs.newLoadFlags(), aArgs.srcdocData(), aArgs.baseUri());
+ if (newChannel) {
+ newChannel->SetLoadGroup(mLoadGroup);
+ }
+
+ if (RefPtr<HttpBaseChannel> httpChannel = do_QueryObject(newChannel)) {
+ httpChannel->SetEarlyHints(std::move(aArgs.earlyHints()));
+ httpChannel->SetEarlyHintLinkType(aArgs.earlyHintLinkType());
+ }
+
+ // This is used to report any errors back to the parent by calling
+ // CrossProcessRedirectFinished.
+ auto scopeExit = MakeScopeExit([&]() {
+ mRedirectResolver(rv);
+ mRedirectResolver = nullptr;
+ });
+
+ if (NS_FAILED(rv)) {
+ return IPC_OK();
+ }
+
+ if (nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(newChannel)) {
+ rv = httpChannel->SetChannelId(aArgs.channelId());
+ }
+ if (NS_FAILED(rv)) {
+ return IPC_OK();
+ }
+
+ rv = newChannel->SetOriginalURI(aArgs.originalURI());
+ if (NS_FAILED(rv)) {
+ return IPC_OK();
+ }
+
+ if (nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal =
+ do_QueryInterface(newChannel)) {
+ rv = httpChannelInternal->SetRedirectMode(aArgs.redirectMode());
+ }
+ if (NS_FAILED(rv)) {
+ return IPC_OK();
+ }
+
+ newChannel->SetNotificationCallbacks(mCallbacks);
+
+ if (aArgs.init()) {
+ HttpBaseChannel::ReplacementChannelConfig config(*aArgs.init());
+ HttpBaseChannel::ConfigureReplacementChannel(
+ newChannel, config,
+ HttpBaseChannel::ReplacementReason::DocumentChannel);
+ }
+
+ if (aArgs.contentDisposition()) {
+ newChannel->SetContentDisposition(*aArgs.contentDisposition());
+ }
+
+ if (aArgs.contentDispositionFilename()) {
+ newChannel->SetContentDispositionFilename(
+ *aArgs.contentDispositionFilename());
+ }
+
+ nsDocShell* docShell = GetDocShell();
+ if (docShell && aArgs.loadingSessionHistoryInfo().isSome()) {
+ docShell->SetLoadingSessionHistoryInfo(
+ aArgs.loadingSessionHistoryInfo().ref());
+ }
+
+ // transfer any properties. This appears to be entirely a content-side
+ // interface and isn't copied across to the parent. Copying the values
+ // for this from this into the new actor will work, since the parent
+ // won't have the right details anyway.
+ // TODO: What about the process switch equivalent
+ // (ContentChild::RecvCrossProcessRedirect)? In that case there is no local
+ // existing actor in the destination process... We really need all information
+ // to go up to the parent, and then come down to the new child actor.
+ if (nsCOMPtr<nsIWritablePropertyBag> bag = do_QueryInterface(newChannel)) {
+ nsHashPropertyBag::CopyFrom(bag, aArgs.properties());
+ }
+
+ // connect parent.
+ nsCOMPtr<nsIChildChannel> childChannel = do_QueryInterface(newChannel);
+ if (childChannel) {
+ rv = childChannel->ConnectParent(
+ aArgs.registrarId()); // creates parent channel
+ if (NS_FAILED(rv)) {
+ return IPC_OK();
+ }
+ }
+ mRedirectChannel = newChannel;
+ mStreamFilterEndpoints = std::move(aEndpoints);
+
+ rv = gHttpHandler->AsyncOnChannelRedirect(this, newChannel,
+ aArgs.redirectFlags(),
+ GetMainThreadSerialEventTarget());
+
+ if (NS_SUCCEEDED(rv)) {
+ scopeExit.release();
+ }
+
+ // scopeExit will call CrossProcessRedirectFinished(rv) here
+ return IPC_OK();
+}
+
+IPCResult DocumentChannelChild::RecvUpgradeObjectLoad(
+ UpgradeObjectLoadResolver&& aResolve) {
+ // We're doing a load for an <object> or <embed> element if we got here.
+ MOZ_ASSERT(mLoadFlags & nsIRequest::LOAD_HTML_OBJECT_DATA,
+ "Should have LOAD_HTML_OBJECT_DATA set");
+ MOZ_ASSERT(!(mLoadFlags & nsIChannel::LOAD_DOCUMENT_URI),
+ "Shouldn't be a LOAD_DOCUMENT_URI load yet");
+ MOZ_ASSERT(mLoadInfo->GetExternalContentPolicyType() ==
+ ExtContentPolicy::TYPE_OBJECT,
+ "Should have the TYPE_OBJECT content policy type");
+
+ // If our load has already failed, or been cancelled, abort this attempt to
+ // upgade the load.
+ if (NS_FAILED(mStatus)) {
+ aResolve(nullptr);
+ return IPC_OK();
+ }
+
+ nsCOMPtr<nsIObjectLoadingContent> loadingContent;
+ NS_QueryNotificationCallbacks(this, loadingContent);
+ if (!loadingContent) {
+ return IPC_FAIL(this, "Channel is not for ObjectLoadingContent!");
+ }
+
+ // We're upgrading to a document channel now. Add the LOAD_DOCUMENT_URI flag
+ // after-the-fact.
+ mLoadFlags |= nsIChannel::LOAD_DOCUMENT_URI;
+
+ RefPtr<BrowsingContext> browsingContext;
+ nsresult rv = loadingContent->UpgradeLoadToDocument(
+ this, getter_AddRefs(browsingContext));
+ if (NS_FAILED(rv) || !browsingContext) {
+ // Oops! Looks like something went wrong, so let's bail out.
+ mLoadFlags &= ~nsIChannel::LOAD_DOCUMENT_URI;
+ aResolve(nullptr);
+ return IPC_OK();
+ }
+
+ aResolve(browsingContext);
+ return IPC_OK();
+}
+
+NS_IMETHODIMP
+DocumentChannelChild::OnRedirectVerifyCallback(nsresult aStatusCode) {
+ LOG(
+ ("DocumentChannelChild OnRedirectVerifyCallback [this=%p, "
+ "aRv=0x%08" PRIx32 " ]",
+ this, static_cast<uint32_t>(aStatusCode)));
+ nsCOMPtr<nsIChannel> redirectChannel = std::move(mRedirectChannel);
+ RedirectToRealChannelResolver redirectResolver = std::move(mRedirectResolver);
+
+ // If we've already shut down, then just notify the parent that
+ // we're done.
+ if (NS_FAILED(mStatus)) {
+ redirectChannel->SetNotificationCallbacks(nullptr);
+ redirectResolver(aStatusCode);
+ return NS_OK;
+ }
+
+ nsresult rv = aStatusCode;
+ if (NS_SUCCEEDED(rv)) {
+ if (nsCOMPtr<nsIChildChannel> childChannel =
+ do_QueryInterface(redirectChannel)) {
+ rv = childChannel->CompleteRedirectSetup(mListener);
+ } else {
+ rv = redirectChannel->AsyncOpen(mListener);
+ }
+ } else {
+ redirectChannel->SetNotificationCallbacks(nullptr);
+ }
+
+ for (auto& endpoint : mStreamFilterEndpoints) {
+ extensions::StreamFilterParent::Attach(redirectChannel,
+ std::move(endpoint));
+ }
+
+ redirectResolver(rv);
+
+ if (NS_FAILED(rv)) {
+ ShutdownListeners(rv);
+ return NS_OK;
+ }
+
+ if (mLoadGroup) {
+ mLoadGroup->RemoveRequest(this, nullptr, NS_BINDING_REDIRECTED);
+ }
+ mCallbacks = nullptr;
+ mListener = nullptr;
+
+ // This calls NeckoChild::DeallocPDocumentChannel(), which deletes |this| if
+ // IPDL holds the last reference. Don't rely on |this| existing after here!
+ if (CanSend()) {
+ Send__delete__(this);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+DocumentChannelChild::Cancel(nsresult aStatusCode) {
+ return CancelWithReason(aStatusCode, "DocumentChannelChild::Cancel"_ns);
+}
+
+NS_IMETHODIMP DocumentChannelChild::CancelWithReason(
+ nsresult aStatusCode, const nsACString& aReason) {
+ if (mCanceled) {
+ return NS_OK;
+ }
+
+ mCanceled = true;
+ if (CanSend()) {
+ SendCancel(aStatusCode, aReason);
+ }
+
+ ShutdownListeners(aStatusCode);
+
+ return NS_OK;
+}
+
+} // namespace net
+} // namespace mozilla
+
+#undef LOG
diff --git a/netwerk/ipc/DocumentChannelChild.h b/netwerk/ipc/DocumentChannelChild.h
new file mode 100644
index 0000000000..7b2461cbb6
--- /dev/null
+++ b/netwerk/ipc/DocumentChannelChild.h
@@ -0,0 +1,76 @@
+/* -*- 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_DocumentChannelChild_h
+#define mozilla_net_DocumentChannelChild_h
+
+#include "mozilla/net/PDocumentChannelChild.h"
+#include "mozilla/net/DocumentChannel.h"
+#include "nsIAsyncVerifyRedirectCallback.h"
+#include "mozilla/dom/nsCSPContext.h"
+
+namespace mozilla {
+namespace net {
+
+/**
+ * DocumentChannelChild is an implementation of DocumentChannel for nsDocShells
+ * in the content process, that uses PDocumentChannel to serialize everything
+ * across IPDL to the parent process.
+ */
+class DocumentChannelChild final : public DocumentChannel,
+ public nsIAsyncVerifyRedirectCallback,
+ public PDocumentChannelChild {
+ public:
+ DocumentChannelChild(nsDocShellLoadState* aLoadState,
+ class LoadInfo* aLoadInfo, nsLoadFlags aLoadFlags,
+ uint32_t aCacheKey, bool aUriModified,
+ bool aIsEmbeddingBlockedError);
+
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_NSIASYNCVERIFYREDIRECTCALLBACK
+
+ NS_IMETHOD AsyncOpen(nsIStreamListener* aListener) override;
+ NS_IMETHOD Cancel(nsresult aStatusCode) override;
+ NS_IMETHOD CancelWithReason(nsresult aStatusCode,
+ const nsACString& aReason) override;
+
+ mozilla::ipc::IPCResult RecvFailedAsyncOpen(const nsresult& aStatusCode);
+
+ mozilla::ipc::IPCResult RecvDisconnectChildListeners(
+ const nsresult& aStatus, const nsresult& aLoadGroupStatus,
+ bool aSwitchedProcess);
+
+ mozilla::ipc::IPCResult RecvDeleteSelf();
+
+ mozilla::ipc::IPCResult RecvRedirectToRealChannel(
+ RedirectToRealChannelArgs&& aArgs,
+ nsTArray<Endpoint<extensions::PStreamFilterParent>>&& aEndpoints,
+ RedirectToRealChannelResolver&& aResolve);
+
+ mozilla::ipc::IPCResult RecvUpgradeObjectLoad(
+ UpgradeObjectLoadResolver&& aResolve);
+
+ private:
+ void DeleteIPDL() override {
+ if (CanSend()) {
+ Send__delete__(this);
+ }
+ }
+
+ ~DocumentChannelChild();
+
+ nsCOMPtr<nsIChannel> mRedirectChannel;
+
+ RedirectToRealChannelResolver mRedirectResolver;
+ nsTArray<Endpoint<extensions::PStreamFilterParent>> mStreamFilterEndpoints;
+ dom::BrowsingContext* mLoadingContext;
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozilla_net_DocumentChannelChild_h
diff --git a/netwerk/ipc/DocumentChannelParent.cpp b/netwerk/ipc/DocumentChannelParent.cpp
new file mode 100644
index 0000000000..a360f76bcb
--- /dev/null
+++ b/netwerk/ipc/DocumentChannelParent.cpp
@@ -0,0 +1,160 @@
+/* -*- 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 "DocumentChannelParent.h"
+
+#include "mozilla/dom/BrowserParent.h"
+#include "mozilla/dom/CanonicalBrowsingContext.h"
+#include "mozilla/dom/ClientInfo.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/net/NeckoChannelParams.h"
+#include "nsDocShellLoadState.h"
+
+extern mozilla::LazyLogModule gDocumentChannelLog;
+#define LOG(fmt) MOZ_LOG(gDocumentChannelLog, mozilla::LogLevel::Verbose, fmt)
+
+using namespace mozilla::dom;
+
+namespace mozilla {
+namespace net {
+
+DocumentChannelParent::DocumentChannelParent() {
+ LOG(("DocumentChannelParent ctor [this=%p]", this));
+}
+
+DocumentChannelParent::~DocumentChannelParent() {
+ LOG(("DocumentChannelParent dtor [this=%p]", this));
+}
+
+bool DocumentChannelParent::Init(dom::CanonicalBrowsingContext* aContext,
+ const DocumentChannelCreationArgs& aArgs) {
+ RefPtr<nsDocShellLoadState> loadState = aArgs.loadState();
+ LOG(("DocumentChannelParent Init [this=%p, uri=%s]", this,
+ loadState->URI()->GetSpecOrDefault().get()));
+ if (aArgs.parentInitiatedNavigationEpoch() <
+ aContext->GetParentInitiatedNavigationEpoch()) {
+ nsresult rv = NS_BINDING_ABORTED;
+ return SendFailedAsyncOpen(rv);
+ }
+
+ ContentParent* contentParent =
+ static_cast<ContentParent*>(Manager()->Manager());
+
+ RefPtr<DocumentLoadListener::OpenPromise> promise;
+ if (loadState->GetChannelInitialized()) {
+ promise = DocumentLoadListener::ClaimParentLoad(
+ getter_AddRefs(mDocumentLoadListener), loadState->GetLoadIdentifier(),
+ Some(aArgs.channelId()));
+ }
+ if (!promise) {
+ bool isDocumentLoad =
+ aArgs.elementCreationArgs().type() ==
+ DocumentChannelElementCreationArgs::TDocumentCreationArgs;
+ mDocumentLoadListener = new DocumentLoadListener(aContext, isDocumentLoad);
+
+ Maybe<ClientInfo> clientInfo;
+ if (aArgs.initialClientInfo().isSome()) {
+ clientInfo.emplace(ClientInfo(aArgs.initialClientInfo().ref()));
+ }
+
+ nsresult rv = NS_ERROR_UNEXPECTED;
+
+ if (isDocumentLoad) {
+ const DocumentCreationArgs& docArgs = aArgs.elementCreationArgs();
+
+ promise = mDocumentLoadListener->OpenDocument(
+ loadState, aArgs.cacheKey(), Some(aArgs.channelId()),
+ aArgs.asyncOpenTime(), aArgs.timing(), std::move(clientInfo),
+ Some(docArgs.uriModified()), Some(docArgs.isEmbeddingBlockedError()),
+ contentParent, &rv);
+ } else {
+ const ObjectCreationArgs& objectArgs = aArgs.elementCreationArgs();
+
+ promise = mDocumentLoadListener->OpenObject(
+ loadState, aArgs.cacheKey(), Some(aArgs.channelId()),
+ aArgs.asyncOpenTime(), aArgs.timing(), std::move(clientInfo),
+ objectArgs.embedderInnerWindowId(), objectArgs.loadFlags(),
+ objectArgs.contentPolicyType(), objectArgs.isUrgentStart(),
+ contentParent, this /* ObjectUpgradeHandler */, &rv);
+ }
+
+ if (NS_FAILED(rv)) {
+ MOZ_ASSERT(!promise);
+ return SendFailedAsyncOpen(rv);
+ }
+ }
+
+ RefPtr<DocumentChannelParent> self = this;
+ promise->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [self](DocumentLoadListener::OpenPromiseSucceededType&& aResolveValue) {
+ // The DLL is waiting for us to resolve the
+ // PDocumentChannel::RedirectToRealChannelPromise given as parameter.
+ auto promise = self->RedirectToRealChannel(
+ std::move(aResolveValue.mStreamFilterEndpoints),
+ aResolveValue.mRedirectFlags, aResolveValue.mLoadFlags,
+ aResolveValue.mEarlyHintLinkType);
+ // We chain the promise the DLL is waiting on to the one returned by
+ // RedirectToRealChannel. As soon as the promise returned is resolved
+ // or rejected, so will the DLL's promise.
+ promise->ChainTo(aResolveValue.mPromise.forget(), __func__);
+ self->mDocumentLoadListener = nullptr;
+ },
+ [self](DocumentLoadListener::OpenPromiseFailedType&& aRejectValue) {
+ if (self->CanSend()) {
+ Unused << self->SendDisconnectChildListeners(
+ aRejectValue.mStatus, aRejectValue.mLoadGroupStatus,
+ aRejectValue.mContinueNavigating);
+ }
+ self->mDocumentLoadListener = nullptr;
+ });
+
+ return true;
+}
+
+auto DocumentChannelParent::UpgradeObjectLoad()
+ -> RefPtr<ObjectUpgradePromise> {
+ return SendUpgradeObjectLoad()->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [](const UpgradeObjectLoadPromise::ResolveOrRejectValue& aValue) {
+ if (!aValue.IsResolve() || aValue.ResolveValue().IsNullOrDiscarded()) {
+ LOG(("DocumentChannelParent object load upgrade failed"));
+ return ObjectUpgradePromise::CreateAndReject(NS_ERROR_FAILURE,
+ __func__);
+ }
+
+ return ObjectUpgradePromise::CreateAndResolve(
+ aValue.ResolveValue().get_canonical(), __func__);
+ });
+}
+
+RefPtr<PDocumentChannelParent::RedirectToRealChannelPromise>
+DocumentChannelParent::RedirectToRealChannel(
+ nsTArray<ipc::Endpoint<extensions::PStreamFilterParent>>&&
+ aStreamFilterEndpoints,
+ uint32_t aRedirectFlags, uint32_t aLoadFlags, uint32_t aEarlyHintLinkType) {
+ if (!CanSend()) {
+ return PDocumentChannelParent::RedirectToRealChannelPromise::
+ CreateAndReject(ResponseRejectReason::ChannelClosed, __func__);
+ }
+
+ ContentParent* cp = static_cast<ContentParent*>(Manager()->Manager());
+ nsTArray<EarlyHintConnectArgs> earlyHints;
+ mDocumentLoadListener->RegisterEarlyHintLinksAndGetConnectArgs(cp->ChildID(),
+ earlyHints);
+
+ RedirectToRealChannelArgs args;
+ mDocumentLoadListener->SerializeRedirectData(
+ args, false, aRedirectFlags, aLoadFlags, std::move(earlyHints),
+ aEarlyHintLinkType);
+ return SendRedirectToRealChannel(args, std::move(aStreamFilterEndpoints));
+}
+
+} // namespace net
+} // namespace mozilla
+
+#undef LOG
diff --git a/netwerk/ipc/DocumentChannelParent.h b/netwerk/ipc/DocumentChannelParent.h
new file mode 100644
index 0000000000..7537c95295
--- /dev/null
+++ b/netwerk/ipc/DocumentChannelParent.h
@@ -0,0 +1,68 @@
+/* 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_DocumentChannelParent_h
+#define mozilla_net_DocumentChannelParent_h
+
+#include "mozilla/net/DocumentLoadListener.h"
+#include "mozilla/net/PDocumentChannelParent.h"
+
+namespace mozilla {
+namespace dom {
+class CanonicalBrowsingContext;
+}
+namespace net {
+
+class EarlyHintConnectArgs;
+
+/**
+ * An actor that forwards all changes across to DocumentChannelChild, the
+ * nsIChannel implementation owned by a content process docshell.
+ */
+class DocumentChannelParent final
+ : public PDocumentChannelParent,
+ public DocumentLoadListener::ObjectUpgradeHandler {
+ public:
+ NS_INLINE_DECL_REFCOUNTING(DocumentChannelParent, override);
+
+ explicit DocumentChannelParent();
+
+ bool Init(dom::CanonicalBrowsingContext* aContext,
+ const DocumentChannelCreationArgs& aArgs);
+
+ // PDocumentChannelParent
+ ipc::IPCResult RecvCancel(const nsresult& aStatus, const nsCString& aReason) {
+ if (mDocumentLoadListener) {
+ mDocumentLoadListener->Cancel(aStatus, aReason);
+ }
+ return IPC_OK();
+ }
+ void ActorDestroy(ActorDestroyReason aWhy) override {
+ if (mDocumentLoadListener) {
+ mDocumentLoadListener->Cancel(NS_BINDING_ABORTED,
+ "DocumentChannelParent::ActorDestroy"_ns);
+ }
+ }
+
+ private:
+ RefPtr<ObjectUpgradePromise> UpgradeObjectLoad() override;
+
+ RefPtr<PDocumentChannelParent::RedirectToRealChannelPromise>
+ RedirectToRealChannel(
+ nsTArray<ipc::Endpoint<extensions::PStreamFilterParent>>&&
+ aStreamFilterEndpoints,
+ uint32_t aRedirectFlags, uint32_t aLoadFlags,
+ uint32_t aEarlyHintLinkType);
+
+ virtual ~DocumentChannelParent();
+
+ RefPtr<DocumentLoadListener> mDocumentLoadListener;
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozilla_net_DocumentChannelParent_h
diff --git a/netwerk/ipc/DocumentLoadListener.cpp b/netwerk/ipc/DocumentLoadListener.cpp
new file mode 100644
index 0000000000..ca1f59e884
--- /dev/null
+++ b/netwerk/ipc/DocumentLoadListener.cpp
@@ -0,0 +1,3027 @@
+/* -*- 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 "DocumentLoadListener.h"
+
+#include "NeckoCommon.h"
+#include "mozilla/AntiTrackingUtils.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/LoadInfo.h"
+#include "mozilla/NullPrincipal.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/ResultVariant.h"
+#include "mozilla/ScopeExit.h"
+#include "mozilla/StaticPrefs_extensions.h"
+#include "mozilla/StaticPrefs_fission.h"
+#include "mozilla/StaticPrefs_security.h"
+#include "mozilla/dom/BrowserParent.h"
+#include "mozilla/dom/BrowsingContextGroup.h"
+#include "mozilla/dom/CanonicalBrowsingContext.h"
+#include "mozilla/dom/ChildProcessChannelListener.h"
+#include "mozilla/dom/ClientChannelHelper.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/ContentProcessManager.h"
+#include "mozilla/dom/ProcessIsolation.h"
+#include "mozilla/dom/SessionHistoryEntry.h"
+#include "mozilla/dom/WindowGlobalParent.h"
+#include "mozilla/dom/ipc/IdType.h"
+#include "mozilla/net/CookieJarSettings.h"
+#include "mozilla/net/HttpChannelParent.h"
+#include "mozilla/net/RedirectChannelRegistrar.h"
+#include "nsContentSecurityUtils.h"
+#include "nsContentSecurityManager.h"
+#include "nsDocShell.h"
+#include "nsDocShellLoadState.h"
+#include "nsDocShellLoadTypes.h"
+#include "nsDOMNavigationTiming.h"
+#include "nsDSURIContentListener.h"
+#include "nsObjectLoadingContent.h"
+#include "nsOpenWindowInfo.h"
+#include "nsExternalHelperAppService.h"
+#include "nsHttpChannel.h"
+#include "nsIBrowser.h"
+#include "nsIHttpChannelInternal.h"
+#include "nsIStreamConverterService.h"
+#include "nsIViewSourceChannel.h"
+#include "nsImportModule.h"
+#include "nsIXULRuntime.h"
+#include "nsMimeTypes.h"
+#include "nsQueryObject.h"
+#include "nsRedirectHistoryEntry.h"
+#include "nsSandboxFlags.h"
+#include "nsSHistory.h"
+#include "nsStringStream.h"
+#include "nsURILoader.h"
+#include "nsWebNavigationInfo.h"
+#include "mozilla/dom/BrowserParent.h"
+#include "mozilla/dom/Element.h"
+#include "mozilla/dom/nsHTTPSOnlyUtils.h"
+#include "mozilla/dom/ReferrerInfo.h"
+#include "mozilla/dom/RemoteWebProgressRequest.h"
+#include "mozilla/net/UrlClassifierFeatureFactory.h"
+#include "mozilla/ExtensionPolicyService.h"
+
+#ifdef ANDROID
+# include "mozilla/widget/nsWindow.h"
+#endif /* ANDROID */
+
+mozilla::LazyLogModule gDocumentChannelLog("DocumentChannel");
+#define LOG(fmt) MOZ_LOG(gDocumentChannelLog, mozilla::LogLevel::Verbose, fmt)
+
+extern mozilla::LazyLogModule gSHIPBFCacheLog;
+
+// Bug 136580: Limit to the number of nested content frames that can have the
+// same URL. This is to stop content that is recursively loading
+// itself. Note that "#foo" on the end of URL doesn't affect
+// whether it's considered identical, but "?foo" or ";foo" are
+// considered and compared.
+// Limit this to 2, like chromium does.
+static constexpr int kMaxSameURLContentFrames = 2;
+
+using namespace mozilla::dom;
+
+namespace mozilla {
+namespace net {
+
+static ContentParentId GetContentProcessId(ContentParent* aContentParent) {
+ return aContentParent ? aContentParent->ChildID() : ContentParentId{0};
+}
+
+static void SetNeedToAddURIVisit(nsIChannel* aChannel,
+ bool aNeedToAddURIVisit) {
+ nsCOMPtr<nsIWritablePropertyBag2> props(do_QueryInterface(aChannel));
+ if (!props) {
+ return;
+ }
+
+ props->SetPropertyAsBool(u"docshell.needToAddURIVisit"_ns,
+ aNeedToAddURIVisit);
+}
+
+static auto SecurityFlagsForLoadInfo(nsDocShellLoadState* aLoadState)
+ -> nsSecurityFlags {
+ // TODO: Block copied from nsDocShell::DoURILoad, refactor out somewhere
+ nsSecurityFlags securityFlags =
+ nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL;
+
+ if (aLoadState->LoadType() == LOAD_ERROR_PAGE) {
+ securityFlags |= nsILoadInfo::SEC_LOAD_ERROR_PAGE;
+ }
+
+ if (aLoadState->PrincipalToInherit()) {
+ bool isSrcdoc = aLoadState->HasInternalLoadFlags(
+ nsDocShell::INTERNAL_LOAD_FLAGS_IS_SRCDOC);
+ bool inheritAttrs = nsContentUtils::ChannelShouldInheritPrincipal(
+ aLoadState->PrincipalToInherit(), aLoadState->URI(),
+ true, // aInheritForAboutBlank
+ isSrcdoc);
+
+ bool isData = SchemeIsData(aLoadState->URI());
+ if (inheritAttrs && !isData) {
+ securityFlags |= nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
+ }
+ }
+
+ return securityFlags;
+}
+
+// Construct a LoadInfo object to use when creating the internal channel for a
+// Document/SubDocument load.
+static auto CreateDocumentLoadInfo(CanonicalBrowsingContext* aBrowsingContext,
+ nsDocShellLoadState* aLoadState)
+ -> already_AddRefed<LoadInfo> {
+ uint32_t sandboxFlags = aBrowsingContext->GetSandboxFlags();
+ RefPtr<LoadInfo> loadInfo;
+
+ auto securityFlags = SecurityFlagsForLoadInfo(aLoadState);
+
+ if (aBrowsingContext->GetParent()) {
+ loadInfo = LoadInfo::CreateForFrame(
+ aBrowsingContext, aLoadState->TriggeringPrincipal(),
+ aLoadState->GetEffectiveTriggeringRemoteType(), securityFlags,
+ sandboxFlags);
+ } else {
+ OriginAttributes attrs;
+ aBrowsingContext->GetOriginAttributes(attrs);
+ loadInfo = LoadInfo::CreateForDocument(
+ aBrowsingContext, aLoadState->URI(), aLoadState->TriggeringPrincipal(),
+ aLoadState->GetEffectiveTriggeringRemoteType(), attrs, securityFlags,
+ sandboxFlags);
+ }
+
+ if (aLoadState->IsExemptFromHTTPSFirstMode()) {
+ uint32_t httpsOnlyStatus = loadInfo->GetHttpsOnlyStatus();
+ httpsOnlyStatus |= nsILoadInfo::HTTPS_FIRST_EXEMPT_NEXT_LOAD;
+ loadInfo->SetHttpsOnlyStatus(httpsOnlyStatus);
+ }
+
+ loadInfo->SetWasSchemelessInput(aLoadState->GetWasSchemelessInput());
+
+ loadInfo->SetTriggeringSandboxFlags(aLoadState->TriggeringSandboxFlags());
+ loadInfo->SetTriggeringWindowId(aLoadState->TriggeringWindowId());
+ loadInfo->SetTriggeringStorageAccess(aLoadState->TriggeringStorageAccess());
+ loadInfo->SetHasValidUserGestureActivation(
+ aLoadState->HasValidUserGestureActivation());
+ loadInfo->SetIsMetaRefresh(aLoadState->IsMetaRefresh());
+
+ return loadInfo.forget();
+}
+
+// Construct a LoadInfo object to use when creating the internal channel for an
+// Object/Embed load.
+static auto CreateObjectLoadInfo(nsDocShellLoadState* aLoadState,
+ uint64_t aInnerWindowId,
+ nsContentPolicyType aContentPolicyType,
+ uint32_t aSandboxFlags)
+ -> already_AddRefed<LoadInfo> {
+ RefPtr<WindowGlobalParent> wgp =
+ WindowGlobalParent::GetByInnerWindowId(aInnerWindowId);
+ MOZ_RELEASE_ASSERT(wgp);
+
+ auto securityFlags = SecurityFlagsForLoadInfo(aLoadState);
+
+ RefPtr<LoadInfo> loadInfo = LoadInfo::CreateForNonDocument(
+ wgp, wgp->DocumentPrincipal(), aContentPolicyType, securityFlags,
+ aSandboxFlags);
+
+ loadInfo->SetHasValidUserGestureActivation(
+ aLoadState->HasValidUserGestureActivation());
+ loadInfo->SetTriggeringSandboxFlags(aLoadState->TriggeringSandboxFlags());
+ loadInfo->SetTriggeringWindowId(aLoadState->TriggeringWindowId());
+ loadInfo->SetTriggeringStorageAccess(aLoadState->TriggeringStorageAccess());
+ loadInfo->SetIsMetaRefresh(aLoadState->IsMetaRefresh());
+
+ return loadInfo.forget();
+}
+
+/**
+ * An extension to nsDocumentOpenInfo that we run in the parent process, so
+ * that we can make the decision to retarget to content handlers or the external
+ * helper app, before we make process switching decisions.
+ *
+ * This modifies the behaviour of nsDocumentOpenInfo so that it can do
+ * retargeting, but doesn't do stream conversion (but confirms that we will be
+ * able to do so later).
+ *
+ * We still run nsDocumentOpenInfo in the content process, but disable
+ * retargeting, so that it can only apply stream conversion, and then send data
+ * to the docshell.
+ */
+class ParentProcessDocumentOpenInfo final : public nsDocumentOpenInfo,
+ public nsIMultiPartChannelListener {
+ public:
+ ParentProcessDocumentOpenInfo(ParentChannelListener* aListener,
+ uint32_t aFlags,
+ mozilla::dom::BrowsingContext* aBrowsingContext,
+ bool aIsDocumentLoad)
+ : nsDocumentOpenInfo(aFlags, false),
+ mBrowsingContext(aBrowsingContext),
+ mListener(aListener),
+ mIsDocumentLoad(aIsDocumentLoad) {
+ LOG(("ParentProcessDocumentOpenInfo ctor [this=%p]", this));
+ }
+
+ NS_DECL_ISUPPORTS_INHERITED
+
+ // The default content listener is always a docshell, so this manually
+ // implements the same checks, and if it succeeds, uses the parent
+ // channel listener so that we forward onto DocumentLoadListener.
+ bool TryDefaultContentListener(nsIChannel* aChannel,
+ const nsCString& aContentType) {
+ uint32_t canHandle = nsWebNavigationInfo::IsTypeSupported(aContentType);
+ if (canHandle != nsIWebNavigationInfo::UNSUPPORTED) {
+ m_targetStreamListener = mListener;
+ nsLoadFlags loadFlags = 0;
+ aChannel->GetLoadFlags(&loadFlags);
+ aChannel->SetLoadFlags(loadFlags | nsIChannel::LOAD_TARGETED);
+ return true;
+ }
+ return false;
+ }
+
+ bool TryDefaultContentListener(nsIChannel* aChannel) override {
+ return TryDefaultContentListener(aChannel, mContentType);
+ }
+
+ // Generally we only support stream converters that can tell
+ // use exactly what type they'll output. If we find one, then
+ // we just target to our default listener directly (without
+ // conversion), and the content process nsDocumentOpenInfo will
+ // run and do the actual conversion.
+ nsresult TryStreamConversion(nsIChannel* aChannel) override {
+ // The one exception is nsUnknownDecoder, which works in the parent
+ // (and we need to know what the content type is before we can
+ // decide if it will be handled in the parent), so we run that here.
+ if (mContentType.LowerCaseEqualsASCII(UNKNOWN_CONTENT_TYPE) ||
+ mContentType.IsEmpty()) {
+ return nsDocumentOpenInfo::TryStreamConversion(aChannel);
+ }
+
+ nsresult rv;
+ nsCOMPtr<nsIStreamConverterService> streamConvService =
+ do_GetService(NS_STREAMCONVERTERSERVICE_CONTRACTID, &rv);
+ nsAutoCString str;
+ rv = streamConvService->ConvertedType(mContentType, aChannel, str);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // We only support passing data to the default content listener
+ // (docshell), and we don't supported chaining converters.
+ if (TryDefaultContentListener(aChannel, str)) {
+ mContentType = str;
+ return NS_OK;
+ }
+ // This is the same result as nsStreamConverterService uses when it
+ // can't find a converter
+ return NS_ERROR_FAILURE;
+ }
+
+ nsresult TryExternalHelperApp(nsIExternalHelperAppService* aHelperAppService,
+ nsIChannel* aChannel) override {
+ RefPtr<nsIStreamListener> listener;
+ nsresult rv = aHelperAppService->CreateListener(
+ mContentType, aChannel, mBrowsingContext, false, nullptr,
+ getter_AddRefs(listener));
+ if (NS_SUCCEEDED(rv)) {
+ m_targetStreamListener = listener;
+ }
+ return rv;
+ }
+
+ nsDocumentOpenInfo* Clone() override {
+ mCloned = true;
+ return new ParentProcessDocumentOpenInfo(mListener, mFlags,
+ mBrowsingContext, mIsDocumentLoad);
+ }
+
+ nsresult OnDocumentStartRequest(nsIRequest* request) {
+ LOG(("ParentProcessDocumentOpenInfo OnDocumentStartRequest [this=%p]",
+ this));
+
+ nsresult rv = nsDocumentOpenInfo::OnStartRequest(request);
+
+ // If we didn't find a content handler,
+ // and we don't have a listener, then just forward to our
+ // default listener. This happens when the channel is in
+ // an error state, and we want to just forward that on to be
+ // handled in the content process.
+ if (NS_SUCCEEDED(rv) && !mUsedContentHandler && !m_targetStreamListener) {
+ m_targetStreamListener = mListener;
+ return m_targetStreamListener->OnStartRequest(request);
+ }
+ if (m_targetStreamListener != mListener) {
+ LOG(
+ ("ParentProcessDocumentOpenInfo targeted to non-default listener "
+ "[this=%p]",
+ this));
+ // If this is the only part, then we can immediately tell our listener
+ // that it won't be getting any content and disconnect it. For multipart
+ // channels we have to wait until we've handled all parts before we know.
+ // This does mean that the content process can still Cancel() a multipart
+ // response while the response is being handled externally, but this
+ // matches the single-process behaviour.
+ // If we got cloned, then we don't need to do this, as only the last link
+ // needs to do it.
+ // Multi-part channels are guaranteed to call OnAfterLastPart, which we
+ // forward to the listeners, so it will handle disconnection at that
+ // point.
+ nsCOMPtr<nsIMultiPartChannel> multiPartChannel =
+ do_QueryInterface(request);
+ if (!multiPartChannel && !mCloned) {
+ DisconnectChildListeners(NS_FAILED(rv) ? rv : NS_BINDING_RETARGETED,
+ rv);
+ }
+ }
+ return rv;
+ }
+
+ nsresult OnObjectStartRequest(nsIRequest* request) {
+ LOG(("ParentProcessDocumentOpenInfo OnObjectStartRequest [this=%p]", this));
+ // Just redirect to the nsObjectLoadingContent in the content process.
+ m_targetStreamListener = mListener;
+ return m_targetStreamListener->OnStartRequest(request);
+ }
+
+ NS_IMETHOD OnStartRequest(nsIRequest* request) override {
+ LOG(("ParentProcessDocumentOpenInfo OnStartRequest [this=%p]", this));
+
+ if (mIsDocumentLoad) {
+ return OnDocumentStartRequest(request);
+ }
+
+ return OnObjectStartRequest(request);
+ }
+
+ NS_IMETHOD OnAfterLastPart(nsresult aStatus) override {
+ mListener->OnAfterLastPart(aStatus);
+ return NS_OK;
+ }
+
+ private:
+ virtual ~ParentProcessDocumentOpenInfo() {
+ LOG(("ParentProcessDocumentOpenInfo dtor [this=%p]", this));
+ }
+
+ void DisconnectChildListeners(nsresult aStatus, nsresult aLoadGroupStatus) {
+ // Tell the DocumentLoadListener to notify the content process that it's
+ // been entirely retargeted, and to stop waiting.
+ // Clear mListener's pointer to the DocumentLoadListener to break the
+ // reference cycle.
+ RefPtr<DocumentLoadListener> doc = do_GetInterface(ToSupports(mListener));
+ MOZ_ASSERT(doc);
+ doc->DisconnectListeners(aStatus, aLoadGroupStatus);
+ mListener->SetListenerAfterRedirect(nullptr);
+ }
+
+ RefPtr<mozilla::dom::BrowsingContext> mBrowsingContext;
+ RefPtr<ParentChannelListener> mListener;
+ const bool mIsDocumentLoad;
+
+ /**
+ * Set to true if we got cloned to create a chained listener.
+ */
+ bool mCloned = false;
+};
+
+NS_IMPL_ADDREF_INHERITED(ParentProcessDocumentOpenInfo, nsDocumentOpenInfo)
+NS_IMPL_RELEASE_INHERITED(ParentProcessDocumentOpenInfo, nsDocumentOpenInfo)
+
+NS_INTERFACE_MAP_BEGIN(ParentProcessDocumentOpenInfo)
+ NS_INTERFACE_MAP_ENTRY(nsIMultiPartChannelListener)
+NS_INTERFACE_MAP_END_INHERITING(nsDocumentOpenInfo)
+
+NS_IMPL_ADDREF(DocumentLoadListener)
+NS_IMPL_RELEASE(DocumentLoadListener)
+
+NS_INTERFACE_MAP_BEGIN(DocumentLoadListener)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInterfaceRequestor)
+ NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
+ NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
+ NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
+ NS_INTERFACE_MAP_ENTRY(nsIParentChannel)
+ NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectReadyCallback)
+ NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink)
+ NS_INTERFACE_MAP_ENTRY(nsIMultiPartChannelListener)
+ NS_INTERFACE_MAP_ENTRY(nsIProgressEventSink)
+ NS_INTERFACE_MAP_ENTRY(nsIEarlyHintObserver)
+ NS_INTERFACE_MAP_ENTRY_CONCRETE(DocumentLoadListener)
+NS_INTERFACE_MAP_END
+
+DocumentLoadListener::DocumentLoadListener(
+ CanonicalBrowsingContext* aLoadingBrowsingContext, bool aIsDocumentLoad)
+ : mIsDocumentLoad(aIsDocumentLoad) {
+ LOG(("DocumentLoadListener ctor [this=%p]", this));
+ mParentChannelListener =
+ new ParentChannelListener(this, aLoadingBrowsingContext);
+}
+
+DocumentLoadListener::~DocumentLoadListener() {
+ LOG(("DocumentLoadListener dtor [this=%p]", this));
+}
+
+void DocumentLoadListener::AddURIVisit(nsIChannel* aChannel,
+ uint32_t aLoadFlags) {
+ if (mLoadStateLoadType == LOAD_ERROR_PAGE ||
+ mLoadStateLoadType == LOAD_BYPASS_HISTORY) {
+ return;
+ }
+
+ nsCOMPtr<nsIURI> uri;
+ NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
+
+ nsCOMPtr<nsIURI> previousURI;
+ uint32_t previousFlags = 0;
+ if (mLoadStateLoadType & nsIDocShell::LOAD_CMD_RELOAD) {
+ previousURI = uri;
+ } else {
+ nsDocShell::ExtractLastVisit(aChannel, getter_AddRefs(previousURI),
+ &previousFlags);
+ }
+
+ // Get the HTTP response code, if available.
+ uint32_t responseStatus = 0;
+ nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
+ if (httpChannel) {
+ Unused << httpChannel->GetResponseStatus(&responseStatus);
+ }
+
+ RefPtr<CanonicalBrowsingContext> browsingContext =
+ GetDocumentBrowsingContext();
+ nsCOMPtr<nsIWidget> widget =
+ browsingContext->GetParentProcessWidgetContaining();
+
+ nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
+
+ // Check if the URI has a http scheme and if either HSTS is enabled for this
+ // host, or if we were upgraded by HTTPS-Only/First. If this is the case, we
+ // want to mark this URI specially, as it will be followed shortly by an
+ // almost identical https history entry. That way the global history
+ // implementation can handle the visit appropriately (e.g. by marking it as
+ // `hidden`, so only the https url appears in default history views).
+ bool wasUpgraded =
+ uri->SchemeIs("http") &&
+ (loadInfo->GetHstsStatus() ||
+ (loadInfo->GetHttpsOnlyStatus() &
+ (nsILoadInfo::HTTPS_ONLY_UPGRADED_HTTPS_FIRST |
+ nsILoadInfo::HTTPS_ONLY_UPGRADED_LISTENER_NOT_REGISTERED |
+ nsILoadInfo::HTTPS_ONLY_UPGRADED_LISTENER_REGISTERED)));
+
+ nsDocShell::InternalAddURIVisit(uri, previousURI, previousFlags,
+ responseStatus, browsingContext, widget,
+ mLoadStateLoadType, wasUpgraded);
+}
+
+CanonicalBrowsingContext* DocumentLoadListener::GetLoadingBrowsingContext()
+ const {
+ return mParentChannelListener ? mParentChannelListener->GetBrowsingContext()
+ : nullptr;
+}
+
+CanonicalBrowsingContext* DocumentLoadListener::GetDocumentBrowsingContext()
+ const {
+ return mIsDocumentLoad ? GetLoadingBrowsingContext() : nullptr;
+}
+
+CanonicalBrowsingContext* DocumentLoadListener::GetTopBrowsingContext() const {
+ auto* loadingContext = GetLoadingBrowsingContext();
+ return loadingContext ? loadingContext->Top() : nullptr;
+}
+
+WindowGlobalParent* DocumentLoadListener::GetParentWindowContext() const {
+ return mParentWindowContext;
+}
+
+bool CheckRecursiveLoad(CanonicalBrowsingContext* aLoadingContext,
+ nsDocShellLoadState* aLoadState, bool aIsDocumentLoad) {
+ // Bug 136580: Check for recursive frame loading excluding about:srcdoc URIs.
+ // srcdoc URIs require their contents to be specified inline, so it isn't
+ // possible for undesirable recursion to occur without the aid of a
+ // non-srcdoc URI, which this method will block normally.
+ // Besides, URI is not enough to guarantee uniqueness of srcdoc documents.
+ nsAutoCString buffer;
+ if (aLoadState->URI()->SchemeIs("about")) {
+ nsresult rv = aLoadState->URI()->GetPathQueryRef(buffer);
+ if (NS_SUCCEEDED(rv) && buffer.EqualsLiteral("srcdoc")) {
+ // Duplicates allowed up to depth limits
+ return true;
+ }
+ }
+
+ RefPtr<WindowGlobalParent> parent;
+ if (!aIsDocumentLoad) { // object load
+ parent = aLoadingContext->GetCurrentWindowGlobal();
+ } else {
+ parent = aLoadingContext->GetParentWindowContext();
+ }
+
+ int matchCount = 0;
+ CanonicalBrowsingContext* ancestorBC;
+ for (WindowGlobalParent* ancestorWGP = parent; ancestorWGP;
+ ancestorWGP = ancestorBC->GetParentWindowContext()) {
+ ancestorBC = ancestorWGP->BrowsingContext();
+ MOZ_ASSERT(ancestorBC);
+ if (nsCOMPtr<nsIURI> parentURI = ancestorWGP->GetDocumentURI()) {
+ bool equal;
+ nsresult rv = aLoadState->URI()->EqualsExceptRef(parentURI, &equal);
+ NS_ENSURE_SUCCESS(rv, false);
+
+ if (equal) {
+ matchCount++;
+ if (matchCount >= kMaxSameURLContentFrames) {
+ NS_WARNING(
+ "Too many nested content frames/objects have the same url "
+ "(recursion?) "
+ "so giving up");
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+}
+
+// Check that the load state, potentially received from a child process, appears
+// to be performing a load of the specified LoadingSessionHistoryInfo.
+// Returns a Result<…> containing the SessionHistoryEntry found for the
+// LoadingSessionHistoryInfo as success value if the validation succeeded, or a
+// static (telemetry-safe) string naming what did not match as a failure value
+// if the validation failed.
+static Result<SessionHistoryEntry*, const char*> ValidateHistoryLoad(
+ CanonicalBrowsingContext* aLoadingContext,
+ nsDocShellLoadState* aLoadState) {
+ MOZ_ASSERT(SessionHistoryInParent());
+ MOZ_ASSERT(aLoadState->LoadIsFromSessionHistory());
+
+ if (!aLoadState->GetLoadingSessionHistoryInfo()) {
+ return Err("Missing LoadingSessionHistoryInfo");
+ }
+
+ SessionHistoryEntry::LoadingEntry* loading = SessionHistoryEntry::GetByLoadId(
+ aLoadState->GetLoadingSessionHistoryInfo()->mLoadId);
+ if (!loading) {
+ return Err("Missing SessionHistoryEntry");
+ }
+
+ SessionHistoryInfo* snapshot = loading->mInfoSnapshotForValidation.get();
+ // History loads do not inherit principal.
+ if (aLoadState->HasInternalLoadFlags(
+ nsDocShell::INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL)) {
+ return Err("LOAD_FLAGS_INHERIT_PRINCIPAL");
+ }
+
+ auto uriEq = [](nsIURI* a, nsIURI* b) -> bool {
+ bool eq = false;
+ return a == b || (a && b && NS_SUCCEEDED(a->Equals(b, &eq)) && eq);
+ };
+ auto principalEq = [](nsIPrincipal* a, nsIPrincipal* b) -> bool {
+ return a == b || (a && b && a->Equals(b));
+ };
+
+ // XXX: Needing to do all of this validation manually is kinda gross.
+ if (!uriEq(snapshot->GetURI(), aLoadState->URI())) {
+ return Err("URI");
+ }
+ if (!uriEq(snapshot->GetOriginalURI(), aLoadState->OriginalURI())) {
+ return Err("OriginalURI");
+ }
+ if (!aLoadState->ResultPrincipalURIIsSome() ||
+ !uriEq(snapshot->GetResultPrincipalURI(),
+ aLoadState->ResultPrincipalURI())) {
+ return Err("ResultPrincipalURI");
+ }
+ if (!uriEq(snapshot->GetUnstrippedURI(), aLoadState->GetUnstrippedURI())) {
+ return Err("UnstrippedURI");
+ }
+ if (!principalEq(snapshot->GetTriggeringPrincipal(),
+ aLoadState->TriggeringPrincipal())) {
+ return Err("TriggeringPrincipal");
+ }
+ if (!principalEq(snapshot->GetPrincipalToInherit(),
+ aLoadState->PrincipalToInherit())) {
+ return Err("PrincipalToInherit");
+ }
+ if (!principalEq(snapshot->GetPartitionedPrincipalToInherit(),
+ aLoadState->PartitionedPrincipalToInherit())) {
+ return Err("PartitionedPrincipalToInherit");
+ }
+
+ // Everything matches!
+ return loading->mEntry;
+}
+
+auto DocumentLoadListener::Open(nsDocShellLoadState* aLoadState,
+ LoadInfo* aLoadInfo, nsLoadFlags aLoadFlags,
+ uint32_t aCacheKey,
+ const Maybe<uint64_t>& aChannelId,
+ const TimeStamp& aAsyncOpenTime,
+ nsDOMNavigationTiming* aTiming,
+ Maybe<ClientInfo>&& aInfo, bool aUrgentStart,
+ dom::ContentParent* aContentParent,
+ nsresult* aRv) -> RefPtr<OpenPromise> {
+ auto* loadingContext = GetLoadingBrowsingContext();
+
+ MOZ_DIAGNOSTIC_ASSERT_IF(loadingContext->GetParent(),
+ loadingContext->GetParentWindowContext());
+
+ OriginAttributes attrs;
+ loadingContext->GetOriginAttributes(attrs);
+
+ mLoadIdentifier = aLoadState->GetLoadIdentifier();
+ // See description of mFileName in nsDocShellLoadState.h
+ mIsDownload = !aLoadState->FileName().IsVoid();
+ mIsLoadingJSURI = net::SchemeIsJavascript(aLoadState->URI());
+
+ // Check for infinite recursive object or iframe loads
+ if (aLoadState->OriginalFrameSrc() || !mIsDocumentLoad) {
+ if (!CheckRecursiveLoad(loadingContext, aLoadState, mIsDocumentLoad)) {
+ *aRv = NS_ERROR_RECURSIVE_DOCUMENT_LOAD;
+ mParentChannelListener = nullptr;
+ return nullptr;
+ }
+ }
+
+ auto* documentContext = GetDocumentBrowsingContext();
+
+ // If we are using SHIP and this load is from session history, validate that
+ // the load matches our local copy of the loading history entry.
+ //
+ // NOTE: Keep this check in-sync with the check in
+ // `nsDocShellLoadState::GetEffectiveTriggeringRemoteType()`!
+ RefPtr<SessionHistoryEntry> existingEntry;
+ if (SessionHistoryInParent() && aLoadState->LoadIsFromSessionHistory() &&
+ aLoadState->LoadType() != LOAD_ERROR_PAGE) {
+ Result<SessionHistoryEntry*, const char*> result =
+ ValidateHistoryLoad(loadingContext, aLoadState);
+ if (result.isErr()) {
+ const char* mismatch = result.unwrapErr();
+ LOG(
+ ("DocumentLoadListener::Open with invalid loading history entry "
+ "[this=%p, mismatch=%s]",
+ this, mismatch));
+#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
+ MOZ_CRASH_UNSAFE_PRINTF(
+ "DocumentLoadListener::Open for invalid history entry due to "
+ "mismatch of '%s'",
+ mismatch);
+#endif
+ *aRv = NS_ERROR_DOM_SECURITY_ERR;
+ mParentChannelListener = nullptr;
+ return nullptr;
+ }
+
+ existingEntry = result.unwrap();
+ if (!existingEntry->IsInSessionHistory() &&
+ !documentContext->HasLoadingHistoryEntry(existingEntry)) {
+ SessionHistoryEntry::RemoveLoadId(
+ aLoadState->GetLoadingSessionHistoryInfo()->mLoadId);
+ LOG(
+ ("DocumentLoadListener::Open with disconnected history entry "
+ "[this=%p]",
+ this));
+
+ *aRv = NS_BINDING_ABORTED;
+ mParentChannelListener = nullptr;
+ mChannel = nullptr;
+ return nullptr;
+ }
+ }
+
+ if (aLoadState->GetRemoteTypeOverride()) {
+ if (!mIsDocumentLoad || !NS_IsAboutBlank(aLoadState->URI()) ||
+ !loadingContext->IsTopContent()) {
+ LOG(
+ ("DocumentLoadListener::Open with invalid remoteTypeOverride "
+ "[this=%p]",
+ this));
+ *aRv = NS_ERROR_DOM_SECURITY_ERR;
+ mParentChannelListener = nullptr;
+ return nullptr;
+ }
+
+ mRemoteTypeOverride = aLoadState->GetRemoteTypeOverride();
+ }
+
+ if (NS_WARN_IF(!loadingContext->IsOwnedByProcess(
+ GetContentProcessId(aContentParent)))) {
+ LOG(
+ ("DocumentLoadListener::Open called from non-current content process "
+ "[this=%p, current=%" PRIu64 ", caller=%" PRIu64 "]",
+ this, loadingContext->OwnerProcessId(),
+ uint64_t(GetContentProcessId(aContentParent))));
+ *aRv = NS_BINDING_ABORTED;
+ mParentChannelListener = nullptr;
+ return nullptr;
+ }
+
+ if (mIsDocumentLoad && loadingContext->IsContent() &&
+ NS_WARN_IF(loadingContext->IsReplaced())) {
+ LOG(
+ ("DocumentLoadListener::Open called from replaced BrowsingContext "
+ "[this=%p, browserid=%" PRIx64 ", bcid=%" PRIx64 "]",
+ this, loadingContext->BrowserId(), loadingContext->Id()));
+ *aRv = NS_BINDING_ABORTED;
+ mParentChannelListener = nullptr;
+ return nullptr;
+ }
+
+ if (!nsDocShell::CreateAndConfigureRealChannelForLoadState(
+ loadingContext, aLoadState, aLoadInfo, mParentChannelListener,
+ nullptr, attrs, aLoadFlags, aCacheKey, *aRv,
+ getter_AddRefs(mChannel))) {
+ LOG(("DocumentLoadListener::Open failed to create channel [this=%p]",
+ this));
+ mParentChannelListener = nullptr;
+ return nullptr;
+ }
+
+ if (documentContext && aLoadState->LoadType() != LOAD_ERROR_PAGE &&
+ mozilla::SessionHistoryInParent()) {
+ // It's hard to know at this point whether session history will be enabled
+ // in the browsing context, so we always create an entry for a load here.
+ mLoadingSessionHistoryInfo =
+ documentContext->CreateLoadingSessionHistoryEntryForLoad(
+ aLoadState, existingEntry, mChannel);
+ MOZ_ASSERT(mLoadingSessionHistoryInfo);
+ }
+
+ nsCOMPtr<nsIURI> uriBeingLoaded;
+ Unused << NS_WARN_IF(
+ NS_FAILED(mChannel->GetURI(getter_AddRefs(uriBeingLoaded))));
+
+ RefPtr<HttpBaseChannel> httpBaseChannel = do_QueryObject(mChannel, aRv);
+ if (uriBeingLoaded && httpBaseChannel) {
+ nsCOMPtr<nsIURI> topWindowURI;
+ if (mIsDocumentLoad && loadingContext->IsTop()) {
+ // If this is for the top level loading, the top window URI should be the
+ // URI which we are loading.
+ topWindowURI = uriBeingLoaded;
+ } else if (RefPtr<WindowGlobalParent> topWindow = AntiTrackingUtils::
+ GetTopWindowExcludingExtensionAccessibleContentFrames(
+ loadingContext, uriBeingLoaded)) {
+ nsCOMPtr<nsIPrincipal> topWindowPrincipal =
+ topWindow->DocumentPrincipal();
+ if (topWindowPrincipal && !topWindowPrincipal->GetIsNullPrincipal()) {
+ auto* basePrin = BasePrincipal::Cast(topWindowPrincipal);
+ basePrin->GetURI(getter_AddRefs(topWindowURI));
+ }
+ }
+ httpBaseChannel->SetTopWindowURI(topWindowURI);
+ }
+
+ nsCOMPtr<nsIIdentChannel> identChannel = do_QueryInterface(mChannel);
+ if (identChannel && aChannelId) {
+ Unused << identChannel->SetChannelId(*aChannelId);
+ }
+ mDocumentChannelId = aChannelId;
+
+ RefPtr<nsHttpChannel> httpChannelImpl = do_QueryObject(mChannel);
+ if (httpChannelImpl) {
+ httpChannelImpl->SetWarningReporter(this);
+
+ if (mIsDocumentLoad && loadingContext->IsTop()) {
+ httpChannelImpl->SetEarlyHintObserver(this);
+ }
+ }
+
+ nsCOMPtr<nsITimedChannel> timedChannel = do_QueryInterface(mChannel);
+ if (timedChannel) {
+ timedChannel->SetAsyncOpen(aAsyncOpenTime);
+ }
+
+ if (nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel)) {
+ Unused << httpChannel->SetRequestContextID(
+ loadingContext->GetRequestContextId());
+
+ nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(httpChannel));
+ if (cos && aUrgentStart) {
+ cos->AddClassFlags(nsIClassOfService::UrgentStart);
+ }
+ }
+
+ // Setup a ClientChannelHelper to watch for redirects, and copy
+ // across any serviceworker related data between channels as needed.
+ AddClientChannelHelperInParent(mChannel, std::move(aInfo));
+
+ if (documentContext && !documentContext->StartDocumentLoad(this)) {
+ LOG(("DocumentLoadListener::Open failed StartDocumentLoad [this=%p]",
+ this));
+ *aRv = NS_BINDING_ABORTED;
+ mParentChannelListener = nullptr;
+ mChannel = nullptr;
+ return nullptr;
+ }
+
+ // Recalculate the openFlags, matching the logic in use in Content process.
+ // NOTE: The only case not handled here to mirror Content process is
+ // redirecting to re-use the channel.
+ MOZ_ASSERT(!aLoadState->GetPendingRedirectedChannel());
+ uint32_t openFlags =
+ nsDocShell::ComputeURILoaderFlags(loadingContext, aLoadState->LoadType());
+
+ RefPtr<ParentProcessDocumentOpenInfo> openInfo =
+ new ParentProcessDocumentOpenInfo(mParentChannelListener, openFlags,
+ loadingContext, mIsDocumentLoad);
+ openInfo->Prepare();
+
+#ifdef ANDROID
+ RefPtr<MozPromise<bool, bool, false>> promise;
+ if (documentContext && aLoadState->LoadType() != LOAD_ERROR_PAGE &&
+ !(aLoadState->HasInternalLoadFlags(
+ nsDocShell::INTERNAL_LOAD_FLAGS_BYPASS_LOAD_URI_DELEGATE)) &&
+ !(aLoadState->LoadType() & LOAD_HISTORY)) {
+ nsCOMPtr<nsIWidget> widget =
+ documentContext->GetParentProcessWidgetContaining();
+ RefPtr<nsWindow> window = nsWindow::From(widget);
+
+ if (window) {
+ promise = window->OnLoadRequest(
+ aLoadState->URI(), nsIBrowserDOMWindow::OPEN_CURRENTWINDOW,
+ aLoadState->InternalLoadFlags(), aLoadState->TriggeringPrincipal(),
+ aLoadState->HasValidUserGestureActivation(),
+ documentContext->IsTopContent());
+ }
+ }
+
+ if (promise) {
+ RefPtr<DocumentLoadListener> self = this;
+ promise->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [=](const MozPromise<bool, bool, false>::ResolveOrRejectValue& aValue) {
+ if (aValue.IsResolve()) {
+ bool handled = aValue.ResolveValue();
+ if (handled) {
+ self->DisconnectListeners(NS_ERROR_ABORT, NS_ERROR_ABORT);
+ mParentChannelListener = nullptr;
+ } else {
+ nsresult rv = mChannel->AsyncOpen(openInfo);
+ if (NS_FAILED(rv)) {
+ self->DisconnectListeners(rv, rv);
+ mParentChannelListener = nullptr;
+ }
+ }
+ }
+ });
+ } else
+#endif /* ANDROID */
+ {
+ *aRv = mChannel->AsyncOpen(openInfo);
+ if (NS_FAILED(*aRv)) {
+ LOG(("DocumentLoadListener::Open failed AsyncOpen [this=%p rv=%" PRIx32
+ "]",
+ this, static_cast<uint32_t>(*aRv)));
+ if (documentContext) {
+ documentContext->EndDocumentLoad(false);
+ }
+ mParentChannelListener = nullptr;
+ return nullptr;
+ }
+ }
+
+ // HTTPS-Only Mode fights potential timeouts caused by upgrades. Instantly
+ // after opening the document channel we have to kick off countermeasures.
+ nsHTTPSOnlyUtils::PotentiallyFireHttpRequestToShortenTimout(this);
+
+ nsCOMPtr<nsILoadInfo> loadInfo = mChannel->LoadInfo();
+ loadInfo->SetChannelCreationOriginalURI(aLoadState->URI());
+
+ mContentParent = aContentParent;
+ mLoadStateExternalLoadFlags = aLoadState->LoadFlags();
+ mLoadStateInternalLoadFlags = aLoadState->InternalLoadFlags();
+ mLoadStateLoadType = aLoadState->LoadType();
+ mTiming = aTiming;
+ mSrcdocData = aLoadState->SrcdocData();
+ mBaseURI = aLoadState->BaseURI();
+ mOriginalUriString = aLoadState->GetOriginalURIString();
+ if (documentContext) {
+ mParentWindowContext = documentContext->GetParentWindowContext();
+ } else {
+ mParentWindowContext =
+ WindowGlobalParent::GetByInnerWindowId(aLoadInfo->GetInnerWindowID());
+ MOZ_RELEASE_ASSERT(mParentWindowContext->GetBrowsingContext() ==
+ GetLoadingBrowsingContext(),
+ "mismatched parent window context?");
+ }
+
+ // For content-initiated loads, this flag is set in nsDocShell::LoadURI.
+ // For parent-initiated loads, we have to set it here.
+ // Below comment is copied from nsDocShell::LoadURI -
+ // If we have a system triggering principal, we can assume that this load was
+ // triggered by some UI in the browser chrome, such as the URL bar or
+ // bookmark bar. This should count as a user interaction for the current sh
+ // entry, so that the user may navigate back to the current entry, from the
+ // entry that is going to be added as part of this load.
+ if (!mSupportsRedirectToRealChannel && aLoadState->TriggeringPrincipal() &&
+ aLoadState->TriggeringPrincipal()->IsSystemPrincipal()) {
+ WindowContext* topWc = loadingContext->GetTopWindowContext();
+ if (topWc && !topWc->IsDiscarded()) {
+ MOZ_ALWAYS_SUCCEEDS(topWc->SetSHEntryHasUserInteraction(true));
+ }
+ }
+
+ *aRv = NS_OK;
+ mOpenPromise = new OpenPromise::Private(__func__);
+ // We make the promise use direct task dispatch in order to reduce the number
+ // of event loops iterations.
+ mOpenPromise->UseDirectTaskDispatch(__func__);
+ return mOpenPromise;
+}
+
+auto DocumentLoadListener::OpenDocument(
+ nsDocShellLoadState* aLoadState, uint32_t aCacheKey,
+ const Maybe<uint64_t>& aChannelId, const TimeStamp& aAsyncOpenTime,
+ nsDOMNavigationTiming* aTiming, Maybe<dom::ClientInfo>&& aInfo,
+ Maybe<bool> aUriModified, Maybe<bool> aIsEmbeddingBlockedError,
+ dom::ContentParent* aContentParent, nsresult* aRv) -> RefPtr<OpenPromise> {
+ LOG(("DocumentLoadListener [%p] OpenDocument [uri=%s]", this,
+ aLoadState->URI()->GetSpecOrDefault().get()));
+
+ MOZ_ASSERT(mIsDocumentLoad);
+
+ RefPtr<CanonicalBrowsingContext> browsingContext =
+ GetDocumentBrowsingContext();
+
+ // If this is a top-level load, then rebuild the LoadInfo from scratch,
+ // since the goal is to be able to initiate loads in the parent, where the
+ // content process won't have provided us with an existing one.
+ RefPtr<LoadInfo> loadInfo =
+ CreateDocumentLoadInfo(browsingContext, aLoadState);
+
+ nsLoadFlags loadFlags = aLoadState->CalculateChannelLoadFlags(
+ browsingContext, std::move(aUriModified),
+ std::move(aIsEmbeddingBlockedError));
+
+ // Keep track of navigation for the Bounce Tracking Protection.
+ if (browsingContext->IsTopContent()) {
+ RefPtr<BounceTrackingState> bounceTrackingState =
+ browsingContext->GetBounceTrackingState();
+
+ // Not every browsing context has a BounceTrackingState. It's also null when
+ // the feature is disabled.
+ if (bounceTrackingState) {
+ nsCOMPtr<nsIPrincipal> triggeringPrincipal;
+ nsresult rv =
+ loadInfo->GetTriggeringPrincipal(getter_AddRefs(triggeringPrincipal));
+
+ if (!NS_WARN_IF(NS_FAILED(rv))) {
+ DebugOnly<nsresult> rv = bounceTrackingState->OnStartNavigation(
+ triggeringPrincipal, loadInfo->GetHasValidUserGestureActivation());
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
+ "BounceTrackingState::OnStartNavigation failed");
+ }
+ }
+ }
+
+ return Open(aLoadState, loadInfo, loadFlags, aCacheKey, aChannelId,
+ aAsyncOpenTime, aTiming, std::move(aInfo), false, aContentParent,
+ aRv);
+}
+
+auto DocumentLoadListener::OpenObject(
+ nsDocShellLoadState* aLoadState, uint32_t aCacheKey,
+ const Maybe<uint64_t>& aChannelId, const TimeStamp& aAsyncOpenTime,
+ nsDOMNavigationTiming* aTiming, Maybe<dom::ClientInfo>&& aInfo,
+ uint64_t aInnerWindowId, nsLoadFlags aLoadFlags,
+ nsContentPolicyType aContentPolicyType, bool aUrgentStart,
+ dom::ContentParent* aContentParent,
+ ObjectUpgradeHandler* aObjectUpgradeHandler, nsresult* aRv)
+ -> RefPtr<OpenPromise> {
+ LOG(("DocumentLoadListener [%p] OpenObject [uri=%s]", this,
+ aLoadState->URI()->GetSpecOrDefault().get()));
+
+ MOZ_ASSERT(!mIsDocumentLoad);
+
+ auto sandboxFlags = aLoadState->TriggeringSandboxFlags();
+
+ RefPtr<LoadInfo> loadInfo = CreateObjectLoadInfo(
+ aLoadState, aInnerWindowId, aContentPolicyType, sandboxFlags);
+
+ mObjectUpgradeHandler = aObjectUpgradeHandler;
+
+ return Open(aLoadState, loadInfo, aLoadFlags, aCacheKey, aChannelId,
+ aAsyncOpenTime, aTiming, std::move(aInfo), aUrgentStart,
+ aContentParent, aRv);
+}
+
+auto DocumentLoadListener::OpenInParent(nsDocShellLoadState* aLoadState,
+ bool aSupportsRedirectToRealChannel)
+ -> RefPtr<OpenPromise> {
+ MOZ_ASSERT(mIsDocumentLoad);
+
+ // We currently only support passing nullptr for aLoadInfo for
+ // top level browsing contexts.
+ auto* browsingContext = GetDocumentBrowsingContext();
+ if (!browsingContext->IsTopContent() ||
+ !browsingContext->GetContentParent()) {
+ LOG(("DocumentLoadListener::OpenInParent failed because of subdoc"));
+ return nullptr;
+ }
+
+ // Clone because this mutates the load flags in the load state, which
+ // breaks nsDocShells expectations of being able to do it.
+ RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(*aLoadState);
+ loadState->CalculateLoadURIFlags();
+
+ RefPtr<nsDOMNavigationTiming> timing = new nsDOMNavigationTiming(nullptr);
+ timing->NotifyNavigationStart(
+ browsingContext->IsActive()
+ ? nsDOMNavigationTiming::DocShellState::eActive
+ : nsDOMNavigationTiming::DocShellState::eInactive);
+
+ const mozilla::dom::LoadingSessionHistoryInfo* loadingInfo =
+ loadState->GetLoadingSessionHistoryInfo();
+
+ uint32_t cacheKey = 0;
+ auto loadType = aLoadState->LoadType();
+ if (loadType == LOAD_HISTORY || loadType == LOAD_RELOAD_NORMAL ||
+ loadType == LOAD_RELOAD_CHARSET_CHANGE ||
+ loadType == LOAD_RELOAD_CHARSET_CHANGE_BYPASS_CACHE ||
+ loadType == LOAD_RELOAD_CHARSET_CHANGE_BYPASS_PROXY_AND_CACHE) {
+ if (loadingInfo) {
+ cacheKey = loadingInfo->mInfo.GetCacheKey();
+ }
+ }
+
+ // Loads start in the content process might have exposed a channel id to
+ // observers, so we need to preserve the value in the parent. That can't have
+ // happened here, so Nothing() is fine.
+ Maybe<uint64_t> channelId = Nothing();
+
+ // Initial client info is only relevant for subdocument loads, which we're
+ // not supporting yet.
+ Maybe<dom::ClientInfo> initialClientInfo;
+
+ mSupportsRedirectToRealChannel = aSupportsRedirectToRealChannel;
+
+ // This is a top-level load, so rebuild the LoadInfo from scratch,
+ // since in the parent the
+ // content process won't have provided us with an existing one.
+ RefPtr<LoadInfo> loadInfo =
+ CreateDocumentLoadInfo(browsingContext, aLoadState);
+
+ nsLoadFlags loadFlags = loadState->CalculateChannelLoadFlags(
+ browsingContext,
+ Some(loadingInfo && loadingInfo->mInfo.GetURIWasModified()), Nothing());
+
+ nsresult rv;
+ return Open(loadState, loadInfo, loadFlags, cacheKey, channelId,
+ TimeStamp::Now(), timing, std::move(initialClientInfo), false,
+ browsingContext->GetContentParent(), &rv);
+}
+
+base::ProcessId DocumentLoadListener::OtherPid() const {
+ return mContentParent ? mContentParent->OtherPid() : base::ProcessId{0};
+}
+
+void DocumentLoadListener::FireStateChange(uint32_t aStateFlags,
+ nsresult aStatus) {
+ nsCOMPtr<nsIChannel> request = GetChannel();
+
+ RefPtr<BrowsingContextWebProgress> webProgress =
+ GetLoadingBrowsingContext()->GetWebProgress();
+
+ if (webProgress) {
+ NS_DispatchToMainThread(
+ NS_NewRunnableFunction("DocumentLoadListener::FireStateChange", [=]() {
+ webProgress->OnStateChange(webProgress, request, aStateFlags,
+ aStatus);
+ }));
+ }
+}
+
+static void SetNavigating(CanonicalBrowsingContext* aBrowsingContext,
+ bool aNavigating) {
+ nsCOMPtr<nsIBrowser> browser;
+ if (RefPtr<Element> currentElement = aBrowsingContext->GetEmbedderElement()) {
+ browser = currentElement->AsBrowser();
+ }
+
+ if (!browser) {
+ return;
+ }
+
+ NS_DispatchToMainThread(NS_NewRunnableFunction(
+ "DocumentLoadListener::SetNavigating",
+ [browser, aNavigating]() { browser->SetIsNavigating(aNavigating); }));
+}
+
+/* static */ bool DocumentLoadListener::LoadInParent(
+ CanonicalBrowsingContext* aBrowsingContext, nsDocShellLoadState* aLoadState,
+ bool aSetNavigating) {
+ SetNavigating(aBrowsingContext, aSetNavigating);
+
+ RefPtr<DocumentLoadListener> load =
+ new DocumentLoadListener(aBrowsingContext, true);
+ RefPtr<DocumentLoadListener::OpenPromise> promise = load->OpenInParent(
+ aLoadState, /* aSupportsRedirectToRealChannel */ false);
+ if (!promise) {
+ SetNavigating(aBrowsingContext, false);
+ return false;
+ }
+
+ // We passed false for aSupportsRedirectToRealChannel, so we should always
+ // take the process switching path, and reject this promise.
+ promise->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [load](DocumentLoadListener::OpenPromise::ResolveOrRejectValue&& aValue) {
+ MOZ_ASSERT(aValue.IsReject());
+ DocumentLoadListener::OpenPromiseFailedType& rejectValue =
+ aValue.RejectValue();
+ if (!rejectValue.mContinueNavigating) {
+ // If we're not switching the load to a new process, then it is
+ // finished (and failed), and we should fire a state change to notify
+ // observers. Normally the docshell would fire this, and it would get
+ // filtered out by BrowserParent if needed.
+ load->FireStateChange(nsIWebProgressListener::STATE_STOP |
+ nsIWebProgressListener::STATE_IS_WINDOW |
+ nsIWebProgressListener::STATE_IS_NETWORK,
+ rejectValue.mStatus);
+ }
+ });
+
+ load->FireStateChange(nsIWebProgressListener::STATE_START |
+ nsIWebProgressListener::STATE_IS_DOCUMENT |
+ nsIWebProgressListener::STATE_IS_REQUEST |
+ nsIWebProgressListener::STATE_IS_WINDOW |
+ nsIWebProgressListener::STATE_IS_NETWORK,
+ NS_OK);
+ SetNavigating(aBrowsingContext, false);
+ return true;
+}
+
+/* static */
+bool DocumentLoadListener::SpeculativeLoadInParent(
+ dom::CanonicalBrowsingContext* aBrowsingContext,
+ nsDocShellLoadState* aLoadState) {
+ LOG(("DocumentLoadListener::OpenFromParent"));
+
+ RefPtr<DocumentLoadListener> listener =
+ new DocumentLoadListener(aBrowsingContext, true);
+
+ auto promise = listener->OpenInParent(aLoadState, true);
+ if (promise) {
+ // Create an entry in the redirect channel registrar to
+ // allocate an identifier for this load.
+ nsCOMPtr<nsIRedirectChannelRegistrar> registrar =
+ RedirectChannelRegistrar::GetOrCreate();
+ uint64_t loadIdentifier = aLoadState->GetLoadIdentifier();
+ DebugOnly<nsresult> rv =
+ registrar->RegisterChannel(nullptr, loadIdentifier);
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+ // Register listener (as an nsIParentChannel) under our new identifier.
+ rv = registrar->LinkChannels(loadIdentifier, listener, nullptr);
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+ }
+ return !!promise;
+}
+
+void DocumentLoadListener::CleanupParentLoadAttempt(uint64_t aLoadIdent) {
+ nsCOMPtr<nsIRedirectChannelRegistrar> registrar =
+ RedirectChannelRegistrar::GetOrCreate();
+
+ nsCOMPtr<nsIParentChannel> parentChannel;
+ registrar->GetParentChannel(aLoadIdent, getter_AddRefs(parentChannel));
+ RefPtr<DocumentLoadListener> loadListener = do_QueryObject(parentChannel);
+
+ if (loadListener) {
+ // If the load listener is still registered, then we must have failed
+ // to connect DocumentChannel into it. Better cancel it!
+ loadListener->NotifyDocumentChannelFailed();
+ }
+
+ registrar->DeregisterChannels(aLoadIdent);
+}
+
+auto DocumentLoadListener::ClaimParentLoad(DocumentLoadListener** aListener,
+ uint64_t aLoadIdent,
+ Maybe<uint64_t> aChannelId)
+ -> RefPtr<OpenPromise> {
+ nsCOMPtr<nsIRedirectChannelRegistrar> registrar =
+ RedirectChannelRegistrar::GetOrCreate();
+
+ nsCOMPtr<nsIParentChannel> parentChannel;
+ registrar->GetParentChannel(aLoadIdent, getter_AddRefs(parentChannel));
+ RefPtr<DocumentLoadListener> loadListener = do_QueryObject(parentChannel);
+ registrar->DeregisterChannels(aLoadIdent);
+
+ if (!loadListener) {
+ // The parent went away unexpectedly.
+ *aListener = nullptr;
+ return nullptr;
+ }
+
+ loadListener->mDocumentChannelId = aChannelId;
+
+ MOZ_DIAGNOSTIC_ASSERT(loadListener->mOpenPromise);
+ loadListener.forget(aListener);
+
+ return (*aListener)->mOpenPromise;
+}
+
+void DocumentLoadListener::NotifyDocumentChannelFailed() {
+ LOG(("DocumentLoadListener NotifyDocumentChannelFailed [this=%p]", this));
+ // There's been no calls to ClaimParentLoad, and so no listeners have been
+ // attached to mOpenPromise yet. As such we can run Then() on it.
+ mOpenPromise->Then(
+ GetMainThreadSerialEventTarget(), __func__,
+ [](DocumentLoadListener::OpenPromiseSucceededType&& aResolveValue) {
+ aResolveValue.mPromise->Resolve(NS_BINDING_ABORTED, __func__);
+ },
+ []() {});
+
+ Cancel(NS_BINDING_ABORTED,
+ "DocumentLoadListener::NotifyDocumentChannelFailed"_ns);
+}
+
+void DocumentLoadListener::Disconnect(bool aContinueNavigating) {
+ LOG(("DocumentLoadListener Disconnect [this=%p, aContinueNavigating=%d]",
+ this, aContinueNavigating));
+ // The nsHttpChannel may have a reference to this parent, release it
+ // to avoid circular references.
+ RefPtr<nsHttpChannel> httpChannelImpl = do_QueryObject(mChannel);
+ if (httpChannelImpl) {
+ httpChannelImpl->SetWarningReporter(nullptr);
+ httpChannelImpl->SetEarlyHintObserver(nullptr);
+ }
+
+ // Don't cancel ongoing early hints when continuing to load the web page.
+ // Early hints are loaded earlier in the code and shouldn't get cancelled
+ // here. See also: Bug 1765652
+ if (!aContinueNavigating) {
+ mEarlyHintsService.Cancel("DocumentLoadListener::Disconnect"_ns);
+ }
+
+ if (auto* ctx = GetDocumentBrowsingContext()) {
+ ctx->EndDocumentLoad(aContinueNavigating);
+ }
+}
+
+void DocumentLoadListener::Cancel(const nsresult& aStatusCode,
+ const nsACString& aReason) {
+ LOG(
+ ("DocumentLoadListener Cancel [this=%p, "
+ "aStatusCode=%" PRIx32 " ]",
+ this, static_cast<uint32_t>(aStatusCode)));
+ if (mOpenPromiseResolved) {
+ return;
+ }
+ if (mChannel) {
+ mChannel->CancelWithReason(aStatusCode, aReason);
+ }
+
+ DisconnectListeners(aStatusCode, aStatusCode);
+}
+
+void DocumentLoadListener::DisconnectListeners(nsresult aStatus,
+ nsresult aLoadGroupStatus,
+ bool aContinueNavigating) {
+ LOG(
+ ("DocumentLoadListener DisconnectListener [this=%p, "
+ "aStatus=%" PRIx32 ", aLoadGroupStatus=%" PRIx32
+ ", aContinueNavigating=%d]",
+ this, static_cast<uint32_t>(aStatus),
+ static_cast<uint32_t>(aLoadGroupStatus), aContinueNavigating));
+
+ RejectOpenPromise(aStatus, aLoadGroupStatus, aContinueNavigating, __func__);
+
+ Disconnect(aContinueNavigating);
+
+ // Clear any pending stream filter requests. If we're going to be sending a
+ // response to the content process due to a navigation, our caller will have
+ // already stashed the array to be passed to `TriggerRedirectToRealChannel`,
+ // so it's safe for us to clear here.
+ // TODO: If we retargeted the stream to a non-default handler (e.g. to trigger
+ // a download), we currently never attach a stream filter. Should we attach a
+ // stream filter in those situations as well?
+ mStreamFilterRequests.Clear();
+}
+
+void DocumentLoadListener::RedirectToRealChannelFinished(nsresult aRv) {
+ LOG(
+ ("DocumentLoadListener RedirectToRealChannelFinished [this=%p, "
+ "aRv=%" PRIx32 " ]",
+ this, static_cast<uint32_t>(aRv)));
+ if (NS_FAILED(aRv)) {
+ FinishReplacementChannelSetup(aRv);
+ return;
+ }
+
+ // Wait for background channel ready on target channel
+ nsCOMPtr<nsIRedirectChannelRegistrar> redirectReg =
+ RedirectChannelRegistrar::GetOrCreate();
+ MOZ_ASSERT(redirectReg);
+
+ nsCOMPtr<nsIParentChannel> redirectParentChannel;
+ redirectReg->GetParentChannel(mRedirectChannelId,
+ getter_AddRefs(redirectParentChannel));
+ if (!redirectParentChannel) {
+ FinishReplacementChannelSetup(NS_ERROR_FAILURE);
+ return;
+ }
+
+ nsCOMPtr<nsIParentRedirectingChannel> redirectingParent =
+ do_QueryInterface(redirectParentChannel);
+ if (!redirectingParent) {
+ // Continue verification procedure if redirecting to non-Http protocol
+ FinishReplacementChannelSetup(NS_OK);
+ return;
+ }
+
+ // Ask redirected channel if verification can proceed.
+ // ReadyToVerify will be invoked when redirected channel is ready.
+ redirectingParent->ContinueVerification(this);
+}
+
+NS_IMETHODIMP
+DocumentLoadListener::ReadyToVerify(nsresult aResultCode) {
+ FinishReplacementChannelSetup(aResultCode);
+ return NS_OK;
+}
+
+void DocumentLoadListener::FinishReplacementChannelSetup(nsresult aResult) {
+ LOG(
+ ("DocumentLoadListener FinishReplacementChannelSetup [this=%p, "
+ "aResult=%x]",
+ this, int(aResult)));
+
+ auto endDocumentLoad = MakeScopeExit([&]() {
+ if (auto* ctx = GetDocumentBrowsingContext()) {
+ ctx->EndDocumentLoad(false);
+ }
+ });
+ mStreamFilterRequests.Clear();
+
+ nsCOMPtr<nsIRedirectChannelRegistrar> registrar =
+ RedirectChannelRegistrar::GetOrCreate();
+ MOZ_ASSERT(registrar);
+
+ nsCOMPtr<nsIParentChannel> redirectChannel;
+ nsresult rv = registrar->GetParentChannel(mRedirectChannelId,
+ getter_AddRefs(redirectChannel));
+ if (NS_FAILED(rv) || !redirectChannel) {
+ aResult = NS_ERROR_FAILURE;
+ }
+
+ // Release all previously registered channels, they are no longer needed to
+ // be kept in the registrar from this moment.
+ registrar->DeregisterChannels(mRedirectChannelId);
+ mRedirectChannelId = 0;
+ if (NS_FAILED(aResult)) {
+ if (redirectChannel) {
+ redirectChannel->Delete();
+ }
+ mChannel->Cancel(aResult);
+ mChannel->Resume();
+ return;
+ }
+
+ MOZ_ASSERT(
+ !SameCOMIdentity(redirectChannel, static_cast<nsIParentChannel*>(this)));
+
+ redirectChannel->SetParentListener(mParentChannelListener);
+
+ ApplyPendingFunctions(redirectChannel);
+
+ if (!ResumeSuspendedChannel(redirectChannel)) {
+ nsCOMPtr<nsILoadGroup> loadGroup;
+ mChannel->GetLoadGroup(getter_AddRefs(loadGroup));
+ if (loadGroup) {
+ // We added ourselves to the load group, but attempting
+ // to resume has notified us that the channel is already
+ // finished. Better remove ourselves from the loadgroup
+ // again. The only time the channel will be in a loadgroup
+ // is if we're connected to the parent process.
+ nsresult status = NS_OK;
+ mChannel->GetStatus(&status);
+ loadGroup->RemoveRequest(mChannel, nullptr, status);
+ }
+ }
+}
+
+void DocumentLoadListener::ApplyPendingFunctions(
+ nsIParentChannel* aChannel) const {
+ // We stored the values from all nsIParentChannel functions called since we
+ // couldn't handle them. Copy them across to the real channel since it
+ // should know what to do.
+
+ nsCOMPtr<nsIParentChannel> parentChannel = aChannel;
+ for (const auto& variant : mIParentChannelFunctions) {
+ variant.match(
+ [parentChannel](const ClassifierMatchedInfoParams& aParams) {
+ parentChannel->SetClassifierMatchedInfo(
+ aParams.mList, aParams.mProvider, aParams.mFullHash);
+ },
+ [parentChannel](const ClassifierMatchedTrackingInfoParams& aParams) {
+ parentChannel->SetClassifierMatchedTrackingInfo(aParams.mLists,
+ aParams.mFullHashes);
+ },
+ [parentChannel](const ClassificationFlagsParams& aParams) {
+ parentChannel->NotifyClassificationFlags(aParams.mClassificationFlags,
+ aParams.mIsThirdParty);
+ });
+ }
+
+ RefPtr<HttpChannelSecurityWarningReporter> reporter;
+ if (RefPtr<HttpChannelParent> httpParent = do_QueryObject(aChannel)) {
+ reporter = httpParent;
+ } else if (RefPtr<nsHttpChannel> httpChannel = do_QueryObject(aChannel)) {
+ reporter = httpChannel->GetWarningReporter();
+ }
+ if (reporter) {
+ for (const auto& variant : mSecurityWarningFunctions) {
+ variant.match(
+ [reporter](const ReportSecurityMessageParams& aParams) {
+ Unused << reporter->ReportSecurityMessage(aParams.mMessageTag,
+ aParams.mMessageCategory);
+ },
+ [reporter](const LogBlockedCORSRequestParams& aParams) {
+ Unused << reporter->LogBlockedCORSRequest(
+ aParams.mMessage, aParams.mCategory, aParams.mIsWarning);
+ },
+ [reporter](const LogMimeTypeMismatchParams& aParams) {
+ Unused << reporter->LogMimeTypeMismatch(
+ aParams.mMessageName, aParams.mWarning, aParams.mURL,
+ aParams.mContentType);
+ });
+ }
+ }
+}
+
+bool DocumentLoadListener::ResumeSuspendedChannel(
+ nsIStreamListener* aListener) {
+ LOG(("DocumentLoadListener ResumeSuspendedChannel [this=%p]", this));
+ RefPtr<nsHttpChannel> httpChannel = do_QueryObject(mChannel);
+ if (httpChannel) {
+ httpChannel->SetApplyConversion(mOldApplyConversion);
+ }
+
+ if (!mIsFinished) {
+ mParentChannelListener->SetListenerAfterRedirect(aListener);
+ }
+
+ // If we failed to suspend the channel, then we might have received
+ // some messages while the redirected was being handled.
+ // Manually send them on now.
+ nsTArray<StreamListenerFunction> streamListenerFunctions =
+ std::move(mStreamListenerFunctions);
+ if (!aListener) {
+ streamListenerFunctions.Clear();
+ }
+
+ ForwardStreamListenerFunctions(std::move(streamListenerFunctions), aListener);
+
+ // We don't expect to get new stream listener functions added
+ // via re-entrancy. If this ever happens, we should understand
+ // exactly why before allowing it.
+ NS_ASSERTION(mStreamListenerFunctions.IsEmpty(),
+ "Should not have added new stream listener function!");
+
+ mChannel->Resume();
+
+ // Our caller will invoke `EndDocumentLoad` for us.
+
+ return !mIsFinished;
+}
+
+void DocumentLoadListener::CancelEarlyHintPreloads() {
+ mEarlyHintsService.Cancel("DocumentLoadListener::CancelEarlyHintPreloads"_ns);
+}
+
+void DocumentLoadListener::RegisterEarlyHintLinksAndGetConnectArgs(
+ dom::ContentParentId aCpId, nsTArray<EarlyHintConnectArgs>& aOutLinks) {
+ mEarlyHintsService.RegisterLinksAndGetConnectArgs(aCpId, aOutLinks);
+}
+
+void DocumentLoadListener::SerializeRedirectData(
+ RedirectToRealChannelArgs& aArgs, bool aIsCrossProcess,
+ uint32_t aRedirectFlags, uint32_t aLoadFlags,
+ nsTArray<EarlyHintConnectArgs>&& aEarlyHints,
+ uint32_t aEarlyHintLinkType) const {
+ aArgs.uri() = GetChannelCreationURI();
+ aArgs.loadIdentifier() = mLoadIdentifier;
+ aArgs.earlyHints() = std::move(aEarlyHints);
+ aArgs.earlyHintLinkType() = aEarlyHintLinkType;
+
+ // I previously used HttpBaseChannel::CloneLoadInfoForRedirect, but that
+ // clears the principal to inherit, which fails tests (probably because this
+ // 'redirect' is usually just an implementation detail). It's also http
+ // only, and mChannel can be anything that we redirected to.
+ nsCOMPtr<nsILoadInfo> channelLoadInfo = mChannel->LoadInfo();
+ nsCOMPtr<nsIPrincipal> principalToInherit;
+ channelLoadInfo->GetPrincipalToInherit(getter_AddRefs(principalToInherit));
+
+ const RefPtr<nsHttpChannel> baseChannel = do_QueryObject(mChannel);
+
+ nsCOMPtr<nsILoadContext> loadContext;
+ NS_QueryNotificationCallbacks(mChannel, loadContext);
+ nsCOMPtr<nsILoadInfo> redirectLoadInfo;
+
+ // Only use CloneLoadInfoForRedirect if we have a load context,
+ // since it internally tries to pull OriginAttributes from the
+ // the load context and asserts if they don't match the load info.
+ // We can end up without a load context if the channel has been aborted
+ // and the callbacks have been cleared.
+ if (baseChannel && loadContext) {
+ redirectLoadInfo = baseChannel->CloneLoadInfoForRedirect(
+ aArgs.uri(), nsIChannelEventSink::REDIRECT_INTERNAL);
+ redirectLoadInfo->SetResultPrincipalURI(aArgs.uri());
+
+ // The clone process clears this, and then we fail tests..
+ // docshell/test/mochitest/test_forceinheritprincipal_overrule_owner.html
+ if (principalToInherit) {
+ redirectLoadInfo->SetPrincipalToInherit(principalToInherit);
+ }
+ } else {
+ redirectLoadInfo =
+ static_cast<mozilla::net::LoadInfo*>(channelLoadInfo.get())->Clone();
+
+ redirectLoadInfo->AppendRedirectHistoryEntry(mChannel, true);
+ }
+
+ const Maybe<ClientInfo>& reservedClientInfo =
+ channelLoadInfo->GetReservedClientInfo();
+ if (reservedClientInfo) {
+ redirectLoadInfo->SetReservedClientInfo(*reservedClientInfo);
+ }
+
+ aArgs.registrarId() = mRedirectChannelId;
+
+#ifdef DEBUG
+ // We only set the granularFingerprintingProtection field when opening http
+ // channels. So, we mark the field as set here if the channel is not a
+ // nsHTTPChannel to pass the assertion check for getting this field in below
+ // LoadInfoToLoadInfoArgs() call.
+ if (!baseChannel) {
+ static_cast<mozilla::net::LoadInfo*>(redirectLoadInfo.get())
+ ->MarkOverriddenFingerprintingSettingsAsSet();
+ }
+#endif
+
+ MOZ_ALWAYS_SUCCEEDS(
+ ipc::LoadInfoToLoadInfoArgs(redirectLoadInfo, &aArgs.loadInfo()));
+
+ mChannel->GetOriginalURI(getter_AddRefs(aArgs.originalURI()));
+
+ // mChannel can be a nsHttpChannel as well as InterceptedHttpChannel so we
+ // can't use baseChannel here.
+ if (nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel)) {
+ MOZ_ALWAYS_SUCCEEDS(httpChannel->GetChannelId(&aArgs.channelId()));
+ }
+
+ aArgs.redirectMode() = nsIHttpChannelInternal::REDIRECT_MODE_FOLLOW;
+ nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal =
+ do_QueryInterface(mChannel);
+ if (httpChannelInternal) {
+ MOZ_ALWAYS_SUCCEEDS(
+ httpChannelInternal->GetRedirectMode(&aArgs.redirectMode()));
+ }
+
+ if (baseChannel) {
+ aArgs.init() =
+ Some(baseChannel
+ ->CloneReplacementChannelConfig(
+ true, aRedirectFlags,
+ HttpBaseChannel::ReplacementReason::DocumentChannel)
+ .Serialize());
+ }
+
+ uint32_t contentDispositionTemp;
+ nsresult rv = mChannel->GetContentDisposition(&contentDispositionTemp);
+ if (NS_SUCCEEDED(rv)) {
+ aArgs.contentDisposition() = Some(contentDispositionTemp);
+ }
+
+ nsString contentDispositionFilenameTemp;
+ rv = mChannel->GetContentDispositionFilename(contentDispositionFilenameTemp);
+ if (NS_SUCCEEDED(rv)) {
+ aArgs.contentDispositionFilename() = Some(contentDispositionFilenameTemp);
+ }
+
+ SetNeedToAddURIVisit(mChannel, false);
+
+ aArgs.newLoadFlags() = aLoadFlags;
+ aArgs.redirectFlags() = aRedirectFlags;
+ aArgs.properties() = do_QueryObject(mChannel);
+ aArgs.srcdocData() = mSrcdocData;
+ aArgs.baseUri() = mBaseURI;
+ aArgs.loadStateExternalLoadFlags() = mLoadStateExternalLoadFlags;
+ aArgs.loadStateInternalLoadFlags() = mLoadStateInternalLoadFlags;
+ aArgs.loadStateLoadType() = mLoadStateLoadType;
+ aArgs.originalUriString() = mOriginalUriString;
+ if (mLoadingSessionHistoryInfo) {
+ aArgs.loadingSessionHistoryInfo().emplace(*mLoadingSessionHistoryInfo);
+ }
+}
+
+static bool IsFirstLoadInWindow(nsIChannel* aChannel) {
+ if (nsCOMPtr<nsIPropertyBag2> props = do_QueryInterface(aChannel)) {
+ bool tmp = false;
+ nsresult rv =
+ props->GetPropertyAsBool(u"docshell.newWindowTarget"_ns, &tmp);
+ return NS_SUCCEEDED(rv) && tmp;
+ }
+ return false;
+}
+
+// Get where the document loaded by this nsIChannel should be rendered. This
+// will be `OPEN_CURRENTWINDOW` unless we're loading an attachment which would
+// normally open in an external program, but we're instead choosing to render
+// internally.
+static int32_t GetWhereToOpen(nsIChannel* aChannel, bool aIsDocumentLoad) {
+ // Ignore content disposition for loads from an object or embed element.
+ if (!aIsDocumentLoad) {
+ return nsIBrowserDOMWindow::OPEN_CURRENTWINDOW;
+ }
+
+ // Always continue in the same window if we're not loading an attachment.
+ uint32_t disposition = nsIChannel::DISPOSITION_INLINE;
+ if (NS_FAILED(aChannel->GetContentDisposition(&disposition)) ||
+ disposition != nsIChannel::DISPOSITION_ATTACHMENT) {
+ return nsIBrowserDOMWindow::OPEN_CURRENTWINDOW;
+ }
+
+ // If the channel is for a new window target, continue in the same window.
+ if (IsFirstLoadInWindow(aChannel)) {
+ return nsIBrowserDOMWindow::OPEN_CURRENTWINDOW;
+ }
+
+ // Respect the user's preferences with browser.link.open_newwindow
+ // FIXME: There should probably be a helper for this, as the logic is
+ // duplicated in a few places.
+ int32_t where = Preferences::GetInt("browser.link.open_newwindow",
+ nsIBrowserDOMWindow::OPEN_NEWTAB);
+ if (where == nsIBrowserDOMWindow::OPEN_CURRENTWINDOW ||
+ where == nsIBrowserDOMWindow::OPEN_NEWWINDOW ||
+ where == nsIBrowserDOMWindow::OPEN_NEWTAB) {
+ return where;
+ }
+ // NOTE: nsIBrowserDOMWindow::OPEN_NEWTAB_BACKGROUND is not allowed as a pref
+ // value.
+ return nsIBrowserDOMWindow::OPEN_NEWTAB;
+}
+
+static DocumentLoadListener::ProcessBehavior GetProcessSwitchBehavior(
+ Element* aBrowserElement) {
+ if (aBrowserElement->HasAttribute(u"maychangeremoteness"_ns)) {
+ return DocumentLoadListener::ProcessBehavior::PROCESS_BEHAVIOR_STANDARD;
+ }
+ nsCOMPtr<nsIBrowser> browser = aBrowserElement->AsBrowser();
+ bool isRemoteBrowser = false;
+ browser->GetIsRemoteBrowser(&isRemoteBrowser);
+ if (isRemoteBrowser) {
+ return DocumentLoadListener::ProcessBehavior::
+ PROCESS_BEHAVIOR_SUBFRAME_ONLY;
+ }
+ return DocumentLoadListener::ProcessBehavior::PROCESS_BEHAVIOR_DISABLED;
+}
+
+static bool ContextCanProcessSwitch(CanonicalBrowsingContext* aBrowsingContext,
+ WindowGlobalParent* aParentWindow,
+ bool aSwitchToNewTab) {
+ if (NS_WARN_IF(!aBrowsingContext)) {
+ MOZ_LOG(gProcessIsolationLog, LogLevel::Warning,
+ ("Process Switch Abort: no browsing context"));
+ return false;
+ }
+ if (!aBrowsingContext->IsContent()) {
+ MOZ_LOG(gProcessIsolationLog, LogLevel::Warning,
+ ("Process Switch Abort: non-content browsing context"));
+ return false;
+ }
+
+ // If we're switching into a new tab, we can skip the remaining checks, as
+ // we're not actually changing the process of aBrowsingContext, so whether or
+ // not it is allowed to process switch isn't relevant.
+ if (aSwitchToNewTab) {
+ return true;
+ }
+
+ if (aParentWindow && !aBrowsingContext->UseRemoteSubframes()) {
+ MOZ_LOG(gProcessIsolationLog, LogLevel::Warning,
+ ("Process Switch Abort: remote subframes disabled"));
+ return false;
+ }
+
+ if (aParentWindow && aParentWindow->IsInProcess()) {
+ MOZ_LOG(gProcessIsolationLog, LogLevel::Warning,
+ ("Process Switch Abort: Subframe with in-process parent"));
+ return false;
+ }
+
+ // Determine what process switching behaviour is being requested by the root
+ // <browser> element.
+ Element* browserElement = aBrowsingContext->Top()->GetEmbedderElement();
+ if (!browserElement) {
+ MOZ_LOG(gProcessIsolationLog, LogLevel::Warning,
+ ("Process Switch Abort: cannot get embedder element"));
+ return false;
+ }
+ nsCOMPtr<nsIBrowser> browser = browserElement->AsBrowser();
+ if (!browser) {
+ MOZ_LOG(gProcessIsolationLog, LogLevel::Warning,
+ ("Process Switch Abort: not loaded within nsIBrowser"));
+ return false;
+ }
+
+ DocumentLoadListener::ProcessBehavior processBehavior =
+ GetProcessSwitchBehavior(browserElement);
+
+ // Check if the process switch we're considering is disabled by the
+ // <browser>'s process behavior.
+ if (processBehavior ==
+ DocumentLoadListener::ProcessBehavior::PROCESS_BEHAVIOR_DISABLED) {
+ MOZ_LOG(gProcessIsolationLog, LogLevel::Warning,
+ ("Process Switch Abort: switch disabled by <browser>"));
+ return false;
+ }
+ if (!aParentWindow && processBehavior ==
+ DocumentLoadListener::ProcessBehavior::
+ PROCESS_BEHAVIOR_SUBFRAME_ONLY) {
+ MOZ_LOG(gProcessIsolationLog, LogLevel::Warning,
+ ("Process Switch Abort: toplevel switch disabled by <browser>"));
+ return false;
+ }
+
+ return true;
+}
+
+static RefPtr<dom::BrowsingContextCallbackReceivedPromise> SwitchToNewTab(
+ CanonicalBrowsingContext* aLoadingBrowsingContext, int32_t aWhere) {
+ MOZ_ASSERT(aWhere == nsIBrowserDOMWindow::OPEN_NEWTAB ||
+ aWhere == nsIBrowserDOMWindow::OPEN_NEWTAB_BACKGROUND ||
+ aWhere == nsIBrowserDOMWindow::OPEN_NEWWINDOW,
+ "Unsupported open location");
+
+ auto promise =
+ MakeRefPtr<dom::BrowsingContextCallbackReceivedPromise::Private>(
+ __func__);
+
+ // Get the nsIBrowserDOMWindow for the given BrowsingContext's tab.
+ nsCOMPtr<nsIBrowserDOMWindow> browserDOMWindow =
+ aLoadingBrowsingContext->GetBrowserDOMWindow();
+ if (NS_WARN_IF(!browserDOMWindow)) {
+ MOZ_LOG(gProcessIsolationLog, LogLevel::Warning,
+ ("Process Switch Abort: Unable to get nsIBrowserDOMWindow"));
+ promise->Reject(NS_ERROR_FAILURE, __func__);
+ return promise;
+ }
+
+ // Open a new content tab by calling into frontend. We don't need to worry
+ // about the triggering principal or CSP, as createContentWindow doesn't
+ // actually start loading anything, but use a null principal anyway in case
+ // something changes.
+ nsCOMPtr<nsIPrincipal> triggeringPrincipal =
+ NullPrincipal::Create(aLoadingBrowsingContext->OriginAttributesRef());
+
+ RefPtr<nsOpenWindowInfo> openInfo = new nsOpenWindowInfo();
+ openInfo->mBrowsingContextReadyCallback =
+ new nsBrowsingContextReadyCallback(promise);
+ openInfo->mOriginAttributes = aLoadingBrowsingContext->OriginAttributesRef();
+ openInfo->mParent = aLoadingBrowsingContext;
+ openInfo->mForceNoOpener = true;
+ openInfo->mIsRemote = true;
+
+ // Do the actual work to open a new tab or window async.
+ nsresult rv = NS_DispatchToMainThread(NS_NewRunnableFunction(
+ "DocumentLoadListener::SwitchToNewTab",
+ [browserDOMWindow, openInfo, aWhere, triggeringPrincipal, promise] {
+ RefPtr<BrowsingContext> bc;
+ nsresult rv = browserDOMWindow->CreateContentWindow(
+ /* uri */ nullptr, openInfo, aWhere,
+ nsIBrowserDOMWindow::OPEN_NO_REFERRER, triggeringPrincipal,
+ /* csp */ nullptr, getter_AddRefs(bc));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ MOZ_LOG(gProcessIsolationLog, LogLevel::Warning,
+ ("Process Switch Abort: CreateContentWindow threw"));
+ promise->Reject(rv, __func__);
+ }
+ if (bc) {
+ promise->Resolve(bc, __func__);
+ }
+ }));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ promise->Reject(NS_ERROR_UNEXPECTED, __func__);
+ }
+ return promise;
+}
+
+bool DocumentLoadListener::MaybeTriggerProcessSwitch(
+ bool* aWillSwitchToRemote) {
+ MOZ_ASSERT(XRE_IsParentProcess());
+ MOZ_DIAGNOSTIC_ASSERT(mChannel);
+ MOZ_DIAGNOSTIC_ASSERT(mParentChannelListener);
+ MOZ_DIAGNOSTIC_ASSERT(aWillSwitchToRemote);
+
+ MOZ_LOG(gProcessIsolationLog, LogLevel::Verbose,
+ ("DocumentLoadListener MaybeTriggerProcessSwitch [this=%p, uri=%s, "
+ "browserid=%" PRIx64 "]",
+ this, GetChannelCreationURI()->GetSpecOrDefault().get(),
+ GetLoadingBrowsingContext()->Top()->BrowserId()));
+
+ // If we're doing an <object>/<embed> load, we may be doing a document load at
+ // this point. We never need to do a process switch for a non-document
+ // <object> or <embed> load.
+ if (!mIsDocumentLoad) {
+ if (!mChannel->IsDocument()) {
+ MOZ_LOG(gProcessIsolationLog, LogLevel::Verbose,
+ ("Process Switch Abort: non-document load"));
+ return false;
+ }
+ nsresult status;
+ if (!nsObjectLoadingContent::IsSuccessfulRequest(mChannel, &status)) {
+ MOZ_LOG(gProcessIsolationLog, LogLevel::Verbose,
+ ("Process Switch Abort: error page"));
+ return false;
+ }
+ }
+
+ // Check if we should handle this load in a different tab or window.
+ int32_t where = GetWhereToOpen(mChannel, mIsDocumentLoad);
+ bool switchToNewTab = where != nsIBrowserDOMWindow::OPEN_CURRENTWINDOW;
+
+ // Get the loading BrowsingContext. This may not be the context which will be
+ // switching processes when switching to a new tab, and in the case of an
+ // <object> or <embed> element, as we don't create the final context until
+ // after process selection.
+ //
+ // - /!\ WARNING /!\ -
+ // Don't use `browsingContext->IsTop()` in this method! It will behave
+ // incorrectly for non-document loads such as `<object>` or `<embed>`.
+ // Instead, check whether or not `parentWindow` is null.
+ RefPtr<CanonicalBrowsingContext> browsingContext =
+ GetLoadingBrowsingContext();
+ // If switching to a new tab, the final BC isn't a frame.
+ RefPtr<WindowGlobalParent> parentWindow =
+ switchToNewTab ? nullptr : GetParentWindowContext();
+ if (!ContextCanProcessSwitch(browsingContext, parentWindow, switchToNewTab)) {
+ return false;
+ }
+
+ if (!browsingContext->IsOwnedByProcess(GetContentProcessId(mContentParent))) {
+ MOZ_LOG(gProcessIsolationLog, LogLevel::Error,
+ ("Process Switch Abort: context no longer owned by creator"));
+ Cancel(NS_BINDING_ABORTED,
+ "Process Switch Abort: context no longer owned by creator"_ns);
+ return false;
+ }
+
+ if (browsingContext->IsReplaced()) {
+ MOZ_LOG(gProcessIsolationLog, LogLevel::Warning,
+ ("Process Switch Abort: replaced browsing context"));
+ Cancel(NS_BINDING_ABORTED,
+ "Process Switch Abort: replaced browsing context"_ns);
+ return false;
+ }
+
+ nsAutoCString currentRemoteType(NOT_REMOTE_TYPE);
+ if (mContentParent) {
+ currentRemoteType = mContentParent->GetRemoteType();
+ }
+
+ auto optionsResult = IsolationOptionsForNavigation(
+ browsingContext->Top(), switchToNewTab ? nullptr : parentWindow.get(),
+ GetChannelCreationURI(), mChannel, currentRemoteType,
+ HasCrossOriginOpenerPolicyMismatch(), switchToNewTab, mLoadStateLoadType,
+ mDocumentChannelId, mRemoteTypeOverride);
+ if (optionsResult.isErr()) {
+ MOZ_LOG(gProcessIsolationLog, LogLevel::Error,
+ ("Process Switch Abort: CheckIsolationForNavigation Failed with %s",
+ GetStaticErrorName(optionsResult.inspectErr())));
+ Cancel(optionsResult.unwrapErr(),
+ "Process Switch Abort: CheckIsolationForNavigation Failed"_ns);
+ return false;
+ }
+
+ NavigationIsolationOptions options = optionsResult.unwrap();
+
+ if (options.mTryUseBFCache) {
+ MOZ_ASSERT(!parentWindow, "Can only BFCache toplevel windows");
+ MOZ_ASSERT(!switchToNewTab, "Can't BFCache for a tab switch");
+ bool sameOrigin = false;
+ if (auto* wgp = browsingContext->GetCurrentWindowGlobal()) {
+ nsCOMPtr<nsIPrincipal> resultPrincipal;
+ MOZ_ALWAYS_SUCCEEDS(
+ nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
+ mChannel, getter_AddRefs(resultPrincipal)));
+ sameOrigin =
+ wgp->DocumentPrincipal()->EqualsConsideringDomain(resultPrincipal);
+ }
+
+ // We only reset the window name for content.
+ mLoadingSessionHistoryInfo->mForceMaybeResetName.emplace(
+ StaticPrefs::privacy_window_name_update_enabled() &&
+ browsingContext->IsContent() && !sameOrigin);
+ }
+
+ MOZ_LOG(
+ gProcessIsolationLog, LogLevel::Verbose,
+ ("CheckIsolationForNavigation -> current:(%s) remoteType:(%s) replace:%d "
+ "group:%" PRIx64 " bfcache:%d shentry:%p newTab:%d",
+ currentRemoteType.get(), options.mRemoteType.get(),
+ options.mReplaceBrowsingContext, options.mSpecificGroupId,
+ options.mTryUseBFCache, options.mActiveSessionHistoryEntry.get(),
+ switchToNewTab));
+
+ // Check if a process switch is needed.
+ if (currentRemoteType == options.mRemoteType &&
+ !options.mReplaceBrowsingContext && !switchToNewTab) {
+ MOZ_LOG(gProcessIsolationLog, LogLevel::Info,
+ ("Process Switch Abort: type (%s) is compatible",
+ options.mRemoteType.get()));
+ return false;
+ }
+
+ if (NS_WARN_IF(parentWindow && options.mRemoteType.IsEmpty())) {
+ MOZ_LOG(gProcessIsolationLog, LogLevel::Error,
+ ("Process Switch Abort: non-remote target process for subframe"));
+ return false;
+ }
+
+ *aWillSwitchToRemote = !options.mRemoteType.IsEmpty();
+
+ // If we've decided to re-target this load into a new tab or window (see
+ // `GetWhereToOpen`), do so before performing a process switch. This will
+ // require creating the new <browser> to load in, which may be performed
+ // async.
+ if (switchToNewTab) {
+ SwitchToNewTab(browsingContext, where)
+ ->Then(
+ GetMainThreadSerialEventTarget(), __func__,
+ [self = RefPtr{this},
+ options](const RefPtr<BrowsingContext>& aBrowsingContext) mutable {
+ if (aBrowsingContext->IsDiscarded()) {
+ MOZ_LOG(
+ gProcessIsolationLog, LogLevel::Error,
+ ("Process Switch: Got invalid new-tab BrowsingContext"));
+ self->RedirectToRealChannelFinished(NS_ERROR_FAILURE);
+ return;
+ }
+
+ MOZ_LOG(gProcessIsolationLog, LogLevel::Verbose,
+ ("Process Switch: Redirected load to new tab"));
+ self->TriggerProcessSwitch(aBrowsingContext->Canonical(), options,
+ /* aIsNewTab */ true);
+ },
+ [self = RefPtr{this}](const CopyableErrorResult&) {
+ MOZ_LOG(gProcessIsolationLog, LogLevel::Error,
+ ("Process Switch: SwitchToNewTab failed"));
+ self->RedirectToRealChannelFinished(NS_ERROR_FAILURE);
+ });
+ return true;
+ }
+
+ // If we're doing a document load, we can immediately perform a process
+ // switch.
+ if (mIsDocumentLoad) {
+ TriggerProcessSwitch(browsingContext, options);
+ return true;
+ }
+
+ // We're not doing a document load, which means we must be performing an
+ // object load. We need a BrowsingContext to perform the switch in, so will
+ // trigger an upgrade.
+ if (!mObjectUpgradeHandler) {
+ MOZ_LOG(gProcessIsolationLog, LogLevel::Warning,
+ ("Process Switch Abort: no object upgrade handler"));
+ return false;
+ }
+
+ if (!StaticPrefs::fission_remoteObjectEmbed()) {
+ MOZ_LOG(gProcessIsolationLog, LogLevel::Verbose,
+ ("Process Switch Abort: remote <object>/<embed> disabled"));
+ return false;
+ }
+
+ mObjectUpgradeHandler->UpgradeObjectLoad()->Then(
+ GetMainThreadSerialEventTarget(), __func__,
+ [self = RefPtr{this}, options, parentWindow](
+ const RefPtr<CanonicalBrowsingContext>& aBrowsingContext) mutable {
+ if (aBrowsingContext->IsDiscarded() ||
+ parentWindow != aBrowsingContext->GetParentWindowContext()) {
+ MOZ_LOG(gProcessIsolationLog, LogLevel::Error,
+ ("Process Switch: Got invalid BrowsingContext from object "
+ "upgrade!"));
+ self->RedirectToRealChannelFinished(NS_ERROR_FAILURE);
+ return;
+ }
+
+ MOZ_LOG(gProcessIsolationLog, LogLevel::Verbose,
+ ("Process Switch: Upgraded Object to Document Load"));
+ self->TriggerProcessSwitch(aBrowsingContext, options);
+ },
+ [self = RefPtr{this}](nsresult aStatusCode) {
+ MOZ_ASSERT(NS_FAILED(aStatusCode), "Status should be error");
+ self->RedirectToRealChannelFinished(aStatusCode);
+ });
+ return true;
+}
+
+void DocumentLoadListener::TriggerProcessSwitch(
+ CanonicalBrowsingContext* aContext,
+ const NavigationIsolationOptions& aOptions, bool aIsNewTab) {
+ MOZ_DIAGNOSTIC_ASSERT(aIsNewTab || aContext->IsOwnedByProcess(
+ GetContentProcessId(mContentParent)),
+ "not owned by creator process anymore?");
+ if (MOZ_LOG_TEST(gProcessIsolationLog, LogLevel::Info)) {
+ nsCString currentRemoteType = "INVALID"_ns;
+ aContext->GetCurrentRemoteType(currentRemoteType, IgnoreErrors());
+
+ MOZ_LOG(gProcessIsolationLog, LogLevel::Info,
+ ("Process Switch: Changing Remoteness from '%s' to '%s'",
+ currentRemoteType.get(), aOptions.mRemoteType.get()));
+ }
+
+ // Stash our stream filter requests to pass to TriggerRedirectToRealChannel,
+ // as the call to `DisconnectListeners` will clear our list.
+ nsTArray<StreamFilterRequest> streamFilterRequests =
+ std::move(mStreamFilterRequests);
+
+ // We're now committing to a process switch, so we can disconnect from
+ // the listeners in the old process.
+ // As the navigation is continuing, we don't actually want to cancel the
+ // request in the old process unless we're redirecting the load into a new
+ // tab.
+ DisconnectListeners(NS_BINDING_ABORTED, NS_BINDING_ABORTED, !aIsNewTab);
+
+ MOZ_LOG(gProcessIsolationLog, LogLevel::Verbose,
+ ("Process Switch: Calling ChangeRemoteness"));
+ aContext->ChangeRemoteness(aOptions, mLoadIdentifier)
+ ->Then(
+ GetMainThreadSerialEventTarget(), __func__,
+ [self = RefPtr{this}, requests = std::move(streamFilterRequests)](
+ BrowserParent* aBrowserParent) mutable {
+ MOZ_ASSERT(self->mChannel,
+ "Something went wrong, channel got cancelled");
+ self->TriggerRedirectToRealChannel(
+ Some(aBrowserParent ? aBrowserParent->Manager() : nullptr),
+ std::move(requests));
+ },
+ [self = RefPtr{this}](nsresult aStatusCode) {
+ MOZ_ASSERT(NS_FAILED(aStatusCode), "Status should be error");
+ self->RedirectToRealChannelFinished(aStatusCode);
+ });
+}
+
+RefPtr<PDocumentChannelParent::RedirectToRealChannelPromise>
+DocumentLoadListener::RedirectToParentProcess(uint32_t aRedirectFlags,
+ uint32_t aLoadFlags) {
+ // This is largely the same as ContentChild::RecvCrossProcessRedirect,
+ // except without needing to deserialize or create an nsIChildChannel.
+
+ RefPtr<nsDocShellLoadState> loadState;
+ nsDocShellLoadState::CreateFromPendingChannel(
+ mChannel, mLoadIdentifier, mRedirectChannelId, getter_AddRefs(loadState));
+
+ loadState->SetLoadFlags(mLoadStateExternalLoadFlags);
+ loadState->SetInternalLoadFlags(mLoadStateInternalLoadFlags);
+ loadState->SetLoadType(mLoadStateLoadType);
+ if (mLoadingSessionHistoryInfo) {
+ loadState->SetLoadingSessionHistoryInfo(*mLoadingSessionHistoryInfo);
+ }
+
+ // This is poorly named now.
+ RefPtr<ChildProcessChannelListener> processListener =
+ ChildProcessChannelListener::GetSingleton();
+
+ auto promise =
+ MakeRefPtr<PDocumentChannelParent::RedirectToRealChannelPromise::Private>(
+ __func__);
+ promise->UseDirectTaskDispatch(__func__);
+ auto resolve = [promise](nsresult aResult) {
+ promise->Resolve(aResult, __func__);
+ };
+
+ nsTArray<ipc::Endpoint<extensions::PStreamFilterParent>> endpoints;
+ processListener->OnChannelReady(loadState, mLoadIdentifier,
+ std::move(endpoints), mTiming,
+ std::move(resolve));
+
+ return promise;
+}
+
+RefPtr<PDocumentChannelParent::RedirectToRealChannelPromise>
+DocumentLoadListener::RedirectToRealChannel(
+ uint32_t aRedirectFlags, uint32_t aLoadFlags,
+ const Maybe<ContentParent*>& aDestinationProcess,
+ nsTArray<ParentEndpoint>&& aStreamFilterEndpoints) {
+ LOG(
+ ("DocumentLoadListener RedirectToRealChannel [this=%p] "
+ "aRedirectFlags=%" PRIx32 ", aLoadFlags=%" PRIx32,
+ this, aRedirectFlags, aLoadFlags));
+
+ if (mIsDocumentLoad) {
+ // TODO(djg): Add the last URI visit to history if success. Is there a
+ // better place to handle this? Need access to the updated aLoadFlags.
+ nsresult status = NS_OK;
+ mChannel->GetStatus(&status);
+ bool updateGHistory =
+ nsDocShell::ShouldUpdateGlobalHistory(mLoadStateLoadType);
+ if (NS_SUCCEEDED(status) && updateGHistory &&
+ !net::ChannelIsPost(mChannel)) {
+ AddURIVisit(mChannel, aLoadFlags);
+ }
+ }
+
+ // Register the new channel and obtain id for it
+ nsCOMPtr<nsIRedirectChannelRegistrar> registrar =
+ RedirectChannelRegistrar::GetOrCreate();
+ MOZ_ASSERT(registrar);
+ nsCOMPtr<nsIChannel> chan = mChannel;
+ if (nsCOMPtr<nsIViewSourceChannel> vsc = do_QueryInterface(chan)) {
+ chan = vsc->GetInnerChannel();
+ }
+ mRedirectChannelId = nsContentUtils::GenerateLoadIdentifier();
+ MOZ_ALWAYS_SUCCEEDS(registrar->RegisterChannel(chan, mRedirectChannelId));
+
+ if (aDestinationProcess) {
+ RefPtr<ContentParent> cp = *aDestinationProcess;
+ if (!cp) {
+ MOZ_ASSERT(aStreamFilterEndpoints.IsEmpty());
+ return RedirectToParentProcess(aRedirectFlags, aLoadFlags);
+ }
+
+ if (!cp->CanSend()) {
+ return PDocumentChannelParent::RedirectToRealChannelPromise::
+ CreateAndReject(ipc::ResponseRejectReason::SendError, __func__);
+ }
+
+ nsTArray<EarlyHintConnectArgs> ehArgs;
+ mEarlyHintsService.RegisterLinksAndGetConnectArgs(cp->ChildID(), ehArgs);
+
+ RedirectToRealChannelArgs args;
+ SerializeRedirectData(args, /* aIsCrossProcess */ true, aRedirectFlags,
+ aLoadFlags, std::move(ehArgs),
+ mEarlyHintsService.LinkType());
+ if (mTiming) {
+ mTiming->Anonymize(args.uri());
+ args.timing() = std::move(mTiming);
+ }
+
+ cp->TransmitBlobDataIfBlobURL(args.uri());
+
+ if (CanonicalBrowsingContext* bc = GetDocumentBrowsingContext()) {
+ if (bc->IsTop() && bc->IsActive()) {
+ nsContentUtils::RequestGeckoTaskBurst();
+ }
+ }
+
+ return cp->SendCrossProcessRedirect(args,
+ std::move(aStreamFilterEndpoints));
+ }
+
+ if (mOpenPromiseResolved) {
+ LOG(
+ ("DocumentLoadListener RedirectToRealChannel [this=%p] "
+ "promise already resolved. Aborting.",
+ this));
+ // The promise has already been resolved or aborted, so we have no way to
+ // return a promise again to the listener which would cancel the operation.
+ // Reject the promise immediately.
+ return PDocumentChannelParent::RedirectToRealChannelPromise::
+ CreateAndResolve(NS_BINDING_ABORTED, __func__);
+ }
+
+ // This promise will be passed on the promise listener which will
+ // resolve this promise for us.
+ auto promise =
+ MakeRefPtr<PDocumentChannelParent::RedirectToRealChannelPromise::Private>(
+ __func__);
+
+ mOpenPromise->Resolve(
+ OpenPromiseSucceededType({std::move(aStreamFilterEndpoints),
+ aRedirectFlags, aLoadFlags,
+ mEarlyHintsService.LinkType(), promise}),
+ __func__);
+
+ // There is no way we could come back here if the promise had been resolved
+ // previously. But for clarity and to avoid all doubt, we set this boolean to
+ // true.
+ mOpenPromiseResolved = true;
+
+ return promise;
+}
+
+void DocumentLoadListener::TriggerRedirectToRealChannel(
+ const Maybe<ContentParent*>& aDestinationProcess,
+ nsTArray<StreamFilterRequest> aStreamFilterRequests) {
+ LOG((
+ "DocumentLoadListener::TriggerRedirectToRealChannel [this=%p] "
+ "aDestinationProcess=%" PRId64,
+ this, aDestinationProcess ? int64_t(*aDestinationProcess) : int64_t(-1)));
+ // This initiates replacing the current DocumentChannel with a
+ // protocol specific 'real' channel, maybe in a different process than
+ // the current DocumentChannelChild, if aDestinationProces is set.
+ // It registers the current mChannel with the registrar to get an ID
+ // so that the remote end can setup a new IPDL channel and lookup
+ // the same underlying channel.
+ // We expect this process to finish with FinishReplacementChannelSetup
+ // (for both in-process and process switch cases), where we cleanup
+ // the registrar and copy across any needed state to the replacing
+ // IPDL parent object.
+
+ nsTArray<ParentEndpoint> parentEndpoints(aStreamFilterRequests.Length());
+ if (!aStreamFilterRequests.IsEmpty()) {
+ ContentParent* cp = aDestinationProcess.valueOr(mContentParent);
+ base::ProcessId pid = cp ? cp->OtherPid() : base::ProcessId{0};
+
+ for (StreamFilterRequest& request : aStreamFilterRequests) {
+ if (!pid) {
+ request.mPromise->Reject(false, __func__);
+ request.mPromise = nullptr;
+ continue;
+ }
+ ParentEndpoint parent;
+ nsresult rv = extensions::PStreamFilter::CreateEndpoints(
+ &parent, &request.mChildEndpoint);
+
+ if (NS_FAILED(rv)) {
+ request.mPromise->Reject(false, __func__);
+ request.mPromise = nullptr;
+ } else {
+ parentEndpoints.AppendElement(std::move(parent));
+ }
+ }
+ }
+
+ // If we didn't have any redirects, then we pass the REDIRECT_INTERNAL flag
+ // for this channel switch so that it isn't recorded in session history etc.
+ // If there were redirect(s), then we want this switch to be recorded as a
+ // real one, since we have a new URI.
+ uint32_t redirectFlags = 0;
+ if (!mHaveVisibleRedirect) {
+ redirectFlags = nsIChannelEventSink::REDIRECT_INTERNAL;
+ }
+
+ uint32_t newLoadFlags = nsIRequest::LOAD_NORMAL;
+ MOZ_ALWAYS_SUCCEEDS(mChannel->GetLoadFlags(&newLoadFlags));
+ // We're pulling our flags from the inner channel, which may not have this
+ // flag set on it. This is the case when loading a 'view-source' channel.
+ if (mIsDocumentLoad || aDestinationProcess) {
+ newLoadFlags |= nsIChannel::LOAD_DOCUMENT_URI;
+ }
+ if (!aDestinationProcess) {
+ newLoadFlags |= nsIChannel::LOAD_REPLACE;
+ }
+
+ // INHIBIT_PERSISTENT_CACHING is clearing during http redirects (from
+ // both parent and content process channel instances), but only ever
+ // re-added to the parent-side nsHttpChannel.
+ // To match that behaviour, we want to explicitly avoid copying this flag
+ // back to our newly created content side channel, otherwise it can
+ // affect sub-resources loads in the same load group.
+ nsCOMPtr<nsIURI> uri;
+ mChannel->GetURI(getter_AddRefs(uri));
+ if (uri && uri->SchemeIs("https")) {
+ newLoadFlags &= ~nsIRequest::INHIBIT_PERSISTENT_CACHING;
+ }
+
+ RefPtr<DocumentLoadListener> self = this;
+ RedirectToRealChannel(redirectFlags, newLoadFlags, aDestinationProcess,
+ std::move(parentEndpoints))
+ ->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [self, requests = std::move(aStreamFilterRequests)](
+ const nsresult& aResponse) mutable {
+ for (StreamFilterRequest& request : requests) {
+ if (request.mPromise) {
+ request.mPromise->Resolve(std::move(request.mChildEndpoint),
+ __func__);
+ request.mPromise = nullptr;
+ }
+ }
+ self->RedirectToRealChannelFinished(aResponse);
+ },
+ [self](const mozilla::ipc::ResponseRejectReason) {
+ self->RedirectToRealChannelFinished(NS_ERROR_FAILURE);
+ });
+}
+
+void DocumentLoadListener::MaybeReportBlockedByURLClassifier(nsresult aStatus) {
+ auto* browsingContext = GetDocumentBrowsingContext();
+ if (!browsingContext || browsingContext->IsTop() ||
+ !StaticPrefs::privacy_trackingprotection_testing_report_blocked_node()) {
+ return;
+ }
+
+ if (!UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(aStatus)) {
+ return;
+ }
+
+ RefPtr<WindowGlobalParent> parent = browsingContext->GetParentWindowContext();
+ if (parent) {
+ Unused << parent->SendAddBlockedFrameNodeByClassifier(browsingContext);
+ }
+}
+
+bool DocumentLoadListener::DocShellWillDisplayContent(nsresult aStatus) {
+ if (NS_SUCCEEDED(aStatus)) {
+ return true;
+ }
+
+ // Always return errored loads to the <object> or <embed> element's process,
+ // as load errors will not be rendered as documents.
+ if (!mIsDocumentLoad) {
+ return false;
+ }
+
+ // nsDocShell attempts urifixup on some failure types,
+ // but also of those also display an error page if we don't
+ // succeed with fixup, so we don't need to check for it
+ // here.
+
+ auto* loadingContext = GetLoadingBrowsingContext();
+
+ bool isInitialDocument = true;
+ if (WindowGlobalParent* currentWindow =
+ loadingContext->GetCurrentWindowGlobal()) {
+ isInitialDocument = currentWindow->IsInitialDocument();
+ }
+
+ nsresult rv = nsDocShell::FilterStatusForErrorPage(
+ aStatus, mChannel, mLoadStateLoadType, loadingContext->IsTop(),
+ loadingContext->GetUseErrorPages(), isInitialDocument, nullptr);
+
+ if (NS_SUCCEEDED(rv)) {
+ MOZ_LOG(gProcessIsolationLog, LogLevel::Verbose,
+ ("Skipping process switch, as DocShell will not display content "
+ "(status: %s) %s",
+ GetStaticErrorName(aStatus),
+ GetChannelCreationURI()->GetSpecOrDefault().get()));
+ }
+
+ // If filtering returned a failure code, then an error page will
+ // be display for that code, so return true;
+ return NS_FAILED(rv);
+}
+
+bool DocumentLoadListener::MaybeHandleLoadErrorWithURIFixup(nsresult aStatus) {
+ RefPtr<CanonicalBrowsingContext> bc = GetDocumentBrowsingContext();
+ if (!bc) {
+ return false;
+ }
+
+ nsCOMPtr<nsIInputStream> newPostData;
+ bool wasSchemelessInput = false;
+ nsCOMPtr<nsIURI> newURI = nsDocShell::AttemptURIFixup(
+ mChannel, aStatus, mOriginalUriString, mLoadStateLoadType, bc->IsTop(),
+ mLoadStateInternalLoadFlags &
+ nsDocShell::INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP,
+ bc->UsePrivateBrowsing(), true, getter_AddRefs(newPostData),
+ &wasSchemelessInput);
+
+ // Since aStatus will be NS_OK for 4xx and 5xx error codes we
+ // have to check each request which was upgraded by https-first.
+ // If an error (including 4xx and 5xx) occured, then let's check if
+ // we can downgrade the scheme to HTTP again.
+ bool isHTTPSFirstFixup = false;
+ if (!newURI) {
+ newURI = nsHTTPSOnlyUtils::PotentiallyDowngradeHttpsFirstRequest(mChannel,
+ aStatus);
+ isHTTPSFirstFixup = true;
+ }
+
+ if (!newURI) {
+ return false;
+ }
+
+ // If we got a new URI, then we should initiate a load with that.
+ // Notify the listeners that this load is complete (with a code that
+ // won't trigger an error page), and then start the new one.
+ DisconnectListeners(NS_BINDING_ABORTED, NS_BINDING_ABORTED);
+
+ RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(newURI);
+ nsCOMPtr<nsILoadInfo> loadInfo = mChannel->LoadInfo();
+
+ nsCOMPtr<nsIContentSecurityPolicy> cspToInherit = loadInfo->GetCspToInherit();
+ loadState->SetCsp(cspToInherit);
+
+ nsCOMPtr<nsIPrincipal> triggeringPrincipal = loadInfo->TriggeringPrincipal();
+ loadState->SetTriggeringPrincipal(triggeringPrincipal);
+
+ loadState->SetPostDataStream(newPostData);
+
+ // Record whether the protocol was added through a fixup.
+ loadState->SetWasSchemelessInput(wasSchemelessInput);
+
+ if (isHTTPSFirstFixup) {
+ // We have to exempt the load from HTTPS-First to prevent a
+ // upgrade-downgrade loop.
+ loadState->SetIsExemptFromHTTPSFirstMode(true);
+ }
+
+ // Ensure to set referrer information in the fallback channel equally to the
+ // not-upgraded original referrer info.
+ //
+ // A simply copy of the referrer info from the upgraded one leads to problems.
+ // For example:
+ // 1. https://some-site.com redirects to http://other-site.com with referrer
+ // policy
+ // "no-referrer-when-downgrade".
+ // 2. https-first upgrades the redirection, so redirects to
+ // https://other-site.com,
+ // according to referrer policy the referrer will be send (https-> https)
+ // 3. Assume other-site.com is not supporting https, https-first performs
+ // fall-
+ // back.
+ // If the referrer info from the upgraded channel gets copied into the
+ // http fallback channel, the referrer info would contain the referrer
+ // (https://some-site.com). That would violate the policy
+ // "no-referrer-when-downgrade". A recreation of the original referrer info
+ // would ensure us that the referrer is set according to the referrer policy.
+ nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
+ if (httpChannel) {
+ nsCOMPtr<nsIReferrerInfo> referrerInfo = httpChannel->GetReferrerInfo();
+ if (referrerInfo) {
+ ReferrerPolicy referrerPolicy = referrerInfo->ReferrerPolicy();
+ nsCOMPtr<nsIURI> originalReferrer = referrerInfo->GetOriginalReferrer();
+ if (originalReferrer) {
+ // Create new ReferrerInfo with the original referrer and the referrer
+ // policy.
+ nsCOMPtr<nsIReferrerInfo> newReferrerInfo =
+ new ReferrerInfo(originalReferrer, referrerPolicy);
+ loadState->SetReferrerInfo(newReferrerInfo);
+ }
+ }
+ }
+
+ bc->LoadURI(loadState, false);
+ return true;
+}
+
+NS_IMETHODIMP
+DocumentLoadListener::OnStartRequest(nsIRequest* aRequest) {
+ LOG(("DocumentLoadListener OnStartRequest [this=%p]", this));
+
+ nsCOMPtr<nsIMultiPartChannel> multiPartChannel = do_QueryInterface(aRequest);
+ if (multiPartChannel) {
+ multiPartChannel->GetBaseChannel(getter_AddRefs(mChannel));
+ } else {
+ mChannel = do_QueryInterface(aRequest);
+ }
+ MOZ_DIAGNOSTIC_ASSERT(mChannel);
+
+ if (mHaveVisibleRedirect && GetDocumentBrowsingContext() &&
+ mLoadingSessionHistoryInfo) {
+ mLoadingSessionHistoryInfo =
+ GetDocumentBrowsingContext()->ReplaceLoadingSessionHistoryEntryForLoad(
+ mLoadingSessionHistoryInfo.get(), mChannel);
+ }
+
+ RefPtr<nsHttpChannel> httpChannel = do_QueryObject(mChannel);
+
+ // Enforce CSP frame-ancestors and x-frame-options checks which
+ // might cancel the channel.
+ nsContentSecurityUtils::PerformCSPFrameAncestorAndXFOCheck(mChannel);
+
+ // HTTPS-Only Mode tries to upgrade connections to https. Once loading
+ // is in progress we set that flag so that timeout counter measures
+ // do not kick in.
+ if (httpChannel) {
+ nsCOMPtr<nsILoadInfo> loadInfo = httpChannel->LoadInfo();
+ bool isPrivateWin = loadInfo->GetOriginAttributes().mPrivateBrowsingId > 0;
+ if (nsHTTPSOnlyUtils::IsHttpsOnlyModeEnabled(isPrivateWin)) {
+ uint32_t httpsOnlyStatus = loadInfo->GetHttpsOnlyStatus();
+ httpsOnlyStatus |= nsILoadInfo::HTTPS_ONLY_TOP_LEVEL_LOAD_IN_PROGRESS;
+ loadInfo->SetHttpsOnlyStatus(httpsOnlyStatus);
+ }
+
+ if (mLoadingSessionHistoryInfo &&
+ nsDocShell::ShouldDiscardLayoutState(httpChannel)) {
+ mLoadingSessionHistoryInfo->mInfo.SetSaveLayoutStateFlag(false);
+ }
+ }
+
+ auto* loadingContext = GetLoadingBrowsingContext();
+ if (!loadingContext || loadingContext->IsDiscarded()) {
+ Cancel(NS_ERROR_UNEXPECTED, "No valid LoadingBrowsingContext."_ns);
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
+ Cancel(NS_ERROR_ILLEGAL_DURING_SHUTDOWN,
+ "Aborting OnStartRequest after shutdown started."_ns);
+ return NS_OK;
+ }
+
+ // Block top-level data URI navigations if triggered by the web. Logging is
+ // performed in AllowTopLevelNavigationToDataURI.
+ if (!nsContentSecurityManager::AllowTopLevelNavigationToDataURI(mChannel)) {
+ mChannel->Cancel(NS_ERROR_DOM_BAD_URI);
+ if (loadingContext) {
+ RefPtr<MaybeCloseWindowHelper> maybeCloseWindowHelper =
+ new MaybeCloseWindowHelper(loadingContext);
+ // If a new window was opened specifically for this request, close it
+ // after blocking the navigation.
+ maybeCloseWindowHelper->SetShouldCloseWindow(
+ IsFirstLoadInWindow(mChannel));
+ Unused << maybeCloseWindowHelper->MaybeCloseWindow();
+ }
+ DisconnectListeners(NS_ERROR_DOM_BAD_URI, NS_ERROR_DOM_BAD_URI);
+ return NS_OK;
+ }
+
+ // Generally we want to switch to a real channel even if the request failed,
+ // since the listener might want to access protocol-specific data (like http
+ // response headers) in its error handling.
+ // An exception to this is when nsExtProtocolChannel handled the request and
+ // returned NS_ERROR_NO_CONTENT, since creating a real one in the content
+ // process will attempt to handle the URI a second time.
+ nsresult status = NS_OK;
+ aRequest->GetStatus(&status);
+ if (status == NS_ERROR_NO_CONTENT) {
+ DisconnectListeners(status, status);
+ return NS_OK;
+ }
+
+ // PerformCSPFrameAncestorAndXFOCheck may cancel a moz-extension request that
+ // needs to be handled here. Without this, the resource would be loaded and
+ // not blocked when the real channel is created in the content process.
+ if (status == NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION && !httpChannel) {
+ DisconnectListeners(status, status);
+ return NS_OK;
+ }
+
+ // If this was a failed load and we want to try fixing the uri, then
+ // this will initiate a new load (and disconnect this one), and we don't
+ // need to do anything else.
+ if (MaybeHandleLoadErrorWithURIFixup(status)) {
+ return NS_OK;
+ }
+
+ mStreamListenerFunctions.AppendElement(StreamListenerFunction{
+ VariantIndex<0>{}, OnStartRequestParams{aRequest}});
+
+ if (mOpenPromiseResolved || mInitiatedRedirectToRealChannel) {
+ // I we have already resolved the promise, there's no point to continue
+ // attempting a process switch or redirecting to the real channel.
+ // We can also have multiple calls to OnStartRequest when dealing with
+ // multi-part content, but only want to redirect once.
+ return NS_OK;
+ }
+
+ // Keep track of server responses resulting in a document for the Bounce
+ // Tracking Protection.
+ if (mIsDocumentLoad && GetParentWindowContext() == nullptr &&
+ loadingContext->IsTopContent()) {
+ RefPtr<BounceTrackingState> bounceTrackingState =
+ loadingContext->GetBounceTrackingState();
+
+ // Not every browsing context has a BounceTrackingState. It's also null when
+ // the feature is disabled.
+ if (bounceTrackingState) {
+ DebugOnly<nsresult> rv =
+ bounceTrackingState->OnDocumentStartRequest(mChannel);
+ NS_WARNING_ASSERTION(
+ NS_SUCCEEDED(rv),
+ "BounceTrackingState::OnDocumentStartRequest failed.");
+ }
+ }
+
+ mChannel->Suspend();
+
+ mInitiatedRedirectToRealChannel = true;
+
+ MaybeReportBlockedByURLClassifier(status);
+
+ // Determine if a new process needs to be spawned. If it does, this will
+ // trigger a cross process switch, and we should hold off on redirecting to
+ // the real channel.
+ // If the channel has failed, and the docshell isn't going to display an
+ // error page for that failure, then don't allow process switching, since
+ // we just want to keep our existing document.
+ bool willBeRemote = false;
+ if (!DocShellWillDisplayContent(status) ||
+ !MaybeTriggerProcessSwitch(&willBeRemote)) {
+ // We're not going to be doing a process switch, so redirect to the real
+ // channel within our current process.
+ nsTArray<StreamFilterRequest> streamFilterRequests =
+ std::move(mStreamFilterRequests);
+ if (!mSupportsRedirectToRealChannel) {
+ RefPtr<BrowserParent> browserParent = loadingContext->GetBrowserParent();
+ if (browserParent->Manager() != mContentParent) {
+ LOG(
+ ("DocumentLoadListener::RedirectToRealChannel failed because "
+ "browsingContext no longer owned by creator"));
+ Cancel(NS_BINDING_ABORTED,
+ "DocumentLoadListener::RedirectToRealChannel failed because "
+ "browsingContext no longer owned by creator"_ns);
+ return NS_OK;
+ }
+ MOZ_DIAGNOSTIC_ASSERT(
+ browserParent->GetBrowsingContext() == loadingContext,
+ "make sure the load is going to the right place");
+
+ // If the existing process is right for this load, but the bridge doesn't
+ // support redirects, then we need to do it manually, by faking a process
+ // switch.
+ DisconnectListeners(NS_BINDING_ABORTED, NS_BINDING_ABORTED,
+ /* aContinueNavigating */ true);
+
+ // Notify the docshell that it should load using the newly connected
+ // channel
+ browserParent->ResumeLoad(mLoadIdentifier);
+
+ // Use the current process ID to run the 'process switch' path and connect
+ // the channel into the current process.
+ TriggerRedirectToRealChannel(Some(mContentParent),
+ std::move(streamFilterRequests));
+ } else {
+ TriggerRedirectToRealChannel(Nothing(), std::move(streamFilterRequests));
+ }
+
+ // If we're not switching, then check if we're currently remote.
+ if (mContentParent) {
+ willBeRemote = true;
+ }
+ }
+
+ if (httpChannel) {
+ uint32_t responseStatus = 0;
+ Unused << httpChannel->GetResponseStatus(&responseStatus);
+ mEarlyHintsService.FinalResponse(responseStatus);
+ } else {
+ mEarlyHintsService.Cancel(
+ "DocumentLoadListener::OnStartRequest: no httpChannel"_ns);
+ }
+
+ // If we're going to be delivering this channel to a remote content
+ // process, then we want to install any required content conversions
+ // in the content process.
+ // The caller of this OnStartRequest will install a conversion
+ // helper after we return if we haven't disabled conversion. Normally
+ // HttpChannelParent::OnStartRequest would disable conversion, but we're
+ // defering calling that until later. Manually disable it now to prevent the
+ // converter from being installed (since we want the child to do it), and
+ // also save the value so that when we do call
+ // HttpChannelParent::OnStartRequest, we can have the value as it originally
+ // was.
+ if (httpChannel) {
+ Unused << httpChannel->GetApplyConversion(&mOldApplyConversion);
+ if (willBeRemote) {
+ httpChannel->SetApplyConversion(false);
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+DocumentLoadListener::OnStopRequest(nsIRequest* aRequest,
+ nsresult aStatusCode) {
+ LOG(("DocumentLoadListener OnStopRequest [this=%p]", this));
+ mStreamListenerFunctions.AppendElement(StreamListenerFunction{
+ VariantIndex<2>{}, OnStopRequestParams{aRequest, aStatusCode}});
+
+ // If we're not a multi-part channel, then we're finished and we don't
+ // expect any further events. If we are, then this might be called again,
+ // so wait for OnAfterLastPart instead.
+ nsCOMPtr<nsIMultiPartChannel> multiPartChannel = do_QueryInterface(aRequest);
+ if (!multiPartChannel) {
+ mIsFinished = true;
+ }
+
+ mStreamFilterRequests.Clear();
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+DocumentLoadListener::OnDataAvailable(nsIRequest* aRequest,
+ nsIInputStream* aInputStream,
+ uint64_t aOffset, uint32_t aCount) {
+ LOG(("DocumentLoadListener OnDataAvailable [this=%p]", this));
+ // This isn't supposed to happen, since we suspended the channel, but
+ // sometimes Suspend just doesn't work. This can happen when we're routing
+ // through nsUnknownDecoder to sniff the content type, and it doesn't handle
+ // being suspended. Let's just store the data and manually forward it to our
+ // redirected channel when it's ready.
+ nsCString data;
+ nsresult rv = NS_ReadInputStreamToString(aInputStream, data, aCount);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mStreamListenerFunctions.AppendElement(StreamListenerFunction{
+ VariantIndex<1>{},
+ OnDataAvailableParams{aRequest, std::move(data), aOffset, aCount}});
+
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// DoucmentLoadListener::nsIMultiPartChannelListener
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+DocumentLoadListener::OnAfterLastPart(nsresult aStatus) {
+ LOG(("DocumentLoadListener OnAfterLastPart [this=%p]", this));
+ if (!mInitiatedRedirectToRealChannel) {
+ // if we get here, and we haven't initiated a redirect to a real
+ // channel, then it means we never got OnStartRequest (maybe a problem?)
+ // and we retargeted everything.
+ LOG(("DocumentLoadListener Disconnecting child"));
+ DisconnectListeners(NS_BINDING_RETARGETED, NS_OK);
+ return NS_OK;
+ }
+ mStreamListenerFunctions.AppendElement(StreamListenerFunction{
+ VariantIndex<3>{}, OnAfterLastPartParams{aStatus}});
+ mIsFinished = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+DocumentLoadListener::GetInterface(const nsIID& aIID, void** result) {
+ RefPtr<CanonicalBrowsingContext> browsingContext =
+ GetLoadingBrowsingContext();
+ if (aIID.Equals(NS_GET_IID(nsILoadContext)) && browsingContext) {
+ browsingContext.forget(result);
+ return NS_OK;
+ }
+
+ return QueryInterface(aIID, result);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// nsIParentChannel
+////////////////////////////////////////////////////////////////////////////////
+
+NS_IMETHODIMP
+DocumentLoadListener::SetParentListener(
+ mozilla::net::ParentChannelListener* listener) {
+ // We don't need this (do we?)
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+DocumentLoadListener::SetClassifierMatchedInfo(const nsACString& aList,
+ const nsACString& aProvider,
+ const nsACString& aFullHash) {
+ ClassifierMatchedInfoParams params;
+ params.mList = aList;
+ params.mProvider = aProvider;
+ params.mFullHash = aFullHash;
+
+ mIParentChannelFunctions.AppendElement(
+ IParentChannelFunction{VariantIndex<0>{}, std::move(params)});
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+DocumentLoadListener::SetClassifierMatchedTrackingInfo(
+ const nsACString& aLists, const nsACString& aFullHash) {
+ ClassifierMatchedTrackingInfoParams params;
+ params.mLists = aLists;
+ params.mFullHashes = aFullHash;
+
+ mIParentChannelFunctions.AppendElement(
+ IParentChannelFunction{VariantIndex<1>{}, std::move(params)});
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+DocumentLoadListener::NotifyClassificationFlags(uint32_t aClassificationFlags,
+ bool aIsThirdParty) {
+ mIParentChannelFunctions.AppendElement(IParentChannelFunction{
+ VariantIndex<2>{},
+ ClassificationFlagsParams{aClassificationFlags, aIsThirdParty}});
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+DocumentLoadListener::Delete() {
+ MOZ_ASSERT_UNREACHABLE("This method is unused");
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+DocumentLoadListener::GetRemoteType(nsACString& aRemoteType) {
+ // FIXME: The remote type here should be pulled from the remote process used
+ // to create this DLL, not from the current `browsingContext`.
+ RefPtr<CanonicalBrowsingContext> browsingContext =
+ GetDocumentBrowsingContext();
+ if (!browsingContext) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ ErrorResult error;
+ browsingContext->GetCurrentRemoteType(aRemoteType, error);
+ if (error.Failed()) {
+ aRemoteType = NOT_REMOTE_TYPE;
+ }
+ return NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// nsIChannelEventSink
+////////////////////////////////////////////////////////////////////////////////
+
+NS_IMETHODIMP
+DocumentLoadListener::AsyncOnChannelRedirect(
+ nsIChannel* aOldChannel, nsIChannel* aNewChannel, uint32_t aFlags,
+ nsIAsyncVerifyRedirectCallback* aCallback) {
+ LOG(("DocumentLoadListener::AsyncOnChannelRedirect [this=%p flags=%" PRIu32
+ "]",
+ this, aFlags));
+ // We generally don't want to notify the content process about redirects,
+ // so just update our channel and tell the callback that we're good to go.
+ mChannel = aNewChannel;
+
+ // We need the original URI of the current channel to use to open the real
+ // channel in the content process. Unfortunately we overwrite the original
+ // uri of the new channel with the original pre-redirect URI, so grab
+ // a copy of it now and save it on the loadInfo corresponding to the
+ // new channel.
+ nsCOMPtr<nsILoadInfo> loadInfoFromChannel = mChannel->LoadInfo();
+ MOZ_ASSERT(loadInfoFromChannel);
+ nsCOMPtr<nsIURI> uri;
+ mChannel->GetOriginalURI(getter_AddRefs(uri));
+ loadInfoFromChannel->SetChannelCreationOriginalURI(uri);
+
+ // Since we're redirecting away from aOldChannel, we should check if it
+ // had a COOP mismatch, since we want the final result for this to
+ // include the state of all channels we redirected through.
+ nsCOMPtr<nsIHttpChannelInternal> httpChannel = do_QueryInterface(aOldChannel);
+ if (httpChannel) {
+ bool isCOOPMismatch = false;
+ Unused << NS_WARN_IF(NS_FAILED(
+ httpChannel->HasCrossOriginOpenerPolicyMismatch(&isCOOPMismatch)));
+ mHasCrossOriginOpenerPolicyMismatch |= isCOOPMismatch;
+ }
+
+ // If HTTPS-Only mode is enabled, we need to check whether the exception-flag
+ // needs to be removed or set, by asking the PermissionManager.
+ nsHTTPSOnlyUtils::TestSitePermissionAndPotentiallyAddExemption(mChannel);
+
+ // We don't need to confirm internal redirects or record any
+ // history for them, so just immediately verify and return.
+ if (aFlags & nsIChannelEventSink::REDIRECT_INTERNAL) {
+ LOG(
+ ("DocumentLoadListener AsyncOnChannelRedirect [this=%p] "
+ "flags=REDIRECT_INTERNAL",
+ this));
+ aCallback->OnRedirectVerifyCallback(NS_OK);
+ return NS_OK;
+ }
+
+ // Cancel cross origin redirects as described by whatwg:
+ // > Note: [The early hint reponse] is discarded if it is succeeded by a
+ // > cross-origin redirect.
+ // https://html.spec.whatwg.org/multipage/semantics.html#early-hints
+ nsCOMPtr<nsIURI> oldURI;
+ aOldChannel->GetURI(getter_AddRefs(oldURI));
+ nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
+ nsresult rv = ssm->CheckSameOriginURI(oldURI, uri, false, false);
+ if (NS_FAILED(rv)) {
+ mEarlyHintsService.Cancel(
+ "DocumentLoadListener::AsyncOnChannelRedirect: cors redirect"_ns);
+ }
+
+ if (GetDocumentBrowsingContext()) {
+ if (!net::ChannelIsPost(aOldChannel)) {
+ AddURIVisit(aOldChannel, 0);
+ nsDocShell::SaveLastVisit(aNewChannel, oldURI, aFlags);
+ }
+ }
+ mHaveVisibleRedirect |= true;
+
+ LOG(
+ ("DocumentLoadListener AsyncOnChannelRedirect [this=%p] "
+ "mHaveVisibleRedirect=%c",
+ this, mHaveVisibleRedirect ? 'T' : 'F'));
+
+ // Clear out our nsIParentChannel functions, since a normal parent
+ // channel would actually redirect and not have those values on the new one.
+ // We expect the URI classifier to run on the redirected channel with
+ // the new URI and set these again.
+ mIParentChannelFunctions.Clear();
+
+ // If we had a remote type override, ensure it's been cleared after a
+ // redirect, as it can't apply anymore.
+ mRemoteTypeOverride.reset();
+
+#ifdef ANDROID
+ nsCOMPtr<nsIURI> uriBeingLoaded =
+ AntiTrackingUtils::MaybeGetDocumentURIBeingLoaded(mChannel);
+
+ RefPtr<MozPromise<bool, bool, false>> promise;
+ RefPtr<CanonicalBrowsingContext> bc =
+ mParentChannelListener->GetBrowsingContext();
+ nsCOMPtr<nsIWidget> widget =
+ bc ? bc->GetParentProcessWidgetContaining() : nullptr;
+ RefPtr<nsWindow> window = nsWindow::From(widget);
+
+ if (window) {
+ promise = window->OnLoadRequest(uriBeingLoaded,
+ nsIBrowserDOMWindow::OPEN_CURRENTWINDOW,
+ nsIWebNavigation::LOAD_FLAGS_IS_REDIRECT,
+ nullptr, false, bc->IsTopContent());
+ }
+
+ if (promise) {
+ RefPtr<nsIAsyncVerifyRedirectCallback> cb = aCallback;
+ promise->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [=](const MozPromise<bool, bool, false>::ResolveOrRejectValue& aValue) {
+ if (aValue.IsResolve()) {
+ bool handled = aValue.ResolveValue();
+ if (handled) {
+ cb->OnRedirectVerifyCallback(NS_ERROR_ABORT);
+ } else {
+ cb->OnRedirectVerifyCallback(NS_OK);
+ }
+ }
+ });
+ } else
+#endif /* ANDROID */
+ {
+ aCallback->OnRedirectVerifyCallback(NS_OK);
+ }
+ return NS_OK;
+}
+
+nsIURI* DocumentLoadListener::GetChannelCreationURI() const {
+ nsCOMPtr<nsILoadInfo> channelLoadInfo = mChannel->LoadInfo();
+
+ nsCOMPtr<nsIURI> uri;
+ channelLoadInfo->GetChannelCreationOriginalURI(getter_AddRefs(uri));
+ if (uri) {
+ // See channelCreationOriginalURI for more info. We use this instead of the
+ // originalURI of the channel to help us avoid the situation when we use
+ // the URI of a redirect that has failed to happen.
+ return uri;
+ }
+
+ // Otherwise, get the original URI from the channel.
+ mChannel->GetOriginalURI(getter_AddRefs(uri));
+ return uri;
+}
+
+// This method returns the cached result of running the Cross-Origin-Opener
+// policy compare algorithm by calling ComputeCrossOriginOpenerPolicyMismatch
+bool DocumentLoadListener::HasCrossOriginOpenerPolicyMismatch() const {
+ // If we found a COOP mismatch on an earlier channel and then
+ // redirected away from that, we should use that result.
+ if (mHasCrossOriginOpenerPolicyMismatch) {
+ return true;
+ }
+
+ nsCOMPtr<nsIHttpChannelInternal> httpChannel = do_QueryInterface(mChannel);
+ if (!httpChannel) {
+ // Not an nsIHttpChannelInternal assume it's okay to switch.
+ return false;
+ }
+
+ bool isCOOPMismatch = false;
+ Unused << NS_WARN_IF(NS_FAILED(
+ httpChannel->HasCrossOriginOpenerPolicyMismatch(&isCOOPMismatch)));
+ return isCOOPMismatch;
+}
+
+auto DocumentLoadListener::AttachStreamFilter()
+ -> RefPtr<ChildEndpointPromise> {
+ LOG(("DocumentLoadListener AttachStreamFilter [this=%p]", this));
+
+ StreamFilterRequest* request = mStreamFilterRequests.AppendElement();
+ request->mPromise = new ChildEndpointPromise::Private(__func__);
+ return request->mPromise;
+}
+
+NS_IMETHODIMP DocumentLoadListener::OnProgress(nsIRequest* aRequest,
+ int64_t aProgress,
+ int64_t aProgressMax) {
+ return NS_OK;
+}
+
+NS_IMETHODIMP DocumentLoadListener::OnStatus(nsIRequest* aRequest,
+ nsresult aStatus,
+ const char16_t* aStatusArg) {
+ nsCOMPtr<nsIChannel> channel = mChannel;
+
+ RefPtr<BrowsingContextWebProgress> webProgress =
+ GetLoadingBrowsingContext()->GetWebProgress();
+ const nsString message(aStatusArg);
+
+ if (webProgress) {
+ NS_DispatchToMainThread(
+ NS_NewRunnableFunction("DocumentLoadListener::OnStatus", [=]() {
+ webProgress->OnStatusChange(webProgress, channel, aStatus,
+ message.get());
+ }));
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP DocumentLoadListener::EarlyHint(const nsACString& aLinkHeader,
+ const nsACString& aReferrerPolicy,
+ const nsACString& aCSPHeader) {
+ LOG(("DocumentLoadListener::EarlyHint.\n"));
+ mEarlyHintsService.EarlyHint(aLinkHeader, GetChannelCreationURI(), mChannel,
+ aReferrerPolicy, aCSPHeader,
+ GetLoadingBrowsingContext());
+ return NS_OK;
+}
+
+} // namespace net
+} // namespace mozilla
+
+#undef LOG
diff --git a/netwerk/ipc/DocumentLoadListener.h b/netwerk/ipc/DocumentLoadListener.h
new file mode 100644
index 0000000000..98aacc98f8
--- /dev/null
+++ b/netwerk/ipc/DocumentLoadListener.h
@@ -0,0 +1,630 @@
+/* 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_DocumentLoadListener_h
+#define mozilla_net_DocumentLoadListener_h
+
+#include "mozilla/MozPromise.h"
+#include "mozilla/Variant.h"
+#include "mozilla/WeakPtr.h"
+#include "mozilla/ipc/Endpoint.h"
+#include "mozilla/dom/SessionHistoryEntry.h"
+#include "EarlyHintsService.h"
+#include "mozilla/net/NeckoCommon.h"
+#include "mozilla/net/NeckoParent.h"
+#include "mozilla/net/PDocumentChannelParent.h"
+#include "mozilla/net/ParentChannelListener.h"
+#include "nsDOMNavigationTiming.h"
+#include "nsIBrowser.h"
+#include "nsIChannelEventSink.h"
+#include "nsIEarlyHintObserver.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIMultiPartChannel.h"
+#include "nsIParentChannel.h"
+#include "nsIParentRedirectingChannel.h"
+#include "nsIProgressEventSink.h"
+#include "nsIRedirectResultListener.h"
+
+#define DOCUMENT_LOAD_LISTENER_IID \
+ { \
+ 0x3b393c56, 0x9e01, 0x11e9, { \
+ 0xa2, 0xa3, 0x2a, 0x2a, 0xe2, 0xdb, 0xcc, 0xe4 \
+ } \
+ }
+
+namespace mozilla {
+namespace dom {
+class CanonicalBrowsingContext;
+struct NavigationIsolationOptions;
+} // namespace dom
+namespace net {
+using ChildEndpointPromise =
+ MozPromise<mozilla::ipc::Endpoint<extensions::PStreamFilterChild>, bool,
+ true>;
+
+// If we've been asked to attach a stream filter to our channel,
+// then we return this promise and defer until we know the final
+// content process. At that point we setup Endpoints between
+// mStramFilterProcessId and the new content process, and send
+// the parent Endpoint to the new process.
+// Once we have confirmation of that being bound in the content
+// process, we resolve the promise the child Endpoint.
+struct StreamFilterRequest {
+ StreamFilterRequest() = default;
+ StreamFilterRequest(StreamFilterRequest&&) = default;
+ ~StreamFilterRequest() {
+ if (mPromise) {
+ mPromise->Reject(false, __func__);
+ }
+ }
+ RefPtr<ChildEndpointPromise::Private> mPromise;
+ mozilla::ipc::Endpoint<extensions::PStreamFilterChild> mChildEndpoint;
+};
+} // namespace net
+} // namespace mozilla
+MOZ_DECLARE_RELOCATE_USING_MOVE_CONSTRUCTOR(mozilla::net::StreamFilterRequest)
+
+namespace mozilla {
+namespace net {
+
+class LoadInfo;
+
+/**
+ * DocumentLoadListener represents a connecting document load for a
+ * CanonicalBrowsingContext (in the parent process).
+ *
+ * It creates a network channel for the document load and then waits for it to
+ * receive a response (after all redirects are resolved). It then decides where
+ * to handle that load (could be in a different process from the initiator),
+ * and then sets up a real network nsIChannel to deliver the data to the final
+ * destination docshell, maybe through an nsIParentChannel/nsIChildChannel IPDL
+ * layer.
+ *
+ * In the case where this was initiated from an nsDocShell, we also create an
+ * nsIChannel to act as a placeholder within the docshell while this process
+ * completes, and then notify the docshell of a 'redirect' when we replace this
+ * channel with the real one.
+ */
+
+// TODO: We currently don't implement nsIProgressEventSink and forward those
+// to the child. Should we? We get the interface requested.
+class DocumentLoadListener : public nsIInterfaceRequestor,
+ public nsIAsyncVerifyRedirectReadyCallback,
+ public nsIParentChannel,
+ public nsIChannelEventSink,
+ public HttpChannelSecurityWarningReporter,
+ public nsIMultiPartChannelListener,
+ public nsIProgressEventSink,
+ public nsIEarlyHintObserver {
+ public:
+ // See the comment on GetLoadingBrowsingContext for explanation of
+ // aLoadingBrowsingContext.
+ DocumentLoadListener(dom::CanonicalBrowsingContext* aLoadingBrowsingContext,
+ bool aIsDocumentLoad);
+
+ struct OpenPromiseSucceededType {
+ nsTArray<mozilla::ipc::Endpoint<extensions::PStreamFilterParent>>
+ mStreamFilterEndpoints;
+ uint32_t mRedirectFlags;
+ uint32_t mLoadFlags;
+ uint32_t mEarlyHintLinkType;
+ RefPtr<PDocumentChannelParent::RedirectToRealChannelPromise::Private>
+ mPromise;
+ };
+ struct OpenPromiseFailedType {
+ nsresult mStatus;
+ nsresult mLoadGroupStatus;
+ // This is set to true if the navigation in the content process should not
+ // be cancelled, as the load is logically continuing within the current
+ // browsing session, just within a different process or browsing context.
+ bool mContinueNavigating = false;
+ };
+
+ using OpenPromise =
+ MozPromise<OpenPromiseSucceededType, OpenPromiseFailedType, true>;
+
+ // Interface which may be provided when performing an <object> or <embed> load
+ // with `DocumentLoadListener`, to allow upgrading the Object load to a proper
+ // Document load.
+ struct ObjectUpgradeHandler : public SupportsWeakPtr {
+ using ObjectUpgradePromise =
+ MozPromise<RefPtr<dom::CanonicalBrowsingContext>, nsresult,
+ true /* isExclusive */>;
+
+ // Upgrade an object load to be a potentially remote document.
+ //
+ // The returned promise will resolve with the BrowsingContext which has been
+ // created in the <object> or <embed> element to finish the load with.
+ virtual RefPtr<ObjectUpgradePromise> UpgradeObjectLoad() = 0;
+ };
+
+ private:
+ // Creates the channel, and then calls AsyncOpen on it.
+ // The DocumentLoadListener will require additional process from the consumer
+ // in order to complete the redirect to the end channel. This is done by
+ // returning a RedirectToRealChannelPromise and then waiting for it to be
+ // resolved or rejected accordingly.
+ // Once that promise is resolved; the consumer no longer needs to hold a
+ // reference to the DocumentLoadListener nor will the consumer required to be
+ // used again.
+ RefPtr<OpenPromise> Open(nsDocShellLoadState* aLoadState, LoadInfo* aLoadInfo,
+ nsLoadFlags aLoadFlags, uint32_t aCacheKey,
+ const Maybe<uint64_t>& aChannelId,
+ const TimeStamp& aAsyncOpenTime,
+ nsDOMNavigationTiming* aTiming,
+ Maybe<dom::ClientInfo>&& aInfo, bool aUrgentStart,
+ dom::ContentParent* aContentParent, nsresult* aRv);
+
+ public:
+ RefPtr<OpenPromise> OpenDocument(
+ nsDocShellLoadState* aLoadState, uint32_t aCacheKey,
+ const Maybe<uint64_t>& aChannelId, const TimeStamp& aAsyncOpenTime,
+ nsDOMNavigationTiming* aTiming, Maybe<dom::ClientInfo>&& aInfo,
+ Maybe<bool> aUriModified, Maybe<bool> aIsEmbeddingBlockedError,
+ dom::ContentParent* aContentParent, nsresult* aRv);
+
+ RefPtr<OpenPromise> OpenObject(
+ nsDocShellLoadState* aLoadState, uint32_t aCacheKey,
+ const Maybe<uint64_t>& aChannelId, const TimeStamp& aAsyncOpenTime,
+ nsDOMNavigationTiming* aTiming, Maybe<dom::ClientInfo>&& aInfo,
+ uint64_t aInnerWindowId, nsLoadFlags aLoadFlags,
+ nsContentPolicyType aContentPolicyType, bool aUrgentStart,
+ dom::ContentParent* aContentParent,
+ ObjectUpgradeHandler* aObjectUpgradeHandler, nsresult* aRv);
+
+ // Creates a DocumentLoadListener entirely in the parent process and opens it,
+ // and never needs a DocumentChannel to connect to an existing docshell.
+ // Once we get a response it takes the 'process switch' path to find the right
+ // process and docshell, and delivers the response there directly.
+ static bool LoadInParent(dom::CanonicalBrowsingContext* aBrowsingContext,
+ nsDocShellLoadState* aLoadState,
+ bool aSetNavigating);
+
+ // Creates a DocumentLoadListener directly in the parent process and opens it,
+ // without needing an existing DocumentChannel.
+ // If successful it registers a unique identifier (return in aOutIdent) to
+ // keep it alive until a future DocumentChannel can attach to it, or we fail
+ // and clean up.
+ static bool SpeculativeLoadInParent(
+ dom::CanonicalBrowsingContext* aBrowsingContext,
+ nsDocShellLoadState* aLoadState);
+
+ // Ensures that a load identifier allocated by OpenFromParent has
+ // been deregistered if it hasn't already been claimed.
+ // This also cancels the load.
+ static void CleanupParentLoadAttempt(uint64_t aLoadIdent);
+
+ // Looks up aLoadIdent to find the associated, cleans up the registration
+ static RefPtr<OpenPromise> ClaimParentLoad(DocumentLoadListener** aListener,
+ uint64_t aLoadIdent,
+ Maybe<uint64_t> aChannelId);
+
+ // Called by the DocumentChannelParent if actor got destroyed or the parent
+ // channel got deleted.
+ void Abort();
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIREQUESTOBSERVER
+ NS_DECL_NSISTREAMLISTENER
+ NS_DECL_NSIPARENTCHANNEL
+ NS_DECL_NSIINTERFACEREQUESTOR
+ NS_DECL_NSIASYNCVERIFYREDIRECTREADYCALLBACK
+ NS_DECL_NSICHANNELEVENTSINK
+ NS_DECL_NSIMULTIPARTCHANNELLISTENER
+ NS_DECL_NSIPROGRESSEVENTSINK
+ NS_DECL_NSIEARLYHINTOBSERVER
+
+ // We suspend the underlying channel when replacing ourselves with
+ // the real listener channel.
+ // This helper resumes the underlying channel again, and manually
+ // forwards any nsIStreamListener messages that arrived while we
+ // were suspended (which might have failed).
+ // Returns true if the channel was finished before we could resume it.
+ bool ResumeSuspendedChannel(nsIStreamListener* aListener);
+
+ NS_DECLARE_STATIC_IID_ACCESSOR(DOCUMENT_LOAD_LISTENER_IID)
+
+ // Called by the DocumentChannel if cancelled.
+ void Cancel(const nsresult& aStatusCode, const nsACString& aReason);
+
+ nsIChannel* GetChannel() const { return mChannel; }
+
+ uint32_t GetRedirectChannelId() const { return mRedirectChannelId; }
+
+ nsresult ReportSecurityMessage(const nsAString& aMessageTag,
+ const nsAString& aMessageCategory) override {
+ ReportSecurityMessageParams params;
+ params.mMessageTag = aMessageTag;
+ params.mMessageCategory = aMessageCategory;
+ mSecurityWarningFunctions.AppendElement(
+ SecurityWarningFunction{VariantIndex<0>{}, std::move(params)});
+ return NS_OK;
+ }
+
+ nsresult LogBlockedCORSRequest(const nsAString& aMessage,
+ const nsACString& aCategory,
+ bool aIsWarning) override {
+ LogBlockedCORSRequestParams params;
+ params.mMessage = aMessage;
+ params.mCategory = aCategory;
+ params.mIsWarning = aIsWarning;
+ mSecurityWarningFunctions.AppendElement(
+ SecurityWarningFunction{VariantIndex<1>{}, std::move(params)});
+ return NS_OK;
+ }
+
+ nsresult LogMimeTypeMismatch(const nsACString& aMessageName, bool aWarning,
+ const nsAString& aURL,
+ const nsAString& aContentType) override {
+ LogMimeTypeMismatchParams params;
+ params.mMessageName = aMessageName;
+ params.mWarning = aWarning;
+ params.mURL = aURL;
+ params.mContentType = aContentType;
+ mSecurityWarningFunctions.AppendElement(
+ SecurityWarningFunction{VariantIndex<2>{}, std::move(params)});
+ return NS_OK;
+ }
+
+ // The content process corresponding to this DocumentLoadListener, or nullptr
+ // if connected to the parent process.
+ dom::ContentParent* GetContentParent() const { return mContentParent; }
+
+ // The process id of the content process that we are being called from
+ // or 0 initiated from a parent process load.
+ base::ProcessId OtherPid() const;
+
+ [[nodiscard]] RefPtr<ChildEndpointPromise> AttachStreamFilter();
+
+ // EarlyHints aren't supported on ParentProcessDocumentChannels yet, allow
+ // EarlyHints to be cancelled from there (Bug 1819886)
+ void CancelEarlyHintPreloads();
+
+ // Gets the EarlyHint preloads for this document to pass them to the
+ // ContentProcess. Registers them in the EarlyHintRegister
+ void RegisterEarlyHintLinksAndGetConnectArgs(
+ dom::ContentParentId aCpId, nsTArray<EarlyHintConnectArgs>& aOutLinks);
+
+ // Serializes all data needed to setup the new replacement channel
+ // in the content process into the RedirectToRealChannelArgs struct.
+ void SerializeRedirectData(RedirectToRealChannelArgs& aArgs,
+ bool aIsCrossProcess, uint32_t aRedirectFlags,
+ uint32_t aLoadFlags,
+ nsTArray<EarlyHintConnectArgs>&& aEarlyHints,
+ uint32_t aEarlyHintLinkType) const;
+
+ uint64_t GetLoadIdentifier() const { return mLoadIdentifier; }
+ uint32_t GetLoadType() const { return mLoadStateLoadType; }
+ bool IsDownload() const { return mIsDownload; }
+ bool IsLoadingJSURI() const { return mIsLoadingJSURI; }
+
+ mozilla::dom::LoadingSessionHistoryInfo* GetLoadingSessionHistoryInfo() {
+ return mLoadingSessionHistoryInfo.get();
+ }
+
+ bool IsDocumentLoad() const { return mIsDocumentLoad; }
+
+ // Determine what process switching behavior a browser element should have.
+ enum ProcessBehavior : uint8_t {
+ // Gecko won't automatically change which process this frame, or it's
+ // subframes, are loaded in.
+ PROCESS_BEHAVIOR_DISABLED,
+
+ // If `useRemoteTabs` is enabled, Gecko will change which process this frame
+ // is loaded in automatically, without calling `performProcessSwitch`.
+ // When `useRemoteSubframes` is enabled, subframes will change processes.
+ PROCESS_BEHAVIOR_STANDARD,
+
+ // Gecko won't automatically change which process this frame is loaded, but
+ // when `useRemoteSubframes` is enabled, subframes will change processes.
+ //
+ // NOTE: This configuration is included only for backwards compatibility,
+ // and will be removed, as it can easily lead to invalid behavior.
+ PROCESS_BEHAVIOR_SUBFRAME_ONLY,
+ };
+
+ protected:
+ virtual ~DocumentLoadListener();
+
+ private:
+ RefPtr<OpenPromise> OpenInParent(nsDocShellLoadState* aLoadState,
+ bool aSupportsRedirectToRealChannel);
+
+ friend class ParentProcessDocumentOpenInfo;
+
+ // Will reject the promise to notify the DLL consumer that we are done.
+ //
+ // If `aContinueNavigating` is true, the navigation in the content process
+ // will not be aborted, as navigation is logically continuing in the existing
+ // browsing session (e.g. due to a process switch or entering the bfcache).
+ void DisconnectListeners(nsresult aStatus, nsresult aLoadGroupStatus,
+ bool aContinueNavigating = false);
+
+ // Called when we were created without a document channel, and creation has
+ // failed, and won't ever be attached.
+ void NotifyDocumentChannelFailed();
+
+ // Initiates the switch from DocumentChannel to the real protocol-specific
+ // channel, and ensures that RedirectToRealChannelFinished is called when
+ // this is complete.
+ void TriggerRedirectToRealChannel(
+ const Maybe<dom::ContentParent*>& aDestinationProcess,
+ nsTArray<StreamFilterRequest> aStreamFilterRequests);
+
+ // Called once the content-process side on setting up a replacement
+ // channel is complete. May wait for the new parent channel to
+ // finish, and then calls into FinishReplacementChannelSetup.
+ void RedirectToRealChannelFinished(nsresult aRv);
+
+ // Completes the replacement of the new channel.
+ // This redirects the ParentChannelListener to forward any future
+ // messages to the new channel, manually forwards any being held
+ // by us, and resumes the underlying source channel.
+ void FinishReplacementChannelSetup(nsresult aResult);
+
+ // Called from `OnStartRequest` to make the decision about whether or not to
+ // change process. This method will return `nullptr` if the current target
+ // process is appropriate.
+ // aWillSwitchToRemote is set to true if we initiate a process switch,
+ // and that the new remote type will be something other than NOT_REMOTE
+ bool MaybeTriggerProcessSwitch(bool* aWillSwitchToRemote);
+
+ // Called when the process switch is going to happen, potentially
+ // asynchronously, from `MaybeTriggerProcessSwitch`.
+ //
+ // aContext should be the target context for the navigation. This will either
+ // be the loading BrowsingContext, the newly created BrowsingContext for an
+ // object or embed element load, or a newly created tab for new tab load.
+ //
+ // If `aIsNewTab` is specified, the navigation in the original process will be
+ // aborted immediately, rather than waiting for a process switch to happen and
+ // the previous page to be unloaded or hidden.
+ void TriggerProcessSwitch(dom::CanonicalBrowsingContext* aContext,
+ const dom::NavigationIsolationOptions& aOptions,
+ bool aIsNewTab = false);
+
+ // A helper for TriggerRedirectToRealChannel that abstracts over
+ // the same-process and cross-process switch cases and returns
+ // a single promise to wait on.
+ using ParentEndpoint =
+ mozilla::ipc::Endpoint<extensions::PStreamFilterParent>;
+ RefPtr<PDocumentChannelParent::RedirectToRealChannelPromise>
+ RedirectToRealChannel(uint32_t aRedirectFlags, uint32_t aLoadFlags,
+ const Maybe<dom::ContentParent*>& aDestinationProcess,
+ nsTArray<ParentEndpoint>&& aStreamFilterEndpoints);
+
+ // A helper for RedirectToRealChannel that handles the case where we started
+ // from a content process and are process switching into the parent process.
+ RefPtr<PDocumentChannelParent::RedirectToRealChannelPromise>
+ RedirectToParentProcess(uint32_t aRedirectFlags, uint32_t aLoadFlags);
+
+ // Return the Browsing Context that is performing the load.
+ // For document loads, the BC is the one that the (sub)doc
+ // will load into. For <object>/<embed>, it's the embedder document's BC.
+ dom::CanonicalBrowsingContext* GetLoadingBrowsingContext() const;
+
+ // Return the Browsing Context that document is being loaded into. For
+ // non-document loads, this will return nullptr.
+ dom::CanonicalBrowsingContext* GetDocumentBrowsingContext() const;
+ dom::CanonicalBrowsingContext* GetTopBrowsingContext() const;
+
+ // Return the Window Context which which contains the element which the load
+ // is being performed in. For toplevel loads, this will return `nullptr`.
+ dom::WindowGlobalParent* GetParentWindowContext() const;
+
+ void AddURIVisit(nsIChannel* aChannel, uint32_t aLoadFlags);
+ bool HasCrossOriginOpenerPolicyMismatch() const;
+ void ApplyPendingFunctions(nsIParentChannel* aChannel) const;
+
+ void Disconnect(bool aContinueNavigating);
+
+ void MaybeReportBlockedByURLClassifier(nsresult aStatus);
+
+ // Returns true if a channel with aStatus will display
+ // some sort of content (could be the actual channel data,
+ // attempt a uri fixup and new load, or an error page).
+ // Returns false if the docshell will ignore the load entirely.
+ bool DocShellWillDisplayContent(nsresult aStatus);
+
+ void FireStateChange(uint32_t aStateFlags, nsresult aStatus);
+
+ // Returns true if this is a failed load, where we have successfully
+ // created a fixed URI to attempt loading instead.
+ // If successful, this calls DisconnectListeners to completely finish
+ // the current load, and calls BrowsingContext::LoadURI to start the new one.
+ bool MaybeHandleLoadErrorWithURIFixup(nsresult aStatus);
+
+ // This defines a variant that describes all the attribute setters (and their
+ // parameters) from nsIParentChannel
+ //
+ // SetClassifierMatchedInfo(const nsACString& aList, const nsACString&
+ // aProvider, const nsACString& aFullHash) = 0;
+ // SetClassifierMatchedTrackingInfo(const nsACString& aLists, const
+ // nsACString& aFullHashes) = 0; NotifyClassificationFlags(uint32_t
+ // aClassificationFlags, bool aIsThirdParty) = 0;
+ struct ClassifierMatchedInfoParams {
+ nsCString mList;
+ nsCString mProvider;
+ nsCString mFullHash;
+ };
+
+ struct ClassifierMatchedTrackingInfoParams {
+ nsCString mLists;
+ nsCString mFullHashes;
+ };
+
+ struct ClassificationFlagsParams {
+ uint32_t mClassificationFlags;
+ bool mIsThirdParty;
+ };
+
+ using IParentChannelFunction =
+ mozilla::Variant<ClassifierMatchedInfoParams,
+ ClassifierMatchedTrackingInfoParams,
+ ClassificationFlagsParams>;
+
+ // Store a list of all the attribute setters that have been called on this
+ // channel, so that we can repeat them on the real channel that we redirect
+ // to.
+ nsTArray<IParentChannelFunction> mIParentChannelFunctions;
+
+ // This defines a variant this describes all the functions
+ // from HttpChannelSecurityWarningReporter so that we can forward
+ // them on to the real channel.
+
+ struct ReportSecurityMessageParams {
+ nsString mMessageTag;
+ nsString mMessageCategory;
+ };
+
+ struct LogBlockedCORSRequestParams {
+ nsString mMessage;
+ nsCString mCategory;
+ bool mIsWarning;
+ };
+
+ struct LogMimeTypeMismatchParams {
+ nsCString mMessageName;
+ bool mWarning = false;
+ nsString mURL;
+ nsString mContentType;
+ };
+
+ using SecurityWarningFunction =
+ mozilla::Variant<ReportSecurityMessageParams, LogBlockedCORSRequestParams,
+ LogMimeTypeMismatchParams>;
+ nsTArray<SecurityWarningFunction> mSecurityWarningFunctions;
+
+ // TODO Backtrack this.
+ // The set of nsIStreamListener functions that got called on this
+ // listener, so that we can replay them onto the replacement channel's
+ // listener. This should generally only be OnStartRequest, since we
+ // Suspend() the channel at that point, but it can fail sometimes
+ // so we have to support holding a list.
+ nsTArray<StreamListenerFunction> mStreamListenerFunctions;
+
+ nsCOMPtr<nsIChannel> mChannel;
+
+ Maybe<uint64_t> mDocumentChannelId;
+
+ // An instance of ParentChannelListener that we use as a listener
+ // between mChannel (and any future redirected mChannels) and us.
+ // This handles service worker interception, and retargetting
+ // OnDataAvailable/OnStopRequest messages onto the listener that
+ // replaces us.
+ RefPtr<ParentChannelListener> mParentChannelListener;
+
+ // Get the channel creation URI for constructing the channel in the content
+ // process. See the function for more details.
+ nsIURI* GetChannelCreationURI() const;
+
+ // The original navigation timing information containing various timestamps
+ // such as when the original load started.
+ // This will be passed back to the new content process should a process
+ // switch occurs.
+ RefPtr<nsDOMNavigationTiming> mTiming;
+
+ net::EarlyHintsService mEarlyHintsService;
+
+ // An optional ObjectUpgradeHandler which can be used to upgrade an <object>
+ // or <embed> element to contain a nsFrameLoader, allowing us to switch them
+ // into a different process.
+ //
+ // A weak pointer is held in order to avoid reference cycles.
+ WeakPtr<ObjectUpgradeHandler> mObjectUpgradeHandler;
+
+ // Used to identify an internal redirect in redirect chain.
+ // True when we have seen at least one non-interal redirect.
+ bool mHaveVisibleRedirect = false;
+
+ // Pending stream filter requests which should be attached when redirecting to
+ // the real channel. Moved into `TriggerRedirectToRealChannel` when the
+ // connection is ready.
+ nsTArray<StreamFilterRequest> mStreamFilterRequests;
+
+ nsString mSrcdocData;
+ nsCOMPtr<nsIURI> mBaseURI;
+
+ mozilla::UniquePtr<mozilla::dom::LoadingSessionHistoryInfo>
+ mLoadingSessionHistoryInfo;
+
+ RefPtr<dom::WindowGlobalParent> mParentWindowContext;
+
+ // Flags from nsDocShellLoadState::LoadFlags/Type that we want to make
+ // available to the new docshell if we switch processes.
+ uint32_t mLoadStateExternalLoadFlags = 0;
+ uint32_t mLoadStateInternalLoadFlags = 0;
+ uint32_t mLoadStateLoadType = 0;
+
+ // Indicates if this load is a download.
+ bool mIsDownload = false;
+
+ // Indicates if we are loading a javascript URI.
+ bool mIsLoadingJSURI = false;
+
+ // Corresponding redirect channel registrar Id for the final channel that
+ // we want to use when redirecting the child, or doing a process switch.
+ // 0 means redirection is not started.
+ uint64_t mRedirectChannelId = 0;
+ // Set to true once we initiate the redirect to a real channel (either
+ // via a process switch or a same-process redirect, and Suspend the
+ // underlying channel.
+ bool mInitiatedRedirectToRealChannel = false;
+ // The value of GetApplyConversion on mChannel when OnStartRequest
+ // was called. We override it to false to prevent a conversion
+ // helper from being installed, but we need to restore the value
+ // later.
+ bool mOldApplyConversion = false;
+ // Set to true if any previous channel that we redirected away
+ // from had a COOP mismatch.
+ bool mHasCrossOriginOpenerPolicyMismatch = false;
+ // Set to true if we've received OnStopRequest, and shouldn't
+ // setup a reference from the ParentChannelListener to the replacement
+ // channel.
+ bool mIsFinished = false;
+
+ // The id of the currently pending load which is
+ // passed to the childChannel in order to identify it in the new process.
+ uint64_t mLoadIdentifier = 0;
+
+ Maybe<nsCString> mOriginalUriString;
+
+ // Parent-initiated loads do not support redirects to real channels.
+ bool mSupportsRedirectToRealChannel = true;
+
+ Maybe<nsCString> mRemoteTypeOverride;
+
+ // The ContentParent which this channel is currently connected to, or nullptr
+ // if connected to the parent process.
+ RefPtr<dom::ContentParent> mContentParent;
+
+ void RejectOpenPromise(nsresult aStatus, nsresult aLoadGroupStatus,
+ bool aContinueNavigating, const char* aLocation) {
+ // It is possible for mOpenPromise to not be set if AsyncOpen failed and
+ // the DocumentChannel got canceled.
+ if (!mOpenPromiseResolved && mOpenPromise) {
+ mOpenPromise->Reject(OpenPromiseFailedType({aStatus, aLoadGroupStatus,
+ aContinueNavigating}),
+ aLocation);
+ mOpenPromiseResolved = true;
+ }
+ }
+ RefPtr<OpenPromise::Private> mOpenPromise;
+ bool mOpenPromiseResolved = false;
+
+ const bool mIsDocumentLoad;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(DocumentLoadListener, DOCUMENT_LOAD_LISTENER_IID)
+
+inline nsISupports* ToSupports(DocumentLoadListener* aObj) {
+ return static_cast<nsIInterfaceRequestor*>(aObj);
+}
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozilla_net_DocumentChannelParent_h
diff --git a/netwerk/ipc/InputChannelThrottleQueueChild.cpp b/netwerk/ipc/InputChannelThrottleQueueChild.cpp
new file mode 100644
index 0000000000..ef7a916b79
--- /dev/null
+++ b/netwerk/ipc/InputChannelThrottleQueueChild.cpp
@@ -0,0 +1,30 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:set ts=4 sw=4 sts=4 et cin: */
+/* 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 "InputChannelThrottleQueueChild.h"
+#include "nsThreadUtils.h"
+
+namespace mozilla {
+namespace net {
+
+NS_IMPL_ISUPPORTS_INHERITED0(InputChannelThrottleQueueChild, ThrottleQueue)
+
+NS_IMETHODIMP
+InputChannelThrottleQueueChild::RecordRead(uint32_t aBytesRead) {
+ ThrottleQueue::RecordRead(aBytesRead);
+
+ RefPtr<InputChannelThrottleQueueChild> self = this;
+ NS_DispatchToMainThread(NS_NewRunnableFunction(
+ "InputChannelThrottleQueueChild::RecordRead", [self, aBytesRead]() {
+ if (self->CanSend()) {
+ Unused << self->SendRecordRead(aBytesRead);
+ }
+ }));
+ return NS_OK;
+}
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/ipc/InputChannelThrottleQueueChild.h b/netwerk/ipc/InputChannelThrottleQueueChild.h
new file mode 100644
index 0000000000..a758dac417
--- /dev/null
+++ b/netwerk/ipc/InputChannelThrottleQueueChild.h
@@ -0,0 +1,33 @@
+/* -*- 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 InputChannelThrottleQueueChild_h__
+#define InputChannelThrottleQueueChild_h__
+
+#include "mozilla/net/PInputChannelThrottleQueueChild.h"
+#include "mozilla/net/ThrottleQueue.h"
+#include "nsISupportsImpl.h"
+
+namespace mozilla {
+namespace net {
+
+class InputChannelThrottleQueueChild final
+ : public PInputChannelThrottleQueueChild,
+ public ThrottleQueue {
+ public:
+ friend class PInputChannelThrottleQueueChild;
+ NS_DECL_ISUPPORTS_INHERITED
+
+ explicit InputChannelThrottleQueueChild() = default;
+ NS_IMETHOD RecordRead(uint32_t aBytesRead) override;
+
+ private:
+ virtual ~InputChannelThrottleQueueChild() = default;
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // InputChannelThrottleQueueChild_h__
diff --git a/netwerk/ipc/InputChannelThrottleQueueParent.cpp b/netwerk/ipc/InputChannelThrottleQueueParent.cpp
new file mode 100644
index 0000000000..d1e6e82f9f
--- /dev/null
+++ b/netwerk/ipc/InputChannelThrottleQueueParent.cpp
@@ -0,0 +1,125 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:set ts=4 sw=4 sts=4 et cin: */
+/* 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 "InputChannelThrottleQueueParent.h"
+#include "mozilla/net/SocketProcessParent.h"
+#include "nsIOService.h"
+
+namespace mozilla {
+namespace net {
+
+NS_IMPL_ADDREF(InputChannelThrottleQueueParent)
+NS_INTERFACE_MAP_BEGIN(InputChannelThrottleQueueParent)
+ NS_INTERFACE_MAP_ENTRY(nsIInputChannelThrottleQueue)
+ NS_INTERFACE_MAP_ENTRY(nsISupports)
+ NS_INTERFACE_MAP_ENTRY_CONCRETE(InputChannelThrottleQueueParent)
+NS_INTERFACE_MAP_END
+
+NS_IMETHODIMP_(MozExternalRefCountType)
+InputChannelThrottleQueueParent::Release(void) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release");
+
+ if (!nsAutoRefCnt::isThreadSafe) {
+ NS_ASSERT_OWNINGTHREAD(InputChannelThrottleQueueParent);
+ }
+
+ nsrefcnt count = --mRefCnt;
+ NS_LOG_RELEASE(this, count, "InputChannelThrottleQueueParent");
+
+ if (count == 0) {
+ if (!nsAutoRefCnt::isThreadSafe) {
+ NS_ASSERT_OWNINGTHREAD(InputChannelThrottleQueueParent);
+ }
+
+ mRefCnt = 1; /* stabilize */
+ delete (this);
+ return 0;
+ }
+
+ // When ref count goes down to 1 (held internally by IPDL), it means that
+ // we are done with this ThrottleQueue. We should send a delete message
+ // to delete the InputChannelThrottleQueueChild in socket process.
+ if (count == 1 && CanSend()) {
+ mozilla::Unused << Send__delete__(this);
+ return 1;
+ }
+ return count;
+}
+
+mozilla::ipc::IPCResult InputChannelThrottleQueueParent::RecvRecordRead(
+ const uint32_t& aBytesRead) {
+ mBytesProcessed += aBytesRead;
+ return IPC_OK();
+}
+
+NS_IMETHODIMP
+InputChannelThrottleQueueParent::RecordRead(uint32_t aBytesRead) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+InputChannelThrottleQueueParent::Available(uint32_t aRemaining,
+ uint32_t* aAvailable) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+InputChannelThrottleQueueParent::Init(uint32_t aMeanBytesPerSecond,
+ uint32_t aMaxBytesPerSecond) {
+ // Can be called on any thread.
+ if (aMeanBytesPerSecond == 0 || aMaxBytesPerSecond == 0 ||
+ aMaxBytesPerSecond < aMeanBytesPerSecond) {
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+
+ mMeanBytesPerSecond = aMeanBytesPerSecond;
+ mMaxBytesPerSecond = aMaxBytesPerSecond;
+
+ RefPtr<InputChannelThrottleQueueParent> self = this;
+ gIOService->CallOrWaitForSocketProcess(
+ [self, meanBytesPerSecond(mMeanBytesPerSecond),
+ maxBytesPerSecond(mMaxBytesPerSecond)] {
+ Unused << SocketProcessParent::GetSingleton()
+ ->SendPInputChannelThrottleQueueConstructor(
+ self, meanBytesPerSecond, maxBytesPerSecond);
+ });
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+InputChannelThrottleQueueParent::BytesProcessed(uint64_t* aResult) {
+ *aResult = mBytesProcessed;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+InputChannelThrottleQueueParent::WrapStream(nsIInputStream* aInputStream,
+ nsIAsyncInputStream** aResult) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+InputChannelThrottleQueueParent::GetMeanBytesPerSecond(
+ uint32_t* aMeanBytesPerSecond) {
+ NS_ENSURE_ARG(aMeanBytesPerSecond);
+
+ *aMeanBytesPerSecond = mMeanBytesPerSecond;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+InputChannelThrottleQueueParent::GetMaxBytesPerSecond(
+ uint32_t* aMaxBytesPerSecond) {
+ NS_ENSURE_ARG(aMaxBytesPerSecond);
+
+ *aMaxBytesPerSecond = mMaxBytesPerSecond;
+ return NS_OK;
+}
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/ipc/InputChannelThrottleQueueParent.h b/netwerk/ipc/InputChannelThrottleQueueParent.h
new file mode 100644
index 0000000000..74d1a00bd7
--- /dev/null
+++ b/netwerk/ipc/InputChannelThrottleQueueParent.h
@@ -0,0 +1,51 @@
+/* -*- 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 InputChannelThrottleQueueParent_h__
+#define InputChannelThrottleQueueParent_h__
+
+#include "nsISupportsImpl.h"
+#include "nsIThrottledInputChannel.h"
+#include "mozilla/net/PInputChannelThrottleQueueParent.h"
+
+namespace mozilla {
+namespace net {
+
+#define INPUT_CHANNEL_THROTTLE_QUEUE_PARENT_IID \
+ { \
+ 0x4f151655, 0x70b3, 0x4350, { \
+ 0x9b, 0xd9, 0xe3, 0x2b, 0xe5, 0xeb, 0xb2, 0x9e \
+ } \
+ }
+
+class InputChannelThrottleQueueParent final
+ : public PInputChannelThrottleQueueParent,
+ public nsIInputChannelThrottleQueue {
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIINPUTCHANNELTHROTTLEQUEUE
+ NS_DECLARE_STATIC_IID_ACCESSOR(INPUT_CHANNEL_THROTTLE_QUEUE_PARENT_IID)
+
+ friend class PInputChannelThrottleQueueParent;
+
+ explicit InputChannelThrottleQueueParent() = default;
+ mozilla::ipc::IPCResult RecvRecordRead(const uint32_t& aBytesRead);
+ void ActorDestroy(ActorDestroyReason aWhy) override {}
+
+ private:
+ virtual ~InputChannelThrottleQueueParent() = default;
+
+ uint64_t mBytesProcessed{0};
+ uint32_t mMeanBytesPerSecond{0};
+ uint32_t mMaxBytesPerSecond{0};
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(InputChannelThrottleQueueParent,
+ INPUT_CHANNEL_THROTTLE_QUEUE_PARENT_IID)
+
+} // namespace net
+} // namespace mozilla
+
+#endif // InputChannelThrottleQueueParent_h__
diff --git a/netwerk/ipc/NeckoChannelParams.ipdlh b/netwerk/ipc/NeckoChannelParams.ipdlh
new file mode 100644
index 0000000000..44af8d4808
--- /dev/null
+++ b/netwerk/ipc/NeckoChannelParams.ipdlh
@@ -0,0 +1,623 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 ft=c: */
+
+/* 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 protocol PHttpChannel;
+include ClientIPCTypes;
+include URIParams;
+include IPCServiceWorkerDescriptor;
+include IPCStream;
+include PBackgroundSharedTypes;
+include DOMTypes;
+include ProtocolTypes;
+
+include "mozilla/dom/FetchIPCTypes.h";
+include "mozilla/dom/PropertyBagUtils.h";
+include "mozilla/dom/ReferrerInfoUtils.h";
+include "mozilla/ipc/URIUtils.h";
+include "mozilla/net/CacheInfoIPCTypes.h";
+include "mozilla/AntiTrackingIPCUtils.h";
+include "mozilla/net/ClassOfService.h";
+
+using mozilla::OriginAttributes from "mozilla/ipc/BackgroundUtils.h";
+using mozilla::net::RequestHeaderTuples from "mozilla/net/PHttpChannelParams.h";
+using struct nsHttpAtom from "nsHttp.h";
+using class mozilla::net::nsHttpResponseHead from "nsHttpResponseHead.h";
+using class mozilla::TimeStamp from "mozilla/TimeStamp.h";
+[RefCounted] using class nsIPropertyBag2 from "nsIPropertyBag2.h";
+[RefCounted] using class nsDOMNavigationTiming from "nsDOMNavigationTiming.h";
+[RefCounted] using class nsDocShellLoadState from "nsDocShellLoadState.h";
+using nsContentPolicyType from "nsIContentPolicy.h";
+using mozilla::net::PreferredAlternativeDataDeliveryTypeIPC from "nsICacheInfoChannel.h";
+using nsILoadInfo::CrossOriginEmbedderPolicy from "nsILoadInfo.h";
+using nsILoadInfo::StoragePermissionState from "nsILoadInfo.h";
+using struct mozilla::dom::LoadingSessionHistoryInfo from "mozilla/dom/SessionHistoryEntry.h";
+using mozilla::dom::RequestMode from "mozilla/dom/RequestBinding.h";
+using mozilla::net::LinkHeader from "nsNetUtil.h";
+
+namespace mozilla {
+namespace net {
+
+//-----------------------------------------------------------------------------
+// CookieJarSettings IPDL structs
+//-----------------------------------------------------------------------------
+
+struct CookiePermissionData
+{
+ PrincipalInfo principalInfo;
+ uint32_t cookiePermission;
+};
+
+struct CookieJarSettingsArgs
+{
+ // Copy of the cookie jar settings for the top-level document.
+ uint32_t cookieBehavior;
+ bool isFirstPartyIsolated;
+ bool shouldResistFingerprinting;
+ bool isOnContentBlockingAllowList;
+ CookiePermissionData[] cookiePermissions;
+ bool isFixed;
+ nsString partitionKey;
+ bool hasFingerprintingRandomizationKey;
+ uint8_t[] fingerprintingRandomizationKey;
+};
+
+//-----------------------------------------------------------------------------
+// Preferrer alternative data type
+//-----------------------------------------------------------------------------
+
+struct PreferredAlternativeDataTypeParams
+{
+ nsCString type;
+ nsCString contentType;
+ PreferredAlternativeDataDeliveryTypeIPC deliverAltData;
+};
+
+//-----------------------------------------------------------------------------
+// LoadInfo IPDL structs
+//-----------------------------------------------------------------------------
+
+struct RedirectHistoryEntryInfo
+{
+ PrincipalInfo principalInfo;
+ URIParams? referrerUri;
+ nsCString remoteAddress;
+};
+
+struct InterceptionInfoArg
+{
+ PrincipalInfo? triggeringPrincipalInfo;
+ nsContentPolicyType contentPolicyType;
+ RedirectHistoryEntryInfo[] redirectChain;
+ bool fromThirdParty;
+};
+
+struct LoadInfoArgs
+{
+ PrincipalInfo? requestingPrincipalInfo;
+ PrincipalInfo triggeringPrincipalInfo;
+ PrincipalInfo? principalToInheritInfo;
+ PrincipalInfo? topLevelPrincipalInfo;
+ URIParams? resultPrincipalURI;
+ nsCString triggeringRemoteType;
+ nsID sandboxedNullPrincipalID;
+ uint32_t securityFlags;
+ uint32_t sandboxFlags;
+ uint32_t triggeringSandboxFlags;
+ uint64_t triggeringWindowId;
+ bool triggeringStorageAccess;
+ nsContentPolicyType contentPolicyType;
+ uint32_t tainting;
+ bool blockAllMixedContent;
+ bool upgradeInsecureRequests;
+ bool browserUpgradeInsecureRequests;
+ bool browserDidUpgradeInsecureRequests;
+ bool browserWouldUpgradeInsecureRequests;
+ bool forceAllowDataURI;
+ bool allowInsecureRedirectToDataURI;
+ bool skipContentPolicyCheckForWebRequest;
+ bool originalFrameSrcLoad;
+ bool forceInheritPrincipalDropped;
+ uint64_t innerWindowID;
+ uint64_t browsingContextID;
+ uint64_t frameBrowsingContextID;
+ bool initialSecurityCheckDone;
+ bool isInThirdPartyContext;
+ bool? isThirdPartyContextToTopWindow;
+ bool isFormSubmission;
+ bool sendCSPViolationEvents;
+ OriginAttributes originAttributes;
+ RedirectHistoryEntryInfo[] redirectChainIncludingInternalRedirects;
+ RedirectHistoryEntryInfo[] redirectChain;
+ bool hasInjectedCookieForCookieBannerHandling;
+ bool wasSchemelessInput;
+
+ /**
+ * ClientInfo structure representing the window or worker that triggered
+ * this network request. May be Nothing if its a system internal request.
+ */
+ IPCClientInfo? clientInfo;
+
+ /**
+ * Non-subresource requests will result in the creation of a window or
+ * worker client. The reserved and initial ClientInfo values represent
+ * this resulting client. An initial ClientInfo represents an initial
+ * about:blank window that will be re-used while a reserved ClientInfo
+ * represents a to-be-newly-created window/worker.
+ */
+ IPCClientInfo? reservedClientInfo;
+ IPCClientInfo? initialClientInfo;
+
+ /**
+ * Subresource loads may have a controller set based on their owning
+ * window/worker client. We must send this across IPC to support
+ * performing interception in the parent.
+ */
+ IPCServiceWorkerDescriptor? controller;
+
+ nsCString[] corsUnsafeHeaders;
+ bool forcePreflight;
+ bool isPreflight;
+ bool loadTriggeredFromExternal;
+ bool serviceWorkerTaintingSynthesized;
+ bool documentHasUserInteracted;
+ bool allowListFutureDocumentsCreatedFromThisRedirectChain;
+ bool needForCheckingAntiTrackingHeuristic;
+ nsString cspNonce;
+ nsString integrityMetadata;
+ bool skipContentSniffing;
+ uint32_t httpsOnlyStatus;
+ bool hstsStatus;
+ bool hasValidUserGestureActivation;
+ bool allowDeprecatedSystemRequests;
+ bool isInDevToolsContext;
+ bool parserCreatedScript;
+ bool isFromProcessingFrameAttributes;
+ bool isMediaRequest;
+ bool isMediaInitialRequest;
+ bool isFromObjectOrEmbed;
+ CookieJarSettingsArgs cookieJarSettings;
+ uint32_t requestBlockingReason;
+ CSPInfo? cspToInheritInfo;
+ StoragePermissionState storagePermission;
+ uint64_t? overriddenFingerprintingSettings;
+ bool isMetaRefresh;
+ CrossOriginEmbedderPolicy loadingEmbedderPolicy;
+ bool originTrialCoepCredentiallessEnabledForTopLevel;
+ nullable nsIURI unstrippedURI;
+ InterceptionInfoArg? interceptionInfo;
+};
+
+/**
+ * This structure is used to carry selected properties of a LoadInfo
+ * object to child processes to merge LoadInfo changes from the parent
+ * process. We don't want to use LoadInfoArgs for that since it's
+ * too huge and we only care about small subpart of properties anyway.
+ */
+struct ParentLoadInfoForwarderArgs
+{
+ // WebExtextensions' WebRequest API allows extensions to intercept and
+ // redirect a channel to a data URI. This modifications happens in
+ // the parent and needs to be mirrored to the child so that security
+ // checks can pass.
+ bool allowInsecureRedirectToDataURI;
+
+ // The ServiceWorker controller that may be set in the parent when
+ // interception occurs.
+ IPCServiceWorkerDescriptor? controller;
+
+ // The service worker may synthesize a Response with a particular
+ // tainting value.
+ uint32_t tainting;
+
+ // This flag is used for any browsing context where we should not sniff
+ // the content type. E.g if an iframe has the XCTO nosniff header, then
+ // that flag is set to true so we skip content sniffing for that browsing
+ bool skipContentSniffing;
+
+ uint32_t httpsOnlyStatus;
+
+ bool hstsStatus;
+
+ // Returns true if at the time of the loadinfo construction the document
+ // that triggered this load has the bit hasValidTransientUserGestureActivation
+ // set or the load was triggered from External. (Mostly this bool is used
+ // in the context of Sec-Fetch-User.)
+ bool hasValidUserGestureActivation;
+
+ // The SystemPrincipal is disallowed to make requests to the public web
+ // and all requests will be cancelled. Setting this flag to true prevents
+ // the request from being cancelled.
+ bool allowDeprecatedSystemRequests;
+
+ bool isInDevToolsContext;
+
+ // Only ever returns true if the loadinfo is of TYPE_SCRIPT and
+ // the script was created by the HTML parser.
+ bool parserCreatedScript;
+
+ // Sandbox Flags of the Document that triggered the load
+ uint32_t triggeringSandboxFlags;
+
+ // Window ID and UsingStorageAccess of the Document that triggered the load.
+ // Used by the Storage Access API to determine if SubDocument loads should
+ // be partitioned or not.
+ uint64_t triggeringWindowId;
+ bool triggeringStorageAccess;
+
+ // We must also note that the tainting value was explicitly set
+ // by the service worker.
+ bool serviceWorkerTaintingSynthesized;
+
+ bool documentHasUserInteracted;
+ bool allowListFutureDocumentsCreatedFromThisRedirectChain;
+
+ CookieJarSettingsArgs? cookieJarSettings;
+
+ uint32_t requestBlockingReason;
+
+ StoragePermissionState storagePermission;
+
+ uint64_t? overriddenFingerprintingSettings;
+
+ bool isMetaRefresh;
+
+ bool? isThirdPartyContextToTopWindow;
+
+ bool isInThirdPartyContext;
+
+ nullable nsIURI unstrippedURI;
+
+ // IMPORTANT: when you add new properites here you must also update
+ // LoadInfoToParentLoadInfoForwarder and MergeParentLoadInfoForwarder
+ // in BackgroundUtils.cpp/.h!
+};
+
+/**
+ * This structure is used to carry selected properties of a LoadInfo
+ * object to the parent process that might have changed in the child
+ * during a redirect. We don't want to use LoadInfoArgs for that since
+ * it's too huge and we only care about small subpart of properties
+ * anyway.
+ */
+struct ChildLoadInfoForwarderArgs
+{
+ // The reserved and initial ClientInfo values may change during a
+ // redirect if the new channel is cross-origin to the old channel.
+ IPCClientInfo? reservedClientInfo;
+ IPCClientInfo? initialClientInfo;
+
+ // The ServiceWorker controller may be cleared in the child during
+ // a redirect.
+ IPCServiceWorkerDescriptor? controller;
+
+ uint32_t requestBlockingReason;
+};
+
+//-----------------------------------------------------------------------------
+// HTTP IPDL structs
+//-----------------------------------------------------------------------------
+
+struct CorsPreflightArgs
+{
+ nsCString[] unsafeHeaders;
+};
+
+struct HttpChannelOpenArgs
+{
+ nullable nsIURI uri;
+ // - TODO: bug 571161: unclear if any HTTP channel clients ever
+ // set originalURI != uri (about:credits?); also not clear if
+ // chrome channel would ever need to know. Get rid of next arg?
+ nullable nsIURI original;
+ nullable nsIURI doc;
+ nullable nsIReferrerInfo referrerInfo;
+ nullable nsIURI apiRedirectTo;
+ nullable nsIURI topWindowURI;
+ RequestHeaderTuples requestHeaders;
+ PreferredAlternativeDataTypeParams[] preferredAlternativeTypes;
+ TimeStamp launchServiceWorkerStart;
+ TimeStamp launchServiceWorkerEnd;
+ TimeStamp dispatchFetchEventStart;
+ TimeStamp dispatchFetchEventEnd;
+ TimeStamp handleFetchEventStart;
+ TimeStamp handleFetchEventEnd;
+ TimeStamp navigationStartTimeStamp;
+ uint64_t startPos;
+ uint64_t requestContextID;
+ uint64_t channelId;
+ uint64_t contentWindowId;
+ uint64_t browserId;
+ uint64_t earlyHintPreloaderId;
+ nsCString requestMethod;
+ ClassOfService classOfService;
+ nsCString entityID;
+ nsCString appCacheClientID;
+ CorsPreflightArgs? preflightArgs;
+ nsCString contentTypeHint;
+ nsString integrityMetadata;
+ IPCStream? uploadStream;
+ LoadInfoArgs loadInfo;
+ uint32_t loadFlags;
+ uint32_t thirdPartyFlags;
+ uint32_t tlsFlags;
+ uint32_t cacheKey;
+ uint32_t initialRwin;
+ uint32_t redirectMode;
+ int16_t priority;
+ bool uploadStreamHasHeaders;
+ bool allowSTS;
+ bool resumeAt;
+ bool allowSpdy;
+ bool allowHttp3;
+ bool allowAltSvc;
+ bool beConservative;
+ bool bypassProxy;
+ bool blockAuthPrompt;
+ bool allowStaleCacheContent;
+ RequestMode requestMode;
+ bool forceValidateCacheContent;
+ bool preferCacheLoadOverBypass;
+ bool forceMainDocumentChannel;
+ uint8_t redirectionLimit;
+ nsString classicScriptHintCharset;
+ nsString documentCharacterSet;
+ bool isUserAgentHeaderModified;
+};
+
+struct HttpChannelConnectArgs
+{
+ uint32_t registrarId;
+};
+
+union HttpChannelCreationArgs
+{
+ HttpChannelOpenArgs; // For AsyncOpen: the common case.
+ HttpChannelConnectArgs; // Used for redirected-to channels
+};
+
+struct ProxyInfoCloneArgs
+{
+ nsCString type;
+ nsCString host;
+ int32_t port;
+ nsCString username;
+ nsCString password;
+ uint32_t flags;
+ uint32_t timeout;
+ uint32_t resolveFlags;
+ nsCString proxyAuthorizationHeader;
+ nsCString connectionIsolationKey;
+};
+
+struct HttpConnectionInfoCloneArgs
+{
+ nsCString host;
+ int32_t port;
+ nsCString npnToken;
+ nsCString username;
+ OriginAttributes originAttributes;
+ bool endToEndSSL;
+ nsCString routedHost;
+ int32_t routedPort;
+ bool anonymous;
+ bool aPrivate; // use prefix to avoid code generation error
+ bool insecureScheme;
+ bool noSpdy;
+ bool beConservative;
+ bool bypassProxy;
+ bool anonymousAllowClientCert;
+ bool fallbackConnection;
+ uint32_t tlsFlags;
+ bool isolated;
+ bool isTrrServiceChannel;
+ uint8_t trrMode;
+ bool isIPv4Disabled;
+ bool isIPv6Disabled;
+ nsCString topWindowOrigin;
+ bool isHttp3;
+ bool webTransport;
+ bool hasIPHintAddress;
+ nsCString echConfig;
+ ProxyInfoCloneArgs[] proxyInfo;
+};
+
+struct ConsoleReportCollected {
+ uint32_t errorFlags;
+ nsCString category;
+ uint32_t propertiesFile;
+ nsCString sourceFileURI;
+ uint32_t lineNumber;
+ uint32_t columnNumber;
+ nsCString messageName;
+ nsString[] stringParams;
+};
+
+struct CookieStruct
+{
+ nsCString name;
+ nsCString value;
+ nsCString host;
+ nsCString path;
+ int64_t expiry;
+ int64_t lastAccessed;
+ int64_t creationTime;
+ bool isHttpOnly;
+ bool isSession;
+ bool isSecure;
+ bool isPartitioned;
+ int32_t sameSite;
+ int32_t rawSameSite;
+ uint8_t schemeMap;
+};
+
+struct DocumentCreationArgs {
+ bool uriModified;
+ bool isEmbeddingBlockedError;
+};
+
+struct ObjectCreationArgs {
+ uint64_t embedderInnerWindowId;
+ uint32_t loadFlags;
+ nsContentPolicyType contentPolicyType;
+ bool isUrgentStart;
+};
+
+union DocumentChannelElementCreationArgs {
+ DocumentCreationArgs;
+ ObjectCreationArgs;
+};
+
+struct DocumentChannelCreationArgs {
+ nsDocShellLoadState loadState;
+ TimeStamp asyncOpenTime;
+ uint64_t channelId;
+ uint32_t cacheKey;
+ nullable nsDOMNavigationTiming timing;
+ IPCClientInfo? initialClientInfo;
+ DocumentChannelElementCreationArgs elementCreationArgs;
+ uint64_t parentInitiatedNavigationEpoch;
+};
+
+struct EarlyHintConnectArgs {
+ LinkHeader link;
+ uint64_t earlyHintPreloaderId;
+};
+
+struct RedirectToRealChannelArgs {
+ uint32_t registrarId;
+ nullable nsIURI uri;
+ uint32_t newLoadFlags;
+ ReplacementChannelConfigInit? init;
+ LoadInfoArgs loadInfo;
+ uint64_t channelId;
+ nullable nsIURI originalURI;
+ uint32_t redirectMode;
+ uint32_t redirectFlags;
+ uint32_t? contentDisposition;
+ nsString? contentDispositionFilename;
+ nullable nsIPropertyBag2 properties;
+ uint32_t loadStateExternalLoadFlags;
+ uint32_t loadStateInternalLoadFlags;
+ uint32_t loadStateLoadType;
+ nullable nsDOMNavigationTiming timing;
+ nsString srcdocData;
+ nullable nsIURI baseUri;
+ LoadingSessionHistoryInfo? loadingSessionHistoryInfo;
+ uint64_t loadIdentifier;
+ nsCString? originalUriString;
+ EarlyHintConnectArgs[] earlyHints;
+ uint32_t earlyHintLinkType;
+};
+
+struct TimingStructArgs {
+ TimeStamp domainLookupStart;
+ TimeStamp domainLookupEnd;
+ TimeStamp connectStart;
+ TimeStamp tcpConnectEnd;
+ TimeStamp secureConnectionStart;
+ TimeStamp connectEnd;
+ TimeStamp requestStart;
+ TimeStamp responseStart;
+ TimeStamp responseEnd;
+ TimeStamp transactionPending;
+};
+
+struct ResourceTimingStructArgs {
+ TimeStamp domainLookupStart;
+ TimeStamp domainLookupEnd;
+ TimeStamp connectStart;
+ TimeStamp tcpConnectEnd;
+ TimeStamp secureConnectionStart;
+ TimeStamp connectEnd;
+ TimeStamp requestStart;
+ TimeStamp responseStart;
+ TimeStamp responseEnd;
+ TimeStamp fetchStart;
+ TimeStamp redirectStart;
+ TimeStamp redirectEnd;
+ uint64_t transferSize;
+ uint64_t encodedBodySize;
+
+ // Not actually part of resource timing, but not part of the transaction
+ // timings either. These need to be passed to HttpChannelChild along with
+ // the rest of the timings so the timing information in the child is complete.
+ TimeStamp cacheReadStart;
+ TimeStamp cacheReadEnd;
+
+ TimeStamp transactionPending;
+};
+
+struct HttpActivity
+{
+ nsCString host;
+ int32_t port;
+ bool endToEndSSL;
+};
+
+struct HttpConnectionActivity
+{
+ nsCString connInfoKey;
+ nsCString host;
+ int32_t port;
+ bool ssl;
+ bool hasECH;
+ bool isHttp3;
+};
+
+union HttpActivityArgs
+{
+ uint64_t;
+ HttpActivity;
+ HttpConnectionActivity;
+};
+
+struct TransactionObserverResult
+{
+ bool versionOk;
+ bool authOk;
+ nsresult closeReason;
+};
+
+struct SpeculativeConnectionOverriderArgs {
+ uint32_t parallelSpeculativeConnectLimit;
+ bool ignoreIdle;
+ bool isFromPredictor;
+ bool allow1918;
+};
+
+//-----------------------------------------------------------------------------
+// GIO IPDL structs
+//-----------------------------------------------------------------------------
+
+struct GIOChannelOpenArgs
+{
+ URIParams uri;
+ uint64_t startPos;
+ nsCString entityID;
+ IPCStream? uploadStream;
+ LoadInfoArgs loadInfo;
+ uint32_t loadFlags;
+};
+
+struct GIOChannelConnectArgs
+{
+ uint32_t channelId;
+};
+
+union GIOChannelCreationArgs
+{
+ GIOChannelOpenArgs; // For AsyncOpen: the common case.
+ GIOChannelConnectArgs; // Used for redirected-to channels
+};
+
+struct RemoteStreamInfo {
+ nullable nsIInputStream inputStream;
+ nsCString contentType;
+ int64_t contentLength;
+};
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/ipc/NeckoChild.cpp b/netwerk/ipc/NeckoChild.cpp
new file mode 100644
index 0000000000..fe2b3df0bf
--- /dev/null
+++ b/netwerk/ipc/NeckoChild.cpp
@@ -0,0 +1,337 @@
+
+/* 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 "nsHttp.h"
+#include "mozilla/net/NeckoChild.h"
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/BrowserChild.h"
+#include "mozilla/net/HttpChannelChild.h"
+#include "mozilla/net/ChildDNSService.h"
+#include "mozilla/net/CookieServiceChild.h"
+#include "mozilla/net/DataChannelChild.h"
+#ifdef MOZ_WIDGET_GTK
+# include "mozilla/net/GIOChannelChild.h"
+#endif
+#include "mozilla/net/FileChannelChild.h"
+#include "mozilla/net/WebSocketChannelChild.h"
+#include "mozilla/net/WebSocketEventListenerChild.h"
+#include "mozilla/net/DNSRequestChild.h"
+#include "mozilla/net/IPCTransportProvider.h"
+#include "mozilla/dom/network/TCPSocketChild.h"
+#include "mozilla/dom/network/TCPServerSocketChild.h"
+#include "mozilla/dom/network/UDPSocketChild.h"
+#include "mozilla/net/AltDataOutputStreamChild.h"
+#include "mozilla/net/SocketProcessBridgeChild.h"
+#ifdef MOZ_WEBRTC
+# include "mozilla/net/StunAddrsRequestChild.h"
+# include "mozilla/net/WebrtcTCPSocketChild.h"
+#endif
+
+#include "SerializedLoadContext.h"
+#include "nsGlobalWindowInner.h"
+#include "nsIOService.h"
+#include "nsINetworkPredictor.h"
+#include "nsINetworkPredictorVerifier.h"
+#include "nsINetworkLinkService.h"
+#include "nsQueryObject.h"
+#include "mozilla/ipc/URIUtils.h"
+#include "nsNetUtil.h"
+#include "SimpleChannel.h"
+
+using mozilla::dom::TCPServerSocketChild;
+using mozilla::dom::TCPSocketChild;
+using mozilla::dom::UDPSocketChild;
+
+namespace mozilla {
+namespace net {
+
+PNeckoChild* gNeckoChild = nullptr;
+
+// C++ file contents
+
+NeckoChild::~NeckoChild() {
+ // Send__delete__(gNeckoChild);
+ gNeckoChild = nullptr;
+}
+
+void NeckoChild::InitNeckoChild() {
+ if (!IsNeckoChild()) {
+ MOZ_ASSERT(false, "InitNeckoChild called by non-child!");
+ return;
+ }
+
+ if (!gNeckoChild) {
+ mozilla::dom::ContentChild* cpc =
+ mozilla::dom::ContentChild::GetSingleton();
+ NS_ASSERTION(cpc, "Content Protocol is NULL!");
+ if (NS_WARN_IF(cpc->IsShuttingDown())) {
+ return;
+ }
+ RefPtr<NeckoChild> child = new NeckoChild();
+ gNeckoChild = cpc->SendPNeckoConstructor(child);
+ NS_ASSERTION(gNeckoChild, "PNecko Protocol init failed!");
+ }
+}
+
+PStunAddrsRequestChild* NeckoChild::AllocPStunAddrsRequestChild() {
+ // We don't allocate here: instead we always use IPDL constructor that takes
+ // an existing object
+ MOZ_ASSERT_UNREACHABLE(
+ "AllocPStunAddrsRequestChild should not be called "
+ "on child");
+ return nullptr;
+}
+
+bool NeckoChild::DeallocPStunAddrsRequestChild(PStunAddrsRequestChild* aActor) {
+#ifdef MOZ_WEBRTC
+ StunAddrsRequestChild* p = static_cast<StunAddrsRequestChild*>(aActor);
+ p->ReleaseIPDLReference();
+#endif
+ return true;
+}
+
+PWebrtcTCPSocketChild* NeckoChild::AllocPWebrtcTCPSocketChild(
+ const Maybe<TabId>& tabId) {
+ // We don't allocate here: instead we always use IPDL constructor that takes
+ // an existing object
+ MOZ_ASSERT_UNREACHABLE(
+ "AllocPWebrtcTCPSocketChild should not be called on"
+ " child");
+ return nullptr;
+}
+
+bool NeckoChild::DeallocPWebrtcTCPSocketChild(PWebrtcTCPSocketChild* aActor) {
+#ifdef MOZ_WEBRTC
+ WebrtcTCPSocketChild* child = static_cast<WebrtcTCPSocketChild*>(aActor);
+ child->ReleaseIPDLReference();
+#endif
+ return true;
+}
+
+PAltDataOutputStreamChild* NeckoChild::AllocPAltDataOutputStreamChild(
+ const nsACString& type, const int64_t& predictedSize,
+ PHttpChannelChild* channel) {
+ // We don't allocate here: see HttpChannelChild::OpenAlternativeOutputStream()
+ MOZ_ASSERT_UNREACHABLE("AllocPAltDataOutputStreamChild should not be called");
+ return nullptr;
+}
+
+bool NeckoChild::DeallocPAltDataOutputStreamChild(
+ PAltDataOutputStreamChild* aActor) {
+ AltDataOutputStreamChild* child =
+ static_cast<AltDataOutputStreamChild*>(aActor);
+ child->ReleaseIPDLReference();
+ return true;
+}
+
+#ifdef MOZ_WIDGET_GTK
+PGIOChannelChild* NeckoChild::AllocPGIOChannelChild(
+ PBrowserChild* aBrowser, const SerializedLoadContext& aSerialized,
+ const GIOChannelCreationArgs& aOpenArgs) {
+ // We don't allocate here: see GIOChannelChild::AsyncOpen()
+ MOZ_CRASH("AllocPGIOChannelChild should not be called");
+ return nullptr;
+}
+
+bool NeckoChild::DeallocPGIOChannelChild(PGIOChannelChild* channel) {
+ MOZ_ASSERT(IsNeckoChild(), "DeallocPGIOChannelChild called by non-child!");
+
+ GIOChannelChild* child = static_cast<GIOChannelChild*>(channel);
+ child->ReleaseIPDLReference();
+ return true;
+}
+#endif
+
+PCookieServiceChild* NeckoChild::AllocPCookieServiceChild() {
+ // We don't allocate here: see CookieService::GetSingleton()
+ MOZ_ASSERT_UNREACHABLE("AllocPCookieServiceChild should not be called");
+ return nullptr;
+}
+
+bool NeckoChild::DeallocPCookieServiceChild(PCookieServiceChild* cs) {
+ NS_ASSERTION(IsNeckoChild(),
+ "DeallocPCookieServiceChild called by non-child!");
+
+ CookieServiceChild* p = static_cast<CookieServiceChild*>(cs);
+ p->Release();
+ return true;
+}
+
+PWebSocketChild* NeckoChild::AllocPWebSocketChild(
+ PBrowserChild* browser, const SerializedLoadContext& aSerialized,
+ const uint32_t& aSerial) {
+ MOZ_ASSERT_UNREACHABLE("AllocPWebSocketChild should not be called");
+ return nullptr;
+}
+
+bool NeckoChild::DeallocPWebSocketChild(PWebSocketChild* child) {
+ WebSocketChannelChild* p = static_cast<WebSocketChannelChild*>(child);
+ p->ReleaseIPDLReference();
+ return true;
+}
+
+PWebSocketEventListenerChild* NeckoChild::AllocPWebSocketEventListenerChild(
+ const uint64_t& aInnerWindowID) {
+ RefPtr<WebSocketEventListenerChild> c = new WebSocketEventListenerChild(
+ aInnerWindowID, GetMainThreadSerialEventTarget());
+ return c.forget().take();
+}
+
+bool NeckoChild::DeallocPWebSocketEventListenerChild(
+ PWebSocketEventListenerChild* aActor) {
+ RefPtr<WebSocketEventListenerChild> c =
+ dont_AddRef(static_cast<WebSocketEventListenerChild*>(aActor));
+ MOZ_ASSERT(c);
+ return true;
+}
+
+PSimpleChannelChild* NeckoChild::AllocPSimpleChannelChild(
+ const uint32_t& channelId) {
+ MOZ_ASSERT_UNREACHABLE("Should never get here");
+ return nullptr;
+}
+
+bool NeckoChild::DeallocPSimpleChannelChild(PSimpleChannelChild* child) {
+ static_cast<SimpleChannelChild*>(child)->Release();
+ return true;
+}
+
+PTCPSocketChild* NeckoChild::AllocPTCPSocketChild(const nsAString& host,
+ const uint16_t& port) {
+ TCPSocketChild* p = new TCPSocketChild(host, port, nullptr);
+ p->AddIPDLReference();
+ return p;
+}
+
+bool NeckoChild::DeallocPTCPSocketChild(PTCPSocketChild* child) {
+ TCPSocketChild* p = static_cast<TCPSocketChild*>(child);
+ p->ReleaseIPDLReference();
+ return true;
+}
+
+PTCPServerSocketChild* NeckoChild::AllocPTCPServerSocketChild(
+ const uint16_t& aLocalPort, const uint16_t& aBacklog,
+ const bool& aUseArrayBuffers) {
+ MOZ_ASSERT_UNREACHABLE("AllocPTCPServerSocket should not be called");
+ return nullptr;
+}
+
+bool NeckoChild::DeallocPTCPServerSocketChild(PTCPServerSocketChild* child) {
+ TCPServerSocketChild* p = static_cast<TCPServerSocketChild*>(child);
+ p->ReleaseIPDLReference();
+ return true;
+}
+
+PUDPSocketChild* NeckoChild::AllocPUDPSocketChild(nsIPrincipal* aPrincipal,
+ const nsACString& aFilter) {
+ MOZ_ASSERT_UNREACHABLE("AllocPUDPSocket should not be called");
+ return nullptr;
+}
+
+bool NeckoChild::DeallocPUDPSocketChild(PUDPSocketChild* child) {
+ UDPSocketChild* p = static_cast<UDPSocketChild*>(child);
+ p->ReleaseIPDLReference();
+ return true;
+}
+
+PTransportProviderChild* NeckoChild::AllocPTransportProviderChild() {
+ // This refcount is transferred to the receiver of the message that
+ // includes the PTransportProviderChild actor.
+ RefPtr<TransportProviderChild> res = new TransportProviderChild();
+
+ return res.forget().take();
+}
+
+bool NeckoChild::DeallocPTransportProviderChild(
+ PTransportProviderChild* aActor) {
+ return true;
+}
+
+/* Predictor Messages */
+mozilla::ipc::IPCResult NeckoChild::RecvPredOnPredictPrefetch(
+ nsIURI* aURI, const uint32_t& aHttpStatus) {
+ MOZ_ASSERT(NS_IsMainThread(),
+ "PredictorChild::RecvOnPredictPrefetch "
+ "off main thread.");
+ if (!aURI) {
+ return IPC_FAIL(this, "aURI is null");
+ }
+
+ // Get the current predictor
+ nsresult rv = NS_OK;
+ nsCOMPtr<nsINetworkPredictorVerifier> predictor =
+ do_GetService("@mozilla.org/network/predictor;1", &rv);
+ NS_ENSURE_SUCCESS(rv, IPC_FAIL_NO_REASON(this));
+
+ predictor->OnPredictPrefetch(aURI, aHttpStatus);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult NeckoChild::RecvPredOnPredictPreconnect(nsIURI* aURI) {
+ MOZ_ASSERT(NS_IsMainThread(),
+ "PredictorChild::RecvOnPredictPreconnect "
+ "off main thread.");
+ if (!aURI) {
+ return IPC_FAIL(this, "aURI is null");
+ }
+ // Get the current predictor
+ nsresult rv = NS_OK;
+ nsCOMPtr<nsINetworkPredictorVerifier> predictor =
+ do_GetService("@mozilla.org/network/predictor;1", &rv);
+ NS_ENSURE_SUCCESS(rv, IPC_FAIL_NO_REASON(this));
+
+ predictor->OnPredictPreconnect(aURI);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult NeckoChild::RecvPredOnPredictDNS(nsIURI* aURI) {
+ MOZ_ASSERT(NS_IsMainThread(),
+ "PredictorChild::RecvOnPredictDNS off "
+ "main thread.");
+ if (!aURI) {
+ return IPC_FAIL(this, "aURI is null");
+ }
+ // Get the current predictor
+ nsresult rv = NS_OK;
+ nsCOMPtr<nsINetworkPredictorVerifier> predictor =
+ do_GetService("@mozilla.org/network/predictor;1", &rv);
+ NS_ENSURE_SUCCESS(rv, IPC_FAIL_NO_REASON(this));
+
+ predictor->OnPredictDNS(aURI);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult NeckoChild::RecvSpeculativeConnectRequest() {
+ nsCOMPtr<nsIObserverService> obsService = services::GetObserverService();
+ if (obsService) {
+ obsService->NotifyObservers(nullptr, "speculative-connect-request",
+ nullptr);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult NeckoChild::RecvNetworkChangeNotification(
+ nsCString const& type) {
+ nsCOMPtr<nsIObserverService> obsService = services::GetObserverService();
+ if (obsService) {
+ obsService->NotifyObservers(nullptr, NS_NETWORK_LINK_TOPIC,
+ NS_ConvertUTF8toUTF16(type).get());
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult NeckoChild::RecvSetTRRDomain(const nsCString& domain) {
+ RefPtr<net::ChildDNSService> dnsServiceChild =
+ dont_AddRef(net::ChildDNSService::GetSingleton());
+ if (dnsServiceChild) {
+ dnsServiceChild->SetTRRDomain(domain);
+ }
+ return IPC_OK();
+}
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/ipc/NeckoChild.h b/netwerk/ipc/NeckoChild.h
new file mode 100644
index 0000000000..2434c02126
--- /dev/null
+++ b/netwerk/ipc/NeckoChild.h
@@ -0,0 +1,93 @@
+/* -*- 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_NeckoChild_h
+#define mozilla_net_NeckoChild_h
+
+#include "mozilla/net/PNeckoChild.h"
+#include "mozilla/net/NeckoCommon.h"
+
+namespace mozilla {
+namespace net {
+
+// Header file contents
+class NeckoChild : public PNeckoChild {
+ friend class PNeckoChild;
+
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(NeckoChild, override)
+
+ NeckoChild() = default;
+
+ static void InitNeckoChild();
+
+ protected:
+ virtual ~NeckoChild();
+
+ PStunAddrsRequestChild* AllocPStunAddrsRequestChild();
+ bool DeallocPStunAddrsRequestChild(PStunAddrsRequestChild* aActor);
+
+ PWebrtcTCPSocketChild* AllocPWebrtcTCPSocketChild(const Maybe<TabId>& tabId);
+ bool DeallocPWebrtcTCPSocketChild(PWebrtcTCPSocketChild* aActor);
+
+ PAltDataOutputStreamChild* AllocPAltDataOutputStreamChild(
+ const nsACString& type, const int64_t& predictedSize,
+ PHttpChannelChild* channel);
+ bool DeallocPAltDataOutputStreamChild(PAltDataOutputStreamChild* aActor);
+
+ PCookieServiceChild* AllocPCookieServiceChild();
+ bool DeallocPCookieServiceChild(PCookieServiceChild*);
+#ifdef MOZ_WIDGET_GTK
+ PGIOChannelChild* AllocPGIOChannelChild(
+ PBrowserChild* aBrowser, const SerializedLoadContext& aSerialized,
+ const GIOChannelCreationArgs& aOpenArgs);
+ bool DeallocPGIOChannelChild(PGIOChannelChild*);
+#endif
+ PWebSocketChild* AllocPWebSocketChild(PBrowserChild*,
+ const SerializedLoadContext&,
+ const uint32_t&);
+ bool DeallocPWebSocketChild(PWebSocketChild*);
+ PTCPSocketChild* AllocPTCPSocketChild(const nsAString& host,
+ const uint16_t& port);
+ bool DeallocPTCPSocketChild(PTCPSocketChild*);
+ PTCPServerSocketChild* AllocPTCPServerSocketChild(
+ const uint16_t& aLocalPort, const uint16_t& aBacklog,
+ const bool& aUseArrayBuffers);
+ bool DeallocPTCPServerSocketChild(PTCPServerSocketChild*);
+ PUDPSocketChild* AllocPUDPSocketChild(nsIPrincipal* aPrincipal,
+ const nsACString& aFilter);
+ bool DeallocPUDPSocketChild(PUDPSocketChild*);
+ PSimpleChannelChild* AllocPSimpleChannelChild(const uint32_t& channelId);
+ bool DeallocPSimpleChannelChild(PSimpleChannelChild* child);
+ PTransportProviderChild* AllocPTransportProviderChild();
+ bool DeallocPTransportProviderChild(PTransportProviderChild* aActor);
+ PWebSocketEventListenerChild* AllocPWebSocketEventListenerChild(
+ const uint64_t& aInnerWindowID);
+ bool DeallocPWebSocketEventListenerChild(PWebSocketEventListenerChild*);
+
+ /* Predictor Messsages */
+ mozilla::ipc::IPCResult RecvPredOnPredictPrefetch(
+ nsIURI* aURI, const uint32_t& aHttpStatus);
+ mozilla::ipc::IPCResult RecvPredOnPredictPreconnect(nsIURI* aURI);
+ mozilla::ipc::IPCResult RecvPredOnPredictDNS(nsIURI* aURI);
+
+ mozilla::ipc::IPCResult RecvSpeculativeConnectRequest();
+ mozilla::ipc::IPCResult RecvNetworkChangeNotification(nsCString const& type);
+
+ mozilla::ipc::IPCResult RecvSetTRRDomain(const nsCString& domain);
+};
+
+/**
+ * Reference to the PNecko Child protocol.
+ * Null if this is not a content process.
+ */
+extern PNeckoChild* gNeckoChild;
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozilla_net_NeckoChild_h
diff --git a/netwerk/ipc/NeckoCommon.cpp b/netwerk/ipc/NeckoCommon.cpp
new file mode 100644
index 0000000000..9ad4483d32
--- /dev/null
+++ b/netwerk/ipc/NeckoCommon.cpp
@@ -0,0 +1,68 @@
+/* -*- 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 "NeckoCommon.h"
+
+#include "nsIInputStream.h"
+#include "nsIMultiPartChannel.h"
+#include "nsIParentChannel.h"
+#include "nsStringStream.h"
+
+namespace mozilla::net {
+
+nsresult ForwardStreamListenerFunctions(nsTArray<StreamListenerFunction> aCalls,
+ nsIStreamListener* aParent) {
+ nsresult rv = NS_OK;
+ for (auto& variant : aCalls) {
+ variant.match(
+ [&](OnStartRequestParams& aParams) {
+ rv = aParent->OnStartRequest(aParams.request);
+ if (NS_FAILED(rv)) {
+ aParams.request->Cancel(rv);
+ }
+ },
+ [&](OnDataAvailableParams& aParams) {
+ // Don't deliver OnDataAvailable if we've
+ // already failed.
+ if (NS_FAILED(rv)) {
+ return;
+ }
+ nsCOMPtr<nsIInputStream> stringStream;
+ rv = NS_NewCStringInputStream(getter_AddRefs(stringStream),
+ std::move(aParams.data));
+ if (NS_SUCCEEDED(rv)) {
+ rv = aParent->OnDataAvailable(aParams.request, stringStream,
+ aParams.offset, aParams.count);
+ }
+ if (NS_FAILED(rv)) {
+ aParams.request->Cancel(rv);
+ }
+ },
+ [&](OnStopRequestParams& aParams) {
+ if (NS_SUCCEEDED(rv)) {
+ aParent->OnStopRequest(aParams.request, aParams.status);
+ } else {
+ aParent->OnStopRequest(aParams.request, rv);
+ }
+ rv = NS_OK;
+ },
+ [&](OnAfterLastPartParams& aParams) {
+ nsCOMPtr<nsIMultiPartChannelListener> multiListener =
+ do_QueryInterface(aParent);
+ if (multiListener) {
+ if (NS_SUCCEEDED(rv)) {
+ multiListener->OnAfterLastPart(aParams.status);
+ } else {
+ multiListener->OnAfterLastPart(rv);
+ }
+ }
+ });
+ }
+ return rv;
+}
+
+} // namespace mozilla::net
diff --git a/netwerk/ipc/NeckoCommon.h b/netwerk/ipc/NeckoCommon.h
new file mode 100644
index 0000000000..57f770db68
--- /dev/null
+++ b/netwerk/ipc/NeckoCommon.h
@@ -0,0 +1,144 @@
+/* -*- 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_NeckoCommon_h
+#define mozilla_net_NeckoCommon_h
+
+#include "mozilla/Preferences.h"
+#include "mozilla/Variant.h"
+#include "nsIRequest.h"
+#include "nsPrintfCString.h"
+#include "nsXULAppAPI.h"
+#include "prenv.h"
+
+class nsIStreamListener;
+
+namespace mozilla {
+namespace dom {
+class BrowserChild;
+} // namespace dom
+} // namespace mozilla
+
+#if defined(DEBUG)
+# define NECKO_ERRORS_ARE_FATAL_DEFAULT true
+#else
+# define NECKO_ERRORS_ARE_FATAL_DEFAULT false
+#endif
+
+// TODO: Eventually remove NECKO_MAYBE_ABORT and DROP_DEAD (bug 575494).
+// Still useful for catching listener interfaces we don't yet support across
+// processes, etc.
+
+#define NECKO_MAYBE_ABORT(msg) \
+ do { \
+ bool abort = NECKO_ERRORS_ARE_FATAL_DEFAULT; \
+ const char* e = PR_GetEnv("NECKO_ERRORS_ARE_FATAL"); \
+ if (e) abort = (*e == '0') ? false : true; \
+ if (abort) { \
+ msg.AppendLiteral( \
+ " (set NECKO_ERRORS_ARE_FATAL=0 in your environment " \
+ "to convert this error into a warning.)"); \
+ MOZ_CRASH_UNSAFE(msg.get()); \
+ } else { \
+ msg.AppendLiteral( \
+ " (set NECKO_ERRORS_ARE_FATAL=1 in your environment " \
+ "to convert this warning into a fatal error.)"); \
+ NS_WARNING(msg.get()); \
+ } \
+ } while (0)
+
+#define DROP_DEAD() \
+ do { \
+ nsPrintfCString msg("NECKO ERROR: '%s' UNIMPLEMENTED", __FUNCTION__); \
+ NECKO_MAYBE_ABORT(msg); \
+ return NS_ERROR_NOT_IMPLEMENTED; \
+ } while (0)
+
+#define ENSURE_CALLED_BEFORE_ASYNC_OPEN() \
+ do { \
+ if (LoadIsPending() || LoadWasOpened()) { \
+ nsPrintfCString msg("'%s' called after AsyncOpen: %s +%d", __FUNCTION__, \
+ __FILE__, __LINE__); \
+ NECKO_MAYBE_ABORT(msg); \
+ } \
+ NS_ENSURE_TRUE(!LoadIsPending(), NS_ERROR_IN_PROGRESS); \
+ NS_ENSURE_TRUE(!LoadWasOpened(), NS_ERROR_ALREADY_OPENED); \
+ } while (0)
+
+// Fails call if made after request observers (on-modify-request, etc) have been
+// called
+
+#define ENSURE_CALLED_BEFORE_CONNECT() \
+ do { \
+ if (LoadRequestObserversCalled()) { \
+ nsPrintfCString msg("'%s' called too late: %s +%d", __FUNCTION__, \
+ __FILE__, __LINE__); \
+ NECKO_MAYBE_ABORT(msg); \
+ if (LoadIsPending()) return NS_ERROR_IN_PROGRESS; \
+ MOZ_ASSERT(LoadWasOpened()); \
+ return NS_ERROR_ALREADY_OPENED; \
+ } \
+ } while (0)
+
+namespace mozilla {
+namespace net {
+
+inline bool IsNeckoChild() {
+ static bool didCheck = false;
+ static bool amChild = false;
+
+ if (!didCheck) {
+ didCheck = true;
+ amChild = (XRE_GetProcessType() == GeckoProcessType_Content);
+ }
+ return amChild;
+}
+
+inline bool IsSocketProcessChild() {
+ static bool amChild = (XRE_GetProcessType() == GeckoProcessType_Socket);
+ return amChild;
+}
+
+class HttpChannelSecurityWarningReporter : public nsISupports {
+ public:
+ [[nodiscard]] virtual nsresult ReportSecurityMessage(
+ const nsAString& aMessageTag, const nsAString& aMessageCategory) = 0;
+ [[nodiscard]] virtual nsresult LogBlockedCORSRequest(
+ const nsAString& aMessage, const nsACString& aCategory,
+ bool aIsWarning = false) = 0;
+ [[nodiscard]] virtual nsresult LogMimeTypeMismatch(
+ const nsACString& aMessageName, bool aWarning, const nsAString& aURL,
+ const nsAString& aContentType) = 0;
+};
+
+struct OnStartRequestParams {
+ nsCOMPtr<nsIRequest> request;
+};
+struct OnDataAvailableParams {
+ nsCOMPtr<nsIRequest> request;
+ nsCString data;
+ uint64_t offset;
+ uint32_t count;
+};
+struct OnStopRequestParams {
+ nsCOMPtr<nsIRequest> request;
+ nsresult status;
+};
+struct OnAfterLastPartParams {
+ nsresult status;
+};
+using StreamListenerFunction =
+ mozilla::Variant<OnStartRequestParams, OnDataAvailableParams,
+ OnStopRequestParams, OnAfterLastPartParams>;
+
+nsresult ForwardStreamListenerFunctions(nsTArray<StreamListenerFunction> aCalls,
+ nsIStreamListener* aParent);
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozilla_net_NeckoCommon_h
diff --git a/netwerk/ipc/NeckoMessageUtils.h b/netwerk/ipc/NeckoMessageUtils.h
new file mode 100644
index 0000000000..a15398330e
--- /dev/null
+++ b/netwerk/ipc/NeckoMessageUtils.h
@@ -0,0 +1,190 @@
+/* -*- Mode: C++; tab-width: 8; 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_NeckoMessageUtils_h
+#define mozilla_net_NeckoMessageUtils_h
+
+#include "mozilla/DebugOnly.h"
+
+#include "ipc/EnumSerializer.h"
+#include "ipc/IPCMessageUtils.h"
+#include "nsExceptionHandler.h"
+#include "nsIHttpChannel.h"
+#include "nsPrintfCString.h"
+#include "nsString.h"
+#include "prio.h"
+#include "mozilla/net/DNS.h"
+#include "ipc/IPCMessageUtilsSpecializations.h"
+#include "nsITRRSkipReason.h"
+#include "nsIDNSService.h"
+
+namespace IPC {
+
+// nsIPermissionManager utilities
+
+struct Permission {
+ nsCString origin, type;
+ uint32_t capability, expireType;
+ int64_t expireTime;
+
+ Permission() : capability(0), expireType(0), expireTime(0) {}
+
+ Permission(const nsCString& aOrigin, const nsACString& aType,
+ const uint32_t aCapability, const uint32_t aExpireType,
+ const int64_t aExpireTime)
+ : origin(aOrigin),
+ type(aType),
+ capability(aCapability),
+ expireType(aExpireType),
+ expireTime(aExpireTime) {}
+
+ bool operator==(const Permission& aOther) const {
+ return aOther.origin == origin && aOther.type == type &&
+ aOther.capability == capability && aOther.expireType == expireType &&
+ aOther.expireTime == expireTime;
+ }
+};
+
+template <>
+struct ParamTraits<Permission> {
+ static void Write(MessageWriter* aWriter, const Permission& aParam) {
+ WriteParam(aWriter, aParam.origin);
+ WriteParam(aWriter, aParam.type);
+ WriteParam(aWriter, aParam.capability);
+ WriteParam(aWriter, aParam.expireType);
+ WriteParam(aWriter, aParam.expireTime);
+ }
+
+ static bool Read(MessageReader* aReader, Permission* aResult) {
+ return ReadParam(aReader, &aResult->origin) &&
+ ReadParam(aReader, &aResult->type) &&
+ ReadParam(aReader, &aResult->capability) &&
+ ReadParam(aReader, &aResult->expireType) &&
+ ReadParam(aReader, &aResult->expireTime);
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::net::NetAddr> {
+ static void Write(MessageWriter* aWriter,
+ const mozilla::net::NetAddr& aParam) {
+ WriteParam(aWriter, aParam.raw.family);
+ if (aParam.raw.family == AF_UNSPEC) {
+ aWriter->WriteBytes(aParam.raw.data, sizeof(aParam.raw.data));
+ } else if (aParam.raw.family == AF_INET) {
+ WriteParam(aWriter, aParam.inet.port);
+ WriteParam(aWriter, aParam.inet.ip);
+ } else if (aParam.raw.family == AF_INET6) {
+ WriteParam(aWriter, aParam.inet6.port);
+ WriteParam(aWriter, aParam.inet6.flowinfo);
+ WriteParam(aWriter, aParam.inet6.ip.u64[0]);
+ WriteParam(aWriter, aParam.inet6.ip.u64[1]);
+ WriteParam(aWriter, aParam.inet6.scope_id);
+#if defined(XP_UNIX)
+ } else if (aParam.raw.family == AF_LOCAL) {
+ // Train's already off the rails: let's get a stack trace at least...
+ MOZ_CRASH(
+ "Error: please post stack trace to "
+ "https://bugzilla.mozilla.org/show_bug.cgi?id=661158");
+ aWriter->WriteBytes(aParam.local.path, sizeof(aParam.local.path));
+#endif
+ } else {
+ if (XRE_IsParentProcess()) {
+ nsPrintfCString msg("%d", aParam.raw.family);
+ CrashReporter::AnnotateCrashReport(
+ CrashReporter::Annotation::UnknownNetAddrSocketFamily, msg);
+ }
+
+ MOZ_CRASH("Unknown socket family");
+ }
+ }
+
+ static bool Read(MessageReader* aReader, mozilla::net::NetAddr* aResult) {
+ if (!ReadParam(aReader, &aResult->raw.family)) return false;
+
+ if (aResult->raw.family == AF_UNSPEC) {
+ return aReader->ReadBytesInto(&aResult->raw.data,
+ sizeof(aResult->raw.data));
+ } else if (aResult->raw.family == AF_INET) {
+ return ReadParam(aReader, &aResult->inet.port) &&
+ ReadParam(aReader, &aResult->inet.ip);
+ } else if (aResult->raw.family == AF_INET6) {
+ return ReadParam(aReader, &aResult->inet6.port) &&
+ ReadParam(aReader, &aResult->inet6.flowinfo) &&
+ ReadParam(aReader, &aResult->inet6.ip.u64[0]) &&
+ ReadParam(aReader, &aResult->inet6.ip.u64[1]) &&
+ ReadParam(aReader, &aResult->inet6.scope_id);
+#if defined(XP_UNIX)
+ } else if (aResult->raw.family == AF_LOCAL) {
+ return aReader->ReadBytesInto(&aResult->local.path,
+ sizeof(aResult->local.path));
+#endif
+ }
+
+ /* We've been tricked by some socket family we don't know about! */
+ return false;
+ }
+};
+
+template <>
+struct ParamTraits<nsIRequest::TRRMode> {
+ static void Write(MessageWriter* aWriter, const nsIRequest::TRRMode& aParam) {
+ WriteParam(aWriter, (uint8_t)aParam);
+ }
+ static bool Read(MessageReader* aReader, nsIRequest::TRRMode* aResult) {
+ uint8_t mode;
+ if (!ReadParam(aReader, &mode)) {
+ return false;
+ }
+ // TODO: sanity check
+ *aResult = static_cast<nsIRequest::TRRMode>(mode);
+ return true;
+ }
+};
+
+template <>
+struct ParamTraits<nsITRRSkipReason::value> {
+ static void Write(MessageWriter* aWriter,
+ const nsITRRSkipReason::value& aParam) {
+ WriteParam(aWriter, (uint8_t)aParam);
+ }
+ static bool Read(MessageReader* aReader, nsITRRSkipReason::value* aResult) {
+ uint8_t reason;
+ if (!ReadParam(aReader, &reason)) {
+ return false;
+ }
+ // TODO: sanity check
+ *aResult = static_cast<nsITRRSkipReason::value>(reason);
+ return true;
+ }
+};
+
+template <>
+struct ParamTraits<nsIDNSService::DNSFlags>
+ : public BitFlagsEnumSerializer<
+ nsIDNSService::DNSFlags, nsIDNSService::DNSFlags::ALL_DNSFLAGS_BITS> {
+};
+
+template <>
+struct ParamTraits<nsIDNSService::ResolverMode> {
+ static void Write(MessageWriter* aWriter,
+ const nsIDNSService::ResolverMode& aParam) {
+ WriteParam(aWriter, (uint8_t)aParam);
+ }
+ static bool Read(MessageReader* aReader,
+ nsIDNSService::ResolverMode* aResult) {
+ uint8_t mode;
+ if (!ReadParam(aReader, &mode)) {
+ return false;
+ }
+ // TODO: sanity check
+ *aResult = static_cast<nsIDNSService::ResolverMode>(mode);
+ return true;
+ }
+};
+
+} // namespace IPC
+
+#endif // mozilla_net_NeckoMessageUtils_h
diff --git a/netwerk/ipc/NeckoParent.cpp b/netwerk/ipc/NeckoParent.cpp
new file mode 100644
index 0000000000..102b46a378
--- /dev/null
+++ b/netwerk/ipc/NeckoParent.cpp
@@ -0,0 +1,889 @@
+/* -*- 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 "nsHttp.h"
+#include "mozilla/BasePrincipal.h"
+#include "mozilla/ContentPrincipal.h"
+#include "mozilla/ipc/IPCStreamUtils.h"
+#include "mozilla/net/ExtensionProtocolHandler.h"
+#include "mozilla/net/PageThumbProtocolHandler.h"
+#include "mozilla/net/NeckoParent.h"
+#include "mozilla/net/HttpChannelParent.h"
+#include "mozilla/net/CookieServiceParent.h"
+#include "mozilla/net/WebSocketChannelParent.h"
+#include "mozilla/net/WebSocketEventListenerParent.h"
+#include "mozilla/net/DataChannelParent.h"
+#ifdef MOZ_WIDGET_GTK
+# include "mozilla/net/GIOChannelParent.h"
+#endif
+#include "mozilla/net/DocumentChannelParent.h"
+#include "mozilla/net/SimpleChannelParent.h"
+#include "mozilla/net/AltDataOutputStreamParent.h"
+#include "mozilla/Unused.h"
+#include "mozilla/net/FileChannelParent.h"
+#include "mozilla/net/DNSRequestParent.h"
+#include "mozilla/net/IPCTransportProvider.h"
+#include "mozilla/net/RemoteStreamGetter.h"
+#include "mozilla/net/RequestContextService.h"
+#include "mozilla/net/SocketProcessParent.h"
+#include "mozilla/net/PSocketProcessBridgeParent.h"
+#ifdef MOZ_WEBRTC
+# include "mozilla/net/StunAddrsRequestParent.h"
+# include "mozilla/net/WebrtcTCPSocketParent.h"
+#endif
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/BrowserParent.h"
+#include "mozilla/dom/MaybeDiscarded.h"
+#include "mozilla/dom/network/TCPSocketParent.h"
+#include "mozilla/dom/network/TCPServerSocketParent.h"
+#include "mozilla/dom/network/UDPSocketParent.h"
+#ifdef MOZ_PLACES
+# include "mozilla/places/PageIconProtocolHandler.h"
+#endif
+#include "mozilla/LoadContext.h"
+#include "mozilla/MozPromise.h"
+#include "nsPrintfCString.h"
+#include "mozilla/dom/HTMLDNSPrefetch.h"
+#include "nsEscape.h"
+#include "SerializedLoadContext.h"
+#include "nsAuthInformationHolder.h"
+#include "nsINetworkPredictor.h"
+#include "nsINetworkPredictorVerifier.h"
+#include "nsISpeculativeConnect.h"
+#include "nsHttpHandler.h"
+#include "nsNetUtil.h"
+#include "nsIOService.h"
+
+using IPC::SerializedLoadContext;
+using mozilla::dom::BrowserParent;
+using mozilla::dom::ContentParent;
+using mozilla::dom::TCPServerSocketParent;
+using mozilla::dom::TCPSocketParent;
+using mozilla::dom::UDPSocketParent;
+using mozilla::ipc::LoadInfoArgsToLoadInfo;
+using mozilla::ipc::PrincipalInfo;
+#ifdef MOZ_PLACES
+using mozilla::places::PageIconProtocolHandler;
+#endif
+
+namespace mozilla {
+namespace net {
+
+// C++ file contents
+NeckoParent::NeckoParent() : mSocketProcessBridgeInited(false) {
+ // Init HTTP protocol handler now since we need atomTable up and running very
+ // early (IPDL argument handling for PHttpChannel constructor needs it) so
+ // normal init (during 1st Http channel request) isn't early enough.
+ nsCOMPtr<nsIProtocolHandler> proto =
+ do_GetService("@mozilla.org/network/protocol;1?name=http");
+}
+
+static PBOverrideStatus PBOverrideStatusFromLoadContext(
+ const SerializedLoadContext& aSerialized) {
+ if (!aSerialized.IsNotNull() && aSerialized.IsPrivateBitValid()) {
+ return (aSerialized.mOriginAttributes.mPrivateBrowsingId > 0)
+ ? kPBOverride_Private
+ : kPBOverride_NotPrivate;
+ }
+ return kPBOverride_Unset;
+}
+
+static already_AddRefed<nsIPrincipal> GetRequestingPrincipal(
+ const LoadInfoArgs& aLoadInfoArgs) {
+ const Maybe<PrincipalInfo>& optionalPrincipalInfo =
+ aLoadInfoArgs.requestingPrincipalInfo();
+
+ if (optionalPrincipalInfo.isNothing()) {
+ return nullptr;
+ }
+
+ const PrincipalInfo& principalInfo = optionalPrincipalInfo.ref();
+
+ auto principalOrErr = PrincipalInfoToPrincipal(principalInfo);
+ return principalOrErr.isOk() ? principalOrErr.unwrap().forget() : nullptr;
+}
+
+static already_AddRefed<nsIPrincipal> GetRequestingPrincipal(
+ const HttpChannelCreationArgs& aArgs) {
+ if (aArgs.type() != HttpChannelCreationArgs::THttpChannelOpenArgs) {
+ return nullptr;
+ }
+
+ const HttpChannelOpenArgs& args = aArgs.get_HttpChannelOpenArgs();
+ return GetRequestingPrincipal(args.loadInfo());
+}
+
+const char* NeckoParent::GetValidatedOriginAttributes(
+ const SerializedLoadContext& aSerialized, PContentParent* aContent,
+ nsIPrincipal* aRequestingPrincipal, OriginAttributes& aAttrs) {
+ if (!aSerialized.IsNotNull()) {
+ // If serialized is null, we cannot validate anything. We have to assume
+ // that this requests comes from a SystemPrincipal.
+ aAttrs = OriginAttributes(false);
+ } else {
+ aAttrs = aSerialized.mOriginAttributes;
+ }
+ return nullptr;
+}
+
+const char* NeckoParent::CreateChannelLoadContext(
+ PBrowserParent* aBrowser, PContentParent* aContent,
+ const SerializedLoadContext& aSerialized,
+ nsIPrincipal* aRequestingPrincipal, nsCOMPtr<nsILoadContext>& aResult) {
+ OriginAttributes attrs;
+ const char* error = GetValidatedOriginAttributes(aSerialized, aContent,
+ aRequestingPrincipal, attrs);
+ if (error) {
+ return error;
+ }
+
+ // if !UsingNeckoIPCSecurity(), we may not have a LoadContext to set. This is
+ // the common case for most xpcshell tests.
+ if (aSerialized.IsNotNull()) {
+ attrs.SyncAttributesWithPrivateBrowsing(
+ aSerialized.mOriginAttributes.mPrivateBrowsingId > 0);
+
+ RefPtr<BrowserParent> browserParent = BrowserParent::GetFrom(aBrowser);
+ dom::Element* topFrameElement = nullptr;
+ if (browserParent) {
+ topFrameElement = browserParent->GetOwnerElement();
+ }
+ aResult = new LoadContext(aSerialized, topFrameElement, attrs);
+ }
+
+ return nullptr;
+}
+
+void NeckoParent::ActorDestroy(ActorDestroyReason aWhy) {
+ // Nothing needed here. Called right before destructor since this is a
+ // non-refcounted class.
+}
+
+already_AddRefed<PHttpChannelParent> NeckoParent::AllocPHttpChannelParent(
+ PBrowserParent* aBrowser, const SerializedLoadContext& aSerialized,
+ const HttpChannelCreationArgs& aOpenArgs) {
+ nsCOMPtr<nsIPrincipal> requestingPrincipal =
+ GetRequestingPrincipal(aOpenArgs);
+
+ nsCOMPtr<nsILoadContext> loadContext;
+ const char* error = CreateChannelLoadContext(
+ aBrowser, Manager(), aSerialized, requestingPrincipal, loadContext);
+ if (error) {
+ printf_stderr(
+ "NeckoParent::AllocPHttpChannelParent: "
+ "FATAL error: %s: KILLING CHILD PROCESS\n",
+ error);
+ return nullptr;
+ }
+ PBOverrideStatus overrideStatus =
+ PBOverrideStatusFromLoadContext(aSerialized);
+ RefPtr<HttpChannelParent> p = new HttpChannelParent(
+ BrowserParent::GetFrom(aBrowser), loadContext, overrideStatus);
+ return p.forget();
+}
+
+mozilla::ipc::IPCResult NeckoParent::RecvPHttpChannelConstructor(
+ PHttpChannelParent* aActor, PBrowserParent* aBrowser,
+ const SerializedLoadContext& aSerialized,
+ const HttpChannelCreationArgs& aOpenArgs) {
+ HttpChannelParent* p = static_cast<HttpChannelParent*>(aActor);
+ if (!p->Init(aOpenArgs)) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ return IPC_OK();
+}
+
+PStunAddrsRequestParent* NeckoParent::AllocPStunAddrsRequestParent() {
+#ifdef MOZ_WEBRTC
+ StunAddrsRequestParent* p = new StunAddrsRequestParent();
+ p->AddRef();
+ return p;
+#else
+ return nullptr;
+#endif
+}
+
+bool NeckoParent::DeallocPStunAddrsRequestParent(
+ PStunAddrsRequestParent* aActor) {
+#ifdef MOZ_WEBRTC
+ StunAddrsRequestParent* p = static_cast<StunAddrsRequestParent*>(aActor);
+ p->Release();
+#endif
+ return true;
+}
+
+PWebrtcTCPSocketParent* NeckoParent::AllocPWebrtcTCPSocketParent(
+ const Maybe<TabId>& aTabId) {
+#ifdef MOZ_WEBRTC
+ WebrtcTCPSocketParent* parent = new WebrtcTCPSocketParent(aTabId);
+ parent->AddRef();
+ return parent;
+#else
+ return nullptr;
+#endif
+}
+
+bool NeckoParent::DeallocPWebrtcTCPSocketParent(
+ PWebrtcTCPSocketParent* aActor) {
+#ifdef MOZ_WEBRTC
+ WebrtcTCPSocketParent* parent = static_cast<WebrtcTCPSocketParent*>(aActor);
+ parent->Release();
+#endif
+ return true;
+}
+
+PAltDataOutputStreamParent* NeckoParent::AllocPAltDataOutputStreamParent(
+ const nsACString& type, const int64_t& predictedSize,
+ PHttpChannelParent* channel) {
+ HttpChannelParent* chan = static_cast<HttpChannelParent*>(channel);
+ nsCOMPtr<nsIAsyncOutputStream> stream;
+ nsresult rv = chan->OpenAlternativeOutputStream(type, predictedSize,
+ getter_AddRefs(stream));
+ AltDataOutputStreamParent* parent = new AltDataOutputStreamParent(stream);
+ parent->AddRef();
+ // If the return value was not NS_OK, the error code will be sent
+ // asynchronously to the child, after receiving the first message.
+ parent->SetError(rv);
+ return parent;
+}
+
+bool NeckoParent::DeallocPAltDataOutputStreamParent(
+ PAltDataOutputStreamParent* aActor) {
+ AltDataOutputStreamParent* parent =
+ static_cast<AltDataOutputStreamParent*>(aActor);
+ parent->Release();
+ return true;
+}
+
+already_AddRefed<PDocumentChannelParent>
+NeckoParent::AllocPDocumentChannelParent(
+ const dom::MaybeDiscarded<dom::BrowsingContext>& aContext,
+ const DocumentChannelCreationArgs& args) {
+ RefPtr<DocumentChannelParent> p = new DocumentChannelParent();
+ return p.forget();
+}
+
+mozilla::ipc::IPCResult NeckoParent::RecvPDocumentChannelConstructor(
+ PDocumentChannelParent* aActor,
+ const dom::MaybeDiscarded<dom::BrowsingContext>& aContext,
+ const DocumentChannelCreationArgs& aArgs) {
+ DocumentChannelParent* p = static_cast<DocumentChannelParent*>(aActor);
+
+ if (aContext.IsNullOrDiscarded()) {
+ Unused << p->SendFailedAsyncOpen(NS_ERROR_FAILURE);
+ return IPC_OK();
+ }
+
+ if (!p->Init(aContext.get_canonical(), aArgs)) {
+ return IPC_FAIL(this, "Couldn't initialize DocumentChannel");
+ }
+
+ return IPC_OK();
+}
+
+PCookieServiceParent* NeckoParent::AllocPCookieServiceParent() {
+ return new CookieServiceParent();
+}
+
+bool NeckoParent::DeallocPCookieServiceParent(PCookieServiceParent* cs) {
+ delete cs;
+ return true;
+}
+
+PWebSocketParent* NeckoParent::AllocPWebSocketParent(
+ PBrowserParent* browser, const SerializedLoadContext& serialized,
+ const uint32_t& aSerial) {
+ nsCOMPtr<nsILoadContext> loadContext;
+ const char* error = CreateChannelLoadContext(browser, Manager(), serialized,
+ nullptr, loadContext);
+ if (error) {
+ printf_stderr(
+ "NeckoParent::AllocPWebSocketParent: "
+ "FATAL error: %s: KILLING CHILD PROCESS\n",
+ error);
+ return nullptr;
+ }
+
+ RefPtr<BrowserParent> browserParent = BrowserParent::GetFrom(browser);
+ PBOverrideStatus overrideStatus = PBOverrideStatusFromLoadContext(serialized);
+ WebSocketChannelParent* p = new WebSocketChannelParent(
+ browserParent, loadContext, overrideStatus, aSerial);
+ p->AddRef();
+ return p;
+}
+
+bool NeckoParent::DeallocPWebSocketParent(PWebSocketParent* actor) {
+ WebSocketChannelParent* p = static_cast<WebSocketChannelParent*>(actor);
+ p->Release();
+ return true;
+}
+
+PWebSocketEventListenerParent* NeckoParent::AllocPWebSocketEventListenerParent(
+ const uint64_t& aInnerWindowID) {
+ RefPtr<WebSocketEventListenerParent> c =
+ new WebSocketEventListenerParent(aInnerWindowID);
+ return c.forget().take();
+}
+
+bool NeckoParent::DeallocPWebSocketEventListenerParent(
+ PWebSocketEventListenerParent* aActor) {
+ RefPtr<WebSocketEventListenerParent> c =
+ dont_AddRef(static_cast<WebSocketEventListenerParent*>(aActor));
+ MOZ_ASSERT(c);
+ return true;
+}
+
+already_AddRefed<PDataChannelParent> NeckoParent::AllocPDataChannelParent(
+ const uint32_t& channelId) {
+ RefPtr<DataChannelParent> p = new DataChannelParent();
+ return p.forget();
+}
+
+mozilla::ipc::IPCResult NeckoParent::RecvPDataChannelConstructor(
+ PDataChannelParent* actor, const uint32_t& channelId) {
+ DataChannelParent* p = static_cast<DataChannelParent*>(actor);
+ DebugOnly<bool> rv = p->Init(channelId);
+ MOZ_ASSERT(rv);
+ return IPC_OK();
+}
+
+#ifdef MOZ_WIDGET_GTK
+static already_AddRefed<nsIPrincipal> GetRequestingPrincipal(
+ const GIOChannelCreationArgs& aArgs) {
+ if (aArgs.type() != GIOChannelCreationArgs::TGIOChannelOpenArgs) {
+ return nullptr;
+ }
+
+ const GIOChannelOpenArgs& args = aArgs.get_GIOChannelOpenArgs();
+ return GetRequestingPrincipal(args.loadInfo());
+}
+
+PGIOChannelParent* NeckoParent::AllocPGIOChannelParent(
+ PBrowserParent* aBrowser, const SerializedLoadContext& aSerialized,
+ const GIOChannelCreationArgs& aOpenArgs) {
+ nsCOMPtr<nsIPrincipal> requestingPrincipal =
+ GetRequestingPrincipal(aOpenArgs);
+
+ nsCOMPtr<nsILoadContext> loadContext;
+ const char* error = CreateChannelLoadContext(
+ aBrowser, Manager(), aSerialized, requestingPrincipal, loadContext);
+ if (error) {
+ printf_stderr(
+ "NeckoParent::AllocPGIOChannelParent: "
+ "FATAL error: %s: KILLING CHILD PROCESS\n",
+ error);
+ return nullptr;
+ }
+ PBOverrideStatus overrideStatus =
+ PBOverrideStatusFromLoadContext(aSerialized);
+ GIOChannelParent* p = new GIOChannelParent(BrowserParent::GetFrom(aBrowser),
+ loadContext, overrideStatus);
+ p->AddRef();
+ return p;
+}
+
+bool NeckoParent::DeallocPGIOChannelParent(PGIOChannelParent* channel) {
+ GIOChannelParent* p = static_cast<GIOChannelParent*>(channel);
+ p->Release();
+ return true;
+}
+
+mozilla::ipc::IPCResult NeckoParent::RecvPGIOChannelConstructor(
+ PGIOChannelParent* actor, PBrowserParent* aBrowser,
+ const SerializedLoadContext& aSerialized,
+ const GIOChannelCreationArgs& aOpenArgs) {
+ GIOChannelParent* p = static_cast<GIOChannelParent*>(actor);
+ DebugOnly<bool> rv = p->Init(aOpenArgs);
+ MOZ_ASSERT(rv);
+ return IPC_OK();
+}
+#endif
+
+PSimpleChannelParent* NeckoParent::AllocPSimpleChannelParent(
+ const uint32_t& channelId) {
+ RefPtr<SimpleChannelParent> p = new SimpleChannelParent();
+ return p.forget().take();
+}
+
+bool NeckoParent::DeallocPSimpleChannelParent(PSimpleChannelParent* actor) {
+ RefPtr<SimpleChannelParent> p =
+ dont_AddRef(actor).downcast<SimpleChannelParent>();
+ return true;
+}
+
+mozilla::ipc::IPCResult NeckoParent::RecvPSimpleChannelConstructor(
+ PSimpleChannelParent* actor, const uint32_t& channelId) {
+ SimpleChannelParent* p = static_cast<SimpleChannelParent*>(actor);
+ MOZ_ALWAYS_TRUE(p->Init(channelId));
+ return IPC_OK();
+}
+
+already_AddRefed<PFileChannelParent> NeckoParent::AllocPFileChannelParent(
+ const uint32_t& channelId) {
+ RefPtr<FileChannelParent> p = new FileChannelParent();
+ return p.forget();
+}
+
+mozilla::ipc::IPCResult NeckoParent::RecvPFileChannelConstructor(
+ PFileChannelParent* actor, const uint32_t& channelId) {
+ FileChannelParent* p = static_cast<FileChannelParent*>(actor);
+ DebugOnly<bool> rv = p->Init(channelId);
+ MOZ_ASSERT(rv);
+ return IPC_OK();
+}
+
+PTCPSocketParent* NeckoParent::AllocPTCPSocketParent(
+ const nsAString& /* host */, const uint16_t& /* port */) {
+ // We actually don't need host/port to construct a TCPSocketParent since
+ // TCPSocketParent will maintain an internal nsIDOMTCPSocket instance which
+ // can be delegated to get the host/port.
+ TCPSocketParent* p = new TCPSocketParent();
+ p->AddIPDLReference();
+ return p;
+}
+
+bool NeckoParent::DeallocPTCPSocketParent(PTCPSocketParent* actor) {
+ TCPSocketParent* p = static_cast<TCPSocketParent*>(actor);
+ p->ReleaseIPDLReference();
+ return true;
+}
+
+PTCPServerSocketParent* NeckoParent::AllocPTCPServerSocketParent(
+ const uint16_t& aLocalPort, const uint16_t& aBacklog,
+ const bool& aUseArrayBuffers) {
+ TCPServerSocketParent* p =
+ new TCPServerSocketParent(this, aLocalPort, aBacklog, aUseArrayBuffers);
+ p->AddIPDLReference();
+ return p;
+}
+
+mozilla::ipc::IPCResult NeckoParent::RecvPTCPServerSocketConstructor(
+ PTCPServerSocketParent* aActor, const uint16_t& aLocalPort,
+ const uint16_t& aBacklog, const bool& aUseArrayBuffers) {
+ static_cast<TCPServerSocketParent*>(aActor)->Init();
+ return IPC_OK();
+}
+
+bool NeckoParent::DeallocPTCPServerSocketParent(PTCPServerSocketParent* actor) {
+ TCPServerSocketParent* p = static_cast<TCPServerSocketParent*>(actor);
+ p->ReleaseIPDLReference();
+ return true;
+}
+
+PUDPSocketParent* NeckoParent::AllocPUDPSocketParent(
+ nsIPrincipal* /* unused */, const nsACString& /* unused */) {
+ RefPtr<UDPSocketParent> p = new UDPSocketParent(this);
+
+ return p.forget().take();
+}
+
+mozilla::ipc::IPCResult NeckoParent::RecvPUDPSocketConstructor(
+ PUDPSocketParent* aActor, nsIPrincipal* aPrincipal,
+ const nsACString& aFilter) {
+ if (!static_cast<UDPSocketParent*>(aActor)->Init(aPrincipal, aFilter)) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ return IPC_OK();
+}
+
+bool NeckoParent::DeallocPUDPSocketParent(PUDPSocketParent* actor) {
+ UDPSocketParent* p = static_cast<UDPSocketParent*>(actor);
+ p->Release();
+ return true;
+}
+
+already_AddRefed<PDNSRequestParent> NeckoParent::AllocPDNSRequestParent(
+ const nsACString& aHost, const nsACString& aTrrServer, const int32_t& aPort,
+ const uint16_t& aType, const OriginAttributes& aOriginAttributes,
+ const nsIDNSService::DNSFlags& aFlags) {
+ RefPtr<DNSRequestHandler> handler = new DNSRequestHandler();
+ RefPtr<DNSRequestParent> actor = new DNSRequestParent(handler);
+ return actor.forget();
+}
+
+mozilla::ipc::IPCResult NeckoParent::RecvPDNSRequestConstructor(
+ PDNSRequestParent* aActor, const nsACString& aHost,
+ const nsACString& aTrrServer, const int32_t& aPort, const uint16_t& aType,
+ const OriginAttributes& aOriginAttributes,
+ const nsIDNSService::DNSFlags& aFlags) {
+ RefPtr<DNSRequestParent> actor = static_cast<DNSRequestParent*>(aActor);
+ RefPtr<DNSRequestHandler> handler =
+ actor->GetDNSRequest()->AsDNSRequestHandler();
+ handler->DoAsyncResolve(aHost, aTrrServer, aPort, aType, aOriginAttributes,
+ aFlags);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult NeckoParent::RecvSpeculativeConnect(
+ nsIURI* aURI, nsIPrincipal* aPrincipal,
+ Maybe<OriginAttributes>&& aOriginAttributes, const bool& aAnonymous) {
+ nsCOMPtr<nsISpeculativeConnect> speculator(gIOService);
+ nsCOMPtr<nsIPrincipal> principal(aPrincipal);
+ if (!aURI) {
+ return IPC_FAIL(this, "aURI must not be null");
+ }
+ if (aURI && speculator) {
+ if (aOriginAttributes) {
+ speculator->SpeculativeConnectWithOriginAttributesNative(
+ aURI, std::move(aOriginAttributes.ref()), nullptr, aAnonymous);
+ } else {
+ speculator->SpeculativeConnect(aURI, principal, nullptr, aAnonymous);
+ }
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult NeckoParent::RecvHTMLDNSPrefetch(
+ const nsAString& hostname, const bool& isHttps,
+ const OriginAttributes& aOriginAttributes,
+ const nsIDNSService::DNSFlags& flags) {
+ dom::HTMLDNSPrefetch::Prefetch(hostname, isHttps, aOriginAttributes, flags);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult NeckoParent::RecvCancelHTMLDNSPrefetch(
+ const nsAString& hostname, const bool& isHttps,
+ const OriginAttributes& aOriginAttributes,
+ const nsIDNSService::DNSFlags& flags, const nsresult& reason) {
+ dom::HTMLDNSPrefetch::CancelPrefetch(hostname, isHttps, aOriginAttributes,
+ flags, reason);
+ return IPC_OK();
+}
+
+PTransportProviderParent* NeckoParent::AllocPTransportProviderParent() {
+ RefPtr<TransportProviderParent> res = new TransportProviderParent();
+ return res.forget().take();
+}
+
+bool NeckoParent::DeallocPTransportProviderParent(
+ PTransportProviderParent* aActor) {
+ RefPtr<TransportProviderParent> provider =
+ dont_AddRef(static_cast<TransportProviderParent*>(aActor));
+ return true;
+}
+
+/* Predictor Messages */
+mozilla::ipc::IPCResult NeckoParent::RecvPredPredict(
+ nsIURI* aTargetURI, nsIURI* aSourceURI, const uint32_t& aReason,
+ const OriginAttributes& aOriginAttributes, const bool& hasVerifier) {
+ // Get the current predictor
+ nsresult rv = NS_OK;
+ nsCOMPtr<nsINetworkPredictor> predictor =
+ do_GetService("@mozilla.org/network/predictor;1", &rv);
+ NS_ENSURE_SUCCESS(rv, IPC_OK());
+
+ nsCOMPtr<nsINetworkPredictorVerifier> verifier;
+ if (hasVerifier) {
+ verifier = do_QueryInterface(predictor);
+ }
+ predictor->PredictNative(aTargetURI, aSourceURI, aReason, aOriginAttributes,
+ verifier);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult NeckoParent::RecvPredLearn(
+ nsIURI* aTargetURI, nsIURI* aSourceURI, const uint32_t& aReason,
+ const OriginAttributes& aOriginAttributes) {
+ // Get the current predictor
+ nsresult rv = NS_OK;
+ nsCOMPtr<nsINetworkPredictor> predictor =
+ do_GetService("@mozilla.org/network/predictor;1", &rv);
+ NS_ENSURE_SUCCESS(rv, IPC_OK());
+
+ predictor->LearnNative(aTargetURI, aSourceURI, aReason, aOriginAttributes);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult NeckoParent::RecvPredReset() {
+ // Get the current predictor
+ nsresult rv = NS_OK;
+ nsCOMPtr<nsINetworkPredictor> predictor =
+ do_GetService("@mozilla.org/network/predictor;1", &rv);
+ NS_ENSURE_SUCCESS(rv, IPC_OK());
+
+ predictor->Reset();
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult NeckoParent::RecvRequestContextLoadBegin(
+ const uint64_t& rcid) {
+ nsCOMPtr<nsIRequestContextService> rcsvc =
+ RequestContextService::GetOrCreate();
+ if (!rcsvc) {
+ return IPC_OK();
+ }
+ nsCOMPtr<nsIRequestContext> rc;
+ rcsvc->GetRequestContext(rcid, getter_AddRefs(rc));
+ if (rc) {
+ rc->BeginLoad();
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult NeckoParent::RecvRequestContextAfterDOMContentLoaded(
+ const uint64_t& rcid) {
+ nsCOMPtr<nsIRequestContextService> rcsvc =
+ RequestContextService::GetOrCreate();
+ if (!rcsvc) {
+ return IPC_OK();
+ }
+ nsCOMPtr<nsIRequestContext> rc;
+ rcsvc->GetRequestContext(rcid, getter_AddRefs(rc));
+ if (rc) {
+ rc->DOMContentLoaded();
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult NeckoParent::RecvRemoveRequestContext(
+ const uint64_t& rcid) {
+ nsCOMPtr<nsIRequestContextService> rcsvc =
+ RequestContextService::GetOrCreate();
+ if (!rcsvc) {
+ return IPC_OK();
+ }
+
+ rcsvc->RemoveRequestContext(rcid);
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult NeckoParent::RecvGetExtensionStream(
+ nsIURI* aURI, GetExtensionStreamResolver&& aResolve) {
+ if (!aURI) {
+ return IPC_FAIL(this, "aURI must not be null");
+ }
+
+ RefPtr<ExtensionProtocolHandler> ph(ExtensionProtocolHandler::GetSingleton());
+ MOZ_ASSERT(ph);
+
+ // Ask the ExtensionProtocolHandler to give us a new input stream for
+ // this URI. The request comes from an ExtensionProtocolHandler in the
+ // child process, but is not guaranteed to be a valid moz-extension URI,
+ // and not guaranteed to represent a resource that the child should be
+ // allowed to access. The ExtensionProtocolHandler is responsible for
+ // validating the request. Specifically, only URI's for local files that
+ // an extension is allowed to access via moz-extension URI's should be
+ // accepted.
+ nsCOMPtr<nsIInputStream> inputStream;
+ bool terminateSender = true;
+ auto inputStreamOrReason = ph->NewStream(aURI, &terminateSender);
+ if (inputStreamOrReason.isOk()) {
+ inputStream = inputStreamOrReason.unwrap();
+ }
+
+ // If NewStream failed, we send back an invalid stream to the child so
+ // it can handle the error. MozPromise rejection is reserved for channel
+ // errors/disconnects.
+ aResolve(inputStream);
+
+ if (terminateSender) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult NeckoParent::RecvGetExtensionFD(
+ nsIURI* aURI, GetExtensionFDResolver&& aResolve) {
+ if (!aURI) {
+ return IPC_FAIL(this, "aURI must not be null");
+ }
+
+ RefPtr<ExtensionProtocolHandler> ph(ExtensionProtocolHandler::GetSingleton());
+ MOZ_ASSERT(ph);
+
+ // Ask the ExtensionProtocolHandler to give us a new input stream for
+ // this URI. The request comes from an ExtensionProtocolHandler in the
+ // child process, but is not guaranteed to be a valid moz-extension URI,
+ // and not guaranteed to represent a resource that the child should be
+ // allowed to access. The ExtensionProtocolHandler is responsible for
+ // validating the request. Specifically, only URI's for local files that
+ // an extension is allowed to access via moz-extension URI's should be
+ // accepted.
+ bool terminateSender = true;
+ auto result = ph->NewFD(aURI, &terminateSender, aResolve);
+
+ if (result.isErr() && terminateSender) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ if (result.isErr()) {
+ FileDescriptor invalidFD;
+ aResolve(invalidFD);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult NeckoParent::RecvInitSocketProcessBridge(
+ InitSocketProcessBridgeResolver&& aResolver) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ // Initing the socket process bridge must be async here in order to
+ // wait for the socket process launch before executing.
+ auto task = [self = RefPtr{this}, resolver = std::move(aResolver)]() {
+ // The content process might be already destroyed.
+ if (!self->CanSend()) {
+ return;
+ }
+
+ Endpoint<PSocketProcessBridgeChild> invalidEndpoint;
+ if (NS_WARN_IF(self->mSocketProcessBridgeInited)) {
+ resolver(std::move(invalidEndpoint));
+ return;
+ }
+
+ SocketProcessParent* parent = SocketProcessParent::GetSingleton();
+ if (NS_WARN_IF(!parent)) {
+ resolver(std::move(invalidEndpoint));
+ return;
+ }
+
+ Endpoint<PSocketProcessBridgeParent> parentEndpoint;
+ Endpoint<PSocketProcessBridgeChild> childEndpoint;
+ if (NS_WARN_IF(NS_FAILED(PSocketProcessBridge::CreateEndpoints(
+ parent->OtherPid(), self->Manager()->OtherPid(), &parentEndpoint,
+ &childEndpoint)))) {
+ resolver(std::move(invalidEndpoint));
+ return;
+ }
+
+ if (NS_WARN_IF(!parent->SendInitSocketProcessBridgeParent(
+ self->Manager()->OtherPid(), std::move(parentEndpoint)))) {
+ resolver(std::move(invalidEndpoint));
+ return;
+ }
+
+ resolver(std::move(childEndpoint));
+ self->mSocketProcessBridgeInited = true;
+ };
+ gIOService->CallOrWaitForSocketProcess(std::move(task));
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult NeckoParent::RecvResetSocketProcessBridge() {
+ // SendResetSocketProcessBridge is called from
+ // SocketProcessBridgeChild::ActorDestroy if the socket process
+ // crashes. This is necessary in order to properly initialize the
+ // restarted socket process.
+ mSocketProcessBridgeInited = false;
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult NeckoParent::RecvEnsureHSTSData(
+ EnsureHSTSDataResolver&& aResolver) {
+ auto callback = [aResolver{std::move(aResolver)}](bool aResult) {
+ aResolver(aResult);
+ };
+ RefPtr<HSTSDataCallbackWrapper> wrapper =
+ new HSTSDataCallbackWrapper(std::move(callback));
+ gHttpHandler->EnsureHSTSDataReadyNative(wrapper);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult NeckoParent::RecvGetPageThumbStream(
+ nsIURI* aURI, const LoadInfoArgs& aLoadInfoArgs,
+ GetPageThumbStreamResolver&& aResolver) {
+ // Only the privileged about content process is allowed to access
+ // things over the moz-page-thumb protocol. Any other content process
+ // that tries to send this should have been blocked via the
+ // ScriptSecurityManager, but if somehow the process has been tricked into
+ // sending this message, we send IPC_FAIL in order to crash that
+ // likely-compromised content process.
+ if (static_cast<ContentParent*>(Manager())->GetRemoteType() !=
+ PRIVILEGEDABOUT_REMOTE_TYPE) {
+ return IPC_FAIL(this, "Wrong process type");
+ }
+
+ RefPtr<PageThumbProtocolHandler> ph(PageThumbProtocolHandler::GetSingleton());
+ MOZ_ASSERT(ph);
+
+ // Ask the PageThumbProtocolHandler to give us a new input stream for
+ // this URI. The request comes from a PageThumbProtocolHandler in the
+ // child process, but is not guaranteed to be a valid moz-page-thumb URI,
+ // and not guaranteed to represent a resource that the child should be
+ // allowed to access. The PageThumbProtocolHandler is responsible for
+ // validating the request.
+ nsCOMPtr<nsIInputStream> inputStream;
+ bool terminateSender = true;
+ auto inputStreamPromise = ph->NewStream(aURI, &terminateSender);
+
+ if (terminateSender) {
+ return IPC_FAIL(this, "Malformed moz-page-thumb request");
+ }
+
+ inputStreamPromise->Then(
+ GetMainThreadSerialEventTarget(), __func__,
+ [aResolver](const RemoteStreamInfo& aInfo) { aResolver(Some(aInfo)); },
+ [aResolver](nsresult aRv) {
+ // If NewStream failed, we send back an invalid stream to the child so
+ // it can handle the error. MozPromise rejection is reserved for channel
+ // errors/disconnects.
+ Unused << NS_WARN_IF(NS_FAILED(aRv));
+ aResolver(Nothing());
+ });
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult NeckoParent::RecvGetPageIconStream(
+ nsIURI* aURI, const LoadInfoArgs& aLoadInfoArgs,
+ GetPageIconStreamResolver&& aResolver) {
+#ifdef MOZ_PLACES
+ const nsACString& remoteType =
+ ContentParent::Cast(Manager())->GetRemoteType();
+
+ // Only the privileged about content process is allowed to access
+ // things over the page-icon protocol. Any other content process
+ // that tries to send this should have been blocked via the
+ // ScriptSecurityManager, but if somehow the process has been tricked into
+ // sending this message, we send IPC_FAIL in order to crash that
+ // likely-compromised content process.
+ if (remoteType != PRIVILEGEDABOUT_REMOTE_TYPE) {
+ return IPC_FAIL(this, "Wrong process type");
+ }
+
+ nsCOMPtr<nsILoadInfo> loadInfo;
+ nsresult rv = mozilla::ipc::LoadInfoArgsToLoadInfo(aLoadInfoArgs, remoteType,
+ getter_AddRefs(loadInfo));
+ if (NS_FAILED(rv)) {
+ return IPC_FAIL(this, "Page-icon request must include loadInfo");
+ }
+
+ RefPtr<PageIconProtocolHandler> ph(PageIconProtocolHandler::GetSingleton());
+ MOZ_ASSERT(ph);
+
+ nsCOMPtr<nsIInputStream> inputStream;
+ bool terminateSender = true;
+ auto inputStreamPromise = ph->NewStream(aURI, loadInfo, &terminateSender);
+
+ if (terminateSender) {
+ return IPC_FAIL(this, "Malformed page-icon request");
+ }
+
+ inputStreamPromise->Then(
+ GetMainThreadSerialEventTarget(), __func__,
+ [aResolver](const RemoteStreamInfo& aInfo) { aResolver(Some(aInfo)); },
+ [aResolver](nsresult aRv) {
+ // If NewStream failed, we send back an invalid stream to the child so
+ // it can handle the error. MozPromise rejection is reserved for channel
+ // errors/disconnects.
+ Unused << NS_WARN_IF(NS_FAILED(aRv));
+ aResolver(Nothing());
+ });
+
+ return IPC_OK();
+#else
+ return IPC_FAIL(this, "page-icon: protocol unavailable");
+#endif
+}
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/ipc/NeckoParent.h b/netwerk/ipc/NeckoParent.h
new file mode 100644
index 0000000000..9c7ffa4636
--- /dev/null
+++ b/netwerk/ipc/NeckoParent.h
@@ -0,0 +1,217 @@
+/* -*- 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 "mozilla/BasePrincipal.h"
+#include "mozilla/net/PNeckoParent.h"
+#include "mozilla/net/NeckoCommon.h"
+#include "nsIAuthPrompt2.h"
+#include "nsINetworkPredictor.h"
+#include "nsNetUtil.h"
+
+#ifndef mozilla_net_NeckoParent_h
+# define mozilla_net_NeckoParent_h
+
+namespace mozilla {
+namespace net {
+
+// Used to override channel Private Browsing status if needed.
+enum PBOverrideStatus {
+ kPBOverride_Unset = 0,
+ kPBOverride_Private,
+ kPBOverride_NotPrivate
+};
+
+// Header file contents
+class NeckoParent : public PNeckoParent {
+ friend class PNeckoParent;
+
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(NeckoParent, override)
+
+ NeckoParent();
+
+ [[nodiscard]] static const char* GetValidatedOriginAttributes(
+ const SerializedLoadContext& aSerialized, PContentParent* aBrowser,
+ nsIPrincipal* aRequestingPrincipal, mozilla::OriginAttributes& aAttrs);
+
+ /*
+ * Creates LoadContext for parent-side of an e10s channel.
+ *
+ * PContentParent corresponds to the process that is requesting the load.
+ *
+ * Returns null if successful, or an error string if failed.
+ */
+ [[nodiscard]] static const char* CreateChannelLoadContext(
+ PBrowserParent* aBrowser, PContentParent* aContent,
+ const SerializedLoadContext& aSerialized,
+ nsIPrincipal* aRequestingPrincipal, nsCOMPtr<nsILoadContext>& aResult);
+
+ virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+ PCookieServiceParent* AllocPCookieServiceParent();
+ virtual mozilla::ipc::IPCResult RecvPCookieServiceConstructor(
+ PCookieServiceParent* aActor) override {
+ return PNeckoParent::RecvPCookieServiceConstructor(aActor);
+ }
+
+ protected:
+ virtual ~NeckoParent() = default;
+
+ bool mSocketProcessBridgeInited;
+
+ already_AddRefed<PHttpChannelParent> AllocPHttpChannelParent(
+ PBrowserParent*, const SerializedLoadContext&,
+ const HttpChannelCreationArgs& aOpenArgs);
+ virtual mozilla::ipc::IPCResult RecvPHttpChannelConstructor(
+ PHttpChannelParent* aActor, PBrowserParent* aBrowser,
+ const SerializedLoadContext& aSerialized,
+ const HttpChannelCreationArgs& aOpenArgs) override;
+
+ PStunAddrsRequestParent* AllocPStunAddrsRequestParent();
+ bool DeallocPStunAddrsRequestParent(PStunAddrsRequestParent* aActor);
+
+ PWebrtcTCPSocketParent* AllocPWebrtcTCPSocketParent(
+ const Maybe<TabId>& aTabId);
+ bool DeallocPWebrtcTCPSocketParent(PWebrtcTCPSocketParent* aActor);
+
+ PAltDataOutputStreamParent* AllocPAltDataOutputStreamParent(
+ const nsACString& type, const int64_t& predictedSize,
+ PHttpChannelParent* channel);
+ bool DeallocPAltDataOutputStreamParent(PAltDataOutputStreamParent* aActor);
+
+ bool DeallocPCookieServiceParent(PCookieServiceParent*);
+ PWebSocketParent* AllocPWebSocketParent(
+ PBrowserParent* browser, const SerializedLoadContext& aSerialized,
+ const uint32_t& aSerial);
+ bool DeallocPWebSocketParent(PWebSocketParent*);
+ PTCPSocketParent* AllocPTCPSocketParent(const nsAString& host,
+ const uint16_t& port);
+
+ already_AddRefed<PDocumentChannelParent> AllocPDocumentChannelParent(
+ const dom::MaybeDiscarded<dom::BrowsingContext>& aContext,
+ const DocumentChannelCreationArgs& args);
+ virtual mozilla::ipc::IPCResult RecvPDocumentChannelConstructor(
+ PDocumentChannelParent* aActor,
+ const dom::MaybeDiscarded<dom::BrowsingContext>& aContext,
+ const DocumentChannelCreationArgs& aArgs) override;
+ bool DeallocPDocumentChannelParent(PDocumentChannelParent* channel);
+
+ bool DeallocPTCPSocketParent(PTCPSocketParent*);
+ PTCPServerSocketParent* AllocPTCPServerSocketParent(
+ const uint16_t& aLocalPort, const uint16_t& aBacklog,
+ const bool& aUseArrayBuffers);
+ virtual mozilla::ipc::IPCResult RecvPTCPServerSocketConstructor(
+ PTCPServerSocketParent*, const uint16_t& aLocalPort,
+ const uint16_t& aBacklog, const bool& aUseArrayBuffers) override;
+ bool DeallocPTCPServerSocketParent(PTCPServerSocketParent*);
+ PUDPSocketParent* AllocPUDPSocketParent(nsIPrincipal* aPrincipal,
+ const nsACString& aFilter);
+ virtual mozilla::ipc::IPCResult RecvPUDPSocketConstructor(
+ PUDPSocketParent*, nsIPrincipal* aPrincipal,
+ const nsACString& aFilter) override;
+ bool DeallocPUDPSocketParent(PUDPSocketParent*);
+ already_AddRefed<PDNSRequestParent> AllocPDNSRequestParent(
+ const nsACString& aHost, const nsACString& aTrrServer,
+ const int32_t& aPort, const uint16_t& aType,
+ const OriginAttributes& aOriginAttributes,
+ const nsIDNSService::DNSFlags& aFlags);
+ virtual mozilla::ipc::IPCResult RecvPDNSRequestConstructor(
+ PDNSRequestParent* actor, const nsACString& aHost,
+ const nsACString& trrServer, const int32_t& aPort, const uint16_t& type,
+ const OriginAttributes& aOriginAttributes,
+ const nsIDNSService::DNSFlags& flags) override;
+ mozilla::ipc::IPCResult RecvSpeculativeConnect(
+ nsIURI* aURI, nsIPrincipal* aPrincipal,
+ Maybe<OriginAttributes>&& aOriginAttributes, const bool& aAnonymous);
+ mozilla::ipc::IPCResult RecvHTMLDNSPrefetch(
+ const nsAString& hostname, const bool& isHttps,
+ const OriginAttributes& aOriginAttributes,
+ const nsIDNSService::DNSFlags& flags);
+ mozilla::ipc::IPCResult RecvCancelHTMLDNSPrefetch(
+ const nsAString& hostname, const bool& isHttps,
+ const OriginAttributes& aOriginAttributes,
+ const nsIDNSService::DNSFlags& flags, const nsresult& reason);
+ PWebSocketEventListenerParent* AllocPWebSocketEventListenerParent(
+ const uint64_t& aInnerWindowID);
+ bool DeallocPWebSocketEventListenerParent(PWebSocketEventListenerParent*);
+
+ already_AddRefed<PDataChannelParent> AllocPDataChannelParent(
+ const uint32_t& channelId);
+
+ virtual mozilla::ipc::IPCResult RecvPDataChannelConstructor(
+ PDataChannelParent* aActor, const uint32_t& channelId) override;
+# ifdef MOZ_WIDGET_GTK
+ PGIOChannelParent* AllocPGIOChannelParent(
+ PBrowserParent* aBrowser, const SerializedLoadContext& aSerialized,
+ const GIOChannelCreationArgs& aOpenArgs);
+ bool DeallocPGIOChannelParent(PGIOChannelParent* channel);
+
+ virtual mozilla::ipc::IPCResult RecvPGIOChannelConstructor(
+ PGIOChannelParent* aActor, PBrowserParent* aBrowser,
+ const SerializedLoadContext& aSerialized,
+ const GIOChannelCreationArgs& aOpenArgs) override;
+# endif
+ PSimpleChannelParent* AllocPSimpleChannelParent(const uint32_t& channelId);
+ bool DeallocPSimpleChannelParent(PSimpleChannelParent* actor);
+
+ virtual mozilla::ipc::IPCResult RecvPSimpleChannelConstructor(
+ PSimpleChannelParent* aActor, const uint32_t& channelId) override;
+
+ already_AddRefed<PFileChannelParent> AllocPFileChannelParent(
+ const uint32_t& channelId);
+
+ virtual mozilla::ipc::IPCResult RecvPFileChannelConstructor(
+ PFileChannelParent* aActor, const uint32_t& channelId) override;
+
+ PTransportProviderParent* AllocPTransportProviderParent();
+ bool DeallocPTransportProviderParent(PTransportProviderParent* aActor);
+
+ /* Predictor Messages */
+ mozilla::ipc::IPCResult RecvPredPredict(
+ nsIURI* aTargetURI, nsIURI* aSourceURI,
+ const PredictorPredictReason& aReason,
+ const OriginAttributes& aOriginAttributes, const bool& hasVerifier);
+
+ mozilla::ipc::IPCResult RecvPredLearn(
+ nsIURI* aTargetURI, nsIURI* aSourceURI,
+ const PredictorPredictReason& aReason,
+ const OriginAttributes& aOriginAttributes);
+ mozilla::ipc::IPCResult RecvPredReset();
+
+ mozilla::ipc::IPCResult RecvRequestContextLoadBegin(const uint64_t& rcid);
+ mozilla::ipc::IPCResult RecvRequestContextAfterDOMContentLoaded(
+ const uint64_t& rcid);
+ mozilla::ipc::IPCResult RecvRemoveRequestContext(const uint64_t& rcid);
+
+ /* WebExtensions */
+ mozilla::ipc::IPCResult RecvGetExtensionStream(
+ nsIURI* aURI, GetExtensionStreamResolver&& aResolve);
+
+ mozilla::ipc::IPCResult RecvGetExtensionFD(nsIURI* aURI,
+ GetExtensionFDResolver&& aResolve);
+
+ /* Page thumbnails remote resource loading */
+ mozilla::ipc::IPCResult RecvGetPageThumbStream(
+ nsIURI* aURI, const LoadInfoArgs& aLoadInfoArgs,
+ GetPageThumbStreamResolver&& aResolve);
+
+ /* Page icon remote resource loading */
+ mozilla::ipc::IPCResult RecvGetPageIconStream(
+ nsIURI* aURI, const LoadInfoArgs& aLoadInfoArgs,
+ GetPageIconStreamResolver&& aResolve);
+
+ mozilla::ipc::IPCResult RecvInitSocketProcessBridge(
+ InitSocketProcessBridgeResolver&& aResolver);
+ mozilla::ipc::IPCResult RecvResetSocketProcessBridge();
+
+ mozilla::ipc::IPCResult RecvEnsureHSTSData(
+ EnsureHSTSDataResolver&& aResolver);
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozilla_net_NeckoParent_h
diff --git a/netwerk/ipc/NeckoTargetHolder.cpp b/netwerk/ipc/NeckoTargetHolder.cpp
new file mode 100644
index 0000000000..90d80ce7d5
--- /dev/null
+++ b/netwerk/ipc/NeckoTargetHolder.cpp
@@ -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 : */
+
+/* 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 "NeckoTargetHolder.h"
+
+#include "nsContentUtils.h"
+
+namespace mozilla {
+namespace net {
+
+already_AddRefed<nsISerialEventTarget> NeckoTargetHolder::GetNeckoTarget() {
+ nsCOMPtr<nsISerialEventTarget> target = mNeckoTarget;
+
+ if (!target) {
+ target = GetMainThreadSerialEventTarget();
+ }
+ return target.forget();
+}
+
+nsresult NeckoTargetHolder::Dispatch(already_AddRefed<nsIRunnable>&& aRunnable,
+ uint32_t aDispatchFlags) {
+ if (mNeckoTarget) {
+ return mNeckoTarget->Dispatch(std::move(aRunnable), aDispatchFlags);
+ }
+
+ nsCOMPtr<nsISerialEventTarget> mainThreadTarget =
+ GetMainThreadSerialEventTarget();
+ MOZ_ASSERT(mainThreadTarget);
+
+ return mainThreadTarget->Dispatch(std::move(aRunnable), aDispatchFlags);
+}
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/ipc/NeckoTargetHolder.h b/netwerk/ipc/NeckoTargetHolder.h
new file mode 100644
index 0000000000..254d6f138f
--- /dev/null
+++ b/netwerk/ipc/NeckoTargetHolder.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_NeckoTargetHolder_h
+#define mozilla_net_NeckoTargetHolder_h
+
+#include "nsIEventTarget.h"
+#include "nsThreadUtils.h"
+
+namespace mozilla {
+namespace net {
+
+// A helper class that implements GetNeckoTarget(). Basically, all e10s child
+// channels should inherit this class in order to get a labeled event target.
+class NeckoTargetHolder {
+ public:
+ explicit NeckoTargetHolder(nsISerialEventTarget* aNeckoTarget)
+ : mNeckoTarget(aNeckoTarget) {}
+
+ protected:
+ virtual ~NeckoTargetHolder() = default;
+ // Get event target for processing network events.
+ virtual already_AddRefed<nsISerialEventTarget> GetNeckoTarget();
+ // When |mNeckoTarget| is not null, use it to dispatch the runnable.
+ // Otherwise, dispatch the runnable to the main thread.
+ nsresult Dispatch(already_AddRefed<nsIRunnable>&& aRunnable,
+ uint32_t aDispatchFlags = NS_DISPATCH_NORMAL);
+
+ // EventTarget for labeling networking events.
+ nsCOMPtr<nsISerialEventTarget> mNeckoTarget;
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif
diff --git a/netwerk/ipc/PDataChannel.ipdl b/netwerk/ipc/PDataChannel.ipdl
new file mode 100644
index 0000000000..52d8642ff7
--- /dev/null
+++ b/netwerk/ipc/PDataChannel.ipdl
@@ -0,0 +1,25 @@
+/* -*- 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 protocol PNecko;
+
+namespace mozilla {
+namespace net {
+
+[ChildImpl=virtual]
+async protocol PDataChannel
+{
+ manager PNecko;
+
+parent:
+ // Note: channels are opened during construction, so no open method here:
+ // see PNecko.ipdl
+ async __delete__();
+};
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/ipc/PDocumentChannel.ipdl b/netwerk/ipc/PDocumentChannel.ipdl
new file mode 100644
index 0000000000..1e40052a25
--- /dev/null
+++ b/netwerk/ipc/PDocumentChannel.ipdl
@@ -0,0 +1,66 @@
+/* -*- 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 protocol PNecko;
+include protocol PStreamFilter;
+include InputStreamParams;
+include PBackgroundSharedTypes;
+include NeckoChannelParams;
+include IPCServiceWorkerDescriptor;
+include IPCStream;
+include DOMTypes;
+
+include "mozilla/net/NeckoMessageUtils.h";
+
+using class mozilla::net::nsHttpHeaderArray from "nsHttpHeaderArray.h";
+using mozilla::net::NetAddr from "mozilla/net/DNS.h";
+
+namespace mozilla {
+namespace net {
+
+protocol PDocumentChannel
+{
+ manager PNecko;
+
+parent:
+
+ async Cancel(nsresult status, nsCString reason);
+
+ async __delete__();
+
+child:
+
+ // Used to cancel child channel if we hit errors during creating and
+ // AsyncOpen of nsHttpChannel on the parent.
+ async FailedAsyncOpen(nsresult status);
+
+ // This message is sent to a child that has been redirected to another process.
+ // As a consequence, it should cleanup the channel listeners and remove the
+ // request from the loadGroup.
+ // aStatus must be an error result.
+ // aLoadGroupReason is used as mStatus when we remove the child channel from
+ // the loadgroup (but aStatus is passed as the parameter to RemoveRequest).
+ // We do this so we can remove using NS_BINDING_RETARGETED, but still have the
+ // channel not be in an error state.
+ async DisconnectChildListeners(nsresult aStatus, nsresult aLoadGroupReason, bool aContinueNavigating);
+
+ // Triggers replacing this DocumentChannel with a 'real' channel (like PHttpChannel),
+ // and notifies the listener via a redirect to the new channel.
+ async RedirectToRealChannel(RedirectToRealChannelArgs args, Endpoint<PStreamFilterParent>[] aEndpoint)
+ returns (nsresult rv);
+
+ // May only be called on a DocumentChannel created by nsObjectLoadingContent
+ // for an object or embed element load.
+ //
+ // Promotes the load from an object load to a proper document load, and
+ // returns the `BrowsingContext` which should be used to host the final load.
+ async UpgradeObjectLoad() returns (MaybeDiscardedBrowsingContext frameContext);
+};
+
+} // namespace net
+} // namespace mozilla
+
diff --git a/netwerk/ipc/PFileChannel.ipdl b/netwerk/ipc/PFileChannel.ipdl
new file mode 100644
index 0000000000..8f86fc81b1
--- /dev/null
+++ b/netwerk/ipc/PFileChannel.ipdl
@@ -0,0 +1,28 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=2 sts=2 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 protocol PNecko;
+
+namespace mozilla {
+namespace net {
+
+/* Used to facilitate http redirects to file:// - see
+ * https://bugzilla.mozilla.org/show_bug.cgi?id=1345094
+ */
+[ChildImpl=virtual]
+async protocol PFileChannel
+{
+ manager PNecko;
+
+parent:
+ // Note: channels are opened during construction, so no open method here:
+ // see PNecko.ipdl
+ async __delete__();
+};
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/ipc/PInputChannelThrottleQueue.ipdl b/netwerk/ipc/PInputChannelThrottleQueue.ipdl
new file mode 100644
index 0000000000..b95d9cc77f
--- /dev/null
+++ b/netwerk/ipc/PInputChannelThrottleQueue.ipdl
@@ -0,0 +1,25 @@
+/* -*- 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 protocol PSocketProcess;
+
+namespace mozilla {
+namespace net {
+
+protocol PInputChannelThrottleQueue
+{
+ manager PSocketProcess;
+
+parent:
+ async RecordRead(uint32_t aBytesRead);
+
+child:
+ async __delete__();
+};
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/ipc/PNecko.ipdl b/netwerk/ipc/PNecko.ipdl
new file mode 100644
index 0000000000..9eb9b5ca27
--- /dev/null
+++ b/netwerk/ipc/PNecko.ipdl
@@ -0,0 +1,178 @@
+/* -*- 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 protocol PContent;
+include protocol PHttpChannel;
+include protocol PCookieService;
+include protocol PBrowser;
+#ifdef MOZ_WIDGET_GTK
+include protocol PGIOChannel;
+#endif
+include protocol PWebSocket;
+include protocol PWebSocketEventListener;
+include protocol PTCPSocket;
+include protocol PTCPServerSocket;
+include protocol PUDPSocket;
+include protocol PDNSRequest;
+include protocol PDataChannel;
+include protocol PSimpleChannel;
+include protocol PTransportProvider;
+include protocol PStunAddrsRequest;
+include protocol PFileChannel;
+include protocol PWebrtcTCPSocket;
+include protocol PSocketProcessBridge;
+include protocol PDocumentChannel;
+
+include IPCStream;
+include NeckoChannelParams;
+include protocol PAltDataOutputStream;
+
+include "mozilla/dom/PermissionMessageUtils.h";
+
+using mozilla::dom::MaybeDiscardedBrowsingContext from "mozilla/dom/BrowsingContext.h";
+using class IPC::SerializedLoadContext from "SerializedLoadContext.h";
+using mozilla::dom::TabId from "mozilla/dom/ipc/IdType.h";
+using nsIDNSService::DNSFlags from "nsIDNSService.h";
+[RefCounted] using class nsIInputStream from "mozilla/ipc/IPCStreamUtils.h";
+[RefCounted] using class nsIURI from "mozilla/ipc/URIUtils.h";
+[RefCounted] using class nsIPrincipal from "nsIPrincipal.h";
+
+
+namespace mozilla {
+namespace net {
+
+//-------------------------------------------------------------------
+[NestedUpTo=inside_cpow] sync protocol PNecko
+{
+ manager PContent;
+ manages PHttpChannel;
+ manages PCookieService;
+ manages PWebSocket;
+ manages PWebSocketEventListener;
+ manages PTCPSocket;
+ manages PTCPServerSocket;
+ manages PUDPSocket;
+ manages PDNSRequest;
+ manages PDataChannel;
+#ifdef MOZ_WIDGET_GTK
+ manages PGIOChannel;
+#endif
+ manages PSimpleChannel;
+ manages PFileChannel;
+ manages PTransportProvider;
+ manages PAltDataOutputStream;
+ manages PStunAddrsRequest;
+ manages PWebrtcTCPSocket;
+ manages PDocumentChannel;
+
+parent:
+ async __delete__();
+
+ [Nested=inside_cpow] async PCookieService();
+ async PHttpChannel(nullable PBrowser browser,
+ SerializedLoadContext loadContext,
+ HttpChannelCreationArgs args);
+
+ async PWebSocket(nullable PBrowser browser, SerializedLoadContext loadContext,
+ uint32_t aSerialID);
+ async PTCPServerSocket(uint16_t localPort, uint16_t backlog, bool useArrayBuffers);
+ async PUDPSocket(nullable nsIPrincipal principal, nsCString filter);
+
+ async PDNSRequest(nsCString hostName, nsCString trrServer, int32_t port,
+ uint16_t type, OriginAttributes originAttributes,
+ DNSFlags flags);
+
+ async PDocumentChannel(MaybeDiscardedBrowsingContext browsingContext,
+ DocumentChannelCreationArgs args);
+
+ async PWebSocketEventListener(uint64_t aInnerWindowID);
+
+ /* Predictor Methods */
+ async PredPredict(nullable nsIURI targetURI, nullable nsIURI sourceURI,
+ uint32_t reason, OriginAttributes originAttributes,
+ bool hasVerifier);
+ async PredLearn(nullable nsIURI targetURI, nullable nsIURI sourceURI,
+ uint32_t reason, OriginAttributes originAttributes);
+ async PredReset();
+
+ async SpeculativeConnect(nullable nsIURI uri,
+ nullable nsIPrincipal principal,
+ OriginAttributes? originAttributes,
+ bool anonymous);
+ async HTMLDNSPrefetch(nsString hostname, bool isHttps,
+ OriginAttributes originAttributes, DNSFlags flags);
+ async CancelHTMLDNSPrefetch(nsString hostname, bool isHttps,
+ OriginAttributes originAttributes,
+ DNSFlags flags, nsresult reason);
+
+ /**
+ * channelId is used to establish a connection between redirect channels in
+ * the parent and the child when we're redirecting to a data: URI.
+ */
+ async PDataChannel(uint32_t channelId);
+#ifdef MOZ_WIDGET_GTK
+ async PGIOChannel(nullable PBrowser browser, SerializedLoadContext loadContext, GIOChannelCreationArgs args);
+#endif
+ async PSimpleChannel(uint32_t channelId);
+ async PFileChannel(uint32_t channelId);
+
+ async RequestContextLoadBegin(uint64_t rcid);
+ async RequestContextAfterDOMContentLoaded(uint64_t rcid);
+ async RemoveRequestContext(uint64_t rcid);
+
+ async PAltDataOutputStream(nsCString type, int64_t predictedSize, PHttpChannel channel);
+
+ async PStunAddrsRequest();
+
+ /* tabId is only required for web-proxy support, which isn't always needed */
+ async PWebrtcTCPSocket(TabId? tabId);
+
+ /**
+ * WebExtension-specific remote resource loading
+ */
+ async GetExtensionStream(nullable nsIURI uri) returns (nullable nsIInputStream stream);
+ async GetExtensionFD(nullable nsIURI uri) returns (FileDescriptor fd);
+
+ async InitSocketProcessBridge()
+ returns (Endpoint<PSocketProcessBridgeChild> endpoint);
+ async ResetSocketProcessBridge();
+
+ async EnsureHSTSData()
+ returns (bool result);
+
+ /**
+ * Page thumbnails remote resource loading
+ */
+ async GetPageThumbStream(nullable nsIURI uri, LoadInfoArgs loadInfo) returns (RemoteStreamInfo? info);
+ async GetPageIconStream(nullable nsIURI uri, LoadInfoArgs loadInfo) returns (RemoteStreamInfo? info);
+
+child:
+ /* Predictor Methods */
+ async PredOnPredictPrefetch(nullable nsIURI uri, uint32_t httpStatus);
+ async PredOnPredictPreconnect(nullable nsIURI uri);
+ async PredOnPredictDNS(nullable nsIURI uri);
+
+ async SpeculativeConnectRequest();
+
+ // Using medium high priority to deliver this notification possibly sooner than we
+ // enter poll() on the child process with infinite timeout.
+ [Priority=mediumhigh] async NetworkChangeNotification(nsCString type);
+
+ async PTransportProvider();
+
+ async SetTRRDomain(nsCString domain);
+
+both:
+ // Actually we need PTCPSocket() for parent. But ipdl disallows us having different
+ // signatures on parent and child. So when constructing the parent side object, we just
+ // leave host/port unused.
+ async PTCPSocket(nsString host, uint16_t port);
+};
+
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/ipc/PProxyAutoConfig.ipdl b/netwerk/ipc/PProxyAutoConfig.ipdl
new file mode 100644
index 0000000000..7018243fa6
--- /dev/null
+++ b/netwerk/ipc/PProxyAutoConfig.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 : */
+/* 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 {
+
+[ChildProc=Socket]
+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);
+};
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/ipc/PProxyConfigLookup.ipdl b/netwerk/ipc/PProxyConfigLookup.ipdl
new file mode 100644
index 0000000000..9aaaf5fbcb
--- /dev/null
+++ b/netwerk/ipc/PProxyConfigLookup.ipdl
@@ -0,0 +1,21 @@
+/* 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 protocol PSocketProcess;
+
+include NeckoChannelParams;
+
+namespace mozilla {
+namespace net {
+
+async protocol PProxyConfigLookup
+{
+ manager PSocketProcess;
+
+child:
+ async __delete__(ProxyInfoCloneArgs[] aProxyInfo, nsresult aResult);
+};
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/ipc/PSimpleChannel.ipdl b/netwerk/ipc/PSimpleChannel.ipdl
new file mode 100644
index 0000000000..cae7fe8f1b
--- /dev/null
+++ b/netwerk/ipc/PSimpleChannel.ipdl
@@ -0,0 +1,25 @@
+/* -*- 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 protocol PNecko;
+
+namespace mozilla {
+namespace net {
+
+[ManualDealloc, ChildImpl=virtual]
+async protocol PSimpleChannel
+{
+ manager PNecko;
+
+parent:
+ // Note: channels are opened during construction, so no open method here:
+ // see PNecko.ipdl
+ async __delete__();
+};
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/ipc/PSocketProcess.ipdl b/netwerk/ipc/PSocketProcess.ipdl
new file mode 100644
index 0000000000..ede6397fa4
--- /dev/null
+++ b/netwerk/ipc/PSocketProcess.ipdl
@@ -0,0 +1,222 @@
+/* -*- 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 protocol PDNSRequest;
+include protocol PSocketProcessBridge;
+include protocol PProfiler;
+include protocol PWebrtcTCPSocket;
+include protocol PHttpTransaction;
+include protocol PHttpConnectionMgr;
+include protocol PInputChannelThrottleQueue;
+include protocol PAltService;
+include protocol PAltSvcTransaction;
+include protocol PTRRService;
+include protocol PProxyConfigLookup;
+include protocol PNativeDNSResolverOverride;
+include protocol PProxyAutoConfig;
+include protocol PSocketProcessBackground;
+
+#if defined(MOZ_SANDBOX) && defined(MOZ_DEBUG) && defined(ENABLE_TESTS)
+include protocol PSandboxTesting;
+#endif
+
+include MemoryReportTypes;
+include NeckoChannelParams;
+include PrefsTypes;
+
+include "mozilla/ipc/ByteBufUtils.h";
+
+using mozilla::dom::NativeThreadId from "mozilla/dom/NativeThreadId.h";
+using mozilla::dom::TabId from "mozilla/dom/ipc/IdType.h";
+using mozilla::Telemetry::HistogramAccumulation from "mozilla/TelemetryComms.h";
+using mozilla::Telemetry::KeyedHistogramAccumulation from "mozilla/TelemetryComms.h";
+using mozilla::Telemetry::ScalarAction from "mozilla/TelemetryComms.h";
+using mozilla::Telemetry::KeyedScalarAction from "mozilla/TelemetryComms.h";
+using mozilla::Telemetry::ChildEventData from "mozilla/TelemetryComms.h";
+using mozilla::Telemetry::DiscardedData from "mozilla/TelemetryComms.h";
+using base::ProcessId from "base/process.h";
+using mozilla::OriginAttributes from "mozilla/ipc/BackgroundUtils.h";
+using PRTime from "prtime.h";
+[RefCounted] using class nsIURI from "mozilla/ipc/URIUtils.h";
+using struct nsID from "nsID.h";
+using mozilla::net::SocketInfo from "mozilla/net/DashboardTypes.h";
+using mozilla::net::DNSCacheEntries from "mozilla/net/DashboardTypes.h";
+using mozilla::net::HttpRetParams from "mozilla/net/DashboardTypes.h";
+using nsIDNSService::DNSFlags from "nsIDNSService.h";
+
+#if defined(XP_WIN)
+[MoveOnly] using mozilla::UntrustedModulesData from "mozilla/UntrustedModulesData.h";
+[MoveOnly] using mozilla::ModulePaths from "mozilla/UntrustedModulesData.h";
+[MoveOnly] using mozilla::ModulesMapResult from "mozilla/UntrustedModulesData.h";
+#endif // defined(XP_WIN)
+
+namespace mozilla {
+namespace net {
+
+struct HttpHandlerInitArgs {
+ nsCString mLegacyAppName;
+ nsCString mLegacyAppVersion;
+ nsCString mPlatform;
+ nsCString mOscpu;
+ nsCString mMisc;
+ nsCString mProduct;
+ nsCString mProductSub;
+ nsCString mAppName;
+ nsCString mAppVersion;
+ nsCString mCompatFirefox;
+ nsCString mCompatDevice;
+ nsCString mDeviceModelId;
+};
+
+struct SocketDataArgs
+{
+ uint64_t totalSent;
+ uint64_t totalRecv;
+ SocketInfo[] info;
+};
+
+struct SocketPorcessInitAttributes {
+ bool mOffline;
+ bool mConnectivity;
+ bool mInitSandbox;
+#if defined(XP_WIN)
+ bool mIsReadyForBackgroundProcessing;
+#endif
+ FileDescriptor? mSandboxBroker;
+};
+
+[NeedsOtherPid, ChildProc=Socket]
+sync protocol PSocketProcess
+{
+ manages PDNSRequest;
+ manages PWebrtcTCPSocket;
+ manages PHttpTransaction;
+ manages PHttpConnectionMgr;
+ manages PInputChannelThrottleQueue;
+ manages PAltService;
+ manages PAltSvcTransaction;
+ manages PTRRService;
+ manages PProxyConfigLookup;
+ manages PNativeDNSResolverOverride;
+
+parent:
+ async InitCrashReporter(NativeThreadId threadId);
+ async AddMemoryReport(MemoryReport aReport);
+ // Messages for sending telemetry to parent process.
+ async AccumulateChildHistograms(HistogramAccumulation[] accumulations);
+ async AccumulateChildKeyedHistograms(KeyedHistogramAccumulation[] accumulations);
+ async UpdateChildScalars(ScalarAction[] actions);
+ async UpdateChildKeyedScalars(KeyedScalarAction[] actions);
+ async RecordChildEvents(ChildEventData[] events);
+ async RecordDiscardedData(DiscardedData data);
+
+ /* tabId is only required for web-proxy support, which isn't always needed */
+ async PWebrtcTCPSocket(TabId? tabId);
+ async ObserveHttpActivity(HttpActivityArgs aActivityArgs,
+ uint32_t aActivityType,
+ uint32_t aActivitySubtype,
+ PRTime aTimestamp,
+ uint64_t aExtraSizeData,
+ nsCString aExtraStringData);
+ async InitSocketBackground(Endpoint<PSocketProcessBackgroundParent> aEndpoint);
+ async PAltService();
+ async PProxyConfigLookup(nullable nsIURI aUri, uint32_t aFlags);
+ async CachePushCheck(nullable nsIURI aPushedURL,
+ OriginAttributes aOriginAttributes,
+ nsCString aRequestString)
+ returns (bool aAccepted);
+
+ async ExcludeHttp2OrHttp3(HttpConnectionInfoCloneArgs aArgs);
+
+ async OnConsoleMessage(nsString aMessage);
+
+ // Sent from time-to-time to limit the amount of telemetry vulnerable to loss
+ // Buffer contains bincoded Rust structs.
+ // https://firefox-source-docs.mozilla.org/toolkit/components/glean/dev/ipc.html
+ async FOGData(ByteBuf buf);
+
+#if defined(XP_WIN)
+ async GetModulesTrust(ModulePaths aModPaths, bool aRunAtNormalPriority)
+ returns (ModulesMapResult? modMapResult);
+#endif // defined(XP_WIN)
+
+child:
+ async Init(SocketPorcessInitAttributes aAttributes);
+ async PreferenceUpdate(Pref pref);
+ async RequestMemoryReport(uint32_t generation,
+ bool anonymize,
+ bool minimizeMemoryUsage,
+ FileDescriptor? DMDFile)
+ returns (uint32_t aGeneration);
+ async SetOffline(bool offline);
+ async SetConnectivity(bool connectivity);
+ async InitLinuxSandbox(FileDescriptor? sandboxBroker);
+ async InitSocketProcessBridgeParent(ProcessId processId, Endpoint<PSocketProcessBridgeParent> endpoint);
+ async InitProfiler(Endpoint<PProfilerChild> aEndpoint);
+#if defined(MOZ_SANDBOX) && defined(MOZ_DEBUG) && defined(ENABLE_TESTS)
+ async InitSandboxTesting(Endpoint<PSandboxTestingChild> aEndpoint);
+#endif
+ // test-only
+ async SocketProcessTelemetryPing();
+
+ async PHttpTransaction();
+ async PHttpConnectionMgr(HttpHandlerInitArgs aArgs);
+ async UpdateDeviceModelId(nsCString aModelId);
+
+ async OnHttpActivityDistributorActivated(bool aIsActivated);
+ async OnHttpActivityDistributorObserveProxyResponse(bool aIsEnabled);
+ async OnHttpActivityDistributorObserveConnection(bool aIsEnabled);
+ async PInputChannelThrottleQueue(uint32_t meanBytesPerSecond,
+ uint32_t maxBytesPerSecond);
+ async PAltSvcTransaction(HttpConnectionInfoCloneArgs aConnInfo,
+ uint32_t aCaps);
+ async ClearSessionCache() returns (void_t ok);
+ async PTRRService(bool aCaptiveIsPassed,
+ bool aParentalControlEnabled,
+ nsCString[] aDNSSuffixList);
+ async PNativeDNSResolverOverride();
+ async NotifyObserver(nsCString aTopic, nsString aData);
+
+ async GetSocketData()
+ returns (SocketDataArgs data);
+ async GetDNSCacheEntries()
+ returns (DNSCacheEntries[] entries);
+ async GetHttpConnectionData()
+ returns (HttpRetParams[] params);
+
+ async InitProxyAutoConfigChild(Endpoint<PProxyAutoConfigChild> endpoint);
+
+ async RecheckIPConnectivity();
+ async RecheckDNS();
+
+ // Tells the Socket process to flush any pending telemetry.
+ // Used in tests and ping assembly. Buffer contains bincoded Rust structs.
+ // https://firefox-source-docs.mozilla.org/toolkit/components/glean/dev/ipc.html
+ async FlushFOGData() returns (ByteBuf buf);
+
+ // Test-only method.
+ // Asks the Socket process to trigger test-only instrumentation.
+ // The unused returned value is to have a promise we can await.
+ async TestTriggerMetrics() returns (bool unused);
+
+#if defined(XP_WIN)
+ async GetUntrustedModulesData() returns (UntrustedModulesData? data);
+
+ /**
+ * This method is used to notifty a child process to start
+ * processing module loading events in UntrustedModulesProcessor.
+ * This should be called when the parent process has gone idle.
+ */
+ async UnblockUntrustedModulesThread();
+#endif // defined(XP_WIN)
+
+both:
+ async PDNSRequest(nsCString hostName, nsCString trrServer, int32_t port,
+ uint16_t type, OriginAttributes originAttributes,
+ DNSFlags flags);
+};
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/ipc/PSocketProcessBackground.ipdl b/netwerk/ipc/PSocketProcessBackground.ipdl
new file mode 100644
index 0000000000..8c703a4ae2
--- /dev/null
+++ b/netwerk/ipc/PSocketProcessBackground.ipdl
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include protocol PVerifySSLServerCert;
+include protocol PSelectTLSClientAuthCert;
+include protocol PIPCClientCerts;
+include protocol PWebSocketConnection;
+
+include PSMIPCTypes;
+
+using mozilla::OriginAttributes from "mozilla/ipc/BackgroundUtils.h";
+
+namespace mozilla {
+namespace net {
+
+/**
+ * PSocketProcessBackground is the top level IPC protocol between
+ * a background task queue in the socket process and the parent process.
+ * We use this to create several IPC protocols between the socket process
+ * and the parent process.
+ */
+[ChildProc=Socket]
+sync protocol PSocketProcessBackground
+{
+parent:
+ async InitVerifySSLServerCert(
+ Endpoint<PVerifySSLServerCertParent> aEndpoint,
+ ByteArray[] aPeerCertChain,
+ nsCString aHostName,
+ int32_t aPort,
+ OriginAttributes aOriginAttributes,
+ ByteArray? aStapledOCSPResponse,
+ ByteArray? aSctsFromTLSExtension,
+ DelegatedCredentialInfoArg? aDcInfo,
+ uint32_t aProviderFlags,
+ uint32_t aCertVerifierFlags);
+
+ async InitSelectTLSClientAuthCert(
+ Endpoint<PSelectTLSClientAuthCertParent> aEndpoint,
+ nsCString aHostName,
+ OriginAttributes aOriginAttributes,
+ int32_t aPort,
+ uint32_t aProviderFlags,
+ uint32_t aProviderTlsFlags,
+ ByteArray aServerCertBytes,
+ ByteArray[] aCANames,
+ uint64_t aBrowserId);
+
+ async InitIPCClientCerts(Endpoint<PIPCClientCertsParent> aEndpoint);
+
+ async InitWebSocketConnection(Endpoint<PWebSocketConnectionParent> aEndpoint,
+ uint32_t aListenerId);
+};
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/ipc/PSocketProcessBridge.ipdl b/netwerk/ipc/PSocketProcessBridge.ipdl
new file mode 100644
index 0000000000..36c808bfdc
--- /dev/null
+++ b/netwerk/ipc/PSocketProcessBridge.ipdl
@@ -0,0 +1,48 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include protocol PBackgroundDataBridge;
+
+#ifdef MOZ_WEBRTC
+include protocol PMediaTransport;
+#endif // MOZ_WEBRTC
+
+
+namespace mozilla {
+namespace net {
+
+/**
+ * PSocketProcessBridge is the IPC protocol between the main thread in
+ * the content process and the main thread in the socket process.
+ * We use this to create PBackgroundDataBridge and PMediaTransport protocols
+ * between content process and socket process.
+ * Once created, PSocketProcessBridgeChild is the actor that lives in
+ * content process and PSocketProcessBridgeParent lives in
+ * socket process.
+ */
+[NeedsOtherPid, ParentProc=Socket, ChildProc=Content]
+sync protocol PSocketProcessBridge
+{
+parent:
+ /**
+ * For setting up PBackgroundDataBridge protocol, we use this message to
+ * create a background task queue and the BackgroundDataBridgeParent actor in
+ * socket process.
+ */
+ async InitBackgroundDataBridge(Endpoint<PBackgroundDataBridgeParent> aEndpoint,
+ uint64_t aChannelID);
+
+#ifdef MOZ_WEBRTC
+ /**
+ * Similar to the above, this message is for PMediaTransport.
+ */
+ async InitMediaTransport(Endpoint<PMediaTransportParent> aEndpoint);
+#endif // MOZ_WEBRTC
+
+};
+
+}
+}
diff --git a/netwerk/ipc/ParentChannelWrapper.cpp b/netwerk/ipc/ParentChannelWrapper.cpp
new file mode 100644
index 0000000000..3b89e3e38f
--- /dev/null
+++ b/netwerk/ipc/ParentChannelWrapper.cpp
@@ -0,0 +1,101 @@
+/* -*- 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 "ParentChannelWrapper.h"
+#include "mozilla/net/HttpBaseChannel.h"
+#include "mozilla/net/UrlClassifierCommon.h"
+#include "mozilla/net/RedirectChannelRegistrar.h"
+#include "nsIViewSourceChannel.h"
+#include "nsNetUtil.h"
+#include "nsQueryObject.h"
+#include "mozilla/dom/RemoteType.h"
+
+namespace mozilla {
+namespace net {
+
+NS_IMPL_ISUPPORTS(ParentChannelWrapper, nsIParentChannel, nsIStreamListener,
+ nsIRequestObserver);
+
+void ParentChannelWrapper::Register(uint64_t aRegistrarId) {
+ nsCOMPtr<nsIRedirectChannelRegistrar> registrar =
+ RedirectChannelRegistrar::GetOrCreate();
+ nsCOMPtr<nsIChannel> dummy;
+ MOZ_ALWAYS_SUCCEEDS(
+ NS_LinkRedirectChannels(aRegistrarId, this, getter_AddRefs(dummy)));
+
+#ifdef DEBUG
+ // The channel registered with the RedirectChannelRegistrar will be the inner
+ // channel when dealing with view-source loads.
+ if (nsCOMPtr<nsIViewSourceChannel> viewSource = do_QueryInterface(mChannel)) {
+ MOZ_ASSERT(dummy == viewSource->GetInnerChannel());
+ } else {
+ MOZ_ASSERT(dummy == mChannel);
+ }
+#endif
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// nsIParentChannel
+////////////////////////////////////////////////////////////////////////////////
+
+NS_IMETHODIMP
+ParentChannelWrapper::SetParentListener(
+ mozilla::net::ParentChannelListener* listener) {
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ParentChannelWrapper::SetClassifierMatchedInfo(const nsACString& aList,
+ const nsACString& aProvider,
+ const nsACString& aFullHash) {
+ nsCOMPtr<nsIClassifiedChannel> classifiedChannel =
+ do_QueryInterface(mChannel);
+ if (classifiedChannel) {
+ classifiedChannel->SetMatchedInfo(aList, aProvider, aFullHash);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ParentChannelWrapper::SetClassifierMatchedTrackingInfo(
+ const nsACString& aLists, const nsACString& aFullHash) {
+ nsCOMPtr<nsIClassifiedChannel> classifiedChannel =
+ do_QueryInterface(mChannel);
+ if (classifiedChannel) {
+ nsTArray<nsCString> lists, fullhashes;
+ for (const nsACString& token : aLists.Split(',')) {
+ lists.AppendElement(token);
+ }
+ for (const nsACString& token : aFullHash.Split(',')) {
+ fullhashes.AppendElement(token);
+ }
+ classifiedChannel->SetMatchedTrackingInfo(lists, fullhashes);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ParentChannelWrapper::NotifyClassificationFlags(uint32_t aClassificationFlags,
+ bool aIsThirdParty) {
+ UrlClassifierCommon::SetClassificationFlagsHelper(
+ mChannel, aClassificationFlags, aIsThirdParty);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ParentChannelWrapper::Delete() { return NS_OK; }
+
+NS_IMETHODIMP
+ParentChannelWrapper::GetRemoteType(nsACString& aRemoteType) {
+ aRemoteType = NOT_REMOTE_TYPE;
+ return NS_OK;
+}
+
+} // namespace net
+} // namespace mozilla
+
+#undef LOG
diff --git a/netwerk/ipc/ParentChannelWrapper.h b/netwerk/ipc/ParentChannelWrapper.h
new file mode 100644
index 0000000000..8d7d6dd73d
--- /dev/null
+++ b/netwerk/ipc/ParentChannelWrapper.h
@@ -0,0 +1,39 @@
+/* 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_ParentChannelWrapper_h
+#define mozilla_net_ParentChannelWrapper_h
+
+#include "nsIParentChannel.h"
+#include "nsIStreamListener.h"
+
+namespace mozilla {
+namespace net {
+
+class ParentChannelWrapper : public nsIParentChannel {
+ public:
+ ParentChannelWrapper(nsIChannel* aChannel, nsIStreamListener* aListener)
+ : mChannel(aChannel), mListener(aListener) {}
+
+ // Registers this nsIParentChannel wrapper with the RedirectChannelRegistrar
+ // and holds a reference.
+ void Register(uint64_t aRegistrarId);
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIPARENTCHANNEL
+ NS_FORWARD_NSISTREAMLISTENER(mListener->)
+ NS_FORWARD_NSIREQUESTOBSERVER(mListener->)
+
+ private:
+ virtual ~ParentChannelWrapper() = default;
+ const nsCOMPtr<nsIChannel> mChannel;
+ const nsCOMPtr<nsIStreamListener> mListener;
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozilla_net_ParentChannelWrapper_h
diff --git a/netwerk/ipc/ParentProcessDocumentChannel.cpp b/netwerk/ipc/ParentProcessDocumentChannel.cpp
new file mode 100644
index 0000000000..ed9decac72
--- /dev/null
+++ b/netwerk/ipc/ParentProcessDocumentChannel.cpp
@@ -0,0 +1,314 @@
+/* -*- 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 "ParentProcessDocumentChannel.h"
+
+#include "mozilla/extensions/StreamFilterParent.h"
+#include "mozilla/net/ParentChannelWrapper.h"
+#include "mozilla/net/UrlClassifierCommon.h"
+#include "mozilla/StaticPrefs_extensions.h"
+#include "nsCRT.h"
+#include "nsDocShell.h"
+#include "nsIObserverService.h"
+#include "nsIClassifiedChannel.h"
+#include "nsIXULRuntime.h"
+#include "nsHttpHandler.h"
+#include "nsDocShellLoadState.h"
+
+extern mozilla::LazyLogModule gDocumentChannelLog;
+#define LOG(fmt) MOZ_LOG(gDocumentChannelLog, mozilla::LogLevel::Verbose, fmt)
+
+namespace mozilla {
+namespace net {
+
+using RedirectToRealChannelPromise =
+ typename PDocumentChannelParent::RedirectToRealChannelPromise;
+
+NS_IMPL_ISUPPORTS_INHERITED(ParentProcessDocumentChannel, DocumentChannel,
+ nsIAsyncVerifyRedirectCallback, nsIObserver)
+
+ParentProcessDocumentChannel::ParentProcessDocumentChannel(
+ nsDocShellLoadState* aLoadState, class LoadInfo* aLoadInfo,
+ nsLoadFlags aLoadFlags, uint32_t aCacheKey, bool aUriModified,
+ bool aIsEmbeddingBlockedError)
+ : DocumentChannel(aLoadState, aLoadInfo, aLoadFlags, aCacheKey,
+ aUriModified, aIsEmbeddingBlockedError) {
+ LOG(("ParentProcessDocumentChannel ctor [this=%p]", this));
+}
+
+ParentProcessDocumentChannel::~ParentProcessDocumentChannel() {
+ LOG(("ParentProcessDocumentChannel dtor [this=%p]", this));
+}
+
+RefPtr<RedirectToRealChannelPromise>
+ParentProcessDocumentChannel::RedirectToRealChannel(
+ nsTArray<ipc::Endpoint<extensions::PStreamFilterParent>>&&
+ aStreamFilterEndpoints,
+ uint32_t aRedirectFlags, uint32_t aLoadFlags,
+ const nsTArray<EarlyHintConnectArgs>& aEarlyHints) {
+ LOG(("ParentProcessDocumentChannel RedirectToRealChannel [this=%p]", this));
+ nsCOMPtr<nsIChannel> channel = mDocumentLoadListener->GetChannel();
+ channel->SetLoadFlags(aLoadFlags);
+ channel->SetNotificationCallbacks(mCallbacks);
+
+ if (mLoadGroup) {
+ channel->SetLoadGroup(mLoadGroup);
+ }
+
+ if (XRE_IsE10sParentProcess()) {
+ nsCOMPtr<nsIURI> uri;
+ MOZ_ALWAYS_SUCCEEDS(NS_GetFinalChannelURI(channel, getter_AddRefs(uri)));
+ if (!nsDocShell::CanLoadInParentProcess(uri)) {
+ nsAutoCString msg;
+ uri->GetSpec(msg);
+ msg.Insert(
+ "Attempt to load a non-authorised load in the parent process: ", 0);
+ NS_ASSERTION(false, msg.get());
+ return RedirectToRealChannelPromise::CreateAndResolve(
+ NS_ERROR_CONTENT_BLOCKED, __func__);
+ }
+ }
+ mStreamFilterEndpoints = std::move(aStreamFilterEndpoints);
+
+ if (mDocumentLoadListener->IsDocumentLoad() &&
+ mozilla::SessionHistoryInParent() && GetDocShell() &&
+ mDocumentLoadListener->GetLoadingSessionHistoryInfo()) {
+ GetDocShell()->SetLoadingSessionHistoryInfo(
+ *mDocumentLoadListener->GetLoadingSessionHistoryInfo());
+ }
+
+ RefPtr<RedirectToRealChannelPromise> p = mPromise.Ensure(__func__);
+ // We make the promise use direct task dispatch in order to reduce the number
+ // of event loops iterations.
+ mPromise.UseDirectTaskDispatch(__func__);
+
+ nsresult rv =
+ gHttpHandler->AsyncOnChannelRedirect(this, channel, aRedirectFlags);
+ if (NS_FAILED(rv)) {
+ LOG(
+ ("ParentProcessDocumentChannel RedirectToRealChannel "
+ "AsyncOnChannelRedirect failed [this=%p "
+ "aRv=%d]",
+ this, int(rv)));
+ OnRedirectVerifyCallback(rv);
+ }
+
+ return p;
+}
+
+NS_IMETHODIMP
+ParentProcessDocumentChannel::OnRedirectVerifyCallback(nsresult aResult) {
+ LOG(
+ ("ParentProcessDocumentChannel OnRedirectVerifyCallback [this=%p "
+ "aResult=%d]",
+ this, int(aResult)));
+
+ MOZ_ASSERT(mDocumentLoadListener);
+
+ if (NS_FAILED(aResult)) {
+ Cancel(aResult);
+ } else if (mCanceled) {
+ aResult = NS_ERROR_ABORT;
+ } else {
+ const nsCOMPtr<nsIChannel> channel = mDocumentLoadListener->GetChannel();
+ mLoadGroup->AddRequest(channel, nullptr);
+ // Adding the channel to the loadgroup could have triggered a status
+ // change with an observer being called destroying the docShell, resulting
+ // in the PPDC to be canceled.
+ if (mCanceled) {
+ aResult = NS_ERROR_ABORT;
+ } else {
+ mLoadGroup->RemoveRequest(this, nullptr, NS_BINDING_REDIRECTED);
+ for (auto& endpoint : mStreamFilterEndpoints) {
+ extensions::StreamFilterParent::Attach(channel, std::move(endpoint));
+ }
+
+ RefPtr<ParentChannelWrapper> wrapper =
+ new ParentChannelWrapper(channel, mListener);
+
+ wrapper->Register(mDocumentLoadListener->GetRedirectChannelId());
+ }
+ }
+
+ mPromise.Resolve(aResult, __func__);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP ParentProcessDocumentChannel::AsyncOpen(
+ nsIStreamListener* aListener) {
+ LOG(("ParentProcessDocumentChannel AsyncOpen [this=%p]", this));
+ auto docShell = RefPtr<nsDocShell>(GetDocShell());
+ MOZ_ASSERT(docShell);
+
+ bool isDocumentLoad = mLoadInfo->GetExternalContentPolicyType() !=
+ ExtContentPolicy::TYPE_OBJECT;
+
+ mDocumentLoadListener = MakeRefPtr<DocumentLoadListener>(
+ docShell->GetBrowsingContext()->Canonical(), isDocumentLoad);
+ LOG(("Created PPDocumentChannel with listener=%p",
+ mDocumentLoadListener.get()));
+
+ // Add observers.
+ nsCOMPtr<nsIObserverService> observerService =
+ mozilla::services::GetObserverService();
+ if (observerService) {
+ MOZ_ALWAYS_SUCCEEDS(observerService->AddObserver(
+ this, NS_HTTP_ON_MODIFY_REQUEST_TOPIC, false));
+ }
+
+ gHttpHandler->OnOpeningDocumentRequest(this);
+
+ if (isDocumentLoad) {
+ // Return value of setting synced field should be checked. See bug 1656492.
+ Unused << GetDocShell()->GetBrowsingContext()->SetCurrentLoadIdentifier(
+ Some(mLoadState->GetLoadIdentifier()));
+ }
+
+ nsresult rv = NS_OK;
+ Maybe<dom::ClientInfo> initialClientInfo = mInitialClientInfo;
+
+ RefPtr<DocumentLoadListener::OpenPromise> promise;
+ if (isDocumentLoad) {
+ promise = mDocumentLoadListener->OpenDocument(
+ mLoadState, mCacheKey, Some(mChannelId), TimeStamp::Now(), mTiming,
+ std::move(initialClientInfo), Some(mUriModified),
+ Some(mIsEmbeddingBlockedError), nullptr /* ContentParent */, &rv);
+ } else {
+ promise = mDocumentLoadListener->OpenObject(
+ mLoadState, mCacheKey, Some(mChannelId), TimeStamp::Now(), mTiming,
+ std::move(initialClientInfo), InnerWindowIDForExtantDoc(docShell),
+ mLoadFlags, mLoadInfo->InternalContentPolicyType(),
+ dom::UserActivation::IsHandlingUserInput(), nullptr /* ContentParent */,
+ nullptr /* ObjectUpgradeHandler */, &rv);
+ }
+
+ if (NS_FAILED(rv)) {
+ MOZ_ASSERT(!promise);
+ mDocumentLoadListener = nullptr;
+ RemoveObserver();
+ return rv;
+ }
+
+ mListener = aListener;
+ if (mLoadGroup) {
+ mLoadGroup->AddRequest(this, nullptr);
+ }
+
+ RefPtr<ParentProcessDocumentChannel> self = this;
+ promise->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [self](DocumentLoadListener::OpenPromiseSucceededType&& aResolveValue) {
+ self->mDocumentLoadListener->CancelEarlyHintPreloads();
+ nsTArray<EarlyHintConnectArgs> earlyHints;
+
+ // The DLL is waiting for us to resolve the
+ // RedirectToRealChannelPromise given as parameter.
+ RefPtr<RedirectToRealChannelPromise> p =
+ self->RedirectToRealChannel(
+ std::move(aResolveValue.mStreamFilterEndpoints),
+ aResolveValue.mRedirectFlags, aResolveValue.mLoadFlags,
+ earlyHints)
+ ->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [self](RedirectToRealChannelPromise::ResolveOrRejectValue&&
+ aValue) -> RefPtr<RedirectToRealChannelPromise> {
+ MOZ_ASSERT(aValue.IsResolve());
+ nsresult rv = aValue.ResolveValue();
+ if (NS_FAILED(rv)) {
+ self->DisconnectChildListeners(rv, rv);
+ }
+ self->mLoadGroup = nullptr;
+ self->mListener = nullptr;
+ self->mCallbacks = nullptr;
+ self->RemoveObserver();
+ auto p =
+ MakeRefPtr<RedirectToRealChannelPromise::Private>(
+ __func__);
+ p->UseDirectTaskDispatch(__func__);
+ p->ResolveOrReject(std::move(aValue), __func__);
+ return p;
+ });
+ // We chain the promise the DLL is waiting on to the one returned by
+ // RedirectToRealChannel. As soon as the promise returned is
+ // resolved or rejected, so will the DLL's promise.
+ p->ChainTo(aResolveValue.mPromise.forget(), __func__);
+ },
+ [self](DocumentLoadListener::OpenPromiseFailedType&& aRejectValue) {
+ // If this is a normal failure, then we want to disconnect our listeners
+ // and notify them of the failure. If this is a process switch, then we
+ // can just ignore it silently, and trust that the switch will shut down
+ // our docshell and cancel us when it's ready.
+ if (!aRejectValue.mContinueNavigating) {
+ self->DisconnectChildListeners(aRejectValue.mStatus,
+ aRejectValue.mLoadGroupStatus);
+ }
+ self->RemoveObserver();
+ });
+ return NS_OK;
+}
+
+NS_IMETHODIMP ParentProcessDocumentChannel::Cancel(nsresult aStatus) {
+ return CancelWithReason(aStatus, "ParentProcessDocumentChannel::Cancel"_ns);
+}
+
+NS_IMETHODIMP ParentProcessDocumentChannel::CancelWithReason(
+ nsresult aStatusCode, const nsACString& aReason) {
+ LOG(("ParentProcessDocumentChannel CancelWithReason [this=%p]", this));
+ if (mCanceled) {
+ return NS_OK;
+ }
+
+ mCanceled = true;
+ // This will force the DocumentListener to abort the promise if there's one
+ // pending.
+ mDocumentLoadListener->Cancel(aStatusCode, aReason);
+
+ return NS_OK;
+}
+
+void ParentProcessDocumentChannel::RemoveObserver() {
+ if (nsCOMPtr<nsIObserverService> observerService =
+ mozilla::services::GetObserverService()) {
+ observerService->RemoveObserver(this, NS_HTTP_ON_MODIFY_REQUEST_TOPIC);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// nsIObserver
+////////////////////////////////////////////////////////////////////////////////
+
+NS_IMETHODIMP
+ParentProcessDocumentChannel::Observe(nsISupports* aSubject, const char* aTopic,
+ const char16_t* aData) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (mRequestObserversCalled) {
+ // We have already emitted the event, we don't want to emit it again.
+ // We only care about forwarding the first NS_HTTP_ON_MODIFY_REQUEST_TOPIC
+ // encountered.
+ return NS_OK;
+ }
+ nsCOMPtr<nsIChannel> channel = do_QueryInterface(aSubject);
+ if (!channel || mDocumentLoadListener->GetChannel() != channel) {
+ // Not a channel we are interested with.
+ return NS_OK;
+ }
+ LOG(("DocumentChannelParent Observe [this=%p aChannel=%p]", this,
+ channel.get()));
+ if (!nsCRT::strcmp(aTopic, NS_HTTP_ON_MODIFY_REQUEST_TOPIC)) {
+ mRequestObserversCalled = true;
+ gHttpHandler->OnModifyDocumentRequest(this);
+ }
+
+ return NS_OK;
+}
+
+} // namespace net
+} // namespace mozilla
+
+#undef LOG
diff --git a/netwerk/ipc/ParentProcessDocumentChannel.h b/netwerk/ipc/ParentProcessDocumentChannel.h
new file mode 100644
index 0000000000..f9b4ad616d
--- /dev/null
+++ b/netwerk/ipc/ParentProcessDocumentChannel.h
@@ -0,0 +1,62 @@
+/* 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_ParentProcessDocumentChannel_h
+#define mozilla_net_ParentProcessDocumentChannel_h
+
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/net/DocumentChannel.h"
+#include "mozilla/net/DocumentLoadListener.h"
+#include "nsIObserver.h"
+#include "nsIAsyncVerifyRedirectCallback.h"
+
+namespace mozilla {
+namespace net {
+
+class EarlyHintConnectArgs;
+
+class ParentProcessDocumentChannel : public DocumentChannel,
+ public nsIAsyncVerifyRedirectCallback,
+ public nsIObserver {
+ public:
+ ParentProcessDocumentChannel(nsDocShellLoadState* aLoadState,
+ class LoadInfo* aLoadInfo,
+ nsLoadFlags aLoadFlags, uint32_t aCacheKey,
+ bool aUriModified,
+ bool aIsEmbeddingBlockedError);
+
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_NSIASYNCVERIFYREDIRECTCALLBACK
+ NS_DECL_NSIOBSERVER
+
+ NS_IMETHOD AsyncOpen(nsIStreamListener* aListener) override;
+ NS_IMETHOD Cancel(nsresult aStatusCode) override;
+ NS_IMETHOD CancelWithReason(nsresult aStatusCode,
+ const nsACString& aReason) override;
+
+ RefPtr<PDocumentChannelParent::RedirectToRealChannelPromise>
+ RedirectToRealChannel(
+ nsTArray<ipc::Endpoint<extensions::PStreamFilterParent>>&&
+ aStreamFilterEndpoints,
+ uint32_t aRedirectFlags, uint32_t aLoadFlags,
+ const nsTArray<EarlyHintConnectArgs>& aEarlyHints);
+
+ private:
+ virtual ~ParentProcessDocumentChannel();
+ void RemoveObserver();
+
+ RefPtr<DocumentLoadListener> mDocumentLoadListener;
+ nsTArray<ipc::Endpoint<extensions::PStreamFilterParent>>
+ mStreamFilterEndpoints;
+ MozPromiseHolder<PDocumentChannelParent::RedirectToRealChannelPromise>
+ mPromise;
+ bool mRequestObserversCalled = false;
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozilla_net_ParentProcessDocumentChannel_h
diff --git a/netwerk/ipc/ProxyAutoConfigChild.cpp b/netwerk/ipc/ProxyAutoConfigChild.cpp
new file mode 100644
index 0000000000..1ab78c65e3
--- /dev/null
+++ b/netwerk/ipc/ProxyAutoConfigChild.cpp
@@ -0,0 +1,225 @@
+/* -*- 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;
+static StaticRefPtr<ProxyAutoConfigChild> sActor;
+
+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
+void ProxyAutoConfigChild::BindProxyAutoConfigChild(
+ RefPtr<ProxyAutoConfigChild>&& aActor,
+ Endpoint<PProxyAutoConfigChild>&& aEndpoint) {
+ // We only allow one ProxyAutoConfigChild at a time, so we need to
+ // wait until the old one to be destroyed.
+ if (sActor) {
+ NS_DispatchToCurrentThread(NS_NewRunnableFunction(
+ "BindProxyAutoConfigChild",
+ [actor = std::move(aActor), endpoint = std::move(aEndpoint)]() mutable {
+ ProxyAutoConfigChild::BindProxyAutoConfigChild(std::move(actor),
+ std::move(endpoint));
+ }));
+ return;
+ }
+
+ if (aEndpoint.Bind(aActor)) {
+ sActor = aActor;
+ }
+}
+
+// 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());
+ ProxyAutoConfigChild::BindProxyAutoConfigChild(std::move(actor),
+ std::move(endpoint));
+ })))) {
+ 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 !sActor; });
+
+ 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);
+}
+
+ProxyAutoConfigChild::~ProxyAutoConfigChild() = default;
+
+mozilla::ipc::IPCResult ProxyAutoConfigChild::RecvConfigurePAC(
+ const nsACString& aPACURI, const nsACString& aPACScriptData,
+ const bool& aIncludePath, const uint32_t& aExtraHeapSize) {
+ mPAC->ConfigurePAC(aPACURI, aPACScriptData, aIncludePath, aExtraHeapSize,
+ GetMainThreadSerialEventTarget());
+ mPACLoaded = true;
+ NS_DispatchToCurrentThread(
+ NewRunnableMethod("ProxyAutoConfigChild::ProcessPendingQ", this,
+ &ProxyAutoConfigChild::ProcessPendingQ));
+ return IPC_OK();
+}
+
+void ProxyAutoConfigChild::PendingQuery::Resolve(nsresult aStatus,
+ const nsACString& aResult) {
+ mResolver(std::tuple<const nsresult&, const nsACString&>(aStatus, aResult));
+}
+
+mozilla::ipc::IPCResult ProxyAutoConfigChild::RecvGetProxyForURI(
+ const nsACString& aTestURI, const nsACString& aTestHost,
+ GetProxyForURIResolver&& aResolver) {
+ mPendingQ.insertBack(
+ new PendingQuery(aTestURI, aTestHost, std::move(aResolver)));
+ ProcessPendingQ();
+ return IPC_OK();
+}
+
+void ProxyAutoConfigChild::ProcessPendingQ() {
+ while (ProcessPending()) {
+ ;
+ }
+
+ if (mShutdown) {
+ mPAC->Shutdown();
+ } else {
+ // do GC while the thread has nothing pending
+ mPAC->GC();
+ }
+}
+
+bool ProxyAutoConfigChild::ProcessPending() {
+ if (mPendingQ.isEmpty()) {
+ return false;
+ }
+
+ if (mInProgress || !mPACLoaded) {
+ return false;
+ }
+
+ if (mShutdown) {
+ return true;
+ }
+
+ mInProgress = true;
+ RefPtr<PendingQuery> query = mPendingQ.popFirst();
+ nsCString result;
+ nsresult rv = mPAC->GetProxyForURI(query->URI(), query->Host(), result);
+ query->Resolve(rv, result);
+ mInProgress = false;
+ return true;
+}
+
+void ProxyAutoConfigChild::ActorDestroy(ActorDestroyReason aWhy) {
+ mPendingQ.clear();
+ mShutdown = true;
+ mPAC->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() { sActor = nullptr; }
+
+} // namespace mozilla::net
diff --git a/netwerk/ipc/ProxyAutoConfigChild.h b/netwerk/ipc/ProxyAutoConfigChild.h
new file mode 100644
index 0000000000..19d757d646
--- /dev/null
+++ b/netwerk/ipc/ProxyAutoConfigChild.h
@@ -0,0 +1,84 @@
+/* -*- 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/LinkedList.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, final)
+
+ static bool Create(Endpoint<PProxyAutoConfigChild>&& aEndpoint);
+ static bool CreatePACThread();
+ static void ShutdownPACThread();
+
+ ProxyAutoConfigChild();
+
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+ mozilla::ipc::IPCResult RecvConfigurePAC(const nsACString& aPACURI,
+ const nsACString& aPACScriptData,
+ const bool& aIncludePath,
+ const uint32_t& aExtraHeapSize);
+ mozilla::ipc::IPCResult RecvGetProxyForURI(
+ const nsACString& aTestURI, const nsACString& aTestHost,
+ GetProxyForURIResolver&& aResolver);
+
+ void Destroy();
+
+ private:
+ virtual ~ProxyAutoConfigChild();
+ void ProcessPendingQ();
+ bool ProcessPending();
+ static void BindProxyAutoConfigChild(
+ RefPtr<ProxyAutoConfigChild>&& aActor,
+ Endpoint<PProxyAutoConfigChild>&& aEndpoint);
+
+ UniquePtr<ProxyAutoConfig> mPAC;
+ bool mInProgress{false};
+ bool mPACLoaded{false};
+ bool mShutdown{false};
+
+ class PendingQuery final : public LinkedListElement<RefPtr<PendingQuery>> {
+ public:
+ NS_INLINE_DECL_REFCOUNTING(PendingQuery)
+
+ explicit PendingQuery(const nsACString& aTestURI,
+ const nsACString& aTestHost,
+ GetProxyForURIResolver&& aResolver)
+ : mURI(aTestURI), mHost(aTestHost), mResolver(std::move(aResolver)) {}
+
+ void Resolve(nsresult aStatus, const nsACString& aResult);
+ const nsCString& URI() const { return mURI; }
+ const nsCString& Host() const { return mHost; }
+
+ private:
+ ~PendingQuery() = default;
+
+ nsCString mURI;
+ nsCString mHost;
+ GetProxyForURIResolver mResolver;
+ };
+
+ LinkedList<RefPtr<PendingQuery>> mPendingQ;
+
+ static StaticRefPtr<nsIThread> sPACThread;
+ static bool sShutdownObserverRegistered;
+ static Atomic<uint32_t> sLiveActorCount;
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // ProxyAutoConfigChild_h__
diff --git a/netwerk/ipc/ProxyAutoConfigParent.cpp b/netwerk/ipc/ProxyAutoConfigParent.cpp
new file mode 100644
index 0000000000..d0a8727ef1
--- /dev/null
+++ b/netwerk/ipc/ProxyAutoConfigParent.cpp
@@ -0,0 +1,21 @@
+/* -*- 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"
+
+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
diff --git a/netwerk/ipc/ProxyAutoConfigParent.h b/netwerk/ipc/ProxyAutoConfigParent.h
new file mode 100644
index 0000000000..f65a0714f2
--- /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, final)
+
+ ProxyAutoConfigParent();
+ void Init(Endpoint<PProxyAutoConfigParent>&& aEndpoint);
+
+ private:
+ virtual ~ProxyAutoConfigParent();
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // ProxyAutoConfigParent_h__
diff --git a/netwerk/ipc/ProxyConfigLookup.cpp b/netwerk/ipc/ProxyConfigLookup.cpp
new file mode 100644
index 0000000000..29ce74091d
--- /dev/null
+++ b/netwerk/ipc/ProxyConfigLookup.cpp
@@ -0,0 +1,99 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ProxyConfigLookup.h"
+#include "ProxyConfigLookupChild.h"
+#include "mozilla/Unused.h"
+#include "nsContentUtils.h"
+#include "nsICancelable.h"
+#include "nsIProtocolProxyService.h"
+#include "nsIProtocolProxyService2.h"
+#include "nsNetUtil.h"
+#include "nsThreadUtils.h"
+#include "nsIChannel.h"
+
+namespace mozilla {
+namespace net {
+
+// static
+nsresult ProxyConfigLookup::Create(
+ std::function<void(nsIProxyInfo*, nsresult)>&& aCallback, nsIURI* aURI,
+ uint32_t aProxyResolveFlags, nsICancelable** aLookupCancellable) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ RefPtr<ProxyConfigLookup> lookUp =
+ new ProxyConfigLookup(std::move(aCallback), aURI, aProxyResolveFlags);
+ return lookUp->DoProxyResolve(aLookupCancellable);
+}
+
+ProxyConfigLookup::ProxyConfigLookup(
+ std::function<void(nsIProxyInfo*, nsresult)>&& aCallback, nsIURI* aURI,
+ uint32_t aProxyResolveFlags)
+ : mCallback(std::move(aCallback)),
+ mURI(aURI),
+ mProxyResolveFlags(aProxyResolveFlags) {}
+
+ProxyConfigLookup::~ProxyConfigLookup() = default;
+
+nsresult ProxyConfigLookup::DoProxyResolve(nsICancelable** aLookupCancellable) {
+ if (!XRE_IsParentProcess()) {
+ RefPtr<ProxyConfigLookup> self = this;
+ bool result = ProxyConfigLookupChild::Create(
+ mURI, mProxyResolveFlags,
+ [self](nsIProxyInfo* aProxyinfo, nsresult aResult) {
+ self->OnProxyAvailable(nullptr, nullptr, aProxyinfo, aResult);
+ });
+ return result ? NS_OK : NS_ERROR_FAILURE;
+ }
+
+ nsresult rv;
+ nsCOMPtr<nsIChannel> channel;
+ rv = NS_NewChannel(getter_AddRefs(channel), mURI,
+ nsContentUtils::GetSystemPrincipal(),
+ nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
+ nsIContentPolicy::TYPE_OTHER);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ nsCOMPtr<nsIProtocolProxyService> pps =
+ do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ // using the nsIProtocolProxyService2 allows a minor performance
+ // optimization, but if an add-on has only provided the original interface
+ // then it is ok to use that version.
+ nsCOMPtr<nsICancelable> proxyRequest;
+ nsCOMPtr<nsIProtocolProxyService2> pps2 = do_QueryInterface(pps);
+ if (pps2) {
+ rv = pps2->AsyncResolve2(channel, mProxyResolveFlags, this, nullptr,
+ getter_AddRefs(proxyRequest));
+ } else {
+ rv = pps->AsyncResolve(channel, mProxyResolveFlags, this, nullptr,
+ getter_AddRefs(proxyRequest));
+ }
+
+ if (aLookupCancellable) {
+ proxyRequest.forget(aLookupCancellable);
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP ProxyConfigLookup::OnProxyAvailable(nsICancelable* aRequest,
+ nsIChannel* aChannel,
+ nsIProxyInfo* aProxyinfo,
+ nsresult aResult) {
+ mCallback(aProxyinfo, aResult);
+ return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS(ProxyConfigLookup, nsIProtocolProxyCallback)
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/ipc/ProxyConfigLookup.h b/netwerk/ipc/ProxyConfigLookup.h
new file mode 100644
index 0000000000..edef151964
--- /dev/null
+++ b/netwerk/ipc/ProxyConfigLookup.h
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_net_ProxyConfigLookup_h
+#define mozilla_net_ProxyConfigLookup_h
+
+#include <functional>
+#include "nsIProtocolProxyCallback.h"
+#include "nsCOMPtr.h"
+
+class nsIURI;
+
+namespace mozilla {
+namespace net {
+
+class ProxyConfigLookup final : public nsIProtocolProxyCallback {
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIPROTOCOLPROXYCALLBACK
+
+ static nsresult Create(
+ std::function<void(nsIProxyInfo*, nsresult)>&& aCallback, nsIURI* aURI,
+ uint32_t aProxyResolveFlags,
+ nsICancelable** aLookupCancellable = nullptr);
+
+ private:
+ explicit ProxyConfigLookup(
+ std::function<void(nsIProxyInfo*, nsresult)>&& aCallback, nsIURI* aURI,
+ uint32_t aProxyResolveFlags);
+ virtual ~ProxyConfigLookup();
+ nsresult DoProxyResolve(nsICancelable** aLookupCancellable);
+
+ std::function<void(nsIProxyInfo*, nsresult)> mCallback;
+ nsCOMPtr<nsIURI> mURI;
+ uint32_t mProxyResolveFlags;
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozilla_net_ProxyConfigLookup_h
diff --git a/netwerk/ipc/ProxyConfigLookupChild.cpp b/netwerk/ipc/ProxyConfigLookupChild.cpp
new file mode 100644
index 0000000000..913b8460c2
--- /dev/null
+++ b/netwerk/ipc/ProxyConfigLookupChild.cpp
@@ -0,0 +1,43 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ProxyConfigLookupChild.h"
+
+#include "mozilla/net/SocketProcessChild.h"
+#include "nsProxyInfo.h"
+
+namespace mozilla {
+namespace net {
+
+// static
+bool ProxyConfigLookupChild::Create(
+ nsIURI* aURI, uint32_t aProxyResolveFlags,
+ std::function<void(nsIProxyInfo*, nsresult)>&& aCallback) {
+ SocketProcessChild* socketChild = SocketProcessChild::GetSingleton();
+ if (!socketChild) {
+ return false;
+ }
+
+ RefPtr<ProxyConfigLookupChild> child =
+ new ProxyConfigLookupChild(std::move(aCallback));
+ return socketChild->SendPProxyConfigLookupConstructor(child, aURI,
+ aProxyResolveFlags);
+}
+
+ProxyConfigLookupChild::ProxyConfigLookupChild(
+ std::function<void(nsIProxyInfo*, nsresult)>&& aCallback)
+ : mCallback(std::move(aCallback)) {}
+
+mozilla::ipc::IPCResult ProxyConfigLookupChild::Recv__delete__(
+ nsTArray<ProxyInfoCloneArgs>&& aProxyInfo, const nsresult& aResult) {
+ nsCOMPtr<nsIProxyInfo> proxyInfo =
+ nsProxyInfo::DeserializeProxyInfo(aProxyInfo);
+ mCallback(proxyInfo, aResult);
+ return IPC_OK();
+}
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/ipc/ProxyConfigLookupChild.h b/netwerk/ipc/ProxyConfigLookupChild.h
new file mode 100644
index 0000000000..0c64f2835e
--- /dev/null
+++ b/netwerk/ipc/ProxyConfigLookupChild.h
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_net_ProxyConfigLookupChild_h
+#define mozilla_net_ProxyConfigLookupChild_h
+
+#include "mozilla/net/PProxyConfigLookupChild.h"
+#include <functional>
+
+class nsIProxyInfo;
+
+namespace mozilla {
+namespace net {
+
+class ProxyConfigLookupChild final : public PProxyConfigLookupChild {
+ public:
+ NS_INLINE_DECL_REFCOUNTING(ProxyConfigLookupChild, override)
+
+ static bool Create(nsIURI* aURI, uint32_t aProxyResolveFlags,
+ std::function<void(nsIProxyInfo*, nsresult)>&& aCallback);
+
+ mozilla::ipc::IPCResult Recv__delete__(
+ nsTArray<ProxyInfoCloneArgs>&& aProxyInfo, const nsresult& aResult);
+
+ private:
+ explicit ProxyConfigLookupChild(
+ std::function<void(nsIProxyInfo*, nsresult)>&& aCallback);
+ virtual ~ProxyConfigLookupChild() = default;
+
+ std::function<void(nsIProxyInfo*, nsresult)> mCallback;
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozilla_net_ProxyConfigLookupChild_h
diff --git a/netwerk/ipc/ProxyConfigLookupParent.cpp b/netwerk/ipc/ProxyConfigLookupParent.cpp
new file mode 100644
index 0000000000..597f43eef5
--- /dev/null
+++ b/netwerk/ipc/ProxyConfigLookupParent.cpp
@@ -0,0 +1,43 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ProxyConfigLookupParent.h"
+#include "ProxyConfigLookup.h"
+#include "mozilla/Unused.h"
+#include "nsProxyInfo.h"
+
+namespace mozilla {
+namespace net {
+
+ProxyConfigLookupParent::ProxyConfigLookupParent(nsIURI* aURI,
+ uint32_t aProxyResolveFlags)
+ : mURI(aURI), mProxyResolveFlags(aProxyResolveFlags) {}
+
+ProxyConfigLookupParent::~ProxyConfigLookupParent() = default;
+
+void ProxyConfigLookupParent::DoProxyLookup() {
+ RefPtr<ProxyConfigLookupParent> self = this;
+ nsresult rv = ProxyConfigLookup::Create(
+ [self](nsIProxyInfo* aProxyInfo, nsresult aStatus) {
+ if (self->CanSend()) {
+ nsTArray<ProxyInfoCloneArgs> proxyInfoArray;
+ if (aProxyInfo && NS_SUCCEEDED(aStatus)) {
+ nsProxyInfo::SerializeProxyInfo(
+ static_cast<nsProxyInfo*>(aProxyInfo), proxyInfoArray);
+ }
+ Unused << Send__delete__(self, proxyInfoArray, aStatus);
+ }
+ },
+ mURI, mProxyResolveFlags);
+
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ nsTArray<ProxyInfoCloneArgs> emptyArray;
+ Unused << Send__delete__(self, emptyArray, rv);
+ }
+}
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/ipc/ProxyConfigLookupParent.h b/netwerk/ipc/ProxyConfigLookupParent.h
new file mode 100644
index 0000000000..6ce23866c2
--- /dev/null
+++ b/netwerk/ipc/ProxyConfigLookupParent.h
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_net_ProxyConfigLookupParent_h
+#define mozilla_net_ProxyConfigLookupParent_h
+
+#include "mozilla/net/PProxyConfigLookupParent.h"
+
+class nsIURI;
+
+namespace mozilla {
+namespace net {
+
+class ProxyConfigLookupParent final : public PProxyConfigLookupParent {
+ public:
+ NS_INLINE_DECL_REFCOUNTING(ProxyConfigLookupParent, override)
+
+ explicit ProxyConfigLookupParent(nsIURI* aURI, uint32_t aProxyResolveFlags);
+
+ void DoProxyLookup();
+
+ private:
+ virtual ~ProxyConfigLookupParent();
+
+ nsCOMPtr<nsIURI> mURI;
+ uint32_t mProxyResolveFlags;
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozilla_net_ProxyConfigLookupParent_h
diff --git a/netwerk/ipc/SocketProcessBackgroundChild.cpp b/netwerk/ipc/SocketProcessBackgroundChild.cpp
new file mode 100644
index 0000000000..d3835ad0f5
--- /dev/null
+++ b/netwerk/ipc/SocketProcessBackgroundChild.cpp
@@ -0,0 +1,113 @@
+/* -*- 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 "SocketProcessBackgroundChild.h"
+#include "SocketProcessLogging.h"
+
+#include "mozilla/ipc/Endpoint.h"
+#include "nsThreadUtils.h"
+
+namespace mozilla::net {
+
+StaticMutex SocketProcessBackgroundChild::sMutex;
+StaticRefPtr<SocketProcessBackgroundChild>
+ SocketProcessBackgroundChild::sInstance;
+StaticRefPtr<nsISerialEventTarget> SocketProcessBackgroundChild::sTaskQueue;
+
+// static
+RefPtr<SocketProcessBackgroundChild>
+SocketProcessBackgroundChild::GetSingleton() {
+ StaticMutexAutoLock lock(sMutex);
+ return sInstance;
+}
+
+// static
+void SocketProcessBackgroundChild::Create(
+ ipc::Endpoint<PSocketProcessBackgroundChild>&& aEndpoint) {
+ if (NS_WARN_IF(!aEndpoint.IsValid())) {
+ MOZ_ASSERT_UNREACHABLE(
+ "Can't create SocketProcessBackgroundChild with invalid endpoint");
+ return;
+ }
+
+ nsCOMPtr<nsISerialEventTarget> transportQueue;
+ if (NS_WARN_IF(NS_FAILED(NS_CreateBackgroundTaskQueue(
+ "SocketBackgroundChildQueue", getter_AddRefs(transportQueue))))) {
+ return;
+ }
+
+ RefPtr<SocketProcessBackgroundChild> actor =
+ new SocketProcessBackgroundChild();
+
+ transportQueue->Dispatch(NS_NewRunnableFunction(
+ "BindSocketBackgroundChild",
+ [endpoint = std::move(aEndpoint), actor]() mutable {
+ // We checked endpoint validity before the dispatch, so this cannot
+ // fail.
+ MOZ_ALWAYS_TRUE(endpoint.Bind(actor));
+ }));
+
+ // Immediately store the actor and queue into the global.
+ // Any messages dispatched to the queue will arrive after it has been bound.
+ LOG(("SocketProcessBackgroundChild::Create"));
+ StaticMutexAutoLock lock(sMutex);
+ MOZ_ASSERT(!sInstance && !sTaskQueue,
+ "Cannot initialize SocketProcessBackgroundChild twice!");
+ sInstance = actor;
+ sTaskQueue = transportQueue;
+}
+
+// static
+void SocketProcessBackgroundChild::Shutdown() {
+ nsCOMPtr<nsISerialEventTarget> taskQueue = TaskQueue();
+ if (!taskQueue) {
+ return;
+ }
+
+ taskQueue->Dispatch(
+ NS_NewRunnableFunction("SocketProcessBackgroundChild::Shutdown", []() {
+ LOG(("SocketProcessBackgroundChild::Shutdown"));
+ StaticMutexAutoLock lock(sMutex);
+ sInstance->Close();
+ sInstance = nullptr;
+ sTaskQueue = nullptr;
+ }));
+}
+
+// static
+already_AddRefed<nsISerialEventTarget>
+SocketProcessBackgroundChild::TaskQueue() {
+ StaticMutexAutoLock lock(sMutex);
+ return do_AddRef(sTaskQueue);
+}
+
+// static
+nsresult SocketProcessBackgroundChild::WithActor(
+ const char* aName,
+ MoveOnlyFunction<void(SocketProcessBackgroundChild*)> aCallback) {
+ nsCOMPtr<nsISerialEventTarget> taskQueue = TaskQueue();
+ if (!taskQueue) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ return taskQueue->Dispatch(NS_NewRunnableFunction(
+ aName, [callback = std::move(aCallback)]() mutable {
+ RefPtr<SocketProcessBackgroundChild> actor =
+ SocketProcessBackgroundChild::GetSingleton();
+ if (actor) {
+ callback(actor);
+ }
+ }));
+}
+
+SocketProcessBackgroundChild::SocketProcessBackgroundChild() {
+ LOG(("SocketProcessBackgroundChild ctor"));
+}
+
+SocketProcessBackgroundChild::~SocketProcessBackgroundChild() {
+ LOG(("SocketProcessBackgroundChild dtor"));
+}
+
+} // namespace mozilla::net
diff --git a/netwerk/ipc/SocketProcessBackgroundChild.h b/netwerk/ipc/SocketProcessBackgroundChild.h
new file mode 100644
index 0000000000..b83c8dbcc1
--- /dev/null
+++ b/netwerk/ipc/SocketProcessBackgroundChild.h
@@ -0,0 +1,45 @@
+/* -*- 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_SocketProcessBackgroundChild_h
+#define mozilla_net_SocketProcessBackgroundChild_h
+
+#include "mozilla/MoveOnlyFunction.h"
+#include "mozilla/net/PSocketProcessBackgroundChild.h"
+#include "mozilla/StaticMutex.h"
+#include "mozilla/StaticPtr.h"
+
+namespace mozilla {
+namespace net {
+
+class SocketProcessBackgroundChild final
+ : public PSocketProcessBackgroundChild {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SocketProcessBackgroundChild, final)
+
+ static void Create(ipc::Endpoint<PSocketProcessBackgroundChild>&& aEndpoint);
+ static void Shutdown();
+ // |aCallback| will be called with the actor asynchronously on |sTaskQueue|.
+ static nsresult WithActor(
+ const char* aName,
+ MoveOnlyFunction<void(SocketProcessBackgroundChild*)> aCallback);
+
+ private:
+ SocketProcessBackgroundChild();
+ virtual ~SocketProcessBackgroundChild();
+
+ static RefPtr<SocketProcessBackgroundChild> GetSingleton();
+ static already_AddRefed<nsISerialEventTarget> TaskQueue();
+
+ static StaticMutex sMutex;
+ static StaticRefPtr<SocketProcessBackgroundChild> sInstance
+ MOZ_GUARDED_BY(sMutex);
+ static StaticRefPtr<nsISerialEventTarget> sTaskQueue MOZ_GUARDED_BY(sMutex);
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozilla_net_SocketProcessBackgroundChild_h
diff --git a/netwerk/ipc/SocketProcessBackgroundParent.cpp b/netwerk/ipc/SocketProcessBackgroundParent.cpp
new file mode 100644
index 0000000000..c17e1467b6
--- /dev/null
+++ b/netwerk/ipc/SocketProcessBackgroundParent.cpp
@@ -0,0 +1,169 @@
+/* -*- 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 "SocketProcessBackgroundParent.h"
+#include "SocketProcessLogging.h"
+
+#include "mozilla/ipc/Endpoint.h"
+#include "mozilla/net/HttpConnectionMgrParent.h"
+#include "mozilla/net/WebSocketConnectionParent.h"
+#include "mozilla/psm/IPCClientCertsParent.h"
+#include "mozilla/psm/VerifySSLServerCertParent.h"
+#include "mozilla/psm/SelectTLSClientAuthCertParent.h"
+#include "nsIHttpChannelInternal.h"
+
+namespace mozilla::net {
+
+SocketProcessBackgroundParent::SocketProcessBackgroundParent() {
+ LOG(("SocketProcessBackgroundParent ctor"));
+}
+
+SocketProcessBackgroundParent::~SocketProcessBackgroundParent() {
+ LOG(("SocketProcessBackgroundParent dtor"));
+}
+
+mozilla::ipc::IPCResult
+SocketProcessBackgroundParent::RecvInitVerifySSLServerCert(
+ Endpoint<PVerifySSLServerCertParent>&& aEndpoint,
+ nsTArray<ByteArray>&& aPeerCertChain, const nsACString& aHostName,
+ const int32_t& aPort, const OriginAttributes& aOriginAttributes,
+ const Maybe<ByteArray>& aStapledOCSPResponse,
+ const Maybe<ByteArray>& aSctsFromTLSExtension,
+ const Maybe<DelegatedCredentialInfoArg>& aDcInfo,
+ const uint32_t& aProviderFlags, const uint32_t& aCertVerifierFlags) {
+ LOG(("SocketProcessBackgroundParent::RecvInitVerifySSLServerCert\n"));
+ if (!aEndpoint.IsValid()) {
+ return IPC_FAIL(this, "Invalid endpoint");
+ }
+
+ nsCOMPtr<nsISerialEventTarget> transportQueue;
+ if (NS_FAILED(NS_CreateBackgroundTaskQueue("VerifySSLServerCert",
+ getter_AddRefs(transportQueue)))) {
+ return IPC_FAIL(this, "NS_CreateBackgroundTaskQueue failed");
+ }
+
+ transportQueue->Dispatch(NS_NewRunnableFunction(
+ "InitVerifySSLServerCert",
+ [endpoint = std::move(aEndpoint),
+ peerCertChain = std::move(aPeerCertChain),
+ hostName = nsCString(aHostName), port(aPort),
+ originAttributes(aOriginAttributes),
+ stapledOCSPResponse = std::move(aStapledOCSPResponse),
+ sctsFromTLSExtension = std::move(aSctsFromTLSExtension),
+ dcInfo = std::move(aDcInfo), providerFlags(aProviderFlags),
+ certVerifierFlags(aCertVerifierFlags)]() mutable {
+ RefPtr<psm::VerifySSLServerCertParent> parent =
+ new psm::VerifySSLServerCertParent();
+ if (!endpoint.Bind(parent)) {
+ return;
+ }
+
+ parent->Dispatch(std::move(peerCertChain), hostName, port,
+ originAttributes, stapledOCSPResponse,
+ sctsFromTLSExtension, dcInfo, providerFlags,
+ certVerifierFlags);
+ }));
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+SocketProcessBackgroundParent::RecvInitSelectTLSClientAuthCert(
+ Endpoint<PSelectTLSClientAuthCertParent>&& aEndpoint,
+ const nsACString& aHostName, const OriginAttributes& aOriginAttributes,
+ const int32_t& aPort, const uint32_t& aProviderFlags,
+ const uint32_t& aProviderTlsFlags, const ByteArray& aServerCertBytes,
+ nsTArray<ByteArray>&& aCANames, const uint64_t& aBrowserId) {
+ LOG(("SocketProcessBackgroundParent::RecvInitSelectTLSClientAuthCert\n"));
+ if (!aEndpoint.IsValid()) {
+ return IPC_FAIL(this, "Invalid endpoint");
+ }
+
+ nsCOMPtr<nsISerialEventTarget> transportQueue;
+ if (NS_FAILED(NS_CreateBackgroundTaskQueue("SelectTLSClientAuthCert",
+ getter_AddRefs(transportQueue)))) {
+ return IPC_FAIL(this, "NS_CreateBackgroundTaskQueue failed");
+ }
+
+ transportQueue->Dispatch(NS_NewRunnableFunction(
+ "InitSelectTLSClientAuthCert",
+ [endpoint = std::move(aEndpoint), hostName = nsCString(aHostName),
+ originAttributes(aOriginAttributes), port(aPort),
+ providerFlags(aProviderFlags), providerTlsFlags(aProviderTlsFlags),
+ serverCertBytes(aServerCertBytes), CANAMEs(std::move(aCANames)),
+ browserId(aBrowserId)]() mutable {
+ RefPtr<psm::SelectTLSClientAuthCertParent> parent =
+ new psm::SelectTLSClientAuthCertParent();
+ if (!endpoint.Bind(parent)) {
+ return;
+ }
+
+ parent->Dispatch(hostName, originAttributes, port, providerFlags,
+ providerTlsFlags, serverCertBytes, std::move(CANAMEs),
+ browserId);
+ }));
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult SocketProcessBackgroundParent::RecvInitIPCClientCerts(
+ Endpoint<PIPCClientCertsParent>&& aEndpoint) {
+ LOG(("SocketProcessBackgroundParent::RecvInitIPCClientCerts\n"));
+ if (!aEndpoint.IsValid()) {
+ return IPC_FAIL(this, "Invalid endpoint");
+ }
+
+ nsCOMPtr<nsISerialEventTarget> transportQueue;
+ if (NS_FAILED(NS_CreateBackgroundTaskQueue("IPCClientCerts",
+ getter_AddRefs(transportQueue)))) {
+ return IPC_FAIL(this, "NS_CreateBackgroundTaskQueue failed");
+ }
+
+ transportQueue->Dispatch(NS_NewRunnableFunction(
+ "InitIPCClientCerts", [endpoint = std::move(aEndpoint)]() mutable {
+ RefPtr<psm::IPCClientCertsParent> parent =
+ new psm::IPCClientCertsParent();
+ endpoint.Bind(parent);
+ }));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+SocketProcessBackgroundParent::RecvInitWebSocketConnection(
+ Endpoint<PWebSocketConnectionParent>&& aEndpoint,
+ const uint32_t& aListenerId) {
+ LOG(("SocketProcessBackgroundParent::RecvInitWebSocketConnection\n"));
+ if (!aEndpoint.IsValid()) {
+ return IPC_FAIL(this, "Invalid endpoint");
+ }
+
+ nsCOMPtr<nsISerialEventTarget> transportQueue;
+ if (NS_FAILED(NS_CreateBackgroundTaskQueue("WebSocketConnection",
+ getter_AddRefs(transportQueue)))) {
+ return IPC_FAIL(this, "NS_CreateBackgroundTaskQueue failed");
+ }
+
+ transportQueue->Dispatch(NS_NewRunnableFunction(
+ "InitWebSocketConnection",
+ [endpoint = std::move(aEndpoint), aListenerId]() mutable {
+ Maybe<nsCOMPtr<nsIHttpUpgradeListener>> listener =
+ net::HttpConnectionMgrParent::GetAndRemoveHttpUpgradeListener(
+ aListenerId);
+ if (!listener) {
+ return;
+ }
+
+ RefPtr<WebSocketConnectionParent> actor =
+ new WebSocketConnectionParent(*listener);
+ endpoint.Bind(actor);
+ }));
+ return IPC_OK();
+}
+
+void SocketProcessBackgroundParent::ActorDestroy(ActorDestroyReason aReason) {
+ LOG(("SocketProcessBackgroundParent::ActorDestroy"));
+}
+
+} // namespace mozilla::net
diff --git a/netwerk/ipc/SocketProcessBackgroundParent.h b/netwerk/ipc/SocketProcessBackgroundParent.h
new file mode 100644
index 0000000000..0714f8dd6e
--- /dev/null
+++ b/netwerk/ipc/SocketProcessBackgroundParent.h
@@ -0,0 +1,53 @@
+/* -*- 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_SocketProcessBackgroundParent_h
+#define mozilla_net_SocketProcessBackgroundParent_h
+
+#include "mozilla/net/PSocketProcessBackgroundParent.h"
+
+namespace mozilla {
+namespace net {
+
+class SocketProcessBackgroundParent final
+ : public PSocketProcessBackgroundParent {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SocketProcessBackgroundParent, final)
+
+ SocketProcessBackgroundParent();
+
+ mozilla::ipc::IPCResult RecvInitVerifySSLServerCert(
+ Endpoint<PVerifySSLServerCertParent>&& aEndpoint,
+ nsTArray<ByteArray>&& aPeerCertChain, const nsACString& aHostName,
+ const int32_t& aPort, const OriginAttributes& aOriginAttributes,
+ const Maybe<ByteArray>& aStapledOCSPResponse,
+ const Maybe<ByteArray>& aSctsFromTLSExtension,
+ const Maybe<DelegatedCredentialInfoArg>& aDcInfo,
+ const uint32_t& aProviderFlags, const uint32_t& aCertVerifierFlags);
+
+ mozilla::ipc::IPCResult RecvInitIPCClientCerts(
+ Endpoint<PIPCClientCertsParent>&& aEndpoint);
+
+ mozilla::ipc::IPCResult RecvInitSelectTLSClientAuthCert(
+ Endpoint<PSelectTLSClientAuthCertParent>&& aEndpoint,
+ const nsACString& aHostName, const OriginAttributes& aOriginAttributes,
+ const int32_t& aPort, const uint32_t& aProviderFlags,
+ const uint32_t& aProviderTlsFlags, const ByteArray& aServerCertBytes,
+ nsTArray<ByteArray>&& aCANames, const uint64_t& aBrowserId);
+
+ mozilla::ipc::IPCResult RecvInitWebSocketConnection(
+ Endpoint<PWebSocketConnectionParent>&& aEndpoint,
+ const uint32_t& aListenerId);
+
+ void ActorDestroy(ActorDestroyReason aReason) override;
+
+ private:
+ ~SocketProcessBackgroundParent();
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozilla_net_SocketProcessBackgroundParent_h
diff --git a/netwerk/ipc/SocketProcessBridgeChild.cpp b/netwerk/ipc/SocketProcessBridgeChild.cpp
new file mode 100644
index 0000000000..798ce37bf1
--- /dev/null
+++ b/netwerk/ipc/SocketProcessBridgeChild.cpp
@@ -0,0 +1,186 @@
+/* -*- 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 "SocketProcessBridgeChild.h"
+#include "SocketProcessLogging.h"
+
+#include "mozilla/AppShutdown.h"
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/ipc/BackgroundChild.h"
+#include "mozilla/ipc/Endpoint.h"
+#include "mozilla/net/NeckoChild.h"
+#include "nsIObserverService.h"
+#include "nsThreadUtils.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/StaticPrefs_network.h"
+
+namespace mozilla {
+
+using dom::ContentChild;
+
+namespace net {
+
+StaticRefPtr<SocketProcessBridgeChild>
+ SocketProcessBridgeChild::sSocketProcessBridgeChild;
+
+NS_IMPL_ISUPPORTS(SocketProcessBridgeChild, nsIObserver)
+
+// static
+bool SocketProcessBridgeChild::Create(
+ Endpoint<PSocketProcessBridgeChild>&& aEndpoint) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ sSocketProcessBridgeChild = new SocketProcessBridgeChild();
+
+ if (!aEndpoint.Bind(sSocketProcessBridgeChild)) {
+ MOZ_ASSERT(false, "Bind failed!");
+ sSocketProcessBridgeChild = nullptr;
+ return false;
+ }
+
+ nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
+ if (os) {
+ os->AddObserver(sSocketProcessBridgeChild, "content-child-shutdown", false);
+ }
+
+ sSocketProcessBridgeChild->mSocketProcessPid = aEndpoint.OtherPid();
+ return true;
+}
+
+// static
+already_AddRefed<SocketProcessBridgeChild>
+SocketProcessBridgeChild::GetSingleton() {
+ RefPtr<SocketProcessBridgeChild> child = sSocketProcessBridgeChild;
+ return child.forget();
+}
+
+// static
+RefPtr<SocketProcessBridgeChild::GetPromise>
+SocketProcessBridgeChild::GetSocketProcessBridge() {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (!StaticPrefs::network_process_enabled()) {
+ return GetPromise::CreateAndReject(nsCString("Socket process disabled!"),
+ __func__);
+ }
+
+ if (!gNeckoChild) {
+ return GetPromise::CreateAndReject(nsCString("No NeckoChild!"), __func__);
+ }
+
+ // ContentChild is shutting down, we should not try to create
+ // SocketProcessBridgeChild.
+ ContentChild* content = ContentChild::GetSingleton();
+ if (!content || content->IsShuttingDown()) {
+ return GetPromise::CreateAndReject(
+ nsCString("ContentChild is shutting down."), __func__);
+ }
+
+ if (sSocketProcessBridgeChild) {
+ return GetPromise::CreateAndResolve(sSocketProcessBridgeChild, __func__);
+ }
+
+ return gNeckoChild->SendInitSocketProcessBridge()->Then(
+ GetMainThreadSerialEventTarget(), __func__,
+ [](NeckoChild::InitSocketProcessBridgePromise::ResolveOrRejectValue&&
+ aResult) {
+ ContentChild* content = ContentChild::GetSingleton();
+ if (!content || content->IsShuttingDown()) {
+ return GetPromise::CreateAndReject(
+ nsCString("ContentChild is shutting down."), __func__);
+ }
+ if (!sSocketProcessBridgeChild) {
+ if (aResult.IsReject()) {
+ return GetPromise::CreateAndReject(
+ nsCString("SendInitSocketProcessBridge failed"), __func__);
+ }
+
+ if (!aResult.ResolveValue().IsValid()) {
+ return GetPromise::CreateAndReject(
+ nsCString(
+ "SendInitSocketProcessBridge resolved with an invalid "
+ "endpoint!"),
+ __func__);
+ }
+
+ if (!SocketProcessBridgeChild::Create(
+ std::move(aResult.ResolveValue()))) {
+ return GetPromise::CreateAndReject(
+ nsCString("SendInitSocketProcessBridge resolved with a valid "
+ "endpoint, "
+ "but SocketProcessBridgeChild::Create failed!"),
+ __func__);
+ }
+ }
+
+ return GetPromise::CreateAndResolve(sSocketProcessBridgeChild,
+ __func__);
+ });
+}
+
+SocketProcessBridgeChild::SocketProcessBridgeChild() : mShuttingDown(false) {
+ LOG(("CONSTRUCT SocketProcessBridgeChild::SocketProcessBridgeChild\n"));
+}
+
+SocketProcessBridgeChild::~SocketProcessBridgeChild() {
+ LOG(("DESTRUCT SocketProcessBridgeChild::SocketProcessBridgeChild\n"));
+}
+
+mozilla::ipc::IPCResult SocketProcessBridgeChild::RecvTest() {
+ LOG(("SocketProcessBridgeChild::RecvTest\n"));
+ return IPC_OK();
+}
+
+void SocketProcessBridgeChild::ActorDestroy(ActorDestroyReason aWhy) {
+ LOG(("SocketProcessBridgeChild::ActorDestroy\n"));
+ if (AbnormalShutdown == aWhy) {
+ if (gNeckoChild &&
+ !AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
+ // Let NeckoParent know that the socket process connections must be
+ // rebuilt.
+ gNeckoChild->SendResetSocketProcessBridge();
+ }
+
+ nsresult res;
+ nsCOMPtr<nsISerialEventTarget> mSTSThread =
+ do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &res);
+ if (NS_SUCCEEDED(res) && mSTSThread) {
+ // This must be called off the main thread. If we don't make this call
+ // ipc::BackgroundChild::GetOrCreateSocketActorForCurrentThread() will
+ // return the previous actor that is no longer able to send. This causes
+ // rebuilding the socket process connections to fail.
+ MOZ_ALWAYS_SUCCEEDS(mSTSThread->Dispatch(NS_NewRunnableFunction(
+ "net::SocketProcessBridgeChild::ActorDestroy",
+ []() { ipc::BackgroundChild::CloseForCurrentThread(); })));
+ }
+ }
+
+ nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
+ if (os) {
+ os->RemoveObserver(this, "content-child-shutdown");
+ }
+ GetCurrentSerialEventTarget()->Dispatch(
+ NewRunnableMethod("net::SocketProcessBridgeChild::DeferredDestroy", this,
+ &SocketProcessBridgeChild::DeferredDestroy));
+ mShuttingDown = true;
+}
+
+NS_IMETHODIMP
+SocketProcessBridgeChild::Observe(nsISupports* aSubject, const char* aTopic,
+ const char16_t* aData) {
+ if (!strcmp(aTopic, "content-child-shutdown")) {
+ PSocketProcessBridgeChild::Close();
+ }
+ return NS_OK;
+}
+
+void SocketProcessBridgeChild::DeferredDestroy() {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ sSocketProcessBridgeChild = nullptr;
+}
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/ipc/SocketProcessBridgeChild.h b/netwerk/ipc/SocketProcessBridgeChild.h
new file mode 100644
index 0000000000..8b1c1ad6cf
--- /dev/null
+++ b/netwerk/ipc/SocketProcessBridgeChild.h
@@ -0,0 +1,50 @@
+/* -*- 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_SocketProcessBridgeChild_h
+#define mozilla_net_SocketProcessBridgeChild_h
+
+#include <functional>
+#include "mozilla/net/PSocketProcessBridgeChild.h"
+#include "nsIObserver.h"
+
+namespace mozilla {
+namespace net {
+
+// The IPC actor implements PSocketProcessBridgeChild in content process.
+// This is allocated and kept alive by NeckoChild. When "content-child-shutdown"
+// topic is observed, this actor will be destroyed.
+class SocketProcessBridgeChild final : public PSocketProcessBridgeChild,
+ public nsIObserver {
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+
+ static already_AddRefed<SocketProcessBridgeChild> GetSingleton();
+ using GetPromise =
+ MozPromise<RefPtr<SocketProcessBridgeChild>, nsCString, false>;
+ static RefPtr<GetPromise> GetSocketProcessBridge();
+
+ mozilla::ipc::IPCResult RecvTest();
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+ void DeferredDestroy();
+ bool IsShuttingDown() const { return mShuttingDown; };
+ ProcessId SocketProcessPid() const { return mSocketProcessPid; };
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SocketProcessBridgeChild);
+ static bool Create(Endpoint<PSocketProcessBridgeChild>&& aEndpoint);
+ explicit SocketProcessBridgeChild();
+ virtual ~SocketProcessBridgeChild();
+
+ static StaticRefPtr<SocketProcessBridgeChild> sSocketProcessBridgeChild;
+ bool mShuttingDown;
+ ProcessId mSocketProcessPid;
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozilla_net_SocketProcessBridgeChild_h
diff --git a/netwerk/ipc/SocketProcessBridgeParent.cpp b/netwerk/ipc/SocketProcessBridgeParent.cpp
new file mode 100644
index 0000000000..b16be05b4d
--- /dev/null
+++ b/netwerk/ipc/SocketProcessBridgeParent.cpp
@@ -0,0 +1,97 @@
+/* -*- 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 "SocketProcessBridgeParent.h"
+#include "SocketProcessLogging.h"
+
+#ifdef MOZ_WEBRTC
+# include "mozilla/dom/MediaTransportParent.h"
+#endif
+#include "mozilla/ipc/BackgroundParent.h"
+#include "mozilla/ipc/Endpoint.h"
+#include "SocketProcessChild.h"
+#include "mozilla/net/BackgroundDataBridgeParent.h"
+#include "nsThreadUtils.h"
+
+namespace mozilla {
+namespace net {
+
+SocketProcessBridgeParent::SocketProcessBridgeParent(ProcessId aId) : mId(aId) {
+ LOG(
+ ("CONSTRUCT SocketProcessBridgeParent::SocketProcessBridgeParent "
+ "mId=%" PRIPID "\n",
+ mId));
+}
+
+SocketProcessBridgeParent::~SocketProcessBridgeParent() {
+ LOG(("DESTRUCT SocketProcessBridgeParent::SocketProcessBridgeParent\n"));
+}
+
+mozilla::ipc::IPCResult SocketProcessBridgeParent::RecvInitBackgroundDataBridge(
+ mozilla::ipc::Endpoint<PBackgroundDataBridgeParent>&& aEndpoint,
+ uint64_t aChannelID) {
+ LOG(("SocketProcessBridgeParent::RecvInitBackgroundDataBridge\n"));
+
+ nsCOMPtr<nsISerialEventTarget> transportQueue;
+ if (NS_FAILED(NS_CreateBackgroundTaskQueue("BackgroundDataBridge",
+ getter_AddRefs(transportQueue)))) {
+ return IPC_FAIL(this, "NS_CreateBackgroundTaskQueue failed");
+ }
+
+ if (!aEndpoint.IsValid()) {
+ return IPC_FAIL(this, "Invalid endpoint");
+ }
+
+ transportQueue->Dispatch(NS_NewRunnableFunction(
+ "BackgroundDataBridgeParent::Bind",
+ [endpoint = std::move(aEndpoint), aChannelID]() mutable {
+ RefPtr<net::BackgroundDataBridgeParent> actor =
+ new net::BackgroundDataBridgeParent(aChannelID);
+ endpoint.Bind(actor);
+ }));
+ return IPC_OK();
+}
+
+#ifdef MOZ_WEBRTC
+mozilla::ipc::IPCResult SocketProcessBridgeParent::RecvInitMediaTransport(
+ mozilla::ipc::Endpoint<mozilla::dom::PMediaTransportParent>&& aEndpoint) {
+ LOG(("SocketProcessBridgeParent::RecvInitMediaTransport\n"));
+
+ if (!aEndpoint.IsValid()) {
+ return IPC_FAIL(this, "Invalid endpoint");
+ }
+
+ if (!mMediaTransportTaskQueue) {
+ nsCOMPtr<nsISerialEventTarget> transportQueue;
+ if (NS_FAILED(NS_CreateBackgroundTaskQueue(
+ "MediaTransport", getter_AddRefs(transportQueue)))) {
+ return IPC_FAIL(this, "NS_CreateBackgroundTaskQueue failed");
+ }
+
+ mMediaTransportTaskQueue = std::move(transportQueue);
+ }
+
+ mMediaTransportTaskQueue->Dispatch(NS_NewRunnableFunction(
+ "BackgroundDataBridgeParent::Bind",
+ [endpoint = std::move(aEndpoint)]() mutable {
+ RefPtr<MediaTransportParent> actor = new MediaTransportParent();
+ endpoint.Bind(actor);
+ }));
+ return IPC_OK();
+}
+#endif
+
+void SocketProcessBridgeParent::ActorDestroy(ActorDestroyReason aReason) {
+ // See bug 1846478. We might be able to remove this dispatch.
+ GetCurrentSerialEventTarget()->Dispatch(NS_NewRunnableFunction(
+ "SocketProcessBridgeParent::ActorDestroy", [id = mId] {
+ if (SocketProcessChild* child = SocketProcessChild::GetSingleton()) {
+ child->DestroySocketProcessBridgeParent(id);
+ }
+ }));
+}
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/ipc/SocketProcessBridgeParent.h b/netwerk/ipc/SocketProcessBridgeParent.h
new file mode 100644
index 0000000000..e9b6053665
--- /dev/null
+++ b/netwerk/ipc/SocketProcessBridgeParent.h
@@ -0,0 +1,44 @@
+/* -*- 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_SocketProcessBridgeParent_h
+#define mozilla_net_SocketProcessBridgeParent_h
+
+#include "mozilla/net/PSocketProcessBridgeParent.h"
+
+namespace mozilla {
+namespace net {
+
+// The IPC actor implements PSocketProcessBridgeParent in socket process.
+// This is allocated and kept alive by SocketProcessChild. When |ActorDestroy|
+// is called, |SocketProcessChild::DestroySocketProcessBridgeParent| will be
+// called to destroy this actor.
+class SocketProcessBridgeParent final : public PSocketProcessBridgeParent {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SocketProcessBridgeParent, final)
+
+ explicit SocketProcessBridgeParent(ProcessId aId);
+
+ mozilla::ipc::IPCResult RecvInitBackgroundDataBridge(
+ Endpoint<PBackgroundDataBridgeParent>&& aEndpoint, uint64_t aChannelID);
+
+#ifdef MOZ_WEBRTC
+ mozilla::ipc::IPCResult RecvInitMediaTransport(
+ Endpoint<PMediaTransportParent>&& aEndpoint);
+#endif
+
+ void ActorDestroy(ActorDestroyReason aReason) override;
+
+ private:
+ ~SocketProcessBridgeParent();
+
+ nsCOMPtr<nsISerialEventTarget> mMediaTransportTaskQueue;
+ ProcessId mId;
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozilla_net_SocketProcessBridgeParent_h
diff --git a/netwerk/ipc/SocketProcessChild.cpp b/netwerk/ipc/SocketProcessChild.cpp
new file mode 100644
index 0000000000..000ebde2fd
--- /dev/null
+++ b/netwerk/ipc/SocketProcessChild.cpp
@@ -0,0 +1,833 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "SocketProcessChild.h"
+#include "SocketProcessLogging.h"
+
+#include "base/task.h"
+#include "InputChannelThrottleQueueChild.h"
+#include "HttpInfo.h"
+#include "HttpTransactionChild.h"
+#include "HttpConnectionMgrChild.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/Components.h"
+#include "mozilla/dom/MemoryReportRequest.h"
+#include "mozilla/FOGIPC.h"
+#include "mozilla/glean/GleanMetrics.h"
+#include "mozilla/ipc/CrashReporterClient.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/SocketProcessBackgroundChild.h"
+#include "mozilla/net/TRRServiceChild.h"
+#include "mozilla/ipc/ProcessUtils.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/RemoteLazyInputStreamChild.h"
+#include "mozilla/StaticPrefs_javascript.h"
+#include "mozilla/StaticPrefs_network.h"
+#include "mozilla/Telemetry.h"
+#include "NetworkConnectivityService.h"
+#include "nsDebugImpl.h"
+#include "nsHttpConnectionInfo.h"
+#include "nsHttpHandler.h"
+#include "nsIDNSService.h"
+#include "nsIHttpActivityObserver.h"
+#include "nsIXULRuntime.h"
+#include "nsNetUtil.h"
+#include "nsNSSComponent.h"
+#include "nsSocketTransportService2.h"
+#include "nsThreadManager.h"
+#include "SocketProcessBridgeParent.h"
+#include "jsapi.h"
+#include "js/Initialization.h"
+#include "js/Prefs.h"
+#include "XPCSelfHostedShmem.h"
+
+#if defined(XP_WIN)
+# include <process.h>
+
+# include "mozilla/WinDllServices.h"
+#else
+# include <unistd.h>
+#endif
+
+#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
+# include "mozilla/Sandbox.h"
+#endif
+
+#include "ChildProfilerController.h"
+
+#ifdef MOZ_WEBRTC
+# include "mozilla/net/WebrtcTCPSocketChild.h"
+#endif
+
+#if defined(MOZ_SANDBOX) && defined(MOZ_DEBUG) && defined(ENABLE_TESTS)
+# include "mozilla/SandboxTestingChild.h"
+#endif
+
+namespace mozilla {
+namespace net {
+
+using namespace ipc;
+
+static bool sInitializedJS = false;
+
+static Atomic<SocketProcessChild*> sSocketProcessChild;
+
+SocketProcessChild::SocketProcessChild() {
+ LOG(("CONSTRUCT SocketProcessChild::SocketProcessChild\n"));
+ nsDebugImpl::SetMultiprocessMode("Socket");
+
+ MOZ_COUNT_CTOR(SocketProcessChild);
+ sSocketProcessChild = this;
+}
+
+SocketProcessChild::~SocketProcessChild() {
+ LOG(("DESTRUCT SocketProcessChild::SocketProcessChild\n"));
+ MOZ_COUNT_DTOR(SocketProcessChild);
+ sSocketProcessChild = nullptr;
+}
+
+/* static */
+SocketProcessChild* SocketProcessChild::GetSingleton() {
+ return sSocketProcessChild;
+}
+
+#if defined(XP_MACOSX)
+extern "C" {
+void CGSShutdownServerConnections();
+};
+#endif
+
+void SocketProcessChild::InitSocketBackground() {
+ Endpoint<PSocketProcessBackgroundParent> parentEndpoint;
+ Endpoint<PSocketProcessBackgroundChild> childEndpoint;
+ if (NS_WARN_IF(NS_FAILED(PSocketProcessBackground::CreateEndpoints(
+ &parentEndpoint, &childEndpoint)))) {
+ return;
+ }
+
+ SocketProcessBackgroundChild::Create(std::move(childEndpoint));
+
+ Unused << SendInitSocketBackground(std::move(parentEndpoint));
+}
+
+namespace {
+
+class NetTeardownObserver final : public nsIObserver {
+ public:
+ NetTeardownObserver() = default;
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+
+ private:
+ ~NetTeardownObserver() = default;
+};
+
+NS_IMPL_ISUPPORTS(NetTeardownObserver, nsIObserver)
+
+NS_IMETHODIMP
+NetTeardownObserver::Observe(nsISupports* aSubject, const char* aTopic,
+ const char16_t* aData) {
+ if (SocketProcessChild* child = SocketProcessChild::GetSingleton()) {
+ child->CloseIPCClientCertsActor();
+ }
+
+ return NS_OK;
+}
+
+} // namespace
+
+bool SocketProcessChild::Init(mozilla::ipc::UntypedEndpoint&& aEndpoint,
+ const char* aParentBuildID) {
+ if (NS_WARN_IF(NS_FAILED(nsThreadManager::get().Init()))) {
+ return false;
+ }
+ if (NS_WARN_IF(!aEndpoint.Bind(this))) {
+ 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();
+ }
+
+ // Init crash reporter support.
+ CrashReporterClient::InitSingleton(this);
+
+ if (NS_FAILED(NS_InitMinimalXPCOM())) {
+ return false;
+ }
+
+ InitSocketBackground();
+
+ 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
+
+ nsresult rv;
+ nsCOMPtr<nsIIOService> ios = do_GetIOService(&rv);
+ if (NS_FAILED(rv)) {
+ return false;
+ }
+
+ nsCOMPtr<nsIProtocolHandler> handler;
+ rv = ios->GetProtocolHandler("http", getter_AddRefs(handler));
+ if (NS_FAILED(rv)) {
+ return false;
+ }
+
+ // Initialize DNS Service here, since it needs to be done in main thread.
+ nsCOMPtr<nsIDNSService> dns =
+ do_GetService("@mozilla.org/network/dns-service;1", &rv);
+ if (NS_FAILED(rv)) {
+ return false;
+ }
+
+ if (!EnsureNSSInitializedChromeOrContent()) {
+ return false;
+ }
+
+ nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+ if (obs) {
+ nsCOMPtr<nsIObserver> observer = new NetTeardownObserver();
+ Unused << obs->AddObserver(observer, "profile-change-net-teardown", false);
+ }
+
+ mSocketThread = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID);
+ if (!mSocketThread) {
+ return false;
+ }
+
+ return true;
+}
+
+void SocketProcessChild::ActorDestroy(ActorDestroyReason aWhy) {
+ LOG(("SocketProcessChild::ActorDestroy\n"));
+
+ {
+ MutexAutoLock lock(mMutex);
+ mShuttingDown = true;
+ }
+
+ if (AbnormalShutdown == aWhy) {
+ NS_WARNING("Shutting down Socket process early due to a crash!");
+ ProcessChild::QuickExit();
+ }
+
+ // Send the last bits of Glean data over to the main process.
+ glean::FlushFOGData(
+ [](ByteBuf&& aBuf) { glean::SendFOGData(std::move(aBuf)); });
+
+ if (mProfilerController) {
+ mProfilerController->Shutdown();
+ mProfilerController = nullptr;
+ }
+
+ CrashReporterClient::DestroySingleton();
+ XRE_ShutdownChildProcess();
+}
+
+void SocketProcessChild::CleanUp() {
+ LOG(("SocketProcessChild::CleanUp\n"));
+
+ SocketProcessBackgroundChild::Shutdown();
+
+ for (const auto& parent : mSocketProcessBridgeParentMap.Values()) {
+ if (parent->CanSend()) {
+ parent->Close();
+ }
+ }
+
+ {
+ MutexAutoLock lock(mMutex);
+ mBackgroundDataBridgeMap.Clear();
+ }
+
+ // Normally, the IPC channel should be already closed at this point, but
+ // sometimes it's not (bug 1788860). When the channel is closed, calling
+ // Close() again is harmless.
+ Close();
+
+ 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());
+ }
+
+#if defined(XP_WIN)
+ RefPtr<DllServices> dllSvc(DllServices::Get());
+ dllSvc->StartUntrustedModulesProcessor(
+ aAttributes.mIsReadyForBackgroundProcessing());
+#endif // defined(XP_WIN)
+
+ return IPC_OK();
+}
+
+IPCResult SocketProcessChild::RecvPreferenceUpdate(const Pref& aPref) {
+ Preferences::SetPreference(aPref);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult SocketProcessChild::RecvRequestMemoryReport(
+ const uint32_t& aGeneration, const bool& aAnonymize,
+ const bool& aMinimizeMemoryUsage,
+ const Maybe<ipc::FileDescriptor>& aDMDFile,
+ const RequestMemoryReportResolver& aResolver) {
+ nsPrintfCString processName("Socket (pid %u)", (unsigned)getpid());
+
+ mozilla::dom::MemoryReportRequestClient::Start(
+ aGeneration, aAnonymize, aMinimizeMemoryUsage, aDMDFile, processName,
+ [&](const MemoryReport& aReport) {
+ Unused << GetSingleton()->SendAddMemoryReport(aReport);
+ },
+ aResolver);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult SocketProcessChild::RecvSetOffline(
+ const bool& aOffline) {
+ LOG(("SocketProcessChild::RecvSetOffline aOffline=%d\n", aOffline));
+
+ nsCOMPtr<nsIIOService> io(do_GetIOService());
+ NS_ASSERTION(io, "IO Service can not be null");
+
+ io->SetOffline(aOffline);
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult SocketProcessChild::RecvSetConnectivity(
+ const bool& aConnectivity) {
+ nsCOMPtr<nsIIOService> io(do_GetIOService());
+ nsCOMPtr<nsIIOServiceInternal> ioInternal(do_QueryInterface(io));
+ NS_ASSERTION(ioInternal, "IO Service can not be null");
+
+ ioInternal->SetConnectivity(aConnectivity);
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult SocketProcessChild::RecvInitLinuxSandbox(
+ const Maybe<ipc::FileDescriptor>& aBrokerFd) {
+#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
+ int fd = -1;
+ if (aBrokerFd.isSome()) {
+ fd = aBrokerFd.value().ClonePlatformHandle().release();
+ }
+ SetSocketProcessSandbox(fd);
+#endif // XP_LINUX && MOZ_SANDBOX
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult SocketProcessChild::RecvInitSocketProcessBridgeParent(
+ const ProcessId& aContentProcessId,
+ Endpoint<mozilla::net::PSocketProcessBridgeParent>&& aEndpoint) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(!mSocketProcessBridgeParentMap.Contains(aContentProcessId));
+
+ if (NS_WARN_IF(!aEndpoint.IsValid())) {
+ return IPC_FAIL(this, "invalid endpoint");
+ }
+
+ auto bridge = MakeRefPtr<SocketProcessBridgeParent>(aContentProcessId);
+ MOZ_ALWAYS_TRUE(aEndpoint.Bind(bridge));
+
+ mSocketProcessBridgeParentMap.InsertOrUpdate(aContentProcessId,
+ std::move(bridge));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult SocketProcessChild::RecvInitProfiler(
+ Endpoint<PProfilerChild>&& aEndpoint) {
+ mProfilerController =
+ mozilla::ChildProfilerController::Create(std::move(aEndpoint));
+ return IPC_OK();
+}
+
+#if defined(MOZ_SANDBOX) && defined(MOZ_DEBUG) && defined(ENABLE_TESTS)
+mozilla::ipc::IPCResult SocketProcessChild::RecvInitSandboxTesting(
+ Endpoint<PSandboxTestingChild>&& aEndpoint) {
+ if (!SandboxTestingChild::Initialize(std::move(aEndpoint))) {
+ return IPC_FAIL(
+ this, "InitSandboxTesting failed to initialise the child process.");
+ }
+ return IPC_OK();
+}
+#endif
+
+mozilla::ipc::IPCResult SocketProcessChild::RecvSocketProcessTelemetryPing() {
+ const uint32_t kExpectedUintValue = 42;
+ Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_SOCKET_ONLY_UINT,
+ kExpectedUintValue);
+ return IPC_OK();
+}
+
+void SocketProcessChild::DestroySocketProcessBridgeParent(ProcessId aId) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ mSocketProcessBridgeParentMap.Remove(aId);
+}
+
+PWebrtcTCPSocketChild* SocketProcessChild::AllocPWebrtcTCPSocketChild(
+ const Maybe<TabId>& tabId) {
+ // We don't allocate here: instead we always use IPDL constructor that takes
+ // an existing object
+ MOZ_ASSERT_UNREACHABLE(
+ "AllocPWebrtcTCPSocketChild should not be called on"
+ " socket child");
+ return nullptr;
+}
+
+bool SocketProcessChild::DeallocPWebrtcTCPSocketChild(
+ PWebrtcTCPSocketChild* aActor) {
+#ifdef MOZ_WEBRTC
+ WebrtcTCPSocketChild* child = static_cast<WebrtcTCPSocketChild*>(aActor);
+ child->ReleaseIPDLReference();
+#endif
+ return true;
+}
+
+already_AddRefed<PHttpTransactionChild>
+SocketProcessChild::AllocPHttpTransactionChild() {
+ RefPtr<HttpTransactionChild> actor = new HttpTransactionChild();
+ return actor.forget();
+}
+
+already_AddRefed<PHttpConnectionMgrChild>
+SocketProcessChild::AllocPHttpConnectionMgrChild(
+ const HttpHandlerInitArgs& aArgs) {
+ LOG(("SocketProcessChild::AllocPHttpConnectionMgrChild \n"));
+ MOZ_ASSERT(gHttpHandler);
+ gHttpHandler->SetHttpHandlerInitArgs(aArgs);
+
+ RefPtr<HttpConnectionMgrChild> actor = new HttpConnectionMgrChild();
+ return actor.forget();
+}
+
+mozilla::ipc::IPCResult SocketProcessChild::RecvUpdateDeviceModelId(
+ const nsACString& aModelId) {
+ MOZ_ASSERT(gHttpHandler);
+ gHttpHandler->SetDeviceModelId(aModelId);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+SocketProcessChild::RecvOnHttpActivityDistributorActivated(
+ const bool& aIsActivated) {
+ if (nsCOMPtr<nsIHttpActivityObserver> distributor =
+ components::HttpActivityDistributor::Service()) {
+ distributor->SetIsActive(aIsActivated);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+SocketProcessChild::RecvOnHttpActivityDistributorObserveProxyResponse(
+ const bool& aIsEnabled) {
+ nsCOMPtr<nsIHttpActivityDistributor> distributor =
+ do_GetService("@mozilla.org/network/http-activity-distributor;1");
+ if (distributor) {
+ Unused << distributor->SetObserveProxyResponse(aIsEnabled);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+SocketProcessChild::RecvOnHttpActivityDistributorObserveConnection(
+ const bool& aIsEnabled) {
+ nsCOMPtr<nsIHttpActivityDistributor> distributor =
+ do_GetService("@mozilla.org/network/http-activity-distributor;1");
+ if (distributor) {
+ Unused << distributor->SetObserveConnection(aIsEnabled);
+ }
+ return IPC_OK();
+}
+
+already_AddRefed<PInputChannelThrottleQueueChild>
+SocketProcessChild::AllocPInputChannelThrottleQueueChild(
+ const uint32_t& aMeanBytesPerSecond, const uint32_t& aMaxBytesPerSecond) {
+ RefPtr<InputChannelThrottleQueueChild> p =
+ new InputChannelThrottleQueueChild();
+ p->Init(aMeanBytesPerSecond, aMaxBytesPerSecond);
+ return p.forget();
+}
+
+already_AddRefed<PAltSvcTransactionChild>
+SocketProcessChild::AllocPAltSvcTransactionChild(
+ const HttpConnectionInfoCloneArgs& aConnInfo, const uint32_t& aCaps) {
+ RefPtr<nsHttpConnectionInfo> cinfo =
+ nsHttpConnectionInfo::DeserializeHttpConnectionInfoCloneArgs(aConnInfo);
+ RefPtr<AltSvcTransactionChild> child =
+ new AltSvcTransactionChild(cinfo, aCaps);
+ return child.forget();
+}
+
+already_AddRefed<PDNSRequestChild> SocketProcessChild::AllocPDNSRequestChild(
+ const nsACString& aHost, const nsACString& aTrrServer, const int32_t& aPort,
+ const uint16_t& aType, const OriginAttributes& aOriginAttributes,
+ const nsIDNSService::DNSFlags& aFlags) {
+ RefPtr<DNSRequestHandler> handler = new DNSRequestHandler();
+ RefPtr<DNSRequestChild> actor = new DNSRequestChild(handler);
+ return actor.forget();
+}
+
+mozilla::ipc::IPCResult SocketProcessChild::RecvPDNSRequestConstructor(
+ PDNSRequestChild* aActor, const nsACString& aHost,
+ const nsACString& aTrrServer, const int32_t& aPort, const uint16_t& aType,
+ const OriginAttributes& aOriginAttributes,
+ const nsIDNSService::DNSFlags& aFlags) {
+ RefPtr<DNSRequestChild> actor = static_cast<DNSRequestChild*>(aActor);
+ RefPtr<DNSRequestHandler> handler =
+ actor->GetDNSRequest()->AsDNSRequestHandler();
+ handler->DoAsyncResolve(aHost, aTrrServer, aPort, aType, aOriginAttributes,
+ aFlags);
+ return IPC_OK();
+}
+
+void SocketProcessChild::AddDataBridgeToMap(
+ uint64_t aChannelId, BackgroundDataBridgeParent* aActor) {
+ MutexAutoLock lock(mMutex);
+ mBackgroundDataBridgeMap.InsertOrUpdate(aChannelId, RefPtr{aActor});
+}
+
+void SocketProcessChild::RemoveDataBridgeFromMap(uint64_t aChannelId) {
+ MutexAutoLock lock(mMutex);
+ mBackgroundDataBridgeMap.Remove(aChannelId);
+}
+
+Maybe<RefPtr<BackgroundDataBridgeParent>>
+SocketProcessChild::GetAndRemoveDataBridge(uint64_t aChannelId) {
+ MutexAutoLock lock(mMutex);
+ return mBackgroundDataBridgeMap.Extract(aChannelId);
+}
+
+mozilla::ipc::IPCResult SocketProcessChild::RecvClearSessionCache(
+ ClearSessionCacheResolver&& aResolve) {
+ nsNSSComponent::DoClearSSLExternalAndInternalSessionCache();
+ aResolve(void_t{});
+ return IPC_OK();
+}
+
+already_AddRefed<PTRRServiceChild> SocketProcessChild::AllocPTRRServiceChild(
+ const bool& aCaptiveIsPassed, const bool& aParentalControlEnabled,
+ const nsTArray<nsCString>& aDNSSuffixList) {
+ RefPtr<TRRServiceChild> actor = new TRRServiceChild();
+ return actor.forget();
+}
+
+mozilla::ipc::IPCResult SocketProcessChild::RecvPTRRServiceConstructor(
+ PTRRServiceChild* aActor, const bool& aCaptiveIsPassed,
+ const bool& aParentalControlEnabled, nsTArray<nsCString>&& aDNSSuffixList) {
+ static_cast<TRRServiceChild*>(aActor)->Init(
+ aCaptiveIsPassed, aParentalControlEnabled, std::move(aDNSSuffixList));
+ return IPC_OK();
+}
+
+already_AddRefed<PNativeDNSResolverOverrideChild>
+SocketProcessChild::AllocPNativeDNSResolverOverrideChild() {
+ RefPtr<NativeDNSResolverOverrideChild> actor =
+ new NativeDNSResolverOverrideChild();
+ return actor.forget();
+}
+
+mozilla::ipc::IPCResult
+SocketProcessChild::RecvPNativeDNSResolverOverrideConstructor(
+ PNativeDNSResolverOverrideChild* aActor) {
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult SocketProcessChild::RecvNotifyObserver(
+ const nsACString& aTopic, const nsAString& aData) {
+ if (nsCOMPtr<nsIObserverService> obs =
+ mozilla::services::GetObserverService()) {
+ obs->NotifyObservers(nullptr, PromiseFlatCString(aTopic).get(),
+ PromiseFlatString(aData).get());
+ }
+ return IPC_OK();
+}
+
+namespace {
+
+class DataResolverBase {
+ public:
+ // This type is threadsafe-refcounted, as it's referenced on the socket
+ // thread, but must be destroyed on the main thread.
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_DELETE_ON_MAIN_THREAD(
+ DataResolverBase)
+
+ DataResolverBase() = default;
+
+ protected:
+ virtual ~DataResolverBase() = default;
+};
+
+template <typename DataType, typename ResolverType>
+class DataResolver final : public DataResolverBase {
+ public:
+ explicit DataResolver(ResolverType&& aResolve)
+ : mResolve(std::move(aResolve)) {}
+
+ void OnResolve(DataType&& aData) {
+ MOZ_ASSERT(OnSocketThread());
+
+ RefPtr<DataResolver<DataType, ResolverType>> self = this;
+ mData = std::move(aData);
+ NS_DispatchToMainThread(NS_NewRunnableFunction(
+ "net::DataResolver::OnResolve",
+ [self{std::move(self)}]() { self->mResolve(std::move(self->mData)); }));
+ }
+
+ private:
+ virtual ~DataResolver() = default;
+
+ ResolverType mResolve;
+ DataType mData;
+};
+
+} // anonymous namespace
+
+mozilla::ipc::IPCResult SocketProcessChild::RecvGetSocketData(
+ GetSocketDataResolver&& aResolve) {
+ if (!gSocketTransportService) {
+ aResolve(SocketDataArgs());
+ return IPC_OK();
+ }
+
+ RefPtr<
+ DataResolver<SocketDataArgs, SocketProcessChild::GetSocketDataResolver>>
+ resolver = new DataResolver<SocketDataArgs,
+ SocketProcessChild::GetSocketDataResolver>(
+ std::move(aResolve));
+ gSocketTransportService->Dispatch(
+ NS_NewRunnableFunction(
+ "net::SocketProcessChild::RecvGetSocketData",
+ [resolver{std::move(resolver)}]() {
+ SocketDataArgs args;
+ gSocketTransportService->GetSocketConnections(&args.info());
+ args.totalSent() = gSocketTransportService->GetSentBytes();
+ args.totalRecv() = gSocketTransportService->GetReceivedBytes();
+ resolver->OnResolve(std::move(args));
+ }),
+ NS_DISPATCH_NORMAL);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult SocketProcessChild::RecvGetDNSCacheEntries(
+ GetDNSCacheEntriesResolver&& aResolve) {
+ nsresult rv = NS_OK;
+ nsCOMPtr<nsIDNSService> dns =
+ do_GetService("@mozilla.org/network/dns-service;1", &rv);
+ if (NS_FAILED(rv)) {
+ aResolve(nsTArray<DNSCacheEntries>());
+ return IPC_OK();
+ }
+
+ RefPtr<DataResolver<nsTArray<DNSCacheEntries>,
+ SocketProcessChild::GetDNSCacheEntriesResolver>>
+ resolver =
+ new DataResolver<nsTArray<DNSCacheEntries>,
+ SocketProcessChild::GetDNSCacheEntriesResolver>(
+ std::move(aResolve));
+ gSocketTransportService->Dispatch(
+ NS_NewRunnableFunction(
+ "net::SocketProcessChild::RecvGetDNSCacheEntries",
+ [resolver{std::move(resolver)}, dns{std::move(dns)}]() {
+ nsTArray<DNSCacheEntries> entries;
+ dns->GetDNSCacheEntries(&entries);
+ resolver->OnResolve(std::move(entries));
+ }),
+ NS_DISPATCH_NORMAL);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult SocketProcessChild::RecvGetHttpConnectionData(
+ GetHttpConnectionDataResolver&& aResolve) {
+ if (!gSocketTransportService) {
+ aResolve(nsTArray<HttpRetParams>());
+ return IPC_OK();
+ }
+
+ RefPtr<DataResolver<nsTArray<HttpRetParams>,
+ SocketProcessChild::GetHttpConnectionDataResolver>>
+ resolver =
+ new DataResolver<nsTArray<HttpRetParams>,
+ SocketProcessChild::GetHttpConnectionDataResolver>(
+ std::move(aResolve));
+ gSocketTransportService->Dispatch(
+ NS_NewRunnableFunction(
+ "net::SocketProcessChild::RecvGetHttpConnectionData",
+ [resolver{std::move(resolver)}]() {
+ 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) {
+ // For parsing PAC.
+ if (!sInitializedJS) {
+ JS::DisableJitBackend();
+
+ // Set all JS::Prefs.
+ SET_JS_PREFS_FROM_BROWSER_PREFS;
+
+ const char* jsInitFailureReason = JS_InitWithFailureDiagnostic();
+ if (jsInitFailureReason) {
+ MOZ_CRASH_UNSAFE(jsInitFailureReason);
+ }
+ sInitializedJS = true;
+
+ xpc::SelfHostedShmem::GetSingleton();
+ }
+
+ Unused << ProxyAutoConfigChild::Create(std::move(aEndpoint));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult SocketProcessChild::RecvRecheckIPConnectivity() {
+ RefPtr<NetworkConnectivityService> ncs =
+ NetworkConnectivityService::GetSingleton();
+ if (ncs) {
+ ncs->RecheckIPConnectivity();
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult SocketProcessChild::RecvRecheckDNS() {
+ RefPtr<NetworkConnectivityService> ncs =
+ NetworkConnectivityService::GetSingleton();
+ if (ncs) {
+ ncs->RecheckDNS();
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult SocketProcessChild::RecvFlushFOGData(
+ FlushFOGDataResolver&& aResolver) {
+ glean::FlushFOGData(std::move(aResolver));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult SocketProcessChild::RecvTestTriggerMetrics(
+ TestTriggerMetricsResolver&& aResolve) {
+ mozilla::glean::test_only_ipc::a_counter.Add(
+ nsIXULRuntime::PROCESS_TYPE_SOCKET);
+ aResolve(true);
+ return IPC_OK();
+}
+
+#if defined(XP_WIN)
+mozilla::ipc::IPCResult SocketProcessChild::RecvGetUntrustedModulesData(
+ GetUntrustedModulesDataResolver&& aResolver) {
+ RefPtr<DllServices> dllSvc(DllServices::Get());
+ dllSvc->GetUntrustedModulesData()->Then(
+ GetMainThreadSerialEventTarget(), __func__,
+ [aResolver](Maybe<UntrustedModulesData>&& aData) {
+ aResolver(std::move(aData));
+ },
+ [aResolver](nsresult aReason) { aResolver(Nothing()); });
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+SocketProcessChild::RecvUnblockUntrustedModulesThread() {
+ if (nsCOMPtr<nsIObserverService> obs =
+ mozilla::services::GetObserverService()) {
+ obs->NotifyObservers(nullptr, "unblock-untrusted-modules-thread", nullptr);
+ }
+ return IPC_OK();
+}
+#endif // defined(XP_WIN)
+
+bool SocketProcessChild::IsShuttingDown() {
+ MutexAutoLock lock(mMutex);
+ return mShuttingDown;
+}
+
+void SocketProcessChild::CloseIPCClientCertsActor() {
+ LOG(("SocketProcessChild::CloseIPCClientCertsActor"));
+ MOZ_ASSERT(NS_IsMainThread());
+
+ mSocketThread->Dispatch(NS_NewRunnableFunction(
+ "CloseIPCClientCertsActor", [self = RefPtr{this}]() {
+ LOG(("CloseIPCClientCertsActor"));
+ if (self->mIPCClientCertsChild) {
+ self->mIPCClientCertsChild->Close();
+ self->mIPCClientCertsChild = nullptr;
+ }
+ }));
+}
+
+already_AddRefed<psm::IPCClientCertsChild>
+SocketProcessChild::GetIPCClientCertsActor() {
+ LOG(("SocketProcessChild::GetIPCClientCertsActor"));
+ // Only socket thread can access the mIPCClientCertsChild.
+ if (!OnSocketThread()) {
+ return nullptr;
+ }
+
+ {
+ MutexAutoLock lock(mMutex);
+ if (mShuttingDown) {
+ return nullptr;
+ }
+ }
+
+ if (mIPCClientCertsChild) {
+ RefPtr<psm::IPCClientCertsChild> actorChild = mIPCClientCertsChild;
+ return actorChild.forget();
+ }
+
+ ipc::Endpoint<psm::PIPCClientCertsParent> parentEndpoint;
+ ipc::Endpoint<psm::PIPCClientCertsChild> childEndpoint;
+ psm::PIPCClientCerts::CreateEndpoints(&parentEndpoint, &childEndpoint);
+
+ if (NS_FAILED(SocketProcessBackgroundChild::WithActor(
+ "SendInitIPCClientCerts",
+ [endpoint = std::move(parentEndpoint)](
+ SocketProcessBackgroundChild* aActor) mutable {
+ Unused << aActor->SendInitIPCClientCerts(std::move(endpoint));
+ }))) {
+ return nullptr;
+ }
+
+ RefPtr<psm::IPCClientCertsChild> actor = new psm::IPCClientCertsChild();
+ if (!childEndpoint.Bind(actor)) {
+ return nullptr;
+ }
+
+ mIPCClientCertsChild = actor;
+ return actor.forget();
+}
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/ipc/SocketProcessChild.h b/netwerk/ipc/SocketProcessChild.h
new file mode 100644
index 0000000000..fbcac7462d
--- /dev/null
+++ b/netwerk/ipc/SocketProcessChild.h
@@ -0,0 +1,188 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_net_SocketProcessChild_h
+#define mozilla_net_SocketProcessChild_h
+
+#include "mozilla/net/PSocketProcessChild.h"
+#include "mozilla/ipc/InputStreamUtils.h"
+#include "mozilla/psm/IPCClientCertsChild.h"
+#include "mozilla/Mutex.h"
+#include "nsRefPtrHashtable.h"
+#include "nsTHashMap.h"
+
+#if defined(MOZ_SANDBOX) && defined(MOZ_DEBUG) && defined(ENABLE_TESTS)
+# include "mozilla/PSandboxTestingChild.h"
+#endif
+
+namespace mozilla {
+class ChildProfilerController;
+}
+
+namespace mozilla {
+namespace net {
+
+class ProxyAutoConfigChild;
+class SocketProcessBackgroundChild;
+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:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SocketProcessChild, final)
+
+ SocketProcessChild();
+
+ static SocketProcessChild* GetSingleton();
+
+ bool Init(mozilla::ipc::UntypedEndpoint&& aEndpoint,
+ const char* aParentBuildID);
+
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ mozilla::ipc::IPCResult RecvInit(
+ const SocketPorcessInitAttributes& aAttributes);
+ mozilla::ipc::IPCResult RecvPreferenceUpdate(const Pref& aPref);
+ mozilla::ipc::IPCResult RecvRequestMemoryReport(
+ const uint32_t& generation, const bool& anonymize,
+ const bool& minimizeMemoryUsage,
+ const Maybe<mozilla::ipc::FileDescriptor>& DMDFile,
+ const RequestMemoryReportResolver& aResolver);
+ mozilla::ipc::IPCResult RecvSetOffline(const bool& aOffline);
+ mozilla::ipc::IPCResult RecvSetConnectivity(const bool& aConnectivity);
+ mozilla::ipc::IPCResult RecvInitLinuxSandbox(
+ const Maybe<ipc::FileDescriptor>& aBrokerFd);
+ mozilla::ipc::IPCResult RecvInitSocketProcessBridgeParent(
+ const ProcessId& aContentProcessId,
+ Endpoint<mozilla::net::PSocketProcessBridgeParent>&& aEndpoint);
+ mozilla::ipc::IPCResult RecvInitProfiler(
+ Endpoint<mozilla::PProfilerChild>&& aEndpoint);
+#if defined(MOZ_SANDBOX) && defined(MOZ_DEBUG) && defined(ENABLE_TESTS)
+ mozilla::ipc::IPCResult RecvInitSandboxTesting(
+ Endpoint<PSandboxTestingChild>&& aEndpoint);
+#endif
+ mozilla::ipc::IPCResult RecvSocketProcessTelemetryPing();
+
+ PWebrtcTCPSocketChild* AllocPWebrtcTCPSocketChild(const Maybe<TabId>& tabId);
+ bool DeallocPWebrtcTCPSocketChild(PWebrtcTCPSocketChild* aActor);
+
+ already_AddRefed<PHttpTransactionChild> AllocPHttpTransactionChild();
+
+ void CleanUp();
+ void DestroySocketProcessBridgeParent(ProcessId aId);
+
+ already_AddRefed<PHttpConnectionMgrChild> AllocPHttpConnectionMgrChild(
+ const HttpHandlerInitArgs& aArgs);
+ mozilla::ipc::IPCResult RecvUpdateDeviceModelId(const nsACString& aModelId);
+ mozilla::ipc::IPCResult RecvOnHttpActivityDistributorActivated(
+ const bool& aIsActivated);
+ mozilla::ipc::IPCResult RecvOnHttpActivityDistributorObserveProxyResponse(
+ const bool& aIsEnabled);
+ mozilla::ipc::IPCResult RecvOnHttpActivityDistributorObserveConnection(
+ const bool& aIsEnabled);
+
+ already_AddRefed<PInputChannelThrottleQueueChild>
+ AllocPInputChannelThrottleQueueChild(const uint32_t& aMeanBytesPerSecond,
+ const uint32_t& aMaxBytesPerSecond);
+
+ already_AddRefed<PAltSvcTransactionChild> AllocPAltSvcTransactionChild(
+ const HttpConnectionInfoCloneArgs& aConnInfo, const uint32_t& aCaps);
+
+ bool IsShuttingDown();
+
+ already_AddRefed<PDNSRequestChild> AllocPDNSRequestChild(
+ const nsACString& aHost, const nsACString& aTrrServer,
+ const int32_t& aPort, const uint16_t& aType,
+ const OriginAttributes& aOriginAttributes,
+ const nsIDNSService::DNSFlags& aFlags);
+ mozilla::ipc::IPCResult RecvPDNSRequestConstructor(
+ PDNSRequestChild* aActor, const nsACString& aHost,
+ const nsACString& aTrrServer, const int32_t& aPort, const uint16_t& aType,
+ const OriginAttributes& aOriginAttributes,
+ const nsIDNSService::DNSFlags& aFlags) override;
+
+ void AddDataBridgeToMap(uint64_t aChannelId,
+ BackgroundDataBridgeParent* aActor);
+ void RemoveDataBridgeFromMap(uint64_t aChannelId);
+ Maybe<RefPtr<BackgroundDataBridgeParent>> GetAndRemoveDataBridge(
+ uint64_t aChannelId);
+
+ mozilla::ipc::IPCResult RecvClearSessionCache(
+ ClearSessionCacheResolver&& aResolve);
+
+ already_AddRefed<PTRRServiceChild> AllocPTRRServiceChild(
+ const bool& aCaptiveIsPassed, const bool& aParentalControlEnabled,
+ const nsTArray<nsCString>& aDNSSuffixList);
+ mozilla::ipc::IPCResult RecvPTRRServiceConstructor(
+ PTRRServiceChild* aActor, const bool& aCaptiveIsPassed,
+ const bool& aParentalControlEnabled,
+ nsTArray<nsCString>&& aDNSSuffixList) override;
+
+ already_AddRefed<PNativeDNSResolverOverrideChild>
+ AllocPNativeDNSResolverOverrideChild();
+ mozilla::ipc::IPCResult RecvPNativeDNSResolverOverrideConstructor(
+ PNativeDNSResolverOverrideChild* aActor) override;
+
+ mozilla::ipc::IPCResult RecvNotifyObserver(const nsACString& aTopic,
+ const nsAString& aData);
+
+ 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);
+
+ mozilla::ipc::IPCResult RecvRecheckIPConnectivity();
+ mozilla::ipc::IPCResult RecvRecheckDNS();
+
+ mozilla::ipc::IPCResult RecvFlushFOGData(FlushFOGDataResolver&& aResolver);
+
+ mozilla::ipc::IPCResult RecvTestTriggerMetrics(
+ TestTriggerMetricsResolver&& aResolve);
+
+#if defined(XP_WIN)
+ mozilla::ipc::IPCResult RecvGetUntrustedModulesData(
+ GetUntrustedModulesDataResolver&& aResolver);
+ mozilla::ipc::IPCResult RecvUnblockUntrustedModulesThread();
+#endif // defined(XP_WIN)
+
+ already_AddRefed<psm::IPCClientCertsChild> GetIPCClientCertsActor();
+ void CloseIPCClientCertsActor();
+
+ protected:
+ friend class SocketProcessImpl;
+ ~SocketProcessChild();
+
+ void InitSocketBackground();
+
+ private:
+ // Mapping of content process id and the SocketProcessBridgeParent.
+ // This table keeps SocketProcessBridgeParent alive in socket process.
+ nsRefPtrHashtable<nsUint32HashKey, SocketProcessBridgeParent>
+ mSocketProcessBridgeParentMap;
+
+ RefPtr<ChildProfilerController> mProfilerController;
+
+ // Protect the table below.
+ Mutex mMutex MOZ_UNANNOTATED{"SocketProcessChild::mMutex"};
+ nsTHashMap<uint64_t, RefPtr<BackgroundDataBridgeParent>>
+ mBackgroundDataBridgeMap;
+
+ bool mShuttingDown MOZ_GUARDED_BY(mMutex) = false;
+
+ nsCOMPtr<nsIEventTarget> mSocketThread;
+ // mIPCClientCertsChild is only accessed on the socket thread.
+ RefPtr<psm::IPCClientCertsChild> mIPCClientCertsChild;
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozilla_net_SocketProcessChild_h
diff --git a/netwerk/ipc/SocketProcessHost.cpp b/netwerk/ipc/SocketProcessHost.cpp
new file mode 100644
index 0000000000..5a09f5200c
--- /dev/null
+++ b/netwerk/ipc/SocketProcessHost.cpp
@@ -0,0 +1,292 @@
+/* -*- 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 "SocketProcessHost.h"
+
+#include "SocketProcessParent.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/ipc/FileDescriptor.h"
+#include "mozilla/ipc/ProcessUtils.h"
+#include "nsAppRunner.h"
+#include "nsIOService.h"
+#include "nsIObserverService.h"
+#include "ProfilerParent.h"
+#include "nsNetUtil.h"
+#include "mozilla/ipc/Endpoint.h"
+#include "mozilla/ipc/ProcessChild.h"
+
+#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
+# include "mozilla/SandboxBroker.h"
+# include "mozilla/SandboxBrokerPolicyFactory.h"
+# include "mozilla/SandboxSettings.h"
+#endif
+
+#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
+# include "mozilla/Sandbox.h"
+#endif
+
+#if defined(XP_WIN)
+# include "mozilla/WinDllServices.h"
+#endif
+
+using namespace mozilla::ipc;
+
+namespace mozilla {
+namespace net {
+
+#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
+bool SocketProcessHost::sLaunchWithMacSandbox = false;
+#endif
+
+SocketProcessHost::SocketProcessHost(Listener* aListener)
+ : GeckoChildProcessHost(GeckoProcessType_Socket),
+ mListener(aListener),
+ mTaskFactory(Some(this)),
+ mLaunchPhase(LaunchPhase::Unlaunched),
+ mShutdownRequested(false),
+ mChannelClosed(false) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_COUNT_CTOR(SocketProcessHost);
+#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
+ if (!sLaunchWithMacSandbox) {
+ sLaunchWithMacSandbox =
+ (PR_GetEnv("MOZ_DISABLE_SOCKET_PROCESS_SANDBOX") == nullptr);
+ }
+ mDisableOSActivityMode = sLaunchWithMacSandbox;
+#endif
+}
+
+SocketProcessHost::~SocketProcessHost() { MOZ_COUNT_DTOR(SocketProcessHost); }
+
+bool SocketProcessHost::Launch() {
+ MOZ_ASSERT(mLaunchPhase == LaunchPhase::Unlaunched);
+ MOZ_ASSERT(!mSocketProcessParent);
+ MOZ_ASSERT(NS_IsMainThread());
+
+ std::vector<std::string> extraArgs;
+ ProcessChild::AddPlatformBuildID(extraArgs);
+
+ SharedPreferenceSerializer prefSerializer;
+ if (!prefSerializer.SerializeToSharedMemory(GeckoProcessType_VR,
+ /* remoteType */ ""_ns)) {
+ return false;
+ }
+ prefSerializer.AddSharedPrefCmdLineArgs(*this, extraArgs);
+
+ mLaunchPhase = LaunchPhase::Waiting;
+ if (!GeckoChildProcessHost::LaunchAndWaitForProcessHandle(extraArgs)) {
+ mLaunchPhase = LaunchPhase::Complete;
+ return false;
+ }
+
+ return true;
+}
+
+static void HandleErrorAfterDestroy(
+ RefPtr<SocketProcessHost::Listener>&& aListener) {
+ if (!aListener) {
+ return;
+ }
+
+ NS_DispatchToMainThread(NS_NewRunnableFunction(
+ "HandleErrorAfterDestroy", [listener = std::move(aListener)]() {
+ listener->OnProcessLaunchComplete(nullptr, false);
+ }));
+}
+
+void SocketProcessHost::OnChannelConnected(base::ProcessId 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);
+ if (!mTaskFactory) {
+ HandleErrorAfterDestroy(std::move(mListener));
+ return;
+ }
+ runnable =
+ (*mTaskFactory)
+ .NewRunnableMethod(&SocketProcessHost::OnChannelConnectedTask);
+ }
+ NS_DispatchToMainThread(runnable);
+}
+
+void SocketProcessHost::OnChannelConnectedTask() {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (mLaunchPhase == LaunchPhase::Waiting) {
+ InitAfterConnect(true);
+ }
+}
+
+void SocketProcessHost::InitAfterConnect(bool aSucceeded) {
+ MOZ_ASSERT(mLaunchPhase == LaunchPhase::Waiting);
+ MOZ_ASSERT(!mSocketProcessParent);
+ MOZ_ASSERT(NS_IsMainThread());
+
+ mLaunchPhase = LaunchPhase::Complete;
+ if (!aSucceeded) {
+ if (mListener) {
+ mListener->OnProcessLaunchComplete(this, false);
+ }
+ return;
+ }
+
+ mSocketProcessParent = MakeRefPtr<SocketProcessParent>(this);
+ DebugOnly<bool> rv = TakeInitialEndpoint().Bind(mSocketProcessParent.get());
+ MOZ_ASSERT(rv);
+
+ SocketPorcessInitAttributes attributes;
+ nsCOMPtr<nsIIOService> ioService(do_GetIOService());
+ MOZ_ASSERT(ioService, "No IO service?");
+ DebugOnly<nsresult> result = ioService->GetOffline(&attributes.mOffline());
+ MOZ_ASSERT(NS_SUCCEEDED(result), "Failed getting offline?");
+ result = ioService->GetConnectivity(&attributes.mConnectivity());
+ MOZ_ASSERT(NS_SUCCEEDED(result), "Failed getting connectivity?");
+
+ attributes.mInitSandbox() = false;
+
+#if defined(XP_WIN)
+ RefPtr<DllServices> dllSvc(DllServices::Get());
+ attributes.mIsReadyForBackgroundProcessing() =
+ dllSvc->IsReadyForBackgroundProcessing();
+#endif
+
+#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
+ if (GetEffectiveSocketProcessSandboxLevel() > 0) {
+ auto policy = SandboxBrokerPolicyFactory::GetSocketProcessPolicy(
+ GetActor()->OtherPid());
+ if (policy != nullptr) {
+ attributes.mSandboxBroker() = Some(FileDescriptor());
+ mSandboxBroker =
+ SandboxBroker::Create(std::move(policy), GetActor()->OtherPid(),
+ attributes.mSandboxBroker().ref());
+ // This is unlikely to fail and probably indicates OS resource
+ // exhaustion.
+ Unused << NS_WARN_IF(mSandboxBroker == nullptr);
+ MOZ_ASSERT(attributes.mSandboxBroker().ref().IsValid());
+ }
+ attributes.mInitSandbox() = true;
+ }
+#endif // XP_LINUX && MOZ_SANDBOX
+
+ Unused << GetActor()->SendInit(attributes);
+
+ Unused << GetActor()->SendInitProfiler(
+ ProfilerParent::CreateForProcess(GetActor()->OtherPid()));
+
+ if (mListener) {
+ mListener->OnProcessLaunchComplete(this, true);
+ }
+}
+
+void SocketProcessHost::Shutdown() {
+ MOZ_ASSERT(!mShutdownRequested);
+ MOZ_ASSERT(NS_IsMainThread());
+
+ mListener = nullptr;
+
+ if (mSocketProcessParent) {
+ // OnChannelClosed uses this to check if the shutdown was expected or
+ // unexpected.
+ mShutdownRequested = true;
+
+ // The channel might already be closed if we got here unexpectedly.
+ if (!mChannelClosed) {
+ mSocketProcessParent->Close();
+ }
+
+ return;
+ }
+
+ DestroyProcess();
+}
+
+void SocketProcessHost::OnChannelClosed() {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ mChannelClosed = true;
+
+ if (!mShutdownRequested && mListener) {
+ // This is an unclean shutdown. Notify our listener that we're going away.
+ mListener->OnProcessUnexpectedShutdown(this);
+ } else {
+ DestroyProcess();
+ }
+
+ // Release the actor.
+ SocketProcessParent::Destroy(std::move(mSocketProcessParent));
+ MOZ_ASSERT(!mSocketProcessParent);
+}
+
+void SocketProcessHost::DestroyProcess() {
+ {
+ MonitorAutoLock lock(mMonitor);
+ mTaskFactory.reset();
+ }
+
+ GetCurrentSerialEventTarget()->Dispatch(NS_NewRunnableFunction(
+ "DestroySocketProcessRunnable", [this] { Destroy(); }));
+}
+
+#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
+bool SocketProcessHost::FillMacSandboxInfo(MacSandboxInfo& aInfo) {
+ GeckoChildProcessHost::FillMacSandboxInfo(aInfo);
+ if (!aInfo.shouldLog && PR_GetEnv("MOZ_SANDBOX_SOCKET_PROCESS_LOGGING")) {
+ aInfo.shouldLog = true;
+ }
+ return true;
+}
+
+/* static */
+MacSandboxType SocketProcessHost::GetMacSandboxType() {
+ return MacSandboxType_Socket;
+}
+#endif
+
+//-----------------------------------------------------------------------------
+// SocketProcessMemoryReporter
+//-----------------------------------------------------------------------------
+
+bool SocketProcessMemoryReporter::IsAlive() const {
+ MOZ_ASSERT(gIOService);
+
+ if (!gIOService->mSocketProcess) {
+ return false;
+ }
+
+ return gIOService->mSocketProcess->IsConnected();
+}
+
+bool SocketProcessMemoryReporter::SendRequestMemoryReport(
+ const uint32_t& aGeneration, const bool& aAnonymize,
+ const bool& aMinimizeMemoryUsage,
+ const Maybe<ipc::FileDescriptor>& aDMDFile) {
+ MOZ_ASSERT(gIOService);
+
+ if (!gIOService->mSocketProcess) {
+ return false;
+ }
+
+ SocketProcessParent* actor = gIOService->mSocketProcess->GetActor();
+ if (!actor) {
+ return false;
+ }
+
+ return actor->SendRequestMemoryReport(aGeneration, aAnonymize,
+ aMinimizeMemoryUsage, aDMDFile);
+}
+
+int32_t SocketProcessMemoryReporter::Pid() const {
+ MOZ_ASSERT(gIOService);
+ return gIOService->SocketProcessPid();
+}
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/ipc/SocketProcessHost.h b/netwerk/ipc/SocketProcessHost.h
new file mode 100644
index 0000000000..44648b7fe7
--- /dev/null
+++ b/netwerk/ipc/SocketProcessHost.h
@@ -0,0 +1,150 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_net_SocketProcessHost_h
+#define mozilla_net_SocketProcessHost_h
+
+#include "mozilla/Maybe.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/ipc/GeckoChildProcessHost.h"
+#include "mozilla/MemoryReportingProcess.h"
+#include "mozilla/ipc/TaskFactory.h"
+
+namespace mozilla {
+
+#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
+class SandboxBroker;
+#endif
+
+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;
+ };
+
+ explicit SocketProcessHost(Listener* listener);
+
+ // 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(base::ProcessId peer_pid) override;
+
+#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
+ // Return the sandbox type to be used with this process type.
+ static MacSandboxType GetMacSandboxType();
+#endif
+
+ private:
+ ~SocketProcessHost();
+
+ // Called on the main thread.
+ void OnChannelConnectedTask();
+
+ // 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();
+
+#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
+ static bool sLaunchWithMacSandbox;
+
+ // Sandbox the Socket process at launch for all instances
+ bool IsMacSandboxLaunchEnabled() override { return sLaunchWithMacSandbox; }
+
+ // Override so we can turn on Socket process-specific sandbox logging
+ bool FillMacSandboxInfo(MacSandboxInfo& aInfo) override;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(SocketProcessHost);
+
+ RefPtr<Listener> mListener;
+ mozilla::Maybe<mozilla::ipc::TaskFactory<SocketProcessHost>> mTaskFactory;
+
+ enum class LaunchPhase { Unlaunched, Waiting, Complete };
+ LaunchPhase mLaunchPhase;
+
+ RefPtr<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;
+
+#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
+ UniquePtr<SandboxBroker> mSandboxBroker;
+#endif
+};
+
+class SocketProcessMemoryReporter : public MemoryReportingProcess {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SocketProcessMemoryReporter, override)
+
+ SocketProcessMemoryReporter() = default;
+
+ bool IsAlive() const override;
+
+ bool SendRequestMemoryReport(
+ const uint32_t& aGeneration, const bool& aAnonymize,
+ const bool& aMinimizeMemoryUsage,
+ const Maybe<mozilla::ipc::FileDescriptor>& aDMDFile) override;
+
+ int32_t Pid() const override;
+
+ protected:
+ virtual ~SocketProcessMemoryReporter() = default;
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozilla_net_SocketProcessHost_h
diff --git a/netwerk/ipc/SocketProcessImpl.cpp b/netwerk/ipc/SocketProcessImpl.cpp
new file mode 100644
index 0000000000..a863d4a875
--- /dev/null
+++ b/netwerk/ipc/SocketProcessImpl.cpp
@@ -0,0 +1,76 @@
+/* -*- 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/BackgroundHangMonitor.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/GeckoArgs.h"
+#include "mozilla/ipc/ProcessUtils.h"
+#include "mozilla/ipc/IOThreadChild.h"
+
+#if defined(XP_WIN) && defined(MOZ_SANDBOX)
+# include "mozilla/sandboxTarget.h"
+#elif defined(__OpenBSD__) && defined(MOZ_SANDBOX)
+# include "mozilla/SandboxSettings.h"
+# include "prlink.h"
+#endif
+
+#ifdef XP_UNIX
+# include <unistd.h> // For sleep().
+#endif
+
+using mozilla::ipc::IOThreadChild;
+
+namespace mozilla {
+namespace net {
+
+LazyLogModule gSocketProcessLog("socketprocess");
+
+SocketProcessImpl::~SocketProcessImpl() = default;
+
+bool SocketProcessImpl::Init(int aArgc, char* aArgv[]) {
+#ifdef XP_UNIX
+ if (PR_GetEnv("MOZ_DEBUG_SOCKET_PROCESS")) {
+ printf_stderr("\n\nSOCKETPROCESSnSOCKETPROCESS\n debug me @ %d\n\n",
+ base::GetCurrentProcId());
+ sleep(30);
+ }
+#endif
+#if defined(MOZ_SANDBOX) && defined(XP_WIN)
+ LoadLibraryW(L"nss3.dll");
+ LoadLibraryW(L"softokn3.dll");
+ LoadLibraryW(L"freebl3.dll");
+ LoadLibraryW(L"ipcclientcerts.dll");
+ LoadLibraryW(L"winmm.dll");
+ mozilla::SandboxTarget::Instance()->StartSandbox();
+#elif defined(__OpenBSD__) && defined(MOZ_SANDBOX)
+ PR_LoadLibrary("libnss3.so");
+ PR_LoadLibrary("libsoftokn3.so");
+ PR_LoadLibrary("libfreebl3.so");
+ PR_LoadLibrary("libipcclientcerts.so");
+ StartOpenBSDSandbox(GeckoProcessType_Socket);
+#endif
+
+ Maybe<const char*> parentBuildID =
+ geckoargs::sParentBuildID.Get(aArgc, aArgv);
+ if (parentBuildID.isNothing()) {
+ return false;
+ }
+
+ if (!ProcessChild::InitPrefs(aArgc, aArgv)) {
+ return false;
+ }
+
+ return mSocketProcessChild->Init(TakeInitialEndpoint(), *parentBuildID);
+}
+
+void SocketProcessImpl::CleanUp() { mSocketProcessChild->CleanUp(); }
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/ipc/SocketProcessImpl.h b/netwerk/ipc/SocketProcessImpl.h
new file mode 100644
index 0000000000..c471ca2834
--- /dev/null
+++ b/netwerk/ipc/SocketProcessImpl.h
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_net_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:
+ using ProcessChild = mozilla::ipc::ProcessChild;
+
+ public:
+ using ProcessChild::ProcessChild;
+ ~SocketProcessImpl();
+
+ bool Init(int aArgc, char* aArgv[]) override;
+ void CleanUp() override;
+
+ private:
+ RefPtr<SocketProcessChild> mSocketProcessChild = new SocketProcessChild;
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozilla_net_SocketProcessImpl_h
diff --git a/netwerk/ipc/SocketProcessLogging.h b/netwerk/ipc/SocketProcessLogging.h
new file mode 100644
index 0000000000..169de4986d
--- /dev/null
+++ b/netwerk/ipc/SocketProcessLogging.h
@@ -0,0 +1,20 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_SocketProcessLogging_h
+#define mozilla_SocketProcessLogging_h
+
+#include "mozilla/Logging.h"
+
+namespace mozilla {
+namespace net {
+extern LazyLogModule gSocketProcessLog;
+}
+} // namespace mozilla
+
+#define LOG(msg) MOZ_LOG(gSocketProcessLog, mozilla::LogLevel::Debug, msg)
+#define LOG_ENABLED() MOZ_LOG_TEST(gSocketProcessLog, mozilla::LogLevel::Debug)
+
+#endif // mozilla_SocketProcessLogging_h
diff --git a/netwerk/ipc/SocketProcessParent.cpp b/netwerk/ipc/SocketProcessParent.cpp
new file mode 100644
index 0000000000..6b1ccfb1ad
--- /dev/null
+++ b/netwerk/ipc/SocketProcessParent.cpp
@@ -0,0 +1,364 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "SocketProcessParent.h"
+#include "SocketProcessLogging.h"
+
+#include "AltServiceParent.h"
+#include "CachePushChecker.h"
+#include "HttpTransactionParent.h"
+#include "SocketProcessHost.h"
+#include "TLSClientAuthCertSelection.h"
+#include "mozilla/Components.h"
+#include "mozilla/dom/MemoryReportRequest.h"
+#include "mozilla/FOGIPC.h"
+#include "mozilla/net/DNSRequestParent.h"
+#include "mozilla/net/ProxyConfigLookupParent.h"
+#include "mozilla/net/SocketProcessBackgroundParent.h"
+#include "mozilla/RemoteLazyInputStreamParent.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/TelemetryIPC.h"
+#include "nsIConsoleService.h"
+#include "nsIHttpActivityObserver.h"
+#include "nsIObserverService.h"
+#include "nsNSSCertificate.h"
+#include "nsNSSComponent.h"
+#include "nsIOService.h"
+#include "nsHttpHandler.h"
+#include "nsHttpConnectionInfo.h"
+#include "secerr.h"
+#ifdef MOZ_WEBRTC
+# include "mozilla/dom/ContentProcessManager.h"
+# include "mozilla/dom/BrowserParent.h"
+# include "mozilla/net/WebrtcTCPSocketParent.h"
+#endif
+#if defined(MOZ_WIDGET_ANDROID)
+# include "mozilla/java/GeckoProcessManagerWrappers.h"
+# include "mozilla/java/GeckoProcessTypeWrappers.h"
+#endif // defined(MOZ_WIDGET_ANDROID)
+#if defined(XP_WIN)
+# include "mozilla/WinDllServices.h"
+#endif
+
+namespace mozilla {
+namespace net {
+
+static SocketProcessParent* sSocketProcessParent;
+
+SocketProcessParent::SocketProcessParent(SocketProcessHost* aHost)
+ : mHost(aHost) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(mHost);
+
+ MOZ_COUNT_CTOR(SocketProcessParent);
+ sSocketProcessParent = this;
+}
+
+SocketProcessParent::~SocketProcessParent() {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ MOZ_COUNT_DTOR(SocketProcessParent);
+ sSocketProcessParent = nullptr;
+}
+
+/* static */
+SocketProcessParent* SocketProcessParent::GetSingleton() {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ return sSocketProcessParent;
+}
+
+void SocketProcessParent::ActorDestroy(ActorDestroyReason aWhy) {
+#if defined(MOZ_WIDGET_ANDROID)
+ nsCOMPtr<nsIEventTarget> launcherThread(ipc::GetIPCLauncher());
+ MOZ_ASSERT(launcherThread);
+
+ auto procType = java::GeckoProcessType::SOCKET();
+ auto selector =
+ java::GeckoProcessManager::Selector::New(procType, OtherPid());
+
+ launcherThread->Dispatch(NS_NewRunnableFunction(
+ "SocketProcessParent::ActorDestroy",
+ [selector = java::GeckoProcessManager::Selector::GlobalRef(selector)]() {
+ java::GeckoProcessManager::ShutdownProcess(selector);
+ }));
+#endif // defined(MOZ_WIDGET_ANDROID)
+
+ if (aWhy == AbnormalShutdown) {
+ GenerateCrashReport(OtherPid());
+ MaybeTerminateProcess();
+ }
+
+ if (mHost) {
+ mHost->OnChannelClosed();
+ }
+}
+
+bool SocketProcessParent::SendRequestMemoryReport(
+ const uint32_t& aGeneration, const bool& aAnonymize,
+ const bool& aMinimizeMemoryUsage,
+ const Maybe<ipc::FileDescriptor>& aDMDFile) {
+ mMemoryReportRequest = MakeUnique<dom::MemoryReportRequestHost>(aGeneration);
+
+ PSocketProcessParent::SendRequestMemoryReport(
+ aGeneration, aAnonymize, aMinimizeMemoryUsage, aDMDFile,
+ [&](const uint32_t& aGeneration2) {
+ MOZ_ASSERT(gIOService);
+ if (!gIOService->SocketProcess()) {
+ return;
+ }
+ SocketProcessParent* actor = gIOService->SocketProcess()->GetActor();
+ if (!actor) {
+ return;
+ }
+ if (actor->mMemoryReportRequest) {
+ actor->mMemoryReportRequest->Finish(aGeneration2);
+ actor->mMemoryReportRequest = nullptr;
+ }
+ },
+ [&](mozilla::ipc::ResponseRejectReason) {
+ MOZ_ASSERT(gIOService);
+ if (!gIOService->SocketProcess()) {
+ return;
+ }
+ SocketProcessParent* actor = gIOService->SocketProcess()->GetActor();
+ if (!actor) {
+ return;
+ }
+ actor->mMemoryReportRequest = nullptr;
+ });
+
+ return true;
+}
+
+mozilla::ipc::IPCResult SocketProcessParent::RecvAddMemoryReport(
+ const MemoryReport& aReport) {
+ if (mMemoryReportRequest) {
+ mMemoryReportRequest->RecvReport(aReport);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult SocketProcessParent::RecvAccumulateChildHistograms(
+ nsTArray<HistogramAccumulation>&& aAccumulations) {
+ TelemetryIPC::AccumulateChildHistograms(Telemetry::ProcessID::Socket,
+ aAccumulations);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult SocketProcessParent::RecvAccumulateChildKeyedHistograms(
+ nsTArray<KeyedHistogramAccumulation>&& aAccumulations) {
+ TelemetryIPC::AccumulateChildKeyedHistograms(Telemetry::ProcessID::Socket,
+ aAccumulations);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult SocketProcessParent::RecvUpdateChildScalars(
+ nsTArray<ScalarAction>&& aScalarActions) {
+ TelemetryIPC::UpdateChildScalars(Telemetry::ProcessID::Socket,
+ aScalarActions);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult SocketProcessParent::RecvUpdateChildKeyedScalars(
+ nsTArray<KeyedScalarAction>&& aScalarActions) {
+ TelemetryIPC::UpdateChildKeyedScalars(Telemetry::ProcessID::Socket,
+ aScalarActions);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult SocketProcessParent::RecvRecordChildEvents(
+ nsTArray<mozilla::Telemetry::ChildEventData>&& aEvents) {
+ TelemetryIPC::RecordChildEvents(Telemetry::ProcessID::Socket, aEvents);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult SocketProcessParent::RecvRecordDiscardedData(
+ const mozilla::Telemetry::DiscardedData& aDiscardedData) {
+ TelemetryIPC::RecordDiscardedData(Telemetry::ProcessID::Socket,
+ aDiscardedData);
+ return IPC_OK();
+}
+
+PWebrtcTCPSocketParent* SocketProcessParent::AllocPWebrtcTCPSocketParent(
+ const Maybe<TabId>& aTabId) {
+#ifdef MOZ_WEBRTC
+ WebrtcTCPSocketParent* parent = new WebrtcTCPSocketParent(aTabId);
+ parent->AddRef();
+ return parent;
+#else
+ return nullptr;
+#endif
+}
+
+bool SocketProcessParent::DeallocPWebrtcTCPSocketParent(
+ PWebrtcTCPSocketParent* aActor) {
+#ifdef MOZ_WEBRTC
+ WebrtcTCPSocketParent* parent = static_cast<WebrtcTCPSocketParent*>(aActor);
+ parent->Release();
+#endif
+ return true;
+}
+
+already_AddRefed<PDNSRequestParent> SocketProcessParent::AllocPDNSRequestParent(
+ const nsACString& aHost, const nsACString& aTrrServer, const int32_t& port,
+ const uint16_t& aType, const OriginAttributes& aOriginAttributes,
+ const nsIDNSService::DNSFlags& aFlags) {
+ RefPtr<DNSRequestHandler> handler = new DNSRequestHandler();
+ RefPtr<DNSRequestParent> actor = new DNSRequestParent(handler);
+ return actor.forget();
+}
+
+mozilla::ipc::IPCResult SocketProcessParent::RecvPDNSRequestConstructor(
+ PDNSRequestParent* aActor, const nsACString& aHost,
+ const nsACString& aTrrServer, const int32_t& port, const uint16_t& aType,
+ const OriginAttributes& aOriginAttributes,
+ const nsIDNSService::DNSFlags& aFlags) {
+ RefPtr<DNSRequestParent> actor = static_cast<DNSRequestParent*>(aActor);
+ RefPtr<DNSRequestHandler> handler =
+ actor->GetDNSRequest()->AsDNSRequestHandler();
+ handler->DoAsyncResolve(aHost, aTrrServer, port, aType, aOriginAttributes,
+ aFlags);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult SocketProcessParent::RecvObserveHttpActivity(
+ const HttpActivityArgs& aArgs, const uint32_t& aActivityType,
+ const uint32_t& aActivitySubtype, const PRTime& aTimestamp,
+ const uint64_t& aExtraSizeData, const nsACString& aExtraStringData) {
+ nsCOMPtr<nsIHttpActivityDistributor> activityDistributor =
+ components::HttpActivityDistributor::Service();
+ MOZ_ASSERT(activityDistributor);
+
+ Unused << activityDistributor->ObserveActivityWithArgs(
+ aArgs, aActivityType, aActivitySubtype, aTimestamp, aExtraSizeData,
+ aExtraStringData);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult SocketProcessParent::RecvInitSocketBackground(
+ Endpoint<PSocketProcessBackgroundParent>&& aEndpoint) {
+ if (!aEndpoint.IsValid()) {
+ return IPC_FAIL(this, "Invalid endpoint");
+ }
+
+ nsCOMPtr<nsISerialEventTarget> transportQueue;
+ if (NS_FAILED(NS_CreateBackgroundTaskQueue("SocketBackgroundParentQueue",
+ getter_AddRefs(transportQueue)))) {
+ return IPC_FAIL(this, "NS_CreateBackgroundTaskQueue failed");
+ }
+
+ transportQueue->Dispatch(
+ NS_NewRunnableFunction("BindSocketBackgroundParent",
+ [endpoint = std::move(aEndpoint)]() mutable {
+ RefPtr<SocketProcessBackgroundParent> parent =
+ new SocketProcessBackgroundParent();
+ endpoint.Bind(parent);
+ }));
+ return IPC_OK();
+}
+
+already_AddRefed<PAltServiceParent>
+SocketProcessParent::AllocPAltServiceParent() {
+ RefPtr<AltServiceParent> actor = new AltServiceParent();
+ return actor.forget();
+}
+
+already_AddRefed<PProxyConfigLookupParent>
+SocketProcessParent::AllocPProxyConfigLookupParent(
+ nsIURI* aURI, const uint32_t& aProxyResolveFlags) {
+ RefPtr<ProxyConfigLookupParent> actor =
+ new ProxyConfigLookupParent(aURI, aProxyResolveFlags);
+ return actor.forget();
+}
+
+mozilla::ipc::IPCResult SocketProcessParent::RecvPProxyConfigLookupConstructor(
+ PProxyConfigLookupParent* aActor, nsIURI* aURI,
+ const uint32_t& aProxyResolveFlags) {
+ static_cast<ProxyConfigLookupParent*>(aActor)->DoProxyLookup();
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult SocketProcessParent::RecvCachePushCheck(
+ nsIURI* aPushedURL, OriginAttributes&& aOriginAttributes,
+ nsCString&& aRequestString, CachePushCheckResolver&& aResolver) {
+ RefPtr<CachePushChecker> checker = new CachePushChecker(
+ aPushedURL, aOriginAttributes, aRequestString, aResolver);
+ if (NS_FAILED(checker->DoCheck())) {
+ aResolver(false);
+ }
+ return IPC_OK();
+}
+
+// To ensure that IPDL is finished before SocketParent gets deleted.
+class DeferredDeleteSocketProcessParent : public Runnable {
+ public:
+ explicit DeferredDeleteSocketProcessParent(
+ RefPtr<SocketProcessParent>&& aParent)
+ : Runnable("net::DeferredDeleteSocketProcessParent"),
+ mParent(std::move(aParent)) {}
+
+ NS_IMETHODIMP Run() override { return NS_OK; }
+
+ private:
+ RefPtr<SocketProcessParent> mParent;
+};
+
+/* static */
+void SocketProcessParent::Destroy(RefPtr<SocketProcessParent>&& aParent) {
+ NS_DispatchToMainThread(
+ new DeferredDeleteSocketProcessParent(std::move(aParent)));
+}
+
+mozilla::ipc::IPCResult SocketProcessParent::RecvExcludeHttp2OrHttp3(
+ const HttpConnectionInfoCloneArgs& aArgs) {
+ RefPtr<nsHttpConnectionInfo> cinfo =
+ nsHttpConnectionInfo::DeserializeHttpConnectionInfoCloneArgs(aArgs);
+ if (!cinfo) {
+ MOZ_ASSERT(false, "failed to deserizlize http connection info");
+ return IPC_OK();
+ }
+
+ 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();
+}
+
+mozilla::ipc::IPCResult SocketProcessParent::RecvFOGData(ByteBuf&& aBuf) {
+ glean::FOGData(std::move(aBuf));
+ return IPC_OK();
+}
+
+#if defined(XP_WIN)
+mozilla::ipc::IPCResult SocketProcessParent::RecvGetModulesTrust(
+ ModulePaths&& aModPaths, bool aRunAtNormalPriority,
+ GetModulesTrustResolver&& aResolver) {
+ RefPtr<DllServices> dllSvc(DllServices::Get());
+ dllSvc->GetModulesTrust(std::move(aModPaths), aRunAtNormalPriority)
+ ->Then(
+ GetMainThreadSerialEventTarget(), __func__,
+ [aResolver](ModulesMapResult&& aResult) {
+ aResolver(Some(ModulesMapResult(std::move(aResult))));
+ },
+ [aResolver](nsresult aRv) { aResolver(Nothing()); });
+ return IPC_OK();
+}
+#endif // defined(XP_WIN)
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/ipc/SocketProcessParent.h b/netwerk/ipc/SocketProcessParent.h
new file mode 100644
index 0000000000..732e431e35
--- /dev/null
+++ b/netwerk/ipc/SocketProcessParent.h
@@ -0,0 +1,117 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_net_SocketProcessParent_h
+#define mozilla_net_SocketProcessParent_h
+
+#include "mozilla/UniquePtr.h"
+#include "mozilla/ipc/CrashReporterHelper.h"
+#include "mozilla/ipc/InputStreamUtils.h"
+#include "mozilla/net/PSocketProcessParent.h"
+
+namespace mozilla {
+
+namespace dom {
+class MemoryReport;
+class MemoryReportRequestHost;
+} // namespace dom
+
+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 ipc::CrashReporterHelper<GeckoProcessType_Socket> {
+ public:
+ friend class SocketProcessHost;
+
+ NS_INLINE_DECL_REFCOUNTING(SocketProcessParent, final)
+
+ explicit SocketProcessParent(SocketProcessHost* aHost);
+
+ static SocketProcessParent* GetSingleton();
+
+ mozilla::ipc::IPCResult RecvAddMemoryReport(const MemoryReport& aReport);
+ mozilla::ipc::IPCResult RecvAccumulateChildHistograms(
+ nsTArray<HistogramAccumulation>&& aAccumulations);
+ mozilla::ipc::IPCResult RecvAccumulateChildKeyedHistograms(
+ nsTArray<KeyedHistogramAccumulation>&& aAccumulations);
+ mozilla::ipc::IPCResult RecvUpdateChildScalars(
+ nsTArray<ScalarAction>&& aScalarActions);
+ mozilla::ipc::IPCResult RecvUpdateChildKeyedScalars(
+ nsTArray<KeyedScalarAction>&& aScalarActions);
+ mozilla::ipc::IPCResult RecvRecordChildEvents(
+ nsTArray<ChildEventData>&& events);
+ mozilla::ipc::IPCResult RecvRecordDiscardedData(
+ const DiscardedData& aDiscardedData);
+
+ PWebrtcTCPSocketParent* AllocPWebrtcTCPSocketParent(
+ const Maybe<TabId>& aTabId);
+ bool DeallocPWebrtcTCPSocketParent(PWebrtcTCPSocketParent* aActor);
+ already_AddRefed<PDNSRequestParent> AllocPDNSRequestParent(
+ const nsACString& aHost, const nsACString& aTrrServer,
+ const int32_t& port, const uint16_t& aType,
+ const OriginAttributes& aOriginAttributes,
+ const nsIDNSService::DNSFlags& aFlags);
+ virtual mozilla::ipc::IPCResult RecvPDNSRequestConstructor(
+ PDNSRequestParent* actor, const nsACString& aHost,
+ const nsACString& trrServer, const int32_t& port, const uint16_t& type,
+ const OriginAttributes& aOriginAttributes,
+ const nsIDNSService::DNSFlags& flags) override;
+
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+ bool SendRequestMemoryReport(const uint32_t& aGeneration,
+ const bool& aAnonymize,
+ const bool& aMinimizeMemoryUsage,
+ const Maybe<ipc::FileDescriptor>& aDMDFile);
+
+ mozilla::ipc::IPCResult RecvObserveHttpActivity(
+ const HttpActivityArgs& aArgs, const uint32_t& aActivityType,
+ const uint32_t& aActivitySubtype, const PRTime& aTimestamp,
+ const uint64_t& aExtraSizeData, const nsACString& aExtraStringData);
+
+ mozilla::ipc::IPCResult RecvInitSocketBackground(
+ Endpoint<PSocketProcessBackgroundParent>&& aEndpoint);
+
+ already_AddRefed<PAltServiceParent> AllocPAltServiceParent();
+
+ already_AddRefed<PProxyConfigLookupParent> AllocPProxyConfigLookupParent(
+ nsIURI* aURI, const uint32_t& aProxyResolveFlags);
+ mozilla::ipc::IPCResult RecvPProxyConfigLookupConstructor(
+ PProxyConfigLookupParent* aActor, nsIURI* aURI,
+ const uint32_t& aProxyResolveFlags) override;
+
+ mozilla::ipc::IPCResult RecvCachePushCheck(
+ nsIURI* aPushedURL, OriginAttributes&& aOriginAttributes,
+ nsCString&& aRequestString, CachePushCheckResolver&& aResolver);
+
+ mozilla::ipc::IPCResult RecvExcludeHttp2OrHttp3(
+ const HttpConnectionInfoCloneArgs& aArgs);
+ mozilla::ipc::IPCResult RecvOnConsoleMessage(const nsString& aMessage);
+
+ mozilla::ipc::IPCResult RecvFOGData(ByteBuf&& aBuf);
+
+#if defined(XP_WIN)
+ mozilla::ipc::IPCResult RecvGetModulesTrust(
+ ModulePaths&& aModPaths, bool aRunAtNormalPriority,
+ GetModulesTrustResolver&& aResolver);
+#endif // defined(XP_WIN)
+
+ private:
+ ~SocketProcessParent();
+
+ SocketProcessHost* mHost;
+ UniquePtr<dom::MemoryReportRequestHost> mMemoryReportRequest;
+
+ static void Destroy(RefPtr<SocketProcessParent>&& aParent);
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozilla_net_SocketProcessParent_h
diff --git a/netwerk/ipc/moz.build b/netwerk/ipc/moz.build
new file mode 100644
index 0000000000..ee87862785
--- /dev/null
+++ b/netwerk/ipc/moz.build
@@ -0,0 +1,117 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+EXPORTS.mozilla.net += [
+ "ChannelEventQueue.h",
+ "DocumentChannel.h",
+ "DocumentChannelChild.h",
+ "DocumentChannelParent.h",
+ "DocumentLoadListener.h",
+ "InputChannelThrottleQueueChild.h",
+ "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",
+ "SocketProcessBackgroundChild.h",
+ "SocketProcessBackgroundParent.h",
+ "SocketProcessBridgeChild.h",
+ "SocketProcessBridgeParent.h",
+ "SocketProcessChild.h",
+ "SocketProcessHost.h",
+ "SocketProcessImpl.h",
+ "SocketProcessParent.h",
+]
+
+UNIFIED_SOURCES += [
+ "ChannelEventQueue.cpp",
+ "DocumentChannel.cpp",
+ "DocumentChannelChild.cpp",
+ "DocumentChannelParent.cpp",
+ "DocumentLoadListener.cpp",
+ "InputChannelThrottleQueueChild.cpp",
+ "InputChannelThrottleQueueParent.cpp",
+ "NeckoChild.cpp",
+ "NeckoCommon.cpp",
+ "NeckoParent.cpp",
+ "NeckoTargetHolder.cpp",
+ "ParentChannelWrapper.cpp",
+ "ParentProcessDocumentChannel.cpp",
+ "ProxyConfigLookup.cpp",
+ "ProxyConfigLookupChild.cpp",
+ "ProxyConfigLookupParent.cpp",
+ "SocketProcessBackgroundChild.cpp",
+ "SocketProcessBackgroundParent.cpp",
+ "SocketProcessBridgeChild.cpp",
+ "SocketProcessBridgeParent.cpp",
+ "SocketProcessChild.cpp",
+ "SocketProcessHost.cpp",
+ "SocketProcessImpl.cpp",
+ "SocketProcessParent.cpp",
+]
+
+SOURCES += [
+ "ProxyAutoConfigChild.cpp",
+ "ProxyAutoConfigParent.cpp",
+]
+
+
+PREPROCESSED_IPDL_SOURCES += [
+ "PNecko.ipdl",
+ "PSocketProcess.ipdl",
+ "PSocketProcessBridge.ipdl",
+]
+
+IPDL_SOURCES = [
+ "NeckoChannelParams.ipdlh",
+ "PDataChannel.ipdl",
+ "PDocumentChannel.ipdl",
+ "PFileChannel.ipdl",
+ "PInputChannelThrottleQueue.ipdl",
+ "PProxyAutoConfig.ipdl",
+ "PProxyConfigLookup.ipdl",
+ "PSimpleChannel.ipdl",
+ "PSocketProcessBackground.ipdl",
+]
+
+# needed so --disable-webrtc builds work (yes, a bit messy)
+if not CONFIG["MOZ_WEBRTC"]:
+ IPDL_SOURCES += [
+ "../../dom/media/webrtc/transport/ipc/PStunAddrsRequest.ipdl",
+ "../../dom/media/webrtc/transport/ipc/PWebrtcTCPSocket.ipdl",
+ "../../dom/media/webrtc/transport/ipc/WebrtcProxyConfig.ipdlh",
+ ]
+ EXPORTS.mozilla.net += [
+ "../../dom/media/webrtc/transport/ipc/NrIceStunAddrMessageUtils.h",
+ "../../dom/media/webrtc/transport/ipc/PStunAddrsParams.h",
+ ]
+
+include("/ipc/chromium/chromium-config.mozbuild")
+
+FINAL_LIBRARY = "xul"
+
+LOCAL_INCLUDES += [
+ "/caps",
+ "/dom/base",
+ "/dom/media/webrtc/transport",
+ "/media/webrtc",
+ "/modules/libjar",
+ "/netwerk/base",
+ "/netwerk/protocol/http",
+ "/security/manager/ssl",
+ "/xpcom/threads",
+]
+
+# Add libFuzzer configuration directives
+include("/tools/fuzzing/libfuzzer-config.mozbuild")