Skip to content

Instantly share code, notes, and snippets.

@jdm
Created September 19, 2013 19:44
Show Gist options
  • Select an option

  • Save jdm/6628822 to your computer and use it in GitHub Desktop.

Select an option

Save jdm/6628822 to your computer and use it in GitHub Desktop.
# 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