Created
September 19, 2013 19:44
-
-
Save jdm/6628822 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # HG changeset patch | |
| # Parent 59beb1868522e2681b8721e29613d0521fe7e6c2 | |
| # User Josh Matthews <[email protected]> | |
| diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp | |
| --- a/docshell/base/nsDocShell.cpp | |
| +++ b/docshell/base/nsDocShell.cpp | |
| @@ -16,16 +16,17 @@ | |
| #include "mozilla/dom/TabChild.h" | |
| #include "mozilla/Preferences.h" | |
| #include "mozilla/Services.h" | |
| #include "mozilla/StartupTimeline.h" | |
| #include "mozilla/Telemetry.h" | |
| #include "mozilla/unused.h" | |
| #include "mozilla/Util.h" | |
| #include "mozilla/VisualEventTracer.h" | |
| +#include "mozilla/net/AlternateSourceChannel.h" | |
| #ifdef MOZ_LOGGING | |
| // so we can get logging even in release builds (but only for some things) | |
| #define FORCE_PR_LOG 1 | |
| #endif | |
| #include "nsIContent.h" | |
| #include "nsIDocument.h" | |
| @@ -191,16 +192,17 @@ static NS_DEFINE_CID(kAppShellCID, NS_AP | |
| #if defined(DEBUG_bryner) || defined(DEBUG_chb) | |
| //#define DEBUG_DOCSHELL_FOCUS | |
| #define DEBUG_PAGE_CACHE | |
| #endif | |
| using namespace mozilla; | |
| using namespace mozilla::dom; | |
| +using namespace mozilla::net; | |
| // Number of documents currently loading | |
| static int32_t gNumberOfDocumentsLoading = 0; | |
| // Global count of existing docshells. | |
| static int32_t gDocShellCount = 0; | |
| // Global count of docshells with the private attribute set | |
| @@ -9747,16 +9749,22 @@ nsDocShell::DoURILoad(nsIURI * aURI, | |
| } | |
| } | |
| nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(channel)); | |
| if (timedChannel) { | |
| timedChannel->SetTimingEnabled(true); | |
| } | |
| + //TODO(jdm): Check document for presence of navigation controller | |
| + if (false) { | |
| + channel = new AlternateSourceChannel(channel); | |
| + //TODO(jdm): inform navigation controller about channel | |
| + } | |
| + | |
| rv = DoChannelLoad(channel, uriLoader, aBypassClassifier); | |
| // | |
| // If the channel load failed, we failed and nsIWebProgress just ain't | |
| // gonna happen. | |
| // | |
| if (NS_SUCCEEDED(rv)) { | |
| if (aDocShell) { | |
| diff --git a/netwerk/base/public/moz.build b/netwerk/base/public/moz.build | |
| --- a/netwerk/base/public/moz.build | |
| +++ b/netwerk/base/public/moz.build | |
| @@ -1,16 +1,17 @@ | |
| # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- | |
| # vim: set filetype=python: | |
| # This Source Code Form is subject to the terms of the Mozilla Public | |
| # License, v. 2.0. If a copy of the MPL was not distributed with this | |
| # file, You can obtain one at http://mozilla.org/MPL/2.0/. | |
| XPIDL_SOURCES += [ | |
| 'mozIThirdPartyUtil.idl', | |
| + 'nsIAlternateSourceChannel.idl', | |
| 'nsIApplicationCache.idl', | |
| 'nsIApplicationCacheChannel.idl', | |
| 'nsIApplicationCacheContainer.idl', | |
| 'nsIApplicationCacheService.idl', | |
| 'nsIArrayBufferInputStream.idl', | |
| 'nsIAsyncStreamCopier.idl', | |
| 'nsIAsyncVerifyRedirectCallback.idl', | |
| 'nsIAuthInformation.idl', | |
| diff --git a/netwerk/base/public/nsIAlternateSourceChannel.idl b/netwerk/base/public/nsIAlternateSourceChannel.idl | |
| new file mode 100644 | |
| --- /dev/null | |
| +++ b/netwerk/base/public/nsIAlternateSourceChannel.idl | |
| @@ -0,0 +1,16 @@ | |
| +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ | |
| +/* This Source Code Form is subject to the terms of the Mozilla Public | |
| + * License, v. 2.0. If a copy of the MPL was not distributed with this | |
| + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | |
| + | |
| +#include "nsISupports.idl" | |
| + | |
| +interface nsIChannel; | |
| +interface nsIInputStream; | |
| + | |
| +[scriptable, uuid(9bd368b6-3893-4fd7-b7e7-40a9cd45e684)] | |
| +interface nsIAlternateSourceChannel : nsISupports { | |
| + void forwardToOriginalChannel(); | |
| + void initiateAlternateResponse(in nsIInputStream body); | |
| + readonly attribute nsIChannel wrappedChannel; | |
| +}; | |
| diff --git a/netwerk/base/public/nsINetUtil.idl b/netwerk/base/public/nsINetUtil.idl | |
| --- a/netwerk/base/public/nsINetUtil.idl | |
| +++ b/netwerk/base/public/nsINetUtil.idl | |
| @@ -1,15 +1,16 @@ | |
| /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ | |
| /* This Source Code Form is subject to the terms of the Mozilla Public | |
| * License, v. 2.0. If a copy of the MPL was not distributed with this | |
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | |
| #include "nsISupports.idl" | |
| +interface nsIChannel; | |
| interface nsIURI; | |
| interface nsIPrefBranch; | |
| /** | |
| * nsINetUtil provides various network-related utility methods. | |
| */ | |
| [scriptable, uuid(ca68c485-9db3-4c12-82a6-4fab7948e2d5)] | |
| interface nsINetUtil : nsISupports | |
| @@ -187,9 +188,11 @@ interface nsINetUtil : nsISupports | |
| * @return whether a charset parameter was found. This can be false even in | |
| * cases when parseContentType would claim to have a charset, if the type | |
| * that won out does not have a charset parameter specified. | |
| */ | |
| boolean extractCharsetFromContentType(in AUTF8String aTypeHeader, | |
| out AUTF8String aCharset, | |
| out long aCharsetStart, | |
| out long aCharsetEnd); | |
| + | |
| + nsIChannel createAlternateSourceChannel(in nsIChannel aChannel); | |
| }; | |
| diff --git a/netwerk/base/src/AlternateSourceChannel.cpp b/netwerk/base/src/AlternateSourceChannel.cpp | |
| new file mode 100644 | |
| --- /dev/null | |
| +++ b/netwerk/base/src/AlternateSourceChannel.cpp | |
| @@ -0,0 +1,410 @@ | |
| +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ | |
| +/* This Source Code Form is subject to the terms of the Mozilla Public | |
| + * License, v. 2.0. If a copy of the MPL was not distributed with this | |
| + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | |
| + | |
| +#include "nsIHttpChannelInternal.h" | |
| +#include "nsInputStreamPump.h" | |
| +#include "AlternateSourceChannel.h" | |
| + | |
| +/*NS_IMPL_ADDREF(AlternateSourceChannel) | |
| +NS_IMPL_RELEASE(AlternateSourceChannel) | |
| + | |
| +NS_INTERFACE_MAP_BEGIN(AlternateSourceChannel) | |
| + NS_INTERFACE_MAP_ENTRY(nsIAlternateSourceChannel) | |
| + NS_INTERFACE_MAP_ENTRY(nsIChannel) | |
| + NS_INTERFACE_MAP_ENTRY(nsISupports) | |
| + NS_INTERFACE_MAP_END*/ | |
| + | |
| +//XXXjdm make QI forward to mWrappedChannel, conditionally after AsyncOpen? | |
| +// Problem - can't forward nsIChannel, and breaks QI contract in addition | |
| + | |
| +class nsIInterfaceRequestor; | |
| + | |
| +namespace mozilla { | |
| +namespace net { | |
| + | |
| +NS_IMPL_ISUPPORTS3(AlternateSourceChannel, | |
| + nsIChannel, | |
| + nsIAlternateSourceChannel, | |
| + nsIStreamListener) | |
| + | |
| +AlternateSourceChannel::AlternateSourceChannel(nsIChannel* aWrappedChannel) | |
| +: mWrappedChannel(aWrappedChannel) | |
| +, mStatus(NS_OK) | |
| +, mSuspendCount(0) | |
| +, mContentLength(-1) | |
| +, mForwardToWrapped(false) | |
| +, mCanceled(false) | |
| +, mPending(false) | |
| +, mWasOpened(false) | |
| +{ | |
| +} | |
| + | |
| +AlternateSourceChannel::~AlternateSourceChannel() | |
| +{ | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::GetWrappedChannel(nsIChannel** aChannel) | |
| +{ | |
| + NS_ADDREF(*aChannel = mWrappedChannel); | |
| + return NS_OK; | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::ForwardToOriginalChannel() | |
| +{ | |
| + if (mForwardToWrapped) { | |
| + return NS_OK; | |
| + } | |
| + | |
| + mForwardToWrapped = true; | |
| + nsCOMPtr<nsIHttpChannelInternal> httpChan; | |
| + if (httpChan) { | |
| + httpChan->FinishAsyncOpen(); | |
| + } else { | |
| + mWrappedChannel->AsyncOpen(mListener, mContext); | |
| + mListener = nullptr; | |
| + mContext = nullptr; | |
| + } | |
| + return NS_OK; | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::InitiateAlternateResponse(nsIInputStream* aBody) | |
| +{ | |
| + mBody = aBody; | |
| + nsresult rv = nsInputStreamPump::Create(getter_AddRefs(mPump), mBody); | |
| + NS_ENSURE_SUCCESS(rv, rv); | |
| + rv = mPump->AsyncRead(this, nullptr); | |
| + NS_ENSURE_SUCCESS(rv, rv); | |
| + return NS_OK; | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::GetOriginalURI(nsIURI** aOriginalURI) | |
| +{ | |
| + return mWrappedChannel->GetOriginalURI(aOriginalURI); | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::SetOriginalURI(nsIURI* aOriginalURI) | |
| +{ | |
| + return mWrappedChannel->SetOriginalURI(aOriginalURI); | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::GetURI(nsIURI** aURI) | |
| +{ | |
| + return mWrappedChannel->GetURI(aURI); | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::GetOwner(nsISupports** aOwner) | |
| +{ | |
| + return mWrappedChannel->GetOwner(aOwner); | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::SetOwner(nsISupports* aOwner) | |
| +{ | |
| + return mWrappedChannel->SetOwner(aOwner); | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::GetNotificationCallbacks(nsIInterfaceRequestor** aCallbacks) | |
| +{ | |
| + return mWrappedChannel->GetNotificationCallbacks(aCallbacks); | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aCallbacks) | |
| +{ | |
| + return mWrappedChannel->SetNotificationCallbacks(aCallbacks); | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::GetSecurityInfo(nsISupports** aSecurityInfo) | |
| +{ | |
| + return mWrappedChannel->GetSecurityInfo(aSecurityInfo); | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::GetContentType(nsACString& aContentType) | |
| +{ | |
| + if (mForwardToWrapped) { | |
| + return mWrappedChannel->GetContentType(aContentType); | |
| + } | |
| + aContentType = mContentType; | |
| + return NS_OK; | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::SetContentType(const nsACString& aContentType) | |
| +{ | |
| + if (mForwardToWrapped) { | |
| + return mWrappedChannel->SetContentType(aContentType); | |
| + } | |
| + mContentType = aContentType; | |
| + return NS_OK; | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::GetContentCharset(nsACString& aContentCharset) | |
| +{ | |
| + if (mForwardToWrapped) { | |
| + return mWrappedChannel->GetContentCharset(aContentCharset); | |
| + } | |
| + aContentCharset = mContentCharset; | |
| + return NS_OK; | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::SetContentCharset(const nsACString& aContentCharset) | |
| +{ | |
| + if (mForwardToWrapped) { | |
| + return mWrappedChannel->SetContentCharset(aContentCharset); | |
| + } | |
| + mContentCharset = aContentCharset; | |
| + return NS_OK; | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::GetContentLength(int64_t* aContentLength) | |
| +{ | |
| + if (mForwardToWrapped) { | |
| + return mWrappedChannel->GetContentLength(aContentLength); | |
| + } | |
| + *aContentLength = mContentLength; | |
| + return NS_OK; | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::SetContentLength(int64_t aContentLength) | |
| +{ | |
| + if (mForwardToWrapped) { | |
| + return mWrappedChannel->SetContentLength(aContentLength); | |
| + } | |
| + mContentLength = aContentLength; | |
| + return NS_OK; | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::Open(nsIInputStream** aStream) | |
| +{ | |
| + return NS_ERROR_NOT_AVAILABLE; | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::AsyncOpen(nsIStreamListener* aListener, nsISupports* aContext) | |
| +{ | |
| + NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_FAILURE); | |
| + | |
| + if (mForwardToWrapped) { | |
| + return mWrappedChannel->AsyncOpen(aListener, aContext); | |
| + } | |
| + mListener = aListener; | |
| + mContext = aContext; | |
| + mPending = true; | |
| + mWasOpened = true; | |
| + | |
| + nsCOMPtr<nsIHttpChannelInternal> httpChan = do_QueryInterface(mWrappedChannel); | |
| + if (httpChan) { | |
| + httpChan->AsyncOpenSetup(aListener, aContext); | |
| + } | |
| + return NS_OK; | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::GetContentDisposition(uint32_t* aContentDisposition) | |
| +{ | |
| + if (mForwardToWrapped) { | |
| + return mWrappedChannel->GetContentDisposition(aContentDisposition); | |
| + } | |
| + return NS_ERROR_NOT_AVAILABLE; //XXXjdm | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::SetContentDisposition(uint32_t aContentDisposition) | |
| +{ | |
| + if (mForwardToWrapped) { | |
| + return mWrappedChannel->SetContentDisposition(aContentDisposition); | |
| + } | |
| + return NS_ERROR_NOT_AVAILABLE; //XXXjdm | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::GetContentDispositionFilename(nsAString& aFilename) | |
| +{ | |
| + if (mForwardToWrapped) { | |
| + return mWrappedChannel->GetContentDispositionFilename(aFilename); | |
| + } | |
| + return NS_ERROR_NOT_AVAILABLE; //XXXjdm | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::SetContentDispositionFilename(const nsAString& aFilename) | |
| +{ | |
| + if (mForwardToWrapped) { | |
| + return mWrappedChannel->SetContentDispositionFilename(aFilename); | |
| + } | |
| + return NS_ERROR_NOT_AVAILABLE; //XXXjdm | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::GetContentDispositionHeader(nsACString& aHeader) | |
| +{ | |
| + if (mForwardToWrapped) { | |
| + return mWrappedChannel->GetContentDispositionHeader(aHeader); | |
| + } | |
| + return NS_ERROR_NOT_AVAILABLE; //XXXjdm | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::GetName(nsACString& aName) | |
| +{ | |
| + return mWrappedChannel->GetName(aName); | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::IsPending(bool* aPending) | |
| +{ | |
| + if (mForwardToWrapped) { | |
| + return mWrappedChannel->IsPending(aPending); | |
| + } | |
| + *aPending = mPending; | |
| + return NS_OK; | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::GetStatus(nsresult* aStatus) | |
| +{ | |
| + if (mForwardToWrapped) { | |
| + return mWrappedChannel->GetStatus(aStatus); | |
| + } | |
| + *aStatus = mStatus; | |
| + return NS_OK; | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::Cancel(nsresult aStatus) | |
| +{ | |
| + if (mForwardToWrapped) { | |
| + return mWrappedChannel->Cancel(aStatus); | |
| + } | |
| + if (mPump) { | |
| + mPump->Cancel(aStatus); | |
| + } | |
| + if (!mCanceled) { | |
| + mCanceled = true; | |
| + mStatus = aStatus; | |
| + } | |
| + return NS_OK; | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::Suspend() | |
| +{ | |
| + if (mForwardToWrapped) { | |
| + return mWrappedChannel->Suspend(); | |
| + } | |
| + if (mPump) { | |
| + mPump->Suspend(); | |
| + } | |
| + mSuspendCount++; | |
| + return NS_OK; | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::Resume() | |
| +{ | |
| + if (mForwardToWrapped) { | |
| + return mWrappedChannel->Resume(); | |
| + } | |
| + NS_ENSURE_TRUE(mSuspendCount > 0, NS_ERROR_FAILURE); | |
| + if (mSuspendCount > 0) { | |
| + mSuspendCount--; | |
| + } | |
| + if (mPump) { | |
| + mPump->Resume(); | |
| + } | |
| + return NS_OK; | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::GetLoadGroup(nsILoadGroup** aLoadGroup) | |
| +{ | |
| + return mWrappedChannel->GetLoadGroup(aLoadGroup); | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::SetLoadGroup(nsILoadGroup* aLoadGroup) | |
| +{ | |
| + return mWrappedChannel->SetLoadGroup(aLoadGroup); | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::GetLoadFlags(nsLoadFlags* aLoadFlags) | |
| +{ | |
| + return mWrappedChannel->GetLoadFlags(aLoadFlags); | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::SetLoadFlags(nsLoadFlags aLoadFlags) | |
| +{ | |
| + return mWrappedChannel->SetLoadFlags(aLoadFlags); | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext) | |
| +{ | |
| + MOZ_ASSERT(!mForwardToWrapped); | |
| + | |
| + nsresult rv = NS_OK; | |
| + if (mListener) { | |
| + rv = mListener->OnStartRequest(this, mContext); | |
| + } | |
| + return rv; | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::OnDataAvailable(nsIRequest* aRequest, nsISupports* aContext, | |
| + nsIInputStream *aInputStream, uint64_t aOffset, | |
| + uint32_t aCount) | |
| +{ | |
| + MOZ_ASSERT(!mCanceled); | |
| + MOZ_ASSERT(!mForwardToWrapped); | |
| + nsresult rv = NS_OK; | |
| + if (mListener) { | |
| + rv = mListener->OnDataAvailable(this, mContext, aInputStream, aOffset, aCount); | |
| + } | |
| + return rv; | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::OnStopRequest(nsIRequest* aRequest, | |
| + nsISupports* aContext, | |
| + nsresult aStatusCode) | |
| +{ | |
| + MOZ_ASSERT(!mForwardToWrapped); | |
| + mStatus = aStatusCode; | |
| + if (!mCanceled && NS_FAILED(mStatus)) { | |
| + mCanceled = true; | |
| + } | |
| + mPending = false; | |
| + | |
| + nsresult rv = NS_OK; | |
| + if (mListener) { | |
| + rv = mListener->OnStopRequest(this, mContext, aStatusCode); | |
| + } | |
| + | |
| + mListener = nullptr; | |
| + mContext = nullptr; | |
| + mBody = nullptr; | |
| + mPump = nullptr; | |
| + return rv; | |
| +} | |
| + | |
| +} // namespace net | |
| +} // namespace mozilla | |
| diff --git a/netwerk/base/src/AlternateSourceChannel.h b/netwerk/base/src/AlternateSourceChannel.h | |
| new file mode 100644 | |
| --- /dev/null | |
| +++ b/netwerk/base/src/AlternateSourceChannel.h | |
| @@ -0,0 +1,55 @@ | |
| +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ | |
| +/* This Source Code Form is subject to the terms of the Mozilla Public | |
| + * License, v. 2.0. If a copy of the MPL was not distributed with this | |
| + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | |
| + | |
| +#ifndef mozilla_net_AlternateSourceChannel_h | |
| +#define mozilla_net_AlternateSourceChannel_h | |
| + | |
| +#include "nsIAlternateSourceChannel.h" | |
| +#include "nsIChannel.h" | |
| +#include "nsIStreamListener.h" | |
| +#include "nsCOMPtr.h" | |
| +#include "nsAutoPtr.h" | |
| +#include "nsString.h" | |
| + | |
| +class nsInputStreamPump; | |
| + | |
| +namespace mozilla { | |
| +namespace net { | |
| + | |
| +class AlternateSourceChannel : public nsIChannel | |
| + , public nsIAlternateSourceChannel | |
| + , public nsIStreamListener { | |
| +public: | |
| + AlternateSourceChannel(nsIChannel* aWrappedChannel); | |
| + virtual ~AlternateSourceChannel(); | |
| + | |
| + NS_DECL_ISUPPORTS | |
| + NS_DECL_NSIALTERNATESOURCECHANNEL | |
| + NS_DECL_NSICHANNEL | |
| + NS_DECL_NSIREQUEST | |
| + NS_DECL_NSISTREAMLISTENER | |
| + NS_DECL_NSIREQUESTOBSERVER | |
| + | |
| +private: | |
| + nsCOMPtr<nsIChannel> mWrappedChannel; | |
| + nsCOMPtr<nsIStreamListener> mListener; | |
| + nsCOMPtr<nsISupports> mContext; | |
| + nsCOMPtr<nsIInputStream> mBody; | |
| + nsRefPtr<nsInputStreamPump> mPump; | |
| + nsCString mContentType; | |
| + nsCString mContentCharset; | |
| + nsresult mStatus; | |
| + uint32_t mSuspendCount; | |
| + int64_t mContentLength; | |
| + bool mForwardToWrapped; | |
| + bool mCanceled; | |
| + bool mPending; | |
| + bool mWasOpened; | |
| +}; | |
| + | |
| +} // namespace net | |
| +} // namespace mozilla | |
| + | |
| +#endif // mozilla_net_AlternateSourceChannel_h | |
| diff --git a/netwerk/base/src/moz.build b/netwerk/base/src/moz.build | |
| --- a/netwerk/base/src/moz.build | |
| +++ b/netwerk/base/src/moz.build | |
| @@ -9,21 +9,23 @@ MODULE = 'necko' | |
| EXPORTS += [ | |
| 'nsFileStreams.h', | |
| 'nsMIMEInputStream.h', | |
| 'nsTemporaryFileInputStream.h', | |
| 'nsURLHelper.h', | |
| ] | |
| EXPORTS.mozilla.net += [ | |
| + 'AlternateSourceChannel.h', | |
| 'Dashboard.h', | |
| 'DashboardTypes.h', | |
| ] | |
| CPP_SOURCES += [ | |
| + 'AlternateSourceChannel.cpp', | |
| 'ArrayBufferInputStream.cpp', | |
| 'BackgroundFileSaver.cpp', | |
| 'Dashboard.cpp', | |
| 'EventTokenBucket.cpp', | |
| 'NetworkActivityMonitor.cpp', | |
| 'ProxyAutoConfig.cpp', | |
| 'RedirectChannelRegistrar.cpp', | |
| 'Tickler.cpp', | |
| diff --git a/netwerk/base/src/nsIOService.cpp b/netwerk/base/src/nsIOService.cpp | |
| --- a/netwerk/base/src/nsIOService.cpp | |
| +++ b/netwerk/base/src/nsIOService.cpp | |
| @@ -39,22 +39,24 @@ | |
| #include "nsIPermissionManager.h" | |
| #include "nsTArray.h" | |
| #include "nsIConsoleService.h" | |
| #include "nsIUploadChannel2.h" | |
| #include "nsXULAppAPI.h" | |
| #include "nsIProxiedChannel.h" | |
| #include "nsIProtocolProxyCallback.h" | |
| #include "nsICancelable.h" | |
| +#include "AlternateSourceChannel.h" | |
| #if defined(XP_WIN) | |
| #include "nsNativeConnectionHelper.h" | |
| #endif | |
| using namespace mozilla; | |
| +using mozilla::net::AlternateSourceChannel; | |
| #define PORT_PREF_PREFIX "network.security.ports." | |
| #define PORT_PREF(x) PORT_PREF_PREFIX x | |
| #define AUTODIAL_PREF "network.autodial-helper.enabled" | |
| #define MANAGE_OFFLINE_STATUS_PREF "network.manage-offline-status" | |
| // Nb: these have been misnomers since bug 715770 removed the buffer cache. | |
| // "network.segment.count" and "network.segment.size" would be better names, | |
| @@ -1173,16 +1175,25 @@ nsIOService::ExtractCharsetFromContentTy | |
| net_ParseContentType(aTypeHeader, ignored, aCharset, aHadCharset, | |
| aCharsetStart, aCharsetEnd); | |
| if (*aHadCharset && *aCharsetStart == *aCharsetEnd) { | |
| *aHadCharset = false; | |
| } | |
| return NS_OK; | |
| } | |
| +NS_IMETHODIMP | |
| +nsIOService::CreateAlternateSourceChannel(nsIChannel* aChannel, | |
| + nsIChannel** aWrappedChannel) | |
| +{ | |
| + nsCOMPtr<nsIChannel> wrapper = new AlternateSourceChannel(aChannel); | |
| + wrapper.forget(aWrappedChannel); | |
| + return NS_OK; | |
| +} | |
| + | |
| // nsISpeculativeConnect | |
| class IOServiceProxyCallback MOZ_FINAL : public nsIProtocolProxyCallback | |
| { | |
| public: | |
| NS_DECL_ISUPPORTS | |
| NS_DECL_NSIPROTOCOLPROXYCALLBACK | |
| IOServiceProxyCallback(nsIInterfaceRequestor *aCallbacks, | |
| diff --git a/netwerk/protocol/http/HttpChannelChild.cpp b/netwerk/protocol/http/HttpChannelChild.cpp | |
| --- a/netwerk/protocol/http/HttpChannelChild.cpp | |
| +++ b/netwerk/protocol/http/HttpChannelChild.cpp | |
| @@ -969,16 +969,28 @@ NS_IMETHODIMP | |
| HttpChannelChild::GetSecurityInfo(nsISupports **aSecurityInfo) | |
| { | |
| NS_ENSURE_ARG_POINTER(aSecurityInfo); | |
| NS_IF_ADDREF(*aSecurityInfo = mSecurityInfo); | |
| return NS_OK; | |
| } | |
| NS_IMETHODIMP | |
| +HttpChannelChild::AsyncOpenSetup(nsIStreamListener *listener, nsISupports *aContext) | |
| +{ | |
| + return NS_ERROR_FAILURE; | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +HttpChannelChild::FinishAsyncOpen() | |
| +{ | |
| + return NS_ERROR_FAILURE; | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| HttpChannelChild::AsyncOpen(nsIStreamListener *listener, nsISupports *aContext) | |
| { | |
| LOG(("HttpChannelChild::AsyncOpen [this=%p uri=%s]\n", this, mSpec.get())); | |
| if (mCanceled) | |
| return mStatus; | |
| NS_ENSURE_TRUE(gNeckoChild != nullptr, NS_ERROR_FAILURE); | |
| diff --git a/netwerk/protocol/http/HttpChannelChild.h b/netwerk/protocol/http/HttpChannelChild.h | |
| --- a/netwerk/protocol/http/HttpChannelChild.h | |
| +++ b/netwerk/protocol/http/HttpChannelChild.h | |
| @@ -72,16 +72,18 @@ public: | |
| bool aMerge); | |
| NS_IMETHOD RedirectTo(nsIURI *newURI); | |
| // nsIHttpChannelInternal | |
| NS_IMETHOD SetupFallbackChannel(const char *aFallbackKey); | |
| NS_IMETHOD GetLocalAddress(nsACString& addr); | |
| NS_IMETHOD GetLocalPort(int32_t* port); | |
| NS_IMETHOD GetRemoteAddress(nsACString& addr); | |
| NS_IMETHOD GetRemotePort(int32_t* port); | |
| + NS_IMETHOD AsyncOpenSetup(nsIStreamListener *listener, nsISupports *aContext); | |
| + NS_IMETHOD FinishAsyncOpen(); | |
| // nsISupportsPriority | |
| NS_IMETHOD SetPriority(int32_t value); | |
| // nsIResumableChannel | |
| NS_IMETHOD ResumeAt(uint64_t startPos, const nsACString& entityID); | |
| // IPDL holds a reference while the PHttpChannel protocol is live (starting at | |
| // AsyncOpen, and ending at either OnStopRequest or any IPDL error, either of | |
| // which call NeckoChild::DeallocPHttpChannelChild()). | |
| diff --git a/netwerk/protocol/http/nsHttpChannel.cpp b/netwerk/protocol/http/nsHttpChannel.cpp | |
| --- a/netwerk/protocol/http/nsHttpChannel.cpp | |
| +++ b/netwerk/protocol/http/nsHttpChannel.cpp | |
| @@ -4502,16 +4502,39 @@ nsHttpChannel::GetSecurityInfo(nsISuppor | |
| NS_IMETHODIMP | |
| nsHttpChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *context) | |
| { | |
| MOZ_EVENT_TRACER_WAIT(this, "net::http::channel"); | |
| LOG(("nsHttpChannel::AsyncOpen [this=%p]\n", this)); | |
| + nsresult rv = AsyncOpenSetup(listener, context); | |
| + if (NS_FAILED(rv)) { | |
| + return rv; | |
| + } | |
| + | |
| + rv = FinishAsyncOpen(); | |
| + return rv; | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +nsHttpChannel::FinishAsyncOpen() | |
| +{ | |
| + nsresult rv = BeginConnect(); | |
| + if (NS_FAILED(rv)) | |
| + ReleaseListeners(); | |
| + return rv; | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +nsHttpChannel::AsyncOpenSetup(nsIStreamListener *listener, nsISupports *context) | |
| +{ | |
| + LOG(("nsHttpChannel::AsyncOpenSetup [this=%p]\n", this)); | |
| + | |
| NS_ENSURE_ARG_POINTER(listener); | |
| NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS); | |
| NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED); | |
| nsresult rv; | |
| rv = NS_CheckPortSafety(mURI); | |
| if (NS_FAILED(rv)) { | |
| @@ -4549,35 +4572,30 @@ nsHttpChannel::AsyncOpen(nsIStreamListen | |
| // timing. | |
| mAsyncOpenTime = TimeStamp::Now(); | |
| // the only time we would already know the proxy information at this | |
| // point would be if we were proxying a non-http protocol like ftp | |
| if (!mProxyInfo && NS_SUCCEEDED(ResolveProxy())) | |
| return NS_OK; | |
| - rv = BeginConnect(); | |
| - if (NS_FAILED(rv)) | |
| - ReleaseListeners(); | |
| - | |
| - return rv; | |
| + return SetupBeginConnect(); | |
| } | |
| nsresult | |
| -nsHttpChannel::BeginConnect() | |
| -{ | |
| - LOG(("nsHttpChannel::BeginConnect [this=%p]\n", this)); | |
| - nsresult rv; | |
| +nsHttpChannel::SetupBeginConnect() | |
| +{ | |
| + LOG(("nsHttpChannel::SetupBeginConnect [this=%p]\n", this)); | |
| // Construct connection info object | |
| nsAutoCString host; | |
| int32_t port = -1; | |
| bool usingSSL = false; | |
| - rv = mURI->SchemeIs("https", &usingSSL); | |
| + nsresult rv = mURI->SchemeIs("https", &usingSSL); | |
| if (NS_SUCCEEDED(rv)) | |
| rv = mURI->GetAsciiHost(host); | |
| if (NS_SUCCEEDED(rv)) | |
| rv = mURI->GetPort(&port); | |
| if (NS_SUCCEEDED(rv)) | |
| rv = mURI->GetAsciiSpec(mSpec); | |
| if (NS_FAILED(rv)) | |
| return rv; | |
| @@ -4590,18 +4608,18 @@ nsHttpChannel::BeginConnect() | |
| nsCOMPtr<nsProxyInfo> proxyInfo; | |
| if (mProxyInfo) | |
| proxyInfo = do_QueryInterface(mProxyInfo); | |
| mConnectionInfo = new nsHttpConnectionInfo(host, port, proxyInfo, usingSSL); | |
| mAuthProvider = | |
| - do_CreateInstance("@mozilla.org/network/http-channel-auth-provider;1", | |
| - &rv); | |
| + do_CreateInstance("@mozilla.org/network/http-channel-auth-provider;1", | |
| + &rv); | |
| if (NS_SUCCEEDED(rv)) | |
| rv = mAuthProvider->Init(this); | |
| if (NS_FAILED(rv)) | |
| return rv; | |
| // check to see if authorization headers should be included | |
| mAuthProvider->AddAuthorizationHeaders(); | |
| @@ -4670,16 +4688,33 @@ nsHttpChannel::BeginConnect() | |
| } | |
| // each sub resource gets a fresh connection | |
| mCaps &= ~(NS_HTTP_ALLOW_KEEPALIVE | NS_HTTP_ALLOW_PIPELINING); | |
| } | |
| // We may have been cancelled already, either by on-modify-request | |
| // listeners or by load group observers; in that case, we should | |
| // not send the request to the server | |
| + if (mCanceled && NS_FAILED(mStatus)) { | |
| + LOG(("Calling AsyncAbort [rv=%x mCanceled=%i]\n", rv, mCanceled)); | |
| + CloseCacheEntry(true); | |
| + AsyncAbort(mStatus); | |
| + } | |
| + | |
| + return NS_OK; | |
| +} | |
| + | |
| +nsresult | |
| +nsHttpChannel::BeginConnect() | |
| +{ | |
| + LOG(("nsHttpChannel::BeginConnect [this=%p]\n", this)); | |
| + nsresult rv; | |
| + // We may have been cancelled already, either by on-modify-request | |
| + // listeners or by load group observers; in that case, we should | |
| + // not send the request to the server | |
| if (mCanceled) | |
| rv = mStatus; | |
| else | |
| rv = Connect(); | |
| if (NS_FAILED(rv)) { | |
| LOG(("Calling AsyncAbort [rv=%x mCanceled=%i]\n", rv, mCanceled)); | |
| CloseCacheEntry(true); | |
| AsyncAbort(rv); | |
| @@ -4751,17 +4786,20 @@ nsHttpChannel::OnProxyAvailable(nsICance | |
| mProxyInfo = pi; | |
| if (!gHttpHandler->Active()) { | |
| LOG(("nsHttpChannel::OnProxyAvailable [this=%p] " | |
| "Handler no longer active.\n", this)); | |
| rv = NS_ERROR_NOT_AVAILABLE; | |
| } | |
| else { | |
| - rv = BeginConnect(); | |
| + rv = SetupBeginConnect(); | |
| + if (NS_SUCCEEDED(rv)) { | |
| + rv = BeginConnect(); //XXXjdm | |
| + } | |
| } | |
| if (NS_FAILED(rv)) { | |
| Cancel(rv); | |
| DoNotifyListener(); | |
| } | |
| return NS_OK; | |
| } | |
| diff --git a/netwerk/protocol/http/nsHttpChannel.h b/netwerk/protocol/http/nsHttpChannel.h | |
| --- a/netwerk/protocol/http/nsHttpChannel.h | |
| +++ b/netwerk/protocol/http/nsHttpChannel.h | |
| @@ -111,16 +111,18 @@ public: | |
| NS_IMETHOD Cancel(nsresult status); | |
| NS_IMETHOD Suspend(); | |
| NS_IMETHOD Resume(); | |
| // nsIChannel | |
| NS_IMETHOD GetSecurityInfo(nsISupports **aSecurityInfo); | |
| NS_IMETHOD AsyncOpen(nsIStreamListener *listener, nsISupports *aContext); | |
| // nsIHttpChannelInternal | |
| NS_IMETHOD SetupFallbackChannel(const char *aFallbackKey); | |
| + NS_IMETHOD AsyncOpenSetup(nsIStreamListener *listener, nsISupports *aContext); | |
| + NS_IMETHOD FinishAsyncOpen(); | |
| // nsISupportsPriority | |
| NS_IMETHOD SetPriority(int32_t value); | |
| // nsIResumableChannel | |
| NS_IMETHOD ResumeAt(uint64_t startPos, const nsACString& entityID); | |
| NS_IMETHOD SetNotificationCallbacks(nsIInterfaceRequestor *aCallbacks); | |
| NS_IMETHOD SetLoadGroup(nsILoadGroup *aLoadGroup); | |
| @@ -156,16 +158,17 @@ public: /* internal necko use only */ | |
| }; | |
| OfflineCacheEntryAsForeignMarker* GetOfflineCacheEntryAsForeignMarker(); | |
| private: | |
| typedef nsresult (nsHttpChannel::*nsContinueRedirectionFunc)(nsresult result); | |
| bool RequestIsConditional(); | |
| + nsresult SetupBeginConnect(); | |
| nsresult BeginConnect(); | |
| nsresult Connect(); | |
| nsresult ContinueConnect(); | |
| void SpeculativeConnect(); | |
| nsresult SetupTransaction(); | |
| void SetupTransactionLoadGroupInfo(); | |
| nsresult CallOnStartRequest(); | |
| nsresult ProcessResponse(); | |
| diff --git a/netwerk/protocol/http/nsIHttpChannelInternal.idl b/netwerk/protocol/http/nsIHttpChannelInternal.idl | |
| --- a/netwerk/protocol/http/nsIHttpChannelInternal.idl | |
| +++ b/netwerk/protocol/http/nsIHttpChannelInternal.idl | |
| @@ -15,16 +15,17 @@ class nsCString; | |
| [ref] native securityMessagesArray(nsCOMArray<nsISecurityConsoleMessage>); | |
| interface nsISocketTransport; | |
| interface nsIAsyncInputStream; | |
| interface nsIAsyncOutputStream; | |
| interface nsIURI; | |
| interface nsIProxyInfo; | |
| interface nsISecurityConsoleMessage; | |
| +interface nsIStreamListener; | |
| /** | |
| * The callback interface for nsIHttpChannelInternal::HTTPUpgrade() | |
| */ | |
| [scriptable, uuid(4b967b6d-cd1c-49ae-a457-23ff76f5a2e8)] | |
| interface nsIHttpUpgradeListener : nsISupports | |
| { | |
| @@ -33,17 +34,17 @@ interface nsIHttpUpgradeListener : nsISu | |
| in nsIAsyncOutputStream aSocketOut); | |
| }; | |
| /** | |
| * Dumping ground for http. This interface will never be frozen. If you are | |
| * using any feature exposed by this interface, be aware that this interface | |
| * will change and you will be broken. You have been warned. | |
| */ | |
| -[scriptable, uuid(5b4b2632-cee4-11e2-8e84-c7506188709b)] | |
| +[scriptable, uuid(ee847842-bea9-4df4-817e-e5449085223a)] | |
| interface nsIHttpChannelInternal : nsISupports | |
| { | |
| /** | |
| * An http channel can own a reference to the document URI | |
| */ | |
| attribute nsIURI documentURI; | |
| /** | |
| @@ -177,9 +178,11 @@ interface nsIHttpChannelInternal : nsISu | |
| /** | |
| * If set, this channel will load in parallel with the rest of the load | |
| * group even if a blocking subset of the group would normally be given | |
| * exclusivity. Default false. | |
| */ | |
| attribute boolean loadUnblocked; | |
| + void asyncOpenSetup(in nsIStreamListener listener, in nsISupports context); | |
| + void finishAsyncOpen(); | |
| }; | |
| diff --git a/netwerk/test/unit/test_alternate_source_channel.js b/netwerk/test/unit/test_alternate_source_channel.js | |
| new file mode 100644 | |
| --- /dev/null | |
| +++ b/netwerk/test/unit/test_alternate_source_channel.js | |
| @@ -0,0 +1,57 @@ | |
| +"use strict"; | |
| +// https://bugzilla.mozilla.org/show_bug.cgi?id=761228 | |
| + | |
| +const Cc = Components.classes; | |
| +const Ci = Components.interfaces; | |
| +const Cu = Components.utils; | |
| +const Cr = Components.results; | |
| + | |
| +Cu.import("resource://testing-common/httpd.js"); | |
| + | |
| +XPCOMUtils.defineLazyGetter(this, "URL", function() { | |
| + return "http://localhost:" + httpServer.identity.primaryPort; | |
| +}); | |
| + | |
| +var httpServer = null; | |
| + | |
| +function make_uri(url) { | |
| + var ios = Cc["@mozilla.org/network/io-service;1"]. | |
| + getService(Ci.nsIIOService); | |
| + return ios.newURI(url, null, null); | |
| +} | |
| + | |
| +function make_channel(url) { | |
| + var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService); | |
| + var chan = ios.newChannel(url, null, null).QueryInterface(Ci.nsIHttpChannel); | |
| + return chan; | |
| +} | |
| + | |
| +const REMOTE_BODY = "http handler body"; | |
| +const NON_REMOTE_BODY = "synthesized body"; | |
| + | |
| +function bodyHandler(metadata, response) { | |
| + response.write(REMOTE_BODY); | |
| +} | |
| + | |
| +function run_test() { | |
| + httpServer = new HttpServer(); | |
| + httpServer.registerPathHandler('/body', bodyHandler); | |
| + httpServer.start(-1); | |
| + | |
| + do_test_pending(); | |
| + | |
| + var chan = make_channel(URL + '/body'); | |
| + var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService); | |
| + chan = ios.QueryInterface(Ci.nsINetUtil).createAlternateSourceChannel(chan); | |
| + chan.QueryInterface(Ci.nsIAlternateSourceChannel); | |
| + chan.asyncOpen(new ChannelListener(handle_response, null), null); | |
| + var synthesized = Cc["@mozilla.org/io/string-input-stream;1"] | |
| + .createInstance(Ci.nsIStringInputStream); | |
| + synthesized.data = NON_REMOTE_BODY; | |
| + chan.initiateAlternateResponse(synthesized); | |
| +} | |
| + | |
| +function handle_response(request, buffer) { | |
| + do_check_eq(buffer, NON_REMOTE_BODY); | |
| + httpServer.stop(do_test_finished); | |
| +} | |
| \ No newline at end of file | |
| diff --git a/netwerk/test/unit/xpcshell.ini b/netwerk/test/unit/xpcshell.ini | |
| --- a/netwerk/test/unit/xpcshell.ini | |
| +++ b/netwerk/test/unit/xpcshell.ini | |
| @@ -11,16 +11,17 @@ skip-if = os == "android" | |
| [test_304_responses.js] | |
| # Bug 675039: test hangs on Android-armv6 | |
| skip-if = os == "android" | |
| [test_cacheForOfflineUse_no-store.js] | |
| [test_307_redirect.js] | |
| [test_NetUtil.js] | |
| [test_URIs.js] | |
| [test_aboutblank.js] | |
| +[test_alternate_source_channel.js] | |
| [test_assoc.js] | |
| [test_auth_jar.js] | |
| [test_auth_proxy.js] | |
| [test_authentication.js] | |
| [test_authpromptwrapper.js] | |
| [test_backgroundfilesaver.js] | |
| [test_bug203271.js] | |
| # Bug 675039: intermittent fail on Android-armv6 | |
| diff --git a/testing/xpcshell/runxpcshelltests.py b/testing/xpcshell/runxpcshelltests.py | |
| --- a/testing/xpcshell/runxpcshelltests.py | |
| +++ b/testing/xpcshell/runxpcshelltests.py | |
| @@ -194,17 +194,17 @@ class XPCShellTestThread(Thread): | |
| def communicate(self, proc): | |
| """ | |
| Simple wrapper to communicate with a process. | |
| On a remote system, this is overloaded to handle remote process communication. | |
| """ | |
| # Processing of incremental output put here to | |
| # sidestep issues on remote platforms, where what we know | |
| # as proc is a file pulled off of a device. | |
| - while True: | |
| + while proc.stdout: | |
| line = proc.stdout.readline() | |
| if not line: | |
| break | |
| self.process_line(line) | |
| if self.saw_proc_start and not self.saw_proc_end: | |
| self.has_failure_output = True | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment