gecko-dev/netwerk/ipc/NeckoChild.cpp
Nika Layzell 53c6040388 Bug 1467223 - Part 3: Support uncached resources by using a redirectTo-like API for process swaps in necko, r=valentin,mayhemer
With the old process selector service implementation, non-cached loads
would skip the call into the process selector.

This implementation piggybacks atop the existing 'redirectTo' mechanism.
This is unfortunately not the perfect system for catching these loads,
as it doesn't provide an opportunity for performing a final process
switch before redirecting to a non-http channel. In addition, it doesn't
provide indication that a redirect is going to happen, causing
potentially unnecessary process switches.

Not all places where 'redirectTo' is supported use this mechanism. This
process switching mechanism is only checked in situations after
http-on-examine-response.

Potential future changes include:

 1. Moving these checks closer to the real 'OnStartRequest' call (e.g.
    in ContinueProcessNormal + ContinueOnStartRequest3). This would mean
    that loads other than the final load will not cause process swaps.

 2. Adding a callback before a redirect is performed, passing in the new
    channel, and allowing modifications to be made. This would allow
    performing a process swap before redirecting to a non-http(s)
    channel.

Depends on D15609

Differential Revision: https://phabricator.services.mozilla.com/D15610

--HG--
extra : moz-landing-system : lando
2019-01-23 21:19:39 +00:00

439 lines
14 KiB
C++

