/* -*- 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 "mozilla/ipc/BackgroundChild.h"
#include "mozilla/ipc/PBackgroundChild.h"
#include "mozilla/IntegerPrintfMacros.h"
#include "mozilla/Unused.h"
#include "nsSocketTransportService2.h"
using mozilla::ipc::BackgroundChild;
using mozilla::ipc::IPCResult;
namespace mozilla {
namespace net {
// 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(OnSocketThread());
NS_ENSURE_ARG(aChannelChild);
mChannelChild = aChannelChild;
if (NS_WARN_IF(!CreateBackgroundChannel())) {
mChannelChild = nullptr;
return NS_ERROR_FAILURE;
}
return NS_OK;
}
void HttpBackgroundChannelChild::OnChannelClosed() {
LOG(("HttpBackgroundChannelChild::OnChannelClosed [this=%p]\n", this));
MOZ_ASSERT(OnSocketThread());
// HttpChannelChild is not going to handle any incoming message.
mChannelChild = nullptr;
// Remove pending IPC messages as well.
mQueuedRunnables.Clear();
}
void HttpBackgroundChannelChild::OnStartRequestReceived() {
LOG(("HttpBackgroundChannelChild::OnStartRequestReceived [this=%p]\n", this));
MOZ_ASSERT(OnSocketThread());
MOZ_ASSERT(mChannelChild);
MOZ_ASSERT(!mStartReceived); // Should only be called once.
mStartReceived = true;
nsTArray<nsCOMPtr<nsIRunnable>> runnables;
runnables.SwapElements(mQueuedRunnables);
for (auto event : runnables) {
// Note: these runnables call Recv* methods on HttpBackgroundChannelChild
// but not the Process* methods on HttpChannelChild.
event->Run();
}
// Ensure no new message is enqueued.
MOZ_ASSERT(mQueuedRunnables.IsEmpty());
}
bool HttpBackgroundChannelChild::CreateBackgroundChannel() {
LOG(("HttpBackgroundChannelChild::CreateBackgroundChannel [this=%p]\n",
this));
MOZ_ASSERT(OnSocketThread());
MOZ_ASSERT(mChannelChild);
PBackgroundChild* actorChild = BackgroundChild::GetOrCreateForCurrentThread();
if (NS_WARN_IF(!actorChild)) {
return false;
}
const uint64_t channelId = mChannelChild->ChannelId();
if (!actorChild->SendPHttpBackgroundChannelConstructor(this, channelId)) {
return false;
}
// hold extra reference for IPDL
RefPtr<HttpBackgroundChannelChild> self = this;
Unused << self.forget().take();
mChannelChild->OnBackgroundChildReady(this);
return true;
}
bool HttpBackgroundChannelChild::IsWaitingOnStartRequest() {
MOZ_ASSERT(OnSocketThread());
// Need to wait for OnStartRequest if it is sent by
// parent process but not received by content process.
return (mStartSent && !mStartReceived);
}
// PHttpBackgroundChannelChild
IPCResult HttpBackgroundChannelChild::RecvOnStartRequestSent() {
LOG(("HttpBackgroundChannelChild::RecvOnStartRequestSent [this=%p]\n", this));
MOZ_ASSERT(OnSocketThread());
MOZ_ASSERT(!mStartSent); // Should only receive this message once.
mStartSent = true;
return IPC_OK();
}
IPCResult HttpBackgroundChannelChild::RecvOnTransportAndData(
const nsresult& aChannelStatus, const nsresult& aTransportStatus,
const uint64_t& aOffset, const uint32_t& aCount, const nsCString& aData) {
LOG(("HttpBackgroundChannelChild::RecvOnTransportAndData [this=%p]\n", this));
MOZ_ASSERT(OnSocketThread());
if (NS_WARN_IF(!mChannelChild)) {
return IPC_OK();
}
if (IsWaitingOnStartRequest()) {
LOG((" > pending until OnStartRequest [offset=%" PRIu64 " count=%" PRIu32
"]\n",
aOffset, aCount));
mQueuedRunnables.AppendElement(
NewRunnableMethod<const nsresult, const nsresult, const uint64_t,
const uint32_t, const nsCString>(
"HttpBackgroundChannelChild::RecvOnTransportAndData", this,
&HttpBackgroundChannelChild::RecvOnTransportAndData, aChannelStatus,
aTransportStatus, aOffset, aCount, aData));
return IPC_OK();
}
mChannelChild->ProcessOnTransportAndData(aChannelStatus, aTransportStatus,
aOffset, aCount, aData);
return IPC_OK();
}
IPCResult HttpBackgroundChannelChild::RecvOnStopRequest(
const nsresult& aChannelStatus, const ResourceTimingStruct& aTiming,
const TimeStamp& aLastActiveTabOptHit,
const nsHttpHeaderArray& aResponseTrailers) {
LOG(("HttpBackgroundChannelChild::RecvOnStopRequest [this=%p]\n", this));
MOZ_ASSERT(OnSocketThread());
// It's enough to set this from (just before) OnStopRequest notification,
// since we don't need this value sooner than a channel was done loading -
// everything this timestamp affects takes place only after a channel's
// OnStopRequest.
nsHttp::SetLastActiveTabLoadOptimizationHit(aLastActiveTabOptHit);
if (NS_WARN_IF(!mChannelChild)) {
return IPC_OK();
}
if (IsWaitingOnStartRequest()) {
LOG((" > pending until OnStartRequest [status=%" PRIx32 "]\n",
static_cast<uint32_t>(aChannelStatus)));
mQueuedRunnables.AppendElement(
NewRunnableMethod<const nsresult, const ResourceTimingStruct,
const TimeStamp, const nsHttpHeaderArray>(
"HttpBackgroundChannelChild::RecvOnStopRequest", this,
&HttpBackgroundChannelChild::RecvOnStopRequest, aChannelStatus,
aTiming, aLastActiveTabOptHit, aResponseTrailers));
return IPC_OK();
}
mChannelChild->ProcessOnStopRequest(aChannelStatus, aTiming,
aResponseTrailers);
return IPC_OK();
}
IPCResult HttpBackgroundChannelChild::RecvOnProgress(
const int64_t& aProgress, const int64_t& aProgressMax) {
LOG(("HttpBackgroundChannelChild::RecvOnProgress [this=%p progress=%" PRId64
" max=%" PRId64 "]\n",
this, aProgress, aProgressMax));
MOZ_ASSERT(OnSocketThread());
if (NS_WARN_IF(!mChannelChild)) {
return IPC_OK();
}
if (IsWaitingOnStartRequest()) {
LOG((" > pending until OnStartRequest [progress=%" PRId64 " max=%" PRId64
"]\n",
aProgress, aProgressMax));
mQueuedRunnables.AppendElement(
NewRunnableMethod<const int64_t, const int64_t>(
"HttpBackgroundChannelChild::RecvOnProgress", this,
&HttpBackgroundChannelChild::RecvOnProgress, aProgress,
aProgressMax));
return IPC_OK();
}
mChannelChild->ProcessOnProgress(aProgress, aProgressMax);
return IPC_OK();
}
IPCResult HttpBackgroundChannelChild::RecvOnStatus(const nsresult& aStatus) {
LOG(("HttpBackgroundChannelChild::RecvOnStatus [this=%p status=%" PRIx32
"]\n",
this, static_cast<uint32_t>(aStatus)));
MOZ_ASSERT(OnSocketThread());
if (NS_WARN_IF(!mChannelChild)) {
return IPC_OK();
}
if (IsWaitingOnStartRequest()) {
LOG((" > pending until OnStartRequest [status=%" PRIx32 "]\n",
static_cast<uint32_t>(aStatus)));
mQueuedRunnables.AppendElement(NewRunnableMethod<const nsresult>(
"HttpBackgroundChannelChild::RecvOnStatus", this,
&HttpBackgroundChannelChild::RecvOnStatus, aStatus));
return IPC_OK();
}
mChannelChild->ProcessOnStatus(aStatus);
return IPC_OK();
}
IPCResult HttpBackgroundChannelChild::RecvFlushedForDiversion() {
LOG(("HttpBackgroundChannelChild::RecvFlushedForDiversion [this=%p]\n",
this));
MOZ_ASSERT(OnSocketThread());
if (NS_WARN_IF(!mChannelChild)) {
return IPC_OK();
}
if (IsWaitingOnStartRequest()) {
LOG((" > pending until OnStartRequest\n"));
mQueuedRunnables.AppendElement(NewRunnableMethod(
"HttpBackgroundChannelChild::RecvFlushedForDiversion", this,
&HttpBackgroundChannelChild::RecvFlushedForDiversion));
return IPC_OK();
}
mChannelChild->ProcessFlushedForDiversion();
return IPC_OK();
}
IPCResult HttpBackgroundChannelChild::RecvDivertMessages() {
LOG(("HttpBackgroundChannelChild::RecvDivertMessages [this=%p]\n", this));
MOZ_ASSERT(OnSocketThread());
if (NS_WARN_IF(!mChannelChild)) {
return IPC_OK();
}
if (IsWaitingOnStartRequest()) {
LOG((" > pending until OnStartRequest\n"));
mQueuedRunnables.AppendElement(NewRunnableMethod(
"HttpBackgroundChannelChild::RecvDivertMessages", this,
&HttpBackgroundChannelChild::RecvDivertMessages));
return IPC_OK();
}
mChannelChild->ProcessDivertMessages();
return IPC_OK();
}
IPCResult HttpBackgroundChannelChild::RecvNotifyTrackingProtectionDisabled() {
LOG(
("HttpBackgroundChannelChild::RecvNotifyTrackingProtectionDisabled "
"[this=%p]\n",
this));
MOZ_ASSERT(OnSocketThread());
if (NS_WARN_IF(!mChannelChild)) {
return IPC_OK();
}
// NotifyTrackingProtectionDisabled has no order dependency to OnStartRequest.
// It this be handled as soon as possible
mChannelChild->ProcessNotifyTrackingProtectionDisabled();
return IPC_OK();
}
IPCResult HttpBackgroundChannelChild::RecvNotifyTrackingResource() {
LOG(("HttpBackgroundChannelChild::RecvNotifyTrackingResource [this=%p]\n",
this));
MOZ_ASSERT(OnSocketThread());
if (NS_WARN_IF(!mChannelChild)) {
return IPC_OK();
}
// NotifyTrackingResource has no order dependency to OnStartRequest.
// It this be handled as soon as possible
mChannelChild->ProcessNotifyTrackingResource();
return IPC_OK();
}
IPCResult HttpBackgroundChannelChild::RecvSetClassifierMatchedInfo(
const ClassifierInfo& info) {
LOG(("HttpBackgroundChannelChild::RecvSetClassifierMatchedInfo [this=%p]\n",
this));
MOZ_ASSERT(OnSocketThread());
if (NS_WARN_IF(!mChannelChild)) {
return IPC_OK();
}
// SetClassifierMatchedInfo has no order dependency to OnStartRequest.
// It this be handled as soon as possible
mChannelChild->ProcessSetClassifierMatchedInfo(info.list(), info.provider(),
info.fullhash());
return IPC_OK();
}
void HttpBackgroundChannelChild::ActorDestroy(ActorDestroyReason aWhy) {
LOG(("HttpBackgroundChannelChild::ActorDestroy[this=%p]\n", this));
// This function might be called during shutdown phase, so OnSocketThread()
// might return false even on STS thread. Use IsOnCurrentThreadInfallible()
// to get correct information.
MOZ_ASSERT(gSocketTransportService);
MOZ_ASSERT(gSocketTransportService->IsOnCurrentThreadInfallible());
// Ensure all IPC messages received before ActorDestroy can be
// handled correctly. If there is any pending IPC message, destroyed
// mChannelChild until those messages are flushed.
// If background channel is not closed by normal IPDL actor deletion,
// remove the HttpChannelChild reference and notify background channel
// destroyed immediately.
if (aWhy == Deletion && !mQueuedRunnables.IsEmpty()) {
LOG((" > pending until queued messages are flushed\n"));
RefPtr<HttpBackgroundChannelChild> self = this;
mQueuedRunnables.AppendElement(NS_NewRunnableFunction(
"HttpBackgroundChannelChild::ActorDestroy", [self]() {
MOZ_ASSERT(OnSocketThread());
RefPtr<HttpChannelChild> channelChild = self->mChannelChild.forget();
if (channelChild) {
channelChild->OnBackgroundChildDestroyed(self);
}
}));
return;
}
if (mChannelChild) {
RefPtr<HttpChannelChild> channelChild = mChannelChild.forget();
channelChild->OnBackgroundChildDestroyed(this);
}
}
} // namespace net
} // namespace mozilla