NS_IMETHODIMP nsBaseChannel::OnTransportStatus(nsITransport *transport, nsresult status, uint64_t progress, uint64_t progressMax) { // In some cases, we may wish to suppress transport-layer status events. if (!mPump || NS_FAILED(mStatus) || HasLoadFlag(LOAD_BACKGROUND)) return NS_OK; SUSPEND_PUMP_FOR_SCOPE(); // Lazily fetch mProgressSink if (!mProgressSink) { if (mQueriedProgressSink) return NS_OK; GetCallback(mProgressSink); mQueriedProgressSink = true; if (!mProgressSink) return NS_OK; } nsAutoString statusArg; if (GetStatusArg(status, statusArg)) mProgressSink->OnStatus(this, mListenerContext, status, statusArg.get()); if (progress) mProgressSink->OnProgress(this, mListenerContext, progress, progressMax); return NS_OK; }
NS_IMETHODIMP nsBaseChannel::OnStartRequest(nsIRequest *request, nsISupports *ctxt) { // If our content type is unknown or if the content type is // application/octet-stream and the caller requested it, use the content type // sniffer. If the sniffer is not available for some reason, then we just keep // going as-is. bool shouldSniff = mContentType.EqualsLiteral(UNKNOWN_CONTENT_TYPE) || ((mLoadFlags & LOAD_TREAT_APPLICATION_OCTET_STREAM_AS_UNKNOWN) && mContentType.EqualsLiteral(APPLICATION_OCTET_STREAM)); if (NS_SUCCEEDED(mStatus) && shouldSniff) { mPump->PeekStream(CallUnknownTypeSniffer, static_cast<nsIChannel*>(this)); } // Now, the general type sniffers. Skip this if we have none. if (mLoadFlags & LOAD_CALL_CONTENT_SNIFFERS) mPump->PeekStream(CallTypeSniffers, static_cast<nsIChannel*>(this)); SUSPEND_PUMP_FOR_SCOPE(); if (mListener) // null in case of redirect return mListener->OnStartRequest(this, mListenerContext); return NS_OK; }
NS_IMETHODIMP nsBaseChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *ctxt) { MOZ_ASSERT(!mLoadInfo || mLoadInfo->GetSecurityMode() == 0 || mLoadInfo->GetInitialSecurityCheckDone() || (mLoadInfo->GetSecurityMode() == nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL && nsContentUtils::IsSystemPrincipal(mLoadInfo->LoadingPrincipal())), "security flags in loadInfo but asyncOpen2() not called"); NS_ENSURE_TRUE(mURI, NS_ERROR_NOT_INITIALIZED); NS_ENSURE_TRUE(!mPump, NS_ERROR_IN_PROGRESS); NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED); NS_ENSURE_ARG(listener); // Skip checking for chrome:// sub-resources. nsAutoCString scheme; mURI->GetScheme(scheme); if (!scheme.EqualsLiteral("file")) { NS_CompareLoadInfoAndLoadContext(this); } // Ensure that this is an allowed port before proceeding. nsresult rv = NS_CheckPortSafety(mURI); if (NS_FAILED(rv)) { mCallbacks = nullptr; return rv; } // Store the listener and context early so that OpenContentStream and the // stream's AsyncWait method (called by AsyncRead) can have access to them // via PushStreamConverter and the StreamListener methods. However, since // this typically introduces a reference cycle between this and the listener, // we need to be sure to break the reference if this method does not succeed. mListener = listener; mListenerContext = ctxt; // This method assigns mPump as a side-effect. We need to clear mPump if // this method fails. rv = BeginPumpingData(); if (NS_FAILED(rv)) { mPump = nullptr; ChannelDone(); mCallbacks = nullptr; return rv; } // At this point, we are going to return success no matter what. mWasOpened = true; SUSPEND_PUMP_FOR_SCOPE(); if (mLoadGroup) mLoadGroup->AddRequest(this, nullptr); ClassifyURI(); return NS_OK; }
nsresult nsBaseChannel::Redirect(nsIChannel *newChannel, uint32_t redirectFlags, bool openNewChannel) { SUSPEND_PUMP_FOR_SCOPE(); // Transfer properties newChannel->SetLoadGroup(mLoadGroup); newChannel->SetNotificationCallbacks(mCallbacks); newChannel->SetLoadFlags(mLoadFlags | LOAD_REPLACE); newChannel->SetLoadInfo(mLoadInfo); // Try to preserve the privacy bit if it has been overridden if (mPrivateBrowsingOverriden) { nsCOMPtr<nsIPrivateBrowsingChannel> newPBChannel = do_QueryInterface(newChannel); if (newPBChannel) { newPBChannel->SetPrivate(mPrivateBrowsing); } } nsCOMPtr<nsIWritablePropertyBag> bag = ::do_QueryInterface(newChannel); if (bag) mPropertyHash.EnumerateRead(CopyProperties, bag.get()); // Notify consumer, giving chance to cancel redirect. For backwards compat, // we support nsIHttpEventSink if we are an HTTP channel and if this is not // an internal redirect. nsRefPtr<nsAsyncRedirectVerifyHelper> redirectCallbackHelper = new nsAsyncRedirectVerifyHelper(); bool checkRedirectSynchronously = !openNewChannel; mRedirectChannel = newChannel; mRedirectFlags = redirectFlags; mOpenRedirectChannel = openNewChannel; nsresult rv = redirectCallbackHelper->Init(this, newChannel, redirectFlags, checkRedirectSynchronously); if (NS_FAILED(rv)) return rv; if (checkRedirectSynchronously && NS_FAILED(mStatus)) return mStatus; return NS_OK; }
NS_IMETHODIMP nsBaseChannel::OnDataAvailable(nsIRequest *request, nsISupports *ctxt, nsIInputStream *stream, uint64_t offset, uint32_t count) { SUSPEND_PUMP_FOR_SCOPE(); nsresult rv = mListener->OnDataAvailable(this, mListenerContext, stream, offset, count); if (mSynthProgressEvents && NS_SUCCEEDED(rv)) { uint64_t prog = offset + count; OnTransportStatus(nullptr, NS_NET_STATUS_READING, prog, mContentLength); } return rv; }
NS_IMETHODIMP nsBaseChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *ctxt) { NS_ENSURE_TRUE(mURI, NS_ERROR_NOT_INITIALIZED); NS_ENSURE_TRUE(!mPump, NS_ERROR_IN_PROGRESS); NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED); NS_ENSURE_ARG(listener); // Ensure that this is an allowed port before proceeding. nsresult rv = NS_CheckPortSafety(mURI); if (NS_FAILED(rv)) { mCallbacks = nullptr; return rv; } // Store the listener and context early so that OpenContentStream and the // stream's AsyncWait method (called by AsyncRead) can have access to them // via PushStreamConverter and the StreamListener methods. However, since // this typically introduces a reference cycle between this and the listener, // we need to be sure to break the reference if this method does not succeed. mListener = listener; mListenerContext = ctxt; // This method assigns mPump as a side-effect. We need to clear mPump if // this method fails. rv = BeginPumpingData(); if (NS_FAILED(rv)) { mPump = nullptr; mListener = nullptr; mListenerContext = nullptr; mCallbacks = nullptr; return rv; } // At this point, we are going to return success no matter what. mWasOpened = true; SUSPEND_PUMP_FOR_SCOPE(); if (mLoadGroup) mLoadGroup->AddRequest(this, nullptr); ClassifyURI(); return NS_OK; }
NS_IMETHODIMP nsBaseChannel::OnDataAvailable(nsIRequest *request, nsISupports *ctxt, nsIInputStream *stream, PRUint32 offset, PRUint32 count) { SUSPEND_PUMP_FOR_SCOPE(); nsresult rv = mListener->OnDataAvailable(this, mListenerContext, stream, offset, count); if (mSynthProgressEvents && NS_SUCCEEDED(rv)) { PRUint64 prog = PRUint64(offset) + count; PRUint64 progMax = ContentLength64(); OnTransportStatus(nsnull, nsITransport::STATUS_READING, prog, progMax); } return rv; }
NS_IMETHODIMP nsBaseChannel::OnStartRequest(nsIRequest *request, nsISupports *ctxt) { // If our content type is unknown, then use the content type sniffer. If the // sniffer is not available for some reason, then we just keep going as-is. if (NS_SUCCEEDED(mStatus) && mContentType.EqualsLiteral(UNKNOWN_CONTENT_TYPE)) { mPump->PeekStream(CallUnknownTypeSniffer, static_cast<nsIChannel*>(this)); } // Now, the general type sniffers. Skip this if we have none. if ((mLoadFlags & LOAD_CALL_CONTENT_SNIFFERS) && gIOService->GetContentSniffers().Count() != 0) mPump->PeekStream(CallTypeSniffers, static_cast<nsIChannel*>(this)); SUSPEND_PUMP_FOR_SCOPE(); return mListener->OnStartRequest(this, mListenerContext); }
NS_IMETHODIMP nsBaseChannel::OnDataAvailable(nsIRequest *request, nsISupports *ctxt, nsIInputStream *stream, uint64_t offset, uint32_t count) { SUSPEND_PUMP_FOR_SCOPE(); nsresult rv = mListener->OnDataAvailable(this, mListenerContext, stream, offset, count); if (mSynthProgressEvents && NS_SUCCEEDED(rv)) { int64_t prog = offset + count; if (NS_IsMainThread()) { OnTransportStatus(nullptr, NS_NET_STATUS_READING, prog, mContentLength); } else { class OnTransportStatusAsyncEvent : public nsRunnable { RefPtr<nsBaseChannel> mChannel; int64_t mProgress; int64_t mContentLength; public: OnTransportStatusAsyncEvent(nsBaseChannel* aChannel, int64_t aProgress, int64_t aContentLength) : mChannel(aChannel), mProgress(aProgress), mContentLength(aContentLength) { } NS_IMETHOD Run() override { return mChannel->OnTransportStatus(nullptr, NS_NET_STATUS_READING, mProgress, mContentLength); } }; nsCOMPtr<nsIRunnable> runnable = new OnTransportStatusAsyncEvent(this, prog, mContentLength); NS_DispatchToMainThread(runnable); } } return rv; }
NS_IMETHODIMP nsBaseChannel::OnStartRequest(nsIRequest *request, nsISupports *ctxt) { MOZ_ASSERT(request == mPump); // If our content type is unknown, use the content type // sniffer. If the sniffer is not available for some reason, then we just keep // going as-is. if (NS_SUCCEEDED(mStatus) && mContentType.EqualsLiteral(UNKNOWN_CONTENT_TYPE)) { mPump->PeekStream(CallUnknownTypeSniffer, static_cast<nsIChannel*>(this)); } // Now, the general type sniffers. Skip this if we have none. if (mLoadFlags & LOAD_CALL_CONTENT_SNIFFERS) mPump->PeekStream(CallTypeSniffers, static_cast<nsIChannel*>(this)); SUSPEND_PUMP_FOR_SCOPE(); if (mListener) // null in case of redirect return mListener->OnStartRequest(this, mListenerContext); return NS_OK; }
nsresult nsBaseChannel::Redirect(nsIChannel *newChannel, uint32_t redirectFlags, bool openNewChannel) { SUSPEND_PUMP_FOR_SCOPE(); // Transfer properties newChannel->SetLoadGroup(mLoadGroup); newChannel->SetNotificationCallbacks(mCallbacks); newChannel->SetLoadFlags(mLoadFlags | LOAD_REPLACE); // make a copy of the loadinfo, append to the redirectchain // and set it on the new channel if (mLoadInfo) { nsCOMPtr<nsILoadInfo> newLoadInfo = static_cast<mozilla::LoadInfo*>(mLoadInfo.get())->Clone(); nsCOMPtr<nsIPrincipal> uriPrincipal; nsIScriptSecurityManager *sm = nsContentUtils::GetSecurityManager(); sm->GetChannelURIPrincipal(this, getter_AddRefs(uriPrincipal)); bool isInternalRedirect = (redirectFlags & (nsIChannelEventSink::REDIRECT_INTERNAL | nsIChannelEventSink::REDIRECT_STS_UPGRADE)); newLoadInfo->AppendRedirectedPrincipal(uriPrincipal, isInternalRedirect); newChannel->SetLoadInfo(newLoadInfo); } else { // the newChannel was created with a dummy loadInfo, we should clear // it in case the original channel does not have a loadInfo newChannel->SetLoadInfo(nullptr); } // Preserve the privacy bit if it has been overridden if (mPrivateBrowsingOverriden) { nsCOMPtr<nsIPrivateBrowsingChannel> newPBChannel = do_QueryInterface(newChannel); if (newPBChannel) { newPBChannel->SetPrivate(mPrivateBrowsing); } } nsCOMPtr<nsIWritablePropertyBag> bag = ::do_QueryInterface(newChannel); if (bag) { for (auto iter = mPropertyHash.Iter(); !iter.Done(); iter.Next()) { bag->SetProperty(iter.Key(), iter.UserData()); } } // Notify consumer, giving chance to cancel redirect. For backwards compat, // we support nsIHttpEventSink if we are an HTTP channel and if this is not // an internal redirect. RefPtr<nsAsyncRedirectVerifyHelper> redirectCallbackHelper = new nsAsyncRedirectVerifyHelper(); bool checkRedirectSynchronously = !openNewChannel; mRedirectChannel = newChannel; mRedirectFlags = redirectFlags; mOpenRedirectChannel = openNewChannel; nsresult rv = redirectCallbackHelper->Init(this, newChannel, redirectFlags, checkRedirectSynchronously); if (NS_FAILED(rv)) return rv; if (checkRedirectSynchronously && NS_FAILED(mStatus)) return mStatus; return NS_OK; }
nsresult nsBaseChannel::Redirect(nsIChannel *newChannel, PRUint32 redirectFlags) { SUSPEND_PUMP_FOR_SCOPE(); // Transfer properties newChannel->SetOriginalURI(OriginalURI()); newChannel->SetLoadGroup(mLoadGroup); newChannel->SetNotificationCallbacks(mCallbacks); newChannel->SetLoadFlags(mLoadFlags | LOAD_REPLACE); nsCOMPtr<nsIWritablePropertyBag> bag = ::do_QueryInterface(newChannel); if (bag) mPropertyHash.EnumerateRead(CopyProperties, bag.get()); // Notify consumer, giving chance to cancel redirect. For backwards compat, // we support nsIHttpEventSink if we are an HTTP channel and if this is not // an internal redirect. // Global observers. These come first so that other observers don't see // redirects that get aborted for security reasons anyway. NS_ASSERTION(gIOService, "Must have an IO service"); nsresult rv = gIOService->OnChannelRedirect(this, newChannel, redirectFlags); if (NS_FAILED(rv)) return rv; // Backwards compat for non-internal redirects from a HTTP channel. if (!(redirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL)) { nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(); if (httpChannel) { nsCOMPtr<nsIHttpEventSink> httpEventSink; GetCallback(httpEventSink); if (httpEventSink) { rv = httpEventSink->OnRedirect(httpChannel, newChannel); if (NS_FAILED(rv)) return rv; } } } nsCOMPtr<nsIChannelEventSink> channelEventSink; // Give our consumer a chance to observe/block this redirect. GetCallback(channelEventSink); if (channelEventSink) { rv = channelEventSink->OnChannelRedirect(this, newChannel, redirectFlags); if (NS_FAILED(rv)) return rv; } // If we fail to open the new channel, then we want to leave this channel // unaffected, so we defer tearing down our channel until we have succeeded // with the redirect. rv = newChannel->AsyncOpen(mListener, mListenerContext); if (NS_FAILED(rv)) return rv; // close down this channel Cancel(NS_BINDING_REDIRECTED); mListener = nsnull; mListenerContext = nsnull; return NS_OK; }