/* 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"
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();
}
void HttpChannelChild::ReleaseMainThreadOnlyReferences() { if (NS_IsMainThread()) { // Already on main thread, let dtor to // take care of releasing references return;
}
// 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()) {
NS_LOG_RELEASE(this, 1, "HttpChannelChild");
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(LoadOnStartRequestCalled() && LoadOnStopRequestCalled()) ||
!mListener || mAlreadyReleased) {
NS_LOG_RELEASE(this, 0, "HttpChannelChild"); deletethis; return 0;
}
// This ensures that when the refcount goes to 0 again, we don't dispatch // yet another runnable and get in a loop.
mAlreadyReleased = true;
// This makes sure we fulfill the stream listener contract all the time. if (NS_SUCCEEDED(mStatus)) {
mStatus = NS_ERROR_ABORT;
}
// Turn the stabilization refcount into a regular strong reference.
// 1) We tell refcount logging about the "stabilization" AddRef, which // will become the reference for |channel|. We do this first so that we // don't tell refcount logging that the refcount has dropped to zero, which // it will interpret as destroying the object.
NS_LOG_ADDREF(this, 2, "HttpChannelChild", sizeof(*this));
// 2) We tell refcount logging about the original call to Release().
NS_LOG_RELEASE(this, 1, "HttpChannelChild");
// 3) Finally, we turn the reference into a regular smart pointer.
RefPtr<HttpChannelChild> channel = dont_AddRef(this);
NS_DispatchToMainThread(NS_NewRunnableFunction( "~HttpChannelChild>DoNotifyListener",
[chan = std::move(channel)] { chan->DoNotifyListener(false); })); // If NS_DispatchToMainThread failed then we're going to leak the runnable, // and thus the channel, so there's no need to do anything else. return mRefCnt;
}
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());
// 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 (LoadOnStartRequestCalled() && 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);
}
// 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);
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.cookieHeaders().IsEmpty()) {
SetCookieHeaders(aArgs.cookieHeaders());
}
// Note: this is where we would notify "http-on-after-examine-response" // observers. We have deliberately disabled this for child processes (see bug // 806753) // // gHttpHandler->OnAfterExamineResponse(this);
if (aArgs.shouldWaitForOnStartRequestSent() &&
!mRecvOnStartRequestSentCalled) {
LOG((" > pending DoOnStartRequest until RecvOnStartRequestSent\n"));
MOZ_ASSERT(NS_IsMainThread());
// 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();
}
}
// We handle all the listener chaining before OnStartRequest at this moment. // Prevent additional listeners being added to the chain after the request // as started.
StoreTracingEnabled(false);
// mListener could be null if the redirect setup is not completed.
MOZ_ASSERT(mListener || LoadOnStartRequestCalled()); if (!mListener) {
Cancel(NS_ERROR_FAILURE); return;
}
// We call MaybeRetarget here to allow the stream converter // the option to request data on another thread, even if the // final listener might not support it if (nsCOMPtr<nsIStreamConverter> conv =
do_QueryInterface((mCompressListener))) {
conv->MaybeRetarget(this);
}
}
}
if (!mCanceled && NS_SUCCEEDED(mStatus)) {
mStatus = aChannelStatus;
}
if (mCanceled || NS_FAILED(mStatus)) { return;
}
if (!mOnDataAvailableStartTime.IsNull()) {
PerfStats::RecordMeasurement(PerfStats::Metric::OnDataAvailableToContent,
TimeStamp::Now() - mOnDataAvailableStartTime);
}
// 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);
// 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)) {
CancelWithReason(rv, "HttpChannelChild NS_NewByteInputStream failed"_ns); return;
}
// 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);
// 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) && LoadIsPending() &&
!(mLoadFlags & LOAD_BACKGROUND)) {
nsAutoCString host;
mURI->GetHost(host);
mProgressSink->OnStatus(aRequest, status,
NS_ConvertUTF8toUTF16(host).get());
}
}
// 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) && LoadIsPending()) { // OnProgress // if (progress > 0) {
mProgressSink->OnProgress(aRequest, progress, progressMax);
}
}
// mOnProgressEventSent indicates we have flushed all the // progress events on the main thread. It is needed if // we do not want to dispatch OnDataFinished before sending // all of the progress updates. if (progress == progressMax) {
mOnProgressEventSent = true;
}
}
// we need to ensure we OnDataFinished only after all the progress // updates are dispatched on the main thread if (StaticPrefs::network_send_OnDataFinished_after_progress_updates() &&
!mOnProgressEventSent) { return;
}
if (mListener) {
nsCOMPtr<nsIThreadRetargetableStreamListener> omtEventListener =
do_QueryInterface(mListener); if (omtEventListener) {
LOG(
("HttpChannelChild::SendOnDataFinished sending data end " "notification[this=%p]\n", this)); // We want to calculate the delta time between this call and // ProcessOnStopRequest. Complicating things is that OnStopRequest // could come first, and that it will run on a different thread, so // we need to synchronize and lock data.
omtEventListener->OnDataFinished(aChannelStatus);
} else {
LOG(
("HttpChannelChild::SendOnDataFinished missing " "nsIThreadRetargetableStreamListener " "implementation [this=%p]\n", this));
}
}
}
class RecordStopRequestDelta final { public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RecordStopRequestDelta);
void HttpChannelChild::ProcessOnStopRequest( const nsresult& aChannelStatus, const ResourceTimingStructArgs& aTiming, const nsHttpHeaderArray& aResponseTrailers,
nsTArray<ConsoleReportCollected>&& aConsoleReports, bool aFromSocketProcess, const TimeStamp& aOnStopRequestStartTime) {
LOG(
("HttpChannelChild::ProcessOnStopRequest [this=%p, " "aFromSocketProcess=%d]\n", this, aFromSocketProcess));
MOZ_ASSERT(OnSocketThread());
{ // assign some of the members that would be accessed by the listeners // upon getting OnDataFinished notications
MutexAutoLock lock(mOnDataFinishedMutex);
mTransferSize = aTiming.transferSize();
mEncodedBodySize = aTiming.encodedBodySize();
}
RefPtr<RecordStopRequestDelta> timing;
TimeStamp start = TimeStamp::Now(); if (StaticPrefs::network_send_OnDataFinished()) {
timing = new RecordStopRequestDelta;
mEventQ->RunOrEnqueue(new ChannelFunctionEvent(
[self = UnsafePtr<HttpChannelChild>(this)]() { return self->GetODATarget();
},
[self = UnsafePtr<HttpChannelChild>(this), status = aChannelStatus,
start, timing]() {
TimeStamp now = TimeStamp::Now();
TimeDuration delay = now - start;
glean::networking::http_content_ondatafinished_delay
.AccumulateRawDuration(delay); // We can be on main thread or background thread at this point // http_content_ondatafinished_delay_2 is used to track // delay observed between dispatch the OnDataFinished on the socket // thread and running OnDataFinished on the background thread if (!NS_IsMainThread()) {
glean::networking::http_content_ondatafinished_delay_2
.AccumulateRawDuration(delay);
}
timing->mOnDataFinishedTime = now;
self->SendOnDataFinished(status);
}));
}
mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent( this, [self = UnsafePtr<HttpChannelChild>(this), aChannelStatus, aTiming,
aResponseTrailers,
consoleReports = CopyableTArray{aConsoleReports.Clone()},
aFromSocketProcess, start, timing]() mutable {
TimeStamp now = TimeStamp::Now();
TimeDuration delay = now - start;
glean::networking::http_content_onstop_delay.AccumulateRawDuration(
delay); if (timing) {
timing->mOnStopRequestTime = now;
}
self->OnStopRequest(aChannelStatus, aTiming, aResponseTrailers); if (!aFromSocketProcess) {
self->DoOnConsoleReport(std::move(consoleReports));
self->ContinueOnStopRequest();
}
}));
}
// 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 (LoadOnStopRequestCalled() && mIPCActorDeleted) { return;
}
nsCOMPtr<nsICompressConvStats> conv = do_QueryInterface(mCompressListener); if (conv) {
conv->GetDecodedDataLength(&mDecodedBodySize);
}
// 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 and mEncodedBodySize are set in ProcessOnStopRequest // TODO: check if we need to move assignments of other members to // ProcessOnStopRequest
{ // 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);
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;
}
CollectMixedContentTelemetry();
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();
}
}
if (!mCanceled && NS_SUCCEEDED(mStatus)) {
mStatus = aStatus;
}
}
// We want to inspect all upgradable mixed content loads // (i.e., loads point to HTTP from an HTTPS page), for // resources that stem from audio, video and img elements. // Of those, we want to measure which succceed and which fail. // Some double negatives, but we check the following:exempt loads that // 1) Request was upgraded as mixed passive content // 2) Request _could_ have been upgraded as mixed passive content if the pref // had been set and Request wasn't upgraded by any other means (URL isn't https) void HttpChannelChild::CollectMixedContentTelemetry() {
MOZ_ASSERT(NS_IsMainThread());
bool wasUpgraded = mLoadInfo->GetBrowserDidUpgradeInsecureRequests(); if (!wasUpgraded) { // If this wasn't upgraded, let's check if it _could_ have been upgraded as // passive mixed content and that it wasn't upgraded with any other method if (!mURI->SchemeIs("https") &&
!mLoadInfo->GetBrowserWouldUpgradeInsecureRequests()) { return;
}
}
// UseCounters require a document.
RefPtr<Document> doc;
mLoadInfo->GetLoadingDocument(getter_AddRefs(doc)); if (!doc) { return;
}
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;
// 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 || !LoadWasOpened()); if (!mListener) { return;
}
MOZ_ASSERT(!LoadOnStopRequestCalled(), "We should not call OnStopRequest twice");
if (mListener) {
nsCOMPtr<nsIStreamListener> listener(mListener);
StoreOnStopRequestCalled(true);
listener->OnStopRequest(aRequest, mStatus);
}
StoreOnStopRequestCalled(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."));
StoreOnStopRequestCalled(false);
StoreOnStartRequestCalled(false); return;
}
// 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);
}
// 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();
}
// Might be called twice in race condition in theory. // (one by RecvFailedAsyncOpen, another by // HttpBackgroundChannelChild::ActorFailed) if (LoadOnStartRequestCalled()) { return;
}
if (NS_SUCCEEDED(mStatus)) {
mStatus = status;
}
// We're already being called from IPDL, therefore already "async"
HandleAsyncAbort();
// The redirection is vetoed. No need to suspend the event queue. if (mSuspendForWaitCompleteRedirectSetup) {
mSuspendForWaitCompleteRedirectSetup = false;
mEventQ->Resume();
}
if (NS_SUCCEEDED(rv) ||
(LoadOnStartRequestCalled() && LoadOnStopRequestCalled())) {
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();
}
// In case nsHttpChannel::OnStartRequest wasn't called (e.g. due to flag // LOAD_ONLY_IF_MODIFIED) we want to set LoadAfterOnStartRequestBegun() to // true before notifying listener. if (!LoadAfterOnStartRequestBegun()) {
StoreAfterOnStartRequestBegun(true);
}
if (mListener && !LoadOnStartRequestCalled()) {
nsCOMPtr<nsIStreamListener> listener = mListener; // avoid reentrancy bugs by setting this now
StoreOnStartRequestCalled(true);
listener->OnStartRequest(this);
}
StoreOnStartRequestCalled(true);
// Make sure IsPending 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.
StoreIsPending(false);
// 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::RecvRedirect1Begin( const uint32_t& aRegistrarId, nsIURI* aNewUri, const uint32_t& aNewLoadFlags, const uint32_t& aRedirectFlags, const ParentLoadInfoForwarderArgs& aLoadInfoForwarder,
nsHttpResponseHead&& aResponseHead, nsITransportSecurityInfo* aSecurityInfo, 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));
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);
}
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->CancelWithReason(
rv, "HttpChannelChild Redirect3 failed"_ns);
// 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;
}
if (RefPtr<HttpChannelChild> httpChannelChild =
do_QueryObject(self->mRedirectChannelChild)) { // For sending an IPC message to parent channel so that the loading // can be cancelled.
Unused << httpChannelChild->CancelWithReason(
status, "HttpChannelChild RecvRedirectFailed"_ns);
// 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();
}
}));
// 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(NS_OK);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.