summaryrefslogtreecommitdiffstats
path: root/netwerk/ipc
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /netwerk/ipc
parentInitial commit. (diff)
downloadfirefox-upstream.tar.xz
firefox-upstream.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--netwerk/ipc/ChannelEventQueue.cpp218
-rw-r--r--netwerk/ipc/ChannelEventQueue.h354
-rw-r--r--netwerk/ipc/DocumentChannel.cpp468
-rw-r--r--netwerk/ipc/DocumentChannel.h119
-rw-r--r--netwerk/ipc/DocumentChannelChild.cpp427
-rw-r--r--netwerk/ipc/DocumentChannelChild.h72
-rw-r--r--netwerk/ipc/DocumentChannelParent.cpp145
-rw-r--r--netwerk/ipc/DocumentChannelParent.h64
-rw-r--r--netwerk/ipc/DocumentLoadListener.cpp2544
-rw-r--r--netwerk/ipc/DocumentLoadListener.h575
-rw-r--r--netwerk/ipc/InputChannelThrottleQueueChild.cpp30
-rw-r--r--netwerk/ipc/InputChannelThrottleQueueChild.h33
-rw-r--r--netwerk/ipc/InputChannelThrottleQueueParent.cpp128
-rw-r--r--netwerk/ipc/InputChannelThrottleQueueParent.h51
-rw-r--r--netwerk/ipc/NeckoChannelParams.ipdlh546
-rw-r--r--netwerk/ipc/NeckoChild.cpp360
-rw-r--r--netwerk/ipc/NeckoChild.h96
-rw-r--r--netwerk/ipc/NeckoCommon.h116
-rw-r--r--netwerk/ipc/NeckoMessageUtils.h149
-rw-r--r--netwerk/ipc/NeckoParent.cpp922
-rw-r--r--netwerk/ipc/NeckoParent.h252
-rw-r--r--netwerk/ipc/NeckoTargetHolder.cpp38
-rw-r--r--netwerk/ipc/NeckoTargetHolder.h40
-rw-r--r--netwerk/ipc/PDataChannel.ipdl24
-rw-r--r--netwerk/ipc/PDocumentChannel.ipdl66
-rw-r--r--netwerk/ipc/PFileChannel.ipdl27
-rw-r--r--netwerk/ipc/PInputChannelThrottleQueue.ipdl25
-rw-r--r--netwerk/ipc/PNecko.ipdl190
-rw-r--r--netwerk/ipc/PProxyConfigLookup.ipdl21
-rw-r--r--netwerk/ipc/PSimpleChannel.ipdl24
-rw-r--r--netwerk/ipc/PSocketProcess.ipdl182
-rw-r--r--netwerk/ipc/PSocketProcessBridge.ipdl30
-rw-r--r--netwerk/ipc/ParentChannelWrapper.cpp107
-rw-r--r--netwerk/ipc/ParentChannelWrapper.h39
-rw-r--r--netwerk/ipc/ParentProcessDocumentChannel.cpp300
-rw-r--r--netwerk/ipc/ParentProcessDocumentChannel.h55
-rw-r--r--netwerk/ipc/ProxyConfigLookup.cpp98
-rw-r--r--netwerk/ipc/ProxyConfigLookup.h43
-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/SocketProcessBridgeChild.cpp180
-rw-r--r--netwerk/ipc/SocketProcessBridgeChild.h53
-rw-r--r--netwerk/ipc/SocketProcessBridgeParent.cpp65
-rw-r--r--netwerk/ipc/SocketProcessBridgeParent.h44
-rw-r--r--netwerk/ipc/SocketProcessChild.cpp618
-rw-r--r--netwerk/ipc/SocketProcessChild.h163
-rw-r--r--netwerk/ipc/SocketProcessHost.cpp292
-rw-r--r--netwerk/ipc/SocketProcessHost.h151
-rw-r--r--netwerk/ipc/SocketProcessImpl.cpp108
-rw-r--r--netwerk/ipc/SocketProcessImpl.h36
-rw-r--r--netwerk/ipc/SocketProcessLogging.h20
-rw-r--r--netwerk/ipc/SocketProcessParent.cpp429
-rw-r--r--netwerk/ipc/SocketProcessParent.h132
-rw-r--r--netwerk/ipc/moz.build99
56 files changed, 11528 insertions, 0 deletions
diff --git a/netwerk/ipc/ChannelEventQueue.cpp b/netwerk/ipc/ChannelEventQueue.cpp
new file mode 100644
index 0000000000..0aa4cf71f4
--- /dev/null
+++ b/netwerk/ipc/ChannelEventQueue.cpp
@@ -0,0 +1,218 @@
+/* -*- 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(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;
+ };
+
+ // 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;
+ }
+
+ 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..75df2d15a0
--- /dev/null
+++ b/netwerk/ipc/ChannelEventQueue.h
@@ -0,0 +1,354 @@
+/* -*- 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(GetMainThreadEventTarget());
+ }
+};
+
+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.
+
+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();
+
+#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
+ bool IsEmpty() const { return mEventQueue.IsEmpty(); }
+#endif
+
+ private:
+ // Private destructor, to discourage deletion outside of Release():
+ ~ChannelEventQueue() = default;
+
+ void SuspendInternal();
+ void ResumeInternal();
+
+ bool MaybeSuspendIfEventsAreSuppressed();
+
+ inline void MaybeFlushQueue();
+ void FlushQueue();
+ inline void CompleteResume();
+
+ ChannelEvent* TakeEvent();
+
+ nsTArray<UniquePtr<ChannelEvent>> mEventQueue;
+
+ uint32_t mSuspendCount;
+ bool mSuspended;
+ uint32_t mForcedCount; // Support ForcedQueueing on multiple thread.
+ bool mFlushing;
+
+ // Whether the queue is associated with an XHR. This is lazily instantiated
+ // the first time it is needed.
+ bool mHasCheckedForXMLHttpRequest;
+ bool mForXMLHttpRequest;
+
+ // Keep ptr to avoid refcount cycle: only grab ref during flushing.
+ nsISupports* mOwner;
+
+ // For atomic mEventQueue operation and state update
+ Mutex mMutex;
+
+ // To guarantee event execution order among threads
+ RecursiveMutex mRunningMutex;
+
+ 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(mOwner);
+ Unused << kungFuDeathGrip; // Not used in this function
+
+ // 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);
+
+ bool enqueue = !!mForcedCount || mSuspended || mFlushing ||
+ !mEventQueue.IsEmpty() ||
+ MaybeSuspendIfEventsAreSuppressed();
+
+ 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.
+ SuspendInternal();
+ mEventQueue.AppendElement(std::move(event));
+ ResumeInternal();
+ return;
+ }
+ }
+
+ MOZ_RELEASE_ASSERT(!aAssertionWhenNotQueued);
+ 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), 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..4dfa0eddf8
--- /dev/null
+++ b/netwerk/ipc/DocumentChannel.cpp
@@ -0,0 +1,468 @@
+/* -*- 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 aIsXFOError)
+ : mAsyncOpenTime(TimeStamp::Now()),
+ mLoadState(aLoadState),
+ mCacheKey(aCacheKey),
+ mLoadFlags(aLoadFlags),
+ mURI(aLoadState->URI()),
+ mLoadInfo(aLoadInfo),
+ mUriModified(aUriModified),
+ mIsXFOError(aIsXFOError) {
+ 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);
+}
+
+// Changes here should also be made in
+// E10SUtils.documentChannelPermittedForURI().
+static bool URIUsesDocChannel(nsIURI* aURI) {
+ if (SchemeIsJavascript(aURI)) {
+ return false;
+ }
+
+ nsCString spec = aURI->GetSpecOrDefault();
+ return !spec.EqualsLiteral("about:printpreview") &&
+ !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 aIsXFOError) {
+ RefPtr<DocumentChannel> channel;
+ if (XRE_IsContentProcess()) {
+ channel = new DocumentChannelChild(aLoadState, aLoadInfo, aLoadFlags,
+ aCacheKey, aUriModified, aIsXFOError);
+ } else {
+ channel =
+ new ParentProcessDocumentChannel(aLoadState, aLoadInfo, aLoadFlags,
+ aCacheKey, aUriModified, aIsXFOError);
+ }
+ 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::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("DocumentChannel::SetLoadFlags: Don't set flags after creation");
+}
+
+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(nsISupports** 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..03a84c7237
--- /dev/null
+++ b/netwerk/ipc/DocumentChannel.h
@@ -0,0 +1,119 @@
+/* -*- 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;
+ }
+
+ /**
+ * 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 aIsXFOError);
+ 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 aIsXFOError);
+
+ void ShutdownListeners(nsresult aStatusCode);
+ void DisconnectChildListeners(const nsresult& aStatus,
+ const nsresult& aLoadGroupStatus);
+ virtual void DeleteIPDL() {}
+
+ nsDocShell* GetDocShell();
+
+ virtual ~DocumentChannel() = default;
+
+ const TimeStamp mAsyncOpenTime;
+ 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;
+ // mIsXFOError is true if we're handling a load error and the status of the
+ // failed channel is NS_ERROR_XFO_VIOLATION.
+ bool mIsXFOError = 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..527e27987a
--- /dev/null
+++ b/netwerk/ipc/DocumentChannelChild.cpp
@@ -0,0 +1,427 @@
+/* -*- 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/extensions/StreamFilterParent.h"
+#include "mozilla/net/HttpBaseChannel.h"
+#include "mozilla/net/NeckoChild.h"
+#include "mozilla/ScopeExit.h"
+#include "nsHashPropertyBag.h"
+#include "nsIHttpChannelInternal.h"
+#include "nsIObjectLoadingContent.h"
+#include "nsIWritablePropertyBag.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 aIsXFOError)
+ : DocumentChannel(aLoadState, aLoadInfo, aLoadFlags, aCacheKey,
+ aUriModified, aIsXFOError) {
+ 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;
+ }
+
+ DocumentChannelCreationArgs args;
+
+ args.loadState() = mLoadState->Serialize();
+ args.cacheKey() = mCacheKey;
+ args.channelId() = mChannelId;
+ args.asyncOpenTime() = mAsyncOpenTime;
+
+ Maybe<IPCClientInfo> ipcClientInfo;
+ if (mInitialClientInfo.isSome()) {
+ ipcClientInfo.emplace(mInitialClientInfo.ref().ToIPC());
+ }
+ args.initialClientInfo() = ipcClientInfo;
+
+ if (mTiming) {
+ args.timing() = Some(mTiming);
+ }
+
+ switch (mLoadInfo->GetExternalContentPolicyType()) {
+ case ExtContentPolicy::TYPE_DOCUMENT:
+ case ExtContentPolicy::TYPE_SUBDOCUMENT: {
+ DocumentCreationArgs docArgs;
+ docArgs.uriModified() = mUriModified;
+ docArgs.isXFOError() = mIsXFOError;
+
+ args.elementCreationArgs() = docArgs;
+ break;
+ }
+
+ case ExtContentPolicy::TYPE_OBJECT: {
+ ObjectCreationArgs objectArgs;
+ objectArgs.embedderInnerWindowId() = InnerWindowIDForExtantDoc(docShell);
+ objectArgs.loadFlags() = mLoadFlags;
+ objectArgs.contentPolicyType() = mLoadInfo->InternalContentPolicyType();
+ objectArgs.isUrgentStart() = UserActivation::IsHandlingUserInput();
+
+ args.elementCreationArgs() = 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;
+ }
+
+ gNeckoChild->SendPDocumentChannelConstructor(this, loadingContext, args);
+
+ mIsPending = true;
+ mWasOpened = true;
+ mListener = listener;
+
+ return NS_OK;
+}
+
+IPCResult DocumentChannelChild::RecvFailedAsyncOpen(
+ const nsresult& aStatusCode) {
+ ShutdownListeners(aStatusCode);
+ return IPC_OK();
+}
+
+IPCResult DocumentChannelChild::RecvDisconnectChildListeners(
+ const nsresult& aStatus, const nsresult& aLoadGroupStatus,
+ bool aSwitchedProcess) {
+ // 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 (!aSwitchedProcess) {
+ DisconnectChildListeners(aStatus, aLoadGroupStatus);
+ }
+ 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(), cspToInheritLoadingDocument, getter_AddRefs(loadInfo)));
+
+ mRedirectResolver = std::move(aResolve);
+
+ nsCOMPtr<nsIChannel> newChannel;
+ MOZ_ASSERT((aArgs.loadStateLoadFlags() &
+ 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);
+ }
+
+ // 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(), GetMainThreadEventTarget());
+
+ 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) {
+ if (mCanceled) {
+ return NS_OK;
+ }
+
+ mCanceled = true;
+ if (CanSend()) {
+ SendCancel(aStatusCode);
+ }
+
+ 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..b7a962b2ee
--- /dev/null
+++ b/netwerk/ipc/DocumentChannelChild.h
@@ -0,0 +1,72 @@
+/* -*- 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 aIsXFOError);
+
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_NSIASYNCVERIFYREDIRECTCALLBACK
+
+ NS_IMETHOD AsyncOpen(nsIStreamListener* aListener) override;
+ NS_IMETHOD Cancel(nsresult aStatusCode) 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;
+};
+
+} // 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..9eec9a1515
--- /dev/null
+++ b/netwerk/ipc/DocumentChannelParent.cpp
@@ -0,0 +1,145 @@
+/* -*- 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 "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 =
+ new nsDocShellLoadState(aArgs.loadState());
+ LOG(("DocumentChannelParent Init [this=%p, uri=%s]", this,
+ loadState->URI()->GetSpecOrDefault().get()));
+
+ RefPtr<DocumentLoadListener::OpenPromise> promise;
+ if (loadState->GetChannelInitialized()) {
+ promise = DocumentLoadListener::ClaimParentLoad(
+ getter_AddRefs(mDocumentLoadListener), loadState->GetLoadIdentifier());
+ }
+ 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().refOr(nullptr),
+ std::move(clientInfo), Some(docArgs.uriModified()),
+ Some(docArgs.isXFOError()), IProtocol::OtherPid(), &rv);
+ } else {
+ const ObjectCreationArgs& objectArgs = aArgs.elementCreationArgs();
+
+ promise = mDocumentLoadListener->OpenObject(
+ loadState, aArgs.cacheKey(), Some(aArgs.channelId()),
+ aArgs.asyncOpenTime(), aArgs.timing().refOr(nullptr),
+ std::move(clientInfo), objectArgs.embedderInnerWindowId(),
+ objectArgs.loadFlags(), objectArgs.contentPolicyType(),
+ objectArgs.isUrgentStart(), IProtocol::OtherPid(),
+ 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);
+ // 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.mSwitchedProcess);
+ }
+ 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) {
+ if (!CanSend()) {
+ return PDocumentChannelParent::RedirectToRealChannelPromise::
+ CreateAndReject(ResponseRejectReason::ChannelClosed, __func__);
+ }
+ RedirectToRealChannelArgs args;
+ mDocumentLoadListener->SerializeRedirectData(
+ args, false, aRedirectFlags, aLoadFlags,
+ static_cast<ContentParent*>(Manager()->Manager()));
+ 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..5bbdcca039
--- /dev/null
+++ b/netwerk/ipc/DocumentChannelParent.h
@@ -0,0 +1,64 @@
+/* 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 {
+
+/**
+ * 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
+ bool RecvCancel(const nsresult& aStatus) {
+ if (mDocumentLoadListener) {
+ mDocumentLoadListener->Cancel(aStatus);
+ }
+ return true;
+ }
+ void ActorDestroy(ActorDestroyReason aWhy) override {
+ if (mDocumentLoadListener) {
+ mDocumentLoadListener->Cancel(NS_BINDING_ABORTED);
+ }
+ }
+
+ private:
+ RefPtr<ObjectUpgradePromise> UpgradeObjectLoad() override;
+
+ RefPtr<PDocumentChannelParent::RedirectToRealChannelPromise>
+ RedirectToRealChannel(
+ nsTArray<ipc::Endpoint<extensions::PStreamFilterParent>>&&
+ aStreamFilterEndpoints,
+ uint32_t aRedirectFlags, uint32_t aLoadFlags);
+
+ 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..3d7611ba7a
--- /dev/null
+++ b/netwerk/ipc/DocumentLoadListener.cpp
@@ -0,0 +1,2544 @@
+/* -*- 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 "mozilla/AntiTrackingUtils.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/LoadInfo.h"
+#include "mozilla/MozPromiseInlines.h" // For MozPromise::FromDomPromise
+#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/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 "nsDocShell.h"
+#include "nsDocShellLoadState.h"
+#include "nsDocShellLoadTypes.h"
+#include "nsDOMNavigationTiming.h"
+#include "nsExternalHelperAppService.h"
+#include "nsHttpChannel.h"
+#include "nsIBrowser.h"
+#include "nsIE10SUtils.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 "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/RemoteWebProgress.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)
+
+using namespace mozilla::dom;
+
+namespace mozilla {
+namespace net {
+
+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->HasLoadFlags(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(),
+ securityFlags, sandboxFlags);
+ } else {
+ OriginAttributes attrs;
+ aBrowsingContext->GetOriginAttributes(attrs);
+ loadInfo = LoadInfo::CreateForDocument(aBrowsingContext,
+ aLoadState->TriggeringPrincipal(),
+ attrs, securityFlags, sandboxFlags);
+ }
+
+ loadInfo->SetTriggeringSandboxFlags(aLoadState->TriggeringSandboxFlags());
+ loadInfo->SetHasValidUserGestureActivation(
+ aLoadState->HasValidUserGestureActivation());
+
+ 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());
+
+ return loadInfo.forget();
+}
+
+static auto WebProgressForBrowsingContext(
+ CanonicalBrowsingContext* aBrowsingContext)
+ -> already_AddRefed<BrowsingContextWebProgress> {
+ return RefPtr<BrowsingContextWebProgress>(aBrowsingContext->GetWebProgress())
+ .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, mBrowsingContext->GetAllowPlugins());
+ 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)) {
+ 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_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,
+ aLoadingBrowsingContext->UsePrivateBrowsing());
+}
+
+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();
+
+ nsDocShell::InternalAddURIVisit(uri, previousURI, previousFlags,
+ responseStatus, browsingContext, widget,
+ mLoadStateLoadType);
+}
+
+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;
+}
+
+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,
+ base::ProcessId aPid, nsresult* aRv)
+ -> RefPtr<OpenPromise> {
+ auto* loadingContext = GetLoadingBrowsingContext();
+
+ MOZ_DIAGNOSTIC_ASSERT_IF(loadingContext->GetParent(),
+ loadingContext->GetParentWindowContext());
+
+ OriginAttributes attrs;
+ loadingContext->GetOriginAttributes(attrs);
+
+ mLoadIdentifier = aLoadState->GetLoadIdentifier();
+
+ 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;
+ }
+
+ auto* documentContext = GetDocumentBrowsingContext();
+ if (documentContext && 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,
+ mChannel);
+ if (!mLoadingSessionHistoryInfo) {
+ *aRv = NS_BINDING_ABORTED;
+ mParentChannelListener = nullptr;
+ mChannel = nullptr;
+ return nullptr;
+ }
+ }
+
+ 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);
+ }
+
+ RefPtr<nsHttpChannel> httpChannelImpl = do_QueryObject(mChannel);
+ if (httpChannelImpl) {
+ httpChannelImpl->SetWarningReporter(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->HasLoadFlags(
+ 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->LoadFlags(), 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);
+
+ mOtherPid = aPid;
+ mChannelCreationURI = aLoadState->URI();
+ mLoadStateLoadFlags = aLoadState->LoadFlags();
+ 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?");
+ }
+
+ *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> aIsXFOError, base::ProcessId aPid,
+ 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(aIsXFOError));
+
+ return Open(aLoadState, loadInfo, loadFlags, aCacheKey, aChannelId,
+ aAsyncOpenTime, aTiming, std::move(aInfo), false, aPid, 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,
+ base::ProcessId aPid, ObjectUpgradeHandler* aObjectUpgradeHandler,
+ nsresult* aRv) -> RefPtr<OpenPromise> {
+ LOG(("DocumentLoadListener [%p] OpenObject [uri=%s]", this,
+ aLoadState->URI()->GetSpecOrDefault().get()));
+
+ MOZ_ASSERT(!mIsDocumentLoad);
+
+ auto sandboxFlags = GetLoadingBrowsingContext()->GetSandboxFlags();
+
+ RefPtr<LoadInfo> loadInfo = CreateObjectLoadInfo(
+ aLoadState, aInnerWindowId, aContentPolicyType, sandboxFlags);
+
+ mObjectUpgradeHandler = aObjectUpgradeHandler;
+
+ return Open(aLoadState, loadInfo, aLoadFlags, aCacheKey, aChannelId,
+ aAsyncOpenTime, aTiming, std::move(aInfo), aUrgentStart, aPid,
+ 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;
+ }
+
+ if (nsCOMPtr<nsIContentSecurityPolicy> csp = aLoadState->Csp()) {
+ // Check CSP navigate-to
+ bool allowsNavigateTo = false;
+ nsresult rv = csp->GetAllowsNavigateTo(aLoadState->URI(),
+ aLoadState->IsFormSubmission(),
+ false, /* aWasRedirected */
+ false, /* aEnforceWhitelist */
+ &allowsNavigateTo);
+ if (NS_FAILED(rv) || !allowsNavigateTo) {
+ 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()->OtherPid(), &rv);
+}
+
+void DocumentLoadListener::FireStateChange(uint32_t aStateFlags,
+ nsresult aStatus) {
+ nsCOMPtr<nsIChannel> request = GetChannel();
+ nsCOMPtr<nsIWebProgress> webProgress =
+ new RemoteWebProgress(GetLoadType(), true, true);
+
+ RefPtr<BrowsingContextWebProgress> loadingWebProgress =
+ WebProgressForBrowsingContext(GetLoadingBrowsingContext());
+
+ if (loadingWebProgress) {
+ NS_DispatchToMainThread(
+ NS_NewRunnableFunction("DocumentLoadListener::FireStateChange", [=]() {
+ loadingWebProgress->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.mSwitchedProcess) {
+ // 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)
+ -> 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;
+ }
+
+ 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);
+}
+
+void DocumentLoadListener::Disconnect() {
+ LOG(("DocumentLoadListener Disconnect [this=%p]", this));
+ // 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);
+ }
+
+ if (auto* ctx = GetDocumentBrowsingContext()) {
+ ctx->EndDocumentLoad(mDoingProcessSwitch);
+ }
+}
+
+void DocumentLoadListener::Cancel(const nsresult& aStatusCode) {
+ LOG(
+ ("DocumentLoadListener Cancel [this=%p, "
+ "aStatusCode=%" PRIx32 " ]",
+ this, static_cast<uint32_t>(aStatusCode)));
+ if (mOpenPromiseResolved) {
+ return;
+ }
+ if (mChannel) {
+ mChannel->Cancel(aStatusCode);
+ }
+
+ DisconnectListeners(aStatusCode, aStatusCode);
+}
+
+void DocumentLoadListener::DisconnectListeners(nsresult aStatus,
+ nsresult aLoadGroupStatus,
+ bool aSwitchedProcess) {
+ LOG(
+ ("DocumentLoadListener DisconnectListener [this=%p, "
+ "aStatus=%" PRIx32 " aLoadGroupStatus=%" PRIx32 " ]",
+ this, static_cast<uint32_t>(aStatus),
+ static_cast<uint32_t>(aLoadGroupStatus)));
+
+ RejectOpenPromise(aStatus, aLoadGroupStatus, aSwitchedProcess, __func__);
+
+ Disconnect();
+
+ if (!aSwitchedProcess) {
+ // If we're not going to send anything else to the content process, and
+ // we haven't yet consumed a stream filter promise, then we're never going
+ // to. If we're disconnecting the old content process due to a proces
+ // switch, then we can rely on FinishReplacementChannelSetup being called
+ // (even if the switch failed), so we clear at that point instead.
+ // TODO: This might be because we retargeted the stream to the download
+ // handler or similar. Do we need to attach a stream filter to that?
+ 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 nsIHttpChannel::FlashPluginState& aState) {
+ parentChannel->NotifyFlashPluginStateChanged(aState);
+ },
+ [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);
+ },
+ [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();
+ }
+ nsresult rv = NS_OK;
+ for (auto& variant : streamListenerFunctions) {
+ variant.match(
+ [&](const OnStartRequestParams& aParams) {
+ rv = aListener->OnStartRequest(aParams.request);
+ if (NS_FAILED(rv)) {
+ aParams.request->Cancel(rv);
+ }
+ },
+ [&](const OnDataAvailableParams& aParams) {
+ // Don't deliver OnDataAvailable if we've
+ // already failed.
+ if (NS_FAILED(rv)) {
+ return;
+ }
+ nsCOMPtr<nsIInputStream> stringStream;
+ rv = NS_NewByteInputStream(
+ getter_AddRefs(stringStream),
+ Span<const char>(aParams.data.get(), aParams.count),
+ NS_ASSIGNMENT_DEPEND);
+ if (NS_SUCCEEDED(rv)) {
+ rv = aListener->OnDataAvailable(aParams.request, stringStream,
+ aParams.offset, aParams.count);
+ }
+ if (NS_FAILED(rv)) {
+ aParams.request->Cancel(rv);
+ }
+ },
+ [&](const OnStopRequestParams& aParams) {
+ if (NS_SUCCEEDED(rv)) {
+ aListener->OnStopRequest(aParams.request, aParams.status);
+ } else {
+ aListener->OnStopRequest(aParams.request, rv);
+ }
+ rv = NS_OK;
+ },
+ [&](const OnAfterLastPartParams& aParams) {
+ nsCOMPtr<nsIMultiPartChannelListener> multiListener =
+ do_QueryInterface(aListener);
+ if (multiListener) {
+ if (NS_SUCCEEDED(rv)) {
+ multiListener->OnAfterLastPart(aParams.status);
+ } else {
+ multiListener->OnAfterLastPart(rv);
+ }
+ }
+ });
+ }
+ // 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();
+
+ if (auto* ctx = GetDocumentBrowsingContext()) {
+ ctx->EndDocumentLoad(mDoingProcessSwitch);
+ }
+
+ return !mIsFinished;
+}
+
+void DocumentLoadListener::SerializeRedirectData(
+ RedirectToRealChannelArgs& aArgs, bool aIsCrossProcess,
+ uint32_t aRedirectFlags, uint32_t aLoadFlags,
+ ContentParent* aParent) const {
+ // Use the original URI of the current channel, as this is what
+ // we'll use to construct the channel in the content process.
+ aArgs.uri() = mChannelCreationURI;
+ if (!aArgs.uri()) {
+ mChannel->GetOriginalURI(getter_AddRefs(aArgs.uri()));
+ }
+
+ aArgs.loadIdentifier() = mLoadIdentifier;
+
+ // 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();
+
+ nsCOMPtr<nsIPrincipal> uriPrincipal;
+ nsIScriptSecurityManager* sm = nsContentUtils::GetSecurityManager();
+ sm->GetChannelURIPrincipal(mChannel, getter_AddRefs(uriPrincipal));
+
+ nsCOMPtr<nsIRedirectHistoryEntry> entry =
+ new nsRedirectHistoryEntry(uriPrincipal, nullptr, ""_ns);
+
+ redirectLoadInfo->AppendRedirectHistoryEntry(entry, true);
+ }
+
+ const Maybe<ClientInfo>& reservedClientInfo =
+ channelLoadInfo->GetReservedClientInfo();
+ if (reservedClientInfo) {
+ redirectLoadInfo->SetReservedClientInfo(*reservedClientInfo);
+ }
+
+ aArgs.registrarId() = mRedirectChannelId;
+
+ 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(aParent));
+ }
+
+ 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.loadStateLoadFlags() = mLoadStateLoadFlags;
+ aArgs.loadStateLoadType() = mLoadStateLoadType;
+ aArgs.originalUriString() = mOriginalUriString;
+ if (mLoadingSessionHistoryInfo) {
+ aArgs.loadingSessionHistoryInfo().emplace(*mLoadingSessionHistoryInfo);
+ }
+}
+
+static bool IsLargeAllocationLoad(CanonicalBrowsingContext* aBrowsingContext,
+ nsIChannel* aChannel) {
+ if (!StaticPrefs::dom_largeAllocationHeader_enabled() ||
+ aBrowsingContext->UseRemoteSubframes()) {
+ return false;
+ }
+
+ nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
+ if (!httpChannel) {
+ return false;
+ }
+
+ nsAutoCString ignoredHeaderValue;
+ nsresult rv =
+ httpChannel->GetResponseHeader("Large-Allocation"_ns, ignoredHeaderValue);
+ if (NS_FAILED(rv)) {
+ return false;
+ }
+
+ // On all platforms other than win32, LargeAllocation is disabled by default,
+ // and has to be force-enabled using `dom.largeAllocation.forceEnable`.
+#if defined(XP_WIN) && defined(_X86_)
+ return true;
+#else
+ return StaticPrefs::dom_largeAllocation_forceEnable();
+#endif
+}
+
+static bool ContextCanProcessSwitch(CanonicalBrowsingContext* aBrowsingContext,
+ WindowGlobalParent* aParentWindow) {
+ if (NS_WARN_IF(!aBrowsingContext)) {
+ LOG(("Process Switch Abort: no browsing context"));
+ return false;
+ }
+ if (!aBrowsingContext->IsContent()) {
+ LOG(("Process Switch Abort: non-content browsing context"));
+ return false;
+ }
+
+ if (aParentWindow && !aBrowsingContext->UseRemoteSubframes()) {
+ LOG(("Process Switch Abort: remote subframes disabled"));
+ return false;
+ }
+
+ if (aParentWindow && aParentWindow->IsInProcess()) {
+ LOG(("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) {
+ LOG(("Process Switch Abort: cannot get embedder element"));
+ return false;
+ }
+ nsCOMPtr<nsIBrowser> browser = browserElement->AsBrowser();
+ if (!browser) {
+ LOG(("Process Switch Abort: not loaded within nsIBrowser"));
+ return false;
+ }
+
+ nsIBrowser::ProcessBehavior processBehavior =
+ nsIBrowser::PROCESS_BEHAVIOR_DISABLED;
+ nsresult rv = browser->GetProcessSwitchBehavior(&processBehavior);
+ if (NS_FAILED(rv)) {
+ MOZ_ASSERT_UNREACHABLE(
+ "nsIBrowser::GetProcessSwitchBehavior shouldn't fail");
+ LOG(("Process Switch Abort: failed to get process switch behavior"));
+ return false;
+ }
+
+ // Check if the process switch we're considering is disabled by the
+ // <browser>'s process behavior.
+ if (processBehavior == nsIBrowser::PROCESS_BEHAVIOR_DISABLED) {
+ LOG(("Process Switch Abort: switch disabled by <browser>"));
+ return false;
+ }
+ if (!aParentWindow &&
+ processBehavior == nsIBrowser::PROCESS_BEHAVIOR_SUBFRAME_ONLY) {
+ LOG(("Process Switch Abort: toplevel switch disabled by <browser>"));
+ return false;
+ }
+
+ return true;
+}
+
+bool DocumentLoadListener::MaybeTriggerProcessSwitch(
+ bool* aWillSwitchToRemote) {
+ MOZ_ASSERT(XRE_IsParentProcess());
+ MOZ_DIAGNOSTIC_ASSERT(!mDoingProcessSwitch,
+ "Already in the middle of switching?");
+ MOZ_DIAGNOSTIC_ASSERT(mChannel);
+ MOZ_DIAGNOSTIC_ASSERT(mParentChannelListener);
+ MOZ_DIAGNOSTIC_ASSERT(aWillSwitchToRemote);
+
+ LOG(("DocumentLoadListener MaybeTriggerProcessSwitch [this=%p]", this));
+
+ // 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 && !mChannel->IsDocument()) {
+ LOG(("Process Switch Abort: non-document load"));
+ return false;
+ }
+
+ // Get the loading BrowsingContext. This may not be the context which will be
+ // switching processes 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();
+ RefPtr<WindowGlobalParent> parentWindow = GetParentWindowContext();
+ if (!ContextCanProcessSwitch(browsingContext, parentWindow)) {
+ return false;
+ }
+
+ // Get the final principal, used to select which process to load into.
+ nsCOMPtr<nsIPrincipal> resultPrincipal;
+ nsresult rv = nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
+ mChannel, getter_AddRefs(resultPrincipal));
+ if (NS_FAILED(rv)) {
+ LOG(("Process Switch Abort: failed to get channel result principal"));
+ return false;
+ }
+
+ nsAutoCString currentRemoteType(NOT_REMOTE_TYPE);
+ if (RefPtr<ContentParent> contentParent =
+ browsingContext->GetContentParent()) {
+ currentRemoteType = contentParent->GetRemoteType();
+ }
+ MOZ_ASSERT_IF(currentRemoteType.IsEmpty(), !OtherPid());
+
+ // Determine what type of content process this load should finish in.
+ nsAutoCString preferredRemoteType(currentRemoteType);
+ bool replaceBrowsingContext = false;
+ uint64_t specificGroupId = 0;
+
+ // If we're in a preloaded browser, force browsing context replacement to
+ // ensure the current process is re-selected.
+ {
+ Element* browserElement = browsingContext->Top()->GetEmbedderElement();
+
+ nsAutoString isPreloadBrowserStr;
+ if (browserElement->GetAttr(kNameSpaceID_None, nsGkAtoms::preloadedState,
+ isPreloadBrowserStr) &&
+ isPreloadBrowserStr.EqualsLiteral("consumed")) {
+ nsCOMPtr<nsIURI> originalURI;
+ if (NS_SUCCEEDED(mChannel->GetOriginalURI(getter_AddRefs(originalURI))) &&
+ !originalURI->GetSpecOrDefault().EqualsLiteral("about:newtab")) {
+ LOG(("Process Switch: leaving preloaded browser"));
+ replaceBrowsingContext = true;
+ browserElement->UnsetAttr(kNameSpaceID_None, nsGkAtoms::preloadedState,
+ true);
+ }
+ }
+ }
+
+ // Update the preferred final process for our load based on the
+ // Cross-Origin-Opener-Policy and Cross-Origin-Embedder-Policy headers.
+ {
+ bool isCOOPSwitch = HasCrossOriginOpenerPolicyMismatch();
+ replaceBrowsingContext |= isCOOPSwitch;
+
+ // Determine our COOP status, which will be used to determine our preferred
+ // remote type.
+ nsILoadInfo::CrossOriginOpenerPolicy coop =
+ nsILoadInfo::OPENER_POLICY_UNSAFE_NONE;
+ if (parentWindow) {
+ coop = browsingContext->Top()->GetOpenerPolicy();
+ } else if (nsCOMPtr<nsIHttpChannelInternal> httpChannel =
+ do_QueryInterface(mChannel)) {
+ MOZ_ALWAYS_SUCCEEDS(httpChannel->GetCrossOriginOpenerPolicy(&coop));
+ }
+
+ if (coop ==
+ nsILoadInfo::OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP) {
+ // We want documents with SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP COOP
+ // policy to be loaded in a separate process in which we can enable
+ // high-resolution timers.
+ nsAutoCString siteOrigin;
+ resultPrincipal->GetSiteOrigin(siteOrigin);
+ preferredRemoteType = WITH_COOP_COEP_REMOTE_TYPE_PREFIX;
+ preferredRemoteType.Append(siteOrigin);
+ } else if (isCOOPSwitch) {
+ // If we're doing a COOP switch, we do not need any affinity to the
+ // current remote type. Clear it back to the default value.
+ preferredRemoteType = DEFAULT_REMOTE_TYPE;
+ }
+ }
+
+ // If we're performing a large allocation load, override the remote type
+ // with `LARGE_ALLOCATION_REMOTE_TYPE` to move it into an exclusive content
+ // process. If we're already in one, and don't otherwise we force ourselves
+ // out of that content process.
+ if (!parentWindow && browsingContext->Group()->Toplevels().Length() == 1) {
+ if (IsLargeAllocationLoad(browsingContext, mChannel)) {
+ preferredRemoteType = LARGE_ALLOCATION_REMOTE_TYPE;
+ replaceBrowsingContext = true;
+ } else if (preferredRemoteType == LARGE_ALLOCATION_REMOTE_TYPE) {
+ preferredRemoteType = DEFAULT_REMOTE_TYPE;
+ replaceBrowsingContext = true;
+ }
+ }
+
+ // Put toplevel BrowsingContexts which load within the extension process into
+ // a specific BrowsingContextGroup.
+ if (auto* addonPolicy = BasePrincipal::Cast(resultPrincipal)->AddonPolicy()) {
+ if (!parentWindow) {
+ // Toplevel extension BrowsingContexts must be loaded in the extension
+ // browsing context group, within the extension content process.
+ if (ExtensionPolicyService::GetSingleton().UseRemoteExtensions()) {
+ preferredRemoteType = EXTENSION_REMOTE_TYPE;
+ } else {
+ preferredRemoteType = NOT_REMOTE_TYPE;
+ }
+
+ if (browsingContext->Group()->Id() !=
+ addonPolicy->GetBrowsingContextGroupId()) {
+ replaceBrowsingContext = true;
+ specificGroupId = addonPolicy->GetBrowsingContextGroupId();
+ }
+ } else {
+ // As a temporary measure, extension iframes must be loaded within the
+ // same process as their parent document.
+ preferredRemoteType = parentWindow->GetRemoteType();
+ }
+ }
+
+ LOG(
+ ("DocumentLoadListener GetRemoteTypeForPrincipal "
+ "[this=%p, contentParent=%s, preferredRemoteType=%s]",
+ this, currentRemoteType.get(), preferredRemoteType.get()));
+
+ nsCOMPtr<nsIE10SUtils> e10sUtils =
+ do_ImportModule("resource://gre/modules/E10SUtils.jsm", "E10SUtils");
+ if (!e10sUtils) {
+ LOG(("Process Switch Abort: Could not import E10SUtils"));
+ return false;
+ }
+
+ // Get information about the current document loaded in our BrowsingContext.
+ nsCOMPtr<nsIPrincipal> currentPrincipal;
+ RefPtr<WindowGlobalParent> wgp = browsingContext->GetCurrentWindowGlobal();
+ if (wgp) {
+ currentPrincipal = wgp->DocumentPrincipal();
+ }
+
+ nsAutoCString remoteType;
+ rv = e10sUtils->GetRemoteTypeForPrincipal(
+ resultPrincipal, mChannelCreationURI, browsingContext->UseRemoteTabs(),
+ browsingContext->UseRemoteSubframes(), preferredRemoteType,
+ currentPrincipal, parentWindow, remoteType);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ LOG(("Process Switch Abort: getRemoteTypeForPrincipal threw an exception"));
+ return false;
+ }
+
+ // If the final decision is to switch from an 'extension' remote type to any
+ // other remote type, ensure the browsing context is replaced so that we leave
+ // the extension-specific BrowsingContextGroup.
+ if (!parentWindow && currentRemoteType != remoteType &&
+ currentRemoteType == EXTENSION_REMOTE_TYPE) {
+ replaceBrowsingContext = true;
+ }
+
+ LOG(("GetRemoteTypeForPrincipal -> current:%s remoteType:%s",
+ currentRemoteType.get(), remoteType.get()));
+
+ // Check if a process switch is needed.
+ if (currentRemoteType == remoteType && !replaceBrowsingContext) {
+ LOG(("Process Switch Abort: type (%s) is compatible", remoteType.get()));
+ return false;
+ }
+
+ if (NS_WARN_IF(parentWindow && remoteType.IsEmpty())) {
+ LOG(("Process Switch Abort: non-remote target process for subframe"));
+ return false;
+ }
+
+ *aWillSwitchToRemote = !remoteType.IsEmpty();
+
+ // If we're doing a document load, we can immediately perform a process
+ // switch.
+ if (mIsDocumentLoad) {
+ TriggerProcessSwitch(browsingContext, remoteType, replaceBrowsingContext,
+ specificGroupId);
+ 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) {
+ LOG(("Process Switch Abort: no object upgrade handler"));
+ return false;
+ }
+
+ if (!StaticPrefs::fission_remoteObjectEmbed()) {
+ LOG(("Process Switch Abort: remote <object>/<embed> disabled"));
+ return false;
+ }
+
+ mObjectUpgradeHandler->UpgradeObjectLoad()->Then(
+ GetMainThreadSerialEventTarget(), __func__,
+ [self = RefPtr{this}, remoteType, replaceBrowsingContext, specificGroupId,
+ wgp](const RefPtr<CanonicalBrowsingContext>& aBrowsingContext) {
+ if (aBrowsingContext->IsDiscarded() ||
+ wgp != aBrowsingContext->GetParentWindowContext()) {
+ LOG(
+ ("Process Switch: Got invalid BrowsingContext from object "
+ "upgrade!"));
+ self->RedirectToRealChannelFinished(NS_ERROR_FAILURE);
+ return;
+ }
+
+ LOG(("Process Switch: Upgraded Object to Document Load"));
+ self->TriggerProcessSwitch(aBrowsingContext, remoteType,
+ replaceBrowsingContext, specificGroupId);
+ },
+ [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 nsCString& aRemoteType,
+ bool aReplaceBrowsingContext, uint64_t aSpecificGroupId) {
+ nsAutoCString currentRemoteType(NOT_REMOTE_TYPE);
+ if (RefPtr<ContentParent> contentParent = aContext->GetContentParent()) {
+ currentRemoteType = contentParent->GetRemoteType();
+ }
+ MOZ_ASSERT_IF(currentRemoteType.IsEmpty(), !OtherPid());
+
+ LOG(("Process Switch: Changing Remoteness from '%s' to '%s'",
+ currentRemoteType.get(), aRemoteType.get()));
+
+ // We're now committing to a process switch, so we can disconnect from
+ // the listeners in the old process.
+ mDoingProcessSwitch = true;
+
+ RefPtr<WindowGlobalParent> wgp = aContext->GetCurrentWindowGlobal();
+ if (wgp && wgp->IsProcessRoot()) {
+ if (RefPtr<BrowserParent> browserParent = wgp->GetBrowserParent()) {
+ // This load has already started, so we want to filter out any 'stop'
+ // progress events coming from the old process as a result of us
+ // disconnecting from it.
+ browserParent->SuspendProgressEvents();
+ }
+ }
+ DisconnectListeners(NS_BINDING_ABORTED, NS_BINDING_ABORTED, true);
+
+ LOG(("Process Switch: Calling ChangeRemoteness"));
+ aContext
+ ->ChangeRemoteness(aRemoteType, mLoadIdentifier, aReplaceBrowsingContext,
+ aSpecificGroupId)
+ ->Then(
+ GetMainThreadSerialEventTarget(), __func__,
+ [self = RefPtr{this}](BrowserParent* aBrowserParent) {
+ MOZ_ASSERT(self->mChannel,
+ "Something went wrong, channel got cancelled");
+ self->TriggerRedirectToRealChannel(Some(
+ aBrowserParent ? aBrowserParent->Manager()->ChildID() : 0));
+ },
+ [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(mLoadStateLoadFlags);
+ 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<uint64_t>& 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) {
+ if (!*aDestinationProcess) {
+ MOZ_ASSERT(aStreamFilterEndpoints.IsEmpty());
+ return RedirectToParentProcess(aRedirectFlags, aLoadFlags);
+ }
+ dom::ContentParent* cp =
+ dom::ContentProcessManager::GetSingleton()->GetContentProcessById(
+ ContentParentId{*aDestinationProcess});
+ if (!cp) {
+ return PDocumentChannelParent::RedirectToRealChannelPromise::
+ CreateAndReject(ipc::ResponseRejectReason::SendError, __func__);
+ }
+
+ RedirectToRealChannelArgs args;
+ SerializeRedirectData(args, !!aDestinationProcess, aRedirectFlags,
+ aLoadFlags, cp);
+ if (mTiming) {
+ mTiming->Anonymize(args.uri());
+ args.timing() = Some(std::move(mTiming));
+ }
+
+ auto loadInfo = args.loadInfo();
+
+ if (loadInfo.isNothing()) {
+ return PDocumentChannelParent::RedirectToRealChannelPromise::
+ CreateAndReject(ipc::ResponseRejectReason::SendError, __func__);
+ }
+
+ auto triggeringPrincipalOrErr =
+ PrincipalInfoToPrincipal(loadInfo.ref().triggeringPrincipalInfo());
+
+ if (triggeringPrincipalOrErr.isOk()) {
+ nsCOMPtr<nsIPrincipal> triggeringPrincipal =
+ triggeringPrincipalOrErr.unwrap();
+ cp->TransmitBlobDataIfBlobURL(args.uri(), triggeringPrincipal);
+ }
+
+ 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, 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<uint64_t>& aDestinationProcess) {
+ 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(mStreamFilterRequests.Length());
+ if (!mStreamFilterRequests.IsEmpty()) {
+ base::ProcessId pid = OtherPid();
+ if (aDestinationProcess) {
+ if (*aDestinationProcess) {
+ dom::ContentParent* cp =
+ dom::ContentProcessManager::GetSingleton()->GetContentProcessById(
+ ContentParentId(*aDestinationProcess));
+ if (cp) {
+ pid = cp->OtherPid();
+ }
+ } else {
+ pid = 0;
+ }
+ }
+
+ for (StreamFilterRequest& request : mStreamFilterRequests) {
+ if (!pid) {
+ request.mPromise->Reject(false, __func__);
+ request.mPromise = nullptr;
+ continue;
+ }
+ ParentEndpoint parent;
+ nsresult rv = extensions::PStreamFilter::CreateEndpoints(
+ pid, request.mChildProcessId, &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(mStreamFilterRequests)](
+ 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 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) {
+ auto* bc = GetDocumentBrowsingContext();
+ if (!bc) {
+ return false;
+ }
+
+ nsCOMPtr<nsIInputStream> newPostData;
+ nsCOMPtr<nsIURI> newURI = nsDocShell::AttemptURIFixup(
+ mChannel, aStatus, mOriginalUriString, mLoadStateLoadType, bc->IsTop(),
+ mLoadStateLoadFlags &
+ nsDocShell::INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP,
+ bc->UsePrivateBrowsing(), true, getter_AddRefs(newPostData));
+ 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);
+
+ 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);
+
+ 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);
+ }
+ }
+
+ auto* loadingContext = GetLoadingBrowsingContext();
+ if (!loadingContext || loadingContext->IsDiscarded()) {
+ DisconnectListeners(NS_ERROR_UNEXPECTED, NS_ERROR_UNEXPECTED);
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ // 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;
+ }
+
+ // 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;
+ }
+
+ 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)) {
+ if (!mSupportsRedirectToRealChannel) {
+ // 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.
+ mDoingProcessSwitch = true;
+
+ // If we're not going to process switch, then we must have an existing
+ // window global, right?
+ MOZ_ASSERT(loadingContext->GetCurrentWindowGlobal());
+
+ RefPtr<BrowserParent> browserParent =
+ loadingContext->GetCurrentWindowGlobal()->GetBrowserParent();
+
+ // XXX(anny) This is currently a dead code path because parent-controlled
+ // DC pref is off. When we enable the pref, we might get extra STATE_START
+ // progress events
+
+ // 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(loadingContext->OwnerProcessId()));
+ } else {
+ TriggerRedirectToRealChannel(Nothing());
+ }
+
+ // If we're not switching, then check if we're currently remote.
+ if (loadingContext->GetContentParent()) {
+ willBeRemote = true;
+ }
+ }
+
+ // 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, 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;
+}
+
+// Rather than forwarding all these nsIParentChannel functions to the child,
+// we cache a list of them, and then ask the 'real' channel to forward them
+// for us after it's created.
+NS_IMETHODIMP
+DocumentLoadListener::NotifyFlashPluginStateChanged(
+ nsIHttpChannel::FlashPluginState aState) {
+ mIParentChannelFunctions.AppendElement(
+ IParentChannelFunction{VariantIndex<0>{}, aState});
+ 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<1>{}, 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<2>{}, std::move(params)});
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+DocumentLoadListener::NotifyClassificationFlags(uint32_t aClassificationFlags,
+ bool aIsThirdParty) {
+ mIParentChannelFunctions.AppendElement(IParentChannelFunction{
+ VariantIndex<3>{},
+ 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;
+
+ // 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;
+ }
+
+ if (GetDocumentBrowsingContext()) {
+ if (mLoadingSessionHistoryInfo) {
+ mLoadingSessionHistoryInfo =
+ GetDocumentBrowsingContext()
+ ->ReplaceLoadingSessionHistoryEntryForLoad(
+ mLoadingSessionHistoryInfo.get(), aNewChannel);
+ }
+ if (!net::ChannelIsPost(aOldChannel)) {
+ AddURIVisit(aOldChannel, 0);
+
+ nsCOMPtr<nsIURI> oldURI;
+ aOldChannel->GetURI(getter_AddRefs(oldURI));
+ nsDocShell::SaveLastVisit(aNewChannel, oldURI, aFlags);
+ }
+ }
+ mHaveVisibleRedirect |= true;
+
+ LOG(
+ ("DocumentLoadListener AsyncOnChannelRedirect [this=%p] "
+ "mHaveVisibleRedirect=%c",
+ this, mHaveVisibleRedirect ? 'T' : 'F'));
+
+ // If this is a cross-origin redirect, then we should no longer allow
+ // mixed content. The destination docshell checks this in its redirect
+ // handling, but if we deliver to a new docshell (with a process switch)
+ // then this doesn't happen.
+ // Manually remove the allow mixed content flags.
+ nsresult rv = nsContentUtils::CheckSameOrigin(aOldChannel, aNewChannel);
+ if (NS_FAILED(rv)) {
+ if (mLoadStateLoadType == LOAD_NORMAL_ALLOW_MIXED_CONTENT) {
+ mLoadStateLoadType = LOAD_NORMAL;
+ } else if (mLoadStateLoadType == LOAD_RELOAD_ALLOW_MIXED_CONTENT) {
+ mLoadStateLoadType = LOAD_RELOAD_NORMAL;
+ }
+ MOZ_ASSERT(!LOAD_TYPE_HAS_FLAGS(
+ mLoadStateLoadType, nsIWebNavigation::LOAD_FLAGS_ALLOW_MIXED_CONTENT));
+ }
+
+ // 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.
+ aNewChannel->GetOriginalURI(getter_AddRefs(mChannelCreationURI));
+
+ // 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();
+
+#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;
+}
+
+// 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(base::ProcessId aChildProcessId)
+ -> RefPtr<ChildEndpointPromise> {
+ LOG(("DocumentLoadListener AttachStreamFilter [this=%p]", this));
+
+ StreamFilterRequest* request = mStreamFilterRequests.AppendElement();
+ request->mPromise = new ChildEndpointPromise::Private(__func__);
+ request->mChildProcessId = aChildProcessId;
+ 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;
+ nsCOMPtr<nsIWebProgress> webProgress =
+ new RemoteWebProgress(mLoadStateLoadType, true, true);
+
+ RefPtr<BrowsingContextWebProgress> topWebProgress =
+ WebProgressForBrowsingContext(GetTopBrowsingContext());
+ const nsString message(aStatusArg);
+
+ if (topWebProgress) {
+ NS_DispatchToMainThread(
+ NS_NewRunnableFunction("DocumentLoadListener::OnStatus", [=]() {
+ topWebProgress->OnStatusChange(webProgress, channel, aStatus,
+ message.get());
+ }));
+ }
+ 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..a77bfa9285
--- /dev/null
+++ b/netwerk/ipc/DocumentLoadListener.h
@@ -0,0 +1,575 @@
+/* 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 "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 "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;
+}
+namespace net {
+using ChildEndpointPromise =
+ MozPromise<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;
+ base::ProcessId mChildProcessId;
+ 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:
+ // See the comment on GetLoadingBrowsingContext for explanation of
+ // aLoadingBrowsingContext.
+ DocumentLoadListener(dom::CanonicalBrowsingContext* aLoadingBrowsingContext,
+ bool aIsDocumentLoad);
+
+ struct OpenPromiseSucceededType {
+ nsTArray<ipc::Endpoint<extensions::PStreamFilterParent>>
+ mStreamFilterEndpoints;
+ uint32_t mRedirectFlags;
+ uint32_t mLoadFlags;
+ RefPtr<PDocumentChannelParent::RedirectToRealChannelPromise::Private>
+ mPromise;
+ };
+ struct OpenPromiseFailedType {
+ nsresult mStatus;
+ nsresult mLoadGroupStatus;
+ // This is set to true if we're rejecting the promise because we
+ // switched to load away to a new process.
+ bool mSwitchedProcess = false;
+ };
+
+ typedef MozPromise<OpenPromiseSucceededType, OpenPromiseFailedType,
+ true /* isExclusive */>
+ OpenPromise;
+
+ // 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,
+ base::ProcessId aPid, 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> aIsXFOError, base::ProcessId aPid,
+ 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,
+ base::ProcessId aPid, ObjectUpgradeHandler* aUpgradeHandler,
+ 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);
+
+ // 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
+
+ // 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);
+
+ 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) override {
+ LogBlockedCORSRequestParams params;
+ params.mMessage = aMessage;
+ params.mCategory = aCategory;
+ 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;
+ }
+
+ base::ProcessId OtherPid() const { return mOtherPid; }
+
+ [[nodiscard]] RefPtr<ChildEndpointPromise> AttachStreamFilter(
+ base::ProcessId aChildProcessId);
+
+ // 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,
+ dom::ContentParent* aParent) const;
+
+ uint64_t GetLoadIdentifier() const { return mLoadIdentifier; }
+ uint32_t GetLoadType() const { return mLoadStateLoadType; }
+
+ mozilla::dom::LoadingSessionHistoryInfo* GetLoadingSessionHistoryInfo() {
+ return mLoadingSessionHistoryInfo.get();
+ }
+
+ bool IsDocumentLoad() const { return mIsDocumentLoad; }
+
+ 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.
+ void DisconnectListeners(nsresult aStatus, nsresult aLoadGroupStatus,
+ bool aSwitchedProcess = 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<uint64_t>& aDestinationProcess);
+
+ // 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);
+ void TriggerProcessSwitch(dom::CanonicalBrowsingContext* aContext,
+ const nsCString& aRemoteType,
+ bool aReplaceBrowsingContext,
+ uint64_t aSpecificGroupId);
+
+ // 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 = ipc::Endpoint<extensions::PStreamFilterParent>;
+ RefPtr<PDocumentChannelParent::RedirectToRealChannelPromise>
+ RedirectToRealChannel(uint32_t aRedirectFlags, uint32_t aLoadFlags,
+ const Maybe<uint64_t>& 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();
+
+ 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
+ //
+ // NotifyFlashPluginStateChanged(nsIHttpChannel::FlashPluginState aState) = 0;
+ // 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;
+ };
+
+ typedef mozilla::Variant<
+ nsIHttpChannel::FlashPluginState, ClassifierMatchedInfoParams,
+ ClassifierMatchedTrackingInfoParams, ClassificationFlagsParams>
+ IParentChannelFunction;
+
+ // 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;
+ };
+
+ struct LogMimeTypeMismatchParams {
+ nsCString mMessageName;
+ bool mWarning;
+ nsString mURL;
+ nsString mContentType;
+ };
+
+ typedef mozilla::Variant<ReportSecurityMessageParams,
+ LogBlockedCORSRequestParams,
+ LogMimeTypeMismatchParams>
+ SecurityWarningFunction;
+ nsTArray<SecurityWarningFunction> mSecurityWarningFunctions;
+
+ 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;
+ };
+ typedef mozilla::Variant<OnStartRequestParams, OnDataAvailableParams,
+ OnStopRequestParams, OnAfterLastPartParams>
+ StreamListenerFunction;
+ // 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;
+
+ // 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;
+
+ // The original URI of the current channel. If there are redirects,
+ // then the value on the channel gets overwritten with the original
+ // URI of the first channel in the redirect chain, so we cache the
+ // value we need here.
+ nsCOMPtr<nsIURI> mChannelCreationURI;
+
+ // 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;
+
+ // 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;
+
+ 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 mLoadStateLoadFlags = 0;
+ uint32_t mLoadStateLoadType = 0;
+
+ // 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;
+ // Set to true if we're currently in the middle of replacing this with
+ // a new channel connected a different process.
+ bool mDoingProcessSwitch = 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;
+
+ bool mSupportsRedirectToRealChannel = true;
+
+ // The process id of the content process that we are being called from
+ // or 0 initiated from a parent process load.
+ base::ProcessId mOtherPid = 0;
+
+ void RejectOpenPromise(nsresult aStatus, nsresult aLoadGroupStatus,
+ bool aSwitchedProcess, 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, aSwitchedProcess}),
+ 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..30a406facb
--- /dev/null
+++ b/netwerk/ipc/InputChannelThrottleQueueParent.cpp
@@ -0,0 +1,128 @@
+/* -*- 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 (!mRefCnt.isThreadSafe) {
+ NS_ASSERT_OWNINGTHREAD(InputChannelThrottleQueueParent);
+ }
+
+ nsrefcnt count = --mRefCnt;
+ NS_LOG_RELEASE(this, count, "InputChannelThrottleQueueParent");
+
+ if (count == 0) {
+ if (!mRefCnt.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;
+}
+
+InputChannelThrottleQueueParent::InputChannelThrottleQueueParent()
+ : mBytesProcessed(0), mMeanBytesPerSecond(0), mMaxBytesPerSecond(0) {}
+
+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..a81424fd30
--- /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();
+ mozilla::ipc::IPCResult RecvRecordRead(const uint32_t& aBytesRead);
+ void ActorDestroy(ActorDestroyReason aWhy) override {}
+
+ private:
+ virtual ~InputChannelThrottleQueueParent() = default;
+
+ uint64_t mBytesProcessed;
+ uint32_t mMeanBytesPerSecond;
+ uint32_t mMaxBytesPerSecond;
+};
+
+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..b6a51b279a
--- /dev/null
+++ b/netwerk/ipc/NeckoChannelParams.ipdlh
@@ -0,0 +1,546 @@
+/* -*- 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 protocol PFTPChannel;
+include protocol PChildToParentStream;
+include BlobTypes;
+include ClientIPCTypes;
+include URIParams;
+include IPCServiceWorkerDescriptor;
+include IPCStream;
+include PBackgroundSharedTypes;
+include DOMTypes;
+
+include "mozilla/dom/PropertyBagUtils.h";
+include "mozilla/dom/ReferrerInfoUtils.h";
+include "mozilla/ipc/URIUtils.h";
+
+using mozilla::OriginAttributes from "mozilla/ipc/BackgroundUtils.h";
+using 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";
+using refcounted class nsIPropertyBag2 from "nsIPropertyBag2.h";
+using refcounted class nsDOMNavigationTiming from "nsDOMNavigationTiming.h";
+using nsContentPolicyType from "nsIContentPolicy.h";
+using nsILoadInfo::CrossOriginEmbedderPolicy from "nsILoadInfo.h";
+using class mozilla::dom::LoadingSessionHistoryInfo from "mozilla/dom/SessionHistoryEntry.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 isOnContentBlockingAllowList;
+ CookiePermissionData[] cookiePermissions;
+ bool isFixed;
+ nsString partitionKey;
+};
+
+//-----------------------------------------------------------------------------
+// Preferrer alternative data type
+//-----------------------------------------------------------------------------
+
+struct PreferredAlternativeDataTypeParams
+{
+ nsCString type;
+ nsCString contentType;
+ bool deliverAltData;
+};
+
+//-----------------------------------------------------------------------------
+// LoadInfo IPDL structs
+//-----------------------------------------------------------------------------
+
+struct RedirectHistoryEntryInfo
+{
+ PrincipalInfo principalInfo;
+ URIParams? referrerUri;
+ nsCString remoteAddress;
+};
+
+struct LoadInfoArgs
+{
+ PrincipalInfo? requestingPrincipalInfo;
+ PrincipalInfo triggeringPrincipalInfo;
+ PrincipalInfo? principalToInheritInfo;
+ PrincipalInfo? sandboxedLoadingPrincipalInfo;
+ PrincipalInfo? topLevelPrincipalInfo;
+ PrincipalInfo? topLevelStorageAreaPrincipalInfo;
+ URIParams? resultPrincipalURI;
+ uint32_t securityFlags;
+ uint32_t sandboxFlags;
+ uint32_t triggeringSandboxFlags;
+ nsContentPolicyType contentPolicyType;
+ uint32_t tainting;
+ bool blockAllMixedContent;
+ bool upgradeInsecureRequests;
+ bool browserUpgradeInsecureRequests;
+ bool browserDidUpgradeInsecureRequests;
+ bool browserWouldUpgradeInsecureRequests;
+ bool forceAllowDataURI;
+ bool allowInsecureRedirectToDataURI;
+ bool bypassCORSChecks;
+ 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;
+
+ /**
+ * 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;
+ nsString cspNonce;
+ bool skipContentSniffing;
+ uint32_t httpsOnlyStatus;
+ bool hasValidUserGestureActivation;
+ bool allowDeprecatedSystemRequests;
+ bool isInDevToolsContext;
+ bool parserCreatedScript;
+ bool isFromProcessingFrameAttributes;
+ CookieJarSettingsArgs cookieJarSettings;
+ uint32_t requestBlockingReason;
+ CSPInfo? cspToInheritInfo;
+ bool hasStoragePermission;
+ CrossOriginEmbedderPolicy loadingEmbedderPolicy;
+};
+
+/**
+ * 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;
+
+ // WebExtextensions WebRequest API allows extentions to redirect a channel
+ // Before the CORS-preflight was fetched, which can lead into unexpeced CORS blocks.
+ // We can set this to skip the Cors Checks for the old Channel.
+ bool bypassCORSChecks;
+
+ // 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;
+
+ // 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;
+
+ // 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;
+
+ bool hasStoragePermission;
+
+ bool isThirdPartyContextToTopWindow;
+
+ bool isInThirdPartyContext;
+
+ // 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
+{
+ URIParams 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?
+ URIParams? original;
+ URIParams? doc;
+ nsIReferrerInfo referrerInfo;
+ URIParams? apiRedirectTo;
+ URIParams? topWindowURI;
+ uint32_t loadFlags;
+ RequestHeaderTuples requestHeaders;
+ nsCString requestMethod;
+ IPCStream? uploadStream;
+ bool uploadStreamHasHeaders;
+ int16_t priority;
+ uint32_t classOfService;
+ uint8_t redirectionLimit;
+ bool allowSTS;
+ uint32_t thirdPartyFlags;
+ bool resumeAt;
+ uint64_t startPos;
+ nsCString entityID;
+ bool chooseApplicationCache;
+ nsCString appCacheClientID;
+ bool allowSpdy;
+ bool allowHttp3;
+ bool allowAltSvc;
+ bool beConservative;
+ uint32_t tlsFlags;
+ LoadInfoArgs? loadInfo;
+ uint32_t cacheKey;
+ uint64_t requestContextID;
+ CorsPreflightArgs? preflightArgs;
+ uint32_t initialRwin;
+ bool blockAuthPrompt;
+ bool allowStaleCacheContent;
+ bool preferCacheLoadOverBypass;
+ nsCString contentTypeHint;
+ uint32_t corsMode;
+ uint32_t redirectMode;
+ uint64_t channelId;
+ nsString integrityMetadata;
+ uint64_t contentWindowId;
+ PreferredAlternativeDataTypeParams[] preferredAlternativeTypes;
+ uint64_t topLevelOuterContentWindowId;
+ TimeStamp launchServiceWorkerStart;
+ TimeStamp launchServiceWorkerEnd;
+ TimeStamp dispatchFetchEventStart;
+ TimeStamp dispatchFetchEventEnd;
+ TimeStamp handleFetchEventStart;
+ TimeStamp handleFetchEventEnd;
+ bool forceMainDocumentChannel;
+ TimeStamp navigationStartTimeStamp;
+ bool hasNonEmptySandboxingFlag;
+};
+
+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;
+ uint32_t tlsFlags;
+ bool isolated;
+ bool isTrrServiceChannel;
+ uint8_t trrMode;
+ bool isIPv4Disabled;
+ bool isIPv6Disabled;
+ nsCString topWindowOrigin;
+ bool isHttp3;
+ 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;
+};
+
+//-----------------------------------------------------------------------------
+// FTP IPDL structs
+//-----------------------------------------------------------------------------
+
+struct FTPChannelOpenArgs
+{
+ URIParams uri;
+ uint64_t startPos;
+ nsCString entityID;
+ IPCStream? uploadStream;
+ LoadInfoArgs? loadInfo;
+ uint32_t loadFlags;
+};
+
+struct FTPChannelConnectArgs
+{
+ uint32_t channelId;
+};
+
+union FTPChannelCreationArgs
+{
+ FTPChannelOpenArgs; // For AsyncOpen: the common case.
+ FTPChannelConnectArgs; // Used for redirected-to channels
+};
+
+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;
+ int32_t sameSite;
+ int32_t rawSameSite;
+ uint8_t schemeMap;
+};
+
+struct DocumentCreationArgs {
+ bool uriModified;
+ bool isXFOError;
+};
+
+struct ObjectCreationArgs {
+ uint64_t embedderInnerWindowId;
+ uint32_t loadFlags;
+ nsContentPolicyType contentPolicyType;
+ bool isUrgentStart;
+};
+
+union DocumentChannelElementCreationArgs {
+ DocumentCreationArgs;
+ ObjectCreationArgs;
+};
+
+struct DocumentChannelCreationArgs {
+ DocShellLoadStateInit loadState;
+ TimeStamp asyncOpenTime;
+ uint64_t channelId;
+ uint32_t cacheKey;
+ nsDOMNavigationTiming? timing;
+ IPCClientInfo? initialClientInfo;
+ DocumentChannelElementCreationArgs elementCreationArgs;
+};
+
+struct RedirectToRealChannelArgs {
+ uint32_t registrarId;
+ nsIURI uri;
+ uint32_t newLoadFlags;
+ ReplacementChannelConfigInit? init;
+ LoadInfoArgs? loadInfo;
+ uint64_t channelId;
+ nsIURI originalURI;
+ uint32_t redirectMode;
+ uint32_t redirectFlags;
+ uint32_t? contentDisposition;
+ nsString? contentDispositionFilename;
+ nsIPropertyBag2 properties;
+ uint32_t loadStateLoadFlags;
+ uint32_t loadStateLoadType;
+ nsDOMNavigationTiming? timing;
+ nsString srcdocData;
+ nsIURI baseUri;
+ LoadingSessionHistoryInfo? loadingSessionHistoryInfo;
+ uint64_t loadIdentifier;
+ nsCString? originalUriString;
+};
+
+struct TimingStructArgs {
+ TimeStamp domainLookupStart;
+ TimeStamp domainLookupEnd;
+ TimeStamp connectStart;
+ TimeStamp tcpConnectEnd;
+ TimeStamp secureConnectionStart;
+ TimeStamp connectEnd;
+ TimeStamp requestStart;
+ TimeStamp responseStart;
+ TimeStamp responseEnd;
+};
+
+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;
+ nsCString protocolVersion;
+
+ // 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;
+};
+
+struct HttpActivity
+{
+ nsCString host;
+ int32_t port;
+ bool endToEndSSL;
+};
+
+union HttpActivityArgs
+{
+ uint64_t;
+ HttpActivity;
+};
+
+struct TransactionObserverResult
+{
+ bool versionOk;
+ bool authOk;
+ nsresult closeReason;
+};
+
+struct SpeculativeConnectionOverriderArgs {
+ uint32_t parallelSpeculativeConnectLimit;
+ bool ignoreIdle;
+ bool isFromPredictor;
+ bool allow1918;
+};
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/ipc/NeckoChild.cpp b/netwerk/ipc/NeckoChild.cpp
new file mode 100644
index 0000000000..401af4c719
--- /dev/null
+++ b/netwerk/ipc/NeckoChild.cpp
@@ -0,0 +1,360 @@
+
+/* 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/CookieServiceChild.h"
+#include "mozilla/net/FTPChannelChild.h"
+#include "mozilla/net/DataChannelChild.h"
+#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/ClassifierDummyChannelChild.h"
+#include "mozilla/net/SocketProcessBridgeChild.h"
+#ifdef MOZ_WEBRTC
+# include "mozilla/net/StunAddrsRequestChild.h"
+# include "mozilla/net/WebrtcTCPSocketChild.h"
+#endif
+
+#include "SerializedLoadContext.h"
+#include "nsGlobalWindow.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;
+ }
+ gNeckoChild = cpc->SendPNeckoConstructor();
+ NS_ASSERTION(gNeckoChild, "PNecko Protocol init failed!");
+ SocketProcessBridgeChild::GetSocketProcessBridge();
+ }
+}
+
+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 nsCString& 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;
+}
+
+PFTPChannelChild* NeckoChild::AllocPFTPChannelChild(
+ PBrowserChild* aBrowser, const SerializedLoadContext& aSerialized,
+ const FTPChannelCreationArgs& aOpenArgs) {
+ // We don't allocate here: see FTPChannelChild::AsyncOpen()
+ MOZ_CRASH("AllocPFTPChannelChild should not be called");
+ return nullptr;
+}
+
+bool NeckoChild::DeallocPFTPChannelChild(PFTPChannelChild* channel) {
+ MOZ_ASSERT(IsNeckoChild(), "DeallocPFTPChannelChild called by non-child!");
+
+ FTPChannelChild* child = static_cast<FTPChannelChild*>(channel);
+ child->ReleaseIPDLReference();
+ return true;
+}
+
+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) {
+ nsCOMPtr<nsISerialEventTarget> target;
+ if (nsGlobalWindowInner* win =
+ nsGlobalWindowInner::GetInnerWindowWithId(aInnerWindowID)) {
+ target = win->EventTargetFor(TaskCategory::Other);
+ }
+
+ RefPtr<WebSocketEventListenerChild> c =
+ new WebSocketEventListenerChild(aInnerWindowID, target);
+
+ if (target) {
+ gNeckoChild->SetEventTargetForActor(c, target);
+ }
+
+ 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 nsString& 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 nsCString& 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;
+}
+
+mozilla::ipc::IPCResult NeckoChild::RecvAsyncAuthPromptForNestedFrame(
+ const TabId& aNestedFrameId, const nsCString& aUri, const nsString& aRealm,
+ const uint64_t& aCallbackId) {
+ RefPtr<dom::BrowserChild> browserChild =
+ dom::BrowserChild::FindBrowserChild(aNestedFrameId);
+ if (!browserChild) {
+ MOZ_CRASH();
+ return IPC_FAIL_NO_REASON(this);
+ }
+ browserChild->SendAsyncAuthPrompt(aUri, aRealm, aCallbackId);
+ return IPC_OK();
+}
+
+/* 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();
+}
+
+PClassifierDummyChannelChild* NeckoChild::AllocPClassifierDummyChannelChild(
+ nsIURI* aURI, nsIURI* aTopWindowURI, const nsresult& aTopWindowURIResult,
+ const Maybe<LoadInfoArgs>& aLoadInfo) {
+ return new ClassifierDummyChannelChild();
+}
+
+bool NeckoChild::DeallocPClassifierDummyChannelChild(
+ PClassifierDummyChannelChild* aActor) {
+ delete static_cast<ClassifierDummyChannelChild*>(aActor);
+ return true;
+}
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/ipc/NeckoChild.h b/netwerk/ipc/NeckoChild.h
new file mode 100644
index 0000000000..efc98e95b9
--- /dev/null
+++ b/netwerk/ipc/NeckoChild.h
@@ -0,0 +1,96 @@
+/* -*- 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:
+ NeckoChild() = default;
+ virtual ~NeckoChild();
+
+ static void InitNeckoChild();
+
+ protected:
+ PStunAddrsRequestChild* AllocPStunAddrsRequestChild();
+ bool DeallocPStunAddrsRequestChild(PStunAddrsRequestChild* aActor);
+
+ PWebrtcTCPSocketChild* AllocPWebrtcTCPSocketChild(const Maybe<TabId>& tabId);
+ bool DeallocPWebrtcTCPSocketChild(PWebrtcTCPSocketChild* aActor);
+
+ PAltDataOutputStreamChild* AllocPAltDataOutputStreamChild(
+ const nsCString& type, const int64_t& predictedSize,
+ PHttpChannelChild* channel);
+ bool DeallocPAltDataOutputStreamChild(PAltDataOutputStreamChild* aActor);
+
+ PCookieServiceChild* AllocPCookieServiceChild();
+ bool DeallocPCookieServiceChild(PCookieServiceChild*);
+ PFTPChannelChild* AllocPFTPChannelChild(
+ PBrowserChild* aBrowser, const SerializedLoadContext& aSerialized,
+ const FTPChannelCreationArgs& aOpenArgs);
+ bool DeallocPFTPChannelChild(PFTPChannelChild*);
+ PWebSocketChild* AllocPWebSocketChild(PBrowserChild*,
+ const SerializedLoadContext&,
+ const uint32_t&);
+ bool DeallocPWebSocketChild(PWebSocketChild*);
+ PTCPSocketChild* AllocPTCPSocketChild(const nsString& 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 nsCString& aFilter);
+ bool DeallocPUDPSocketChild(PUDPSocketChild*);
+ PSimpleChannelChild* AllocPSimpleChannelChild(const uint32_t& channelId);
+ bool DeallocPSimpleChannelChild(PSimpleChannelChild* child);
+ PTransportProviderChild* AllocPTransportProviderChild();
+ bool DeallocPTransportProviderChild(PTransportProviderChild* aActor);
+ mozilla::ipc::IPCResult RecvAsyncAuthPromptForNestedFrame(
+ const TabId& aNestedFrameId, const nsCString& aUri,
+ const nsString& aRealm, const uint64_t& aCallbackId);
+ 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);
+
+ PClassifierDummyChannelChild* AllocPClassifierDummyChannelChild(
+ nsIURI* aURI, nsIURI* aTopWindowURI, const nsresult& aTopWindowURIResult,
+ const Maybe<LoadInfoArgs>& aLoadInfo);
+
+ bool DeallocPClassifierDummyChannelChild(
+ PClassifierDummyChannelChild* aChannel);
+};
+
+/**
+ * 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.h b/netwerk/ipc/NeckoCommon.h
new file mode 100644
index 0000000000..4de3c05fa8
--- /dev/null
+++ b/netwerk/ipc/NeckoCommon.h
@@ -0,0 +1,116 @@
+/* -*- 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 "nsXULAppAPI.h"
+#include "prenv.h"
+#include "nsPrintfCString.h"
+#include "mozilla/Preferences.h"
+
+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) = 0;
+ [[nodiscard]] virtual nsresult LogMimeTypeMismatch(
+ const nsACString& aMessageName, bool aWarning, const nsAString& aURL,
+ const nsAString& aContentType) = 0;
+};
+
+} // 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..81887f8b61
--- /dev/null
+++ b/netwerk/ipc/NeckoMessageUtils.h
@@ -0,0 +1,149 @@
+/* -*- 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"
+
+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(Message* aMsg, const Permission& aParam) {
+ WriteParam(aMsg, aParam.origin);
+ WriteParam(aMsg, aParam.type);
+ WriteParam(aMsg, aParam.capability);
+ WriteParam(aMsg, aParam.expireType);
+ WriteParam(aMsg, aParam.expireTime);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter,
+ Permission* aResult) {
+ return ReadParam(aMsg, aIter, &aResult->origin) &&
+ ReadParam(aMsg, aIter, &aResult->type) &&
+ ReadParam(aMsg, aIter, &aResult->capability) &&
+ ReadParam(aMsg, aIter, &aResult->expireType) &&
+ ReadParam(aMsg, aIter, &aResult->expireTime);
+ }
+
+ static void Log(const Permission& p, std::wstring* l) {
+ l->append(L"(");
+ LogParam(p.origin, l);
+ l->append(L", ");
+ LogParam(p.capability, l);
+ l->append(L", ");
+ LogParam(p.expireTime, l);
+ l->append(L", ");
+ LogParam(p.expireType, l);
+ l->append(L")");
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::net::NetAddr> {
+ static void Write(Message* aMsg, const mozilla::net::NetAddr& aParam) {
+ WriteParam(aMsg, aParam.raw.family);
+ if (aParam.raw.family == AF_UNSPEC) {
+ aMsg->WriteBytes(aParam.raw.data, sizeof(aParam.raw.data));
+ } else if (aParam.raw.family == AF_INET) {
+ WriteParam(aMsg, aParam.inet.port);
+ WriteParam(aMsg, aParam.inet.ip);
+ } else if (aParam.raw.family == AF_INET6) {
+ WriteParam(aMsg, aParam.inet6.port);
+ WriteParam(aMsg, aParam.inet6.flowinfo);
+ WriteParam(aMsg, aParam.inet6.ip.u64[0]);
+ WriteParam(aMsg, aParam.inet6.ip.u64[1]);
+ WriteParam(aMsg, 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");
+ aMsg->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(const Message* aMsg, PickleIterator* aIter,
+ mozilla::net::NetAddr* aResult) {
+ if (!ReadParam(aMsg, aIter, &aResult->raw.family)) return false;
+
+ if (aResult->raw.family == AF_UNSPEC) {
+ return aMsg->ReadBytesInto(aIter, &aResult->raw.data,
+ sizeof(aResult->raw.data));
+ } else if (aResult->raw.family == AF_INET) {
+ return ReadParam(aMsg, aIter, &aResult->inet.port) &&
+ ReadParam(aMsg, aIter, &aResult->inet.ip);
+ } else if (aResult->raw.family == AF_INET6) {
+ return ReadParam(aMsg, aIter, &aResult->inet6.port) &&
+ ReadParam(aMsg, aIter, &aResult->inet6.flowinfo) &&
+ ReadParam(aMsg, aIter, &aResult->inet6.ip.u64[0]) &&
+ ReadParam(aMsg, aIter, &aResult->inet6.ip.u64[1]) &&
+ ReadParam(aMsg, aIter, &aResult->inet6.scope_id);
+#if defined(XP_UNIX)
+ } else if (aResult->raw.family == AF_LOCAL) {
+ return aMsg->ReadBytesInto(aIter, &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<nsIHttpChannel::FlashPluginState>
+ : public ContiguousEnumSerializerInclusive<
+ nsIHttpChannel::FlashPluginState, nsIHttpChannel::FlashPluginUnknown,
+ nsIHttpChannel::FlashPluginLastValue> {};
+
+} // 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..f0ee16d855
--- /dev/null
+++ b/netwerk/ipc/NeckoParent.cpp
@@ -0,0 +1,922 @@
+/* -*- 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/FTPChannelParent.h"
+#include "mozilla/net/WebSocketChannelParent.h"
+#include "mozilla/net/WebSocketEventListenerParent.h"
+#include "mozilla/net/DataChannelParent.h"
+#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/ClassifierDummyChannelParent.h"
+#include "mozilla/net/IPCTransportProvider.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/ChromeUtils.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/TabContext.h"
+#include "mozilla/dom/BrowserParent.h"
+#include "mozilla/dom/network/TCPSocketParent.h"
+#include "mozilla/dom/network/TCPServerSocketParent.h"
+#include "mozilla/dom/network/UDPSocketParent.h"
+#include "mozilla/dom/ServiceWorkerManager.h"
+#include "mozilla/LoadContext.h"
+#include "mozilla/MozPromise.h"
+#include "nsPrintfCString.h"
+#include "nsHTMLDNSPrefetch.h"
+#include "nsEscape.h"
+#include "SerializedLoadContext.h"
+#include "nsAuthInformationHolder.h"
+#include "nsIAuthPromptCallback.h"
+#include "nsINetworkPredictor.h"
+#include "nsINetworkPredictorVerifier.h"
+#include "nsISpeculativeConnect.h"
+#include "nsHttpHandler.h"
+#include "nsNetUtil.h"
+
+using IPC::SerializedLoadContext;
+using mozilla::OriginAttributes;
+using mozilla::dom::BrowserParent;
+using mozilla::dom::ChromeUtils;
+using mozilla::dom::ContentParent;
+using mozilla::dom::ServiceWorkerManager;
+using mozilla::dom::TabContext;
+using mozilla::dom::TCPServerSocketParent;
+using mozilla::dom::TCPSocketParent;
+using mozilla::dom::UDPSocketParent;
+using mozilla::ipc::LoadInfoArgsToLoadInfo;
+using mozilla::ipc::PrincipalInfo;
+using mozilla::net::PTCPServerSocketParent;
+using mozilla::net::PTCPSocketParent;
+using mozilla::net::PUDPSocketParent;
+
+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 Maybe<LoadInfoArgs>& aOptionalLoadInfoArgs) {
+ if (aOptionalLoadInfoArgs.isNothing()) {
+ return nullptr;
+ }
+
+ const LoadInfoArgs& loadInfoArgs = aOptionalLoadInfoArgs.ref();
+ const Maybe<PrincipalInfo>& optionalPrincipalInfo =
+ loadInfoArgs.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());
+}
+
+static already_AddRefed<nsIPrincipal> GetRequestingPrincipal(
+ const FTPChannelCreationArgs& aArgs) {
+ if (aArgs.type() != FTPChannelCreationArgs::TFTPChannelOpenArgs) {
+ return nullptr;
+ }
+
+ const FTPChannelOpenArgs& args = aArgs.get_FTPChannelOpenArgs();
+ 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 nsCString& 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;
+}
+
+PFTPChannelParent* NeckoParent::AllocPFTPChannelParent(
+ PBrowserParent* aBrowser, const SerializedLoadContext& aSerialized,
+ const FTPChannelCreationArgs& aOpenArgs) {
+ nsCOMPtr<nsIPrincipal> requestingPrincipal =
+ GetRequestingPrincipal(aOpenArgs);
+
+ nsCOMPtr<nsILoadContext> loadContext;
+ const char* error = CreateChannelLoadContext(
+ aBrowser, Manager(), aSerialized, requestingPrincipal, loadContext);
+ if (error) {
+ printf_stderr(
+ "NeckoParent::AllocPFTPChannelParent: "
+ "FATAL error: %s: KILLING CHILD PROCESS\n",
+ error);
+ return nullptr;
+ }
+ PBOverrideStatus overrideStatus =
+ PBOverrideStatusFromLoadContext(aSerialized);
+ FTPChannelParent* p = new FTPChannelParent(BrowserParent::GetFrom(aBrowser),
+ loadContext, overrideStatus);
+ p->AddRef();
+ return p;
+}
+
+bool NeckoParent::DeallocPFTPChannelParent(PFTPChannelParent* channel) {
+ FTPChannelParent* p = static_cast<FTPChannelParent*>(channel);
+ p->Release();
+ return true;
+}
+
+mozilla::ipc::IPCResult NeckoParent::RecvPFTPChannelConstructor(
+ PFTPChannelParent* aActor, PBrowserParent* aBrowser,
+ const SerializedLoadContext& aSerialized,
+ const FTPChannelCreationArgs& aOpenArgs) {
+ FTPChannelParent* p = static_cast<FTPChannelParent*>(aActor);
+ if (!p->Init(aOpenArgs)) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ return IPC_OK();
+}
+
+already_AddRefed<PDocumentChannelParent>
+NeckoParent::AllocPDocumentChannelParent(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ const DocumentChannelCreationArgs& args) {
+ RefPtr<DocumentChannelParent> p = new DocumentChannelParent();
+ return p.forget();
+}
+
+mozilla::ipc::IPCResult NeckoParent::RecvPDocumentChannelConstructor(
+ PDocumentChannelParent* aActor,
+ const MaybeDiscarded<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();
+}
+
+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 nsString& /* 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 nsCString& /* unused */) {
+ RefPtr<UDPSocketParent> p = new UDPSocketParent(this);
+
+ return p.forget().take();
+}
+
+mozilla::ipc::IPCResult NeckoParent::RecvPUDPSocketConstructor(
+ PUDPSocketParent* aActor, nsIPrincipal* aPrincipal,
+ const nsCString& 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 nsCString& aHost, const nsCString& aTrrServer, const uint16_t& aType,
+ const OriginAttributes& aOriginAttributes, const uint32_t& aFlags) {
+ RefPtr<DNSRequestHandler> handler = new DNSRequestHandler();
+ RefPtr<DNSRequestParent> actor = new DNSRequestParent(handler);
+ return actor.forget();
+}
+
+mozilla::ipc::IPCResult NeckoParent::RecvPDNSRequestConstructor(
+ PDNSRequestParent* aActor, const nsCString& aHost,
+ const nsCString& aTrrServer, const uint16_t& aType,
+ const OriginAttributes& aOriginAttributes, const uint32_t& aFlags) {
+ RefPtr<DNSRequestParent> actor = static_cast<DNSRequestParent*>(aActor);
+ RefPtr<DNSRequestHandler> handler =
+ actor->GetDNSRequest()->AsDNSRequestHandler();
+ handler->DoAsyncResolve(aHost, aTrrServer, aType, aOriginAttributes, aFlags);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult NeckoParent::RecvSpeculativeConnect(
+ nsIURI* aURI, nsIPrincipal* aPrincipal, 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 (aAnonymous) {
+ speculator->SpeculativeAnonymousConnect(aURI, principal, nullptr);
+ } else {
+ speculator->SpeculativeConnect(aURI, principal, nullptr);
+ }
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult NeckoParent::RecvHTMLDNSPrefetch(
+ const nsString& hostname, const bool& isHttps,
+ const OriginAttributes& aOriginAttributes, const uint32_t& flags) {
+ nsHTMLDNSPrefetch::Prefetch(hostname, isHttps, aOriginAttributes, flags);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult NeckoParent::RecvCancelHTMLDNSPrefetch(
+ const nsString& hostname, const bool& isHttps,
+ const OriginAttributes& aOriginAttributes, const uint32_t& flags,
+ const nsresult& reason) {
+ nsHTMLDNSPrefetch::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;
+}
+
+namespace {
+std::map<uint64_t, nsCOMPtr<nsIAuthPromptCallback> >& CallbackMap() {
+ MOZ_ASSERT(NS_IsMainThread());
+ static std::map<uint64_t, nsCOMPtr<nsIAuthPromptCallback> > sCallbackMap;
+ return sCallbackMap;
+}
+} // namespace
+
+NS_IMPL_ISUPPORTS(NeckoParent::NestedFrameAuthPrompt, nsIAuthPrompt2)
+
+NeckoParent::NestedFrameAuthPrompt::NestedFrameAuthPrompt(PNeckoParent* aParent,
+ TabId aNestedFrameId)
+ : mNeckoParent(aParent), mNestedFrameId(aNestedFrameId) {}
+
+NS_IMETHODIMP
+NeckoParent::NestedFrameAuthPrompt::AsyncPromptAuth(
+ nsIChannel* aChannel, nsIAuthPromptCallback* callback, nsISupports*,
+ uint32_t, nsIAuthInformation* aInfo, nsICancelable**) {
+ static uint64_t callbackId = 0;
+ MOZ_ASSERT(XRE_IsParentProcess());
+ nsCOMPtr<nsIURI> uri;
+ nsresult rv = aChannel->GetURI(getter_AddRefs(uri));
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsAutoCString spec;
+ if (uri) {
+ rv = uri->GetSpec(spec);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ nsString realm;
+ rv = aInfo->GetRealm(realm);
+ NS_ENSURE_SUCCESS(rv, rv);
+ callbackId++;
+ if (mNeckoParent->SendAsyncAuthPromptForNestedFrame(mNestedFrameId, spec,
+ realm, callbackId)) {
+ CallbackMap()[callbackId] = callback;
+ return NS_OK;
+ }
+ return NS_ERROR_FAILURE;
+}
+
+mozilla::ipc::IPCResult NeckoParent::RecvOnAuthAvailable(
+ const uint64_t& aCallbackId, const nsString& aUser,
+ const nsString& aPassword, const nsString& aDomain) {
+ nsCOMPtr<nsIAuthPromptCallback> callback = CallbackMap()[aCallbackId];
+ if (!callback) {
+ return IPC_OK();
+ }
+ CallbackMap().erase(aCallbackId);
+
+ RefPtr<nsAuthInformationHolder> holder =
+ new nsAuthInformationHolder(0, u""_ns, ""_ns);
+ holder->SetUsername(aUser);
+ holder->SetPassword(aPassword);
+ holder->SetDomain(aDomain);
+
+ callback->OnAuthAvailable(nullptr, holder);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult NeckoParent::RecvOnAuthCancelled(
+ const uint64_t& aCallbackId, const bool& aUserCancel) {
+ nsCOMPtr<nsIAuthPromptCallback> callback = CallbackMap()[aCallbackId];
+ if (!callback) {
+ return IPC_OK();
+ }
+ CallbackMap().erase(aCallbackId);
+ callback->OnAuthCancelled(nullptr, aUserCancel);
+ return IPC_OK();
+}
+
+/* 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();
+}
+
+PClassifierDummyChannelParent* NeckoParent::AllocPClassifierDummyChannelParent(
+ nsIURI* aURI, nsIURI* aTopWindowURI, const nsresult& aTopWindowURIResult,
+ const Maybe<LoadInfoArgs>& aLoadInfo) {
+ RefPtr<ClassifierDummyChannelParent> c = new ClassifierDummyChannelParent();
+ return c.forget().take();
+}
+
+mozilla::ipc::IPCResult NeckoParent::RecvPClassifierDummyChannelConstructor(
+ PClassifierDummyChannelParent* aActor, nsIURI* aURI, nsIURI* aTopWindowURI,
+ const nsresult& aTopWindowURIResult, const Maybe<LoadInfoArgs>& aLoadInfo) {
+ ClassifierDummyChannelParent* p =
+ static_cast<ClassifierDummyChannelParent*>(aActor);
+
+ if (NS_WARN_IF(!aURI)) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ nsCOMPtr<nsILoadInfo> loadInfo;
+ nsresult rv = LoadInfoArgsToLoadInfo(aLoadInfo, getter_AddRefs(loadInfo));
+ if (NS_WARN_IF(NS_FAILED(rv)) || !loadInfo) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ p->Init(aURI, aTopWindowURI, aTopWindowURIResult, loadInfo);
+ return IPC_OK();
+}
+
+bool NeckoParent::DeallocPClassifierDummyChannelParent(
+ PClassifierDummyChannelParent* aActor) {
+ RefPtr<ClassifierDummyChannelParent> c =
+ dont_AddRef(static_cast<ClassifierDummyChannelParent*>(aActor));
+ MOZ_ASSERT(c);
+ return true;
+}
+
+mozilla::ipc::IPCResult NeckoParent::RecvInitSocketProcessBridge(
+ InitSocketProcessBridgeResolver&& aResolver) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ Endpoint<PSocketProcessBridgeChild> invalidEndpoint;
+ if (NS_WARN_IF(mSocketProcessBridgeInited)) {
+ aResolver(std::move(invalidEndpoint));
+ return IPC_OK();
+ }
+
+ SocketProcessParent* parent = SocketProcessParent::GetSingleton();
+ if (NS_WARN_IF(!parent)) {
+ aResolver(std::move(invalidEndpoint));
+ return IPC_OK();
+ }
+
+ Endpoint<PSocketProcessBridgeParent> parentEndpoint;
+ Endpoint<PSocketProcessBridgeChild> childEndpoint;
+ if (NS_WARN_IF(NS_FAILED(PSocketProcessBridge::CreateEndpoints(
+ parent->OtherPid(), Manager()->OtherPid(), &parentEndpoint,
+ &childEndpoint)))) {
+ aResolver(std::move(invalidEndpoint));
+ return IPC_OK();
+ }
+
+ if (NS_WARN_IF(!parent->SendInitSocketProcessBridgeParent(
+ Manager()->OtherPid(), std::move(parentEndpoint)))) {
+ aResolver(std::move(invalidEndpoint));
+ return IPC_OK();
+ }
+
+ aResolver(std::move(childEndpoint));
+ mSocketProcessBridgeInited = true;
+ 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, 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 nsCOMPtr<nsIInputStream>& aStream) {
+ aResolver(aStream);
+ },
+ [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(nullptr);
+ });
+
+ return IPC_OK();
+}
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/ipc/NeckoParent.h b/netwerk/ipc/NeckoParent.h
new file mode 100644
index 0000000000..1137c94b01
--- /dev/null
+++ b/netwerk/ipc/NeckoParent.h
@@ -0,0 +1,252 @@
+/* -*- 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:
+ NeckoParent();
+ virtual ~NeckoParent() = default;
+
+ [[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);
+ }
+
+ /*
+ * This implementation of nsIAuthPrompt2 is used for nested remote iframes
+ * that want an auth prompt. This class lives in the parent process and
+ * informs the NeckoChild that we want an auth prompt, which forwards the
+ * request to the BrowserParent in the remote iframe that contains the nested
+ * iframe
+ */
+ class NestedFrameAuthPrompt final : public nsIAuthPrompt2 {
+ ~NestedFrameAuthPrompt() {}
+
+ public:
+ NS_DECL_ISUPPORTS
+
+ NestedFrameAuthPrompt(PNeckoParent* aParent, TabId aNestedFrameId);
+
+ NS_IMETHOD PromptAuth(nsIChannel*, uint32_t, nsIAuthInformation*,
+ bool*) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+
+ NS_IMETHOD AsyncPromptAuth(nsIChannel* aChannel,
+ nsIAuthPromptCallback* callback, nsISupports*,
+ uint32_t, nsIAuthInformation* aInfo,
+ nsICancelable**) override;
+
+ protected:
+ PNeckoParent* mNeckoParent;
+ TabId mNestedFrameId;
+ };
+
+ protected:
+ 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 nsCString& type, const int64_t& predictedSize,
+ PHttpChannelParent* channel);
+ bool DeallocPAltDataOutputStreamParent(PAltDataOutputStreamParent* aActor);
+
+ bool DeallocPCookieServiceParent(PCookieServiceParent*);
+ PFTPChannelParent* AllocPFTPChannelParent(
+ PBrowserParent* aBrowser, const SerializedLoadContext& aSerialized,
+ const FTPChannelCreationArgs& aOpenArgs);
+ virtual mozilla::ipc::IPCResult RecvPFTPChannelConstructor(
+ PFTPChannelParent* aActor, PBrowserParent* aBrowser,
+ const SerializedLoadContext& aSerialized,
+ const FTPChannelCreationArgs& aOpenArgs) override;
+ bool DeallocPFTPChannelParent(PFTPChannelParent*);
+ PWebSocketParent* AllocPWebSocketParent(
+ PBrowserParent* browser, const SerializedLoadContext& aSerialized,
+ const uint32_t& aSerial);
+ bool DeallocPWebSocketParent(PWebSocketParent*);
+ PTCPSocketParent* AllocPTCPSocketParent(const nsString& 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 nsCString& aFilter);
+ virtual mozilla::ipc::IPCResult RecvPUDPSocketConstructor(
+ PUDPSocketParent*, nsIPrincipal* aPrincipal,
+ const nsCString& aFilter) override;
+ bool DeallocPUDPSocketParent(PUDPSocketParent*);
+ already_AddRefed<PDNSRequestParent> AllocPDNSRequestParent(
+ const nsCString& aHost, const nsCString& aTrrServer,
+ const uint16_t& aType, const OriginAttributes& aOriginAttributes,
+ const uint32_t& aFlags);
+ virtual mozilla::ipc::IPCResult RecvPDNSRequestConstructor(
+ PDNSRequestParent* actor, const nsCString& hostName,
+ const nsCString& trrServer, const uint16_t& type,
+ const OriginAttributes& aOriginAttributes,
+ const uint32_t& flags) override;
+ mozilla::ipc::IPCResult RecvSpeculativeConnect(nsIURI* aURI,
+ nsIPrincipal* aPrincipal,
+ const bool& aAnonymous);
+ mozilla::ipc::IPCResult RecvHTMLDNSPrefetch(
+ const nsString& hostname, const bool& isHttps,
+ const OriginAttributes& aOriginAttributes, const uint32_t& flags);
+ mozilla::ipc::IPCResult RecvCancelHTMLDNSPrefetch(
+ const nsString& hostname, const bool& isHttps,
+ const OriginAttributes& aOriginAttributes, const uint32_t& 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;
+
+ PSimpleChannelParent* AllocPSimpleChannelParent(const uint32_t& channelId);
+ bool DeallocPSimpleChannelParent(PSimpleChannelParent* parent);
+
+ 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);
+
+ mozilla::ipc::IPCResult RecvOnAuthAvailable(const uint64_t& aCallbackId,
+ const nsString& aUser,
+ const nsString& aPassword,
+ const nsString& aDomain);
+ mozilla::ipc::IPCResult RecvOnAuthCancelled(const uint64_t& aCallbackId,
+ const bool& aUserCancel);
+
+ /* 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, GetPageThumbStreamResolver&& aResolve);
+
+ PClassifierDummyChannelParent* AllocPClassifierDummyChannelParent(
+ nsIURI* aURI, nsIURI* aTopWindowURI, const nsresult& aTopWindowURIResult,
+ const Maybe<LoadInfoArgs>& aLoadInfo);
+
+ bool DeallocPClassifierDummyChannelParent(
+ PClassifierDummyChannelParent* aParent);
+
+ virtual mozilla::ipc::IPCResult RecvPClassifierDummyChannelConstructor(
+ PClassifierDummyChannelParent* aActor, nsIURI* aURI,
+ nsIURI* aTopWindowURI, const nsresult& aTopWindowURIResult,
+ const Maybe<LoadInfoArgs>& aLoadInfo) override;
+
+ mozilla::ipc::IPCResult RecvInitSocketProcessBridge(
+ InitSocketProcessBridgeResolver&& aResolver);
+
+ 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..2dba003fe4
--- /dev/null
+++ b/netwerk/ipc/PDataChannel.ipdl
@@ -0,0 +1,24 @@
+/* -*- 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 {
+
+async refcounted 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..b6fdbb7b20
--- /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 nsHttpHeaderArray from "nsHttpHeaderArray.h";
+using mozilla::net::NetAddr from "mozilla/net/DNS.h";
+
+namespace mozilla {
+namespace net {
+
+refcounted protocol PDocumentChannel
+{
+ manager PNecko;
+
+parent:
+
+ async Cancel(nsresult status);
+
+ 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 aSwitchedProcess);
+
+ // 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..cc5f456166
--- /dev/null
+++ b/netwerk/ipc/PFileChannel.ipdl
@@ -0,0 +1,27 @@
+/* -*- 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
+ */
+async refcounted 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..5668fed06a
--- /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 {
+
+refcounted 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..624645e2a1
--- /dev/null
+++ b/netwerk/ipc/PNecko.ipdl
@@ -0,0 +1,190 @@
+/* -*- 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;
+include protocol PFTPChannel;
+include protocol PWebSocket;
+include protocol PWebSocketEventListener;
+include protocol PTCPSocket;
+include protocol PTCPServerSocket;
+include protocol PUDPSocket;
+include protocol PDNSRequest;
+include protocol PFileDescriptorSet;
+include protocol PDataChannel;
+include protocol PSimpleChannel;
+include protocol PTransportProvider;
+include protocol PChildToParentStream; //FIXME: bug #792908
+include protocol PParentToChildStream; //FIXME: bug #792908
+include protocol PStunAddrsRequest;
+include protocol PFileChannel;
+include protocol PClassifierDummyChannel;
+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 refcounted class nsIInputStream from "mozilla/ipc/IPCStreamUtils.h";
+using refcounted class nsIURI from "mozilla/ipc/URIUtils.h";
+using refcounted class nsIPrincipal from "nsIPrincipal.h";
+
+
+namespace mozilla {
+namespace net {
+
+//-------------------------------------------------------------------
+nested(upto inside_cpow) sync protocol PNecko
+{
+ manager PContent;
+ manages PHttpChannel;
+ manages PCookieService;
+ manages PFTPChannel;
+ manages PWebSocket;
+ manages PWebSocketEventListener;
+ manages PTCPSocket;
+ manages PTCPServerSocket;
+ manages PUDPSocket;
+ manages PDNSRequest;
+ manages PDataChannel;
+ manages PSimpleChannel;
+ manages PFileChannel;
+ manages PTransportProvider;
+ manages PAltDataOutputStream;
+ manages PStunAddrsRequest;
+ manages PClassifierDummyChannel;
+ manages PWebrtcTCPSocket;
+ manages PDocumentChannel;
+
+parent:
+ async __delete__();
+
+ nested(inside_cpow) async PCookieService();
+ async PHttpChannel(nullable PBrowser browser,
+ SerializedLoadContext loadContext,
+ HttpChannelCreationArgs args);
+ async PFTPChannel(nullable PBrowser browser, SerializedLoadContext loadContext,
+ FTPChannelCreationArgs args);
+
+ async PWebSocket(nullable PBrowser browser, SerializedLoadContext loadContext,
+ uint32_t aSerialID);
+ async PTCPServerSocket(uint16_t localPort, uint16_t backlog, bool useArrayBuffers);
+ async PUDPSocket(nsIPrincipal principal, nsCString filter);
+
+ async PDNSRequest(nsCString hostName, nsCString trrServer, uint16_t type,
+ OriginAttributes originAttributes, uint32_t flags);
+
+ async PDocumentChannel(MaybeDiscardedBrowsingContext browsingContext,
+ DocumentChannelCreationArgs args);
+
+ async PWebSocketEventListener(uint64_t aInnerWindowID);
+
+ /* Predictor Methods */
+ async PredPredict(nsIURI targetURI, nsIURI sourceURI,
+ uint32_t reason, OriginAttributes originAttributes,
+ bool hasVerifier);
+ async PredLearn(nsIURI targetURI, nsIURI sourceURI,
+ uint32_t reason, OriginAttributes originAttributes);
+ async PredReset();
+
+ async SpeculativeConnect(nsIURI uri, nsIPrincipal principal, bool anonymous);
+ async HTMLDNSPrefetch(nsString hostname, bool isHttps,
+ OriginAttributes originAttributes, uint32_t flags);
+ async CancelHTMLDNSPrefetch(nsString hostname, bool isHttps,
+ OriginAttributes originAttributes,
+ uint32_t 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);
+ async PSimpleChannel(uint32_t channelId);
+ async PFileChannel(uint32_t channelId);
+
+ async PClassifierDummyChannel(nsIURI uri, nsIURI aTopWindowURI,
+ nsresult aTopWindowURIResult,
+ LoadInfoArgs? loadInfo);
+
+ /**
+ * These are called from the child with the results of the auth prompt.
+ * callbackId is the id that was passed in PBrowser::AsyncAuthPrompt,
+ * corresponding to an nsIAuthPromptCallback
+ */
+ async OnAuthAvailable(uint64_t callbackId, nsString user,
+ nsString password, nsString domain);
+ async OnAuthCancelled(uint64_t callbackId, bool userCancel);
+
+ 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(nsIURI uri) returns (nsIInputStream stream);
+ async GetExtensionFD(nsIURI uri) returns (FileDescriptor fd);
+
+ async InitSocketProcessBridge()
+ returns (Endpoint<PSocketProcessBridgeChild> endpoint);
+
+ async EnsureHSTSData()
+ returns (bool result);
+
+ /**
+ * Page thumbnails remote resource loading
+ */
+ async GetPageThumbStream(nsIURI uri) returns (nsIInputStream stream);
+
+child:
+ /*
+ * Bring up the http auth prompt for a nested remote mozbrowser.
+ * NestedFrameId is the id corresponding to the PBrowser. It is the same id
+ * that was passed to the PBrowserOrId param in to the PHttpChannel constructor
+ */
+ async AsyncAuthPromptForNestedFrame(TabId nestedFrameId, nsCString uri,
+ nsString realm, uint64_t callbackId);
+
+ /* Predictor Methods */
+ async PredOnPredictPrefetch(nsIURI uri, uint32_t httpStatus);
+ async PredOnPredictPreconnect(nsIURI uri);
+ async PredOnPredictDNS(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.
+ prio(mediumhigh) async NetworkChangeNotification(nsCString type);
+
+ async PTransportProvider();
+
+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/PProxyConfigLookup.ipdl b/netwerk/ipc/PProxyConfigLookup.ipdl
new file mode 100644
index 0000000000..49cb69071a
--- /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 refcounted 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..74a113e301
--- /dev/null
+++ b/netwerk/ipc/PSimpleChannel.ipdl
@@ -0,0 +1,24 @@
+/* -*- 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 {
+
+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..b885d6a6a7
--- /dev/null
+++ b/netwerk/ipc/PSocketProcess.ipdl
@@ -0,0 +1,182 @@
+/* -*- 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 PFileDescriptorSet;
+include protocol PChildToParentStream;
+include protocol PParentToChildStream;
+include protocol PInputChannelThrottleQueue;
+include protocol PBackground;
+include protocol PAltService;
+include protocol PAltSvcTransaction;
+include protocol PTRRService;
+include protocol PProxyConfigLookup;
+include protocol PNativeDNSResolverOverride;
+include protocol PRemoteLazyInputStream;
+
+include MemoryReportTypes;
+include NeckoChannelParams;
+include PrefsTypes;
+include PSMIPCTypes;
+
+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";
+using refcounted 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";
+
+namespace mozilla {
+namespace net {
+
+struct HttpHandlerInitArgs {
+ bool mFastOpenSupported;
+ 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;
+ FileDescriptor? mSandboxBroker;
+};
+
+sync protocol PSocketProcess
+{
+ manages PDNSRequest;
+ manages PWebrtcTCPSocket;
+ manages PFileDescriptorSet;
+ manages PHttpTransaction;
+ manages PHttpConnectionMgr;
+ manages PChildToParentStream;
+ manages PParentToChildStream;
+ manages PInputChannelThrottleQueue;
+ manages PAltService;
+ manages PAltSvcTransaction;
+ manages PTRRService;
+ manages PProxyConfigLookup;
+ manages PNativeDNSResolverOverride;
+ manages PRemoteLazyInputStream;
+
+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 PChildToParentStream();
+ async ObserveHttpActivity(HttpActivityArgs aActivityArgs,
+ uint32_t aActivityType,
+ uint32_t aActivitySubtype,
+ PRTime aTimestamp,
+ uint64_t aExtraSizeData,
+ nsCString aExtraStringData);
+ async InitBackground(Endpoint<PBackgroundParent> aEndpoint);
+ async PAltService();
+ sync GetTLSClientCert(nsCString aHostName,
+ OriginAttributes aOriginAttributes,
+ int32_t aPort,
+ uint32_t aProviderFlags,
+ uint32_t aProviderTlsFlags,
+ ByteArray aServerCert,
+ ByteArray? aClientCert,
+ ByteArray[] aCollectedCANames)
+ returns (bool aSucceeded, ByteArray aOutCert, ByteArray aOutKey, ByteArray[] aBuiltChain);
+ async PProxyConfigLookup(nsIURI aUri, uint32_t aFlags);
+ async CachePushCheck(nsIURI aPushedURL,
+ OriginAttributes aOriginAttributes,
+ nsCString aRequestString)
+ returns (bool aAccepted);
+
+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);
+ // test-only
+ async SocketProcessTelemetryPing();
+
+ async PHttpTransaction();
+ async PParentToChildStream();
+ async PHttpConnectionMgr(HttpHandlerInitArgs aArgs);
+ async UpdateDeviceModelId(nsCString aModelId);
+
+ async OnHttpActivityDistributorActivated(bool aIsActivated);
+ async PInputChannelThrottleQueue(uint32_t meanBytesPerSecond,
+ uint32_t maxBytesPerSecond);
+ async PAltSvcTransaction(HttpConnectionInfoCloneArgs aConnInfo,
+ uint32_t aCaps);
+ async ClearSessionCache();
+ async PTRRService(bool aCaptiveIsPassed,
+ bool aParentalControlEnabled,
+ nsCString[] aDNSSuffixList);
+ async PNativeDNSResolverOverride();
+ async NotifyObserver(nsCString aTopic, nsString aData);
+
+ async PRemoteLazyInputStream(nsID aID, uint64_t aSize);
+
+ async GetSocketData()
+ returns (SocketDataArgs data);
+ async GetDNSCacheEntries()
+ returns (DNSCacheEntries[] entries);
+ async GetHttpConnectionData()
+ returns (HttpRetParams[] params);
+
+both:
+ async PFileDescriptorSet(FileDescriptor fd);
+ async PDNSRequest(nsCString hostName, nsCString trrServer, uint16_t type,
+ OriginAttributes originAttributes, uint32_t flags);
+};
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/ipc/PSocketProcessBridge.ipdl b/netwerk/ipc/PSocketProcessBridge.ipdl
new file mode 100644
index 0000000000..b57eca181b
--- /dev/null
+++ b/netwerk/ipc/PSocketProcessBridge.ipdl
@@ -0,0 +1,30 @@
+/* -*- 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 PBackground;
+
+namespace mozilla {
+namespace net {
+
+/**
+ * PSocketProcessBridge is the IPC protocol between content process and
+ * socket process. This protocol allows socket process to send data to
+ * content process bypassing parent process.
+ * Once created, PSocketProcessBridgeChild is the actor that lives in
+ * content process and PSocketProcessBridgeParent lives in
+ * socket process.
+ */
+nested(upto inside_cpow) sync protocol PSocketProcessBridge
+{
+
+parent:
+ async InitBackground(Endpoint<PBackgroundParent> aEndpoint);
+both:
+ async Test();
+};
+
+}
+}
diff --git a/netwerk/ipc/ParentChannelWrapper.cpp b/netwerk/ipc/ParentChannelWrapper.cpp
new file mode 100644
index 0000000000..57b9fe37c1
--- /dev/null
+++ b/netwerk/ipc/ParentChannelWrapper.cpp
@@ -0,0 +1,107 @@
+/* -*- 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/UrlClassifierCommon.h"
+#include "nsIRedirectChannelRegistrar.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::NotifyFlashPluginStateChanged(
+ nsIHttpChannel::FlashPluginState aState) {
+ // For now, only HttpChannel use this attribute.
+ RefPtr<HttpBaseChannel> httpChannel = do_QueryObject(mChannel);
+ if (httpChannel) {
+ httpChannel->SetFlashPluginState(aState);
+ }
+ 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..b9856a2913
--- /dev/null
+++ b/netwerk/ipc/ParentProcessDocumentChannel.cpp
@@ -0,0 +1,300 @@
+/* -*- 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/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"
+
+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 aIsXFOError)
+ : DocumentChannel(aLoadState, aLoadInfo, aLoadFlags, aCacheKey,
+ aUriModified, aIsXFOError) {
+ 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) {
+ 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()) {
+ 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), mAsyncOpenTime, mTiming,
+ std::move(initialClientInfo), Some(mUriModified), Some(mIsXFOError),
+ 0 /* ProcessId */, &rv);
+ } else {
+ promise = mDocumentLoadListener->OpenObject(
+ mLoadState, mCacheKey, Some(mChannelId), mAsyncOpenTime, mTiming,
+ std::move(initialClientInfo), InnerWindowIDForExtantDoc(docShell),
+ mLoadFlags, mLoadInfo->InternalContentPolicyType(),
+ UserActivation::IsHandlingUserInput(), 0 /* ProcessId */,
+ 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) {
+ // 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)
+ ->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.mSwitchedProcess) {
+ self->DisconnectChildListeners(aRejectValue.mStatus,
+ aRejectValue.mLoadGroupStatus);
+ }
+ self->RemoveObserver();
+ });
+ return NS_OK;
+}
+
+NS_IMETHODIMP ParentProcessDocumentChannel::Cancel(nsresult aStatus) {
+ LOG(("ParentProcessDocumentChannel Cancel [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(aStatus);
+
+ 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..5331be8d8b
--- /dev/null
+++ b/netwerk/ipc/ParentProcessDocumentChannel.h
@@ -0,0 +1,55 @@
+/* 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 "ProtocolUtils.h"
+#include "mozilla/net/DocumentChannel.h"
+#include "mozilla/net/DocumentLoadListener.h"
+#include "nsIObserver.h"
+
+namespace mozilla {
+namespace net {
+
+class ParentProcessDocumentChannel : public DocumentChannel,
+ public nsIAsyncVerifyRedirectCallback,
+ public nsIObserver {
+ public:
+ ParentProcessDocumentChannel(nsDocShellLoadState* aLoadState,
+ class LoadInfo* aLoadInfo,
+ nsLoadFlags aLoadFlags, uint32_t aCacheKey,
+ bool aUriModified, bool aIsXFOError);
+
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_NSIASYNCVERIFYREDIRECTCALLBACK
+ NS_DECL_NSIOBSERVER
+
+ NS_IMETHOD AsyncOpen(nsIStreamListener* aListener) override;
+ NS_IMETHOD Cancel(nsresult aStatusCode) override;
+
+ RefPtr<PDocumentChannelParent::RedirectToRealChannelPromise>
+ RedirectToRealChannel(
+ nsTArray<ipc::Endpoint<extensions::PStreamFilterParent>>&&
+ aStreamFilterEndpoints,
+ uint32_t aRedirectFlags, uint32_t aLoadFlags);
+
+ 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/ProxyConfigLookup.cpp b/netwerk/ipc/ProxyConfigLookup.cpp
new file mode 100644
index 0000000000..58f6088d31
--- /dev/null
+++ b/netwerk/ipc/ProxyConfigLookup.cpp
@@ -0,0 +1,98 @@
+/* -*- 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"
+
+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..da935c9028
--- /dev/null
+++ b/netwerk/ipc/ProxyConfigLookup.h
@@ -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/. */
+
+#ifndef mozilla_net_ProxyConfigLookup_h
+#define mozilla_net_ProxyConfigLookup_h
+
+#include <functional>
+#include "nsIProtocolProxyCallback.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/SocketProcessBridgeChild.cpp b/netwerk/ipc/SocketProcessBridgeChild.cpp
new file mode 100644
index 0000000000..01a5a7ad35
--- /dev/null
+++ b/netwerk/ipc/SocketProcessBridgeChild.cpp
@@ -0,0 +1,180 @@
+/* -*- 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/dom/ContentChild.h"
+#include "mozilla/net/NeckoChild.h"
+#include "nsIObserverService.h"
+#include "nsThreadUtils.h"
+#include "mozilla/Preferences.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(std::move(aEndpoint));
+ if (sSocketProcessBridgeChild->Inited()) {
+ return true;
+ }
+
+ sSocketProcessBridgeChild = nullptr;
+ return false;
+}
+
+// static
+already_AddRefed<SocketProcessBridgeChild>
+SocketProcessBridgeChild::GetSingleton() {
+ RefPtr<SocketProcessBridgeChild> child = sSocketProcessBridgeChild;
+ return child.forget();
+}
+
+static bool SocketProcessEnabled() {
+ static bool sInited = false;
+ static bool sSocketProcessEnabled = false;
+ if (!sInited) {
+ sSocketProcessEnabled = Preferences::GetBool("network.process.enabled") &&
+ XRE_IsContentProcess();
+ sInited = true;
+ }
+
+ return sSocketProcessEnabled;
+}
+
+// static
+RefPtr<SocketProcessBridgeChild::GetPromise>
+SocketProcessBridgeChild::GetSocketProcessBridge() {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (!SocketProcessEnabled()) {
+ 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(
+ Endpoint<PSocketProcessBridgeChild>&& aEndpoint)
+ : mShuttingDown(false) {
+ LOG(("CONSTRUCT SocketProcessBridgeChild::SocketProcessBridgeChild\n"));
+
+ mInited = aEndpoint.Bind(this);
+ if (!mInited) {
+ MOZ_ASSERT(false, "Bind failed!");
+ return;
+ }
+
+ nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
+ if (os) {
+ os->AddObserver(this, "content-child-shutdown", false);
+ }
+
+ mSocketProcessPid = aEndpoint.OtherPid();
+}
+
+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"));
+ 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..385d8e8ac6
--- /dev/null
+++ b/netwerk/ipc/SocketProcessBridgeChild.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_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();
+ typedef MozPromise<RefPtr<SocketProcessBridgeChild>, nsCString, false>
+ GetPromise;
+ static RefPtr<GetPromise> GetSocketProcessBridge();
+
+ mozilla::ipc::IPCResult RecvTest();
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+ void DeferredDestroy();
+ bool IsShuttingDown() const { return mShuttingDown; };
+ bool Inited() const { return mInited; };
+ ProcessId SocketProcessPid() const { return mSocketProcessPid; };
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SocketProcessBridgeChild);
+ static bool Create(Endpoint<PSocketProcessBridgeChild>&& aEndpoint);
+ explicit SocketProcessBridgeChild(
+ Endpoint<PSocketProcessBridgeChild>&& aEndpoint);
+ virtual ~SocketProcessBridgeChild();
+
+ static StaticRefPtr<SocketProcessBridgeChild> sSocketProcessBridgeChild;
+ bool mShuttingDown;
+ bool mInited = false;
+ 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..f28fa98925
--- /dev/null
+++ b/netwerk/ipc/SocketProcessBridgeParent.cpp
@@ -0,0 +1,65 @@
+/* -*- 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"
+
+#include "mozilla/ipc/BackgroundParent.h"
+#include "mozilla/ipc/Endpoint.h"
+#include "SocketProcessChild.h"
+
+namespace mozilla {
+namespace net {
+
+SocketProcessBridgeParent::SocketProcessBridgeParent(
+ ProcessId aId, Endpoint<PSocketProcessBridgeParent>&& aEndpoint)
+ : mId(aId), mClosed(false) {
+ LOG((
+ "CONSTRUCT SocketProcessBridgeParent::SocketProcessBridgeParent mId=%d\n",
+ mId));
+ MOZ_COUNT_CTOR(SocketProcessBridgeParent);
+ DebugOnly<bool> ok = aEndpoint.Bind(this);
+ MOZ_ASSERT(ok);
+}
+
+SocketProcessBridgeParent::~SocketProcessBridgeParent() {
+ LOG(("DESTRUCT SocketProcessBridgeParent::SocketProcessBridgeParent mId=%d\n",
+ mId));
+ MOZ_COUNT_DTOR(SocketProcessBridgeParent);
+}
+
+mozilla::ipc::IPCResult SocketProcessBridgeParent::RecvTest() {
+ LOG(("SocketProcessBridgeParent::RecvTest\n"));
+ Unused << SendTest();
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult SocketProcessBridgeParent::RecvInitBackground(
+ Endpoint<PBackgroundParent>&& aEndpoint) {
+ LOG(("SocketProcessBridgeParent::RecvInitBackground mId=%d\n", mId));
+ if (!ipc::BackgroundParent::Alloc(nullptr, std::move(aEndpoint))) {
+ return IPC_FAIL(this, "BackgroundParent::Alloc failed");
+ }
+
+ return IPC_OK();
+}
+
+void SocketProcessBridgeParent::ActorDestroy(ActorDestroyReason aWhy) {
+ LOG(("SocketProcessBridgeParent::ActorDestroy mId=%d\n", mId));
+
+ mClosed = true;
+ GetCurrentSerialEventTarget()->Dispatch(
+ NewRunnableMethod("net::SocketProcessBridgeParent::DeferredDestroy", this,
+ &SocketProcessBridgeParent::DeferredDestroy));
+}
+
+void SocketProcessBridgeParent::DeferredDestroy() {
+ if (SocketProcessChild* child = SocketProcessChild::GetSingleton()) {
+ child->DestroySocketProcessBridgeParent(mId);
+ }
+}
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/ipc/SocketProcessBridgeParent.h b/netwerk/ipc/SocketProcessBridgeParent.h
new file mode 100644
index 0000000000..f35559d464
--- /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)
+
+ explicit SocketProcessBridgeParent(
+ ProcessId aId, Endpoint<PSocketProcessBridgeParent>&& aEndpoint);
+
+ mozilla::ipc::IPCResult RecvTest();
+ mozilla::ipc::IPCResult RecvInitBackground(
+ Endpoint<PBackgroundParent>&& aEndpoint);
+
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+ void DeferredDestroy();
+
+ bool Closed() const { return mClosed; }
+
+ private:
+ ~SocketProcessBridgeParent();
+
+ ProcessId mId;
+ bool mClosed;
+};
+
+} // 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..3d422a0f7c
--- /dev/null
+++ b/netwerk/ipc/SocketProcessChild.cpp
@@ -0,0 +1,618 @@
+/* -*- 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/dom/MemoryReportRequest.h"
+#include "mozilla/ipc/CrashReporterClient.h"
+#include "mozilla/ipc/BackgroundChild.h"
+#include "mozilla/ipc/BackgroundParent.h"
+#include "mozilla/ipc/FileDescriptorSetChild.h"
+#include "mozilla/ipc/IPCStreamAlloc.h"
+#include "mozilla/ipc/ProcessChild.h"
+#include "mozilla/net/AltSvcTransactionChild.h"
+#include "mozilla/net/BackgroundDataBridgeParent.h"
+#include "mozilla/net/DNSRequestChild.h"
+#include "mozilla/net/DNSRequestParent.h"
+#include "mozilla/net/NativeDNSResolverOverrideChild.h"
+#include "mozilla/net/TRRServiceChild.h"
+#include "mozilla/ipc/PChildToParentStreamChild.h"
+#include "mozilla/ipc/PParentToChildStreamChild.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/RemoteLazyInputStreamChild.h"
+#include "mozilla/Telemetry.h"
+#include "nsDebugImpl.h"
+#include "nsHttpConnectionInfo.h"
+#include "nsHttpHandler.h"
+#include "nsIDNSService.h"
+#include "nsIHttpActivityObserver.h"
+#include "nsNetUtil.h"
+#include "nsNSSComponent.h"
+#include "nsSocketTransportService2.h"
+#include "nsThreadManager.h"
+#include "ProcessUtils.h"
+#include "SocketProcessBridgeParent.h"
+
+#if defined(XP_WIN)
+# include <process.h>
+#else
+# include <unistd.h>
+#endif
+
+#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
+# include "mozilla/Sandbox.h"
+#endif
+
+#ifdef MOZ_GECKO_PROFILER
+# include "ChildProfilerController.h"
+#endif
+
+#ifdef MOZ_WEBRTC
+# include "mozilla/net/WebrtcTCPSocketChild.h"
+#endif
+
+namespace mozilla {
+namespace net {
+
+using namespace ipc;
+
+SocketProcessChild* sSocketProcessChild;
+
+SocketProcessChild::SocketProcessChild()
+ : mShuttingDown(false), mMutex("SocketProcessChild::mMutex") {
+ 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
+
+bool SocketProcessChild::Init(base::ProcessId aParentPid,
+ const char* aParentBuildID, MessageLoop* aIOLoop,
+ UniquePtr<IPC::Channel> aChannel) {
+ if (NS_WARN_IF(NS_FAILED(nsThreadManager::get().Init()))) {
+ return false;
+ }
+ if (NS_WARN_IF(!Open(std::move(aChannel), aParentPid, aIOLoop))) {
+ return false;
+ }
+ // This must be sent before any IPDL message, which may hit sentinel
+ // errors due to parent and content processes having different
+ // versions.
+ MessageChannel* channel = GetIPCChannel();
+ if (channel && !channel->SendBuildIDsMatchMessage(aParentBuildID)) {
+ // We need to quit this process if the buildID doesn't match the parent's.
+ // This can occur when an update occurred in the background.
+ ProcessChild::QuickExit();
+ }
+
+ // Init crash reporter support.
+ CrashReporterClient::InitSingleton(this);
+
+ if (NS_FAILED(NS_InitMinimalXPCOM())) {
+ return false;
+ }
+
+ BackgroundChild::Startup();
+ SetThisProcessName("Socket Process");
+#if defined(XP_MACOSX)
+ // Close all current connections to the WindowServer. This ensures that the
+ // Activity Monitor will not label the socket process as "Not responding"
+ // because it's not running a native event loop. See bug 1384336.
+ CGSShutdownServerConnections();
+#endif // XP_MACOSX
+
+ 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;
+ }
+
+ return true;
+}
+
+void SocketProcessChild::ActorDestroy(ActorDestroyReason aWhy) {
+ LOG(("SocketProcessChild::ActorDestroy\n"));
+
+ mShuttingDown = true;
+
+ if (AbnormalShutdown == aWhy) {
+ NS_WARNING("Shutting down Socket process early due to a crash!");
+ ProcessChild::QuickExit();
+ }
+
+#ifdef MOZ_GECKO_PROFILER
+ if (mProfilerController) {
+ mProfilerController->Shutdown();
+ mProfilerController = nullptr;
+ }
+#endif
+
+ CrashReporterClient::DestroySingleton();
+ XRE_ShutdownChildProcess();
+}
+
+void SocketProcessChild::CleanUp() {
+ LOG(("SocketProcessChild::CleanUp\n"));
+
+ for (auto iter = mSocketProcessBridgeParentMap.Iter(); !iter.Done();
+ iter.Next()) {
+ if (!iter.Data()->Closed()) {
+ iter.Data()->Close();
+ }
+ }
+
+ {
+ MutexAutoLock lock(mMutex);
+ mBackgroundDataBridgeMap.Clear();
+ }
+ NS_ShutdownXPCOM(nullptr);
+}
+
+mozilla::ipc::IPCResult SocketProcessChild::RecvInit(
+ const SocketPorcessInitAttributes& aAttributes) {
+ Unused << RecvSetOffline(aAttributes.mOffline());
+ Unused << RecvSetConnectivity(aAttributes.mConnectivity());
+ if (aAttributes.mInitSandbox()) {
+ Unused << RecvInitLinuxSandbox(aAttributes.mSandboxBroker());
+ }
+ 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.Get(aContentProcessId, nullptr));
+
+ mSocketProcessBridgeParentMap.Put(
+ aContentProcessId, MakeRefPtr<SocketProcessBridgeParent>(
+ aContentProcessId, std::move(aEndpoint)));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult SocketProcessChild::RecvInitProfiler(
+ Endpoint<PProfilerChild>&& aEndpoint) {
+#ifdef MOZ_GECKO_PROFILER
+ mProfilerController =
+ mozilla::ChildProfilerController::Create(std::move(aEndpoint));
+#endif
+ return IPC_OK();
+}
+
+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();
+}
+
+PFileDescriptorSetChild* SocketProcessChild::AllocPFileDescriptorSetChild(
+ const FileDescriptor& aFD) {
+ return new FileDescriptorSetChild(aFD);
+}
+
+bool SocketProcessChild::DeallocPFileDescriptorSetChild(
+ PFileDescriptorSetChild* aActor) {
+ delete aActor;
+ return true;
+}
+
+PChildToParentStreamChild*
+SocketProcessChild::AllocPChildToParentStreamChild() {
+ MOZ_CRASH("PChildToParentStreamChild actors should be manually constructed!");
+}
+
+bool SocketProcessChild::DeallocPChildToParentStreamChild(
+ PChildToParentStreamChild* aActor) {
+ delete aActor;
+ return true;
+}
+
+PParentToChildStreamChild*
+SocketProcessChild::AllocPParentToChildStreamChild() {
+ return mozilla::ipc::AllocPParentToChildStreamChild();
+}
+
+bool SocketProcessChild::DeallocPParentToChildStreamChild(
+ PParentToChildStreamChild* aActor) {
+ delete aActor;
+ return true;
+}
+
+PChildToParentStreamChild*
+SocketProcessChild::SendPChildToParentStreamConstructor(
+ PChildToParentStreamChild* aActor) {
+ MOZ_ASSERT(NS_IsMainThread());
+ return PSocketProcessChild::SendPChildToParentStreamConstructor(aActor);
+}
+
+PFileDescriptorSetChild* SocketProcessChild::SendPFileDescriptorSetConstructor(
+ const FileDescriptor& aFD) {
+ MOZ_ASSERT(NS_IsMainThread());
+ return PSocketProcessChild::SendPFileDescriptorSetConstructor(aFD);
+}
+
+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 nsCString& aModelId) {
+ MOZ_ASSERT(gHttpHandler);
+ gHttpHandler->SetDeviceModelId(aModelId);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+SocketProcessChild::RecvOnHttpActivityDistributorActivated(
+ const bool& aIsActivated) {
+ if (nsCOMPtr<nsIHttpActivityObserver> distributor =
+ services::GetHttpActivityDistributor()) {
+ distributor->SetIsActive(aIsActivated);
+ }
+ 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 nsCString& aHost, const nsCString& aTrrServer, const uint16_t& aType,
+ const OriginAttributes& aOriginAttributes, const uint32_t& aFlags) {
+ RefPtr<DNSRequestHandler> handler = new DNSRequestHandler();
+ RefPtr<DNSRequestChild> actor = new DNSRequestChild(handler);
+ return actor.forget();
+}
+
+mozilla::ipc::IPCResult SocketProcessChild::RecvPDNSRequestConstructor(
+ PDNSRequestChild* aActor, const nsCString& aHost,
+ const nsCString& aTrrServer, const uint16_t& aType,
+ const OriginAttributes& aOriginAttributes, const uint32_t& aFlags) {
+ RefPtr<DNSRequestChild> actor = static_cast<DNSRequestChild*>(aActor);
+ RefPtr<DNSRequestHandler> handler =
+ actor->GetDNSRequest()->AsDNSRequestHandler();
+ handler->DoAsyncResolve(aHost, aTrrServer, aType, aOriginAttributes, aFlags);
+ return IPC_OK();
+}
+
+void SocketProcessChild::AddDataBridgeToMap(
+ uint64_t aChannelId, BackgroundDataBridgeParent* aActor) {
+ ipc::AssertIsOnBackgroundThread();
+ MutexAutoLock lock(mMutex);
+ mBackgroundDataBridgeMap.Put(aChannelId, aActor);
+}
+
+void SocketProcessChild::RemoveDataBridgeFromMap(uint64_t aChannelId) {
+ ipc::AssertIsOnBackgroundThread();
+ MutexAutoLock lock(mMutex);
+ mBackgroundDataBridgeMap.Remove(aChannelId);
+}
+
+Maybe<RefPtr<BackgroundDataBridgeParent>>
+SocketProcessChild::GetAndRemoveDataBridge(uint64_t aChannelId) {
+ MutexAutoLock lock(mMutex);
+ return mBackgroundDataBridgeMap.GetAndRemove(aChannelId);
+}
+
+mozilla::ipc::IPCResult SocketProcessChild::RecvClearSessionCache() {
+ if (EnsureNSSInitializedChromeOrContent()) {
+ nsNSSComponent::DoClearSSLExternalAndInternalSessionCache();
+ }
+ 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 nsCString& aTopic, const nsString& aData) {
+ nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+ if (obs) {
+ obs->NotifyObservers(nullptr, aTopic.get(), aData.get());
+ }
+ return IPC_OK();
+}
+
+already_AddRefed<PRemoteLazyInputStreamChild>
+SocketProcessChild::AllocPRemoteLazyInputStreamChild(const nsID& aID,
+ const uint64_t& aSize) {
+ RefPtr<RemoteLazyInputStreamChild> actor =
+ new RemoteLazyInputStreamChild(aID, aSize);
+ return actor.forget();
+}
+
+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();
+}
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/ipc/SocketProcessChild.h b/netwerk/ipc/SocketProcessChild.h
new file mode 100644
index 0000000000..2cee1d18a5
--- /dev/null
+++ b/netwerk/ipc/SocketProcessChild.h
@@ -0,0 +1,163 @@
+/* -*- 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/Mutex.h"
+#include "nsRefPtrHashtable.h"
+
+namespace mozilla {
+class ChildProfilerController;
+}
+
+namespace mozilla {
+namespace net {
+
+class SocketProcessBridgeParent;
+class BackgroundDataBridgeParent;
+
+// The IPC actor implements PSocketProcessChild in child process.
+// This is allocated and kept alive by SocketProcessImpl.
+class SocketProcessChild final
+ : public PSocketProcessChild,
+ public mozilla::ipc::ChildToParentStreamActorManager {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SocketProcessChild)
+
+ SocketProcessChild();
+
+ static SocketProcessChild* GetSingleton();
+
+ bool Init(base::ProcessId aParentPid, const char* aParentBuildID,
+ MessageLoop* aIOLoop, UniquePtr<IPC::Channel> aChannel);
+
+ 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);
+ mozilla::ipc::IPCResult RecvSocketProcessTelemetryPing();
+
+ PWebrtcTCPSocketChild* AllocPWebrtcTCPSocketChild(const Maybe<TabId>& tabId);
+ bool DeallocPWebrtcTCPSocketChild(PWebrtcTCPSocketChild* aActor);
+
+ already_AddRefed<PHttpTransactionChild> AllocPHttpTransactionChild();
+
+ PFileDescriptorSetChild* AllocPFileDescriptorSetChild(
+ const FileDescriptor& fd);
+ bool DeallocPFileDescriptorSetChild(PFileDescriptorSetChild* aActor);
+
+ PChildToParentStreamChild* AllocPChildToParentStreamChild();
+ bool DeallocPChildToParentStreamChild(PChildToParentStreamChild* aActor);
+ PParentToChildStreamChild* AllocPParentToChildStreamChild();
+ bool DeallocPParentToChildStreamChild(PParentToChildStreamChild* aActor);
+
+ void CleanUp();
+ void DestroySocketProcessBridgeParent(ProcessId aId);
+
+ PChildToParentStreamChild* SendPChildToParentStreamConstructor(
+ PChildToParentStreamChild* aActor) override;
+ PFileDescriptorSetChild* SendPFileDescriptorSetConstructor(
+ const FileDescriptor& aFD) override;
+ already_AddRefed<PHttpConnectionMgrChild> AllocPHttpConnectionMgrChild(
+ const HttpHandlerInitArgs& aArgs);
+ mozilla::ipc::IPCResult RecvUpdateDeviceModelId(const nsCString& aModelId);
+ mozilla::ipc::IPCResult RecvOnHttpActivityDistributorActivated(
+ const bool& aIsActivated);
+
+ 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() { return mShuttingDown; }
+
+ already_AddRefed<PDNSRequestChild> AllocPDNSRequestChild(
+ const nsCString& aHost, const nsCString& aTrrServer,
+ const uint16_t& aType, const OriginAttributes& aOriginAttributes,
+ const uint32_t& aFlags);
+ mozilla::ipc::IPCResult RecvPDNSRequestConstructor(
+ PDNSRequestChild* aActor, const nsCString& aHost,
+ const nsCString& aTrrServer, const uint16_t& aType,
+ const OriginAttributes& aOriginAttributes,
+ const uint32_t& 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();
+
+ 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 nsCString& aTopic,
+ const nsString& aData);
+
+ virtual already_AddRefed<PRemoteLazyInputStreamChild>
+ AllocPRemoteLazyInputStreamChild(const nsID& aID, const uint64_t& aSize);
+
+ mozilla::ipc::IPCResult RecvGetSocketData(GetSocketDataResolver&& aResolve);
+ mozilla::ipc::IPCResult RecvGetDNSCacheEntries(
+ GetDNSCacheEntriesResolver&& aResolve);
+ mozilla::ipc::IPCResult RecvGetHttpConnectionData(
+ GetHttpConnectionDataResolver&& aResolve);
+
+ protected:
+ friend class SocketProcessImpl;
+ ~SocketProcessChild();
+
+ private:
+ // Mapping of content process id and the SocketProcessBridgeParent.
+ // This table keeps SocketProcessBridgeParent alive in socket process.
+ nsRefPtrHashtable<nsUint32HashKey, SocketProcessBridgeParent>
+ mSocketProcessBridgeParentMap;
+
+#ifdef MOZ_GECKO_PROFILER
+ RefPtr<ChildProfilerController> mProfilerController;
+#endif
+
+ bool mShuttingDown;
+ // Protect the table below.
+ Mutex mMutex;
+ nsDataHashtable<nsUint64HashKey, RefPtr<BackgroundDataBridgeParent>>
+ mBackgroundDataBridgeMap;
+};
+
+} // 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..bf8ca10085
--- /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 "ProcessUtils.h"
+#include "SocketProcessParent.h"
+#include "mozilla/ipc/FileDescriptor.h"
+#include "nsAppRunner.h"
+#include "nsIOService.h"
+#include "nsIObserverService.h"
+
+#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
+# include "mozilla/SandboxBroker.h"
+# include "mozilla/SandboxBrokerPolicyFactory.h"
+# include "mozilla/SandboxSettings.h"
+#endif
+
+#ifdef MOZ_GECKO_PROFILER
+# include "ProfilerParent.h"
+#endif
+
+#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
+# include "mozilla/Sandbox.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(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;
+
+ nsAutoCString parentBuildID(mozilla::PlatformBuildID());
+ extraArgs.push_back("-parentBuildID");
+ extraArgs.push_back(parentBuildID.get());
+
+ SharedPreferenceSerializer prefSerializer;
+ if (!prefSerializer.SerializeToSharedMemory()) {
+ return false;
+ }
+ prefSerializer.AddSharedPrefCmdLineArgs(*this, extraArgs);
+
+ mLaunchPhase = LaunchPhase::Waiting;
+ if (!GeckoChildProcessHost::LaunchAndWaitForProcessHandle(extraArgs)) {
+ mLaunchPhase = LaunchPhase::Complete;
+ return false;
+ }
+
+ return true;
+}
+
+void SocketProcessHost::OnChannelConnected(int32_t peer_pid) {
+ MOZ_ASSERT(!NS_IsMainThread());
+
+ GeckoChildProcessHost::OnChannelConnected(peer_pid);
+
+ // Post a task to the main thread. Take the lock because mTaskFactory is not
+ // thread-safe.
+ RefPtr<Runnable> runnable;
+ {
+ MonitorAutoLock lock(mMonitor);
+ runnable = mTaskFactory.NewRunnableMethod(
+ &SocketProcessHost::OnChannelConnectedTask);
+ }
+ NS_DispatchToMainThread(runnable);
+}
+
+void SocketProcessHost::OnChannelError() {
+ MOZ_ASSERT(!NS_IsMainThread());
+ GeckoChildProcessHost::OnChannelError();
+
+ // Post a task to the main thread. Take the lock because mTaskFactory is not
+ // thread-safe.
+ RefPtr<Runnable> runnable;
+ {
+ MonitorAutoLock lock(mMonitor);
+ runnable =
+ mTaskFactory.NewRunnableMethod(&SocketProcessHost::OnChannelErrorTask);
+ }
+ NS_DispatchToMainThread(runnable);
+}
+
+void SocketProcessHost::OnChannelConnectedTask() {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (mLaunchPhase == LaunchPhase::Waiting) {
+ InitAfterConnect(true);
+ }
+}
+
+void SocketProcessHost::OnChannelErrorTask() {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (mLaunchPhase == LaunchPhase::Waiting) {
+ InitAfterConnect(false);
+ }
+}
+
+void SocketProcessHost::InitAfterConnect(bool aSucceeded) {
+ MOZ_ASSERT(mLaunchPhase == LaunchPhase::Waiting);
+ MOZ_ASSERT(!mSocketProcessParent);
+ MOZ_ASSERT(NS_IsMainThread());
+
+ mLaunchPhase = LaunchPhase::Complete;
+ if (!aSucceeded) {
+ if (mListener) {
+ mListener->OnProcessLaunchComplete(this, false);
+ }
+ return;
+ }
+
+ mSocketProcessParent = MakeUnique<SocketProcessParent>(this);
+ DebugOnly<bool> rv = mSocketProcessParent->Open(
+ TakeChannel(), base::GetProcId(GetChildProcessHandle()));
+ 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_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);
+
+#ifdef MOZ_GECKO_PROFILER
+ Unused << GetActor()->SendInitProfiler(
+ ProfilerParent::CreateForProcess(GetActor()->OtherPid()));
+#endif
+
+ 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.RevokeAll();
+ }
+
+ 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..beb1e13c89
--- /dev/null
+++ b/netwerk/ipc/SocketProcessHost.h
@@ -0,0 +1,151 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_net_SocketProcessHost_h
+#define mozilla_net_SocketProcessHost_h
+
+#include "mozilla/UniquePtr.h"
+#include "mozilla/ipc/GeckoChildProcessHost.h"
+#include "mozilla/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(int32_t peer_pid) override;
+ void OnChannelError() 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();
+ void OnChannelErrorTask();
+
+ // Called on the main thread after a connection has been established.
+ void InitAfterConnect(bool aSucceeded);
+
+ // Called on the main thread when the mSocketParent actor is shutting down.
+ void OnChannelClosed();
+
+ void DestroyProcess();
+
+#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::ipc::TaskFactory<SocketProcessHost> mTaskFactory;
+
+ enum class LaunchPhase { Unlaunched, Waiting, Complete };
+ LaunchPhase mLaunchPhase;
+
+ UniquePtr<SocketProcessParent> mSocketProcessParent;
+ // mShutdownRequested is set to true only when Shutdown() is called.
+ // If mShutdownRequested is false and the IPC channel is closed,
+ // OnProcessUnexpectedShutdown will be invoked.
+ bool mShutdownRequested;
+ bool mChannelClosed;
+
+#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..a67dd9c9bd
--- /dev/null
+++ b/netwerk/ipc/SocketProcessImpl.cpp
@@ -0,0 +1,108 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "SocketProcessImpl.h"
+
+#include "ProcessUtils.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 "ProcessUtils.h"
+#include "mozilla/ipc/IOThreadChild.h"
+
+#if defined(OS_WIN) && defined(MOZ_SANDBOX)
+# include "mozilla/sandboxTarget.h"
+#endif
+
+#ifdef OS_POSIX
+# include <unistd.h> // For sleep().
+#endif
+
+using mozilla::ipc::IOThreadChild;
+
+namespace mozilla {
+namespace net {
+
+LazyLogModule gSocketProcessLog("socketprocess");
+
+SocketProcessImpl::SocketProcessImpl(ProcessId aParentPid)
+ : ProcessChild(aParentPid) {}
+
+SocketProcessImpl::~SocketProcessImpl() = default;
+
+bool SocketProcessImpl::Init(int aArgc, char* aArgv[]) {
+#ifdef OS_POSIX
+ if (PR_GetEnv("MOZ_DEBUG_SOCKET_PROCESS")) {
+ printf_stderr("\n\nSOCKETPROCESSnSOCKETPROCESS\n debug me @ %d\n\n",
+ base::GetCurrentProcId());
+ sleep(30);
+ }
+#endif
+#if defined(MOZ_SANDBOX) && defined(OS_WIN)
+ LoadLibraryW(L"nss3.dll");
+ LoadLibraryW(L"softokn3.dll");
+ LoadLibraryW(L"freebl3.dll");
+ mozilla::SandboxTarget::Instance()->StartSandbox();
+#endif
+ char* parentBuildID = nullptr;
+ char* prefsHandle = nullptr;
+ char* prefMapHandle = nullptr;
+ char* prefsLen = nullptr;
+ char* prefMapSize = nullptr;
+
+ for (int i = 1; i < aArgc; i++) {
+ if (!aArgv[i]) {
+ continue;
+ }
+
+ if (strcmp(aArgv[i], "-parentBuildID") == 0) {
+ if (++i == aArgc) {
+ return false;
+ }
+
+ parentBuildID = aArgv[i];
+
+#ifdef XP_WIN
+ } else if (strcmp(aArgv[i], "-prefsHandle") == 0) {
+ if (++i == aArgc) {
+ return false;
+ }
+ prefsHandle = aArgv[i];
+ } else if (strcmp(aArgv[i], "-prefMapHandle") == 0) {
+ if (++i == aArgc) {
+ return false;
+ }
+ prefMapHandle = aArgv[i];
+#endif
+ } else if (strcmp(aArgv[i], "-prefsLen") == 0) {
+ if (++i == aArgc) {
+ return false;
+ }
+ prefsLen = aArgv[i];
+ } else if (strcmp(aArgv[i], "-prefMapSize") == 0) {
+ if (++i == aArgc) {
+ return false;
+ }
+ prefMapSize = aArgv[i];
+ }
+ }
+
+ ipc::SharedPreferenceDeserializer deserializer;
+ if (!deserializer.DeserializeFromSharedMemory(prefsHandle, prefMapHandle,
+ prefsLen, prefMapSize)) {
+ return false;
+ }
+
+ return mSocketProcessChild.Init(ParentPid(), parentBuildID,
+ IOThreadChild::message_loop(),
+ IOThreadChild::TakeChannel());
+}
+
+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..51d609b4a9
--- /dev/null
+++ b/netwerk/ipc/SocketProcessImpl.h
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_net_SocketProcessImpl_h
+#define mozilla_net_SocketProcessImpl_h
+
+#include "mozilla/ipc/ProcessChild.h"
+#include "SocketProcessChild.h"
+
+namespace mozilla {
+namespace net {
+
+// This class owns the subprocess instance of socket child process.
+// It is instantiated as a singleton in XRE_InitChildProcess.
+class SocketProcessImpl final : public mozilla::ipc::ProcessChild {
+ protected:
+ typedef mozilla::ipc::ProcessChild ProcessChild;
+
+ public:
+ explicit SocketProcessImpl(ProcessId aParentPid);
+ ~SocketProcessImpl();
+
+ bool Init(int aArgc, char* aArgv[]) override;
+ void CleanUp() override;
+
+ private:
+ SocketProcessChild mSocketProcessChild;
+ DISALLOW_COPY_AND_ASSIGN(SocketProcessImpl);
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozilla_net_SocketProcessImpl_h
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..7e3e0ac63c
--- /dev/null
+++ b/netwerk/ipc/SocketProcessParent.cpp
@@ -0,0 +1,429 @@
+/* -*- 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 "mozilla/dom/MemoryReportRequest.h"
+#include "mozilla/ipc/FileDescriptorSetParent.h"
+#include "mozilla/ipc/IPCStreamAlloc.h"
+#include "mozilla/ipc/PChildToParentStreamParent.h"
+#include "mozilla/ipc/PParentToChildStreamParent.h"
+#include "mozilla/net/DNSRequestParent.h"
+#include "mozilla/net/ProxyConfigLookupParent.h"
+#include "mozilla/RemoteLazyInputStreamParent.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/TelemetryIPC.h"
+#include "nsIAppStartup.h"
+#include "nsIHttpActivityObserver.h"
+#include "nsNSSIOLayer.h"
+#include "PSMIPCCommon.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)
+
+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());
+
+ if (PR_GetEnv("MOZ_CRASHREPORTER_SHUTDOWN")) {
+ printf_stderr("Shutting down due to socket process crash.\n");
+ nsCOMPtr<nsIAppStartup> appService =
+ do_GetService("@mozilla.org/toolkit/app-startup;1");
+ if (appService) {
+ bool userAllowedQuit = true;
+ appService->Quit(nsIAppStartup::eForceQuit, 1, &userAllowedQuit);
+ }
+ }
+ }
+
+ 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 nsCString& aHost, const nsCString& aTrrServer, const uint16_t& aType,
+ const OriginAttributes& aOriginAttributes, const uint32_t& aFlags) {
+ RefPtr<DNSRequestHandler> handler = new DNSRequestHandler();
+ RefPtr<DNSRequestParent> actor = new DNSRequestParent(handler);
+ return actor.forget();
+}
+
+mozilla::ipc::IPCResult SocketProcessParent::RecvPDNSRequestConstructor(
+ PDNSRequestParent* aActor, const nsCString& aHost,
+ const nsCString& aTrrServer, const uint16_t& aType,
+ const OriginAttributes& aOriginAttributes, const uint32_t& aFlags) {
+ RefPtr<DNSRequestParent> actor = static_cast<DNSRequestParent*>(aActor);
+ RefPtr<DNSRequestHandler> handler =
+ actor->GetDNSRequest()->AsDNSRequestHandler();
+ handler->DoAsyncResolve(aHost, aTrrServer, aType, aOriginAttributes, aFlags);
+ return IPC_OK();
+}
+
+mozilla::ipc::PFileDescriptorSetParent*
+SocketProcessParent::AllocPFileDescriptorSetParent(const FileDescriptor& aFD) {
+ return new mozilla::ipc::FileDescriptorSetParent(aFD);
+}
+
+bool SocketProcessParent::DeallocPFileDescriptorSetParent(
+ PFileDescriptorSetParent* aActor) {
+ delete static_cast<mozilla::ipc::FileDescriptorSetParent*>(aActor);
+ return true;
+}
+
+mozilla::ipc::PChildToParentStreamParent*
+SocketProcessParent::AllocPChildToParentStreamParent() {
+ return mozilla::ipc::AllocPChildToParentStreamParent();
+}
+
+bool SocketProcessParent::DeallocPChildToParentStreamParent(
+ PChildToParentStreamParent* aActor) {
+ delete aActor;
+ return true;
+}
+
+mozilla::ipc::PParentToChildStreamParent*
+SocketProcessParent::AllocPParentToChildStreamParent() {
+ MOZ_CRASH("PParentToChildStreamChild actors should be manually constructed!");
+}
+
+bool SocketProcessParent::DeallocPParentToChildStreamParent(
+ PParentToChildStreamParent* aActor) {
+ delete aActor;
+ return true;
+}
+
+mozilla::ipc::PParentToChildStreamParent*
+SocketProcessParent::SendPParentToChildStreamConstructor(
+ PParentToChildStreamParent* aActor) {
+ MOZ_ASSERT(NS_IsMainThread());
+ return PSocketProcessParent::SendPParentToChildStreamConstructor(aActor);
+}
+
+mozilla::ipc::PFileDescriptorSetParent*
+SocketProcessParent::SendPFileDescriptorSetConstructor(
+ const FileDescriptor& aFD) {
+ MOZ_ASSERT(NS_IsMainThread());
+ return PSocketProcessParent::SendPFileDescriptorSetConstructor(aFD);
+}
+
+mozilla::ipc::IPCResult SocketProcessParent::RecvObserveHttpActivity(
+ const HttpActivityArgs& aArgs, const uint32_t& aActivityType,
+ const uint32_t& aActivitySubtype, const PRTime& aTimestamp,
+ const uint64_t& aExtraSizeData, const nsCString& aExtraStringData) {
+ nsCOMPtr<nsIHttpActivityDistributor> activityDistributor =
+ services::GetHttpActivityDistributor();
+ MOZ_ASSERT(activityDistributor);
+
+ Unused << activityDistributor->ObserveActivityWithArgs(
+ aArgs, aActivityType, aActivitySubtype, aTimestamp, aExtraSizeData,
+ aExtraStringData);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult SocketProcessParent::RecvInitBackground(
+ Endpoint<PBackgroundParent>&& aEndpoint) {
+ LOG(("SocketProcessParent::RecvInitBackground\n"));
+ if (!ipc::BackgroundParent::Alloc(nullptr, std::move(aEndpoint))) {
+ return IPC_FAIL(this, "BackgroundParent::Alloc failed");
+ }
+
+ return IPC_OK();
+}
+
+already_AddRefed<PAltServiceParent>
+SocketProcessParent::AllocPAltServiceParent() {
+ RefPtr<AltServiceParent> actor = new AltServiceParent();
+ return actor.forget();
+}
+
+mozilla::ipc::IPCResult SocketProcessParent::RecvGetTLSClientCert(
+ const nsCString& aHostName, const OriginAttributes& aOriginAttributes,
+ const int32_t& aPort, const uint32_t& aProviderFlags,
+ const uint32_t& aProviderTlsFlags, const ByteArray& aServerCert,
+ Maybe<ByteArray>&& aClientCert, nsTArray<ByteArray>&& aCollectedCANames,
+ bool* aSucceeded, ByteArray* aOutCert, ByteArray* aOutKey,
+ nsTArray<ByteArray>* aBuiltChain) {
+ *aSucceeded = false;
+
+ SECItem serverCertItem = {
+ siBuffer, const_cast<uint8_t*>(aServerCert.data().Elements()),
+ static_cast<unsigned int>(aServerCert.data().Length())};
+ UniqueCERTCertificate serverCert(CERT_NewTempCertificate(
+ CERT_GetDefaultCertDB(), &serverCertItem, nullptr, false, true));
+ if (!serverCert) {
+ return IPC_OK();
+ }
+
+ RefPtr<nsIX509Cert> clientCert;
+ if (aClientCert) {
+ clientCert = nsNSSCertificate::ConstructFromDER(
+ BitwiseCast<char*, uint8_t*>(aClientCert->data().Elements()),
+ aClientCert->data().Length());
+ if (!clientCert) {
+ return IPC_OK();
+ }
+ }
+
+ ClientAuthInfo info(aHostName, aOriginAttributes, aPort, aProviderFlags,
+ aProviderTlsFlags, clientCert);
+ nsTArray<nsTArray<uint8_t>> collectedCANames;
+ for (auto& name : aCollectedCANames) {
+ collectedCANames.AppendElement(std::move(name.data()));
+ }
+
+ UniqueCERTCertificate cert;
+ UniqueSECKEYPrivateKey key;
+ UniqueCERTCertList builtChain;
+ SECStatus status =
+ DoGetClientAuthData(std::move(info), serverCert,
+ std::move(collectedCANames), cert, key, builtChain);
+ if (status != SECSuccess) {
+ return IPC_OK();
+ }
+
+ SerializeClientCertAndKey(cert, key, *aOutCert, *aOutKey);
+
+ if (builtChain) {
+ for (CERTCertListNode* n = CERT_LIST_HEAD(builtChain);
+ !CERT_LIST_END(n, builtChain); n = CERT_LIST_NEXT(n)) {
+ ByteArray array;
+ array.data().AppendElements(n->cert->derCert.data, n->cert->derCert.len);
+ aBuiltChain->AppendElement(std::move(array));
+ }
+ }
+
+ *aSucceeded = true;
+ return IPC_OK();
+}
+
+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(
+ UniquePtr<SocketProcessParent>&& aParent)
+ : Runnable("net::DeferredDeleteSocketProcessParent"),
+ mParent(std::move(aParent)) {}
+
+ NS_IMETHODIMP Run() override { return NS_OK; }
+
+ private:
+ UniquePtr<SocketProcessParent> mParent;
+};
+
+/* static */
+void SocketProcessParent::Destroy(UniquePtr<SocketProcessParent>&& aParent) {
+ NS_DispatchToMainThread(
+ new DeferredDeleteSocketProcessParent(std::move(aParent)));
+}
+
+already_AddRefed<PRemoteLazyInputStreamParent>
+SocketProcessParent::AllocPRemoteLazyInputStreamParent(const nsID& aID,
+ const uint64_t& aSize) {
+ RefPtr<RemoteLazyInputStreamParent> actor =
+ RemoteLazyInputStreamParent::Create(aID, aSize, this);
+ return actor.forget();
+}
+
+mozilla::ipc::IPCResult
+SocketProcessParent::RecvPRemoteLazyInputStreamConstructor(
+ PRemoteLazyInputStreamParent* aActor, const nsID& aID,
+ const uint64_t& aSize) {
+ if (!static_cast<RemoteLazyInputStreamParent*>(aActor)->HasValidStream()) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ return IPC_OK();
+}
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/ipc/SocketProcessParent.h b/netwerk/ipc/SocketProcessParent.h
new file mode 100644
index 0000000000..679ccefac5
--- /dev/null
+++ b/netwerk/ipc/SocketProcessParent.h
@@ -0,0 +1,132 @@
+/* -*- 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/BackgroundParent.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 ipc::ParentToChildStreamActorManager {
+ public:
+ friend class SocketProcessHost;
+
+ explicit SocketProcessParent(SocketProcessHost* aHost);
+ ~SocketProcessParent();
+
+ 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 nsCString& aHost, const nsCString& aTrrServer,
+ const uint16_t& aType, const OriginAttributes& aOriginAttributes,
+ const uint32_t& aFlags);
+ virtual mozilla::ipc::IPCResult RecvPDNSRequestConstructor(
+ PDNSRequestParent* actor, const nsCString& hostName,
+ const nsCString& trrServer, const uint16_t& type,
+ const OriginAttributes& aOriginAttributes,
+ const uint32_t& flags) override;
+
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+ bool SendRequestMemoryReport(const uint32_t& aGeneration,
+ const bool& aAnonymize,
+ const bool& aMinimizeMemoryUsage,
+ const Maybe<ipc::FileDescriptor>& aDMDFile);
+
+ PFileDescriptorSetParent* AllocPFileDescriptorSetParent(
+ const FileDescriptor& fd);
+ bool DeallocPFileDescriptorSetParent(PFileDescriptorSetParent* aActor);
+
+ PChildToParentStreamParent* AllocPChildToParentStreamParent();
+ bool DeallocPChildToParentStreamParent(PChildToParentStreamParent* aActor);
+ PParentToChildStreamParent* AllocPParentToChildStreamParent();
+ bool DeallocPParentToChildStreamParent(PParentToChildStreamParent* aActor);
+
+ PParentToChildStreamParent* SendPParentToChildStreamConstructor(
+ PParentToChildStreamParent* aActor) override;
+ PFileDescriptorSetParent* SendPFileDescriptorSetConstructor(
+ const FileDescriptor& aFD) override;
+
+ mozilla::ipc::IPCResult RecvObserveHttpActivity(
+ const HttpActivityArgs& aArgs, const uint32_t& aActivityType,
+ const uint32_t& aActivitySubtype, const PRTime& aTimestamp,
+ const uint64_t& aExtraSizeData, const nsCString& aExtraStringData);
+
+ mozilla::ipc::IPCResult RecvInitBackground(
+ Endpoint<PBackgroundParent>&& aEndpoint);
+
+ already_AddRefed<PAltServiceParent> AllocPAltServiceParent();
+
+ mozilla::ipc::IPCResult RecvGetTLSClientCert(
+ const nsCString& aHostName, const OriginAttributes& aOriginAttributes,
+ const int32_t& aPort, const uint32_t& aProviderFlags,
+ const uint32_t& aProviderTlsFlags, const ByteArray& aServerCert,
+ Maybe<ByteArray>&& aClientCert, nsTArray<ByteArray>&& aCollectedCANames,
+ bool* aSucceeded, ByteArray* aOutCert, ByteArray* aOutKey,
+ nsTArray<ByteArray>* aBuiltChain);
+
+ 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);
+
+ already_AddRefed<PRemoteLazyInputStreamParent>
+ AllocPRemoteLazyInputStreamParent(const nsID& aID, const uint64_t& aSize);
+
+ mozilla::ipc::IPCResult RecvPRemoteLazyInputStreamConstructor(
+ PRemoteLazyInputStreamParent* aActor, const nsID& aID,
+ const uint64_t& aSize);
+
+ private:
+ SocketProcessHost* mHost;
+ UniquePtr<dom::MemoryReportRequestHost> mMemoryReportRequest;
+
+ static void Destroy(UniquePtr<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..6124d4ff2a
--- /dev/null
+++ b/netwerk/ipc/moz.build
@@ -0,0 +1,99 @@
+# -*- 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",
+ "ProxyConfigLookup.h",
+ "ProxyConfigLookupChild.h",
+ "ProxyConfigLookupParent.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",
+ "NeckoParent.cpp",
+ "NeckoTargetHolder.cpp",
+ "ParentChannelWrapper.cpp",
+ "ParentProcessDocumentChannel.cpp",
+ "ProxyConfigLookup.cpp",
+ "ProxyConfigLookupChild.cpp",
+ "ProxyConfigLookupParent.cpp",
+ "SocketProcessBridgeChild.cpp",
+ "SocketProcessBridgeParent.cpp",
+ "SocketProcessChild.cpp",
+ "SocketProcessHost.cpp",
+ "SocketProcessImpl.cpp",
+ "SocketProcessParent.cpp",
+]
+
+IPDL_SOURCES = [
+ "NeckoChannelParams.ipdlh",
+ "PDataChannel.ipdl",
+ "PDocumentChannel.ipdl",
+ "PFileChannel.ipdl",
+ "PInputChannelThrottleQueue.ipdl",
+ "PNecko.ipdl",
+ "PProxyConfigLookup.ipdl",
+ "PSimpleChannel.ipdl",
+ "PSocketProcess.ipdl",
+ "PSocketProcessBridge.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")