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: 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 "nsHttp.h" michael@0: #include "mozilla/unused.h" michael@0: #include "mozilla/dom/ContentChild.h" michael@0: #include "mozilla/dom/TabChild.h" michael@0: #include "mozilla/dom/FileDescriptorSetChild.h" michael@0: #include "mozilla/net/NeckoChild.h" michael@0: #include "mozilla/net/HttpChannelChild.h" michael@0: michael@0: #include "nsStringStream.h" michael@0: #include "nsHttpHandler.h" michael@0: #include "nsNetUtil.h" michael@0: #include "nsSerializationHelper.h" michael@0: #include "mozilla/Attributes.h" michael@0: #include "mozilla/ipc/InputStreamUtils.h" michael@0: #include "mozilla/ipc/URIUtils.h" michael@0: #include "mozilla/net/ChannelDiverterChild.h" michael@0: #include "mozilla/net/DNS.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: //----------------------------------------------------------------------------- michael@0: // HttpChannelChild michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: HttpChannelChild::HttpChannelChild() michael@0: : HttpAsyncAborter(MOZ_THIS_IN_INITIALIZER_LIST()) michael@0: , mIsFromCache(false) michael@0: , mCacheEntryAvailable(false) michael@0: , mCacheExpirationTime(nsICache::NO_EXPIRATION_TIME) michael@0: , mSendResumeAt(false) michael@0: , mIPCOpen(false) michael@0: , mKeptAlive(false) michael@0: , mDivertingToParent(false) michael@0: , mFlushedForDiversion(false) michael@0: , mSuspendSent(false) michael@0: { michael@0: LOG(("Creating HttpChannelChild @%x\n", this)); michael@0: michael@0: mChannelCreationTime = PR_Now(); michael@0: mChannelCreationTimestamp = TimeStamp::Now(); michael@0: mAsyncOpenTime = TimeStamp::Now(); michael@0: mEventQ = new ChannelEventQueue(static_cast(this)); michael@0: } michael@0: michael@0: HttpChannelChild::~HttpChannelChild() michael@0: { michael@0: LOG(("Destroying HttpChannelChild @%x\n", this)); michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // HttpChannelChild::nsISupports michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: // Override nsHashPropertyBag's AddRef: we don't need thread-safe refcnt michael@0: NS_IMPL_ADDREF(HttpChannelChild) michael@0: michael@0: NS_IMETHODIMP_(MozExternalRefCountType) HttpChannelChild::Release() michael@0: { michael@0: NS_PRECONDITION(0 != mRefCnt, "dup release"); michael@0: NS_ASSERT_OWNINGTHREAD(HttpChannelChild); michael@0: --mRefCnt; michael@0: NS_LOG_RELEASE(this, mRefCnt, "HttpChannelChild"); michael@0: michael@0: // Normally we Send_delete in OnStopRequest, but when we need to retain the michael@0: // remote channel for security info IPDL itself holds 1 reference, so we michael@0: // Send_delete when refCnt==1. But if !mIPCOpen, then there's nobody to send michael@0: // to, so we fall through. michael@0: if (mKeptAlive && mRefCnt == 1 && mIPCOpen) { michael@0: mKeptAlive = false; michael@0: // Send_delete calls NeckoChild::DeallocPHttpChannel, which will release michael@0: // again to refcount==0 michael@0: PHttpChannelChild::Send__delete__(this); michael@0: return 0; michael@0: } michael@0: michael@0: if (mRefCnt == 0) { michael@0: mRefCnt = 1; /* stabilize */ michael@0: delete this; michael@0: return 0; michael@0: } michael@0: return mRefCnt; michael@0: } michael@0: michael@0: NS_INTERFACE_MAP_BEGIN(HttpChannelChild) michael@0: NS_INTERFACE_MAP_ENTRY(nsIRequest) michael@0: NS_INTERFACE_MAP_ENTRY(nsIChannel) michael@0: NS_INTERFACE_MAP_ENTRY(nsIHttpChannel) michael@0: NS_INTERFACE_MAP_ENTRY(nsIHttpChannelInternal) michael@0: NS_INTERFACE_MAP_ENTRY(nsICacheInfoChannel) michael@0: NS_INTERFACE_MAP_ENTRY(nsIResumableChannel) michael@0: NS_INTERFACE_MAP_ENTRY(nsISupportsPriority) michael@0: NS_INTERFACE_MAP_ENTRY(nsIProxiedChannel) michael@0: NS_INTERFACE_MAP_ENTRY(nsITraceableChannel) michael@0: NS_INTERFACE_MAP_ENTRY(nsIApplicationCacheContainer) michael@0: NS_INTERFACE_MAP_ENTRY(nsIApplicationCacheChannel) michael@0: NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectCallback) michael@0: NS_INTERFACE_MAP_ENTRY(nsIChildChannel) michael@0: NS_INTERFACE_MAP_ENTRY(nsIHttpChannelChild) michael@0: NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAssociatedContentSecurity, GetAssociatedContentSecurity()) michael@0: NS_INTERFACE_MAP_ENTRY(nsIDivertableChannel) michael@0: NS_INTERFACE_MAP_END_INHERITING(HttpBaseChannel) michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // HttpChannelChild::PHttpChannelChild michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: void michael@0: HttpChannelChild::AddIPDLReference() michael@0: { michael@0: MOZ_ASSERT(!mIPCOpen, "Attempt to retain more than one IPDL reference"); michael@0: mIPCOpen = true; michael@0: AddRef(); michael@0: } michael@0: michael@0: void michael@0: HttpChannelChild::ReleaseIPDLReference() michael@0: { michael@0: MOZ_ASSERT(mIPCOpen, "Attempt to release nonexistent IPDL reference"); michael@0: mIPCOpen = false; michael@0: Release(); michael@0: } michael@0: michael@0: class AssociateApplicationCacheEvent : public ChannelEvent michael@0: { michael@0: public: michael@0: AssociateApplicationCacheEvent(HttpChannelChild* child, michael@0: const nsCString &groupID, michael@0: const nsCString &clientID) michael@0: : mChild(child) michael@0: , groupID(groupID) michael@0: , clientID(clientID) {} michael@0: michael@0: void Run() { mChild->AssociateApplicationCache(groupID, clientID); } michael@0: private: michael@0: HttpChannelChild* mChild; michael@0: nsCString groupID; michael@0: nsCString clientID; michael@0: }; michael@0: michael@0: bool michael@0: HttpChannelChild::RecvAssociateApplicationCache(const nsCString &groupID, michael@0: const nsCString &clientID) michael@0: { michael@0: if (mEventQ->ShouldEnqueue()) { michael@0: mEventQ->Enqueue(new AssociateApplicationCacheEvent(this, groupID, clientID)); michael@0: } else { michael@0: AssociateApplicationCache(groupID, clientID); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: HttpChannelChild::AssociateApplicationCache(const nsCString &groupID, michael@0: const nsCString &clientID) michael@0: { michael@0: nsresult rv; michael@0: mApplicationCache = do_CreateInstance(NS_APPLICATIONCACHE_CONTRACTID, &rv); michael@0: if (NS_FAILED(rv)) michael@0: return; michael@0: michael@0: mLoadedFromApplicationCache = true; michael@0: mApplicationCache->InitAsHandle(groupID, clientID); michael@0: } michael@0: michael@0: class StartRequestEvent : public ChannelEvent michael@0: { michael@0: public: michael@0: StartRequestEvent(HttpChannelChild* child, michael@0: const nsresult& channelStatus, michael@0: const nsHttpResponseHead& responseHead, michael@0: const bool& useResponseHead, michael@0: const nsHttpHeaderArray& requestHeaders, michael@0: const bool& isFromCache, michael@0: const bool& cacheEntryAvailable, michael@0: const uint32_t& cacheExpirationTime, michael@0: const nsCString& cachedCharset, michael@0: const nsCString& securityInfoSerialization, michael@0: const NetAddr& selfAddr, michael@0: const NetAddr& peerAddr) michael@0: : mChild(child) michael@0: , mChannelStatus(channelStatus) michael@0: , mResponseHead(responseHead) michael@0: , mRequestHeaders(requestHeaders) michael@0: , mUseResponseHead(useResponseHead) michael@0: , mIsFromCache(isFromCache) michael@0: , mCacheEntryAvailable(cacheEntryAvailable) michael@0: , mCacheExpirationTime(cacheExpirationTime) michael@0: , mCachedCharset(cachedCharset) michael@0: , mSecurityInfoSerialization(securityInfoSerialization) michael@0: , mSelfAddr(selfAddr) michael@0: , mPeerAddr(peerAddr) michael@0: {} michael@0: michael@0: void Run() michael@0: { michael@0: mChild->OnStartRequest(mChannelStatus, mResponseHead, mUseResponseHead, michael@0: mRequestHeaders, mIsFromCache, mCacheEntryAvailable, michael@0: mCacheExpirationTime, mCachedCharset, michael@0: mSecurityInfoSerialization, mSelfAddr, mPeerAddr); michael@0: } michael@0: private: michael@0: HttpChannelChild* mChild; michael@0: nsresult mChannelStatus; michael@0: nsHttpResponseHead mResponseHead; michael@0: nsHttpHeaderArray mRequestHeaders; michael@0: bool mUseResponseHead; michael@0: bool mIsFromCache; michael@0: bool mCacheEntryAvailable; michael@0: uint32_t mCacheExpirationTime; michael@0: nsCString mCachedCharset; michael@0: nsCString mSecurityInfoSerialization; michael@0: NetAddr mSelfAddr; michael@0: NetAddr mPeerAddr; michael@0: }; michael@0: michael@0: bool michael@0: HttpChannelChild::RecvOnStartRequest(const nsresult& channelStatus, michael@0: const nsHttpResponseHead& responseHead, michael@0: const bool& useResponseHead, michael@0: const nsHttpHeaderArray& requestHeaders, michael@0: const bool& isFromCache, michael@0: const bool& cacheEntryAvailable, michael@0: const uint32_t& cacheExpirationTime, michael@0: const nsCString& cachedCharset, michael@0: const nsCString& securityInfoSerialization, michael@0: const NetAddr& selfAddr, michael@0: const NetAddr& peerAddr, michael@0: const int16_t& redirectCount) michael@0: { michael@0: // mFlushedForDiversion and mDivertingToParent should NEVER be set at this michael@0: // stage, as they are set in the listener's OnStartRequest. michael@0: MOZ_RELEASE_ASSERT(!mFlushedForDiversion, michael@0: "mFlushedForDiversion should be unset before OnStartRequest!"); michael@0: MOZ_RELEASE_ASSERT(!mDivertingToParent, michael@0: "mDivertingToParent should be unset before OnStartRequest!"); michael@0: michael@0: michael@0: mRedirectCount = redirectCount; michael@0: michael@0: if (mEventQ->ShouldEnqueue()) { michael@0: mEventQ->Enqueue(new StartRequestEvent(this, channelStatus, responseHead, michael@0: useResponseHead, requestHeaders, michael@0: isFromCache, cacheEntryAvailable, michael@0: cacheExpirationTime, cachedCharset, michael@0: securityInfoSerialization, selfAddr, michael@0: peerAddr)); michael@0: } else { michael@0: OnStartRequest(channelStatus, responseHead, useResponseHead, requestHeaders, michael@0: isFromCache, cacheEntryAvailable, cacheExpirationTime, michael@0: cachedCharset, securityInfoSerialization, selfAddr, michael@0: peerAddr); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: HttpChannelChild::OnStartRequest(const nsresult& channelStatus, michael@0: const nsHttpResponseHead& responseHead, michael@0: const bool& useResponseHead, michael@0: const nsHttpHeaderArray& requestHeaders, michael@0: const bool& isFromCache, michael@0: const bool& cacheEntryAvailable, michael@0: const uint32_t& cacheExpirationTime, michael@0: const nsCString& cachedCharset, michael@0: const nsCString& securityInfoSerialization, michael@0: const NetAddr& selfAddr, michael@0: const NetAddr& peerAddr) michael@0: { michael@0: LOG(("HttpChannelChild::RecvOnStartRequest [this=%p]\n", this)); michael@0: michael@0: // mFlushedForDiversion and mDivertingToParent should NEVER be set at this michael@0: // stage, as they are set in the listener's OnStartRequest. michael@0: MOZ_RELEASE_ASSERT(!mFlushedForDiversion, michael@0: "mFlushedForDiversion should be unset before OnStartRequest!"); michael@0: MOZ_RELEASE_ASSERT(!mDivertingToParent, michael@0: "mDivertingToParent should be unset before OnStartRequest!"); michael@0: michael@0: if (!mCanceled && NS_SUCCEEDED(mStatus)) { michael@0: mStatus = channelStatus; michael@0: } michael@0: michael@0: if (useResponseHead && !mCanceled) michael@0: mResponseHead = new nsHttpResponseHead(responseHead); michael@0: michael@0: if (!securityInfoSerialization.IsEmpty()) { michael@0: NS_DeserializeObject(securityInfoSerialization, michael@0: getter_AddRefs(mSecurityInfo)); michael@0: } michael@0: michael@0: mIsFromCache = isFromCache; michael@0: mCacheEntryAvailable = cacheEntryAvailable; michael@0: mCacheExpirationTime = cacheExpirationTime; michael@0: mCachedCharset = cachedCharset; michael@0: michael@0: AutoEventEnqueuer ensureSerialDispatch(mEventQ); michael@0: michael@0: // replace our request headers with what actually got sent in the parent michael@0: mRequestHead.Headers() = requestHeaders; michael@0: michael@0: // Note: this is where we would notify "http-on-examine-response" observers. michael@0: // We have deliberately disabled this for child processes (see bug 806753) michael@0: // michael@0: // gHttpHandler->OnExamineResponse(this); michael@0: michael@0: mTracingEnabled = false; michael@0: michael@0: nsresult rv = mListener->OnStartRequest(this, mListenerContext); michael@0: if (NS_FAILED(rv)) { michael@0: Cancel(rv); michael@0: return; michael@0: } michael@0: michael@0: if (mDivertingToParent) { michael@0: mListener = nullptr; michael@0: mListenerContext = nullptr; michael@0: if (mLoadGroup) { michael@0: mLoadGroup->RemoveRequest(this, nullptr, mStatus); michael@0: } michael@0: } michael@0: michael@0: if (mResponseHead) michael@0: SetCookie(mResponseHead->PeekHeader(nsHttp::Set_Cookie)); michael@0: michael@0: rv = ApplyContentConversions(); michael@0: if (NS_FAILED(rv)) michael@0: Cancel(rv); michael@0: michael@0: mSelfAddr = selfAddr; michael@0: mPeerAddr = peerAddr; michael@0: } michael@0: michael@0: class TransportAndDataEvent : public ChannelEvent michael@0: { michael@0: public: michael@0: TransportAndDataEvent(HttpChannelChild* child, michael@0: const nsresult& channelStatus, michael@0: const nsresult& transportStatus, michael@0: const uint64_t& progress, michael@0: const uint64_t& progressMax, michael@0: const nsCString& data, michael@0: const uint64_t& offset, michael@0: const uint32_t& count) michael@0: : mChild(child) michael@0: , mChannelStatus(channelStatus) michael@0: , mTransportStatus(transportStatus) michael@0: , mProgress(progress) michael@0: , mProgressMax(progressMax) michael@0: , mData(data) michael@0: , mOffset(offset) michael@0: , mCount(count) {} michael@0: michael@0: void Run() michael@0: { michael@0: mChild->OnTransportAndData(mChannelStatus, mTransportStatus, mProgress, michael@0: mProgressMax, mData, mOffset, mCount); michael@0: } michael@0: private: michael@0: HttpChannelChild* mChild; michael@0: nsresult mChannelStatus; michael@0: nsresult mTransportStatus; michael@0: uint64_t mProgress; michael@0: uint64_t mProgressMax; michael@0: nsCString mData; michael@0: uint64_t mOffset; michael@0: uint32_t mCount; michael@0: }; michael@0: michael@0: bool michael@0: HttpChannelChild::RecvOnTransportAndData(const nsresult& channelStatus, michael@0: const nsresult& transportStatus, michael@0: const uint64_t& progress, michael@0: const uint64_t& progressMax, michael@0: const nsCString& data, michael@0: const uint64_t& offset, michael@0: const uint32_t& count) michael@0: { michael@0: MOZ_RELEASE_ASSERT(!mFlushedForDiversion, michael@0: "Should not be receiving any more callbacks from parent!"); michael@0: michael@0: if (mEventQ->ShouldEnqueue()) { michael@0: mEventQ->Enqueue(new TransportAndDataEvent(this, channelStatus, michael@0: transportStatus, progress, michael@0: progressMax, data, offset, michael@0: count)); michael@0: } else { michael@0: MOZ_RELEASE_ASSERT(!mDivertingToParent, michael@0: "ShouldEnqueue when diverting to parent!"); michael@0: michael@0: OnTransportAndData(channelStatus, transportStatus, progress, progressMax, michael@0: data, offset, count); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: HttpChannelChild::OnTransportAndData(const nsresult& channelStatus, michael@0: const nsresult& transportStatus, michael@0: const uint64_t progress, michael@0: const uint64_t& progressMax, michael@0: const nsCString& data, michael@0: const uint64_t& offset, michael@0: const uint32_t& count) michael@0: { michael@0: LOG(("HttpChannelChild::OnTransportAndData [this=%p]\n", this)); michael@0: michael@0: if (!mCanceled && NS_SUCCEEDED(mStatus)) { michael@0: mStatus = channelStatus; michael@0: } michael@0: michael@0: // For diversion to parent, just SendDivertOnDataAvailable. michael@0: if (mDivertingToParent) { michael@0: MOZ_RELEASE_ASSERT(!mFlushedForDiversion, michael@0: "Should not be processing any more callbacks from parent!"); michael@0: michael@0: SendDivertOnDataAvailable(data, offset, count); michael@0: return; michael@0: } michael@0: michael@0: if (mCanceled) michael@0: return; michael@0: michael@0: // cache the progress sink so we don't have to query for it each time. michael@0: if (!mProgressSink) michael@0: GetCallback(mProgressSink); michael@0: michael@0: // Hold queue lock throughout all three calls, else we might process a later michael@0: // necko msg in between them. michael@0: AutoEventEnqueuer ensureSerialDispatch(mEventQ); michael@0: michael@0: // block status/progress after Cancel or OnStopRequest has been called, michael@0: // or if channel has LOAD_BACKGROUND set. michael@0: // - JDUELL: may not need mStatus/mIsPending checks, given this is always called michael@0: // during OnDataAvailable, and we've already checked mCanceled. Code michael@0: // dupe'd from nsHttpChannel michael@0: if (mProgressSink && NS_SUCCEEDED(mStatus) && mIsPending && michael@0: !(mLoadFlags & LOAD_BACKGROUND)) michael@0: { michael@0: // OnStatus michael@0: // michael@0: MOZ_ASSERT(transportStatus == NS_NET_STATUS_RECEIVING_FROM || michael@0: transportStatus == NS_NET_STATUS_READING); michael@0: michael@0: nsAutoCString host; michael@0: mURI->GetHost(host); michael@0: mProgressSink->OnStatus(this, nullptr, transportStatus, michael@0: NS_ConvertUTF8toUTF16(host).get()); michael@0: // OnProgress michael@0: // michael@0: if (progress > 0) { michael@0: MOZ_ASSERT(progress <= progressMax, "unexpected progress values"); michael@0: mProgressSink->OnProgress(this, nullptr, progress, progressMax); michael@0: } michael@0: } michael@0: michael@0: // OnDataAvailable michael@0: // michael@0: // NOTE: the OnDataAvailable contract requires the client to read all the data michael@0: // in the inputstream. This code relies on that ('data' will go away after michael@0: // this function). Apparently the previous, non-e10s behavior was to actually michael@0: // support only reading part of the data, allowing later calls to read the michael@0: // rest. 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: Cancel(rv); michael@0: return; michael@0: } michael@0: michael@0: rv = mListener->OnDataAvailable(this, mListenerContext, michael@0: stringStream, offset, count); michael@0: stringStream->Close(); michael@0: if (NS_FAILED(rv)) { michael@0: Cancel(rv); michael@0: } michael@0: } michael@0: michael@0: class StopRequestEvent : public ChannelEvent michael@0: { michael@0: public: michael@0: StopRequestEvent(HttpChannelChild* child, michael@0: const nsresult& channelStatus) michael@0: : mChild(child) michael@0: , mChannelStatus(channelStatus) {} michael@0: michael@0: void Run() { mChild->OnStopRequest(mChannelStatus); } michael@0: private: michael@0: HttpChannelChild* mChild; michael@0: nsresult mChannelStatus; michael@0: }; michael@0: michael@0: bool michael@0: HttpChannelChild::RecvOnStopRequest(const nsresult& channelStatus) michael@0: { michael@0: MOZ_RELEASE_ASSERT(!mFlushedForDiversion, michael@0: "Should not be receiving any more callbacks from parent!"); michael@0: michael@0: if (mEventQ->ShouldEnqueue()) { michael@0: mEventQ->Enqueue(new StopRequestEvent(this, channelStatus)); michael@0: } else { michael@0: MOZ_ASSERT(!mDivertingToParent, "ShouldEnqueue when diverting to parent!"); michael@0: michael@0: OnStopRequest(channelStatus); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: HttpChannelChild::OnStopRequest(const nsresult& channelStatus) michael@0: { michael@0: LOG(("HttpChannelChild::OnStopRequest [this=%p status=%x]\n", michael@0: this, channelStatus)); michael@0: michael@0: if (mDivertingToParent) { michael@0: MOZ_RELEASE_ASSERT(!mFlushedForDiversion, michael@0: "Should not be processing any more callbacks from parent!"); michael@0: michael@0: SendDivertOnStopRequest(channelStatus); michael@0: return; michael@0: } michael@0: michael@0: mIsPending = false; michael@0: michael@0: if (!mCanceled && NS_SUCCEEDED(mStatus)) { michael@0: mStatus = channelStatus; michael@0: } michael@0: michael@0: { // We must flush the queue before we Send__delete__ michael@0: // (although we really shouldn't receive any msgs after OnStop), michael@0: // so make sure this goes out of scope before then. michael@0: AutoEventEnqueuer ensureSerialDispatch(mEventQ); michael@0: michael@0: mListener->OnStopRequest(this, mListenerContext, mStatus); michael@0: michael@0: mListener = 0; michael@0: mListenerContext = 0; michael@0: mCacheEntryAvailable = false; michael@0: if (mLoadGroup) michael@0: mLoadGroup->RemoveRequest(this, nullptr, mStatus); michael@0: } michael@0: michael@0: if (mLoadFlags & LOAD_DOCUMENT_URI) { michael@0: // Keep IPDL channel open, but only for updating security info. michael@0: mKeptAlive = true; michael@0: SendDocumentChannelCleanup(); michael@0: } else { michael@0: // This calls NeckoChild::DeallocPHttpChannelChild(), which deletes |this| if IPDL michael@0: // holds the last reference. Don't rely on |this| existing after here. michael@0: PHttpChannelChild::Send__delete__(this); michael@0: } michael@0: } michael@0: michael@0: class ProgressEvent : public ChannelEvent michael@0: { michael@0: public: michael@0: ProgressEvent(HttpChannelChild* child, michael@0: const uint64_t& progress, michael@0: const uint64_t& progressMax) michael@0: : mChild(child) michael@0: , mProgress(progress) michael@0: , mProgressMax(progressMax) {} michael@0: michael@0: void Run() { mChild->OnProgress(mProgress, mProgressMax); } michael@0: private: michael@0: HttpChannelChild* mChild; michael@0: uint64_t mProgress, mProgressMax; michael@0: }; michael@0: michael@0: bool michael@0: HttpChannelChild::RecvOnProgress(const uint64_t& progress, michael@0: const uint64_t& progressMax) michael@0: { michael@0: if (mEventQ->ShouldEnqueue()) { michael@0: mEventQ->Enqueue(new ProgressEvent(this, progress, progressMax)); michael@0: } else { michael@0: OnProgress(progress, progressMax); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: HttpChannelChild::OnProgress(const uint64_t& progress, michael@0: const uint64_t& progressMax) michael@0: { michael@0: LOG(("HttpChannelChild::OnProgress [this=%p progress=%llu/%llu]\n", michael@0: this, progress, progressMax)); michael@0: michael@0: if (mCanceled) michael@0: return; michael@0: michael@0: // cache the progress sink so we don't have to query for it each time. michael@0: if (!mProgressSink) michael@0: GetCallback(mProgressSink); michael@0: michael@0: AutoEventEnqueuer ensureSerialDispatch(mEventQ); michael@0: michael@0: // block socket status event after Cancel or OnStopRequest has been called, michael@0: // or if channel has LOAD_BACKGROUND set michael@0: if (mProgressSink && NS_SUCCEEDED(mStatus) && mIsPending && michael@0: !(mLoadFlags & LOAD_BACKGROUND)) michael@0: { michael@0: if (progress > 0) { michael@0: MOZ_ASSERT(progress <= progressMax, "unexpected progress values"); michael@0: mProgressSink->OnProgress(this, nullptr, progress, progressMax); michael@0: } michael@0: } michael@0: } michael@0: michael@0: class StatusEvent : public ChannelEvent michael@0: { michael@0: public: michael@0: StatusEvent(HttpChannelChild* child, michael@0: const nsresult& status) michael@0: : mChild(child) michael@0: , mStatus(status) {} michael@0: michael@0: void Run() { mChild->OnStatus(mStatus); } michael@0: private: michael@0: HttpChannelChild* mChild; michael@0: nsresult mStatus; michael@0: }; michael@0: michael@0: bool michael@0: HttpChannelChild::RecvOnStatus(const nsresult& status) michael@0: { michael@0: if (mEventQ->ShouldEnqueue()) { michael@0: mEventQ->Enqueue(new StatusEvent(this, status)); michael@0: } else { michael@0: OnStatus(status); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: HttpChannelChild::OnStatus(const nsresult& status) michael@0: { michael@0: LOG(("HttpChannelChild::OnStatus [this=%p status=%x]\n", this, status)); michael@0: michael@0: if (mCanceled) michael@0: return; michael@0: michael@0: // cache the progress sink so we don't have to query for it each time. michael@0: if (!mProgressSink) michael@0: GetCallback(mProgressSink); michael@0: michael@0: AutoEventEnqueuer ensureSerialDispatch(mEventQ); michael@0: michael@0: // block socket status event after Cancel or OnStopRequest has been called, michael@0: // or if channel has LOAD_BACKGROUND set michael@0: if (mProgressSink && NS_SUCCEEDED(mStatus) && mIsPending && michael@0: !(mLoadFlags & LOAD_BACKGROUND)) michael@0: { michael@0: nsAutoCString host; michael@0: mURI->GetHost(host); michael@0: mProgressSink->OnStatus(this, nullptr, status, michael@0: NS_ConvertUTF8toUTF16(host).get()); michael@0: } michael@0: } michael@0: michael@0: class FailedAsyncOpenEvent : public ChannelEvent michael@0: { michael@0: public: michael@0: FailedAsyncOpenEvent(HttpChannelChild* child, const nsresult& status) michael@0: : mChild(child) michael@0: , mStatus(status) {} michael@0: michael@0: void Run() { mChild->FailedAsyncOpen(mStatus); } michael@0: private: michael@0: HttpChannelChild* mChild; michael@0: nsresult mStatus; michael@0: }; michael@0: michael@0: bool michael@0: HttpChannelChild::RecvFailedAsyncOpen(const nsresult& status) michael@0: { michael@0: if (mEventQ->ShouldEnqueue()) { michael@0: mEventQ->Enqueue(new FailedAsyncOpenEvent(this, status)); michael@0: } else { michael@0: FailedAsyncOpen(status); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: // We need to have an implementation of this function just so that we can keep michael@0: // all references to mCallOnResume of type HttpChannelChild: it's not OK in C++ michael@0: // to set a member function ptr to a base class function. michael@0: void michael@0: HttpChannelChild::HandleAsyncAbort() michael@0: { michael@0: HttpAsyncAborter::HandleAsyncAbort(); michael@0: } michael@0: michael@0: void michael@0: HttpChannelChild::FailedAsyncOpen(const nsresult& status) michael@0: { michael@0: LOG(("HttpChannelChild::FailedAsyncOpen [this=%p status=%x]\n", this, status)); michael@0: michael@0: mStatus = status; michael@0: mIsPending = false; michael@0: // We're already being called from IPDL, therefore already "async" michael@0: HandleAsyncAbort(); michael@0: } michael@0: michael@0: void michael@0: HttpChannelChild::DoNotifyListenerCleanup() michael@0: { michael@0: if (mIPCOpen) michael@0: PHttpChannelChild::Send__delete__(this); michael@0: } michael@0: michael@0: class DeleteSelfEvent : public ChannelEvent michael@0: { michael@0: public: michael@0: DeleteSelfEvent(HttpChannelChild* child) : mChild(child) {} michael@0: void Run() { mChild->DeleteSelf(); } michael@0: private: michael@0: HttpChannelChild* mChild; michael@0: }; michael@0: michael@0: bool michael@0: HttpChannelChild::RecvDeleteSelf() michael@0: { michael@0: if (mEventQ->ShouldEnqueue()) { michael@0: mEventQ->Enqueue(new DeleteSelfEvent(this)); michael@0: } else { michael@0: DeleteSelf(); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: HttpChannelChild::DeleteSelf() michael@0: { michael@0: Send__delete__(this); michael@0: } michael@0: michael@0: class Redirect1Event : public ChannelEvent michael@0: { michael@0: public: michael@0: Redirect1Event(HttpChannelChild* child, michael@0: const uint32_t& newChannelId, michael@0: const URIParams& newURI, michael@0: const uint32_t& redirectFlags, michael@0: const nsHttpResponseHead& responseHead) michael@0: : mChild(child) michael@0: , mNewChannelId(newChannelId) michael@0: , mNewURI(newURI) michael@0: , mRedirectFlags(redirectFlags) michael@0: , mResponseHead(responseHead) {} michael@0: michael@0: void Run() michael@0: { michael@0: mChild->Redirect1Begin(mNewChannelId, mNewURI, mRedirectFlags, michael@0: mResponseHead); michael@0: } michael@0: private: michael@0: HttpChannelChild* mChild; michael@0: uint32_t mNewChannelId; michael@0: URIParams mNewURI; michael@0: uint32_t mRedirectFlags; michael@0: nsHttpResponseHead mResponseHead; michael@0: }; michael@0: michael@0: bool michael@0: HttpChannelChild::RecvRedirect1Begin(const uint32_t& newChannelId, michael@0: const URIParams& newUri, michael@0: const uint32_t& redirectFlags, michael@0: const nsHttpResponseHead& responseHead) michael@0: { michael@0: if (mEventQ->ShouldEnqueue()) { michael@0: mEventQ->Enqueue(new Redirect1Event(this, newChannelId, newUri, michael@0: redirectFlags, responseHead)); michael@0: } else { michael@0: Redirect1Begin(newChannelId, newUri, redirectFlags, responseHead); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: HttpChannelChild::Redirect1Begin(const uint32_t& newChannelId, michael@0: const URIParams& newUri, michael@0: const uint32_t& redirectFlags, michael@0: const nsHttpResponseHead& responseHead) michael@0: { michael@0: nsresult rv; michael@0: michael@0: nsCOMPtr ioService; michael@0: rv = gHttpHandler->GetIOService(getter_AddRefs(ioService)); michael@0: if (NS_FAILED(rv)) { michael@0: // Veto redirect. nsHttpChannel decides to cancel or continue. michael@0: OnRedirectVerifyCallback(rv); michael@0: return; michael@0: } michael@0: michael@0: nsCOMPtr uri = DeserializeURI(newUri); michael@0: michael@0: nsCOMPtr newChannel; michael@0: rv = ioService->NewChannelFromURI(uri, getter_AddRefs(newChannel)); michael@0: if (NS_FAILED(rv)) { michael@0: // Veto redirect. nsHttpChannel decides to cancel or continue. michael@0: OnRedirectVerifyCallback(rv); michael@0: return; michael@0: } michael@0: michael@0: // We won't get OnStartRequest, set cookies here. michael@0: mResponseHead = new nsHttpResponseHead(responseHead); michael@0: SetCookie(mResponseHead->PeekHeader(nsHttp::Set_Cookie)); michael@0: michael@0: bool rewriteToGET = HttpBaseChannel::ShouldRewriteRedirectToGET(mResponseHead->Status(), michael@0: mRequestHead.ParsedMethod()); michael@0: michael@0: rv = SetupReplacementChannel(uri, newChannel, !rewriteToGET); michael@0: if (NS_FAILED(rv)) { michael@0: // Veto redirect. nsHttpChannel decides to cancel or continue. michael@0: OnRedirectVerifyCallback(rv); michael@0: return; michael@0: } michael@0: michael@0: mRedirectChannelChild = do_QueryInterface(newChannel); michael@0: if (mRedirectChannelChild) { michael@0: mRedirectChannelChild->ConnectParent(newChannelId); michael@0: rv = gHttpHandler->AsyncOnChannelRedirect(this, michael@0: newChannel, michael@0: redirectFlags); michael@0: } else { michael@0: LOG((" redirecting to a protocol that doesn't implement" michael@0: " nsIChildChannel")); michael@0: rv = NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: if (NS_FAILED(rv)) michael@0: OnRedirectVerifyCallback(rv); michael@0: } michael@0: michael@0: class Redirect3Event : public ChannelEvent michael@0: { michael@0: public: michael@0: Redirect3Event(HttpChannelChild* child) : mChild(child) {} michael@0: void Run() { mChild->Redirect3Complete(); } michael@0: private: michael@0: HttpChannelChild* mChild; michael@0: }; michael@0: michael@0: bool michael@0: HttpChannelChild::RecvRedirect3Complete() michael@0: { michael@0: if (mEventQ->ShouldEnqueue()) { michael@0: mEventQ->Enqueue(new Redirect3Event(this)); michael@0: } else { michael@0: Redirect3Complete(); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: class HttpFlushedForDiversionEvent : public ChannelEvent michael@0: { michael@0: public: michael@0: HttpFlushedForDiversionEvent(HttpChannelChild* aChild) michael@0: : mChild(aChild) michael@0: { michael@0: MOZ_RELEASE_ASSERT(aChild); michael@0: } michael@0: michael@0: void Run() michael@0: { michael@0: mChild->FlushedForDiversion(); michael@0: } michael@0: private: michael@0: HttpChannelChild* mChild; michael@0: }; michael@0: michael@0: bool michael@0: HttpChannelChild::RecvFlushedForDiversion() michael@0: { michael@0: MOZ_RELEASE_ASSERT(mDivertingToParent); michael@0: MOZ_RELEASE_ASSERT(mEventQ->ShouldEnqueue()); michael@0: michael@0: mEventQ->Enqueue(new HttpFlushedForDiversionEvent(this)); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: HttpChannelChild::FlushedForDiversion() michael@0: { michael@0: MOZ_RELEASE_ASSERT(mDivertingToParent); michael@0: michael@0: // Once this is set, it should not be unset before HttpChannelChild is taken michael@0: // down. After it is set, no OnStart/OnData/OnStop callbacks should be michael@0: // received from the parent channel, nor dequeued from the ChannelEventQueue. michael@0: mFlushedForDiversion = true; michael@0: michael@0: SendDivertComplete(); michael@0: } michael@0: michael@0: bool michael@0: HttpChannelChild::RecvDivertMessages() michael@0: { michael@0: MOZ_RELEASE_ASSERT(mDivertingToParent); michael@0: MOZ_RELEASE_ASSERT(mSuspendCount > 0); michael@0: michael@0: // DivertTo() has been called on parent, so we can now start sending queued michael@0: // IPDL messages back to parent listener. michael@0: MOZ_RELEASE_ASSERT(NS_SUCCEEDED(Resume())); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: HttpChannelChild::Redirect3Complete() michael@0: { michael@0: nsresult rv = NS_OK; michael@0: michael@0: // Chrome channel has been AsyncOpen'd. Reflect this in child. michael@0: if (mRedirectChannelChild) michael@0: rv = mRedirectChannelChild->CompleteRedirectSetup(mListener, michael@0: mListenerContext); michael@0: michael@0: // Redirecting to new channel: shut this down and init new channel michael@0: if (mLoadGroup) michael@0: mLoadGroup->RemoveRequest(this, nullptr, NS_BINDING_ABORTED); michael@0: michael@0: if (NS_FAILED(rv)) michael@0: NS_WARNING("CompleteRedirectSetup failed, HttpChannelChild already open?"); michael@0: michael@0: // Release ref to new channel. michael@0: mRedirectChannelChild = nullptr; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // HttpChannelChild::nsIChildChannel michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMETHODIMP michael@0: HttpChannelChild::ConnectParent(uint32_t id) michael@0: { michael@0: mozilla::dom::TabChild* tabChild = nullptr; michael@0: nsCOMPtr iTabChild; michael@0: GetCallback(iTabChild); michael@0: if (iTabChild) { michael@0: tabChild = static_cast(iTabChild.get()); michael@0: } michael@0: if (MissingRequiredTabChild(tabChild, "http")) { michael@0: return NS_ERROR_ILLEGAL_VALUE; michael@0: } michael@0: michael@0: // The socket transport in the chrome process now holds a logical ref to us michael@0: // until OnStopRequest, or we do a redirect, or we hit an IPDL error. michael@0: AddIPDLReference(); michael@0: michael@0: HttpChannelConnectArgs connectArgs(id); michael@0: if (!gNeckoChild-> michael@0: SendPHttpChannelConstructor(this, tabChild, michael@0: IPC::SerializedLoadContext(this), michael@0: connectArgs)) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpChannelChild::CompleteRedirectSetup(nsIStreamListener *listener, michael@0: nsISupports *aContext) michael@0: { michael@0: LOG(("HttpChannelChild::FinishRedirectSetup [this=%p]\n", this)); michael@0: michael@0: NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS); michael@0: NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED); michael@0: michael@0: /* michael@0: * No need to check for cancel: we don't get here if nsHttpChannel canceled michael@0: * before AsyncOpen(); if it's canceled after that, OnStart/Stop will just michael@0: * get called with error code as usual. So just setup mListener and make the michael@0: * channel reflect AsyncOpen'ed state. michael@0: */ michael@0: michael@0: mIsPending = true; michael@0: mWasOpened = true; michael@0: mListener = listener; michael@0: mListenerContext = aContext; michael@0: michael@0: // add ourselves to the load group. michael@0: if (mLoadGroup) michael@0: mLoadGroup->AddRequest(this, nullptr); michael@0: michael@0: // We already have an open IPDL connection to the parent. If on-modify-request michael@0: // listeners or load group observers canceled us, let the parent handle it michael@0: // and send it back to us naturally. michael@0: return NS_OK; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // HttpChannelChild::nsIAsyncVerifyRedirectCallback michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMETHODIMP michael@0: HttpChannelChild::OnRedirectVerifyCallback(nsresult result) michael@0: { michael@0: OptionalURIParams redirectURI; michael@0: nsCOMPtr newHttpChannel = michael@0: do_QueryInterface(mRedirectChannelChild); michael@0: michael@0: if (newHttpChannel) { michael@0: // Must not be called until after redirect observers called. michael@0: newHttpChannel->SetOriginalURI(mOriginalURI); michael@0: } michael@0: michael@0: RequestHeaderTuples emptyHeaders; michael@0: RequestHeaderTuples* headerTuples = &emptyHeaders; michael@0: michael@0: nsCOMPtr newHttpChannelChild = michael@0: do_QueryInterface(mRedirectChannelChild); michael@0: if (newHttpChannelChild && NS_SUCCEEDED(result)) { michael@0: newHttpChannelChild->AddCookiesToRequest(); michael@0: newHttpChannelChild->GetClientSetRequestHeaders(&headerTuples); michael@0: } michael@0: michael@0: /* If the redirect was canceled, bypass OMR and send an empty API michael@0: * redirect URI */ michael@0: SerializeURI(nullptr, redirectURI); michael@0: michael@0: if (NS_SUCCEEDED(result)) { michael@0: // Note: this is where we would notify "http-on-modify-response" observers. michael@0: // We have deliberately disabled this for child processes (see bug 806753) michael@0: // michael@0: // After we verify redirect, nsHttpChannel may hit the network: must give michael@0: // "http-on-modify-request" observers the chance to cancel before that. michael@0: //base->CallOnModifyRequestObservers(); michael@0: michael@0: nsCOMPtr newHttpChannelInternal = michael@0: do_QueryInterface(mRedirectChannelChild); michael@0: if (newHttpChannelInternal) { michael@0: nsCOMPtr apiRedirectURI; michael@0: nsresult rv = newHttpChannelInternal->GetApiRedirectToURI( michael@0: getter_AddRefs(apiRedirectURI)); michael@0: if (NS_SUCCEEDED(rv) && apiRedirectURI) { michael@0: /* If there was an API redirect of this channel, we need to send it michael@0: * up here, since it can't be sent via SendAsyncOpen. */ michael@0: SerializeURI(apiRedirectURI, redirectURI); michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (mIPCOpen) michael@0: SendRedirect2Verify(result, *headerTuples, redirectURI); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // HttpChannelChild::nsIRequest michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMETHODIMP michael@0: HttpChannelChild::Cancel(nsresult status) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: if (!mCanceled) { michael@0: // If this cancel occurs before nsHttpChannel has been set up, AsyncOpen michael@0: // is responsible for cleaning up. michael@0: mCanceled = true; michael@0: mStatus = status; michael@0: if (RemoteChannelExists()) michael@0: SendCancel(status); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpChannelChild::Suspend() michael@0: { michael@0: NS_ENSURE_TRUE(RemoteChannelExists(), NS_ERROR_NOT_AVAILABLE); michael@0: michael@0: // SendSuspend only once, when suspend goes from 0 to 1. michael@0: // Don't SendSuspend at all if we're diverting callbacks to the parent; michael@0: // suspend will be called at the correct time in the parent itself. michael@0: if (!mSuspendCount++ && !mDivertingToParent) { michael@0: SendSuspend(); michael@0: mSuspendSent = true; michael@0: } michael@0: mEventQ->Suspend(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpChannelChild::Resume() michael@0: { michael@0: NS_ENSURE_TRUE(RemoteChannelExists(), NS_ERROR_NOT_AVAILABLE); michael@0: NS_ENSURE_TRUE(mSuspendCount > 0, NS_ERROR_UNEXPECTED); michael@0: michael@0: nsresult rv = NS_OK; michael@0: michael@0: // SendResume only once, when suspend count drops to 0. michael@0: // Don't SendResume at all if we're diverting callbacks to the parent (unless michael@0: // suspend was sent earlier); otherwise, resume will be called at the correct michael@0: // time in the parent itself. michael@0: if (!--mSuspendCount && (!mDivertingToParent || mSuspendSent)) { michael@0: SendResume(); michael@0: if (mCallOnResume) { michael@0: AsyncCall(mCallOnResume); michael@0: mCallOnResume = nullptr; michael@0: } michael@0: } michael@0: mEventQ->Resume(); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // HttpChannelChild::nsIChannel michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMETHODIMP michael@0: HttpChannelChild::GetSecurityInfo(nsISupports **aSecurityInfo) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aSecurityInfo); michael@0: NS_IF_ADDREF(*aSecurityInfo = mSecurityInfo); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpChannelChild::AsyncOpen(nsIStreamListener *listener, nsISupports *aContext) michael@0: { michael@0: LOG(("HttpChannelChild::AsyncOpen [this=%p uri=%s]\n", this, mSpec.get())); michael@0: michael@0: if (mCanceled) michael@0: return mStatus; michael@0: michael@0: NS_ENSURE_TRUE(gNeckoChild != nullptr, NS_ERROR_FAILURE); michael@0: NS_ENSURE_ARG_POINTER(listener); michael@0: NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS); michael@0: NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED); michael@0: michael@0: mAsyncOpenTime = TimeStamp::Now(); michael@0: michael@0: // Port checked in parent, but duplicate here so we can return with error michael@0: // immediately michael@0: nsresult rv; michael@0: rv = NS_CheckPortSafety(mURI); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: const char *cookieHeader = mRequestHead.PeekHeader(nsHttp::Cookie); michael@0: if (cookieHeader) { michael@0: mUserSetCookieHeader = cookieHeader; michael@0: } michael@0: michael@0: AddCookiesToRequest(); michael@0: michael@0: // michael@0: // NOTE: From now on we must return NS_OK; all errors must be handled via michael@0: // OnStart/OnStopRequest michael@0: // michael@0: michael@0: // Note: this is where we would notify "http-on-modify-request" observers. michael@0: // We have deliberately disabled this for child processes (see bug 806753) michael@0: // michael@0: // notify "http-on-modify-request" observers michael@0: //CallOnModifyRequestObservers(); michael@0: michael@0: mIsPending = true; michael@0: mWasOpened = true; michael@0: mListener = listener; michael@0: mListenerContext = aContext; michael@0: michael@0: // add ourselves to the load group. michael@0: if (mLoadGroup) michael@0: mLoadGroup->AddRequest(this, nullptr); michael@0: michael@0: if (mCanceled) { michael@0: // We may have been canceled already, either by on-modify-request michael@0: // listeners or by load group observers; in that case, don't create IPDL michael@0: // connection. See nsHttpChannel::AsyncOpen(). michael@0: AsyncAbort(mStatus); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsCString appCacheClientId; michael@0: if (mInheritApplicationCache) { michael@0: // Pick up an application cache from the notification michael@0: // callbacks if available michael@0: nsCOMPtr appCacheContainer; michael@0: GetCallback(appCacheContainer); michael@0: michael@0: if (appCacheContainer) { michael@0: nsCOMPtr appCache; michael@0: rv = appCacheContainer->GetApplicationCache(getter_AddRefs(appCache)); michael@0: if (NS_SUCCEEDED(rv) && appCache) { michael@0: appCache->GetClientID(appCacheClientId); michael@0: } michael@0: } michael@0: } michael@0: michael@0: // michael@0: // Send request to the chrome process... michael@0: // michael@0: michael@0: mozilla::dom::TabChild* tabChild = nullptr; michael@0: nsCOMPtr iTabChild; michael@0: GetCallback(iTabChild); michael@0: if (iTabChild) { michael@0: tabChild = static_cast(iTabChild.get()); michael@0: } michael@0: if (MissingRequiredTabChild(tabChild, "http")) { michael@0: return NS_ERROR_ILLEGAL_VALUE; michael@0: } michael@0: michael@0: HttpChannelOpenArgs openArgs; michael@0: // No access to HttpChannelOpenArgs members, but they each have a michael@0: // function with the struct name that returns a ref. michael@0: SerializeURI(mURI, openArgs.uri()); michael@0: SerializeURI(mOriginalURI, openArgs.original()); michael@0: SerializeURI(mDocumentURI, openArgs.doc()); michael@0: SerializeURI(mReferrer, openArgs.referrer()); michael@0: SerializeURI(mAPIRedirectToURI, openArgs.apiRedirectTo()); michael@0: openArgs.loadFlags() = mLoadFlags; michael@0: openArgs.requestHeaders() = mClientSetRequestHeaders; michael@0: openArgs.requestMethod() = mRequestHead.Method(); michael@0: michael@0: nsTArray fds; michael@0: SerializeInputStream(mUploadStream, openArgs.uploadStream(), fds); michael@0: michael@0: PFileDescriptorSetChild* fdSet = nullptr; michael@0: if (!fds.IsEmpty()) { michael@0: MOZ_ASSERT(gNeckoChild->Manager()); michael@0: michael@0: fdSet = gNeckoChild->Manager()->SendPFileDescriptorSetConstructor(fds[0]); michael@0: for (uint32_t i = 1; i < fds.Length(); ++i) { michael@0: unused << fdSet->SendAddFileDescriptor(fds[i]); michael@0: } michael@0: } michael@0: michael@0: OptionalFileDescriptorSet optionalFDs; michael@0: if (fdSet) { michael@0: optionalFDs = fdSet; michael@0: } else { michael@0: optionalFDs = mozilla::void_t(); michael@0: } michael@0: michael@0: openArgs.fds() = optionalFDs; michael@0: michael@0: openArgs.uploadStreamHasHeaders() = mUploadStreamHasHeaders; michael@0: openArgs.priority() = mPriority; michael@0: openArgs.redirectionLimit() = mRedirectionLimit; michael@0: openArgs.allowPipelining() = mAllowPipelining; michael@0: openArgs.forceAllowThirdPartyCookie() = mForceAllowThirdPartyCookie; michael@0: openArgs.resumeAt() = mSendResumeAt; michael@0: openArgs.startPos() = mStartPos; michael@0: openArgs.entityID() = mEntityID; michael@0: openArgs.chooseApplicationCache() = mChooseApplicationCache; michael@0: openArgs.appCacheClientID() = appCacheClientId; michael@0: openArgs.allowSpdy() = mAllowSpdy; michael@0: michael@0: // The socket transport in the chrome process now holds a logical ref to us michael@0: // until OnStopRequest, or we do a redirect, or we hit an IPDL error. michael@0: AddIPDLReference(); michael@0: michael@0: gNeckoChild->SendPHttpChannelConstructor(this, tabChild, michael@0: IPC::SerializedLoadContext(this), michael@0: openArgs); michael@0: michael@0: if (fdSet) { michael@0: FileDescriptorSetChild* fdSetActor = michael@0: static_cast(fdSet); michael@0: michael@0: fdSetActor->ForgetFileDescriptors(fds); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // HttpChannelChild::nsIHttpChannel michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMETHODIMP michael@0: HttpChannelChild::SetRequestHeader(const nsACString& aHeader, michael@0: const nsACString& aValue, michael@0: bool aMerge) michael@0: { michael@0: nsresult rv = HttpBaseChannel::SetRequestHeader(aHeader, aValue, aMerge); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: RequestHeaderTuple* tuple = mClientSetRequestHeaders.AppendElement(); michael@0: if (!tuple) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: tuple->mHeader = aHeader; michael@0: tuple->mValue = aValue; michael@0: tuple->mMerge = aMerge; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpChannelChild::RedirectTo(nsIURI *newURI) michael@0: { michael@0: // disabled until/unless addons run in child or something else needs this michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // HttpChannelChild::nsIHttpChannelInternal michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMETHODIMP michael@0: HttpChannelChild::SetupFallbackChannel(const char *aFallbackKey) michael@0: { michael@0: DROP_DEAD(); michael@0: } michael@0: michael@0: // The next four _should_ be implemented, but we need to figure out how michael@0: // to transfer the data from the chrome process first. michael@0: michael@0: NS_IMETHODIMP michael@0: HttpChannelChild::GetRemoteAddress(nsACString & _result) michael@0: { michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpChannelChild::GetRemotePort(int32_t * _result) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(_result); michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpChannelChild::GetLocalAddress(nsACString & _result) michael@0: { michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpChannelChild::GetLocalPort(int32_t * _result) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(_result); michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // HttpChannelChild::nsICacheInfoChannel michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMETHODIMP michael@0: HttpChannelChild::GetCacheTokenExpirationTime(uint32_t *_retval) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(_retval); michael@0: if (!mCacheEntryAvailable) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: *_retval = mCacheExpirationTime; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpChannelChild::GetCacheTokenCachedCharset(nsACString &_retval) michael@0: { michael@0: if (!mCacheEntryAvailable) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: _retval = mCachedCharset; michael@0: return NS_OK; michael@0: } michael@0: NS_IMETHODIMP michael@0: HttpChannelChild::SetCacheTokenCachedCharset(const nsACString &aCharset) michael@0: { michael@0: if (!mCacheEntryAvailable || !RemoteChannelExists()) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: mCachedCharset = aCharset; michael@0: if (!SendSetCacheTokenCachedCharset(PromiseFlatCString(aCharset))) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpChannelChild::IsFromCache(bool *value) michael@0: { michael@0: if (!mIsPending) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: *value = mIsFromCache; michael@0: return NS_OK; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // HttpChannelChild::nsIResumableChannel michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMETHODIMP michael@0: HttpChannelChild::ResumeAt(uint64_t startPos, const nsACString& entityID) michael@0: { michael@0: ENSURE_CALLED_BEFORE_CONNECT(); michael@0: mStartPos = startPos; michael@0: mEntityID = entityID; michael@0: mSendResumeAt = true; michael@0: return NS_OK; michael@0: } michael@0: michael@0: // GetEntityID is shared in HttpBaseChannel michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // HttpChannelChild::nsISupportsPriority michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMETHODIMP michael@0: HttpChannelChild::SetPriority(int32_t aPriority) michael@0: { michael@0: int16_t newValue = clamped(aPriority, INT16_MIN, INT16_MAX); michael@0: if (mPriority == newValue) michael@0: return NS_OK; michael@0: mPriority = newValue; michael@0: if (RemoteChannelExists()) michael@0: SendSetPriority(mPriority); michael@0: return NS_OK; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // HttpChannelChild::nsIProxiedChannel michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMETHODIMP michael@0: HttpChannelChild::GetProxyInfo(nsIProxyInfo **aProxyInfo) michael@0: { michael@0: DROP_DEAD(); michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // HttpChannelChild::nsIApplicationCacheContainer michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMETHODIMP michael@0: HttpChannelChild::GetApplicationCache(nsIApplicationCache **aApplicationCache) michael@0: { michael@0: NS_IF_ADDREF(*aApplicationCache = mApplicationCache); michael@0: return NS_OK; michael@0: } michael@0: NS_IMETHODIMP michael@0: HttpChannelChild::SetApplicationCache(nsIApplicationCache *aApplicationCache) michael@0: { michael@0: NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED); michael@0: michael@0: mApplicationCache = aApplicationCache; michael@0: return NS_OK; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // HttpChannelChild::nsIApplicationCacheChannel michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMETHODIMP michael@0: HttpChannelChild::GetApplicationCacheForWrite(nsIApplicationCache **aApplicationCache) michael@0: { michael@0: *aApplicationCache = nullptr; michael@0: return NS_OK; michael@0: } michael@0: NS_IMETHODIMP michael@0: HttpChannelChild::SetApplicationCacheForWrite(nsIApplicationCache *aApplicationCache) michael@0: { michael@0: NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED); michael@0: michael@0: // Child channels are not intended to be used for cache writes michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpChannelChild::GetLoadedFromApplicationCache(bool *aLoadedFromApplicationCache) michael@0: { michael@0: *aLoadedFromApplicationCache = mLoadedFromApplicationCache; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpChannelChild::GetInheritApplicationCache(bool *aInherit) michael@0: { michael@0: *aInherit = mInheritApplicationCache; michael@0: return NS_OK; michael@0: } michael@0: NS_IMETHODIMP michael@0: HttpChannelChild::SetInheritApplicationCache(bool aInherit) michael@0: { michael@0: mInheritApplicationCache = aInherit; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpChannelChild::GetChooseApplicationCache(bool *aChoose) michael@0: { michael@0: *aChoose = mChooseApplicationCache; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpChannelChild::SetChooseApplicationCache(bool aChoose) michael@0: { michael@0: mChooseApplicationCache = aChoose; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpChannelChild::MarkOfflineCacheEntryAsForeign() michael@0: { michael@0: SendMarkOfflineCacheEntryAsForeign(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // HttpChannelChild::nsIAssociatedContentSecurity michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: bool michael@0: HttpChannelChild::GetAssociatedContentSecurity( michael@0: nsIAssociatedContentSecurity** _result) michael@0: { michael@0: if (!mSecurityInfo) michael@0: return false; michael@0: michael@0: nsCOMPtr assoc = michael@0: do_QueryInterface(mSecurityInfo); michael@0: if (!assoc) michael@0: return false; michael@0: michael@0: if (_result) michael@0: assoc.forget(_result); michael@0: return true; michael@0: } michael@0: michael@0: /* attribute unsigned long countSubRequestsBrokenSecurity; */ michael@0: NS_IMETHODIMP michael@0: HttpChannelChild::GetCountSubRequestsBrokenSecurity( michael@0: int32_t *aSubRequestsBrokenSecurity) michael@0: { michael@0: nsCOMPtr assoc; michael@0: if (!GetAssociatedContentSecurity(getter_AddRefs(assoc))) michael@0: return NS_OK; michael@0: michael@0: return assoc->GetCountSubRequestsBrokenSecurity(aSubRequestsBrokenSecurity); michael@0: } michael@0: NS_IMETHODIMP michael@0: HttpChannelChild::SetCountSubRequestsBrokenSecurity( michael@0: int32_t aSubRequestsBrokenSecurity) michael@0: { michael@0: nsCOMPtr assoc; michael@0: if (!GetAssociatedContentSecurity(getter_AddRefs(assoc))) michael@0: return NS_OK; michael@0: michael@0: return assoc->SetCountSubRequestsBrokenSecurity(aSubRequestsBrokenSecurity); michael@0: } michael@0: michael@0: /* attribute unsigned long countSubRequestsNoSecurity; */ michael@0: NS_IMETHODIMP michael@0: HttpChannelChild::GetCountSubRequestsNoSecurity(int32_t *aSubRequestsNoSecurity) michael@0: { michael@0: nsCOMPtr assoc; michael@0: if (!GetAssociatedContentSecurity(getter_AddRefs(assoc))) michael@0: return NS_OK; michael@0: michael@0: return assoc->GetCountSubRequestsNoSecurity(aSubRequestsNoSecurity); michael@0: } michael@0: NS_IMETHODIMP michael@0: HttpChannelChild::SetCountSubRequestsNoSecurity(int32_t aSubRequestsNoSecurity) michael@0: { michael@0: nsCOMPtr assoc; michael@0: if (!GetAssociatedContentSecurity(getter_AddRefs(assoc))) michael@0: return NS_OK; michael@0: michael@0: return assoc->SetCountSubRequestsNoSecurity(aSubRequestsNoSecurity); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpChannelChild::Flush() michael@0: { michael@0: nsCOMPtr assoc; michael@0: if (!GetAssociatedContentSecurity(getter_AddRefs(assoc))) michael@0: return NS_OK; michael@0: michael@0: nsresult rv; michael@0: int32_t broken, no; michael@0: michael@0: rv = assoc->GetCountSubRequestsBrokenSecurity(&broken); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: rv = assoc->GetCountSubRequestsNoSecurity(&no); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (mIPCOpen) michael@0: SendUpdateAssociatedContentSecurity(broken, no); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // HttpChannelChild::nsIHttpChannelChild michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMETHODIMP HttpChannelChild::AddCookiesToRequest() michael@0: { michael@0: HttpBaseChannel::AddCookiesToRequest(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP HttpChannelChild::GetClientSetRequestHeaders(RequestHeaderTuples **aRequestHeaders) michael@0: { michael@0: *aRequestHeaders = &mClientSetRequestHeaders; michael@0: return NS_OK; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // HttpChannelChild::nsIDivertableChannel michael@0: //----------------------------------------------------------------------------- michael@0: NS_IMETHODIMP michael@0: HttpChannelChild::DivertToParent(ChannelDiverterChild **aChild) michael@0: { michael@0: MOZ_RELEASE_ASSERT(aChild); michael@0: MOZ_RELEASE_ASSERT(gNeckoChild); michael@0: MOZ_RELEASE_ASSERT(!mDivertingToParent); michael@0: michael@0: // We must fail DivertToParent() if there's no parent end of the channel (and michael@0: // won't be!) due to early failure. michael@0: if (NS_FAILED(mStatus) && !RemoteChannelExists()) { michael@0: return mStatus; michael@0: } michael@0: michael@0: nsresult rv = Suspend(); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) { michael@0: return rv; michael@0: } michael@0: michael@0: // Once this is set, it should not be unset before the child is taken down. michael@0: mDivertingToParent = true; michael@0: michael@0: PChannelDiverterChild* diverter = michael@0: gNeckoChild->SendPChannelDiverterConstructor(this); michael@0: MOZ_RELEASE_ASSERT(diverter); michael@0: michael@0: *aChild = static_cast(diverter); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: }} // mozilla::net