--- a/ipc/glue/BackgroundChildImpl.cpp
+++ b/ipc/glue/BackgroundChildImpl.cpp
@@ -28,16 +28,17 @@
#include "mozilla/dom/GamepadEventChannelChild.h"
#include "mozilla/dom/GamepadTestChannelChild.h"
#include "mozilla/dom/MessagePortChild.h"
#include "mozilla/ipc/IPCStreamAlloc.h"
#include "mozilla/ipc/PBackgroundTestChild.h"
#include "mozilla/ipc/PChildToParentStreamChild.h"
#include "mozilla/ipc/PParentToChildStreamChild.h"
#include "mozilla/layout/VsyncChild.h"
+#include "mozilla/net/HttpBackgroundChannelChild.h"
#include "mozilla/net/PUDPSocketChild.h"
#include "mozilla/dom/network/UDPSocketChild.h"
#include "nsID.h"
#include "nsTraceRefcnt.h"
namespace {
class TestChild final : public mozilla::ipc::PBackgroundTestChild
@@ -531,18 +532,21 @@ BackgroundChildImpl::AllocPHttpBackgroun
{
MOZ_CRASH("PHttpBackgroundChannelChild actor should be manually constructed!");
return nullptr;
}
bool
BackgroundChildImpl::DeallocPHttpBackgroundChannelChild(PHttpBackgroundChannelChild* aActor)
{
- MOZ_ASSERT(aActor);
- // TODO
+ // The reference is increased in BackgroundChannelCreateCallback::ActorCreated
+ // of HttpBackgroundChannelChild.cpp. We should decrease it after IPC
+ // destroyed.
+ RefPtr<net::HttpBackgroundChannelChild> child =
+ dont_AddRef(static_cast<net::HttpBackgroundChannelChild*>(aActor));
return true;
}
} // namespace ipc
} // namespace mozilla
mozilla::ipc::IPCResult
TestChild::Recv__delete__(const nsCString& aTestArg)
--- a/ipc/glue/BackgroundParentImpl.cpp
+++ b/ipc/glue/BackgroundParentImpl.cpp
@@ -871,41 +871,54 @@ BackgroundParentImpl::DeallocPGamepadTes
}
net::PHttpBackgroundChannelParent*
BackgroundParentImpl::AllocPHttpBackgroundChannelParent(const nsID& aChannelId)
{
AssertIsInMainProcess();
AssertIsOnBackgroundThread();
- return new net::HttpBackgroundChannelParent();
+ RefPtr<net::HttpBackgroundChannelParent> actor =
+ new net::HttpBackgroundChannelParent();
+
+ // hold extra refcount for IPDL
+ return actor.forget().take();
}
mozilla::ipc::IPCResult
BackgroundParentImpl::RecvPHttpBackgroundChannelConstructor(
net::PHttpBackgroundChannelParent *aActor,
const nsID& aChannelId)
{
MOZ_ASSERT(aActor);
AssertIsInMainProcess();
AssertIsOnBackgroundThread();
- //TODO
+ net::HttpBackgroundChannelParent* aParent =
+ static_cast<net::HttpBackgroundChannelParent*>(aActor);
+
+ if (NS_WARN_IF(NS_FAILED(aParent->Init(aChannelId)))) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+
return IPC_OK();
}
bool
BackgroundParentImpl::DeallocPHttpBackgroundChannelParent(
net::PHttpBackgroundChannelParent *aActor)
{
MOZ_ASSERT(aActor);
AssertIsInMainProcess();
AssertIsOnBackgroundThread();
- //TODO
+ // release extra refcount hold by AllocPHttpBackgroundChannelParent
+ RefPtr<net::HttpBackgroundChannelParent> actor =
+ dont_AddRef(static_cast<net::HttpBackgroundChannelParent*>(aActor));
+
return true;
}
} // namespace ipc
} // namespace mozilla
void
TestParent::ActorDestroy(ActorDestroyReason aWhy)
--- a/netwerk/base/nsIParentRedirectingChannel.idl
+++ b/netwerk/base/nsIParentRedirectingChannel.idl
@@ -3,16 +3,30 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsIParentChannel.idl"
interface nsITabParent;
interface nsIChannel;
interface nsIAsyncVerifyRedirectCallback;
+[builtinclass, uuid(01987690-48cf-45de-bae3-e143c2adc2a8)]
+interface nsIAsyncVerifyRedirectReadyCallback : nsISupports
+{
+ /**
+ * Asynchronous callback when redirected channel finishes the preparation for
+ * completing the verification procedure.
+ *
+ * @param result
+ * SUCCEEDED if preparation for redirection verification succceed.
+ * If FAILED the redirection must be aborted.
+ */
+ void readyToVerify(in nsresult result);
+};
+
/**
* Implemented by chrome side of IPC protocols that support redirect responses.
*/
[scriptable, uuid(3ed1d288-5324-46ee-8a98-33ac37d1080b)]
interface nsIParentRedirectingChannel : nsIParentChannel
{
/**
@@ -29,16 +43,27 @@ interface nsIParentRedirectingChannel :
* nsIChannelEventSink defines it
*/
void startRedirect(in uint32_t newChannelId,
in nsIChannel newChannel,
in uint32_t redirectFlags,
in nsIAsyncVerifyRedirectCallback callback);
/**
+ * Called to new channel when the original channel got Redirect2Verify
+ * response from child. Callback will be invoked when the new channel
+ * finishes the preparation for Redirect2Verify
+ *
+ * @param callback
+ * redirect ready callback, will be called when redirect verification
+ * procedure can proceed.
+ */
+ void continueVerification(in nsIAsyncVerifyRedirectReadyCallback callback);
+
+ /**
* Called after we are done with redirecting process and we know if to
* redirect or not. Forward the redirect result to the child process. From
* that moment the nsIParentChannel implementation expects it will be
* forwarded all notifications from the 'real' channel.
*
* Primarilly used by HttpChannelParentListener::OnRedirectResult and kept
* as mActiveChannel and mRedirectChannel in that class.
*/
--- a/netwerk/build/nsNetCID.h
+++ b/netwerk/build/nsNetCID.h
@@ -643,16 +643,28 @@
#define NS_THROTTLEQUEUE_CID \
{ /* 4c39159c-cd90-4dd3-97a7-06af5e6d84c4 */ \
0x4c39159c, \
0xcd90, \
0x4dd3, \
{0x97, 0xa7, 0x06, 0xaf, 0x5e, 0x6d, 0x84, 0xc4} \
}
+// Background channel registrar used for pairing HttpChannelParent
+// and its background channel
+#define NS_BACKGROUNDCHANNELREGISTRAR_CONTRACTID \
+ "@mozilla.org/network/background-channel-registrar;1"
+#define NS_BACKGROUNDCHANNELREGISTRAR_CID \
+{ /* 6907788a-17cc-4c2a-a7c5-59ad2d9cc079 */ \
+ 0x6907788a, \
+ 0x17cc, \
+ 0x4c2a, \
+ { 0xa7, 0xc5, 0x59, 0xad, 0x2d, 0x9c, 0xc0, 0x79} \
+}
+
/******************************************************************************
* netwerk/protocol/ftp/ classes
*/
#define NS_FTPPROTOCOLHANDLER_CID \
{ /* 25029490-F132-11d2-9588-00805F369F95 */ \
0x25029490, \
0xf132, \
--- a/netwerk/build/nsNetModule.cpp
+++ b/netwerk/build/nsNetModule.cpp
@@ -263,29 +263,31 @@ NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsFt
#undef LOG_ENABLED
#include "nsHttpAuthManager.h"
#include "nsHttpChannelAuthProvider.h"
#include "nsHttpBasicAuth.h"
#include "nsHttpDigestAuth.h"
#include "nsHttpNTLMAuth.h"
#include "nsHttpActivityDistributor.h"
#include "ThrottleQueue.h"
+#include "BackgroundChannelRegistrar.h"
#undef LOG
#undef LOG_ENABLED
namespace mozilla {
namespace net {
NS_GENERIC_FACTORY_CONSTRUCTOR(nsHttpNTLMAuth)
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsHttpHandler, Init)
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsHttpsHandler, Init)
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsHttpAuthManager, Init)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsHttpChannelAuthProvider)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsHttpActivityDistributor)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsHttpBasicAuth)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsHttpDigestAuth)
NS_GENERIC_FACTORY_CONSTRUCTOR(ThrottleQueue)
+NS_GENERIC_FACTORY_CONSTRUCTOR(BackgroundChannelRegistrar)
} // namespace net
} // namespace mozilla
#endif // !NECKO_PROTOCOL_http
#include "mozilla/net/Dashboard.h"
namespace mozilla {
namespace net {
NS_GENERIC_FACTORY_CONSTRUCTOR(Dashboard)
@@ -801,16 +803,17 @@ NS_DEFINE_NAMED_CID(NS_HTTPPROTOCOLHANDL
NS_DEFINE_NAMED_CID(NS_HTTPSPROTOCOLHANDLER_CID);
NS_DEFINE_NAMED_CID(NS_HTTPBASICAUTH_CID);
NS_DEFINE_NAMED_CID(NS_HTTPDIGESTAUTH_CID);
NS_DEFINE_NAMED_CID(NS_HTTPNTLMAUTH_CID);
NS_DEFINE_NAMED_CID(NS_HTTPAUTHMANAGER_CID);
NS_DEFINE_NAMED_CID(NS_HTTPCHANNELAUTHPROVIDER_CID);
NS_DEFINE_NAMED_CID(NS_HTTPACTIVITYDISTRIBUTOR_CID);
NS_DEFINE_NAMED_CID(NS_THROTTLEQUEUE_CID);
+NS_DEFINE_NAMED_CID(NS_BACKGROUNDCHANNELREGISTRAR_CID);
#endif // !NECKO_PROTOCOL_http
#ifdef NECKO_PROTOCOL_ftp
NS_DEFINE_NAMED_CID(NS_FTPPROTOCOLHANDLER_CID);
#endif
#ifdef NECKO_PROTOCOL_res
NS_DEFINE_NAMED_CID(NS_RESPROTOCOLHANDLER_CID);
NS_DEFINE_NAMED_CID(NS_EXTENSIONPROTOCOLHANDLER_CID);
NS_DEFINE_NAMED_CID(NS_SUBSTITUTINGURL_CID);
@@ -953,16 +956,17 @@ static const mozilla::Module::CIDEntry k
{ &kNS_HTTPSPROTOCOLHANDLER_CID, false, nullptr, mozilla::net::nsHttpsHandlerConstructor },
{ &kNS_HTTPBASICAUTH_CID, false, nullptr, mozilla::net::nsHttpBasicAuthConstructor },
{ &kNS_HTTPDIGESTAUTH_CID, false, nullptr, mozilla::net::nsHttpDigestAuthConstructor },
{ &kNS_HTTPNTLMAUTH_CID, false, nullptr, mozilla::net::nsHttpNTLMAuthConstructor },
{ &kNS_HTTPAUTHMANAGER_CID, false, nullptr, mozilla::net::nsHttpAuthManagerConstructor },
{ &kNS_HTTPCHANNELAUTHPROVIDER_CID, false, nullptr, mozilla::net::nsHttpChannelAuthProviderConstructor },
{ &kNS_HTTPACTIVITYDISTRIBUTOR_CID, false, nullptr, mozilla::net::nsHttpActivityDistributorConstructor },
{ &kNS_THROTTLEQUEUE_CID, false, nullptr, mozilla::net::ThrottleQueueConstructor },
+ { &kNS_BACKGROUNDCHANNELREGISTRAR_CID, false, nullptr, mozilla::net::BackgroundChannelRegistrarConstructor },
#endif // !NECKO_PROTOCOL_http
#ifdef NECKO_PROTOCOL_ftp
{ &kNS_FTPPROTOCOLHANDLER_CID, false, nullptr, nsFtpProtocolHandlerConstructor },
#endif
#ifdef NECKO_PROTOCOL_res
{ &kNS_RESPROTOCOLHANDLER_CID, false, nullptr, nsResProtocolHandlerConstructor },
{ &kNS_EXTENSIONPROTOCOLHANDLER_CID, false, nullptr, mozilla::ExtensionProtocolHandlerConstructor },
{ &kNS_SUBSTITUTINGURL_CID, false, nullptr, mozilla::SubstitutingURLConstructor },
@@ -1115,16 +1119,17 @@ static const mozilla::Module::ContractID
{ NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "https", &kNS_HTTPSPROTOCOLHANDLER_CID },
{ NS_HTTP_AUTHENTICATOR_CONTRACTID_PREFIX "basic", &kNS_HTTPBASICAUTH_CID },
{ NS_HTTP_AUTHENTICATOR_CONTRACTID_PREFIX "digest", &kNS_HTTPDIGESTAUTH_CID },
{ NS_HTTP_AUTHENTICATOR_CONTRACTID_PREFIX "ntlm", &kNS_HTTPNTLMAUTH_CID },
{ NS_HTTPAUTHMANAGER_CONTRACTID, &kNS_HTTPAUTHMANAGER_CID },
{ NS_HTTPCHANNELAUTHPROVIDER_CONTRACTID, &kNS_HTTPCHANNELAUTHPROVIDER_CID },
{ NS_HTTPACTIVITYDISTRIBUTOR_CONTRACTID, &kNS_HTTPACTIVITYDISTRIBUTOR_CID },
{ NS_THROTTLEQUEUE_CONTRACTID, &kNS_THROTTLEQUEUE_CID },
+ { NS_BACKGROUNDCHANNELREGISTRAR_CONTRACTID, &kNS_BACKGROUNDCHANNELREGISTRAR_CID },
#endif // !NECKO_PROTOCOL_http
#ifdef NECKO_PROTOCOL_ftp
{ NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "ftp", &kNS_FTPPROTOCOLHANDLER_CID },
#endif
#ifdef NECKO_PROTOCOL_res
{ NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "resource", &kNS_RESPROTOCOLHANDLER_CID },
{ NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "moz-extension", &kNS_EXTENSIONPROTOCOLHANDLER_CID },
#endif
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/BackgroundChannelRegistrar.cpp
@@ -0,0 +1,98 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "BackgroundChannelRegistrar.h"
+
+#include "HttpBackgroundChannelParent.h"
+#include "HttpChannelParent.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsXULAppAPI.h"
+
+namespace mozilla {
+namespace net {
+
+NS_IMPL_ISUPPORTS(BackgroundChannelRegistrar, nsIBackgroundChannelRegistrar)
+
+BackgroundChannelRegistrar::BackgroundChannelRegistrar()
+{
+ MOZ_ASSERT(XRE_IsParentProcess());
+ MOZ_ASSERT(NS_IsMainThread());
+}
+
+BackgroundChannelRegistrar::~BackgroundChannelRegistrar()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ mChannels.Clear();
+ mBgChannels.Clear();
+}
+
+void
+BackgroundChannelRegistrar::NotifyChannelLinked(
+ HttpChannelParent* aChannelParent,
+ HttpBackgroundChannelParent* aBgParent)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aChannelParent);
+ MOZ_ASSERT(aBgParent);
+
+ aBgParent->LinkToChannel(aChannelParent);
+ aChannelParent->OnBackgroundParentReady(aBgParent);
+}
+
+// nsIBackgroundChannelRegistrar
+void
+BackgroundChannelRegistrar::DeleteChannel(const nsID& aKey)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ mChannels.Remove(const_cast<nsID&>(aKey));
+ mBgChannels.Remove(const_cast<nsID&>(aKey));
+}
+
+void
+BackgroundChannelRegistrar::LinkHttpChannel(
+ const nsID& aKey,
+ HttpChannelParent* aChannel)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aChannel);
+
+ RefPtr<HttpBackgroundChannelParent> bgParent;
+ bool found = mBgChannels.Remove(const_cast<nsID&>(aKey),
+ getter_AddRefs(bgParent));
+
+ if (!found) {
+ mChannels.Put(aKey, aChannel);
+ return;
+ }
+
+ MOZ_ASSERT(bgParent);
+ NotifyChannelLinked(aChannel, bgParent);
+}
+
+void
+BackgroundChannelRegistrar::LinkBackgroundChannel(
+ const nsID& aKey,
+ HttpBackgroundChannelParent* aBgChannel)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aBgChannel);
+
+ RefPtr<HttpChannelParent> parent;
+ bool found = mChannels.Remove(const_cast<nsID&>(aKey), getter_AddRefs(parent));
+
+ if (!found) {
+ mBgChannels.Put(aKey, aBgChannel);
+ return;
+ }
+
+ MOZ_ASSERT(parent);
+ NotifyChannelLinked(parent, aBgChannel);
+}
+
+} // namespace net
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/BackgroundChannelRegistrar.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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_BackgroundChannelRegistrar_h__
+#define mozilla_net_BackgroundChannelRegistrar_h__
+
+#include "nsIBackgroundChannelRegistrar.h"
+#include "nsRefPtrHashtable.h"
+
+namespace mozilla {
+namespace net {
+
+class HttpBackgroundChannelParent;
+class HttpChannelParent;
+
+class BackgroundChannelRegistrar final : public nsIBackgroundChannelRegistrar
+{
+ typedef nsRefPtrHashtable<nsIDHashKey, HttpChannelParent>
+ ChannelHashtable;
+ typedef nsRefPtrHashtable<nsIDHashKey, HttpBackgroundChannelParent>
+ BackgroundChannelHashtable;
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIBACKGROUNDCHANNELREGISTRAR
+
+ explicit BackgroundChannelRegistrar();
+
+private:
+ virtual ~BackgroundChannelRegistrar();
+
+ // Notify linked channel found to both HttpChannelParent and
+ // HttpBackgroundChannelParent.
+ void NotifyChannelLinked(HttpChannelParent* aChannelParent,
+ HttpBackgroundChannelParent* aBgParent);
+
+ // Store unlinked HttpChannelParent objects.
+ ChannelHashtable mChannels;
+
+ // Store unlinked HttpBackgroundChannelParent objects.
+ BackgroundChannelHashtable mBgChannels;
+
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozilla_net_BackgroundChannelRegistrar_h__
--- a/netwerk/protocol/http/HttpBackgroundChannelChild.cpp
+++ b/netwerk/protocol/http/HttpBackgroundChannelChild.cpp
@@ -1,30 +1,152 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 et tw=80 : */
/* 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/. */
+// 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/Unused.h"
+#include "nsIIPCBackgroundChildCreateCallback.h"
+
+using mozilla::ipc::BackgroundChild;
using mozilla::ipc::IPCResult;
namespace mozilla {
namespace net {
+// Callbacks for PBackgroundChild creation
+class BackgroundChannelCreateCallback final
+ : public nsIIPCBackgroundChildCreateCallback
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIIPCBACKGROUNDCHILDCREATECALLBACK
+
+ explicit BackgroundChannelCreateCallback(HttpBackgroundChannelChild* aChild)
+ : mChild(aChild)
+ {
+ MOZ_ASSERT(aChild);
+ }
+
+private:
+ virtual ~BackgroundChannelCreateCallback() { }
+
+ RefPtr<HttpBackgroundChannelChild> mChild;
+};
+
+NS_IMPL_ISUPPORTS(BackgroundChannelCreateCallback,
+ nsIIPCBackgroundChildCreateCallback)
+
+void
+BackgroundChannelCreateCallback::ActorCreated(PBackgroundChild* aActor)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aActor);
+ MOZ_ASSERT(mChild);
+
+ if (!mChild->mChannelChild) {
+ // HttpChannelChild is closed during PBackground creation,
+ // abort the rest of steps.
+ return;
+ }
+
+ if(!aActor->SendPHttpBackgroundChannelConstructor(mChild,
+ mChild->mChannelChild->ChannelId())) {
+ ActorFailed();
+ return;
+ }
+
+ // hold extra reference for IPDL
+ RefPtr<HttpBackgroundChannelChild> child = mChild;
+ Unused << child.forget().take();
+}
+
+void
+BackgroundChannelCreateCallback::ActorFailed()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(mChild);
+
+ mChild->NotifyActorFailed();
+}
+
+// HttpBackgroundChannelChild
HttpBackgroundChannelChild::HttpBackgroundChannelChild()
{
}
HttpBackgroundChannelChild::~HttpBackgroundChannelChild()
{
}
+nsresult
+HttpBackgroundChannelChild::Init(HttpChannelChild* aChannelChild)
+{
+ LOG(("HttpBackgroundChannelChild::Init [this=%p httpChannel=%p channelId=%s]\n",
+ this, aChannelChild, nsIDToCString(aChannelChild->ChannelId()).get()));
+ MOZ_ASSERT(NS_IsMainThread());
+ NS_ENSURE_ARG(aChannelChild);
+
+ mChannelChild = aChannelChild;
+
+ CreateBackgroundChannel();
+
+ return NS_OK;
+}
+
+void
+HttpBackgroundChannelChild::OnChannelClosed()
+{
+ LOG(("HttpBackgroundChannelChild::OnChannelClosed [this=%p]\n", this));
+ MOZ_ASSERT(NS_IsMainThread());
+
+ //XXX not sure if this is still necessary
+ // HttpChannelChild is not going to handle any incoming message.
+ mChannelChild = nullptr;
+}
+
+void
+HttpBackgroundChannelChild::NotifyActorFailed()
+{
+ LOG(("HttpBackgroundChannelChild::NotifyActorFailed [this=%p]\n", this));
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (mChannelChild) {
+ RefPtr<HttpChannelChild> channelChild = mChannelChild.forget();
+ channelChild->FailedAsyncOpen(NS_ERROR_UNEXPECTED);
+ }
+}
+
+void
+HttpBackgroundChannelChild::CreateBackgroundChannel()
+{
+ LOG(("HttpBackgroundChannelChild::CreateBackgroundChannel [this=%p]\n", this));
+ MOZ_ASSERT(NS_IsMainThread());
+
+ RefPtr<BackgroundChannelCreateCallback> callback =
+ new BackgroundChannelCreateCallback(this);
+
+ if (NS_WARN_IF(!BackgroundChild::GetOrCreateForCurrentThread(callback))) {
+ MOZ_ASSERT(false);
+ return;
+ }
+}
+
+// PHttpBackgroundChannelChild
IPCResult
HttpBackgroundChannelChild::RecvOnStartRequest(
const nsresult& aChannelStatus,
const nsHttpResponseHead& aResponseHead,
const bool& aUseResponseHead,
const nsHttpHeaderArray& aRequestHeaders,
const bool& aIsFromCache,
const bool& aCacheEntryAvailable,
@@ -89,12 +211,19 @@ IPCResult
HttpBackgroundChannelChild::RecvDivertMessages()
{
return IPC_OK();
}
void
HttpBackgroundChannelChild::ActorDestroy(ActorDestroyReason aWhy)
{
+ LOG(("HttpBackgroundChannelChild::ActorDestroy[this=%p]\n", this));
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (mChannelChild) {
+ RefPtr<HttpChannelChild> channelChild = mChannelChild.forget();
+ channelChild->OnBackgroundChildDestroyed();
+ }
}
} // namespace net
} // namespace mozilla
--- a/netwerk/protocol/http/HttpBackgroundChannelChild.h
+++ b/netwerk/protocol/http/HttpBackgroundChannelChild.h
@@ -4,26 +4,35 @@
/* 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_HttpBackgroundChannelChild_h
#define mozilla_net_HttpBackgroundChannelChild_h
#include "mozilla/net/PHttpBackgroundChannelChild.h"
+#include "nsISupportsImpl.h"
namespace mozilla {
namespace net {
+class HttpChannelChild;
+
class HttpBackgroundChannelChild final : public PHttpBackgroundChannelChild
{
+ friend class BackgroundChannelCreateCallback;
public:
explicit HttpBackgroundChannelChild();
- virtual ~HttpBackgroundChannelChild();
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(HttpBackgroundChannelChild)
+
+ nsresult Init(HttpChannelChild* aChannelChild);
+
+ void OnChannelClosed();
+ void NotifyActorFailed();
protected:
mozilla::ipc::IPCResult
RecvOnStartRequest(const nsresult& aChannelStatus,
const nsHttpResponseHead& aResponseHead,
const bool& aUseResponseHead,
const nsHttpHeaderArray& aRequestHeaders,
const bool& aIsFromCache,
@@ -62,14 +71,22 @@ protected:
mozilla::ipc::IPCResult
RecvFlushedForDiversion() override;
mozilla::ipc::IPCResult
RecvDivertMessages() override;
void
ActorDestroy(ActorDestroyReason aWhy) override;
+
+private:
+ virtual ~HttpBackgroundChannelChild();
+
+ void CreateBackgroundChannel();
+
+ // associated HttpChannelChild for handling the channel events
+ RefPtr<HttpChannelChild> mChannelChild;
};
} // namespace net
} // namespace mozilla
#endif // mozilla_net_HttpBackgroundChannelChild_h
--- a/netwerk/protocol/http/HttpBackgroundChannelParent.cpp
+++ b/netwerk/protocol/http/HttpBackgroundChannelParent.cpp
@@ -1,29 +1,162 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 et tw=80 : */
/* 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/. */
+// HttpLog.h should generally be included first
+#include "HttpLog.h"
+
#include "HttpBackgroundChannelParent.h"
+#include "HttpChannelParent.h"
+#include "mozilla/ipc/BackgroundParent.h"
+#include "mozilla/Unused.h"
+#include "nsIBackgroundChannelRegistrar.h"
+#include "nsNetCID.h"
+#include "nsQueryObject.h"
+#include "nsThreadUtils.h"
+
+using mozilla::dom::ContentParent;
+using mozilla::ipc::AssertIsInMainProcess;
+using mozilla::ipc::AssertIsOnBackgroundThread;
+using mozilla::ipc::BackgroundParent;
using mozilla::ipc::IPCResult;
namespace mozilla {
namespace net {
+/*
+ * Helper class for continuing the AsyncOpen procedure on main thread.
+ */
+class ContinueAsyncOpenRunnable final : public Runnable
+{
+public:
+ ContinueAsyncOpenRunnable(HttpBackgroundChannelParent* aActor,
+ const nsID& aChannelId)
+ : mActor(aActor)
+ , mChannelId(aChannelId)
+ {
+ AssertIsInMainProcess();
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(mActor);
+ }
+
+ NS_IMETHOD Run() override
+ {
+ LOG(("HttpBackgroundChannelParent::ContinueAsyncOpen [this=%p "
+ "channelId=%s]\n", mActor.get(), nsIDToCString(mChannelId).get()));
+ AssertIsInMainProcess();
+ MOZ_ASSERT(NS_IsMainThread());
+
+ nsCOMPtr<nsIBackgroundChannelRegistrar> registrar =
+ do_GetService(NS_BACKGROUNDCHANNELREGISTRAR_CONTRACTID);
+ MOZ_ASSERT(registrar);
+
+ registrar->LinkBackgroundChannel(mChannelId, mActor);
+ return NS_OK;
+ }
+
+private:
+ RefPtr<HttpBackgroundChannelParent> mActor;
+ const nsID mChannelId;
+};
+
HttpBackgroundChannelParent::HttpBackgroundChannelParent()
+ : mIPCOpened(true)
+ , mBackgroundThread(NS_GetCurrentThread())
{
+ AssertIsInMainProcess();
+ AssertIsOnBackgroundThread();
}
HttpBackgroundChannelParent::~HttpBackgroundChannelParent()
{
+ MOZ_ASSERT(!mIPCOpened);
+}
+
+nsresult
+HttpBackgroundChannelParent::Init(const nsID& aChannelId)
+{
+ LOG(("HttpBackgroundChannelParent::Init [this=%p channelId=%s]\n",
+ this, nsIDToCString(aChannelId).get()));
+ AssertIsInMainProcess();
+ AssertIsOnBackgroundThread();
+
+ RefPtr<ContinueAsyncOpenRunnable> runnable =
+ new ContinueAsyncOpenRunnable(this, aChannelId);
+
+ return NS_DispatchToMainThread(runnable);
+}
+
+void
+HttpBackgroundChannelParent::LinkToChannel(HttpChannelParent* aChannelParent)
+{
+ LOG(("HttpBackgroundChannelParent::LinkToChannel [this=%p channel=%p]\n",
+ this, aChannelParent));
+ AssertIsInMainProcess();
+ MOZ_ASSERT(NS_IsMainThread());
+
+ MOZ_DIAGNOSTIC_ASSERT(mIPCOpened);
+ if (!mIPCOpened) {
+ return;
+ }
+
+ mChannelParent = aChannelParent;
+}
+
+void
+HttpBackgroundChannelParent::OnChannelClosed()
+{
+ LOG(("HttpBackgroundChannelParent::OnChannelClosed [this=%p]\n", this));
+ AssertIsInMainProcess();
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (!mIPCOpened) {
+ return;
+ }
+
+ nsresult rv;
+
+ RefPtr<HttpBackgroundChannelParent> self = this;
+ rv = mBackgroundThread->Dispatch(NS_NewRunnableFunction([self]() {
+ LOG(("HttpBackgroundChannelParent::DeleteRunnable [this=%p]\n", self.get()));
+ AssertIsOnBackgroundThread();
+
+ if (!self->mIPCOpened.compareExchange(true, false)) {
+ return;
+ }
+
+ Unused << self->Send__delete__(self);
+ }), NS_DISPATCH_NORMAL);
+
+ Unused << NS_WARN_IF(NS_FAILED(rv));
}
void
HttpBackgroundChannelParent::ActorDestroy(ActorDestroyReason aWhy)
{
+ LOG(("HttpBackgroundChannelParent::ActorDestroy [this=%p]\n", this));
+ AssertIsInMainProcess();
+ AssertIsOnBackgroundThread();
+
+ mIPCOpened = false;
+
+ RefPtr<HttpBackgroundChannelParent> self = this;
+ DebugOnly<nsresult> rv =
+ NS_DispatchToMainThread(NS_NewRunnableFunction([self]() {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ RefPtr<HttpChannelParent> channelParent =
+ self->mChannelParent.forget();
+
+ if (channelParent) {
+ channelParent->OnBackgroundParentDestroyed();
+ }
+ }));
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
}
} // namespace net
} // namespace mozilla
--- a/netwerk/protocol/http/HttpBackgroundChannelParent.h
+++ b/netwerk/protocol/http/HttpBackgroundChannelParent.h
@@ -4,28 +4,51 @@
/* 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_HttpBackgroundChannelParent_h
#define mozilla_net_HttpBackgroundChannelParent_h
#include "mozilla/net/PHttpBackgroundChannelParent.h"
+#include "mozilla/Atomics.h"
+#include "nsID.h"
+#include "nsISupportsImpl.h"
+
+class nsIThread;
namespace mozilla {
namespace net {
+class HttpChannelParent;
+
class HttpBackgroundChannelParent final : public PHttpBackgroundChannelParent
{
public:
explicit HttpBackgroundChannelParent();
- virtual ~HttpBackgroundChannelParent();
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(HttpBackgroundChannelParent)
+
+ nsresult Init(const nsID& aChannelId);
+
+ void LinkToChannel(HttpChannelParent* aChannelParent);
+
+ void OnChannelClosed();
protected:
void
ActorDestroy(ActorDestroyReason aWhy) override;
+
+private:
+ virtual ~HttpBackgroundChannelParent();
+
+ Atomic<bool> mIPCOpened;
+
+ nsCOMPtr<nsIThread> mBackgroundThread;
+
+ // associated HttpChannelParent for generating the channel events
+ RefPtr<HttpChannelParent> mChannelParent;
};
} // namespace net
} // namespace mozilla
#endif // mozilla_net_HttpBackgroundChannelParent_h
--- a/netwerk/protocol/http/HttpBaseChannel.h
+++ b/netwerk/protocol/http/HttpBaseChannel.h
@@ -357,16 +357,21 @@ public: /* Necko internal use only... */
// the new mUploadStream.
void EnsureUploadStreamIsCloneableComplete(nsresult aStatus);
void SetIsTrackingResource()
{
mIsTrackingResource = true;
}
+ const nsID& ChannelId() const
+ {
+ return mChannelId;
+ }
+
protected:
// Handle notifying listener, removing from loadgroup if request failed.
void DoNotifyListener();
virtual void DoNotifyListenerCleanup() = 0;
// drop reference to listener, its callbacks, and the progress sink
void ReleaseListeners();
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -16,16 +16,17 @@
#include "mozilla/dom/TabChild.h"
#include "mozilla/dom/TabGroup.h"
#include "mozilla/ipc/FileDescriptorSetChild.h"
#include "mozilla/ipc/IPCStreamUtils.h"
#include "mozilla/net/NeckoChild.h"
#include "mozilla/net/HttpChannelChild.h"
#include "AltDataOutputStreamChild.h"
+#include "HttpBackgroundChannelChild.h"
#include "nsCOMPtr.h"
#include "nsISupportsPrimitives.h"
#include "nsChannelClassifier.h"
#include "nsGlobalWindow.h"
#include "nsStringStream.h"
#include "nsHttpHandler.h"
#include "nsNetUtil.h"
#include "nsSerializationHelper.h"
@@ -268,16 +269,36 @@ HttpChannelChild::AddIPDLReference()
void
HttpChannelChild::ReleaseIPDLReference()
{
MOZ_ASSERT(mIPCOpen, "Attempt to release nonexistent IPDL reference");
mIPCOpen = false;
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);
+}
+
+void
+HttpChannelChild::OnBackgroundChildDestroyed()
+{
+ LOG(("HttpChannelChild::OnBackgroundChildDestroyed [this=%p]\n", this));
+ MOZ_ASSERT(NS_IsMainThread());
+
+ mBgChild = nullptr;
+}
+
class AssociateApplicationCacheEvent : public ChannelEvent
{
public:
AssociateApplicationCacheEvent(HttpChannelChild* aChild,
const nsCString &aGroupID,
const nsCString &aClientID)
: mChild(aChild)
, groupID(aGroupID)
@@ -1021,16 +1042,18 @@ HttpChannelChild::OnStopRequest(const ns
// so make sure this goes out of scope before then.
AutoEventEnqueuer ensureSerialDispatch(mEventQ);
DoOnStopRequest(this, channelStatus, mListenerContext);
}
ReleaseListeners();
+ CleanupBackgroundChannel();
+
// DocumentChannelCleanup actually nulls out mCacheEntry in the parent, which
// we might need later to open the Alt-Data output stream, so just return here
if (!mPreferredCachedAltDataType.IsEmpty()) {
mKeptAlive = true;
return;
}
if (mLoadFlags & LOAD_DOCUMENT_URI) {
@@ -1252,27 +1275,55 @@ HttpChannelChild::HandleAsyncAbort()
}
void
HttpChannelChild::FailedAsyncOpen(const nsresult& status)
{
LOG(("HttpChannelChild::FailedAsyncOpen [this=%p status=%" PRIx32 "]\n",
this, static_cast<uint32_t>(status)));
+ // Might be called twice in race condition in theory.
+ // (one by RecvFailedAsyncOpen, another by
+ // HttpBackgroundChannelChild::ActorFailed)
+ if (NS_WARN_IF(NS_FAILED(mStatus))) {
+ return;
+ }
+
mStatus = status;
// We're already being called from IPDL, therefore already "async"
HandleAsyncAbort();
if (mIPCOpen) {
TrySendDeletingChannel();
}
}
void
+HttpChannelChild::CleanupBackgroundChannel()
+{
+ LOG(("HttpChannelChild::CleanupBackgroundChannel [this=%p]\n", this));
+
+ if (!mBgChild) {
+ return;
+ }
+
+ RefPtr<HttpBackgroundChannelChild> bgChild = mBgChild.forget();
+
+ if (!NS_IsMainThread()) {
+ SystemGroup::Dispatch(
+ "HttpChannelChild::CleanupBackgroundChannel",
+ TaskCategory::Other,
+ NewRunnableMethod(bgChild, &HttpBackgroundChannelChild::OnChannelClosed));
+ } else {
+ bgChild->OnChannelClosed();
+ }
+}
+
+void
HttpChannelChild::DoNotifyListenerCleanup()
{
LOG(("HttpChannelChild::DoNotifyListenerCleanup [this=%p]\n", this));
if (mInterceptListener) {
mInterceptListener->Cleanup();
mInterceptListener = nullptr;
}
@@ -1806,16 +1857,30 @@ HttpChannelChild::ConnectParent(uint32_t
->GetBrowserOrId(tabChild);
if (!gNeckoChild->
SendPHttpChannelConstructor(this, browser,
IPC::SerializedLoadContext(this),
connectArgs)) {
return NS_ERROR_FAILURE;
}
+ {
+ MOZ_ASSERT(!mBgChild);
+
+ RefPtr<HttpBackgroundChannelChild> bgChild =
+ new HttpBackgroundChannelChild();
+
+ nsresult rv = bgChild->Init(this);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ mBgChild = bgChild.forget();
+ }
+
return NS_OK;
}
NS_IMETHODIMP
HttpChannelChild::CompleteRedirectSetup(nsIStreamListener *listener,
nsISupports *aContext)
{
LOG(("HttpChannelChild::FinishRedirectSetup [this=%p]\n", this));
@@ -2434,16 +2499,28 @@ HttpChannelChild::ContinueAsyncOpen()
PBrowserOrId browser = cc->GetBrowserOrId(tabChild);
if (!gNeckoChild->SendPHttpChannelConstructor(this, browser,
IPC::SerializedLoadContext(this),
openArgs)) {
return NS_ERROR_FAILURE;
}
+ {
+ RefPtr<HttpBackgroundChannelChild> bgChild =
+ new HttpBackgroundChannelChild();
+
+ rv = bgChild->Init(this);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ mBgChild = bgChild.forget();
+ }
+
return NS_OK;
}
//-----------------------------------------------------------------------------
// HttpChannelChild::nsIHttpChannel
//-----------------------------------------------------------------------------
NS_IMETHODIMP
--- a/netwerk/protocol/http/HttpChannelChild.h
+++ b/netwerk/protocol/http/HttpChannelChild.h
@@ -34,16 +34,17 @@
#include "mozilla/net/DNS.h"
class nsIEventTarget;
class nsInputStreamPump;
namespace mozilla {
namespace net {
+class HttpBackgroundChannelChild;
class InterceptedChannelContent;
class InterceptStreamListener;
class HttpChannelChild final : public PHttpChannelChild
, public HttpBaseChannel
, public HttpAsyncAborter<HttpChannelChild>
, public nsICacheInfoChannel
, public nsIProxiedChannel
@@ -110,16 +111,21 @@ public:
MOZ_MUST_USE bool IsSuspended();
mozilla::ipc::IPCResult RecvNotifyTrackingProtectionDisabled() override;
mozilla::ipc::IPCResult RecvNotifyTrackingResource() override;
void FlushedForDiversion();
mozilla::ipc::IPCResult RecvSetClassifierMatchedInfo(const ClassifierInfo& aInfo) override;
+ // Callback while background channel is ready.
+ void OnBackgroundChildReady(HttpBackgroundChannelChild* aBgChild);
+ // Callback while background channel is destroyed.
+ void OnBackgroundChildDestroyed();
+
protected:
mozilla::ipc::IPCResult RecvOnStartRequest(const nsresult& channelStatus,
const nsHttpResponseHead& responseHead,
const bool& useResponseHead,
const nsHttpHeaderArray& requestHeaders,
const bool& isFromCache,
const bool& cacheEntryAvailable,
const uint32_t& cacheExpirationTime,
@@ -300,16 +306,19 @@ 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;
+ RefPtr<HttpBackgroundChannelChild> mBgChild;
+ void CleanupBackgroundChannel();
+
// Needed to call AsyncOpen in FinishInterceptedRedirect
nsCOMPtr<nsIStreamListener> mInterceptedRedirectListener;
nsCOMPtr<nsISupports> mInterceptedRedirectContext;
// Needed to call CleanupRedirectingChannel in FinishInterceptedRedirect
RefPtr<HttpChannelChild> mInterceptingChannel;
// Used to call OverrideWithSynthesizedResponse in FinishInterceptedRedirect
RefPtr<OverrideRunnable> mOverrideRunnable;
@@ -391,16 +400,17 @@ private:
friend class Redirect1Event;
friend class Redirect3Event;
friend class DeleteSelfEvent;
friend class HttpFlushedForDiversionEvent;
friend class CancelEvent;
friend class HttpAsyncAborter<HttpChannelChild>;
friend class InterceptStreamListener;
friend class InterceptedChannelContent;
+ friend class HttpBackgroundChannelChild;
};
// A stream listener interposed between the nsInputStreamPump used for intercepted channels
// and this channel's original listener. This is only used to ensure the original listener
// sees the channel as the request object, and to synthesize OnStatus and OnProgress notifications.
class InterceptStreamListener : public nsIStreamListener
, public nsIProgressEventSink
{
--- a/netwerk/protocol/http/HttpChannelParent.cpp
+++ b/netwerk/protocol/http/HttpChannelParent.cpp
@@ -5,26 +5,31 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// HttpLog.h should generally be included first
#include "HttpLog.h"
#include "mozilla/ipc/FileDescriptorSetParent.h"
#include "mozilla/ipc/IPCStreamUtils.h"
#include "mozilla/net/HttpChannelParent.h"
+#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/TabParent.h"
#include "mozilla/net/NeckoParent.h"
+#include "mozilla/IntegerPrintfMacros.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/Unused.h"
+#include "HttpBackgroundChannelParent.h"
#include "HttpChannelParentListener.h"
#include "nsHttpHandler.h"
+#include "nsNetCID.h"
#include "nsNetUtil.h"
#include "nsISupportsPriority.h"
#include "nsIAuthPromptProvider.h"
+#include "nsIBackgroundChannelRegistrar.h"
#include "nsSerializationHelper.h"
#include "nsISerializable.h"
#include "nsIAssociatedContentSecurity.h"
#include "nsIApplicationCacheService.h"
#include "mozilla/ipc/InputStreamUtils.h"
#include "mozilla/ipc/URIUtils.h"
#include "SerializedLoadContext.h"
#include "nsIAuthInformation.h"
@@ -33,38 +38,37 @@
#include "mozilla/ipc/BackgroundUtils.h"
#include "nsICachingChannel.h"
#include "mozilla/LoadInfo.h"
#include "nsQueryObject.h"
#include "mozilla/BasePrincipal.h"
#include "nsCORSListenerProxy.h"
#include "nsIIPCSerializableInputStream.h"
#include "nsIPrompt.h"
+#include "nsIRedirectChannelRegistrar.h"
#include "nsIWindowWatcher.h"
#include "nsIDocument.h"
#include "nsStreamUtils.h"
#include "nsStringStream.h"
#include "nsIStorageStream.h"
#include "nsQueryObject.h"
#include "nsIURIClassifier.h"
-#include "mozilla/dom/ContentParent.h"
using mozilla::BasePrincipal;
using namespace mozilla::dom;
using namespace mozilla::ipc;
namespace mozilla {
namespace net {
HttpChannelParent::HttpChannelParent(const PBrowserOrId& iframeEmbedding,
nsILoadContext* aLoadContext,
PBOverrideStatus aOverrideStatus)
: mIPCClosed(false)
, mIgnoreProgress(false)
- , mSentRedirect1Begin(false)
, mSentRedirect1BeginFailed(false)
, mReceivedRedirect2Verify(false)
, mPBOverride(aOverrideStatus)
, mLoadContext(aLoadContext)
, mStatus(NS_OK)
, mPendingDiversion(false)
, mDivertingFromChild(false)
, mDivertedOnStartRequest(false)
@@ -89,31 +93,34 @@ HttpChannelParent::HttpChannelParent(con
}
mEventQ = new ChannelEventQueue(static_cast<nsIParentRedirectingChannel*>(this));
}
HttpChannelParent::~HttpChannelParent()
{
LOG(("Destroying HttpChannelParent [this=%p]\n", this));
+ CleanupBackgroundChannel();
}
void
HttpChannelParent::ActorDestroy(ActorDestroyReason why)
{
// We may still have refcount>0 if nsHttpChannel hasn't called OnStopRequest
// yet, but child process has crashed. We must not try to send any more msgs
// to child, or IPDL will kill chrome process, too.
mIPCClosed = true;
// If this is an intercepted channel, we need to make sure that any resources are
// cleaned up to avoid leaks.
if (mParentListener) {
mParentListener->ClearInterceptedChannel();
}
+
+ CleanupBackgroundChannel();
}
bool
HttpChannelParent::Init(const HttpChannelCreationArgs& aArgs)
{
LOG(("HttpChannelParent::Init [this=%p]\n", this));
switch (aArgs.type()) {
case HttpChannelCreationArgs::THttpChannelOpenArgs:
@@ -143,31 +150,119 @@ HttpChannelParent::Init(const HttpChanne
return ConnectChannel(cArgs.registrarId(), cArgs.shouldIntercept());
}
default:
NS_NOTREACHED("unknown open type");
return false;
}
}
+void
+HttpChannelParent::TryInvokeAsyncOpen(nsresult aRv)
+{
+ LOG(("HttpChannelParent::TryInvokeAsyncOpen [this=%p barrier=%u rv=%" PRIx32
+ "]\n", this, mAsyncOpenBarrier, static_cast<uint32_t>(aRv)));
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(mAsyncOpenBarrier > 0);
+
+ if (--mAsyncOpenBarrier > 0 && NS_SUCCEEDED(aRv)) {
+ // Need to wait for more events.
+ return;
+ }
+
+ InvokeAsyncOpen(aRv);
+}
+
+void
+HttpChannelParent::OnBackgroundParentReady(
+ HttpBackgroundChannelParent* aBgParent)
+{
+ LOG(("HttpChannelParent::OnBackgroundParentReady [this=%p bgParent=%p]\n",
+ this, aBgParent));
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(!mBgParent);
+
+ mBgParent = aBgParent;
+
+ mPromise.ResolveIfExists(true, __func__);
+}
+
+void
+HttpChannelParent::OnBackgroundParentDestroyed()
+{
+ LOG(("HttpChannelParent::OnBackgroundParentDestroyed [this=%p]\n", this));
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (!mPromise.IsEmpty()) {
+ MOZ_ASSERT(!mBgParent);
+ mPromise.Reject(NS_ERROR_FAILURE, __func__);
+ return;
+ }
+
+ if (!mBgParent) {
+ return;
+ }
+
+ // Background channel is closed unexpectly, abort PHttpChannel operation.
+ mBgParent = nullptr;
+ Delete();
+}
+
+void
+HttpChannelParent::CleanupBackgroundChannel()
+{
+ LOG(("HttpChannelParent::CleanupBackgroundChannel [this=%p bgParent=%p]\n",
+ this, mBgParent.get()));
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (mBgParent) {
+ RefPtr<HttpBackgroundChannelParent> bgParent = mBgParent.forget();
+ bgParent->OnChannelClosed();
+ return;
+ }
+
+ if (!mPromise.IsEmpty()) {
+ mRequest.DisconnectIfExists();
+ mPromise.Reject(NS_ERROR_FAILURE, __func__);
+
+ if (!mChannel) {
+ return;
+ }
+
+ // This HttpChannelParent might still have a reference from
+ // BackgroundChannelRegistrar.
+ nsCOMPtr<nsIBackgroundChannelRegistrar> registrar =
+ do_GetService(NS_BACKGROUNDCHANNELREGISTRAR_CONTRACTID);
+ MOZ_ASSERT(registrar);
+
+ const nsID& channelId = mChannel->ChannelId();
+
+ registrar->DeleteChannel(channelId);
+
+ mAsyncOpenBarrier = 0;
+
+ }
+}
+
//-----------------------------------------------------------------------------
// HttpChannelParent::nsISupports
//-----------------------------------------------------------------------------
NS_IMPL_ADDREF(HttpChannelParent)
NS_IMPL_RELEASE(HttpChannelParent)
NS_INTERFACE_MAP_BEGIN(HttpChannelParent)
NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
NS_INTERFACE_MAP_ENTRY(nsIProgressEventSink)
NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
NS_INTERFACE_MAP_ENTRY(nsIParentChannel)
NS_INTERFACE_MAP_ENTRY(nsIAuthPromptProvider)
NS_INTERFACE_MAP_ENTRY(nsIParentRedirectingChannel)
NS_INTERFACE_MAP_ENTRY(nsIDeprecationWarner)
+ NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectReadyCallback)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIParentRedirectingChannel)
if (aIID.Equals(NS_GET_IID(HttpChannelParent))) {
foundInterface = static_cast<nsIInterfaceRequestor*>(this);
} else
NS_INTERFACE_MAP_END
//-----------------------------------------------------------------------------
// HttpChannelParent::nsIInterfaceRequestor
@@ -227,16 +322,20 @@ HttpChannelParent::GetInterface(const ns
//-----------------------------------------------------------------------------
// HttpChannelParent::PHttpChannelParent
//-----------------------------------------------------------------------------
void
HttpChannelParent::InvokeAsyncOpen(nsresult rv)
{
+ LOG(("HttpChannelParent::InvokeAsyncOpen [this=%p rv=%" PRIx32 "]\n",
+ this, static_cast<uint32_t>(rv)));
+ MOZ_ASSERT(NS_IsMainThread());
+
if (NS_FAILED(rv)) {
Unused << SendFailedAsyncOpen(rv);
return;
}
nsCOMPtr<nsILoadInfo> loadInfo;
rv = mChannel->GetLoadInfo(getter_AddRefs(loadInfo));
if (NS_FAILED(rv)) {
@@ -265,17 +364,17 @@ public:
: mChannel(aChannel)
, mStatus(aStatus)
{
}
NS_IMETHOD Run()
{
RefPtr<HttpChannelParent> channel = do_QueryObject(mChannel.get());
- channel->InvokeAsyncOpen(mStatus);
+ channel->TryInvokeAsyncOpen(mStatus);
return NS_OK;
}
};
struct UploadStreamClosure {
nsMainThreadPtrHandle<nsIInterfaceRequestor> mChannel;
explicit UploadStreamClosure(const nsMainThreadPtrHandle<nsIInterfaceRequestor>& aChannel)
@@ -427,25 +526,24 @@ HttpChannelParent::DoAsyncOpen( const U
mChannel->SetRequestMethod(nsDependentCString(requestMethod.get()));
if (aCorsPreflightArgs.type() == OptionalCorsPreflightArgs::TCorsPreflightArgs) {
const CorsPreflightArgs& args = aCorsPreflightArgs.get_CorsPreflightArgs();
mChannel->SetCorsPreflightParameters(args.unsafeHeaders());
}
- bool delayAsyncOpen = false;
nsCOMPtr<nsIInputStream> stream = DeserializeIPCStream(uploadStream);
if (stream) {
// FIXME: The fast path of using the existing stream currently only applies to streams
// that have had their entire contents serialized from the child at this point.
// Once bug 1294446 and bug 1294450 are fixed it is worth revisiting this heuristic.
nsCOMPtr<nsIIPCSerializableInputStream> completeStream = do_QueryInterface(stream);
if (!completeStream) {
- delayAsyncOpen = true;
+ ++mAsyncOpenBarrier;
// buffer size matches PChildToParentStream transfer size.
const uint32_t kBufferSize = 32768;
nsCOMPtr<nsIStorageStream> storageStream;
nsresult rv = NS_NewStorageStream(kBufferSize, UINT32_MAX,
getter_AddRefs(storageStream));
if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -581,23 +679,62 @@ HttpChannelParent::DoAsyncOpen( const U
}
nsID requestContextID;
requestContextID.Parse(aRequestContextID.BeginReading());
mChannel->SetRequestContextID(requestContextID);
mSuspendAfterSynthesizeResponse = aSuspendAfterSynthesizeResponse;
- if (!delayAsyncOpen) {
- InvokeAsyncOpen(NS_OK);
+ if (!mBgParent) {
+ MOZ_ASSERT(mPromise.IsEmpty());
+ // Wait for HttpBackgrounChannel to continue the async open procedure.
+ RefPtr<GenericPromise> promise = WaitForBgParent();
+ RefPtr<HttpChannelParent> self = this;
+ promise->Then(AbstractThread::MainThread(), __func__,
+ [self]() {
+ self->mRequest.Complete();
+ self->TryInvokeAsyncOpen(NS_OK);
+ },
+ [self](nsresult aStatus) {
+ self->mRequest.Complete();
+ self->TryInvokeAsyncOpen(aStatus);
+ })
+ ->Track(mRequest);
+ ++mAsyncOpenBarrier;
}
+ TryInvokeAsyncOpen(NS_OK);
+
return true;
}
+already_AddRefed<GenericPromise>
+HttpChannelParent::WaitForBgParent()
+{
+ LOG(("HttpChannelParent::WaitForBgParent [this=%p]\n", this));
+ MOZ_ASSERT(!mBgParent);
+ MOZ_ASSERT(mChannel);
+
+
+ nsCOMPtr<nsIBackgroundChannelRegistrar> registrar =
+ do_GetService(NS_BACKGROUNDCHANNELREGISTRAR_CONTRACTID);
+ MOZ_ASSERT(registrar);
+ registrar->LinkHttpChannel(mChannel->ChannelId(), this);
+
+ if (mBgParent) {
+ already_AddRefed<GenericPromise> promise = mPromise.Ensure(__func__);
+ // resolve promise immediatedly if bg channel is ready.
+ mPromise.Resolve(true, __func__);
+ return promise;
+ }
+
+ return mPromise.Ensure(__func__);;
+}
+
bool
HttpChannelParent::ConnectChannel(const uint32_t& registrarId, const bool& shouldIntercept)
{
nsresult rv;
LOG(("HttpChannelParent::ConnectChannel: Looking for a registered channel "
"[this=%p, id=%" PRIu32 "]\n", this, registrarId));
nsCOMPtr<nsIChannel> channel;
@@ -628,16 +765,33 @@ HttpChannelParent::ConnectChannel(const
if (mPBOverride != kPBOverride_Unset) {
// redirected-to channel may not support PB
nsCOMPtr<nsIPrivateBrowsingChannel> pbChannel = do_QueryObject(mChannel);
if (pbChannel) {
pbChannel->SetPrivate(mPBOverride == kPBOverride_Private ? true : false);
}
}
+ if (!mBgParent) {
+ MOZ_ASSERT(mPromise.IsEmpty());
+ // Waiting for background channel
+ RefPtr<GenericPromise> promise = WaitForBgParent();
+ RefPtr<HttpChannelParent> self = this;
+ promise->Then(AbstractThread::MainThread(), __func__,
+ [self]() {
+ self->mRequest.Complete();
+ },
+ [self](const nsresult& aResult) {
+ NS_ERROR("failed to establish the background channel");
+ self->mRequest.Complete();
+ })
+ ->Track(mRequest);
+ return true;
+ }
+
return true;
}
mozilla::ipc::IPCResult
HttpChannelParent::RecvSetPriority(const int16_t& priority)
{
LOG(("HttpChannelParent::RecvSetPriority [this=%p, priority=%d]\n",
this, priority));
@@ -727,16 +881,17 @@ HttpChannelParent::RecvRedirect2Verify(c
const OptionalCorsPreflightArgs& aCorsPreflightArgs,
const bool& aForceHSTSPriming,
const bool& aMixedContentWouldBlock,
const bool& aChooseAppcache)
{
LOG(("HttpChannelParent::RecvRedirect2Verify [this=%p result=%" PRIx32 "]\n",
this, static_cast<uint32_t>(result)));
nsresult rv;
+
if (NS_SUCCEEDED(result)) {
nsCOMPtr<nsIHttpChannel> newHttpChannel =
do_QueryInterface(mRedirectChannel);
if (newHttpChannel) {
nsCOMPtr<nsIURI> apiRedirectUri = DeserializeURI(aAPIRedirectURI);
if (apiRedirectUri) {
@@ -784,50 +939,90 @@ HttpChannelParent::RecvRedirect2Verify(c
nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel =
do_QueryInterface(newHttpChannel);
if (appCacheChannel) {
appCacheChannel->SetChooseApplicationCache(aChooseAppcache);
}
}
}
+ // Continue the verification procedure if child has veto the redirection.
+ if (NS_FAILED(result)) {
+ ContinueRedirect2Verify(result);
+ return IPC_OK();
+ }
+
+ // Wait for background channel ready on target channel
+ nsCOMPtr<nsIRedirectChannelRegistrar> redirectReg =
+ do_GetService(NS_REDIRECTCHANNELREGISTRAR_CONTRACTID);
+ MOZ_ASSERT(redirectReg);
+
+ nsCOMPtr<nsIParentChannel> parentChannel;
+ rv = redirectReg->GetParentChannel(mRedirectRegistrarId,
+ getter_AddRefs(parentChannel));
+ MOZ_ASSERT(parentChannel);
+ if (!parentChannel) {
+ ContinueRedirect2Verify(rv);
+ return IPC_OK();
+ }
+
+ nsCOMPtr<nsIParentRedirectingChannel> redirectedParent =
+ do_QueryInterface(parentChannel);
+ if (!redirectedParent) {
+ // Continue verification procedure if redirecting to non-Http protocol
+ ContinueRedirect2Verify(result);
+ return IPC_OK();
+ }
+
+ // Ask redirected channel if verification can proceed.
+ redirectedParent->ContinueVerification(this);
+
+ return IPC_OK();
+}
+
+void
+HttpChannelParent::ContinueRedirect2Verify(const nsresult& aResult)
+{
+ LOG(("HttpChannelParent::ContinueRedirect2Verify [this=%p result=%" PRIx32 "]\n",
+ this, static_cast<uint32_t>(aResult)));
+ MOZ_ASSERT(mBgParent);
+
if (!mRedirectCallback) {
// This should according the logic never happen, log the situation.
if (mReceivedRedirect2Verify)
LOG(("RecvRedirect2Verify[%p]: Duplicate fire", this));
if (mSentRedirect1BeginFailed)
LOG(("RecvRedirect2Verify[%p]: Send to child failed", this));
- if (mSentRedirect1Begin && NS_FAILED(result))
+ if ((mRedirectRegistrarId > 0) && NS_FAILED(aResult))
LOG(("RecvRedirect2Verify[%p]: Redirect failed", this));
- if (mSentRedirect1Begin && NS_SUCCEEDED(result))
+ if ((mRedirectRegistrarId > 0) && NS_SUCCEEDED(aResult))
LOG(("RecvRedirect2Verify[%p]: Redirect succeeded", this));
if (!mRedirectChannel)
LOG(("RecvRedirect2Verify[%p]: Missing redirect channel", this));
NS_ERROR("Unexpcted call to HttpChannelParent::RecvRedirect2Verify, "
"mRedirectCallback null");
}
mReceivedRedirect2Verify = true;
if (mRedirectCallback) {
- LOG(("HttpChannelParent::RecvRedirect2Verify call OnRedirectVerifyCallback"
+ LOG(("HttpChannelParent::ContinueRedirect2Verify call OnRedirectVerifyCallback"
" [this=%p result=%" PRIx32 ", mRedirectCallback=%p]\n",
- this, static_cast<uint32_t>(result), mRedirectCallback.get()));
- mRedirectCallback->OnRedirectVerifyCallback(result);
+ this, static_cast<uint32_t>(aResult), mRedirectCallback.get()));
+ mRedirectCallback->OnRedirectVerifyCallback(aResult);
mRedirectCallback = nullptr;
}
-
- return IPC_OK();
}
mozilla::ipc::IPCResult
HttpChannelParent::RecvDocumentChannelCleanup()
{
// From now on only using mAssociatedContentSecurity. Free everything else.
+ CleanupBackgroundChannel(); // Background channel can be closed.
mChannel = nullptr; // Reclaim some memory sooner.
mCacheEntry = nullptr; // Else we'll block other channels reading same URI
return IPC_OK();
}
mozilla::ipc::IPCResult
HttpChannelParent::RecvMarkOfflineCacheEntryAsForeign()
{
@@ -1197,16 +1392,18 @@ HttpChannelParent::OnStartRequest(nsIReq
return rv;
}
}
nsAutoCString altDataType;
chan->GetAlternativeDataType(altDataType);
int64_t altDataLen = chan->GetAltDataLength();
+ MOZ_ASSERT(mIPCClosed || mBgParent || !mPromise.IsEmpty());
+
// !!! We need to lock headers and please don't forget to unlock them !!!
requestHead->Enter();
nsresult rv = NS_OK;
if (mIPCClosed ||
!SendOnStartRequest(channelStatus,
responseHead ? *responseHead : nsHttpResponseHead(),
!!responseHead,
requestHead->Headers(),
@@ -1216,16 +1413,17 @@ HttpChannelParent::OnStartRequest(nsIReq
chan->GetSelfAddr(), chan->GetPeerAddr(),
redirectCount,
cacheKeyValue,
altDataType,
altDataLen))
{
rv = NS_ERROR_UNEXPECTED;
}
+
requestHead->Exit();
return rv;
}
NS_IMETHODIMP
HttpChannelParent::OnStopRequest(nsIRequest *aRequest,
nsISupports *aContext,
nsresult aStatusCode)
@@ -1250,18 +1448,21 @@ HttpChannelParent::OnStopRequest(nsIRequ
mChannel->GetEncodedBodySize(&timing.encodedBodySize);
// decodedBodySize can be computed in the child process so it doesn't need
// to be passed down.
mChannel->GetProtocolVersion(timing.protocolVersion);
mChannel->GetCacheReadStart(&timing.cacheReadStart);
mChannel->GetCacheReadEnd(&timing.cacheReadEnd);
+ MOZ_ASSERT(mIPCClosed || mBgParent || !mPromise.IsEmpty());
+
if (mIPCClosed || !SendOnStopRequest(aStatusCode, timing))
return NS_ERROR_UNEXPECTED;
+
return NS_OK;
}
//-----------------------------------------------------------------------------
// HttpChannelParent::nsIStreamListener
//-----------------------------------------------------------------------------
NS_IMETHODIMP
@@ -1294,20 +1495,23 @@ HttpChannelParent::OnDataAvailable(nsIRe
}
while (aCount) {
nsresult rv = NS_ReadInputStreamToString(aInputStream, data, toRead);
if (NS_FAILED(rv)) {
return rv;
}
+ MOZ_ASSERT(mIPCClosed || mBgParent || !mPromise.IsEmpty());
+
if (mIPCClosed || !SendOnTransportAndData(channelStatus, transportStatus,
aOffset, toRead, data)) {
return NS_ERROR_UNEXPECTED;
}
+ MOZ_ASSERT(mBgParent);
aOffset += toRead;
aCount -= toRead;
toRead = std::min<uint32_t>(aCount, kCopyChunkSize);
}
return NS_OK;
}
@@ -1323,16 +1527,18 @@ HttpChannelParent::OnProgress(nsIRequest
int64_t aProgressMax)
{
// If it indicates this precedes OnDataAvailable, child can derive the value in ODA.
if (mIgnoreProgress) {
mIgnoreProgress = false;
return NS_OK;
}
+ MOZ_ASSERT(mIPCClosed || mBgParent || !mPromise.IsEmpty());
+
// Send OnProgress events to the child for data upload progress notifications
// (i.e. status == NS_NET_STATUS_SENDING_TO) or if the channel has
// LOAD_BACKGROUND set.
if (mIPCClosed || !SendOnProgress(aProgress, aProgressMax)) {
return NS_ERROR_UNEXPECTED;
}
return NS_OK;
@@ -1349,16 +1555,18 @@ HttpChannelParent::OnStatus(nsIRequest *
aStatus == NS_NET_STATUS_READING) {
// The transport status and progress generated by ODA will be coalesced
// into one IPC message. Therefore, we can ignore the next OnProgress event
// since it is generated by ODA as well.
mIgnoreProgress = true;
return NS_OK;
}
+ MOZ_ASSERT(mIPCClosed || mBgParent || !mPromise.IsEmpty());
+
// Otherwise, send to child now
if (mIPCClosed || !SendOnStatus(aStatus)) {
return NS_ERROR_UNEXPECTED;
}
return NS_OK;
}
@@ -1467,26 +1675,60 @@ HttpChannelParent::StartRedirect(uint32_
}
if (!result) {
// Bug 621446 investigation
mSentRedirect1BeginFailed = true;
return NS_BINDING_ABORTED;
}
// Bug 621446 investigation
- mSentRedirect1Begin = true;
+ // Store registrar Id of the new channel to find the redirect
+ // HttpChannelParent later in verification phase.
+ mRedirectRegistrarId = registrarId;
// Result is handled in RecvRedirect2Verify above
mRedirectChannel = newChannel;
mRedirectCallback = callback;
return NS_OK;
}
NS_IMETHODIMP
+HttpChannelParent::ContinueVerification(nsIAsyncVerifyRedirectReadyCallback* aCallback)
+{
+ LOG(("HttpChannelParent::ContinueVerification [this=%p callback=%p]\n",
+ this, aCallback));
+
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aCallback);
+
+ // Continue the verification procedure if background channel is ready.
+ if (mBgParent) {
+ aCallback->ReadyToVerify(NS_OK);
+ return NS_OK;
+ }
+
+ // ConnectChannel must be received before Redirect2Verify.
+ MOZ_ASSERT(!mPromise.IsEmpty());
+
+ // Otherwise, wait for the background channel.
+ RefPtr<GenericPromise> promise = WaitForBgParent();
+ nsCOMPtr<nsIAsyncVerifyRedirectReadyCallback> callback = aCallback;
+ promise->Then(AbstractThread::MainThread(), __func__,
+ [callback]() {
+ callback->ReadyToVerify(NS_OK);
+ },
+ [callback](const nsresult& aResult) {
+ NS_ERROR("failed to establish the background channel");
+ callback->ReadyToVerify(aResult);
+ });
+ return NS_OK;
+}
+
+NS_IMETHODIMP
HttpChannelParent::CompleteRedirect(bool succeeded)
{
LOG(("HttpChannelParent::CompleteRedirect [this=%p succeeded=%d]\n",
this, succeeded));
if (succeeded && !mIPCClosed) {
// TODO: check return value: assume child dead if failed
Unused << SendRedirect3Complete();
@@ -1684,16 +1926,18 @@ HttpChannelParent::StartDiversion()
mDivertListener = converterListener.forget();
}
// Now mParentListener can be diverted to mDivertListener.
DebugOnly<nsresult> rvdbg = mParentListener->DivertTo(mDivertListener);
MOZ_ASSERT(NS_SUCCEEDED(rvdbg));
mDivertListener = nullptr;
+ MOZ_ASSERT(mIPCClosed || mBgParent || !mPromise.IsEmpty());
+
if (NS_WARN_IF(mIPCClosed || !SendFlushedForDiversion())) {
FailDiversion(NS_ERROR_UNEXPECTED);
return;
}
// The listener chain should now be setup; tell HttpChannelChild to divert
// the OnDataAvailables and OnStopRequest to this HttpChannelParent.
if (NS_WARN_IF(mIPCClosed || !SendDivertMessages())) {
@@ -1769,22 +2013,23 @@ HttpChannelParent::NotifyDiversionFailed
mParentListener->OnStartRequest(mChannel, nullptr);
mChannel->ForcePending(false);
}
// If the channel is pending, it will call OnStopRequest itself; otherwise, do
// it here.
if (!isPending) {
mParentListener->OnStopRequest(mChannel, nullptr, aErrorCode);
}
- mParentListener = nullptr;
- mChannel = nullptr;
if (!mIPCClosed) {
Unused << DoSendDeleteSelf();
}
+
+ mParentListener = nullptr;
+ mChannel = nullptr;
}
nsresult
HttpChannelParent::OpenAlternativeOutputStream(const nsACString & type, nsIOutputStream * *_retval)
{
// We need to make sure the child does not call SendDocumentChannelCleanup()
// before opening the altOutputStream, because that clears mCacheEntry.
if (!mCacheEntry) {
@@ -1817,16 +2062,19 @@ HttpChannelParent::UpdateAndSerializeSec
}
}
bool
HttpChannelParent::DoSendDeleteSelf()
{
bool rv = SendDeleteSelf();
mIPCClosed = true;
+
+ CleanupBackgroundChannel();
+
return rv;
}
mozilla::ipc::IPCResult
HttpChannelParent::RecvDeletingChannel()
{
// We need to ensure that the parent channel will not be sending any more IPC
// messages after this, as the child is going away. DoSendDeleteSelf will
@@ -1867,16 +2115,32 @@ HttpChannelParent::ReportSecurityMessage
NS_IMETHODIMP
HttpChannelParent::IssueWarning(uint32_t aWarning, bool aAsError)
{
Unused << SendIssueDeprecationWarning(aWarning, aAsError);
return NS_OK;
}
+//-----------------------------------------------------------------------------
+// nsIAsyncVerifyRedirectReadyCallback
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+HttpChannelParent::ReadyToVerify(nsresult aResult)
+{
+ LOG(("HttpChannelParent::RecvRedirect2Verify [this=%p result=%" PRIx32 "]\n",
+ this, static_cast<uint32_t>(aResult)));
+ MOZ_ASSERT(NS_IsMainThread());
+
+ ContinueRedirect2Verify(aResult);
+
+ return NS_OK;
+}
+
void
HttpChannelParent::DoSendSetPriority(int16_t aValue)
{
if (!mIPCClosed) {
Unused << SendSetPriority(aValue);
}
}
--- a/netwerk/protocol/http/HttpChannelParent.h
+++ b/netwerk/protocol/http/HttpChannelParent.h
@@ -8,16 +8,17 @@
#ifndef mozilla_net_HttpChannelParent_h
#define mozilla_net_HttpChannelParent_h
#include "ADivertableParentChannel.h"
#include "nsHttp.h"
#include "mozilla/net/PHttpChannelParent.h"
#include "mozilla/net/NeckoCommon.h"
#include "mozilla/net/NeckoParent.h"
+#include "mozilla/MozPromise.h"
#include "nsIObserver.h"
#include "nsIParentRedirectingChannel.h"
#include "nsIProgressEventSink.h"
#include "nsHttpChannel.h"
#include "nsIAuthPromptProvider.h"
#include "mozilla/dom/ipc/IdType.h"
#include "nsIDeprecationWarner.h"
@@ -32,44 +33,47 @@ namespace mozilla {
namespace dom{
class TabParent;
class PBrowserOrId;
} // namespace dom
namespace net {
+class HttpBackgroundChannelParent;
class HttpChannelParentListener;
class ChannelEventQueue;
// Note: nsIInterfaceRequestor must be the first base so that do_QueryObject()
// works correctly on this object, as it's needed to compute a void* pointing to
// the beginning of this object.
class HttpChannelParent final : public nsIInterfaceRequestor
, public PHttpChannelParent
, public nsIParentRedirectingChannel
, public nsIProgressEventSink
, public ADivertableParentChannel
, public nsIAuthPromptProvider
, public nsIDeprecationWarner
, public HttpChannelSecurityWarningReporter
+ , public nsIAsyncVerifyRedirectReadyCallback
{
virtual ~HttpChannelParent();
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIREQUESTOBSERVER
NS_DECL_NSISTREAMLISTENER
NS_DECL_NSIPARENTCHANNEL
NS_DECL_NSIPARENTREDIRECTINGCHANNEL
NS_DECL_NSIPROGRESSEVENTSINK
NS_DECL_NSIINTERFACEREQUESTOR
NS_DECL_NSIAUTHPROMPTPROVIDER
NS_DECL_NSIDEPRECATIONWARNER
+ NS_DECL_NSIASYNCVERIFYREDIRECTREADYCALLBACK
NS_DECLARE_STATIC_IID_ACCESSOR(HTTP_CHANNEL_PARENT_IID)
HttpChannelParent(const dom::PBrowserOrId& iframeEmbedding,
nsILoadContext* aLoadContext,
PBOverrideStatus aStatus);
MOZ_MUST_USE bool Init(const HttpChannelCreationArgs& aOpenArgs);
@@ -94,20 +98,27 @@ public:
if (mChannel) {
mChannel->SetApplyConversion(aApplyConversion);
}
}
MOZ_MUST_USE nsresult OpenAlternativeOutputStream(const nsACString & type,
nsIOutputStream * *_retval);
+ void TryInvokeAsyncOpen(nsresult aRv);
void InvokeAsyncOpen(nsresult rv);
// Calls SendSetPriority if mIPCClosed is false.
void DoSendSetPriority(int16_t aValue);
+
+ // Callback while background channel is ready.
+ void OnBackgroundParentReady(HttpBackgroundChannelParent* aBgParent);
+ // Callback while background channel is destroyed.
+ void OnBackgroundParentDestroyed();
+
protected:
// used to connect redirected-to channel in parent with just created
// ChildChannel. Used during redirects.
MOZ_MUST_USE bool ConnectChannel(const uint32_t& channelId,
const bool& shouldIntercept);
MOZ_MUST_USE bool
DoAsyncOpen(const URIParams& uri,
@@ -205,17 +216,22 @@ private:
void DivertOnDataAvailable(const nsCString& data,
const uint64_t& offset,
const uint32_t& count);
void DivertOnStopRequest(const nsresult& statusCode);
void DivertComplete();
void MaybeFlushPendingDiversion();
void ResponseSynthesized();
+ void ContinueRedirect2Verify(const nsresult& aResult);
+ already_AddRefed<GenericPromise> WaitForBgParent();
+ void CleanupBackgroundChannel();
+
+ friend class HttpBackgroundChannelParent;
friend class DivertDataAvailableEvent;
friend class DivertStopRequestEvent;
friend class DivertCompleteEvent;
RefPtr<nsHttpChannel> mChannel;
nsCOMPtr<nsICacheEntry> mCacheEntry;
nsCOMPtr<nsIAssociatedContentSecurity> mAssociatedContentSecurity;
bool mIPCClosed; // PHttpChannel actor has been Closed()
@@ -225,17 +241,16 @@ private:
nsAutoPtr<class nsHttpChannel::OfflineCacheEntryAsForeignMarker> mOfflineForeignMarker;
// OnStatus is always called before OnProgress.
// Set true in OnStatus if next OnProgress can be ignored
// since the information can be recontructed from ODA.
bool mIgnoreProgress : 1;
- bool mSentRedirect1Begin : 1;
bool mSentRedirect1BeginFailed : 1;
bool mReceivedRedirect2Verify : 1;
PBOverrideStatus mPBOverride;
nsCOMPtr<nsILoadContext> mLoadContext;
RefPtr<nsHttpHandler> mHttpHandler;
@@ -261,16 +276,27 @@ private:
// Set if this channel should be suspended after synthesizing a response.
bool mSuspendAfterSynthesizeResponse;
// Set if this channel will synthesize its response.
bool mWillSynthesizeResponse;
dom::TabId mNestedFrameId;
RefPtr<ChannelEventQueue> mEventQ;
+
+ RefPtr<HttpBackgroundChannelParent> mBgParent;
+ // Number of events to wait before actually invoking AsyncOpen on the main
+ // channel. This attribute is main thread only.
+ uint8_t mAsyncOpenBarrier = 1;
+
+ // Corresponding redirect channel registrar Id. 0 means redirection is not started.
+ uint32_t mRedirectRegistrarId = 0;
+
+ MozPromiseHolder<GenericPromise> mPromise;
+ MozPromiseRequestHolder<GenericPromise> mRequest;
};
NS_DEFINE_STATIC_IID_ACCESSOR(HttpChannelParent,
HTTP_CHANNEL_PARENT_IID)
} // namespace net
} // namespace mozilla
--- a/netwerk/protocol/http/moz.build
+++ b/netwerk/protocol/http/moz.build
@@ -3,16 +3,17 @@
# 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/.
with Files('**'):
BUG_COMPONENT = ('Core', 'Networking: HTTP')
XPIDL_SOURCES += [
+ 'nsIBackgroundChannelRegistrar.idl',
'nsIHstsPrimingCallback.idl',
'nsIHttpActivityObserver.idl',
'nsIHttpAuthenticableChannel.idl',
'nsIHttpAuthenticator.idl',
'nsIHttpAuthManager.idl',
'nsIHttpChannel.idl',
'nsIHttpChannelAuthProvider.idl',
'nsIHttpChannelChild.idl',
@@ -58,16 +59,17 @@ SOURCES += [
'ASpdySession.cpp',
'nsHttpAuthCache.cpp',
'nsHttpChannelAuthProvider.cpp', # redefines GetAuthType
]
UNIFIED_SOURCES += [
'AltDataOutputStreamChild.cpp',
'AltDataOutputStreamParent.cpp',
+ 'BackgroundChannelRegistrar.cpp',
'CacheControlParser.cpp',
'ConnectionDiagnostics.cpp',
'HSTSPrimerListener.cpp',
'Http2Compression.cpp',
'Http2Push.cpp',
'Http2Session.cpp',
'Http2Stream.cpp',
'HttpAuthUtils.cpp',
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/nsIBackgroundChannelRegistrar.idl
@@ -0,0 +1,30 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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"
+
+%{ C++
+namespace mozilla {
+namespace net {
+class HttpBackgroundChannelParent;
+class HttpChannelParent;
+}
+}
+%}
+
+[ptr] native HttpChannelParent(mozilla::net::HttpChannelParent);
+[ptr] native HttpBackgroundChannelParent(mozilla::net::HttpBackgroundChannelParent);
+
+[builtinclass, uuid(8acaa9b1-f0c4-4ade-baeb-39b0d4b96e5b)]
+interface nsIBackgroundChannelRegistrar : nsISupports
+{
+ [noscript,notxpcom,nostdcall] void linkHttpChannel(in nsIDRef aKey, in HttpChannelParent aChannel);
+
+ [noscript,notxpcom,nostdcall] void linkBackgroundChannel(in nsIDRef aKey, in HttpBackgroundChannelParent aBgChannel);
+
+ [noscript,notxpcom,nostdcall] void deleteChannel(in nsIDRef aKey);
+
+};