michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set sw=2 ts=8 et tw=80 : */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: // HttpLog.h should generally be included first michael@0: #include "HttpLog.h" michael@0: michael@0: #include "mozilla/dom/FileDescriptorSetParent.h" michael@0: #include "mozilla/net/HttpChannelParent.h" michael@0: #include "mozilla/dom/TabParent.h" michael@0: #include "mozilla/net/NeckoParent.h" michael@0: #include "mozilla/unused.h" michael@0: #include "HttpChannelParentListener.h" michael@0: #include "nsHttpHandler.h" michael@0: #include "nsNetUtil.h" michael@0: #include "nsISupportsPriority.h" michael@0: #include "nsIAuthPromptProvider.h" michael@0: #include "nsIScriptSecurityManager.h" michael@0: #include "nsSerializationHelper.h" michael@0: #include "nsISerializable.h" michael@0: #include "nsIAssociatedContentSecurity.h" michael@0: #include "nsIApplicationCacheService.h" michael@0: #include "mozilla/ipc/InputStreamUtils.h" michael@0: #include "mozilla/ipc/URIUtils.h" michael@0: #include "SerializedLoadContext.h" michael@0: michael@0: using namespace mozilla::dom; michael@0: using namespace mozilla::ipc; michael@0: michael@0: namespace mozilla { michael@0: namespace net { michael@0: michael@0: HttpChannelParent::HttpChannelParent(PBrowserParent* iframeEmbedding, michael@0: nsILoadContext* aLoadContext, michael@0: PBOverrideStatus aOverrideStatus) michael@0: : mIPCClosed(false) michael@0: , mStoredStatus(NS_OK) michael@0: , mStoredProgress(0) michael@0: , mStoredProgressMax(0) michael@0: , mSentRedirect1Begin(false) michael@0: , mSentRedirect1BeginFailed(false) michael@0: , mReceivedRedirect2Verify(false) michael@0: , mPBOverride(aOverrideStatus) michael@0: , mLoadContext(aLoadContext) michael@0: , mStatus(NS_OK) michael@0: , mDivertingFromChild(false) michael@0: , mDivertedOnStartRequest(false) michael@0: , mSuspendedForDiversion(false) michael@0: { michael@0: // Ensure gHttpHandler is initialized: we need the atom table up and running. michael@0: nsCOMPtr dummyInitializer = michael@0: do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http"); michael@0: michael@0: MOZ_ASSERT(gHttpHandler); michael@0: mHttpHandler = gHttpHandler; michael@0: michael@0: mTabParent = static_cast(iframeEmbedding); michael@0: } michael@0: michael@0: HttpChannelParent::~HttpChannelParent() michael@0: { michael@0: } michael@0: michael@0: void michael@0: HttpChannelParent::ActorDestroy(ActorDestroyReason why) michael@0: { michael@0: // We may still have refcount>0 if nsHttpChannel hasn't called OnStopRequest michael@0: // yet, but child process has crashed. We must not try to send any more msgs michael@0: // to child, or IPDL will kill chrome process, too. michael@0: mIPCClosed = true; michael@0: } michael@0: michael@0: bool michael@0: HttpChannelParent::Init(const HttpChannelCreationArgs& aArgs) michael@0: { michael@0: switch (aArgs.type()) { michael@0: case HttpChannelCreationArgs::THttpChannelOpenArgs: michael@0: { michael@0: const HttpChannelOpenArgs& a = aArgs.get_HttpChannelOpenArgs(); michael@0: return DoAsyncOpen(a.uri(), a.original(), a.doc(), a.referrer(), michael@0: a.apiRedirectTo(), a.loadFlags(), a.requestHeaders(), michael@0: a.requestMethod(), a.uploadStream(), michael@0: a.uploadStreamHasHeaders(), a.priority(), michael@0: a.redirectionLimit(), a.allowPipelining(), michael@0: a.forceAllowThirdPartyCookie(), a.resumeAt(), michael@0: a.startPos(), a.entityID(), a.chooseApplicationCache(), michael@0: a.appCacheClientID(), a.allowSpdy(), a.fds()); michael@0: } michael@0: case HttpChannelCreationArgs::THttpChannelConnectArgs: michael@0: { michael@0: const HttpChannelConnectArgs& cArgs = aArgs.get_HttpChannelConnectArgs(); michael@0: return ConnectChannel(cArgs.channelId()); michael@0: } michael@0: default: michael@0: NS_NOTREACHED("unknown open type"); michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // HttpChannelParent::nsISupports michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMPL_ISUPPORTS(HttpChannelParent, michael@0: nsIInterfaceRequestor, michael@0: nsIProgressEventSink, michael@0: nsIRequestObserver, michael@0: nsIStreamListener, michael@0: nsIParentChannel, michael@0: nsIParentRedirectingChannel) michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // HttpChannelParent::nsIInterfaceRequestor michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMETHODIMP michael@0: HttpChannelParent::GetInterface(const nsIID& aIID, void **result) michael@0: { michael@0: if (aIID.Equals(NS_GET_IID(nsIAuthPromptProvider)) || michael@0: aIID.Equals(NS_GET_IID(nsISecureBrowserUI))) { michael@0: if (!mTabParent) michael@0: return NS_NOINTERFACE; michael@0: michael@0: return mTabParent->QueryInterface(aIID, result); michael@0: } michael@0: michael@0: // Only support nsILoadContext if child channel's callbacks did too michael@0: if (aIID.Equals(NS_GET_IID(nsILoadContext)) && mLoadContext) { michael@0: NS_ADDREF(mLoadContext); michael@0: *result = static_cast(mLoadContext); michael@0: return NS_OK; michael@0: } michael@0: michael@0: return QueryInterface(aIID, result); michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // HttpChannelParent::PHttpChannelParent michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: bool michael@0: HttpChannelParent::DoAsyncOpen( const URIParams& aURI, michael@0: const OptionalURIParams& aOriginalURI, michael@0: const OptionalURIParams& aDocURI, michael@0: const OptionalURIParams& aReferrerURI, michael@0: const OptionalURIParams& aAPIRedirectToURI, michael@0: const uint32_t& loadFlags, michael@0: const RequestHeaderTuples& requestHeaders, michael@0: const nsCString& requestMethod, michael@0: const OptionalInputStreamParams& uploadStream, michael@0: const bool& uploadStreamHasHeaders, michael@0: const uint16_t& priority, michael@0: const uint8_t& redirectionLimit, michael@0: const bool& allowPipelining, michael@0: const bool& forceAllowThirdPartyCookie, michael@0: const bool& doResumeAt, michael@0: const uint64_t& startPos, michael@0: const nsCString& entityID, michael@0: const bool& chooseApplicationCache, michael@0: const nsCString& appCacheClientID, michael@0: const bool& allowSpdy, michael@0: const OptionalFileDescriptorSet& aFds) michael@0: { michael@0: nsCOMPtr uri = DeserializeURI(aURI); michael@0: if (!uri) { michael@0: // URIParams does MOZ_ASSERT if null, but we need to protect opt builds from michael@0: // null deref here. michael@0: return false; michael@0: } michael@0: nsCOMPtr originalUri = DeserializeURI(aOriginalURI); michael@0: nsCOMPtr docUri = DeserializeURI(aDocURI); michael@0: nsCOMPtr referrerUri = DeserializeURI(aReferrerURI); michael@0: nsCOMPtr apiRedirectToUri = DeserializeURI(aAPIRedirectToURI); michael@0: michael@0: nsCString uriSpec; michael@0: uri->GetSpec(uriSpec); michael@0: LOG(("HttpChannelParent RecvAsyncOpen [this=%p uri=%s]\n", michael@0: this, uriSpec.get())); michael@0: michael@0: nsresult rv; michael@0: michael@0: nsCOMPtr ios(do_GetIOService(&rv)); michael@0: if (NS_FAILED(rv)) michael@0: return SendFailedAsyncOpen(rv); michael@0: michael@0: nsCOMPtr channel; michael@0: rv = NS_NewChannel(getter_AddRefs(channel), uri, ios, nullptr, nullptr, loadFlags); michael@0: if (NS_FAILED(rv)) michael@0: return SendFailedAsyncOpen(rv); michael@0: michael@0: mChannel = static_cast(channel.get()); michael@0: if (mPBOverride != kPBOverride_Unset) { michael@0: mChannel->SetPrivate(mPBOverride == kPBOverride_Private ? true : false); michael@0: } michael@0: michael@0: if (doResumeAt) michael@0: mChannel->ResumeAt(startPos, entityID); michael@0: michael@0: if (originalUri) michael@0: mChannel->SetOriginalURI(originalUri); michael@0: if (docUri) michael@0: mChannel->SetDocumentURI(docUri); michael@0: if (referrerUri) michael@0: mChannel->SetReferrerInternal(referrerUri); michael@0: if (apiRedirectToUri) michael@0: mChannel->RedirectTo(apiRedirectToUri); michael@0: if (loadFlags != nsIRequest::LOAD_NORMAL) michael@0: mChannel->SetLoadFlags(loadFlags); michael@0: michael@0: for (uint32_t i = 0; i < requestHeaders.Length(); i++) { michael@0: mChannel->SetRequestHeader(requestHeaders[i].mHeader, michael@0: requestHeaders[i].mValue, michael@0: requestHeaders[i].mMerge); michael@0: } michael@0: michael@0: mParentListener = new HttpChannelParentListener(this); michael@0: michael@0: mChannel->SetNotificationCallbacks(mParentListener); michael@0: michael@0: mChannel->SetRequestMethod(nsDependentCString(requestMethod.get())); michael@0: michael@0: nsTArray fds; michael@0: if (aFds.type() == OptionalFileDescriptorSet::TPFileDescriptorSetParent) { michael@0: FileDescriptorSetParent* fdSetActor = michael@0: static_cast(aFds.get_PFileDescriptorSetParent()); michael@0: MOZ_ASSERT(fdSetActor); michael@0: michael@0: fdSetActor->ForgetFileDescriptors(fds); michael@0: MOZ_ASSERT(!fds.IsEmpty()); michael@0: michael@0: unused << fdSetActor->Send__delete__(fdSetActor); michael@0: } michael@0: michael@0: nsCOMPtr stream = DeserializeInputStream(uploadStream, fds); michael@0: if (stream) { michael@0: mChannel->InternalSetUploadStream(stream); michael@0: mChannel->SetUploadStreamHasHeaders(uploadStreamHasHeaders); michael@0: } michael@0: michael@0: if (priority != nsISupportsPriority::PRIORITY_NORMAL) michael@0: mChannel->SetPriority(priority); michael@0: mChannel->SetRedirectionLimit(redirectionLimit); michael@0: mChannel->SetAllowPipelining(allowPipelining); michael@0: mChannel->SetForceAllowThirdPartyCookie(forceAllowThirdPartyCookie); michael@0: mChannel->SetAllowSpdy(allowSpdy); michael@0: michael@0: nsCOMPtr appCacheChan = michael@0: do_QueryObject(mChannel); michael@0: nsCOMPtr appCacheService = michael@0: do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID); michael@0: michael@0: bool setChooseApplicationCache = chooseApplicationCache; michael@0: if (appCacheChan && appCacheService) { michael@0: // We might potentially want to drop this flag (that is TRUE by default) michael@0: // after we successfully associate the channel with an application cache michael@0: // reported by the channel child. Dropping it here may be too early. michael@0: appCacheChan->SetInheritApplicationCache(false); michael@0: if (!appCacheClientID.IsEmpty()) { michael@0: nsCOMPtr appCache; michael@0: rv = appCacheService->GetApplicationCache(appCacheClientID, michael@0: getter_AddRefs(appCache)); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: appCacheChan->SetApplicationCache(appCache); michael@0: setChooseApplicationCache = false; michael@0: } michael@0: } michael@0: michael@0: if (setChooseApplicationCache) { michael@0: bool inBrowser = false; michael@0: uint32_t appId = NECKO_NO_APP_ID; michael@0: if (mLoadContext) { michael@0: mLoadContext->GetIsInBrowserElement(&inBrowser); michael@0: mLoadContext->GetAppId(&appId); michael@0: } michael@0: michael@0: bool chooseAppCache = false; michael@0: nsCOMPtr secMan = michael@0: do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID); michael@0: if (secMan) { michael@0: nsCOMPtr principal; michael@0: secMan->GetAppCodebasePrincipal(uri, appId, inBrowser, getter_AddRefs(principal)); michael@0: michael@0: // This works because we've already called SetNotificationCallbacks and michael@0: // done mPBOverride logic by this point. michael@0: chooseAppCache = NS_ShouldCheckAppCache(principal, NS_UsePrivateBrowsing(mChannel)); michael@0: } michael@0: michael@0: appCacheChan->SetChooseApplicationCache(chooseAppCache); michael@0: } michael@0: } michael@0: michael@0: rv = mChannel->AsyncOpen(mParentListener, nullptr); michael@0: if (NS_FAILED(rv)) michael@0: return SendFailedAsyncOpen(rv); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: HttpChannelParent::ConnectChannel(const uint32_t& channelId) michael@0: { michael@0: nsresult rv; michael@0: michael@0: LOG(("Looking for a registered channel [this=%p, id=%d]", this, channelId)); michael@0: nsCOMPtr channel; michael@0: rv = NS_LinkRedirectChannels(channelId, this, getter_AddRefs(channel)); michael@0: mChannel = static_cast(channel.get()); michael@0: LOG((" found channel %p, rv=%08x", mChannel.get(), rv)); michael@0: michael@0: if (mPBOverride != kPBOverride_Unset) { michael@0: // redirected-to channel may not support PB michael@0: nsCOMPtr pbChannel = do_QueryObject(mChannel); michael@0: if (pbChannel) { michael@0: pbChannel->SetPrivate(mPBOverride == kPBOverride_Private ? true : false); michael@0: } michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: HttpChannelParent::RecvSetPriority(const uint16_t& priority) michael@0: { michael@0: if (mChannel) { michael@0: mChannel->SetPriority(priority); michael@0: } michael@0: michael@0: nsCOMPtr priorityRedirectChannel = michael@0: do_QueryInterface(mRedirectChannel); michael@0: if (priorityRedirectChannel) michael@0: priorityRedirectChannel->SetPriority(priority); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: HttpChannelParent::RecvSuspend() michael@0: { michael@0: if (mChannel) { michael@0: mChannel->Suspend(); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: HttpChannelParent::RecvResume() michael@0: { michael@0: if (mChannel) { michael@0: mChannel->Resume(); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: HttpChannelParent::RecvCancel(const nsresult& status) michael@0: { michael@0: // May receive cancel before channel has been constructed! michael@0: if (mChannel) { michael@0: mChannel->Cancel(status); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: michael@0: bool michael@0: HttpChannelParent::RecvSetCacheTokenCachedCharset(const nsCString& charset) michael@0: { michael@0: if (mCacheEntry) michael@0: mCacheEntry->SetMetaDataElement("charset", charset.get()); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: HttpChannelParent::RecvUpdateAssociatedContentSecurity(const int32_t& broken, michael@0: const int32_t& no) michael@0: { michael@0: if (mAssociatedContentSecurity) { michael@0: mAssociatedContentSecurity->SetCountSubRequestsBrokenSecurity(broken); michael@0: mAssociatedContentSecurity->SetCountSubRequestsNoSecurity(no); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: HttpChannelParent::RecvRedirect2Verify(const nsresult& result, michael@0: const RequestHeaderTuples& changedHeaders, michael@0: const OptionalURIParams& aAPIRedirectURI) michael@0: { michael@0: if (NS_SUCCEEDED(result)) { michael@0: nsCOMPtr newHttpChannel = michael@0: do_QueryInterface(mRedirectChannel); michael@0: michael@0: if (newHttpChannel) { michael@0: nsCOMPtr apiRedirectUri = DeserializeURI(aAPIRedirectURI); michael@0: michael@0: if (apiRedirectUri) michael@0: newHttpChannel->RedirectTo(apiRedirectUri); michael@0: michael@0: for (uint32_t i = 0; i < changedHeaders.Length(); i++) { michael@0: newHttpChannel->SetRequestHeader(changedHeaders[i].mHeader, michael@0: changedHeaders[i].mValue, michael@0: changedHeaders[i].mMerge); michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (!mRedirectCallback) { michael@0: // This should according the logic never happen, log the situation. michael@0: if (mReceivedRedirect2Verify) michael@0: LOG(("RecvRedirect2Verify[%p]: Duplicate fire", this)); michael@0: if (mSentRedirect1BeginFailed) michael@0: LOG(("RecvRedirect2Verify[%p]: Send to child failed", this)); michael@0: if (mSentRedirect1Begin && NS_FAILED(result)) michael@0: LOG(("RecvRedirect2Verify[%p]: Redirect failed", this)); michael@0: if (mSentRedirect1Begin && NS_SUCCEEDED(result)) michael@0: LOG(("RecvRedirect2Verify[%p]: Redirect succeeded", this)); michael@0: if (!mRedirectChannel) michael@0: LOG(("RecvRedirect2Verify[%p]: Missing redirect channel", this)); michael@0: michael@0: NS_ERROR("Unexpcted call to HttpChannelParent::RecvRedirect2Verify, " michael@0: "mRedirectCallback null"); michael@0: } michael@0: michael@0: mReceivedRedirect2Verify = true; michael@0: michael@0: if (mRedirectCallback) { michael@0: mRedirectCallback->OnRedirectVerifyCallback(result); michael@0: mRedirectCallback = nullptr; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: HttpChannelParent::RecvDocumentChannelCleanup() michael@0: { michael@0: // From now on only using mAssociatedContentSecurity. Free everything else. michael@0: mChannel = 0; // Reclaim some memory sooner. michael@0: mCacheEntry = 0; // Else we'll block other channels reading same URI michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: HttpChannelParent::RecvMarkOfflineCacheEntryAsForeign() michael@0: { michael@0: if (mOfflineForeignMarker) { michael@0: mOfflineForeignMarker->MarkAsForeign(); michael@0: mOfflineForeignMarker = 0; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: HttpChannelParent::RecvDivertOnDataAvailable(const nsCString& data, michael@0: const uint64_t& offset, michael@0: const uint32_t& count) michael@0: { michael@0: MOZ_ASSERT(mParentListener); michael@0: if (NS_WARN_IF(!mDivertingFromChild)) { michael@0: MOZ_ASSERT(mDivertingFromChild, michael@0: "Cannot RecvDivertOnDataAvailable if diverting is not set!"); michael@0: FailDiversion(NS_ERROR_UNEXPECTED); michael@0: return false; michael@0: } michael@0: michael@0: // Drop OnDataAvailables if the parent was canceled already. michael@0: if (NS_FAILED(mStatus)) { michael@0: return true; michael@0: } michael@0: michael@0: nsCOMPtr stringStream; michael@0: nsresult rv = NS_NewByteInputStream(getter_AddRefs(stringStream), data.get(), michael@0: count, NS_ASSIGNMENT_DEPEND); michael@0: if (NS_FAILED(rv)) { michael@0: if (mChannel) { michael@0: mChannel->Cancel(rv); michael@0: } michael@0: mStatus = rv; michael@0: return true; michael@0: } michael@0: michael@0: rv = mParentListener->OnDataAvailable(mChannel, nullptr, stringStream, michael@0: offset, count); michael@0: stringStream->Close(); michael@0: if (NS_FAILED(rv)) { michael@0: if (mChannel) { michael@0: mChannel->Cancel(rv); michael@0: } michael@0: mStatus = rv; michael@0: return true; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: HttpChannelParent::RecvDivertOnStopRequest(const nsresult& statusCode) michael@0: { michael@0: MOZ_ASSERT(mParentListener); michael@0: if (NS_WARN_IF(!mDivertingFromChild)) { michael@0: MOZ_ASSERT(mDivertingFromChild, michael@0: "Cannot RecvDivertOnStopRequest if diverting is not set!"); michael@0: FailDiversion(NS_ERROR_UNEXPECTED); michael@0: return false; michael@0: } michael@0: michael@0: // Honor the channel's status even if the underlying transaction completed. michael@0: nsresult status = NS_FAILED(mStatus) ? mStatus : statusCode; michael@0: michael@0: // Reset fake pending status in case OnStopRequest has already been called. michael@0: if (mChannel) { michael@0: mChannel->ForcePending(false); michael@0: } michael@0: michael@0: mParentListener->OnStopRequest(mChannel, nullptr, status); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: HttpChannelParent::RecvDivertComplete() michael@0: { michael@0: MOZ_ASSERT(mParentListener); michael@0: mParentListener = nullptr; michael@0: if (NS_WARN_IF(!mDivertingFromChild)) { michael@0: MOZ_ASSERT(mDivertingFromChild, michael@0: "Cannot RecvDivertComplete if diverting is not set!"); michael@0: FailDiversion(NS_ERROR_UNEXPECTED); michael@0: return false; michael@0: } michael@0: michael@0: nsresult rv = ResumeForDiversion(); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) { michael@0: FailDiversion(NS_ERROR_UNEXPECTED); michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // HttpChannelParent::nsIRequestObserver michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMETHODIMP michael@0: HttpChannelParent::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext) michael@0: { michael@0: LOG(("HttpChannelParent::OnStartRequest [this=%p]\n", this)); michael@0: michael@0: MOZ_RELEASE_ASSERT(!mDivertingFromChild, michael@0: "Cannot call OnStartRequest if diverting is set!"); michael@0: michael@0: nsHttpChannel *chan = static_cast(aRequest); michael@0: nsHttpResponseHead *responseHead = chan->GetResponseHead(); michael@0: nsHttpRequestHead *requestHead = chan->GetRequestHead(); michael@0: bool isFromCache = false; michael@0: chan->IsFromCache(&isFromCache); michael@0: uint32_t expirationTime = nsICache::NO_EXPIRATION_TIME; michael@0: chan->GetCacheTokenExpirationTime(&expirationTime); michael@0: nsCString cachedCharset; michael@0: chan->GetCacheTokenCachedCharset(cachedCharset); michael@0: michael@0: bool loadedFromApplicationCache; michael@0: chan->GetLoadedFromApplicationCache(&loadedFromApplicationCache); michael@0: if (loadedFromApplicationCache) { michael@0: mOfflineForeignMarker = chan->GetOfflineCacheEntryAsForeignMarker(); michael@0: nsCOMPtr appCache; michael@0: chan->GetApplicationCache(getter_AddRefs(appCache)); michael@0: nsCString appCacheGroupId; michael@0: nsCString appCacheClientId; michael@0: appCache->GetGroupID(appCacheGroupId); michael@0: appCache->GetClientID(appCacheClientId); michael@0: if (mIPCClosed || michael@0: !SendAssociateApplicationCache(appCacheGroupId, appCacheClientId)) michael@0: { michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: } michael@0: michael@0: nsCOMPtr encodedChannel = do_QueryInterface(aRequest); michael@0: if (encodedChannel) michael@0: encodedChannel->SetApplyConversion(false); michael@0: michael@0: // Keep the cache entry for future use in RecvSetCacheTokenCachedCharset(). michael@0: // It could be already released by nsHttpChannel at that time. michael@0: nsCOMPtr cacheEntry; michael@0: chan->GetCacheToken(getter_AddRefs(cacheEntry)); michael@0: mCacheEntry = do_QueryInterface(cacheEntry); michael@0: michael@0: nsresult channelStatus = NS_OK; michael@0: chan->GetStatus(&channelStatus); michael@0: michael@0: nsCString secInfoSerialization; michael@0: nsCOMPtr secInfoSupp; michael@0: chan->GetSecurityInfo(getter_AddRefs(secInfoSupp)); michael@0: if (secInfoSupp) { michael@0: mAssociatedContentSecurity = do_QueryInterface(secInfoSupp); michael@0: nsCOMPtr secInfoSer = do_QueryInterface(secInfoSupp); michael@0: if (secInfoSer) michael@0: NS_SerializeToString(secInfoSer, secInfoSerialization); michael@0: } michael@0: michael@0: uint16_t redirectCount = 0; michael@0: mChannel->GetRedirectCount(&redirectCount); michael@0: if (mIPCClosed || michael@0: !SendOnStartRequest(channelStatus, michael@0: responseHead ? *responseHead : nsHttpResponseHead(), michael@0: !!responseHead, michael@0: requestHead->Headers(), michael@0: isFromCache, michael@0: mCacheEntry ? true : false, michael@0: expirationTime, cachedCharset, secInfoSerialization, michael@0: mChannel->GetSelfAddr(), mChannel->GetPeerAddr(), michael@0: redirectCount)) michael@0: { michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpChannelParent::OnStopRequest(nsIRequest *aRequest, michael@0: nsISupports *aContext, michael@0: nsresult aStatusCode) michael@0: { michael@0: LOG(("HttpChannelParent::OnStopRequest: [this=%p status=%x]\n", michael@0: this, aStatusCode)); michael@0: michael@0: MOZ_RELEASE_ASSERT(!mDivertingFromChild, michael@0: "Cannot call OnStopRequest if diverting is set!"); michael@0: michael@0: if (mIPCClosed || !SendOnStopRequest(aStatusCode)) michael@0: return NS_ERROR_UNEXPECTED; michael@0: return NS_OK; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // HttpChannelParent::nsIStreamListener michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMETHODIMP michael@0: HttpChannelParent::OnDataAvailable(nsIRequest *aRequest, michael@0: nsISupports *aContext, michael@0: nsIInputStream *aInputStream, michael@0: uint64_t aOffset, michael@0: uint32_t aCount) michael@0: { michael@0: LOG(("HttpChannelParent::OnDataAvailable [this=%p]\n", this)); michael@0: michael@0: MOZ_RELEASE_ASSERT(!mDivertingFromChild, michael@0: "Cannot call OnDataAvailable if diverting is set!"); michael@0: michael@0: nsCString data; michael@0: nsresult rv = NS_ReadInputStreamToString(aInputStream, data, aCount); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: nsresult channelStatus = NS_OK; michael@0: mChannel->GetStatus(&channelStatus); michael@0: michael@0: // OnDataAvailable is always preceded by OnStatus/OnProgress calls that set michael@0: // mStoredStatus/mStoredProgress(Max) to appropriate values, unless michael@0: // LOAD_BACKGROUND set. In that case, they'll have garbage values, but michael@0: // child doesn't use them. michael@0: if (mIPCClosed || !SendOnTransportAndData(channelStatus, mStoredStatus, michael@0: mStoredProgress, mStoredProgressMax, michael@0: data, aOffset, aCount)) { michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // HttpChannelParent::nsIProgressEventSink michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMETHODIMP michael@0: HttpChannelParent::OnProgress(nsIRequest *aRequest, michael@0: nsISupports *aContext, michael@0: uint64_t aProgress, michael@0: uint64_t aProgressMax) michael@0: { michael@0: // OnStatus has always just set mStoredStatus. If it indicates this precedes michael@0: // OnDataAvailable, store and ODA will send to child. michael@0: if (mStoredStatus == NS_NET_STATUS_RECEIVING_FROM || michael@0: mStoredStatus == NS_NET_STATUS_READING) michael@0: { michael@0: mStoredProgress = aProgress; michael@0: mStoredProgressMax = aProgressMax; michael@0: } else { michael@0: // Send to child now. The only case I've observed that this handles (i.e. michael@0: // non-ODA status with progress > 0) is data upload progress notification michael@0: // (status == NS_NET_STATUS_SENDING_TO) michael@0: if (mIPCClosed || !SendOnProgress(aProgress, aProgressMax)) michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpChannelParent::OnStatus(nsIRequest *aRequest, michael@0: nsISupports *aContext, michael@0: nsresult aStatus, michael@0: const char16_t *aStatusArg) michael@0: { michael@0: // If this precedes OnDataAvailable, store and ODA will send to child. michael@0: if (aStatus == NS_NET_STATUS_RECEIVING_FROM || michael@0: aStatus == NS_NET_STATUS_READING) michael@0: { michael@0: mStoredStatus = aStatus; michael@0: return NS_OK; michael@0: } michael@0: // Otherwise, send to child now michael@0: if (mIPCClosed || !SendOnStatus(aStatus)) michael@0: return NS_ERROR_UNEXPECTED; michael@0: return NS_OK; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // HttpChannelParent::nsIParentChannel michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMETHODIMP michael@0: HttpChannelParent::SetParentListener(HttpChannelParentListener* aListener) michael@0: { michael@0: MOZ_ASSERT(aListener); michael@0: MOZ_ASSERT(!mParentListener, "SetParentListener should only be called for " michael@0: "new HttpChannelParents after a redirect, when " michael@0: "mParentListener is null."); michael@0: mParentListener = aListener; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpChannelParent::Delete() michael@0: { michael@0: if (!mIPCClosed) michael@0: unused << SendDeleteSelf(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // HttpChannelParent::nsIParentRedirectingChannel michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMETHODIMP michael@0: HttpChannelParent::StartRedirect(uint32_t newChannelId, michael@0: nsIChannel* newChannel, michael@0: uint32_t redirectFlags, michael@0: nsIAsyncVerifyRedirectCallback* callback) michael@0: { michael@0: if (mIPCClosed) michael@0: return NS_BINDING_ABORTED; michael@0: michael@0: nsCOMPtr newURI; michael@0: newChannel->GetURI(getter_AddRefs(newURI)); michael@0: michael@0: URIParams uriParams; michael@0: SerializeURI(newURI, uriParams); michael@0: michael@0: nsHttpResponseHead *responseHead = mChannel->GetResponseHead(); michael@0: bool result = SendRedirect1Begin(newChannelId, uriParams, redirectFlags, michael@0: responseHead ? *responseHead michael@0: : nsHttpResponseHead()); michael@0: if (!result) { michael@0: // Bug 621446 investigation michael@0: mSentRedirect1BeginFailed = true; michael@0: return NS_BINDING_ABORTED; michael@0: } michael@0: michael@0: // Bug 621446 investigation michael@0: mSentRedirect1Begin = true; michael@0: michael@0: // Result is handled in RecvRedirect2Verify above michael@0: michael@0: mRedirectChannel = newChannel; michael@0: mRedirectCallback = callback; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpChannelParent::CompleteRedirect(bool succeeded) michael@0: { michael@0: if (succeeded && !mIPCClosed) { michael@0: // TODO: check return value: assume child dead if failed michael@0: unused << SendRedirect3Complete(); michael@0: } michael@0: michael@0: mRedirectChannel = nullptr; michael@0: return NS_OK; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // HttpChannelParent::ADivertableParentChannel michael@0: //----------------------------------------------------------------------------- michael@0: nsresult michael@0: HttpChannelParent::SuspendForDiversion() michael@0: { michael@0: MOZ_ASSERT(mChannel); michael@0: MOZ_ASSERT(mParentListener); michael@0: if (NS_WARN_IF(mDivertingFromChild)) { michael@0: MOZ_ASSERT(!mDivertingFromChild, "Already suspended for diversion!"); michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: // Try suspending the channel. Allow it to fail, since OnStopRequest may have michael@0: // been called and thus the channel may not be pending. michael@0: nsresult rv = mChannel->Suspend(); michael@0: MOZ_ASSERT(NS_SUCCEEDED(rv) || rv == NS_ERROR_NOT_AVAILABLE); michael@0: mSuspendedForDiversion = NS_SUCCEEDED(rv); michael@0: michael@0: rv = mParentListener->SuspendForDiversion(); michael@0: MOZ_ASSERT(NS_SUCCEEDED(rv)); michael@0: michael@0: // Once this is set, no more OnStart/OnData/OnStop callbacks should be sent michael@0: // to the child. michael@0: mDivertingFromChild = true; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* private, supporting function for ADivertableParentChannel */ michael@0: nsresult michael@0: HttpChannelParent::ResumeForDiversion() michael@0: { michael@0: MOZ_ASSERT(mChannel); michael@0: if (NS_WARN_IF(!mDivertingFromChild)) { michael@0: MOZ_ASSERT(mDivertingFromChild, michael@0: "Cannot ResumeForDiversion if not diverting!"); michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: if (mSuspendedForDiversion) { michael@0: // The nsHttpChannel will deliver remaining OnData/OnStop for the transfer. michael@0: nsresult rv = mChannel->Resume(); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) { michael@0: FailDiversion(NS_ERROR_UNEXPECTED, true); michael@0: return rv; michael@0: } michael@0: mSuspendedForDiversion = false; michael@0: } michael@0: michael@0: if (NS_WARN_IF(mIPCClosed || !SendDeleteSelf())) { michael@0: FailDiversion(NS_ERROR_UNEXPECTED); michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: HttpChannelParent::DivertTo(nsIStreamListener *aListener) michael@0: { michael@0: MOZ_ASSERT(mParentListener); michael@0: if (NS_WARN_IF(!mDivertingFromChild)) { michael@0: MOZ_ASSERT(mDivertingFromChild, michael@0: "Cannot DivertTo new listener if diverting is not set!"); michael@0: return; michael@0: } michael@0: michael@0: DebugOnly rv = mParentListener->DivertTo(aListener); michael@0: MOZ_ASSERT(NS_SUCCEEDED(rv)); michael@0: michael@0: if (NS_WARN_IF(mIPCClosed || !SendFlushedForDiversion())) { michael@0: FailDiversion(NS_ERROR_UNEXPECTED); michael@0: return; michael@0: } michael@0: michael@0: // Call OnStartRequest and SendDivertMessages asynchronously to avoid michael@0: // reentering client context. michael@0: NS_DispatchToCurrentThread( michael@0: NS_NewRunnableMethod(this, &HttpChannelParent::StartDiversion)); michael@0: return; michael@0: } michael@0: michael@0: void michael@0: HttpChannelParent::StartDiversion() michael@0: { michael@0: if (NS_WARN_IF(!mDivertingFromChild)) { michael@0: MOZ_ASSERT(mDivertingFromChild, michael@0: "Cannot StartDiversion if diverting is not set!"); michael@0: return; michael@0: } michael@0: michael@0: // Fake pending status in case OnStopRequest has already been called. michael@0: if (mChannel) { michael@0: mChannel->ForcePending(true); michael@0: } michael@0: michael@0: // Call OnStartRequest for the "DivertTo" listener. michael@0: nsresult rv = mParentListener->OnStartRequest(mChannel, nullptr); michael@0: if (NS_FAILED(rv)) { michael@0: if (mChannel) { michael@0: mChannel->Cancel(rv); michael@0: } michael@0: mStatus = rv; michael@0: } michael@0: mDivertedOnStartRequest = true; michael@0: michael@0: // After OnStartRequest has been called, tell HttpChannelChild to divert the michael@0: // OnDataAvailables and OnStopRequest to this HttpChannelParent. michael@0: if (NS_WARN_IF(mIPCClosed || !SendDivertMessages())) { michael@0: FailDiversion(NS_ERROR_UNEXPECTED); michael@0: return; michael@0: } michael@0: } michael@0: michael@0: class HTTPFailDiversionEvent : public nsRunnable michael@0: { michael@0: public: michael@0: HTTPFailDiversionEvent(HttpChannelParent *aChannelParent, michael@0: nsresult aErrorCode, michael@0: bool aSkipResume) michael@0: : mChannelParent(aChannelParent) michael@0: , mErrorCode(aErrorCode) michael@0: , mSkipResume(aSkipResume) michael@0: { michael@0: MOZ_RELEASE_ASSERT(aChannelParent); michael@0: MOZ_RELEASE_ASSERT(NS_FAILED(aErrorCode)); michael@0: } michael@0: NS_IMETHOD Run() michael@0: { michael@0: mChannelParent->NotifyDiversionFailed(mErrorCode, mSkipResume); michael@0: return NS_OK; michael@0: } michael@0: private: michael@0: nsRefPtr mChannelParent; michael@0: nsresult mErrorCode; michael@0: bool mSkipResume; michael@0: }; michael@0: michael@0: void michael@0: HttpChannelParent::FailDiversion(nsresult aErrorCode, michael@0: bool aSkipResume) michael@0: { michael@0: MOZ_RELEASE_ASSERT(NS_FAILED(aErrorCode)); michael@0: MOZ_RELEASE_ASSERT(mDivertingFromChild); michael@0: MOZ_RELEASE_ASSERT(mParentListener); michael@0: MOZ_RELEASE_ASSERT(mChannel); michael@0: michael@0: NS_DispatchToCurrentThread( michael@0: new HTTPFailDiversionEvent(this, aErrorCode, aSkipResume)); michael@0: } michael@0: michael@0: void michael@0: HttpChannelParent::NotifyDiversionFailed(nsresult aErrorCode, michael@0: bool aSkipResume) michael@0: { michael@0: MOZ_RELEASE_ASSERT(NS_FAILED(aErrorCode)); michael@0: MOZ_RELEASE_ASSERT(mDivertingFromChild); michael@0: MOZ_RELEASE_ASSERT(mParentListener); michael@0: MOZ_RELEASE_ASSERT(mChannel); michael@0: michael@0: mChannel->Cancel(aErrorCode); michael@0: michael@0: mChannel->ForcePending(false); michael@0: michael@0: bool isPending = false; michael@0: nsresult rv = mChannel->IsPending(&isPending); michael@0: MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv)); michael@0: michael@0: // Resume only if we suspended earlier. michael@0: if (mSuspendedForDiversion) { michael@0: mChannel->Resume(); michael@0: } michael@0: // Channel has already sent OnStartRequest to the child, so ensure that we michael@0: // call it here if it hasn't already been called. michael@0: if (!mDivertedOnStartRequest) { michael@0: mChannel->ForcePending(true); michael@0: mParentListener->OnStartRequest(mChannel, nullptr); michael@0: mChannel->ForcePending(false); michael@0: } michael@0: // If the channel is pending, it will call OnStopRequest itself; otherwise, do michael@0: // it here. michael@0: if (!isPending) { michael@0: mParentListener->OnStopRequest(mChannel, nullptr, aErrorCode); michael@0: } michael@0: mParentListener = nullptr; michael@0: mChannel = nullptr; michael@0: michael@0: if (!mIPCClosed) { michael@0: unused << SendDeleteSelf(); michael@0: } michael@0: } michael@0: michael@0: }} // mozilla::net