--- a/netwerk/protocol/http/HttpBackgroundChannelChild.cpp
+++ b/netwerk/protocol/http/HttpBackgroundChannelChild.cpp
@@ -6,22 +6,22 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// HttpLog.h should generally be included first
#include "HttpLog.h"
#include "HttpBackgroundChannelChild.h"
#include "HttpChannelChild.h"
-#include "MainThreadUtils.h"
#include "mozilla/ipc/BackgroundChild.h"
#include "mozilla/ipc/PBackgroundChild.h"
#include "mozilla/IntegerPrintfMacros.h"
#include "mozilla/Unused.h"
#include "nsIIPCBackgroundChildCreateCallback.h"
+#include "nsSocketTransportService2.h"
using mozilla::ipc::BackgroundChild;
using mozilla::ipc::IPCResult;
namespace mozilla {
namespace net {
// Callbacks for PBackgroundChild creation
@@ -30,32 +30,33 @@ class BackgroundChannelCreateCallback fi
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIIPCBACKGROUNDCHILDCREATECALLBACK
explicit BackgroundChannelCreateCallback(HttpBackgroundChannelChild* aBgChild)
: mBgChild(aBgChild)
{
+ MOZ_ASSERT(OnSocketThread());
MOZ_ASSERT(aBgChild);
}
private:
virtual ~BackgroundChannelCreateCallback() { }
RefPtr<HttpBackgroundChannelChild> mBgChild;
};
NS_IMPL_ISUPPORTS(BackgroundChannelCreateCallback,
nsIIPCBackgroundChildCreateCallback)
void
BackgroundChannelCreateCallback::ActorCreated(PBackgroundChild* aActor)
{
- MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(OnSocketThread());
MOZ_ASSERT(aActor);
MOZ_ASSERT(mBgChild);
if (!mBgChild->mChannelChild) {
// HttpChannelChild is closed during PBackground creation,
// abort the rest of steps.
return;
}
@@ -65,22 +66,24 @@ BackgroundChannelCreateCallback::ActorCr
channelId)) {
ActorFailed();
return;
}
// hold extra reference for IPDL
RefPtr<HttpBackgroundChannelChild> child = mBgChild;
Unused << child.forget().take();
+
+ mBgChild->mChannelChild->OnBackgroundChildReady(mBgChild);
}
void
BackgroundChannelCreateCallback::ActorFailed()
{
- MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(OnSocketThread());
MOZ_ASSERT(mBgChild);
mBgChild->OnBackgroundChannelCreationFailed();
}
// HttpBackgroundChannelChild
HttpBackgroundChannelChild::HttpBackgroundChannelChild()
{
@@ -90,44 +93,44 @@ HttpBackgroundChannelChild::~HttpBackgro
{
}
nsresult
HttpBackgroundChannelChild::Init(HttpChannelChild* aChannelChild)
{
LOG(("HttpBackgroundChannelChild::Init [this=%p httpChannel=%p channelId=%"
PRIu64 "]\n", this, aChannelChild, aChannelChild->ChannelId()));
- MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(OnSocketThread());
NS_ENSURE_ARG(aChannelChild);
mChannelChild = aChannelChild;
if (NS_WARN_IF(!CreateBackgroundChannel())) {
mChannelChild = nullptr;
return NS_ERROR_FAILURE;
}
return NS_OK;
}
void
HttpBackgroundChannelChild::OnChannelClosed()
{
LOG(("HttpBackgroundChannelChild::OnChannelClosed [this=%p]\n", this));
- MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(OnSocketThread());
// HttpChannelChild is not going to handle any incoming message.
mChannelChild = nullptr;
}
void
HttpBackgroundChannelChild::OnStartRequestReceived()
{
LOG(("HttpBackgroundChannelChild::OnStartRequestReceived [this=%p]\n", this));
- MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(OnSocketThread());
MOZ_ASSERT(mChannelChild);
MOZ_ASSERT(!mStartReceived); // Should only be called once.
mStartReceived = true;
nsTArray<nsCOMPtr<nsIRunnable>> runnables;
runnables.SwapElements(mQueuedRunnables);
@@ -141,81 +144,82 @@ HttpBackgroundChannelChild::OnStartReque
MOZ_ASSERT(mQueuedRunnables.IsEmpty());
}
void
HttpBackgroundChannelChild::OnBackgroundChannelCreationFailed()
{
LOG(("HttpBackgroundChannelChild::OnBackgroundChannelCreationFailed"
" [this=%p]\n", this));
- MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(OnSocketThread());
if (mChannelChild) {
RefPtr<HttpChannelChild> channelChild = mChannelChild.forget();
- channelChild->FailedAsyncOpen(NS_ERROR_UNEXPECTED);
+ channelChild->OnBackgroundChildDestroyed(this);
}
}
bool
HttpBackgroundChannelChild::CreateBackgroundChannel()
{
LOG(("HttpBackgroundChannelChild::CreateBackgroundChannel [this=%p]\n", this));
- MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(OnSocketThread());
RefPtr<BackgroundChannelCreateCallback> callback =
new BackgroundChannelCreateCallback(this);
return BackgroundChild::GetOrCreateForCurrentThread(callback);
}
bool
HttpBackgroundChannelChild::IsWaitingOnStartRequest()
{
+ MOZ_ASSERT(OnSocketThread());
// Need to wait for OnStartRequest if it is sent by
// parent process but not received by content process.
return (mStartSent && !mStartReceived);
}
// PHttpBackgroundChannelChild
IPCResult
HttpBackgroundChannelChild::RecvOnStartRequestSent()
{
LOG(("HttpBackgroundChannelChild::RecvOnStartRequestSent [this=%p]\n", this));
- MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(OnSocketThread());
MOZ_ASSERT(!mStartSent); // Should only receive this message once.
mStartSent = true;
return IPC_OK();
}
IPCResult
HttpBackgroundChannelChild::RecvOnTransportAndData(
const nsresult& aChannelStatus,
const nsresult& aTransportStatus,
const uint64_t& aOffset,
const uint32_t& aCount,
const nsCString& aData)
{
LOG(("HttpBackgroundChannelChild::RecvOnTransportAndData [this=%p]\n", this));
- MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(OnSocketThread());
if (NS_WARN_IF(!mChannelChild)) {
return IPC_OK();
}
if (IsWaitingOnStartRequest()) {
LOG((" > pending until OnStartRequest [offset=%" PRIu64 " count=%" PRIu32
"]\n", aOffset, aCount));
mQueuedRunnables.AppendElement(NewRunnableMethod<const nsresult,
const nsresult,
const uint64_t,
const uint32_t,
const nsCString>(
- "net::HttpBackgroundChannelChild::RecvOnTransportAndData",
+ "HttpBackgroundChannelChild::RecvOnTransportAndData",
this,
&HttpBackgroundChannelChild::RecvOnTransportAndData,
aChannelStatus,
aTransportStatus,
aOffset,
aCount,
aData));
@@ -232,29 +236,29 @@ HttpBackgroundChannelChild::RecvOnTransp
}
IPCResult
HttpBackgroundChannelChild::RecvOnStopRequest(
const nsresult& aChannelStatus,
const ResourceTimingStruct& aTiming)
{
LOG(("HttpBackgroundChannelChild::RecvOnStopRequest [this=%p]\n", this));
- MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(OnSocketThread());
if (NS_WARN_IF(!mChannelChild)) {
return IPC_OK();
}
if (IsWaitingOnStartRequest()) {
LOG((" > pending until OnStartRequest [status=%" PRIx32 "]\n",
static_cast<uint32_t>(aChannelStatus)));
mQueuedRunnables.AppendElement(
NewRunnableMethod<const nsresult, const ResourceTimingStruct>(
- "net::HttpBackgroundChannelChild::RecvOnStopRequest",
+ "HttpBackgroundChannelChild::RecvOnStopRequest",
this,
&HttpBackgroundChannelChild::RecvOnStopRequest,
aChannelStatus,
aTiming));
return IPC_OK();
}
@@ -264,29 +268,29 @@ HttpBackgroundChannelChild::RecvOnStopRe
}
IPCResult
HttpBackgroundChannelChild::RecvOnProgress(const int64_t& aProgress,
const int64_t& aProgressMax)
{
LOG(("HttpBackgroundChannelChild::RecvOnProgress [this=%p progress=%"
PRId64 " max=%" PRId64 "]\n", this, aProgress, aProgressMax));
- MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(OnSocketThread());
if (NS_WARN_IF(!mChannelChild)) {
return IPC_OK();
}
if (IsWaitingOnStartRequest()) {
LOG((" > pending until OnStartRequest [progress=%" PRId64 " max=%"
PRId64 "]\n", aProgress, aProgressMax));
mQueuedRunnables.AppendElement(
NewRunnableMethod<const int64_t, const int64_t>(
- "net::HttpBackgroundChannelChild::RecvOnProgress",
+ "HttpBackgroundChannelChild::RecvOnProgress",
this,
&HttpBackgroundChannelChild::RecvOnProgress,
aProgress,
aProgressMax));
return IPC_OK();
}
@@ -295,168 +299,183 @@ HttpBackgroundChannelChild::RecvOnProgre
return IPC_OK();
}
IPCResult
HttpBackgroundChannelChild::RecvOnStatus(const nsresult& aStatus)
{
LOG(("HttpBackgroundChannelChild::RecvOnStatus [this=%p status=%"
PRIx32 "]\n", this, static_cast<uint32_t>(aStatus)));
- MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(OnSocketThread());
if (NS_WARN_IF(!mChannelChild)) {
return IPC_OK();
}
if (IsWaitingOnStartRequest()) {
LOG((" > pending until OnStartRequest [status=%" PRIx32 "]\n",
static_cast<uint32_t>(aStatus)));
mQueuedRunnables.AppendElement(NewRunnableMethod<const nsresult>(
- "net::HttpBackgroundChannelChild::RecvOnStatus",
+ "HttpBackgroundChannelChild::RecvOnStatus",
this,
&HttpBackgroundChannelChild::RecvOnStatus,
aStatus));
return IPC_OK();
}
mChannelChild->ProcessOnStatus(aStatus);
return IPC_OK();
}
IPCResult
HttpBackgroundChannelChild::RecvFlushedForDiversion()
{
LOG(("HttpBackgroundChannelChild::RecvFlushedForDiversion [this=%p]\n", this));
- MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(OnSocketThread());
if (NS_WARN_IF(!mChannelChild)) {
return IPC_OK();
}
if (IsWaitingOnStartRequest()) {
LOG((" > pending until OnStartRequest\n"));
mQueuedRunnables.AppendElement(NewRunnableMethod(
- "net::HttpBackgroundChannelChild::RecvFlushedForDiversion",
+ "HttpBackgroundChannelChild::RecvFlushedForDiversion",
this,
&HttpBackgroundChannelChild::RecvFlushedForDiversion));
return IPC_OK();
}
mChannelChild->ProcessFlushedForDiversion();
return IPC_OK();
}
IPCResult
HttpBackgroundChannelChild::RecvDivertMessages()
{
LOG(("HttpBackgroundChannelChild::RecvDivertMessages [this=%p]\n", this));
- MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(OnSocketThread());
if (NS_WARN_IF(!mChannelChild)) {
return IPC_OK();
}
if (IsWaitingOnStartRequest()) {
LOG((" > pending until OnStartRequest\n"));
mQueuedRunnables.AppendElement(
- NewRunnableMethod("net::HttpBackgroundChannelChild::RecvDivertMessages",
+ NewRunnableMethod("HttpBackgroundChannelChild::RecvDivertMessages",
this,
&HttpBackgroundChannelChild::RecvDivertMessages));
return IPC_OK();
}
mChannelChild->ProcessDivertMessages();
return IPC_OK();
}
IPCResult
HttpBackgroundChannelChild::RecvNotifyTrackingProtectionDisabled()
{
LOG(("HttpBackgroundChannelChild::RecvNotifyTrackingProtectionDisabled [this=%p]\n", this));
- MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(OnSocketThread());
if (NS_WARN_IF(!mChannelChild)) {
return IPC_OK();
}
// NotifyTrackingProtectionDisabled has no order dependency to OnStartRequest.
// It this be handled as soon as possible
mChannelChild->ProcessNotifyTrackingProtectionDisabled();
return IPC_OK();
}
IPCResult
HttpBackgroundChannelChild::RecvNotifyTrackingResource()
{
LOG(("HttpBackgroundChannelChild::RecvNotifyTrackingResource [this=%p]\n", this));
- MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(OnSocketThread());
if (NS_WARN_IF(!mChannelChild)) {
return IPC_OK();
}
// NotifyTrackingResource has no order dependency to OnStartRequest.
// It this be handled as soon as possible
mChannelChild->ProcessNotifyTrackingResource();
return IPC_OK();
}
IPCResult
HttpBackgroundChannelChild::RecvSetClassifierMatchedInfo(const ClassifierInfo& info)
{
LOG(("HttpBackgroundChannelChild::RecvSetClassifierMatchedInfo [this=%p]\n", this));
- MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(OnSocketThread());
if (NS_WARN_IF(!mChannelChild)) {
return IPC_OK();
}
// SetClassifierMatchedInfo has no order dependency to OnStartRequest.
// It this be handled as soon as possible
mChannelChild->ProcessSetClassifierMatchedInfo(info.list(), info.provider(), info.prefix());
return IPC_OK();
}
void
HttpBackgroundChannelChild::ActorDestroy(ActorDestroyReason aWhy)
{
LOG(("HttpBackgroundChannelChild::ActorDestroy[this=%p]\n", this));
- MOZ_ASSERT(NS_IsMainThread());
+
+ if (!OnSocketThread()) {
+ // PBackgroundChild might be destroyed during shutdown and
+ // ActorDestroy will be called on main thread directly.
+ // Simply disconnect with HttpChannelChild to release memory.
+ mChannelChild = nullptr;
+ RefPtr<HttpBackgroundChannelChild> self = this;
+ mQueuedRunnables.AppendElement(NS_NewRunnableFunction(
+ "HttpBackgroundChannelChild::ActorDestroyNonSTSThread", [self]() {
+ MOZ_ASSERT(NS_IsMainThread());
+ self->mChannelChild = nullptr;
+ }));
+ return;
+ }
+
+ MOZ_ASSERT(OnSocketThread());
// Ensure all IPC messages received before ActorDestroy can be
// handled correctly. If there is any pending IPC message, destroyed
// mChannelChild until those messages are flushed.
if (!mQueuedRunnables.IsEmpty()) {
LOG((" > pending until queued messages are flushed\n"));
RefPtr<HttpBackgroundChannelChild> self = this;
mQueuedRunnables.AppendElement(NS_NewRunnableFunction(
- "net::HttpBackgroundChannelChild::ActorDestroy", [self]() {
+ "HttpBackgroundChannelChild::ActorDestroy", [self]() {
MOZ_ASSERT(NS_IsMainThread());
RefPtr<HttpChannelChild> channelChild = self->mChannelChild.forget();
if (channelChild) {
- channelChild->OnBackgroundChildDestroyed();
+ channelChild->OnBackgroundChildDestroyed(self);
}
}));
return;
}
if (mChannelChild) {
RefPtr<HttpChannelChild> channelChild = mChannelChild.forget();
- channelChild->OnBackgroundChildDestroyed();
+ channelChild->OnBackgroundChildDestroyed(this);
}
}
} // namespace net
} // namespace mozilla
--- a/netwerk/protocol/http/HttpBackgroundChannelChild.h
+++ b/netwerk/protocol/http/HttpBackgroundChannelChild.h
@@ -90,22 +90,25 @@ private:
// Associated HttpChannelChild for handling the channel events.
// Will be removed while failed to create background channel,
// destruction of the background channel, or explicitly dissociation
// via OnChannelClosed callback.
RefPtr<HttpChannelChild> mChannelChild;
// True if OnStartRequest is received by HttpChannelChild.
+ // Should only access on STS thread.
bool mStartReceived = false;
// True if OnStartRequest is sent by HttpChannelParent.
+ // Should only access on STS thread.
bool mStartSent = false;
// Store pending messages that require to be handled after OnStartRequest.
// Should be flushed after OnStartRequest is received and handled.
+ // Should only access on STS thread.
nsTArray<nsCOMPtr<nsIRunnable>> mQueuedRunnables;
};
} // namespace net
} // namespace mozilla
#endif // mozilla_net_HttpBackgroundChannelChild_h
--- a/netwerk/protocol/http/HttpBaseChannel.h
+++ b/netwerk/protocol/http/HttpBaseChannel.h
@@ -494,17 +494,17 @@ protected:
// HTTP Upgrade Data
nsCString mUpgradeProtocol;
nsCOMPtr<nsIHttpUpgradeListener> mUpgradeProtocolCallback;
// Resumable channel specific data
nsCString mEntityID;
uint64_t mStartPos;
- nsresult mStatus;
+ Atomic<nsresult, ReleaseAcquire> mStatus;
uint32_t mLoadFlags;
uint32_t mCaps;
uint32_t mClassOfService;
int16_t mPriority;
uint8_t mRedirectionLimit;
uint32_t mApplyConversion : 1;
uint32_t mIsPending : 1;
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -45,16 +45,17 @@
#include "nsContentSecurityManager.h"
#include "nsIDeprecationWarner.h"
#include "nsICompressConvStats.h"
#include "nsIDocument.h"
#include "nsIDOMDocument.h"
#include "nsIDOMWindowUtils.h"
#include "nsIEventTarget.h"
#include "nsRedirectHistoryEntry.h"
+#include "nsSocketTransportService2.h"
#include "nsStreamUtils.h"
#include "nsThreadUtils.h"
#ifdef MOZ_TASK_TRACER
#include "GeckoTaskTracer.h"
#endif
using namespace mozilla::dom;
@@ -172,16 +173,17 @@ HttpChannelChild::HttpChannelChild()
, mSuspendSent(false)
, mSynthesizedResponse(false)
, mShouldInterceptSubsequentRedirect(false)
, mRedirectingForSubsequentSynthesizedResponse(false)
, mPostRedirectChannelShouldIntercept(false)
, mPostRedirectChannelShouldUpgrade(false)
, mShouldParentIntercept(false)
, mSuspendParentAfterSynthesizeResponse(false)
+ , mBgChildMutex("HttpChannelChild::BgChildMutex")
, mEventTargetMutex("HttpChannelChild::EventTargetMutex")
{
LOG(("Creating HttpChannelChild @%p\n", this));
mChannelCreationTime = PR_Now();
mChannelCreationTimestamp = TimeStamp::Now();
mAsyncOpenTime = TimeStamp::Now();
mEventQ = new ChannelEventQueue(static_cast<nsIHttpChannel*>(this));
@@ -290,29 +292,56 @@ HttpChannelChild::ReleaseIPDLReference()
Release();
}
void
HttpChannelChild::OnBackgroundChildReady(HttpBackgroundChannelChild* aBgChild)
{
LOG(("HttpChannelChild::OnBackgroundChildReady [this=%p, bgChild=%p]\n",
this, aBgChild));
- MOZ_ASSERT(NS_IsMainThread());
- MOZ_ASSERT(mBgChild);
-
- MOZ_ASSERT(mBgChild == aBgChild);
+ MOZ_ASSERT(OnSocketThread());
+
+ {
+ MutexAutoLock lock(mBgChildMutex);
+
+ // mBgChild might be removed or replaced while the original background
+ // channel is inited on STS thread.
+ if (mBgChild != aBgChild) {
+ return;
+ }
+
+ MOZ_ASSERT(mBgInitFailCallback);
+ mBgInitFailCallback = nullptr;
+ }
}
void
-HttpChannelChild::OnBackgroundChildDestroyed()
+HttpChannelChild::OnBackgroundChildDestroyed(HttpBackgroundChannelChild* aBgChild)
{
LOG(("HttpChannelChild::OnBackgroundChildDestroyed [this=%p]\n", this));
- MOZ_ASSERT(NS_IsMainThread());
-
- mBgChild = nullptr;
+ MOZ_ASSERT(OnSocketThread());
+
+ nsCOMPtr<nsIRunnable> callback;
+ {
+ MutexAutoLock lock(mBgChildMutex);
+
+ // mBgChild might be removed or replaced while the original background
+ // channel is destroyed on STS thread.
+ if (aBgChild != mBgChild) {
+ return;
+ }
+
+ mBgChild = nullptr;
+ callback = mBgInitFailCallback.forget();
+ }
+
+ if (callback) {
+ nsCOMPtr<nsIEventTarget> neckoTarget = GetNeckoTarget();
+ neckoTarget->Dispatch(callback, NS_DISPATCH_NORMAL);
+ }
}
class AssociateApplicationCacheEvent : public NeckoTargetChannelEvent<HttpChannelChild>
{
public:
AssociateApplicationCacheEvent(HttpChannelChild* aChild,
const nsCString &aGroupID,
const nsCString &aClientID)
@@ -454,19 +483,33 @@ HttpChannelChild::RecvOnStartRequest(con
useResponseHead, requestHeaders,
isFromCache, cacheEntryAvailable,
cacheFetchCount, cacheLastFetched,
cacheExpirationTime, cachedCharset,
securityInfoSerialization,
selfAddr, peerAddr, cacheKey,
altDataType, altDataLen));
- MOZ_ASSERT(mBgChild);
- if (mBgChild) {
- mBgChild->OnStartRequestReceived();
+ {
+ // Child's mEventQ is to control the execution order of the IPC messages
+ // from both main thread IPDL and PBackground IPDL.
+ // To guarantee the ordering, PBackground IPC messages that are sent after
+ // OnStartRequest will be throttled until OnStartRequest hits the Child's
+ // mEventQ.
+ MutexAutoLock lock(mBgChildMutex);
+
+ if (mBgChild) {
+ MOZ_RELEASE_ASSERT(gSocketTransportService);
+ DebugOnly<nsresult> rv =
+ gSocketTransportService->Dispatch(
+ NewRunnableMethod(
+ "HttpBackgroundChannelChild::OnStartRequestReceived",
+ mBgChild, &HttpBackgroundChannelChild::OnStartRequestReceived),
+ NS_DISPATCH_NORMAL);
+ }
}
return IPC_OK();
}
void
HttpChannelChild::OnStartRequest(const nsresult& channelStatus,
const nsHttpResponseHead& responseHead,
@@ -692,17 +735,17 @@ class TransportAndDataEvent : public Cha
void
HttpChannelChild::ProcessOnTransportAndData(const nsresult& aChannelStatus,
const nsresult& aTransportStatus,
const uint64_t& aOffset,
const uint32_t& aCount,
const nsCString& aData)
{
LOG(("HttpChannelChild::ProcessOnTransportAndData [this=%p]\n", this));
- MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(OnSocketThread());
MOZ_RELEASE_ASSERT(!mFlushedForDiversion,
"Should not be receiving any more callbacks from parent!");
mEventQ->RunOrEnqueue(new TransportAndDataEvent(this, aChannelStatus,
aTransportStatus, aData,
aOffset, aCount),
mDivertingToParent);
}
@@ -751,29 +794,31 @@ HttpChannelChild::OnTransportAndData(con
LOG(("HttpChannelChild::OnTransportAndData [this=%p]\n", this));
if (!mCanceled && NS_SUCCEEDED(mStatus)) {
mStatus = channelStatus;
}
// For diversion to parent, just SendDivertOnDataAvailable.
if (mDivertingToParent) {
+ MOZ_ASSERT(NS_IsMainThread());
MOZ_RELEASE_ASSERT(!mFlushedForDiversion,
"Should not be processing any more callbacks from parent!");
SendDivertOnDataAvailable(data, offset, count);
return;
}
if (mCanceled)
return;
if (mUnknownDecoderInvolved) {
LOG(("UnknownDecoder is involved queue OnDataAvailable call. [this=%p]",
this));
+ MOZ_ASSERT(NS_IsMainThread());
mUnknownDecoderEventQ.AppendElement(
MakeUnique<MaybeDivertOnDataHttpEvent>(this, data, offset, count));
}
// Hold queue lock throughout all three calls, else we might process a later
// necko msg in between them.
AutoEventEnqueuer ensureSerialDispatch(mEventQ);
@@ -918,17 +963,17 @@ class StopRequestEvent : public NeckoTar
ResourceTimingStruct mTiming;
};
void
HttpChannelChild::ProcessOnStopRequest(const nsresult& aChannelStatus,
const ResourceTimingStruct& aTiming)
{
LOG(("HttpChannelChild::ProcessOnStopRequest [this=%p]\n", this));
- MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(OnSocketThread());
MOZ_RELEASE_ASSERT(!mFlushedForDiversion,
"Should not be receiving any more callbacks from parent!");
mEventQ->RunOrEnqueue(new StopRequestEvent(this, aChannelStatus, aTiming),
mDivertingToParent);
}
class MaybeDivertOnStopHttpEvent : public NeckoTargetChannelEvent<HttpChannelChild>
@@ -948,17 +993,18 @@ class MaybeDivertOnStopHttpEvent : publi
private:
nsresult mChannelStatus;
};
void
HttpChannelChild::MaybeDivertOnStop(const nsresult& aChannelStatus)
{
LOG(("HttpChannelChild::MaybeDivertOnStop [this=%p, "
- "mDivertingToParent=%d status=%" PRIx32 "]", this, mDivertingToParent,
+ "mDivertingToParent=%d status=%" PRIx32 "]", this,
+ static_cast<bool>(mDivertingToParent),
static_cast<uint32_t>(aChannelStatus)));
if (mDivertingToParent) {
SendDivertOnStopRequest(aChannelStatus);
}
}
void
HttpChannelChild::OnStopRequest(const nsresult& channelStatus,
@@ -971,45 +1017,20 @@ HttpChannelChild::OnStopRequest(const ns
if (mDivertingToParent) {
MOZ_RELEASE_ASSERT(!mFlushedForDiversion,
"Should not be processing any more callbacks from parent!");
SendDivertOnStopRequest(channelStatus);
return;
}
- // In thread retargeting is enabled, there might be Runnable for
- // DoOnStatus/DoOnProgress sit in the main thread event target. We need to
- // ensure OnStopRequest is fired after that by postponing the
- // ChannelEventQueue processing to the end of main thread event target.
- // This workaround can be removed after bug 1338493 is complete.
- if (mODATarget) {
- {
- MutexAutoLock lock(mEventTargetMutex);
- mODATarget = nullptr;
- }
- mEventQ->Suspend();
- UniquePtr<ChannelEvent> stopEvent =
- MakeUnique<StopRequestEvent>(this, channelStatus, timing);
- mEventQ->PrependEvent(stopEvent);
-
- nsCOMPtr<nsIEventTarget> neckoTarget = GetNeckoTarget();
- MOZ_ASSERT(neckoTarget);
-
- DebugOnly<nsresult> rv = neckoTarget->Dispatch(
- NewRunnableMethod(
- "net::ChannelEventQueue::Resume", mEventQ, &ChannelEventQueue::Resume),
- NS_DISPATCH_NORMAL);
- MOZ_ASSERT(NS_SUCCEEDED(rv));
- return;
- }
-
if (mUnknownDecoderInvolved) {
LOG(("UnknownDecoder is involved queue OnStopRequest call. [this=%p]",
this));
+ MOZ_ASSERT(NS_IsMainThread());
mUnknownDecoderEventQ.AppendElement(
MakeUnique<MaybeDivertOnStopHttpEvent>(this, channelStatus));
}
nsCOMPtr<nsICompressConvStats> conv = do_QueryInterface(mCompressListener);
if (conv) {
conv->GetDecodedDataLength(&mDecodedBodySize);
}
@@ -1155,17 +1176,17 @@ class ProgressEvent : public NeckoTarget
int64_t mProgress, mProgressMax;
};
void
HttpChannelChild::ProcessOnProgress(const int64_t& aProgress,
const int64_t& aProgressMax)
{
LOG(("HttpChannelChild::ProcessOnProgress [this=%p]\n", this));
- MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(OnSocketThread());
mEventQ->RunOrEnqueue(new ProgressEvent(this, aProgress, aProgressMax));
}
void
HttpChannelChild::OnProgress(const int64_t& progress,
const int64_t& progressMax)
{
LOG(("HttpChannelChild::OnProgress [this=%p progress=%" PRId64 "/%" PRId64 "]\n",
@@ -1203,17 +1224,17 @@ class StatusEvent : public NeckoTargetCh
private:
nsresult mStatus;
};
void
HttpChannelChild::ProcessOnStatus(const nsresult& aStatus)
{
LOG(("HttpChannelChild::ProcessOnStatus [this=%p]\n", this));
- MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(OnSocketThread());
mEventQ->RunOrEnqueue(new StatusEvent(this, aStatus));
}
void
HttpChannelChild::OnStatus(const nsresult& status)
{
LOG(("HttpChannelChild::OnStatus [this=%p status=%" PRIx32 "]\n",
this, static_cast<uint32_t>(status)));
@@ -1294,31 +1315,36 @@ HttpChannelChild::FailedAsyncOpen(const
if (mIPCOpen) {
TrySendDeletingChannel();
}
}
void
HttpChannelChild::CleanupBackgroundChannel()
{
+ MutexAutoLock lock(mBgChildMutex);
+
LOG(("HttpChannelChild::CleanupBackgroundChannel [this=%p bgChild=%p]\n",
this, mBgChild.get()));
+
+ mBgInitFailCallback = nullptr;
+
if (!mBgChild) {
return;
}
RefPtr<HttpBackgroundChannelChild> bgChild = mBgChild.forget();
- if (!NS_IsMainThread()) {
- SystemGroup::Dispatch(
- "HttpChannelChild::CleanupBackgroundChannel",
- TaskCategory::Other,
- NewRunnableMethod("net::HttpBackgroundChannelChild::OnChannelClosed",
- bgChild,
- &HttpBackgroundChannelChild::OnChannelClosed));
+ MOZ_RELEASE_ASSERT(gSocketTransportService);
+ if (!OnSocketThread()) {
+ gSocketTransportService->Dispatch(
+ NewRunnableMethod(
+ "HttpBackgroundChannelChild::OnChannelClosed",
+ bgChild, &HttpBackgroundChannelChild::OnChannelClosed),
+ NS_DISPATCH_NORMAL);
} else {
bgChild->OnChannelClosed();
}
}
void
HttpChannelChild::DoNotifyListenerCleanup()
{
@@ -1680,38 +1706,51 @@ class HttpFlushedForDiversionEvent : pub
mChild->FlushedForDiversion();
}
};
void
HttpChannelChild::ProcessFlushedForDiversion()
{
LOG(("HttpChannelChild::ProcessFlushedForDiversion [this=%p]\n", this));
- MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(OnSocketThread());
MOZ_RELEASE_ASSERT(mDivertingToParent);
mEventQ->RunOrEnqueue(new HttpFlushedForDiversionEvent(this), true);
}
void
HttpChannelChild::ProcessNotifyTrackingProtectionDisabled()
{
LOG(("HttpChannelChild::ProcessNotifyTrackingProtectionDisabled [this=%p]\n", this));
- MOZ_ASSERT(NS_IsMainThread());
-
- nsChannelClassifier::NotifyTrackingProtectionDisabled(this);
+ MOZ_ASSERT(OnSocketThread());
+
+ RefPtr<HttpChannelChild> self = this;
+ nsCOMPtr<nsIEventTarget> mainTarget = GetMainThreadEventTarget();
+ mainTarget->Dispatch(
+ NS_NewRunnableFunction(
+ "nsChannelClassifier::NotifyTrackingProtectionDisabled",
+ [self]() {
+ nsChannelClassifier::NotifyTrackingProtectionDisabled(self);
+ }),
+ NS_DISPATCH_NORMAL);
}
void
HttpChannelChild::ProcessNotifyTrackingResource()
{
LOG(("HttpChannelChild::ProcessNotifyTrackingResource [this=%p]\n", this));
- MOZ_ASSERT(NS_IsMainThread());
-
- SetIsTrackingResource();
+ MOZ_ASSERT(OnSocketThread());
+
+ nsCOMPtr<nsIEventTarget> mainTarget = GetMainThreadEventTarget();
+ mainTarget->Dispatch(
+ NewRunnableMethod(
+ "HttpChannelChild::SetIsTrackingResource",
+ this, &HttpChannelChild::SetIsTrackingResource),
+ NS_DISPATCH_NORMAL);
}
void
HttpChannelChild::FlushedForDiversion()
{
LOG(("HttpChannelChild::FlushedForDiversion [this=%p]\n", this));
MOZ_RELEASE_ASSERT(mDivertingToParent);
@@ -1724,32 +1763,46 @@ HttpChannelChild::FlushedForDiversion()
}
void
HttpChannelChild::ProcessSetClassifierMatchedInfo(const nsCString& aList,
const nsCString& aProvider,
const nsCString& aPrefix)
{
LOG(("HttpChannelChild::ProcessSetClassifierMatchedInfo [this=%p]\n", this));
- MOZ_ASSERT(NS_IsMainThread());
-
- SetMatchedInfo(aList, aProvider, aPrefix);
+ MOZ_ASSERT(OnSocketThread());
+
+ nsCOMPtr<nsIEventTarget> mainTarget = GetMainThreadEventTarget();
+ mainTarget->Dispatch(
+ NewRunnableMethod<const nsCString, const nsCString, const nsCString>
+ ("HttpChannelChild::SetMatchedInfo",
+ this, &HttpChannelChild::SetMatchedInfo,
+ aList, aProvider, aPrefix),
+ NS_DISPATCH_NORMAL);
}
void
HttpChannelChild::ProcessDivertMessages()
{
LOG(("HttpChannelChild::ProcessDivertMessages [this=%p]\n", this));
- MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(OnSocketThread());
MOZ_RELEASE_ASSERT(mDivertingToParent);
- MOZ_RELEASE_ASSERT(mSuspendCount > 0);
// DivertTo() has been called on parent, so we can now start sending queued
// IPDL messages back to parent listener.
- MOZ_RELEASE_ASSERT(NS_SUCCEEDED(Resume()));
+ nsCOMPtr<nsIEventTarget> neckoTarget = GetNeckoTarget();
+ MOZ_ASSERT(neckoTarget);
+ nsresult rv =
+ neckoTarget->Dispatch(
+ NewRunnableMethod(
+ "HttpChannelChild::Resume",
+ this, &HttpChannelChild::Resume),
+ NS_DISPATCH_NORMAL);
+
+ MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
}
// Returns true if has actually completed the redirect and cleaned up the
// channel, or false the interception logic kicked in and we need to asyncly
// call FinishInterceptedRedirect and CleanupRedirectingChannel.
// The argument is an optional OverrideRunnable that we pass to the redirected
// channel.
bool
@@ -1861,22 +1914,39 @@ HttpChannelChild::ConnectParent(uint32_t
if (!gNeckoChild->
SendPHttpChannelConstructor(this, browser,
slc,
connectArgs)) {
return NS_ERROR_FAILURE;
}
{
+ MutexAutoLock lock(mBgChildMutex);
+
MOZ_ASSERT(!mBgChild);
+ MOZ_ASSERT(!mBgInitFailCallback);
+
+ mBgInitFailCallback = NewRunnableMethod<nsresult>(
+ "HttpChannelChild::OnRedirectVerifyCallback",
+ this, &HttpChannelChild::OnRedirectVerifyCallback,
+ NS_ERROR_FAILURE);
RefPtr<HttpBackgroundChannelChild> bgChild =
new HttpBackgroundChannelChild();
- nsresult rv = bgChild->Init(this);
+ MOZ_RELEASE_ASSERT(gSocketTransportService);
+
+ RefPtr<HttpChannelChild> self = this;
+ nsresult rv =
+ gSocketTransportService->Dispatch(
+ NewRunnableMethod<RefPtr<HttpChannelChild>>(
+ "HttpBackgroundChannelChild::Init",
+ bgChild, &HttpBackgroundChannelChild::Init, Move(self)),
+ NS_DISPATCH_NORMAL);
+
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
mBgChild = bgChild.forget();
}
return NS_OK;
@@ -2097,17 +2167,18 @@ HttpChannelChild::Cancel(nsresult status
}
return NS_OK;
}
NS_IMETHODIMP
HttpChannelChild::Suspend()
{
LOG(("HttpChannelChild::Suspend [this=%p, mSuspendCount=%" PRIu32 ", "
- "mDivertingToParent=%d]\n", this, mSuspendCount+1, mDivertingToParent));
+ "mDivertingToParent=%d]\n", this, mSuspendCount + 1,
+ static_cast<bool>(mDivertingToParent)));
NS_ENSURE_TRUE(RemoteChannelExists() || mInterceptListener,
NS_ERROR_NOT_AVAILABLE);
// SendSuspend only once, when suspend goes from 0 to 1.
// Don't SendSuspend at all if we're diverting callbacks to the parent;
// suspend will be called at the correct time in the parent itself.
if (!mSuspendCount++ && !mDivertingToParent) {
if (RemoteChannelExists()) {
@@ -2122,17 +2193,18 @@ HttpChannelChild::Suspend()
return NS_OK;
}
NS_IMETHODIMP
HttpChannelChild::Resume()
{
LOG(("HttpChannelChild::Resume [this=%p, mSuspendCount=%" PRIu32 ", "
- "mDivertingToParent=%d]\n", this, mSuspendCount-1, mDivertingToParent));
+ "mDivertingToParent=%d]\n", this, mSuspendCount - 1,
+ static_cast<bool>(mDivertingToParent)));
NS_ENSURE_TRUE(RemoteChannelExists() || mInterceptListener,
NS_ERROR_NOT_AVAILABLE);
NS_ENSURE_TRUE(mSuspendCount > 0, NS_ERROR_UNEXPECTED);
nsresult rv = NS_OK;
// SendResume only once, when suspend count drops to 0.
// Don't SendResume at all if we're diverting callbacks to the parent (unless
@@ -2506,28 +2578,51 @@ HttpChannelChild::ContinueAsyncOpen()
"SerializedLoadContext should not be null");
if (!gNeckoChild->SendPHttpChannelConstructor(this, browser,
slc,
openArgs)) {
return NS_ERROR_FAILURE;
}
{
+ MutexAutoLock lock(mBgChildMutex);
+
+ MOZ_RELEASE_ASSERT(gSocketTransportService);
+
// Service worker might use the same HttpChannelChild to do async open
// twice. Need to disconnect with previous background channel before
- // creating the new one.
+ // creating the new one, to prevent receiving further notification
+ // from it.
if (mBgChild) {
RefPtr<HttpBackgroundChannelChild> prevBgChild = mBgChild.forget();
- prevBgChild->OnChannelClosed();
+ gSocketTransportService->Dispatch(
+ NewRunnableMethod(
+ "HttpBackgroundChannelChild::OnChannelClosed",
+ prevBgChild, &HttpBackgroundChannelChild::OnChannelClosed),
+ NS_DISPATCH_NORMAL);
}
+ MOZ_ASSERT(!mBgInitFailCallback);
+
+ mBgInitFailCallback = NewRunnableMethod<nsresult>(
+ "HttpChannelChild::FailedAsyncOpen",
+ this, &HttpChannelChild::FailedAsyncOpen,
+ NS_ERROR_FAILURE);
+
RefPtr<HttpBackgroundChannelChild> bgChild =
new HttpBackgroundChannelChild();
- rv = bgChild->Init(this);
+ RefPtr<HttpChannelChild> self = this;
+ nsresult rv =
+ gSocketTransportService->Dispatch(
+ NewRunnableMethod<RefPtr<HttpChannelChild>>(
+ "HttpBackgroundChannelChild::Init",
+ bgChild, &HttpBackgroundChannelChild::Init, self),
+ NS_DISPATCH_NORMAL);
+
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
mBgChild = bgChild.forget();
}
return NS_OK;
@@ -3110,25 +3205,30 @@ HttpChannelChild::DivertToParent(Channel
return NS_OK;
}
NS_IMETHODIMP
HttpChannelChild::UnknownDecoderInvolvedKeepData()
{
LOG(("HttpChannelChild::UnknownDecoderInvolvedKeepData [this=%p]",
this));
+ MOZ_ASSERT(NS_IsMainThread());
+
mUnknownDecoderInvolved = true;
return NS_OK;
}
NS_IMETHODIMP
HttpChannelChild::UnknownDecoderInvolvedOnStartRequestCalled()
{
LOG(("HttpChannelChild::UnknownDecoderInvolvedOnStartRequestCalled "
- "[this=%p, mDivertingToParent=%d]", this, mDivertingToParent));
+ "[this=%p, mDivertingToParent=%d]", this,
+ static_cast<bool>(mDivertingToParent)));
+ MOZ_ASSERT(NS_IsMainThread());
+
mUnknownDecoderInvolved = false;
nsresult rv = NS_OK;
if (mDivertingToParent) {
rv = mEventQ->PrependEvents(mUnknownDecoderEventQ);
}
mUnknownDecoderEventQ.Clear();
--- a/netwerk/protocol/http/HttpChannelChild.h
+++ b/netwerk/protocol/http/HttpChannelChild.h
@@ -115,17 +115,17 @@ public:
void FlushedForDiversion();
void OnCopyComplete(nsresult aStatus) override;
// Callback while background channel is ready.
void OnBackgroundChildReady(HttpBackgroundChannelChild* aBgChild);
// Callback while background channel is destroyed.
- void OnBackgroundChildDestroyed();
+ void OnBackgroundChildDestroyed(HttpBackgroundChannelChild* aBgChild);
protected:
mozilla::ipc::IPCResult RecvOnStartRequest(const nsresult& channelStatus,
const nsHttpResponseHead& responseHead,
const bool& useResponseHead,
const nsHttpHeaderArray& requestHeaders,
const bool& isFromCache,
const bool& cacheEntryAvailable,
@@ -289,23 +289,23 @@ private:
Atomic<bool> mIPCOpen;
bool mKeptAlive; // IPC kept open, but only for security info
RefPtr<ChannelEventQueue> mEventQ;
// If nsUnknownDecoder is involved OnStartRequest call will be delayed and
// this queue keeps OnDataAvailable data until OnStartRequest is finally
// called.
nsTArray<UniquePtr<ChannelEvent>> mUnknownDecoderEventQ;
- bool mUnknownDecoderInvolved;
+ Atomic<bool, ReleaseAcquire> mUnknownDecoderInvolved;
// Once set, OnData and possibly OnStop will be diverted to the parent.
- bool mDivertingToParent;
+ Atomic<bool, ReleaseAcquire> mDivertingToParent;
// Once set, no OnStart/OnData/OnStop callbacks should be received from the
// parent channel, nor dequeued from the ChannelEventQueue.
- bool mFlushedForDiversion;
+ Atomic<bool, ReleaseAcquire> mFlushedForDiversion;
// Set if SendSuspend is called. Determines if SendResume is needed when
// diverting callbacks to parent.
bool mSuspendSent;
// Set if a response was synthesized, indicating that any forthcoming redirects
// should be intercepted.
bool mSynthesizedResponse;
@@ -327,18 +327,25 @@ private:
// Set if the corresponding parent channel should force an interception to occur
// before the network transaction is initiated.
bool mShouldParentIntercept;
// Set if the corresponding parent channel should suspend after a response
// is synthesized.
bool mSuspendParentAfterSynthesizeResponse;
+ // Used to ensure atomicity of mBgChild and mBgInitFailCallback
+ Mutex mBgChildMutex;
+
+ // Associated HTTP background channel
RefPtr<HttpBackgroundChannelChild> mBgChild;
+ // Error handling procedure if failed to establish PBackground IPC
+ nsCOMPtr<nsIRunnable> mBgInitFailCallback;
+
// Remove the association with background channel after OnStopRequest
// or AsyncAbort.
void CleanupBackgroundChannel();
// Needed to call AsyncOpen in FinishInterceptedRedirect
nsCOMPtr<nsIStreamListener> mInterceptedRedirectListener;
nsCOMPtr<nsISupports> mInterceptedRedirectContext;
// Needed to call CleanupRedirectingChannel in FinishInterceptedRedirect
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -4407,17 +4407,17 @@ nsHttpChannel::OnCacheEntryAvailableInte
bool aNew,
nsIApplicationCache* aAppCache,
nsresult status)
{
nsresult rv;
if (mCanceled) {
LOG(("channel was canceled [this=%p status=%" PRIx32 "]\n",
- this, static_cast<uint32_t>(mStatus)));
+ this, static_cast<uint32_t>(static_cast<nsresult>(mStatus))));
return mStatus;
}
if (aAppCache) {
if (mApplicationCache == aAppCache && !mCacheEntry) {
rv = OnOfflineCacheEntryAvailable(entry, aNew, aAppCache, status);
}
else if (mApplicationCacheForWrite == aAppCache && aNew && !mOfflineCacheEntry) {
@@ -5042,17 +5042,17 @@ void
nsHttpChannel::CloseCacheEntry(bool doomOnFailure)
{
mCacheInputStream.CloseAndRelease();
if (!mCacheEntry)
return;
LOG(("nsHttpChannel::CloseCacheEntry [this=%p] mStatus=%" PRIx32 " mCacheEntryIsWriteOnly=%x",
- this, static_cast<uint32_t>(mStatus), mCacheEntryIsWriteOnly));
+ this, static_cast<uint32_t>(static_cast<nsresult>(mStatus)), mCacheEntryIsWriteOnly));
// If we have begun to create or replace a cache entry, and that cache
// entry is not complete and not resumable, then it needs to be doomed.
// Otherwise, CheckCache will make the mistake of thinking that the
// partial cache entry is complete.
bool doom = false;
if (mInitedCacheEntry) {
@@ -6738,17 +6738,18 @@ nsHttpChannel::ClearClassFlags(uint32_t
//-----------------------------------------------------------------------------
NS_IMETHODIMP
nsHttpChannel::OnProxyAvailable(nsICancelable *request, nsIChannel *channel,
nsIProxyInfo *pi, nsresult status)
{
LOG(("nsHttpChannel::OnProxyAvailable [this=%p pi=%p status=%" PRIx32
" mStatus=%" PRIx32 "]\n",
- this, pi, static_cast<uint32_t>(status), static_cast<uint32_t>(mStatus)));
+ this, pi, static_cast<uint32_t>(status),
+ static_cast<uint32_t>(static_cast<nsresult>(mStatus))));
mProxyRequest = nullptr;
nsresult rv;
// If status is a failure code, then it means that we failed to resolve
// proxy info. That is a non-fatal error assuming it wasn't because the
// request was canceled. We just failover to DIRECT when proxy resolution
// fails (failure can mean that the PAC URL could not be loaded).
@@ -6962,21 +6963,23 @@ nsHttpChannel::OnStartRequest(nsIRequest
{
nsresult rv;
AUTO_PROFILER_LABEL("nsHttpChannel::OnStartRequest", NETWORK);
if (!(mCanceled || NS_FAILED(mStatus)) && !WRONG_RACING_RESPONSE_SOURCE(request)) {
// capture the request's status, so our consumers will know ASAP of any
// connection failures, etc - bug 93581
- request->GetStatus(&mStatus);
+ nsresult status;
+ request->GetStatus(&status);
+ mStatus = status;
}
LOG(("nsHttpChannel::OnStartRequest [this=%p request=%p status=%" PRIx32 "]\n",
- this, request, static_cast<uint32_t>(mStatus)));
+ this, request, static_cast<uint32_t>(static_cast<nsresult>(mStatus))));
if (mRaceCacheWithNetwork) {
LOG((" racingNetAndCache - mFirstResponseSource:%d fromCache:%d fromNet:%d\n",
mFirstResponseSource, request == mCachePump, request == mTransactionPump));
if (mFirstResponseSource == RESPONSE_PENDING) {
// When the cache wins mFirstResponseSource is set to RESPONSE_FROM_CACHE
// earlier in ReadFromCache, so this must be a response from the network.
MOZ_ASSERT(request == mTransactionPump);