Source code

Revision control

Other Tools

/* -*- 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/. */
// HttpLog.h should generally be included first
#include "HttpLog.h"
#include "nsHttp.h"
#include "nsICacheEntry.h"
#include "mozilla/BasePrincipal.h"
#include "mozilla/Unused.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/DocGroup.h"
#include "mozilla/dom/ServiceWorkerUtils.h"
#include "mozilla/dom/BrowserChild.h"
#include "mozilla/extensions/StreamFilterParent.h"
#include "mozilla/ipc/FileDescriptorSetChild.h"
#include "mozilla/ipc/IPCStreamUtils.h"
#include "mozilla/net/NeckoChild.h"
#include "mozilla/net/HttpChannelChild.h"
#include "mozilla/net/UrlClassifierCommon.h"
#include "mozilla/net/UrlClassifierFeatureFactory.h"
#include "AltDataOutputStreamChild.h"
#include "CookieServiceChild.h"
#include "HttpBackgroundChannelChild.h"
#include "nsCOMPtr.h"
#include "nsContentPolicyUtils.h"
#include "nsDOMNavigationTiming.h"
#include "nsGlobalWindow.h"
#include "nsStringStream.h"
#include "nsHttpChannel.h"
#include "nsHttpHandler.h"
#include "nsNetUtil.h"
#include "nsSerializationHelper.h"
#include "mozilla/Attributes.h"
#include "mozilla/dom/PerformanceStorage.h"
#include "mozilla/ipc/InputStreamUtils.h"
#include "mozilla/ipc/URIUtils.h"
#include "mozilla/ipc/BackgroundUtils.h"
#include "mozilla/net/DNS.h"
#include "mozilla/net/SocketProcessBridgeChild.h"
#include "mozilla/StaticPrefs_network.h"
#include "mozilla/StoragePrincipalHelper.h"
#include "SerializedLoadContext.h"
#include "nsInputStreamPump.h"
#include "InterceptedChannel.h"
#include "nsContentSecurityManager.h"
#include "nsICompressConvStats.h"
#include "nsIDeprecationWarner.h"
#include "mozilla/dom/Document.h"
#include "nsIScriptError.h"
#include "nsISerialEventTarget.h"
#include "nsRedirectHistoryEntry.h"
#include "nsSocketTransportService2.h"
#include "nsStreamUtils.h"
#include "nsThreadUtils.h"
#include "nsCORSListenerProxy.h"
#include "nsApplicationCache.h"
#include "ClassifierDummyChannel.h"
#include "nsIOService.h"
#ifdef MOZ_TASK_TRACER
# include "GeckoTaskTracer.h"
#endif
#ifdef MOZ_GECKO_PROFILER
# include "ProfilerMarkerPayload.h"
#endif
#include <functional>
using namespace mozilla::dom;
using namespace mozilla::ipc;
namespace mozilla {
namespace net {
//-----------------------------------------------------------------------------
// HttpChannelChild
//-----------------------------------------------------------------------------
HttpChannelChild::HttpChannelChild()
: HttpAsyncAborter<HttpChannelChild>(this),
NeckoTargetHolder(nullptr),
mBgChildMutex("HttpChannelChild::BgChildMutex"),
mEventTargetMutex("HttpChannelChild::EventTargetMutex"),
mCacheEntryId(0),
mCacheKey(0),
mCacheFetchCount(0),
mCacheExpirationTime(nsICacheEntry::NO_EXPIRATION_TIME),
mDeletingChannelSent(false),
mIsFromCache(false),
mIsRacing(false),
mCacheNeedToReportBytesReadInitialized(false),
mNeedToReportBytesRead(true),
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
mBackgroundChildQueueFinalState(BCKCHILD_UNKNOWN),
#endif
mCacheEntryAvailable(false),
mAltDataCacheEntryAvailable(false),
mSendResumeAt(false),
mKeptAlive(false),
mIPCActorDeleted(false),
mSuspendSent(false),
mIsLastPartOfMultiPart(false),
mSuspendForWaitCompleteRedirectSetup(false),
mRecvOnStartRequestSentCalled(false),
mSuspendedByWaitingForPermissionCookie(false) {
LOG(("Creating HttpChannelChild @%p\n", this));
mChannelCreationTime = PR_Now();
mChannelCreationTimestamp = TimeStamp::Now();
mLastStatusReported =
mChannelCreationTimestamp; // in case we enable the profiler after Init()
mAsyncOpenTime = TimeStamp::Now();
mEventQ = new ChannelEventQueue(static_cast<nsIHttpChannel*>(this));
// Ensure that the cookie service is initialized before the first
// IPC HTTP channel is created.
// We require that the parent cookie service actor exists while
// processing HTTP responses.
RefPtr<CookieServiceChild> cookieService = CookieServiceChild::GetSingleton();
}
HttpChannelChild::~HttpChannelChild() {
LOG(("Destroying HttpChannelChild @%p\n", this));
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
if (mDoDiagnosticAssertWhenOnStopNotCalledOnDestroy && mAsyncOpenSucceeded &&
!mSuccesfullyRedirected && !mOnStopRequestCalled) {
bool emptyBgChildQueue, nullBgChild;
{
MutexAutoLock lock(mBgChildMutex);
nullBgChild = !mBgChild;
emptyBgChildQueue = !nullBgChild && mBgChild->IsQueueEmpty();
}
uint32_t flags =
(mRedirectChannelChild ? 1 << 0 : 0) |
(mEventQ->IsEmpty() ? 1 << 1 : 0) | (nullBgChild ? 1 << 2 : 0) |
(emptyBgChildQueue ? 1 << 3 : 0) |
(mOnStartRequestCalled ? 1 << 4 : 0) |
(mBackgroundChildQueueFinalState == BCKCHILD_EMPTY ? 1 << 5 : 0) |
(mBackgroundChildQueueFinalState == BCKCHILD_NON_EMPTY ? 1 << 6 : 0) |
(mRemoteChannelExistedAtCancel ? 1 << 7 : 0) |
(mEverHadBgChildAtAsyncOpen ? 1 << 8 : 0) |
(mEverHadBgChildAtConnectParent ? 1 << 9 : 0) |
(mCreateBackgroundChannelFailed ? 1 << 10 : 0) |
(mBgInitFailCallbackTriggered ? 1 << 11 : 0) |
(mCanSendAtCancel ? 1 << 12 : 0) | (!!mSuspendCount ? 1 << 13 : 0) |
(!!mCallOnResume ? 1 << 14 : 0);
MOZ_CRASH_UNSAFE_PRINTF(
"~HttpChannelChild, mOnStopRequestCalled=false, mStatus=0x%08x, "
"mActorDestroyReason=%d, 20200717 flags=%u",
static_cast<uint32_t>(nsresult(mStatus)),
static_cast<int32_t>(mActorDestroyReason ? *mActorDestroyReason : -1),
flags);
}
#endif
ReleaseMainThreadOnlyReferences();
}
void HttpChannelChild::ReleaseMainThreadOnlyReferences() {
if (NS_IsMainThread()) {
// Already on main thread, let dtor to
// take care of releasing references
return;
}
NS_ReleaseOnMainThread("HttpChannelChild::mRedirectChannelChild",
mRedirectChannelChild.forget());
}
//-----------------------------------------------------------------------------
// HttpChannelChild::nsISupports
//-----------------------------------------------------------------------------
NS_IMPL_ADDREF(HttpChannelChild)
NS_IMETHODIMP_(MozExternalRefCountType) HttpChannelChild::Release() {
if (!NS_IsMainThread()) {
nsrefcnt count = mRefCnt;
nsresult rv = NS_DispatchToMainThread(NewNonOwningRunnableMethod(
"HttpChannelChild::Release", this, &HttpChannelChild::Release));
// Continue Release procedure if failed to dispatch to main thread.
if (!NS_WARN_IF(NS_FAILED(rv))) {
return count - 1;
}
}
nsrefcnt count = --mRefCnt;
MOZ_ASSERT(int32_t(count) >= 0, "dup release");
NS_LOG_RELEASE(this, count, "HttpChannelChild");
// Normally we Send_delete in OnStopRequest, but when we need to retain the
// remote channel for security info IPDL itself holds 1 reference, so we
// Send_delete when refCnt==1. But if !CanSend(), then there's nobody to send
// to, so we fall through.
if (mKeptAlive && count == 1 && CanSend()) {
mKeptAlive = false;
// We send a message to the parent, which calls SendDelete, and then the
// child calling Send__delete__() to finally drop the refcount to 0.
TrySendDeletingChannel();
return 1;
}
if (count == 0) {
mRefCnt = 1; /* stabilize */
// We don't have a listener when AsyncOpen has failed or when this channel
// has been sucessfully redirected.
if (MOZ_LIKELY(mOnStartRequestCalled && mOnStopRequestCalled) ||
!mListener) {
delete this;
return 0;
}
// This makes sure we fulfill the stream listener contract all the time.
if (NS_SUCCEEDED(mStatus)) {
mStatus = NS_ERROR_ABORT;
}
nsresult rv = NS_DispatchToMainThread(
NewRunnableMethod("~HttpChannelChild>DoNotifyListener", this,
&HttpChannelChild::DoNotifyListener));
if (NS_FAILED(rv)) {
// Prevent loops.
mOnStartRequestCalled = true;
mOnStopRequestCalled = true;
// This reverts the stabilization we have made above. Instead of
// doing `delete this` it's safer to call Release() again in case the
// dispatch somehow leaks and there has been a reference added.
return Release();
}
return 0;
}
return count;
}
NS_INTERFACE_MAP_BEGIN(HttpChannelChild)
NS_INTERFACE_MAP_ENTRY(nsIRequest)
NS_INTERFACE_MAP_ENTRY(nsIChannel)
NS_INTERFACE_MAP_ENTRY(nsIHttpChannel)
NS_INTERFACE_MAP_ENTRY(nsIHttpChannelInternal)
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsICacheInfoChannel,
!mMultiPartID.isSome())
NS_INTERFACE_MAP_ENTRY(nsIResumableChannel)
NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)
NS_INTERFACE_MAP_ENTRY(nsIClassOfService)
NS_INTERFACE_MAP_ENTRY(nsIProxiedChannel)
NS_INTERFACE_MAP_ENTRY(nsITraceableChannel)
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIApplicationCacheContainer,
!mMultiPartID.isSome())
NS_INTERFACE_MAP_ENTRY(nsIApplicationCacheChannel)
NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectCallback)
NS_INTERFACE_MAP_ENTRY(nsIChildChannel)
NS_INTERFACE_MAP_ENTRY(nsIHttpChannelChild)
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIMultiPartChannel, mMultiPartID.isSome())
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIThreadRetargetableRequest,
!mMultiPartID.isSome())
NS_INTERFACE_MAP_ENTRY_CONCRETE(HttpChannelChild)
NS_INTERFACE_MAP_END_INHERITING(HttpBaseChannel)
//-----------------------------------------------------------------------------
// HttpChannelChild::PHttpChannelChild
//-----------------------------------------------------------------------------
void HttpChannelChild::OnBackgroundChildReady(
HttpBackgroundChannelChild* aBgChild) {
LOG(("HttpChannelChild::OnBackgroundChildReady [this=%p, bgChild=%p]\n", this,
aBgChild));
MOZ_ASSERT(OnSocketThread());
{
MutexAutoLock lock(mBgChildMutex);
// mBgChild might be removed or replaced while the original background
// channel is inited on STS thread.
if (mBgChild != aBgChild) {
return;
}
MOZ_ASSERT(mBgInitFailCallback);
mBgInitFailCallback = nullptr;
}
}
void HttpChannelChild::OnBackgroundChildDestroyed(
HttpBackgroundChannelChild* aBgChild) {
LOG(("HttpChannelChild::OnBackgroundChildDestroyed [this=%p]\n", this));
// This function might be called during shutdown phase, so OnSocketThread()
// might return false even on STS thread. Use IsOnCurrentThreadInfallible()
// to get correct information.
MOZ_ASSERT(gSocketTransportService);
MOZ_ASSERT(gSocketTransportService->IsOnCurrentThreadInfallible());
nsCOMPtr<nsIRunnable> callback;
{
MutexAutoLock lock(mBgChildMutex);
// mBgChild might be removed or replaced while the original background
// channel is destroyed on STS thread.
if (aBgChild != mBgChild) {
return;
}
mBgChild = nullptr;
callback = std::move(mBgInitFailCallback);
}
if (callback) {
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
mBgInitFailCallbackTriggered = true;
#endif
nsCOMPtr<nsISerialEventTarget> neckoTarget = GetNeckoTarget();
neckoTarget->Dispatch(callback, NS_DISPATCH_NORMAL);
}
}
void HttpChannelChild::AssociateApplicationCache(const nsCString& aGroupID,
const nsCString& aClientID) {
LOG(("HttpChannelChild::AssociateApplicationCache [this=%p]\n", this));
mApplicationCache = new nsApplicationCache();
mLoadedFromApplicationCache = true;
mApplicationCache->InitAsHandle(aGroupID, aClientID);
}
mozilla::ipc::IPCResult HttpChannelChild::RecvOnStartRequestSent() {
LOG(("HttpChannelChild::RecvOnStartRequestSent [this=%p]\n", this));
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!mRecvOnStartRequestSentCalled);
mRecvOnStartRequestSentCalled = true;
if (mSuspendedByWaitingForPermissionCookie) {
mSuspendedByWaitingForPermissionCookie = false;
mEventQ->Resume();
}
return IPC_OK();
}
void HttpChannelChild::ProcessOnStartRequest(
const nsHttpResponseHead& aResponseHead, const bool& aUseResponseHead,
const nsHttpHeaderArray& aRequestHeaders,
const HttpChannelOnStartRequestArgs& aArgs) {
LOG(("HttpChannelChild::ProcessOnStartRequest [this=%p]\n", this));
MOZ_ASSERT(OnSocketThread());
mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
this, [self = UnsafePtr<HttpChannelChild>(this), aResponseHead,
aUseResponseHead, aRequestHeaders, aArgs]() {
self->OnStartRequest(aResponseHead, aUseResponseHead, aRequestHeaders,
aArgs);
}));
}
static void ResourceTimingStructArgsToTimingsStruct(
const ResourceTimingStructArgs& aArgs, TimingStruct& aTimings) {
aTimings.domainLookupStart = aArgs.domainLookupStart();
aTimings.domainLookupEnd = aArgs.domainLookupEnd();
aTimings.connectStart = aArgs.connectStart();
aTimings.tcpConnectEnd = aArgs.tcpConnectEnd();
aTimings.secureConnectionStart = aArgs.secureConnectionStart();
aTimings.connectEnd = aArgs.connectEnd();
aTimings.requestStart = aArgs.requestStart();
aTimings.responseStart = aArgs.responseStart();
aTimings.responseEnd = aArgs.responseEnd();
}
void HttpChannelChild::OnStartRequest(
const nsHttpResponseHead& aResponseHead, const bool& aUseResponseHead,
const nsHttpHeaderArray& aRequestHeaders,
const HttpChannelOnStartRequestArgs& aArgs) {
LOG(("HttpChannelChild::OnStartRequest [this=%p]\n", this));
// If this channel was aborted by ActorDestroy, then there may be other
// OnStartRequest/OnStopRequest/OnDataAvailable IPC messages that need to
// be handled. In that case we just ignore them to avoid calling the listener
// twice.
if (mOnStartRequestCalled && mIPCActorDeleted) {
return;
}
// Copy arguments only. It's possible to handle other IPC between
// OnStartRequest and DoOnStartRequest.
mComputedCrossOriginOpenerPolicy = aArgs.openerPolicy();
if (!mCanceled && NS_SUCCEEDED(mStatus)) {
mStatus = aArgs.channelStatus();
}
// Cookies headers should not be visible to the child process
MOZ_ASSERT(!aRequestHeaders.HasHeader(nsHttp::Cookie));
MOZ_ASSERT(!nsHttpResponseHead(aResponseHead).HasHeader(nsHttp::Set_Cookie));
if (aUseResponseHead && !mCanceled)
mResponseHead = MakeUnique<nsHttpResponseHead>(aResponseHead);
if (!aArgs.securityInfoSerialization().IsEmpty()) {
[[maybe_unused]] nsresult rv = NS_DeserializeObject(
aArgs.securityInfoSerialization(), getter_AddRefs(mSecurityInfo));
MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv),
"Deserializing security info should not fail");
}
ipc::MergeParentLoadInfoForwarder(aArgs.loadInfoForwarder(), mLoadInfo);
mIsFromCache = aArgs.isFromCache();
mIsRacing = aArgs.isRacing();
mCacheEntryAvailable = aArgs.cacheEntryAvailable();
mCacheEntryId = aArgs.cacheEntryId();
mCacheFetchCount = aArgs.cacheFetchCount();
mCacheExpirationTime = aArgs.cacheExpirationTime();
mCachedCharset = aArgs.cachedCharset();
mSelfAddr = aArgs.selfAddr();
mPeerAddr = aArgs.peerAddr();
mRedirectCount = aArgs.redirectCount();
mAvailableCachedAltDataType = aArgs.altDataType();
mDeliveringAltData = aArgs.deliveringAltData();
mAltDataLength = aArgs.altDataLength();
mResolvedByTRR = aArgs.isResolvedByTRR();
SetApplyConversion(aArgs.applyConversion());
mAfterOnStartRequestBegun = true;
AutoEventEnqueuer ensureSerialDispatch(mEventQ);
mCacheKey = aArgs.cacheKey();
// replace our request headers with what actually got sent in the parent
mRequestHead.SetHeaders(aRequestHeaders);
// Note: this is where we would notify "http-on-examine-response" observers.
// We have deliberately disabled this for child processes (see bug 806753)
//
// gHttpHandler->OnExamineResponse(this);
ResourceTimingStructArgsToTimingsStruct(aArgs.timing(), mTransactionTimings);
mAllRedirectsSameOrigin = aArgs.allRedirectsSameOrigin();
mMultiPartID = aArgs.multiPartID();
mIsLastPartOfMultiPart = aArgs.isLastPartOfMultiPart();
if (!aArgs.appCacheGroupId().IsEmpty() &&
!aArgs.appCacheClientId().IsEmpty()) {
AssociateApplicationCache(aArgs.appCacheGroupId(),
aArgs.appCacheClientId());
}
if (aArgs.overrideReferrerInfo()) {
// The arguments passed to SetReferrerInfoInternal here should mirror the
// arguments passed in
// nsHttpChannel::ReEvaluateReferrerAfterTrackingStatusIsKnown(), except for
// aRespectBeforeConnect which we pass false here since we're intentionally
// overriding the referrer after BeginConnect().
Unused << SetReferrerInfoInternal(aArgs.overrideReferrerInfo(), false, true,
false);
}
if (!aArgs.cookie().IsEmpty()) {
SetCookie(aArgs.cookie());
}
if (aArgs.shouldWaitForOnStartRequestSent() &&
!mRecvOnStartRequestSentCalled) {
LOG((" > pending DoOnStartRequest until RecvOnStartRequestSent\n"));
MOZ_ASSERT(NS_IsMainThread());
mEventQ->Suspend();
mSuspendedByWaitingForPermissionCookie = true;
mEventQ->PrependEvent(MakeUnique<NeckoTargetChannelFunctionEvent>(
this, [self = UnsafePtr<HttpChannelChild>(this)]() {
self->DoOnStartRequest(self, nullptr);
}));
return;
}
DoOnStartRequest(this, nullptr);
}
void HttpChannelChild::ProcessOnAfterLastPart(const nsresult& aStatus) {
LOG(("HttpChannelChild::ProcessOnAfterLastPart [this=%p]\n", this));
MOZ_ASSERT(OnSocketThread());
mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
this, [self = UnsafePtr<HttpChannelChild>(this), aStatus]() {
self->OnAfterLastPart(aStatus);
}));
}
void HttpChannelChild::OnAfterLastPart(const nsresult& aStatus) {
if (mOnStopRequestCalled) {
return;
}
mOnStopRequestCalled = true;
// notify "http-on-stop-connect" observers
gHttpHandler->OnStopRequest(this);
ReleaseListeners();
// If a preferred alt-data type was set, the parent would hold a reference to
// the cache entry in case the child calls openAlternativeOutputStream().
// (see nsHttpChannel::OnStopRequest)
if (!mPreferredCachedAltDataTypes.IsEmpty()) {
mAltDataCacheEntryAvailable = mCacheEntryAvailable;
}
mCacheEntryAvailable = false;
if (mLoadGroup) mLoadGroup->RemoveRequest(this, nullptr, mStatus);
CleanupBackgroundChannel();
if (mLoadFlags & LOAD_DOCUMENT_URI) {
// Keep IPDL channel open, but only for updating security info.
// If IPDL is already closed, then do nothing.
if (CanSend()) {
mKeptAlive = true;
SendDocumentChannelCleanup(true);
}
} else {
// The parent process will respond by sending a DeleteSelf message and
// making sure not to send any more messages after that.
TrySendDeletingChannel();
}
}
void HttpChannelChild::DoOnStartRequest(nsIRequest* aRequest,
nsISupports* aContext) {
nsresult rv;
LOG(("HttpChannelChild::DoOnStartRequest [this=%p]\n", this));
// We handle all the listener chaining before OnStartRequest at this moment.
// Prevent additional listeners being added to the chain after the request
// as started.
mTracingEnabled = false;
// mListener could be null if the redirect setup is not completed.
MOZ_ASSERT(mListener || mOnStartRequestCalled);
if (!mListener) {
Cancel(NS_ERROR_FAILURE);
return;
}
if (mListener) {
nsCOMPtr<nsIStreamListener> listener(mListener);
mOnStartRequestCalled = true;
rv = listener->OnStartRequest(aRequest);
} else {
rv = NS_ERROR_UNEXPECTED;
}
mOnStartRequestCalled = true;
if (NS_FAILED(rv)) {
Cancel(rv);
return;
}
nsCOMPtr<nsIStreamListener> listener;
rv = DoApplyContentConversions(mListener, getter_AddRefs(listener), nullptr);
if (NS_FAILED(rv)) {
Cancel(rv);
} else if (listener) {
mListener = listener;
mCompressListener = listener;
}
}
void HttpChannelChild::ProcessOnTransportAndData(
const nsresult& aChannelStatus, const nsresult& aTransportStatus,
const uint64_t& aOffset, const uint32_t& aCount, const nsCString& aData) {
LOG(("HttpChannelChild::ProcessOnTransportAndData [this=%p]\n", this));
MOZ_ASSERT(OnSocketThread());
mEventQ->RunOrEnqueue(new ChannelFunctionEvent(
[self = UnsafePtr<HttpChannelChild>(this)]() {
return self->GetODATarget();
},
[self = UnsafePtr<HttpChannelChild>(this), aChannelStatus,
aTransportStatus, aOffset, aCount, aData]() {
self->OnTransportAndData(aChannelStatus, aTransportStatus, aOffset,
aCount, aData);
}));
}
void HttpChannelChild::OnTransportAndData(const nsresult& aChannelStatus,
const nsresult& aTransportStatus,
const uint64_t& aOffset,
const uint32_t& aCount,
const nsCString& aData) {
LOG(("HttpChannelChild::OnTransportAndData [this=%p]\n", this));
if (!mCanceled && NS_SUCCEEDED(mStatus)) {
mStatus = aChannelStatus;
}
if (mCanceled || NS_FAILED(mStatus)) {
return;
}
// Hold queue lock throughout all three calls, else we might process a later
// necko msg in between them.
AutoEventEnqueuer ensureSerialDispatch(mEventQ);
int64_t progressMax;
if (NS_FAILED(GetContentLength(&progressMax))) {
progressMax = -1;
}
const int64_t progress = aOffset + aCount;
// OnTransportAndData will be run on retargeted thread if applicable, however
// OnStatus/OnProgress event can only be fired on main thread. We need to
// dispatch the status/progress event handling back to main thread with the
// appropriate event target for networking.
if (NS_IsMainThread()) {
DoOnStatus(this, aTransportStatus);
DoOnProgress(this, progress, progressMax);
} else {
RefPtr<HttpChannelChild> self = this;
nsCOMPtr<nsISerialEventTarget> neckoTarget = GetNeckoTarget();
MOZ_ASSERT(neckoTarget);
DebugOnly<nsresult> rv = neckoTarget->Dispatch(
NS_NewRunnableFunction(
"net::HttpChannelChild::OnTransportAndData",
[self, aTransportStatus, progress, progressMax]() {
self->DoOnStatus(self, aTransportStatus);
self->DoOnProgress(self, progress, progressMax);
}),
NS_DISPATCH_NORMAL);
MOZ_ASSERT(NS_SUCCEEDED(rv));
}
// OnDataAvailable
//
// NOTE: the OnDataAvailable contract requires the client to read all the data
// in the inputstream. This code relies on that ('data' will go away after
// this function). Apparently the previous, non-e10s behavior was to actually
// support only reading part of the data, allowing later calls to read the
// rest.
nsCOMPtr<nsIInputStream> stringStream;
nsresult rv =
NS_NewByteInputStream(getter_AddRefs(stringStream),
Span(aData).To(aCount), NS_ASSIGNMENT_DEPEND);
if (NS_FAILED(rv)) {
Cancel(rv);
return;
}
DoOnDataAvailable(this, nullptr, stringStream, aOffset, aCount);
stringStream->Close();
// TODO: Bug 1523916 backpressure needs to take into account if the data is
// coming from the main process or from the socket process via PBackground.
if (NeedToReportBytesRead()) {
mUnreportBytesRead += aCount;
if (mUnreportBytesRead >= gHttpHandler->SendWindowSize() >> 2) {
if (NS_IsMainThread()) {
Unused << SendBytesRead(mUnreportBytesRead);
} else {
// PHttpChannel connects to the main thread
RefPtr<HttpChannelChild> self = this;
int32_t bytesRead = mUnreportBytesRead;
nsCOMPtr<nsISerialEventTarget> neckoTarget = GetNeckoTarget();
MOZ_ASSERT(neckoTarget);
DebugOnly<nsresult> rv = neckoTarget->Dispatch(
NS_NewRunnableFunction("net::HttpChannelChild::SendBytesRead",
[self, bytesRead]() {
Unused << self->SendBytesRead(bytesRead);
}),
NS_DISPATCH_NORMAL);
MOZ_ASSERT(NS_SUCCEEDED(rv));
}
mUnreportBytesRead = 0;
}
}
}
bool HttpChannelChild::NeedToReportBytesRead() {
if (mCacheNeedToReportBytesReadInitialized) {
return mNeedToReportBytesRead;
}
// Might notify parent for partial cache, and the IPC message is ignored by
// parent.
int64_t contentLength = -1;
if (gHttpHandler->SendWindowSize() == 0 || mIsFromCache ||
NS_FAILED(GetContentLength(&contentLength)) ||
contentLength < gHttpHandler->SendWindowSize()) {
mNeedToReportBytesRead = false;
}
mCacheNeedToReportBytesReadInitialized = true;
return mNeedToReportBytesRead;
}
void HttpChannelChild::DoOnStatus(nsIRequest* aRequest, nsresult status) {
LOG(("HttpChannelChild::DoOnStatus [this=%p]\n", this));
MOZ_ASSERT(NS_IsMainThread());
if (mCanceled) return;
// cache the progress sink so we don't have to query for it each time.
if (!mProgressSink) GetCallback(mProgressSink);
// block status/progress after Cancel or OnStopRequest has been called,
// or if channel has LOAD_BACKGROUND set.
if (mProgressSink && NS_SUCCEEDED(mStatus) && mIsPending &&
!(mLoadFlags & LOAD_BACKGROUND)) {
nsAutoCString host;
mURI->GetHost(host);
mProgressSink->OnStatus(aRequest, status,
NS_ConvertUTF8toUTF16(host).get());
}
}
void HttpChannelChild::DoOnProgress(nsIRequest* aRequest, int64_t progress,
int64_t progressMax) {
LOG(("HttpChannelChild::DoOnProgress [this=%p]\n", this));
MOZ_ASSERT(NS_IsMainThread());
if (mCanceled) return;
// cache the progress sink so we don't have to query for it each time.
if (!mProgressSink) GetCallback(mProgressSink);
// block status/progress after Cancel or OnStopRequest has been called,
// or if channel has LOAD_BACKGROUND set.
if (mProgressSink && NS_SUCCEEDED(mStatus) && mIsPending) {
// OnProgress
//
if (progress > 0) {
mProgressSink->OnProgress(aRequest, progress, progressMax);
}
}
}
void HttpChannelChild::DoOnDataAvailable(nsIRequest* aRequest,
nsISupports* aContext,
nsIInputStream* aStream,
uint64_t aOffset, uint32_t aCount) {
AUTO_PROFILER_LABEL("HttpChannelChild::DoOnDataAvailable", NETWORK);
LOG(("HttpChannelChild::DoOnDataAvailable [this=%p]\n", this));
if (mCanceled) return;
if (mListener) {
nsCOMPtr<nsIStreamListener> listener(mListener);
nsresult rv = listener->OnDataAvailable(aRequest, aStream, aOffset, aCount);
if (NS_FAILED(rv)) {
CancelOnMainThread(rv);
}
}
}
void HttpChannelChild::ProcessOnStopRequest(
const nsresult& aChannelStatus, const ResourceTimingStructArgs& aTiming,
const nsHttpHeaderArray& aResponseTrailers,
nsTArray<ConsoleReportCollected>&& aConsoleReports,
bool aFromSocketProcess) {
LOG(
("HttpChannelChild::ProcessOnStopRequest [this=%p, "
"aFromSocketProcess=%d]\n",
this, aFromSocketProcess));
MOZ_ASSERT(OnSocketThread());
mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
this, [self = UnsafePtr<HttpChannelChild>(this), aChannelStatus, aTiming,
aResponseTrailers,
consoleReports = CopyableTArray{aConsoleReports.Clone()},
aFromSocketProcess]() mutable {
self->OnStopRequest(aChannelStatus, aTiming, aResponseTrailers);
if (!aFromSocketProcess) {
self->DoOnConsoleReport(std::move(consoleReports));
self->ContinueOnStopRequest();
}
}));
}
void HttpChannelChild::ProcessOnConsoleReport(
nsTArray<ConsoleReportCollected>&& aConsoleReports) {
LOG(("HttpChannelChild::ProcessOnConsoleReport [this=%p]\n", this));
MOZ_ASSERT(OnSocketThread());
mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
this,
[self = UnsafePtr<HttpChannelChild>(this),
consoleReports = CopyableTArray{aConsoleReports.Clone()}]() mutable {
self->DoOnConsoleReport(std::move(consoleReports));
self->ContinueOnStopRequest();
}));
}
void HttpChannelChild::DoOnConsoleReport(
nsTArray<ConsoleReportCollected>&& aConsoleReports) {
if (aConsoleReports.IsEmpty()) {
return;
}
for (ConsoleReportCollected& report : aConsoleReports) {
if (report.propertiesFile() <
nsContentUtils::PropertiesFile::PropertiesFile_COUNT) {
AddConsoleReport(report.errorFlags(), report.category(),
nsContentUtils::PropertiesFile(report.propertiesFile()),
report.sourceFileURI(), report.lineNumber(),
report.columnNumber(), report.messageName(),
report.stringParams());
}
}
MaybeFlushConsoleReports();
}
void HttpChannelChild::OnStopRequest(
const nsresult& aChannelStatus, const ResourceTimingStructArgs& aTiming,
const nsHttpHeaderArray& aResponseTrailers) {
LOG(("HttpChannelChild::OnStopRequest [this=%p status=%" PRIx32 "]\n", this,
static_cast<uint32_t>(aChannelStatus)));
MOZ_ASSERT(NS_IsMainThread());
// If this channel was aborted by ActorDestroy, then there may be other
// OnStartRequest/OnStopRequest/OnDataAvailable IPC messages that need to
// be handled. In that case we just ignore them to avoid calling the listener
// twice.
if (mOnStopRequestCalled && mIPCActorDeleted) {
return;
}
nsCOMPtr<nsICompressConvStats> conv = do_QueryInterface(mCompressListener);
if (conv) {
conv->GetDecodedDataLength(&mDecodedBodySize);
}
ResourceTimingStructArgsToTimingsStruct(aTiming, mTransactionTimings);
// Do not overwrite or adjust the original mAsyncOpenTime by timing.fetchStart
// We must use the original child process time in order to account for child
// side work and IPC transit overhead.
// XXX: This depends on TimeStamp being equivalent across processes.
// This is true for modern hardware but for older platforms it is not always
// true.
mRedirectStartTimeStamp = aTiming.redirectStart();
mRedirectEndTimeStamp = aTiming.redirectEnd();
mTransferSize = aTiming.transferSize();
mEncodedBodySize = aTiming.encodedBodySize();
mProtocolVersion = aTiming.protocolVersion();
mCacheReadStart = aTiming.cacheReadStart();
mCacheReadEnd = aTiming.cacheReadEnd();
#ifdef MOZ_GECKO_PROFILER
if (profiler_can_accept_markers()) {
nsAutoCString requestMethod;
GetRequestMethod(requestMethod);
nsAutoCString contentType;
if (mResponseHead) {
mResponseHead->ContentType(contentType);
}
int32_t priority = PRIORITY_NORMAL;
GetPriority(&priority);
profiler_add_network_marker(
mURI, requestMethod, priority, mChannelId, NetworkLoadType::LOAD_STOP,
mLastStatusReported, TimeStamp::Now(), mTransferSize, kCacheUnknown,
mLoadInfo->GetInnerWindowID(), &mTransactionTimings, nullptr,
std::move(mSource), Some(nsDependentCString(contentType.get())));
}
#endif
mResponseTrailers = MakeUnique<nsHttpHeaderArray>(aResponseTrailers);
DoPreOnStopRequest(aChannelStatus);
{ // We must flush the queue before we Send__delete__
// (although we really shouldn't receive any msgs after OnStop),
// so make sure this goes out of scope before then.
AutoEventEnqueuer ensureSerialDispatch(mEventQ);
DoOnStopRequest(this, aChannelStatus, nullptr);
// DoOnStopRequest() calls ReleaseListeners()
}
}
void HttpChannelChild::ContinueOnStopRequest() {
// If we're a multi-part stream, then don't cleanup yet, and we'll do so
// in OnAfterLastPart.
if (mMultiPartID) {
LOG(
("HttpChannelChild::OnStopRequest - Expecting future parts on a "
"multipart channel postpone cleaning up."));
return;
}
CleanupBackgroundChannel();
// If there is a possibility we might want to write alt data to the cache
// entry, we keep the channel alive. We still send the DocumentChannelCleanup
// message but request the cache entry to be kept by the parent.
// If the channel has failed, the cache entry is in a non-writtable state and
// we want to release it to not block following consumers.
if (NS_SUCCEEDED(mStatus) && !mPreferredCachedAltDataTypes.IsEmpty()) {
mKeptAlive = true;
SendDocumentChannelCleanup(false); // don't clear cache entry
return;
}
if (mLoadFlags & LOAD_DOCUMENT_URI) {
// Keep IPDL channel open, but only for updating security info.
// If IPDL is already closed, then do nothing.
if (CanSend()) {
mKeptAlive = true;
SendDocumentChannelCleanup(true);
}
} else {
// The parent process will respond by sending a DeleteSelf message and
// making sure not to send any more messages after that.
TrySendDeletingChannel();
}
}
void HttpChannelChild::DoPreOnStopRequest(nsresult aStatus) {
AUTO_PROFILER_LABEL("HttpChannelChild::DoPreOnStopRequest", NETWORK);
LOG(("HttpChannelChild::DoPreOnStopRequest [this=%p status=%" PRIx32 "]\n",
this, static_cast<uint32_t>(aStatus)));
mIsPending = false;
MaybeReportTimingData();
if (!mCanceled && NS_SUCCEEDED(mStatus)) {
mStatus = aStatus;
}
CollectOMTTelemetry();
}
void HttpChannelChild::CollectOMTTelemetry() {
MOZ_ASSERT(NS_IsMainThread());
// Only collect telemetry for HTTP channel that is loaded successfully and
// completely.
if (mCanceled || NS_FAILED(mStatus)) {
return;
}
// Use content policy type to accumulate data by usage.
nsAutoCString key(
NS_CP_ContentTypeName(mLoadInfo->InternalContentPolicyType()));
Telemetry::AccumulateCategoricalKeyed(key, mOMTResult);
}
void HttpChannelChild::DoOnStopRequest(nsIRequest* aRequest,
nsresult aChannelStatus,
nsISupports* aContext) {
AUTO_PROFILER_LABEL("HttpChannelChild::DoOnStopRequest", NETWORK);
LOG(("HttpChannelChild::DoOnStopRequest [this=%p]\n", this));
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!mIsPending);
auto checkForBlockedContent = [&]() {
// NB: We use aChannelStatus here instead of mStatus because if there was an
// nsCORSListenerProxy on this request, it will override the tracking
// protection's return value.
if (UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(
aChannelStatus) ||
aChannelStatus == NS_ERROR_MALWARE_URI ||
aChannelStatus == NS_ERROR_UNWANTED_URI ||
aChannelStatus == NS_ERROR_BLOCKED_URI ||
aChannelStatus == NS_ERROR_HARMFUL_URI ||
aChannelStatus == NS_ERROR_PHISHING_URI) {
nsCString list, provider, fullhash;
nsresult rv = GetMatchedList(list);
NS_ENSURE_SUCCESS_VOID(rv);
rv = GetMatchedProvider(provider);
NS_ENSURE_SUCCESS_VOID(rv);
rv = GetMatchedFullHash(fullhash);
NS_ENSURE_SUCCESS_VOID(rv);
UrlClassifierCommon::SetBlockedContent(this, aChannelStatus, list,
provider, fullhash);
}
};
checkForBlockedContent();
// See bug 1587686. If the redirect setup is not completed, the post-redirect
// channel will be not opened and mListener will be null.
MOZ_ASSERT(mListener || !mWasOpened);
if (!mListener) {
return;
}
MOZ_ASSERT(!mOnStopRequestCalled, "We should not call OnStopRequest twice");
if (mListener) {
nsCOMPtr<nsIStreamListener> listener(mListener);
mOnStopRequestCalled = true;
listener->OnStopRequest(aRequest, mStatus);
}
mOnStopRequestCalled = true;
// If we're a multi-part stream, then don't cleanup yet, and we'll do so
// in OnAfterLastPart.
if (mMultiPartID) {
LOG(
("HttpChannelChild::DoOnStopRequest - Expecting future parts on a "
"multipart channel not releasing listeners."));
mOnStopRequestCalled = false;
mOnStartRequestCalled = false;
return;
}
// notify "http-on-stop-connect" observers
gHttpHandler->OnStopRequest(this);
ReleaseListeners();
// If a preferred alt-data type was set, the parent would hold a reference to
// the cache entry in case the child calls openAlternativeOutputStream().
// (see nsHttpChannel::OnStopRequest)
if (!mPreferredCachedAltDataTypes.IsEmpty()) {
mAltDataCacheEntryAvailable = mCacheEntryAvailable;
}
mCacheEntryAvailable = false;
if (mLoadGroup) mLoadGroup->RemoveRequest(this, nullptr, mStatus);
}
void HttpChannelChild::ProcessOnProgress(const int64_t& aProgress,
const int64_t& aProgressMax) {
MOZ_ASSERT(OnSocketThread());
LOG(("HttpChannelChild::ProcessOnProgress [this=%p]\n", this));
mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
this,
[self = UnsafePtr<HttpChannelChild>(this), aProgress, aProgressMax]() {
AutoEventEnqueuer ensureSerialDispatch(self->mEventQ);
self->DoOnProgress(self, aProgress, aProgressMax);
}));
}
void HttpChannelChild::ProcessOnStatus(const nsresult& aStatus) {
MOZ_ASSERT(OnSocketThread());
LOG(("HttpChannelChild::ProcessOnStatus [this=%p]\n", this));
mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
this, [self = UnsafePtr<HttpChannelChild>(this), aStatus]() {
AutoEventEnqueuer ensureSerialDispatch(self->mEventQ);
self->DoOnStatus(self, aStatus);
}));
}
mozilla::ipc::IPCResult HttpChannelChild::RecvFailedAsyncOpen(
const nsresult& aStatus) {
LOG(("HttpChannelChild::RecvFailedAsyncOpen [this=%p]\n", this));
mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
this, [self = UnsafePtr<HttpChannelChild>(this), aStatus]() {
self->FailedAsyncOpen(aStatus);
}));
return IPC_OK();
}
// We need to have an implementation of this function just so that we can keep
// all references to mCallOnResume of type HttpChannelChild: it's not OK in C++
// to set a member function ptr to a base class function.
void HttpChannelChild::HandleAsyncAbort() {
HttpAsyncAborter<HttpChannelChild>::HandleAsyncAbort();
// Ignore all the messages from background channel after channel aborted.
CleanupBackgroundChannel();
}
void HttpChannelChild::FailedAsyncOpen(const nsresult& status) {
LOG(("HttpChannelChild::FailedAsyncOpen [this=%p status=%" PRIx32 "]\n", this,
static_cast<uint32_t>(status)));
MOZ_ASSERT(NS_IsMainThread());
// Might be called twice in race condition in theory.
// (one by RecvFailedAsyncOpen, another by
// HttpBackgroundChannelChild::ActorFailed)
if (mOnStartRequestCalled) {
return;
}
if (NS_SUCCEEDED(mStatus)) {
mStatus = status;
}
// We're already being called from IPDL, therefore already "async"
HandleAsyncAbort();
if (CanSend()) {
TrySendDeletingChannel();
}
}
void HttpChannelChild::CleanupBackgroundChannel() {
MutexAutoLock lock(mBgChildMutex);
AUTO_PROFILER_LABEL("HttpChannelChild::CleanupBackgroundChannel", NETWORK);
LOG(("HttpChannelChild::CleanupBackgroundChannel [this=%p bgChild=%p]\n",
this, mBgChild.get()));
mBgInitFailCallback = nullptr;
if (!mBgChild) {
return;
}
RefPtr<HttpBackgroundChannelChild> bgChild = std::move(mBgChild);
MOZ_RELEASE_ASSERT(gSocketTransportService);
if (!OnSocketThread()) {
gSocketTransportService->Dispatch(
NewRunnableMethod("HttpBackgroundChannelChild::OnChannelClosed",
bgChild,
&HttpBackgroundChannelChild::OnChannelClosed),
NS_DISPATCH_NORMAL);
} else {
bgChild->OnChannelClosed();
}
}
void HttpChannelChild::DoNotifyListenerCleanup() {
LOG(("HttpChannelChild::DoNotifyListenerCleanup [this=%p]\n", this));
}
void HttpChannelChild::DoAsyncAbort(nsresult aStatus) {
Unused << AsyncAbort(aStatus);
}
mozilla::ipc::IPCResult HttpChannelChild::RecvDeleteSelf() {
LOG(("HttpChannelChild::RecvDeleteSelf [this=%p]\n", this));
MOZ_ASSERT(NS_IsMainThread());
// The redirection is vetoed. No need to suspend the event queue.
if (mSuspendForWaitCompleteRedirectSetup) {
mSuspendForWaitCompleteRedirectSetup = false;
mEventQ->Resume();
}
mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
this,
[self = UnsafePtr<HttpChannelChild>(this)]() { self->DeleteSelf(); }));
return IPC_OK();
}
void HttpChannelChild::DeleteSelf() { Send__delete__(this); }
void HttpChannelChild::NotifyOrReleaseListeners(nsresult rv) {
MOZ_ASSERT(NS_IsMainThread());
if (NS_SUCCEEDED(rv) || (mOnStartRequestCalled && mOnStopRequestCalled)) {
ReleaseListeners();
return;
}
if (NS_SUCCEEDED(mStatus)) {
mStatus = rv;
}
// This is enough what we need. Undelivered notifications will be pushed.
// DoNotifyListener ensures the call to ReleaseListeners when done.
DoNotifyListener();
}
void HttpChannelChild::DoNotifyListener() {
LOG(("HttpChannelChild::DoNotifyListener this=%p", this));
MOZ_ASSERT(NS_IsMainThread());
// In case nsHttpChannel::OnStartRequest wasn't called (e.g. due to flag
// LOAD_ONLY_IF_MODIFIED) we want to set mAfterOnStartRequestBegun to true
// before notifying listener.
if (!mAfterOnStartRequestBegun) {
mAfterOnStartRequestBegun = true;
}
if (mListener && !mOnStartRequestCalled) {
nsCOMPtr<nsIStreamListener> listener = mListener;
mOnStartRequestCalled = true; // avoid reentrancy bugs by setting this now
listener->OnStartRequest(this);
}
mOnStartRequestCalled = true;
mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
this, [self = UnsafePtr<HttpChannelChild>(this)] {
self->ContinueDoNotifyListener();
}));
}
void HttpChannelChild::ContinueDoNotifyListener() {
LOG(("HttpChannelChild::ContinueDoNotifyListener this=%p", this));
MOZ_ASSERT(NS_IsMainThread());
// Make sure mIsPending is set to false. At this moment we are done from
// the point of view of our consumer and we have to report our self
// as not-pending.
mIsPending = false;
if (mListener && !mOnStopRequestCalled) {
nsCOMPtr<nsIStreamListener> listener = mListener;
mOnStopRequestCalled = true;
listener->OnStopRequest(this, mStatus);
}
mOnStopRequestCalled = true;
// notify "http-on-stop-request" observers
gHttpHandler->OnStopRequest(this);
// This channel has finished its job, potentially release any tail-blocked
// requests with this.
RemoveAsNonTailRequest();
// We have to make sure to drop the references to listeners and callbacks
// no longer needed.
ReleaseListeners();
DoNotifyListenerCleanup();
// If this is a navigation, then we must let the docshell flush the reports
// to the console later. The LoadDocument() is pointing at the detached
// document that started the navigation. We want to show the reports on the
// new document. Otherwise the console is wiped and the user never sees
// the information.
if (!IsNavigation()) {
if (mLoadGroup) {
FlushConsoleReports(mLoadGroup);
} else {
RefPtr<dom::Document> doc;
mLoadInfo->GetLoadingDocument(getter_AddRefs(doc));
FlushConsoleReports(doc);
}
}
}
mozilla::ipc::IPCResult HttpChannelChild::RecvReportSecurityMessage(
const nsString& messageTag, const nsString& messageCategory) {
DebugOnly<nsresult> rv = AddSecurityMessage(messageTag, messageCategory);
MOZ_ASSERT(NS_SUCCEEDED(rv));
return IPC_OK();
}
mozilla::ipc::IPCResult HttpChannelChild::RecvRedirect1Begin(
const uint32_t& aRegistrarId, const URIParams& aNewUri,
const uint32_t& aNewLoadFlags, const uint32_t& aRedirectFlags,
const ParentLoadInfoForwarderArgs& aLoadInfoForwarder,
const nsHttpResponseHead& aResponseHead,
const nsCString& aSecurityInfoSerialization, const uint64_t& aChannelId,
const NetAddr& aOldPeerAddr, const ResourceTimingStructArgs& aTiming) {
// TODO: handle security info
LOG(("HttpChannelChild::RecvRedirect1Begin [this=%p]\n", this));
// We set peer address of child to the old peer,
// Then it will be updated to new peer in OnStartRequest
mPeerAddr = aOldPeerAddr;
// Cookies headers should not be visible to the child process
MOZ_ASSERT(!nsHttpResponseHead(aResponseHead).HasHeader(nsHttp::Set_Cookie));
mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
this, [self = UnsafePtr<HttpChannelChild>(this), aRegistrarId, aNewUri,
aNewLoadFlags, aRedirectFlags, aLoadInfoForwarder, aResponseHead,
aSecurityInfoSerialization, aChannelId, aTiming]() {
self->Redirect1Begin(aRegistrarId, aNewUri, aNewLoadFlags,
aRedirectFlags, aLoadInfoForwarder, aResponseHead,
aSecurityInfoSerialization, aChannelId, aTiming);
}));
return IPC_OK();
}
nsresult HttpChannelChild::SetupRedirect(nsIURI* uri,
const nsHttpResponseHead* responseHead,
const uint32_t& redirectFlags,
nsIChannel** outChannel) {
LOG(("HttpChannelChild::SetupRedirect [this=%p]\n", this));
if (mCanceled) {
return NS_ERROR_ABORT;
}
nsresult rv;
nsCOMPtr<nsIIOService> ioService;
rv = gHttpHandler->GetIOService(getter_AddRefs(ioService));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIChannel> newChannel;
nsCOMPtr<nsILoadInfo> redirectLoadInfo =
CloneLoadInfoForRedirect(uri, redirectFlags);
rv = NS_NewChannelInternal(getter_AddRefs(newChannel), uri, redirectLoadInfo,
nullptr, // PerformanceStorage
nullptr, // aLoadGroup
nullptr, // aCallbacks
nsIRequest::LOAD_NORMAL, ioService);
NS_ENSURE_SUCCESS(rv, rv);
// We won't get OnStartRequest, set cookies here.
mResponseHead = MakeUnique<nsHttpResponseHead>(*responseHead);
bool rewriteToGET = HttpBaseChannel::ShouldRewriteRedirectToGET(
mResponseHead->Status(), mRequestHead.ParsedMethod());
rv = SetupReplacementChannel(uri, newChannel, !rewriteToGET, redirectFlags);
NS_ENSURE_SUCCESS(rv, rv);
mRedirectChannelChild = do_QueryInterface(newChannel);
newChannel.forget(outChannel);
return NS_OK;
}
void HttpChannelChild::Redirect1Begin(
const uint32_t& registrarId, const URIParams& newOriginalURI,
const uint32_t& newLoadFlags, const uint32_t& redirectFlags,
const ParentLoadInfoForwarderArgs& loadInfoForwarder,
const nsHttpResponseHead& responseHead,
const nsACString& securityInfoSerialization, const uint64_t& channelId,
const ResourceTimingStructArgs& timing) {
nsresult rv;
LOG(("HttpChannelChild::Redirect1Begin [this=%p]\n", this));
ipc::MergeParentLoadInfoForwarder(loadInfoForwarder, mLoadInfo);
nsCOMPtr<nsIURI> uri = DeserializeURI(newOriginalURI);
ResourceTimingStructArgsToTimingsStruct(timing, mTransactionTimings);
#ifdef MOZ_GECKO_PROFILER
if (profiler_can_accept_markers()) {
nsAutoCString requestMethod;
GetRequestMethod(requestMethod);
nsAutoCString contentType;
responseHead.ContentType(contentType);
profiler_add_network_marker(
mURI, requestMethod, mPriority, mChannelId,
NetworkLoadType::LOAD_REDIRECT, mLastStatusReported, TimeStamp::Now(),
0, kCacheUnknown, mLoadInfo->GetInnerWindowID(), &mTransactionTimings,
uri, std::move(mSource), Some(nsDependentCString(contentType.get())));
}
#endif
if (!securityInfoSerialization.IsEmpty()) {
rv = NS_DeserializeObject(securityInfoSerialization,
getter_AddRefs(mSecurityInfo));
MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv),
"Deserializing security info should not fail");
}
nsCOMPtr<nsIChannel> newChannel;
rv = SetupRedirect(uri, &responseHead, redirectFlags,
getter_AddRefs(newChannel));
if (NS_SUCCEEDED(rv)) {
MOZ_ALWAYS_SUCCEEDS(newChannel->SetLoadFlags(newLoadFlags));
if (mRedirectChannelChild) {
// Set the channelId allocated in parent to the child instance
nsCOMPtr<nsIHttpChannel> httpChannel =
do_QueryInterface(mRedirectChannelChild);
if (httpChannel) {
rv = httpChannel->SetChannelId(channelId);
MOZ_ASSERT(NS_SUCCEEDED(rv));
}
mRedirectChannelChild->ConnectParent(registrarId);
}
nsCOMPtr<nsISerialEventTarget> target = GetNeckoTarget();
MOZ_ASSERT(target);
rv = gHttpHandler->AsyncOnChannelRedirect(this, newChannel, redirectFlags,
target);
}
if (NS_FAILED(rv)) OnRedirectVerifyCallback(rv);
}
mozilla::ipc::IPCResult HttpChannelChild::RecvRedirect3Complete() {
LOG(("HttpChannelChild::RecvRedirect3Complete [this=%p]\n", this));
nsCOMPtr<nsIChannel> redirectChannel =
do_QueryInterface(mRedirectChannelChild);
MOZ_ASSERT(redirectChannel);
mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
this, [self = UnsafePtr<HttpChannelChild>(this), redirectChannel]() {
nsresult rv = NS_OK;
Unused << self->GetStatus(&rv);
if (NS_FAILED(rv)) {
// Pre-redirect channel was canceled. Call |HandleAsyncAbort|, so
// mListener's OnStart/StopRequest can be called. Nothing else will
// trigger these notification after this point.
// We do this before |CompleteRedirectSetup|, so post-redirect channel
// stays unopened and we also make sure that OnStart/StopRequest won't
// be called twice.
self->HandleAsyncAbort();
nsCOMPtr<nsIHttpChannelChild> chan =
do_QueryInterface(redirectChannel);
RefPtr<HttpChannelChild> httpChannelChild =
static_cast<HttpChannelChild*>(chan.get());
if (httpChannelChild) {
// For sending an IPC message to parent channel so that the loading
// can be cancelled.
Unused << httpChannelChild->Cancel(rv);
// The post-redirect channel could still get OnStart/StopRequest IPC
// messages from parent, but the mListener is still null. So, we
// call |DoNotifyListener| to pretend that OnStart/StopRequest are
// already called.
httpChannelChild->DoNotifyListener();
}
return;
}
self->Redirect3Complete();
}));
return IPC_OK();
}
void HttpChannelChild::ProcessNotifyClassificationFlags(
uint32_t aClassificationFlags, bool aIsThirdParty) {
LOG(
("HttpChannelChild::ProcessNotifyClassificationFlags thirdparty=%d "
"flags=%" PRIu32 " [this=%p]\n",
static_cast<int>(aIsThirdParty), aClassificationFlags, this));
MOZ_ASSERT(OnSocketThread());
mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
this, [self = UnsafePtr<HttpChannelChild>(this), aClassificationFlags,
aIsThirdParty]() {
self->AddClassificationFlags(aClassificationFlags, aIsThirdParty);
}));
}
void HttpChannelChild::ProcessNotifyFlashPluginStateChanged(
nsIHttpChannel::FlashPluginState aState) {
LOG(("HttpChannelChild::ProcessNotifyFlashPluginStateChanged [this=%p]\n",
this));
MOZ_ASSERT(OnSocketThread());
mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
this, [self = UnsafePtr<HttpChannelChild>(this), aState]() {
self->SetFlashPluginState(aState);
}));
}
void HttpChannelChild::ProcessSetClassifierMatchedInfo(
const nsCString& aList, const nsCString& aProvider,
const nsCString& aFullHash) {
LOG(("HttpChannelChild::ProcessSetClassifierMatchedInfo [this=%p]\n", this));
MOZ_ASSERT(OnSocketThread());
mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
this,
[self = UnsafePtr<HttpChannelChild>(this), aList, aProvider,
aFullHash]() { self->SetMatchedInfo(aList, aProvider, aFullHash); }));
}
void HttpChannelChild::ProcessSetClassifierMatchedTrackingInfo(
const nsCString& aLists, const nsCString& aFullHashes) {
LOG(("HttpChannelChild::ProcessSetClassifierMatchedTrackingInfo [this=%p]\n",
this));
MOZ_ASSERT(OnSocketThread());
nsTArray<nsCString> lists, fullhashes;
for (const nsACString& token : aLists.Split(',')) {
lists.AppendElement(token);
}
for (const nsACString& token : aFullHashes.Split(',')) {
fullhashes.AppendElement(token);
}
mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
this, [self = UnsafePtr<HttpChannelChild>(this),
lists = CopyableTArray{std::move(lists)},
fullhashes = CopyableTArray{std::move(fullhashes)}]() {
self->SetMatchedTrackingInfo(lists, fullhashes);
}));
}
// Completes the redirect and cleans up the old channel.
void HttpChannelChild::Redirect3Complete() {
LOG(("HttpChannelChild::Redirect3Complete [this=%p]\n", this));
MOZ_ASSERT(NS_IsMainThread());
// Using an error as the default so that when we fail to forward this redirect
// to the target channel, we make sure to notify the current listener from
// CleanupRedirectingChannel.
nsresult rv = NS_BINDING_ABORTED;
nsCOMPtr<nsIRedirectResultListener> vetoHook;
GetCallback(vetoHook);
if (vetoHook) {
vetoHook->OnRedirectResult(true);
}
// Chrome channel has been AsyncOpen'd. Reflect this in child.
if (mRedirectChannelChild) {
rv = mRedirectChannelChild->CompleteRedirectSetup(mListener);
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
mSuccesfullyRedirected = NS_SUCCEEDED(rv);
#endif
}
CleanupRedirectingChannel(rv);
}
void HttpChannelChild::CleanupRedirectingChannel(nsresult rv) {
// Redirecting to new channel: shut this down and init new channel
if (mLoadGroup) mLoadGroup->RemoveRequest(this, nullptr, NS_BINDING_ABORTED);
if (NS_SUCCEEDED(rv)) {
nsCString remoteAddress;
Unused << GetRemoteAddress(remoteAddress);
nsCOMPtr<nsIURI> referrer;
if (mReferrerInfo) {
referrer = mReferrerInfo->GetComputedReferrer();
}
nsCOMPtr<nsIRedirectHistoryEntry> entry =
new nsRedirectHistoryEntry(GetURIPrincipal(), referrer, remoteAddress);
mLoadInfo->AppendRedirectHistoryEntry(entry, false);
} else {
NS_WARNING("CompleteRedirectSetup failed, HttpChannelChild already open?");
}
// Release ref to new channel.
mRedirectChannelChild = nullptr;
NotifyOrReleaseListeners(rv);
CleanupBackgroundChannel();
}
//-----------------------------------------------------------------------------
// HttpChannelChild::nsIChildChannel
//-----------------------------------------------------------------------------
NS_IMETHODIMP
HttpChannelChild::ConnectParent(uint32_t registrarId) {
LOG(("HttpChannelChild::ConnectParent [this=%p, id=%" PRIu32 "]\n", this,
registrarId));
MOZ_ASSERT(NS_IsMainThread());
mozilla::dom::BrowserChild* browserChild = nullptr;
nsCOMPtr<nsIBrowserChild> iBrowserChild;
GetCallback(iBrowserChild);
if (iBrowserChild) {
browserChild =
static_cast<mozilla::dom::BrowserChild*>(iBrowserChild.get());
}
if (browserChild && !browserChild->IPCOpen()) {
return NS_ERROR_FAILURE;
}
ContentChild* cc = static_cast<ContentChild*>(gNeckoChild->Manager());
if (cc->IsShuttingDown()) {
return NS_ERROR_FAILURE;
}
HttpBaseChannel::SetDocshellUserAgentOverride();
// This must happen before the constructor message is sent. Otherwise messages
// from the parent could arrive quickly and be delivered to the wrong event
// target.
SetEventTarget();
HttpChannelConnectArgs connectArgs(registrarId);
if (!gNeckoChild->SendPHttpChannelConstructor(
this, browserChild, IPC::SerializedLoadContext(this), connectArgs)) {
return NS_ERROR_FAILURE;
}
{
MutexAutoLock lock(mBgChildMutex);
MOZ_ASSERT(!mBgChild);
MOZ_ASSERT(!mBgInitFailCallback);
mBgInitFailCallback = NewRunnableMethod<nsresult>(
"HttpChannelChild::OnRedirectVerifyCallback", this,
&HttpChannelChild::OnRedirectVerifyCallback, NS_ERROR_FAILURE);
RefPtr<HttpBackgroundChannelChild> bgChild =
new HttpBackgroundChannelChild();
MOZ_RELEASE_ASSERT(gSocketTransportService);
RefPtr<HttpChannelChild> self = this;
nsresult rv = gSocketTransportService->Dispatch(
NewRunnableMethod<RefPtr<HttpChannelChild>>(
"HttpBackgroundChannelChild::Init", bgChild,
&HttpBackgroundChannelChild::Init, std::move(self)),
NS_DISPATCH_NORMAL);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
mBgChild = std::move(bgChild);
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
mEverHadBgChildAtConnectParent = true;
#endif
}
// Should wait for CompleteRedirectSetup to set the listener.
mEventQ->Suspend();
MOZ_ASSERT(!mSuspendForWaitCompleteRedirectSetup);
mSuspendForWaitCompleteRedirectSetup = true;
// Connect to socket process after mEventQ is suspended.
MaybeConnectToSocketProcess();
return NS_OK;
}
NS_IMETHODIMP
HttpChannelChild::CompleteRedirectSetup(nsIStreamListener* aListener) {
LOG(("HttpChannelChild::CompleteRedirectSetup [this=%p]\n", this));
MOZ_ASSERT(NS_IsMainThread());
NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);
// Resume the suspension in ConnectParent.
auto eventQueueResumeGuard = MakeScopeExit([&] {
MOZ_ASSERT(mSuspendForWaitCompleteRedirectSetup);
mEventQ->Resume();
mSuspendForWaitCompleteRedirectSetup = false;
});
/*
* No need to check for cancel: we don't get here if nsHttpChannel canceled
* before AsyncOpen(); if it's canceled after that, OnStart/Stop will just
* get called with error code as usual. So just setup mListener and make the
* channel reflect AsyncOpen'ed state.
*/
mLastStatusReported = TimeStamp::Now();
#ifdef MOZ_GECKO_PROFILER
if (profiler_can_accept_markers()) {
nsAutoCString requestMethod;
GetRequestMethod(requestMethod);
profiler_add_network_marker(
mURI, requestMethod, mPriority, mChannelId, NetworkLoadType::LOAD_START,
mChannelCreationTimestamp, mLastStatusReported, 0, kCacheUnknown,
mLoadInfo->GetInnerWindowID(), nullptr, nullptr);
}
#endif
mIsPending = true;
mWasOpened = true;
mListener = aListener;
// add ourselves to the load group.
if (mLoadGroup) mLoadGroup->AddRequest(this, nullptr);
// We already have an open IPDL connection to the parent. If on-modify-request
// listeners or load group observers canceled us, let the parent handle it
// and send it back to us naturally.
return NS_OK;
}
//-----------------------------------------------------------------------------
// HttpChannelChild::nsIAsyncVerifyRedirectCallback
//-----------------------------------------------------------------------------
NS_IMETHODIMP
HttpChannelChild::OnRedirectVerifyCallback(nsresult aResult) {
LOG(("HttpChannelChild::OnRedirectVerifyCallback [this=%p]\n", this));
MOZ_ASSERT(NS_IsMainThread());
Maybe<URIParams> redirectURI;
nsresult rv;
nsCOMPtr<nsIHttpChannel> newHttpChannel =
do_QueryInterface(mRedirectChannelChild);
if (NS_SUCCEEDED(aResult) && !mRedirectChannelChild) {
// mRedirectChannelChild doesn't exist means we're redirecting to a protocol
// that doesn't implement nsIChildChannel. The redirect result should be set
// as failed by veto listeners and shouldn't enter this condition. As the
// last resort, we synthesize the error result as NS_ERROR_DOM_BAD_URI here
// to let nsHttpChannel::ContinueProcessResponse2 know it's redirecting to
// another protocol and throw an error.
LOG((" redirecting to a protocol that doesn't implement nsIChildChannel"));
aResult = NS_ERROR_DOM_BAD_URI;
}
nsCOMPtr<nsIReferrerInfo> referrerInfo;
if (newHttpChannel) {
// Must not be called until after redirect observers called.
newHttpChannel->SetOriginalURI(mOriginalURI);
referrerInfo = newHttpChannel->GetReferrerInfo();
}
RequestHeaderTuples emptyHeaders;
RequestHeaderTuples* headerTuples = &emptyHeaders;
nsLoadFlags loadFlags = 0;
Maybe<CorsPreflightArgs> corsPreflightArgs;
nsCOMPtr<nsIHttpChannelChild> newHttpChannelChild =
do_QueryInterface(mRedirectChannelChild);
if (newHttpChannelChild && NS_SUCCEEDED(aResult)) {
rv = newHttpChannelChild->AddCookiesToRequest();
MOZ_ASSERT(NS_SUCCEEDED(rv));
rv = newHttpChannelChild->GetClientSetRequestHeaders(&headerTuples);
MOZ_ASSERT(NS_SUCCEEDED(rv));
newHttpChannelChild->GetClientSetCorsPreflightParameters(corsPreflightArgs);
}
/* If the redirect was canceled, bypass OMR and send an empty API
* redirect URI */
SerializeURI(nullptr, redirectURI);
if (NS_SUCCEEDED(aResult)) {
// Note: this is where we would notify "http-on-modify-response" observers.
// We have deliberately disabled this for child processes (see bug 806753)
//
// After we verify redirect, nsHttpChannel may hit the network: must give
// "http-on-modify-request" observers the chance to cancel before that.
// base->CallOnModifyRequestObservers();
nsCOMPtr<nsIHttpChannelInternal> newHttpChannelInternal =
do_QueryInterface(mRedirectChannelChild);
if (newHttpChannelInternal) {
nsCOMPtr<nsIURI> apiRedirectURI;
rv = newHttpChannelInternal->GetApiRedirectToURI(
getter_AddRefs(apiRedirectURI));
if (NS_SUCCEEDED(rv) && apiRedirectURI) {
/* If there was an API redirect of this channel, we need to send it
* up here, since it can't be sent via SendAsyncOpen. */
SerializeURI(apiRedirectURI, redirectURI);
}
}
nsCOMPtr<nsIRequest> request = do_QueryInterface(mRedirectChannelChild);
if (request) {
request->GetLoadFlags(&loadFlags);
}
}
bool chooseAppcache = false;
nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel =
do_QueryInterface(newHttpChannel);
if (appCacheChannel) {
appCacheChannel->GetChooseApplicationCache(&chooseAppcache);
}
uint32_t sourceRequestBlockingReason = 0;
mLoadInfo->GetRequestBlockingReason(&sourceRequestBlockingReason);
Maybe<ChildLoadInfoForwarderArgs> targetLoadInfoForwarder;
nsCOMPtr<nsIChannel> newChannel = do_QueryInterface(mRedirectChannelChild);
if (newChannel) {
ChildLoadInfoForwarderArgs args;
nsCOMPtr<nsILoadInfo> loadInfo = newChannel->LoadInfo();
LoadInfoToChildLoadInfoForwarder(loadInfo, &args);
targetLoadInfoForwarder.emplace(args);
}
if (CanSend())
SendRedirect2Verify(aResult, *headerTuples, sourceRequestBlockingReason,
targetLoadInfoForwarder, loadFlags, referrerInfo,
redirectURI, corsPreflightArgs, chooseAppcache);
return NS_OK;
}
//-----------------------------------------------------------------------------
// HttpChannelChild::nsIRequest
//-----------------------------------------------------------------------------
NS_IMETHODIMP
HttpChannelChild::Cancel(nsresult aStatus) {
LOG(("HttpChannelChild::Cancel [this=%p, status=%" PRIx32 "]\n", this,
static_cast<uint32_t>(aStatus)));
LogCallingScriptLocation(this);
MOZ_ASSERT(NS_IsMainThread());
if (!mCanceled) {
// If this cancel occurs before nsHttpChannel has been set up, AsyncOpen
// is responsible for cleaning up.
mCanceled = true;
mStatus = aStatus;
bool remoteChannelExists = RemoteChannelExists();
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
mCanSendAtCancel = CanSend();
mRemoteChannelExistedAtCancel = remoteChannelExists;
#endif
if (remoteChannelExists) {
SendCancel(aStatus, mLoadInfo->GetRequestBlockingReason());
}
}
return NS_OK;
}
NS_IMETHODIMP
HttpChannelChild::Suspend() {
LOG(("HttpChannelChild::Suspend [this=%p, mSuspendCount=%" PRIu32 "\n", this,
mSuspendCount + 1));
MOZ_ASSERT(NS_IsMainThread());
NS_ENSURE_TRUE(RemoteChannelExists(), NS_ERROR_NOT_AVAILABLE);
LogCallingScriptLocation(this);
// SendSuspend only once, when suspend goes from 0 to 1.
// Don't SendSuspend at all if we're diverting callbacks to the parent;
// suspend will be called at the correct time in the parent itself.
if (!mSuspendCount++) {
if (RemoteChannelExists()) {
SendSuspend();
mSuspendSent = true;
}
}
mEventQ->Suspend();
return NS_OK;
}
NS_IMETHODIMP
HttpChannelChild::Resume() {
LOG(("HttpChannelChild::Resume [this=%p, mSuspendCount=%" PRIu32 "\n", this,
mSuspendCount - 1));
MOZ_ASSERT(NS_IsMainThread());
NS_ENSURE_TRUE(RemoteChannelExists(), NS_ERROR_NOT_AVAILABLE);
NS_ENSURE_TRUE(mSuspendCount > 0, NS_ERROR_UNEXPECTED);
LogCallingScriptLocation(this);
nsresult rv = NS_OK;
// SendResume only once, when suspend count drops to 0.
// Don't SendResume at all if we're diverting callbacks to the parent (unless
// suspend was sent earlier); otherwise, resume will be called at the correct
// time in the parent itself.
if (!--mSuspendCount && mSuspendSent) {
if (RemoteChannelExists()) {
SendResume();
}
if (mCallOnResume) {
nsCOMPtr<nsISerialEventTarget> neckoTarget = GetNeckoTarget();
MOZ_ASSERT(neckoTarget);
RefPtr<HttpChannelChild> self = this;
std::function<nsresult(HttpChannelChild*)> callOnResume = nullptr;
std::swap(callOnResume, mCallOnResume);
rv = neckoTarget->Dispatch(
NS_NewRunnableFunction(
"net::HttpChannelChild::mCallOnResume",
[callOnResume, self{std::move(self)}]() { callOnResume(self); }),
NS_DISPATCH_NORMAL);
}
}
mEventQ->Resume();
return rv;
}
//-----------------------------------------------------------------------------
// HttpChannelChild::nsIChannel
//-----------------------------------------------------------------------------
NS_IMETHODIMP
HttpChannelChild::GetSecurityInfo(nsISupports** aSecurityInfo) {
NS_ENSURE_ARG_POINTER(aSecurityInfo);
NS_IF_ADDREF(*aSecurityInfo = mSecurityInfo);
return NS_OK;
}
NS_IMETHODIMP
HttpChannelChild::AsyncOpen(nsIStreamListener* aListener) {
LOG(("HttpChannelChild::AsyncOpen [this=%p uri=%s]\n", this, mSpec.get()));
nsresult rv = AsyncOpenInternal(aListener);
if (NS_FAILED(rv)) {
uint32_t blockingReason = 0;
mLoadInfo->GetRequestBlockingReason(&blockingReason);
LOG(
("HttpChannelChild::AsyncOpen failed [this=%p rv=0x%08x "
"blocking-reason=%u]\n",
this, static_cast<uint32_t>(rv), blockingReason));
gHttpHandler->OnFailedOpeningRequest(this);
}
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
mAsyncOpenSucceeded = NS_SUCCEEDED(rv);
#endif
return rv;
}
nsresult HttpChannelChild::AsyncOpenInternal(nsIStreamListener* aListener) {
nsresult rv;
nsCOMPtr<nsIStreamListener> listener = aListener;
rv = nsContentSecurityManager::doContentSecurityCheck(this, listener);
if (NS_WARN_IF(NS_FAILED(rv))) {
ReleaseListeners();
return rv;
}
MOZ_ASSERT(
mLoadInfo->GetSecurityMode() == 0 ||
mLoadInfo->GetInitialSecurityCheckDone() ||
(mLoadInfo->GetSecurityMode() ==
nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL &&
mLoadInfo->GetLoadingPrincipal() &&
mLoadInfo->GetLoadingPrincipal()->IsSystemPrincipal()),
"security flags in loadInfo but doContentSecurityCheck() not called");
LogCallingScriptLocation(this);
if (!mLoadGroup && !mCallbacks) {
// If no one called SetLoadGroup or SetNotificationCallbacks, the private
// state has not been updated on PrivateBrowsingChannel (which we derive
// from) Hence, we have to call UpdatePrivateBrowsing() here
UpdatePrivateBrowsing();
}
#ifdef DEBUG
AssertPrivateBrowsingId();
#endif
if (mCanceled) {
ReleaseListeners();
return mStatus;
}
NS_ENSURE_TRUE(gNeckoChild != nullptr, NS_ERROR_FAILURE);
NS_ENSURE_ARG_POINTER(listener);
NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);