/* 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/. */
#include "necko-config.h"
#include "nsHttp.h"
#include "mozilla/net/NeckoChild.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/TabChild.h"
#include "mozilla/net/HttpChannelChild.h"
#include "mozilla/net/CookieServiceChild.h"
#include "mozilla/net/WyciwygChannelChild.h"
#include "mozilla/net/FTPChannelChild.h"
#include "mozilla/net/WebSocketChannelChild.h"
#include "mozilla/net/WebSocketEventListenerChild.h"
#include "mozilla/net/DNSRequestChild.h"
#include "mozilla/net/ChannelDiverterChild.h"
#include "mozilla/net/IPCTransportProvider.h"
#include "mozilla/dom/network/TCPSocketChild.h"
#include "mozilla/dom/network/TCPServerSocketChild.h"
#include "mozilla/dom/network/UDPSocketChild.h"
#include "mozilla/net/AltDataOutputStreamChild.h"
#include "mozilla/net/TrackingDummyChannelChild.h"
#ifdef MOZ_WEBRTC
# include "mozilla/net/StunAddrsRequestChild.h"
# include "mozilla/net/WebrtcProxyChannelChild.h"
#endif
#include "SerializedLoadContext.h"
#include "nsGlobalWindow.h"
#include "nsIOService.h"
#include "nsINetworkPredictor.h"
#include "nsINetworkPredictorVerifier.h"
#include "nsINetworkLinkService.h"
#include "nsQueryObject.h"
#include "mozilla/ipc/URIUtils.h"
#include "nsNetUtil.h"
using mozilla::dom::TCPServerSocketChild;
using mozilla::dom::TCPSocketChild;
using mozilla::dom::UDPSocketChild;
namespace mozilla {
namespace net {
PNeckoChild* gNeckoChild = nullptr;
// C++ file contents
NeckoChild::~NeckoChild() {
// Send__delete__(gNeckoChild);
gNeckoChild = nullptr;
}
void NeckoChild::InitNeckoChild() {
MOZ_ASSERT(IsNeckoChild(), "InitNeckoChild called by non-child!");
if (!gNeckoChild) {
mozilla::dom::ContentChild* cpc =
mozilla::dom::ContentChild::GetSingleton();
NS_ASSERTION(cpc, "Content Protocol is NULL!");
if (NS_WARN_IF(cpc->IsShuttingDown())) {
return;
}
gNeckoChild = cpc->SendPNeckoConstructor();
NS_ASSERTION(gNeckoChild, "PNecko Protocol init failed!");
}
}
PHttpChannelChild* NeckoChild::AllocPHttpChannelChild(
const PBrowserOrId& browser, const SerializedLoadContext& loadContext,
const HttpChannelCreationArgs& aOpenArgs) {
// We don't allocate here: instead we always use IPDL constructor that takes
// an existing HttpChildChannel
MOZ_ASSERT_UNREACHABLE(
"AllocPHttpChannelChild should not be called on "
"child");
return nullptr;
}
bool NeckoChild::DeallocPHttpChannelChild(PHttpChannelChild* channel) {
MOZ_ASSERT(IsNeckoChild(), "DeallocPHttpChannelChild called by non-child!");
HttpChannelChild* child = static_cast<HttpChannelChild*>(channel);
child->ReleaseIPDLReference();
return true;
}
PStunAddrsRequestChild* NeckoChild::AllocPStunAddrsRequestChild() {
// We don't allocate here: instead we always use IPDL constructor that takes
// an existing object
MOZ_ASSERT_UNREACHABLE(
"AllocPStunAddrsRequestChild should not be called "
"on child");
return nullptr;
}
bool NeckoChild::DeallocPStunAddrsRequestChild(PStunAddrsRequestChild* aActor) {
#ifdef MOZ_WEBRTC
StunAddrsRequestChild* p = static_cast<StunAddrsRequestChild*>(aActor);
p->ReleaseIPDLReference();
#endif
return true;
}
PWebrtcProxyChannelChild* NeckoChild::AllocPWebrtcProxyChannelChild(
const PBrowserOrId& browser) {
// We don't allocate here: instead we always use IPDL constructor that takes
// an existing object
MOZ_ASSERT_UNREACHABLE(
"AllocPWebrtcProxyChannelChild should not be called on"
" child");
return nullptr;
}
bool NeckoChild::DeallocPWebrtcProxyChannelChild(
PWebrtcProxyChannelChild* aActor) {
#ifdef MOZ_WEBRTC
WebrtcProxyChannelChild* child =
static_cast<WebrtcProxyChannelChild*>(aActor);
child->ReleaseIPDLReference();
#endif
return true;
}
PAltDataOutputStreamChild* NeckoChild::AllocPAltDataOutputStreamChild(
const nsCString& type, const int64_t& predictedSize,
PHttpChannelChild* channel) {
// We don't allocate here: see HttpChannelChild::OpenAlternativeOutputStream()
MOZ_ASSERT_UNREACHABLE("AllocPAltDataOutputStreamChild should not be called");
return nullptr;
}
bool NeckoChild::DeallocPAltDataOutputStreamChild(
PAltDataOutputStreamChild* aActor) {
AltDataOutputStreamChild* child =
static_cast<AltDataOutputStreamChild*>(aActor);
child->ReleaseIPDLReference();
return true;
}
PFTPChannelChild* NeckoChild::AllocPFTPChannelChild(
const PBrowserOrId& aBrowser, const SerializedLoadContext& aSerialized,
const FTPChannelCreationArgs& aOpenArgs) {
// We don't allocate here: see FTPChannelChild::AsyncOpen()
MOZ_CRASH("AllocPFTPChannelChild should not be called");
return nullptr;
}
bool NeckoChild::DeallocPFTPChannelChild(PFTPChannelChild* channel) {
MOZ_ASSERT(IsNeckoChild(), "DeallocPFTPChannelChild called by non-child!");
FTPChannelChild* child = static_cast<FTPChannelChild*>(channel);
child->ReleaseIPDLReference();
return true;
}
PCookieServiceChild* NeckoChild::AllocPCookieServiceChild() {
// We don't allocate here: see nsCookieService::GetSingleton()
MOZ_ASSERT_UNREACHABLE("AllocPCookieServiceChild should not be called");
return nullptr;
}
bool NeckoChild::DeallocPCookieServiceChild(PCookieServiceChild* cs) {
NS_ASSERTION(IsNeckoChild(),
"DeallocPCookieServiceChild called by non-child!");
CookieServiceChild* p = static_cast<CookieServiceChild*>(cs);
p->Release();
return true;
}
PWyciwygChannelChild* NeckoChild::AllocPWyciwygChannelChild() {
// We don't allocate here: see nsWyciwygProtocolHandler::NewChannel2()
MOZ_ASSERT_UNREACHABLE("AllocPWyciwygChannelChild should not be called");
return nullptr;
}
bool NeckoChild::DeallocPWyciwygChannelChild(PWyciwygChannelChild* channel) {
MOZ_ASSERT(IsNeckoChild(),
"DeallocPWyciwygChannelChild called by non-child!");
WyciwygChannelChild* p = static_cast<WyciwygChannelChild*>(channel);
p->ReleaseIPDLReference();
return true;
}
PWebSocketChild* NeckoChild::AllocPWebSocketChild(
const PBrowserOrId& browser, const SerializedLoadContext& aSerialized,
const uint32_t& aSerial) {
MOZ_ASSERT_UNREACHABLE("AllocPWebSocketChild should not be called");
return nullptr;
}
bool NeckoChild::DeallocPWebSocketChild(PWebSocketChild* child) {
WebSocketChannelChild* p = static_cast<WebSocketChannelChild*>(child);
p->ReleaseIPDLReference();
return true;
}
PWebSocketEventListenerChild* NeckoChild::AllocPWebSocketEventListenerChild(
const uint64_t& aInnerWindowID) {
nsCOMPtr<nsIEventTarget> target;
if (nsGlobalWindowInner* win =
nsGlobalWindowInner::GetInnerWindowWithId(aInnerWindowID)) {
target = win->EventTargetFor(TaskCategory::Other);
}
RefPtr<WebSocketEventListenerChild> c =
new WebSocketEventListenerChild(aInnerWindowID, target);
if (target) {
gNeckoChild->SetEventTargetForActor(c, target);
}
return c.forget().take();
}
bool NeckoChild::DeallocPWebSocketEventListenerChild(
PWebSocketEventListenerChild* aActor) {
RefPtr<WebSocketEventListenerChild> c =
dont_AddRef(static_cast<WebSocketEventListenerChild*>(aActor));
MOZ_ASSERT(c);
return true;
}
PDataChannelChild* NeckoChild::AllocPDataChannelChild(
const uint32_t& channelId) {
MOZ_ASSERT_UNREACHABLE("Should never get here");
return nullptr;
}
bool NeckoChild::DeallocPDataChannelChild(PDataChannelChild* child) {
// NB: See DataChannelChild::ActorDestroy.
return true;
}
PFileChannelChild* NeckoChild::AllocPFileChannelChild(
const uint32_t& channelId) {
MOZ_ASSERT_UNREACHABLE("Should never get here");
return nullptr;
}
bool NeckoChild::DeallocPFileChannelChild(PFileChannelChild* child) {
// NB: See FileChannelChild::ActorDestroy.
return true;
}
PSimpleChannelChild* NeckoChild::AllocPSimpleChannelChild(
const uint32_t& channelId) {
MOZ_ASSERT_UNREACHABLE("Should never get here");
return nullptr;
}
bool NeckoChild::DeallocPSimpleChannelChild(PSimpleChannelChild* child) {
// NB: See SimpleChannelChild::ActorDestroy.
return true;
}
PTCPSocketChild* NeckoChild::AllocPTCPSocketChild(const nsString& host,
const uint16_t& port) {
TCPSocketChild* p = new TCPSocketChild(host, port, nullptr);
p->AddIPDLReference();
return p;
}
bool NeckoChild::DeallocPTCPSocketChild(PTCPSocketChild* child) {
TCPSocketChild* p = static_cast<TCPSocketChild*>(child);
p->ReleaseIPDLReference();
return true;
}
PTCPServerSocketChild* NeckoChild::AllocPTCPServerSocketChild(
const uint16_t& aLocalPort, const uint16_t& aBacklog,
const bool& aUseArrayBuffers) {
MOZ_ASSERT_UNREACHABLE("AllocPTCPServerSocket should not be called");
return nullptr;
}
bool NeckoChild::DeallocPTCPServerSocketChild(PTCPServerSocketChild* child) {
TCPServerSocketChild* p = static_cast<TCPServerSocketChild*>(child);
p->ReleaseIPDLReference();
return true;
}
PUDPSocketChild* NeckoChild::AllocPUDPSocketChild(const Principal& aPrincipal,
const nsCString& aFilter) {
MOZ_ASSERT_UNREACHABLE("AllocPUDPSocket should not be called");
return nullptr;
}
bool NeckoChild::DeallocPUDPSocketChild(PUDPSocketChild* child) {
UDPSocketChild* p = static_cast<UDPSocketChild*>(child);
p->ReleaseIPDLReference();
return true;
}
PDNSRequestChild* NeckoChild::AllocPDNSRequestChild(
const nsCString& aHost, const OriginAttributes& aOriginAttributes,
const uint32_t& aFlags) {
// We don't allocate here: instead we always use IPDL constructor that takes
// an existing object
MOZ_ASSERT_UNREACHABLE("AllocPDNSRequestChild should not be called on child");
return nullptr;
}
bool NeckoChild::DeallocPDNSRequestChild(PDNSRequestChild* aChild) {
DNSRequestChild* p = static_cast<DNSRequestChild*>(aChild);
p->ReleaseIPDLReference();
return true;
}
PChannelDiverterChild* NeckoChild::AllocPChannelDiverterChild(
const ChannelDiverterArgs& channel) {
return new ChannelDiverterChild();
;
}
bool NeckoChild::DeallocPChannelDiverterChild(PChannelDiverterChild* child) {
delete static_cast<ChannelDiverterChild*>(child);
return true;
}
PTransportProviderChild* NeckoChild::AllocPTransportProviderChild() {
// This refcount is transferred to the receiver of the message that
// includes the PTransportProviderChild actor.
RefPtr<TransportProviderChild> res = new TransportProviderChild();
return res.forget().take();
}
bool NeckoChild::DeallocPTransportProviderChild(
PTransportProviderChild* aActor) {
return true;
}
mozilla::ipc::IPCResult NeckoChild::RecvAsyncAuthPromptForNestedFrame(
const TabId& aNestedFrameId, const nsCString& aUri, const nsString& aRealm,
const uint64_t& aCallbackId) {
RefPtr<dom::TabChild> tabChild = dom::TabChild::FindTabChild(aNestedFrameId);
if (!tabChild) {
MOZ_CRASH();
return IPC_FAIL_NO_REASON(this);
}
tabChild->SendAsyncAuthPrompt(aUri, aRealm, aCallbackId);
return IPC_OK();
}
/* Predictor Messages */
mozilla::ipc::IPCResult NeckoChild::RecvPredOnPredictPrefetch(
const URIParams& aURI, const uint32_t& aHttpStatus) {
MOZ_ASSERT(NS_IsMainThread(),
"PredictorChild::RecvOnPredictPrefetch "
"off main thread.");
nsCOMPtr<nsIURI> uri = DeserializeURI(aURI);
// Get the current predictor
nsresult rv = NS_OK;
nsCOMPtr<nsINetworkPredictorVerifier> predictor =
do_GetService("@mozilla.org/network/predictor;1", &rv);
NS_ENSURE_SUCCESS(rv, IPC_FAIL_NO_REASON(this));
predictor->OnPredictPrefetch(uri, aHttpStatus);
return IPC_OK();
}
mozilla::ipc::IPCResult NeckoChild::RecvPredOnPredictPreconnect(
const URIParams& aURI) {
MOZ_ASSERT(NS_IsMainThread(),
"PredictorChild::RecvOnPredictPreconnect "
"off main thread.");
nsCOMPtr<nsIURI> uri = DeserializeURI(aURI);
// Get the current predictor
nsresult rv = NS_OK;
nsCOMPtr<nsINetworkPredictorVerifier> predictor =
do_GetService("@mozilla.org/network/predictor;1", &rv);
NS_ENSURE_SUCCESS(rv, IPC_FAIL_NO_REASON(this));
predictor->OnPredictPreconnect(uri);
return IPC_OK();
}
mozilla::ipc::IPCResult NeckoChild::RecvPredOnPredictDNS(
const URIParams& aURI) {
MOZ_ASSERT(NS_IsMainThread(),
"PredictorChild::RecvOnPredictDNS off "
"main thread.");
nsCOMPtr<nsIURI> uri = DeserializeURI(aURI);
// Get the current predictor
nsresult rv = NS_OK;
nsCOMPtr<nsINetworkPredictorVerifier> predictor =
do_GetService("@mozilla.org/network/predictor;1", &rv);
NS_ENSURE_SUCCESS(rv, IPC_FAIL_NO_REASON(this));
predictor->OnPredictDNS(uri);
return IPC_OK();
}
mozilla::ipc::IPCResult NeckoChild::RecvSpeculativeConnectRequest() {
nsCOMPtr<nsIObserverService> obsService = services::GetObserverService();
if (obsService) {
obsService->NotifyObservers(nullptr, "speculative-connect-request",
nullptr);
}
return IPC_OK();
}
mozilla::ipc::IPCResult NeckoChild::RecvNetworkChangeNotification(
nsCString const& type) {
nsCOMPtr<nsIObserverService> obsService = services::GetObserverService();
if (obsService) {
obsService->NotifyObservers(nullptr, NS_NETWORK_LINK_TOPIC,
NS_ConvertUTF8toUTF16(type).get());
}
return IPC_OK();
}
PTrackingDummyChannelChild* NeckoChild::AllocPTrackingDummyChannelChild(
nsIURI* aURI, nsIURI* aTopWindowURI, const nsresult& aTopWindowURIResult,
const OptionalLoadInfoArgs& aLoadInfo) {
return new TrackingDummyChannelChild();
}
bool NeckoChild::DeallocPTrackingDummyChannelChild(
PTrackingDummyChannelChild* aActor) {
delete static_cast<TrackingDummyChannelChild*>(aActor);
return true;
}
} // namespace net
} // namespace mozilla