--- 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 "mozilla/dom/WebAuthnTransactionChild.h"
#include "nsID.h"
#include "nsTraceRefcnt.h"
namespace {
@@ -564,18 +565,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
@@ -874,41 +874,54 @@ BackgroundParentImpl::DeallocPWebAuthnTr
}
net::PHttpBackgroundChannelParent*
BackgroundParentImpl::AllocPHttpBackgroundChannelParent(const uint64_t& 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 uint64_t& 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 and can be called immediately.
+ *
+ * @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
@@ -632,16 +632,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
@@ -262,29 +262,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)
@@ -800,16 +802,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);
@@ -951,16 +954,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 },
@@ -1112,16 +1116,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,97 @@
+/* -*- 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()
+{
+ // BackgroundChannelRegistrar is a main-thread-only object.
+ // All the operations should be run on main thread.
+ // It should be used on chrome process only.
+ MOZ_ASSERT(XRE_IsParentProcess());
+ MOZ_ASSERT(NS_IsMainThread());
+}
+
+BackgroundChannelRegistrar::~BackgroundChannelRegistrar()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+}
+
+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(uint64_t aKey)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ mChannels.Remove(aKey);
+ mBgChannels.Remove(aKey);
+}
+
+void
+BackgroundChannelRegistrar::LinkHttpChannel(
+ uint64_t aKey,
+ HttpChannelParent* aChannel)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aChannel);
+
+ RefPtr<HttpBackgroundChannelParent> bgParent;
+ bool found = mBgChannels.Remove(aKey, getter_AddRefs(bgParent));
+
+ if (!found) {
+ mChannels.Put(aKey, aChannel);
+ return;
+ }
+
+ MOZ_ASSERT(bgParent);
+ NotifyChannelLinked(aChannel, bgParent);
+}
+
+void
+BackgroundChannelRegistrar::LinkBackgroundChannel(
+ uint64_t aKey,
+ HttpBackgroundChannelParent* aBgChannel)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aBgChannel);
+
+ RefPtr<HttpChannelParent> parent;
+ bool found = mChannels.Remove(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,52 @@
+/* -*- 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<nsUint64HashKey, HttpChannelParent>
+ ChannelHashtable;
+ typedef nsRefPtrHashtable<nsUint64HashKey, HttpBackgroundChannelParent>
+ BackgroundChannelHashtable;
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIBACKGROUNDCHANNELREGISTRAR
+
+ explicit BackgroundChannelRegistrar();
+
+private:
+ virtual ~BackgroundChannelRegistrar();
+
+ // A helper function for BackgroundChannelRegistrar itself to callback
+ // HttpChannelParent and HttpBackgroundChannelParent when both objects are
+ // ready. aChannelParent and aBgParent is the pair of HttpChannelParent and
+ // HttpBackgroundChannelParent that should be linked together.
+ 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,153 @@
/* -*- 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* aBgChild)
+ : mBgChild(aBgChild)
+ {
+ 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(aActor);
+ MOZ_ASSERT(mBgChild);
+
+ if (!mBgChild->mChannelChild) {
+ // HttpChannelChild is closed during PBackground creation,
+ // abort the rest of steps.
+ return;
+ }
+
+ const uint64_t channelId = mBgChild->mChannelChild->ChannelId();
+ if (!aActor->SendPHttpBackgroundChannelConstructor(mBgChild,
+ channelId)) {
+ ActorFailed();
+ return;
+ }
+
+ // hold extra reference for IPDL
+ RefPtr<HttpBackgroundChannelChild> child = mBgChild;
+ Unused << child.forget().take();
+}
+
+void
+BackgroundChannelCreateCallback::ActorFailed()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(mBgChild);
+
+ mBgChild->OnBackgroundChannelCreationFailed();
+}
+
+// HttpBackgroundChannelChild
HttpBackgroundChannelChild::HttpBackgroundChannelChild()
{
}
HttpBackgroundChannelChild::~HttpBackgroundChannelChild()
{
}
+nsresult
+HttpBackgroundChannelChild::Init(HttpChannelChild* aChannelChild)
+{
+ LOG(("HttpBackgroundChannelChild::Init [this=%p httpChannel=%p channelId=%"
+ PRIu64 "]\n", this, aChannelChild, aChannelChild->ChannelId()));
+ MOZ_ASSERT(NS_IsMainThread());
+ 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());
+
+ // HttpChannelChild is not going to handle any incoming message.
+ mChannelChild = nullptr;
+}
+
+void
+HttpBackgroundChannelChild::OnBackgroundChannelCreationFailed()
+{
+ LOG(("HttpBackgroundChannelChild::OnBackgroundChannelCreationFailed"
+ " [this=%p]\n", this));
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (mChannelChild) {
+ RefPtr<HttpChannelChild> channelChild = mChannelChild.forget();
+ channelChild->FailedAsyncOpen(NS_ERROR_UNEXPECTED);
+ }
+}
+
+bool
+HttpBackgroundChannelChild::CreateBackgroundChannel()
+{
+ LOG(("HttpBackgroundChannelChild::CreateBackgroundChannel [this=%p]\n", this));
+ MOZ_ASSERT(NS_IsMainThread());
+
+ RefPtr<BackgroundChannelCreateCallback> callback =
+ new BackgroundChannelCreateCallback(this);
+
+ return BackgroundChild::GetOrCreateForCurrentThread(callback);
+}
+
+// PHttpBackgroundChannelChild
IPCResult
HttpBackgroundChannelChild::RecvOnStartRequestSent()
{
return IPC_OK();
}
IPCResult
HttpBackgroundChannelChild::RecvOnTransportAndData(
@@ -68,12 +191,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
@@ -10,22 +10,36 @@
#include "mozilla/net/PHttpBackgroundChannelChild.h"
using mozilla::ipc::IPCResult;
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)
+
+ // Associate this background channel with a HttpChannelChild and
+ // initiate the createion of the PBackground IPC channel.
+ nsresult Init(HttpChannelChild* aChannelChild);
+
+ // Callback while the associated HttpChannelChild is not going to
+ // handle any incoming messages over background channel.
+ void OnChannelClosed();
+
+ // Callback while failed to create PBackground IPC channel.
+ void OnBackgroundChannelCreationFailed();
protected:
IPCResult RecvOnTransportAndData(const nsresult& aChannelStatus,
const nsresult& aTransportStatus,
const uint64_t& aOffset,
const uint32_t& aCount,
const nsCString& aData) override;
@@ -39,14 +53,27 @@ protected:
IPCResult RecvFlushedForDiversion() override;
IPCResult RecvDivertMessages() override;
IPCResult RecvOnStartRequestSent() override;
void ActorDestroy(ActorDestroyReason aWhy) override;
+
+private:
+ virtual ~HttpBackgroundChannelChild();
+
+ // Initiate the creation of the PBckground IPC channel.
+ // Return false if failed.
+ bool CreateBackgroundChannel();
+
+ // 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;
};
} // namespace net
} // namespace mozilla
#endif // mozilla_net_HttpBackgroundChannelChild_h
--- a/netwerk/protocol/http/HttpBackgroundChannelParent.cpp
+++ b/netwerk/protocol/http/HttpBackgroundChannelParent.cpp
@@ -1,29 +1,164 @@
/* -*- 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;
+using mozilla::ipc::IsOnBackgroundThread;
namespace mozilla {
namespace net {
+/*
+ * Helper class for continuing the AsyncOpen procedure on main thread.
+ */
+class ContinueAsyncOpenRunnable final : public Runnable
+{
+public:
+ ContinueAsyncOpenRunnable(HttpBackgroundChannelParent* aActor,
+ const uint64_t& aChannelId)
+ : mActor(aActor)
+ , mChannelId(aChannelId)
+ {
+ AssertIsInMainProcess();
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(mActor);
+ }
+
+ NS_IMETHOD Run() override
+ {
+ LOG(("HttpBackgroundChannelParent::ContinueAsyncOpen [this=%p channelId=%"
+ PRIu64 "]\n", mActor.get(), mChannelId));
+ 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 uint64_t mChannelId;
+};
+
HttpBackgroundChannelParent::HttpBackgroundChannelParent()
+ : mIPCOpened(true)
+ , mBackgroundThread(NS_GetCurrentThread())
{
+ AssertIsInMainProcess();
+ AssertIsOnBackgroundThread();
}
HttpBackgroundChannelParent::~HttpBackgroundChannelParent()
{
+ MOZ_ASSERT(NS_IsMainThread() || IsOnBackgroundThread());
+ MOZ_ASSERT(!mIPCOpened);
+}
+
+nsresult
+HttpBackgroundChannelParent::Init(const uint64_t& aChannelId)
+{
+ LOG(("HttpBackgroundChannelParent::Init [this=%p channelId=%" PRIu64 "]\n",
+ this, aChannelId));
+ 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,27 +4,56 @@
/* 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 nsIEventTarget;
namespace mozilla {
namespace net {
+class HttpChannelParent;
+
class HttpBackgroundChannelParent final : public PHttpBackgroundChannelParent
{
public:
explicit HttpBackgroundChannelParent();
- virtual ~HttpBackgroundChannelParent();
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(HttpBackgroundChannelParent)
+
+ // Try to find associated HttpChannelParent with the same
+ // channel Id.
+ nsresult Init(const uint64_t& aChannelId);
+
+ // Callbacks for BackgroundChannelRegistrar to notify
+ // the associated HttpChannelParent is found.
+ void LinkToChannel(HttpChannelParent* aChannelParent);
+
+ // Callbacks for HttpChannelParent to close the background
+ // IPC channel.
+ void OnChannelClosed();
protected:
void ActorDestroy(ActorDestroyReason aWhy) override;
+
+private:
+ virtual ~HttpBackgroundChannelParent();
+
+ Atomic<bool> mIPCOpened;
+
+ nsCOMPtr<nsIEventTarget> 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
@@ -359,16 +359,21 @@ public: /* Necko internal use only... */
// |EnsureUploadStreamIsCloneableComplete| to main thread.
virtual void OnCopyComplete(nsresult aStatus);
void SetIsTrackingResource()
{
mIsTrackingResource = true;
}
+ const uint64_t& 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 "nsHttpChannel.h"
#include "nsHttpHandler.h"
#include "nsNetUtil.h"
@@ -267,16 +268,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)
@@ -1025,16 +1046,18 @@ HttpChannelChild::OnStopRequest(const ns
// (although we really shouldn't receive any msgs after OnStop),
// so make sure this goes out of scope before then.
AutoEventEnqueuer ensureSerialDispatch(mEventQ);
DoOnStopRequest(this, channelStatus, mListenerContext);
// DoOnStopRequest() calls 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) {
@@ -1247,35 +1270,67 @@ HttpChannelChild::RecvFailedAsyncOpen(co
// We need to have an implementation of this function just so that we can keep
// all references to mCallOnResume of type HttpChannelChild: it's not OK in C++
// to set a member function ptr to a base class function.
void
HttpChannelChild::HandleAsyncAbort()
{
HttpAsyncAborter<HttpChannelChild>::HandleAsyncAbort();
+
+ // Ignore all the messages from background channel after channel aborted.
+ CleanupBackgroundChannel();
}
void
HttpChannelChild::FailedAsyncOpen(const nsresult& status)
{
LOG(("HttpChannelChild::FailedAsyncOpen [this=%p status=%" PRIx32 "]\n",
this, static_cast<uint32_t>(status)));
+ MOZ_ASSERT(NS_IsMainThread());
+
+ // 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;
}
@@ -1830,16 +1885,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));
@@ -2458,16 +2527,36 @@ HttpChannelChild::ContinueAsyncOpen()
PBrowserOrId browser = cc->GetBrowserOrId(tabChild);
if (!gNeckoChild->SendPHttpChannelConstructor(this, browser,
IPC::SerializedLoadContext(this),
openArgs)) {
return NS_ERROR_FAILURE;
}
+ {
+ // Service worker might use the same HttpChannelChild to do async open
+ // twice. Need to disconnect with previous background channel before
+ // creating the new one.
+ if (mBgChild) {
+ RefPtr<HttpBackgroundChannelChild> prevBgChild = mBgChild.forget();
+ prevBgChild->OnChannelClosed();
+ }
+
+ 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
@@ -112,16 +113,21 @@ public:
mozilla::ipc::IPCResult RecvNotifyTrackingProtectionDisabled() override;
mozilla::ipc::IPCResult RecvNotifyTrackingResource() override;
void FlushedForDiversion();
mozilla::ipc::IPCResult RecvSetClassifierMatchedInfo(const ClassifierInfo& aInfo) override;
void OnCopyComplete(nsresult aStatus) 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,
@@ -307,16 +313,22 @@ 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;
+
+ // 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
RefPtr<HttpChannelChild> mInterceptingChannel;
// Used to call OverrideWithSynthesizedResponse in FinishInterceptedRedirect
RefPtr<OverrideRunnable> mOverrideRunnable;
@@ -398,16 +410,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:
@@ -149,31 +156,127 @@ 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());
+
+ // TryInvokeAsyncOpen is called more than we expected.
+ // Assert in nightly build but ignore it in release channel.
+ MOZ_DIAGNOSTIC_ASSERT(mAsyncOpenBarrier > 0);
+ if (NS_WARN_IF(!mAsyncOpenBarrier)) {
+ return;
+ }
+
+ 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);
+
+ registrar->DeleteChannel(mChannel->ChannelId());
+
+ // If mAsyncOpenBarrier is greater than zero, it means AsyncOpen procedure
+ // is still on going. we need to abort AsyncOpen with failure to destroy
+ // PHttpChannel actor.
+ if (mAsyncOpenBarrier) {
+ TryInvokeAsyncOpen(NS_ERROR_FAILURE);
+ }
+ }
+}
+
//-----------------------------------------------------------------------------
// 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
@@ -249,16 +352,20 @@ HttpChannelParent::AsyncOpenFailed(nsres
if (!mIPCClosed) {
Unused << SendFailedAsyncOpen(aRv);
}
}
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)) {
AsyncOpenFailed(rv);
return;
}
nsCOMPtr<nsILoadInfo> loadInfo;
rv = mChannel->GetLoadInfo(getter_AddRefs(loadInfo));
if (NS_FAILED(rv)) {
@@ -287,17 +394,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)
@@ -454,25 +561,25 @@ HttpChannelParent::DoAsyncOpen( const U
httpChannel->SetRequestMethod(nsDependentCString(requestMethod.get()));
if (aCorsPreflightArgs.type() == OptionalCorsPreflightArgs::TCorsPreflightArgs) {
const CorsPreflightArgs& args = aCorsPreflightArgs.get_CorsPreflightArgs();
httpChannel->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;
+ // Wait for completion of async copying IPC upload stream to a local input stream.
+ ++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))) {
@@ -621,21 +728,57 @@ HttpChannelParent::DoAsyncOpen( const U
// cycle reference in fail case and to avoid memory leakage.
mChannel = httpChannel.forget();
mParentListener = parentListener.forget();
mChannel->SetNotificationCallbacks(mParentListener);
mSuspendAfterSynthesizeResponse = aSuspendAfterSynthesizeResponse;
- if (!delayAsyncOpen) {
- InvokeAsyncOpen(NS_OK);
+ MOZ_ASSERT(!mBgParent);
+ MOZ_ASSERT(mPromise.IsEmpty());
+ // Wait for HttpBackgrounChannel to continue the async open procedure.
+ ++mAsyncOpenBarrier;
+ 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);
+
+ 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 true;
+ return mPromise.Ensure(__func__);;
}
bool
HttpChannelParent::ConnectChannel(const uint32_t& registrarId, const bool& shouldIntercept)
{
nsresult rv;
LOG(("HttpChannelParent::ConnectChannel: Looking for a registered channel "
@@ -668,16 +811,30 @@ 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);
}
}
+ MOZ_ASSERT(!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;
}
mozilla::ipc::IPCResult
HttpChannelParent::RecvSetPriority(const int16_t& priority)
{
LOG(("HttpChannelParent::RecvSetPriority [this=%p, priority=%d]\n",
this, priority));
@@ -824,50 +981,123 @@ 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> redirectParentChannel;
+ rv = redirectReg->GetParentChannel(mRedirectRegistrarId,
+ getter_AddRefs(redirectParentChannel));
+ MOZ_ASSERT(redirectParentChannel);
+ if (!redirectParentChannel) {
+ ContinueRedirect2Verify(rv);
+ return IPC_OK();
+ }
+
+ nsCOMPtr<nsIParentRedirectingChannel> redirectedParent =
+ do_QueryInterface(redirectParentChannel);
+ if (!redirectedParent) {
+ // Continue verification procedure if redirecting to non-Http protocol
+ ContinueRedirect2Verify(result);
+ return IPC_OK();
+ }
+
+ // Ask redirected channel if verification can proceed.
+ // ContinueRedirect2Verify will be invoked when redirected channel is ready.
+ redirectedParent->ContinueVerification(this);
+
+ return IPC_OK();
+}
+
+// from nsIParentRedirectingChannel
+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;
+}
+
+void
+HttpChannelParent::ContinueRedirect2Verify(const nsresult& aResult)
+{
+ LOG(("HttpChannelParent::ContinueRedirect2Verify [this=%p result=%" PRIx32 "]\n",
+ this, static_cast<uint32_t>(aResult)));
+
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()
{
@@ -1292,16 +1522,17 @@ HttpChannelParent::OnStopRequest(nsIRequ
// to be passed down.
mChannel->GetProtocolVersion(timing.protocolVersion);
mChannel->GetCacheReadStart(&timing.cacheReadStart);
mChannel->GetCacheReadEnd(&timing.cacheReadEnd);
if (mIPCClosed || !SendOnStopRequest(aStatusCode, timing))
return NS_ERROR_UNEXPECTED;
+
return NS_OK;
}
//-----------------------------------------------------------------------------
// HttpChannelParent::nsIStreamListener
//-----------------------------------------------------------------------------
NS_IMETHODIMP
@@ -1508,17 +1739,19 @@ 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;
}
@@ -1810,22 +2043,26 @@ 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();
}
+
+ // DoSendDeleteSelf will need channel Id to remove the strong reference in
+ // BackgroundChannelRegistrar if channel pairing is aborted.
+ // Thus we need to keep mChannel until DoSendDeleteSelf is done.
+ 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) {
@@ -1858,16 +2095,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
@@ -1908,16 +2148,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,32 @@ public:
if (mChannel) {
mChannel->SetApplyConversion(aApplyConversion);
}
}
MOZ_MUST_USE nsresult OpenAlternativeOutputStream(const nsACString & type,
nsIOutputStream * *_retval);
+ // Callbacks for each asynchronous tasks required in AsyncOpen
+ // procedure, will call InvokeAsyncOpen when all the expected
+ // tasks is finished successfully or when any failure happened.
+ // @see mAsyncOpenBarrier.
+ 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,
@@ -212,18 +228,36 @@ 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();
+ // final step for Redirect2Verify procedure, will be invoked while both
+ // redirecting and redirected channel are ready or any error happened.
+ // OnRedirectVerifyCallback will be invoked for finishing the async
+ // redirect verification procedure.
+ void ContinueRedirect2Verify(const nsresult& aResult);
+
void AsyncOpenFailed(nsresult aRv);
+ // Request to pair with a HttpBackgroundChannelParent with the same channel
+ // id, a promise will be returned so the caller can append callbacks on it.
+ // If called multiple times before mBgParent is available, the same promise
+ // will be returned and the callbacks will be invoked in order.
+ already_AddRefed<GenericPromise> WaitForBgParent();
+
+ // Remove the association with background channel after main-thread IPC
+ // is about to be destroyed or no further event is going to be sent, i.e.,
+ // DocumentChannelCleanup.
+ 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()
@@ -233,17 +267,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;
@@ -269,16 +302,30 @@ 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. For each asynchronous step required before InvokeAsyncOpen, should
+ // increase 1 to mAsyncOpenBarrier and invoke TryInvokeAsyncOpen after
+ // finished. This attribute is main thread only.
+ uint8_t mAsyncOpenBarrier = 0;
+
+ // 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',
@@ -55,16 +56,17 @@ SOURCES += [
'nsHttpChannelAuthProvider.cpp', # redefines GetAuthType
]
UNIFIED_SOURCES += [
'AltDataOutputStreamChild.cpp',
'AltDataOutputStreamParent.cpp',
'AlternateServices.cpp',
'ASpdySession.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,63 @@
+/* -*- 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);
+
+/*
+ * Registrar for pairing HttpChannelParent and HttpBackgroundChannelParent via
+ * channel Id. HttpChannelParent::OnBackgroundParentReady and
+ * HttpBackgroundChannelParent::LinkToChannel will be invoked to notify the
+ * existence of associated channel object.
+ */
+[builtinclass, uuid(8acaa9b1-f0c4-4ade-baeb-39b0d4b96e5b)]
+interface nsIBackgroundChannelRegistrar : nsISupports
+{
+ /*
+ * Link the provided channel parent actor with the given channel Id.
+ * callbacks will be invoked immediately when the HttpBackgroundChannelParent
+ * associated with the same channel Id is found. Store the HttpChannelParent
+ * until a matched linkBackgroundChannel is invoked.
+ *
+ * @param aKey the channel Id
+ * @param aChannel the channel parent actor to be paired
+ */
+ [noscript,notxpcom,nostdcall] void linkHttpChannel(in uint64_t aKey,
+ in HttpChannelParent aChannel);
+
+ /*
+ * Link the provided background channel with the given channel Id.
+ * callbacks will be invoked immediately when the HttpChannelParent associated
+ * with the same channel Id is found. Store the HttpBackgroundChannelParent
+ * until a matched linkHttpChannel is invoked.
+ *
+ * @param aKey the channel Id
+ * @param aBgChannel the background channel to be paired
+ */
+ [noscript,notxpcom,nostdcall] void linkBackgroundChannel(in uint64_t aKey,
+ in HttpBackgroundChannelParent aBgChannel);
+
+ /*
+ * Delete previous stored HttpChannelParent or HttpBackgroundChannelParent
+ * if no need to wait for the paired channel object, e.g. background channel
+ * is destroyed before pairing is completed.
+ *
+ * @param aKey the channel Id
+ */
+ [noscript,notxpcom,nostdcall] void deleteChannel(in uint64_t aKey);
+
+};