fune/netwerk/protocol/gio/GIOChannelChild.cpp
Nika Layzell 09d88e5fd2 Bug 1749059 - Remove Quantum DOM support from IPDL, r=ipc-reviewers,mccr8
This is no longer necessary as the Quantum DOM project is no longer
happening, and removing support simplifies various components inside of
IPDL.

As some code used the support to get a `nsISerialEventTarget` for an
actor's worker thread, that method was replaced with a method which
instead pulls the nsISerialEventTarget from the MessageChannel and
should work on all actors.

Differential Revision: https://phabricator.services.mozilla.com/D135411
2022-01-25 20:29:46 +00:00

460 lines
14 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=4 sw=2 sts=2 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 "mozilla/net/NeckoChild.h"
#include "GIOChannelChild.h"
#include "nsGIOProtocolHandler.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/BrowserChild.h"
#include "nsContentUtils.h"
#include "nsIBrowserChild.h"
#include "nsStringStream.h"
#include "nsNetUtil.h"
#include "base/compiler_specific.h"
#include "mozilla/ipc/IPCStreamUtils.h"
#include "mozilla/ipc/URIUtils.h"
#include "SerializedLoadContext.h"
#include "mozilla/ipc/BackgroundUtils.h"
#include "nsIURIMutator.h"
#include "nsContentSecurityManager.h"
#include "SerializedLoadContext.h"
#include "mozilla/Logging.h"
using mozilla::dom::ContentChild;
namespace mozilla {
#undef LOG
#define LOG(args) MOZ_LOG(gGIOLog, mozilla::LogLevel::Debug, args)
namespace net {
GIOChannelChild::GIOChannelChild(nsIURI* aUri)
: mEventQ(new ChannelEventQueue(static_cast<nsIChildChannel*>(this))) {
SetURI(aUri);
// We could support thread retargeting, but as long as we're being driven by
// IPDL on the main thread it doesn't buy us anything.
DisallowThreadRetargeting();
}
void GIOChannelChild::AddIPDLReference() {
MOZ_ASSERT(!mIPCOpen, "Attempt to retain more than one IPDL reference");
mIPCOpen = true;
AddRef();
}
void GIOChannelChild::ReleaseIPDLReference() {
MOZ_ASSERT(mIPCOpen, "Attempt to release nonexistent IPDL reference");
mIPCOpen = false;
Release();
}
//-----------------------------------------------------------------------------
// GIOChannelChild::nsISupports
//-----------------------------------------------------------------------------
NS_IMPL_ISUPPORTS_INHERITED(GIOChannelChild, nsBaseChannel, nsIChildChannel)
//-----------------------------------------------------------------------------
NS_IMETHODIMP
GIOChannelChild::AsyncOpen(nsIStreamListener* aListener) {
nsCOMPtr<nsIStreamListener> listener = aListener;
nsresult rv =
nsContentSecurityManager::doContentSecurityCheck(this, listener);
NS_ENSURE_SUCCESS(rv, rv);
LOG(("GIOChannelChild::AsyncOpen [this=%p]\n", this));
NS_ENSURE_TRUE((gNeckoChild), NS_ERROR_FAILURE);
NS_ENSURE_TRUE(
!static_cast<ContentChild*>(gNeckoChild->Manager())->IsShuttingDown(),
NS_ERROR_FAILURE);
NS_ENSURE_ARG_POINTER(listener);
NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);
// Port checked in parent, but duplicate here so we can return with error
// immediately, as we've done since before e10s.
rv = NS_CheckPortSafety(nsBaseChannel::URI()); // Need to disambiguate,
// because in the child ipdl,
// a typedef URI is defined...
if (NS_FAILED(rv)) {
return rv;
}
mozilla::dom::BrowserChild* browserChild = nullptr;
nsCOMPtr<nsIBrowserChild> iBrowserChild;
NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup,
NS_GET_IID(nsIBrowserChild),
getter_AddRefs(iBrowserChild));
GetCallback(iBrowserChild);
if (iBrowserChild) {
browserChild =
static_cast<mozilla::dom::BrowserChild*>(iBrowserChild.get());
}
mListener = listener;
// add ourselves to the load group.
if (mLoadGroup) {
mLoadGroup->AddRequest(this, nullptr);
}
mozilla::ipc::AutoIPCStream autoStream;
autoStream.Serialize(mUploadStream,
static_cast<ContentChild*>(gNeckoChild->Manager()));
uint32_t loadFlags = 0;
GetLoadFlags(&loadFlags);
GIOChannelOpenArgs openArgs;
SerializeURI(nsBaseChannel::URI(), openArgs.uri());
openArgs.startPos() = mStartPos;
openArgs.entityID() = mEntityID;
openArgs.uploadStream() = autoStream.TakeOptionalValue();
openArgs.loadFlags() = loadFlags;
nsCOMPtr<nsILoadInfo> loadInfo = LoadInfo();
rv = mozilla::ipc::LoadInfoToLoadInfoArgs(loadInfo, &openArgs.loadInfo());
NS_ENSURE_SUCCESS(rv, rv);
// This must happen before the constructor message is sent.
SetupNeckoTarget();
gNeckoChild->SendPGIOChannelConstructor(
this, browserChild, IPC::SerializedLoadContext(this), openArgs);
// The socket transport layer in the chrome process now has a logical ref to
// us until OnStopRequest is called.
AddIPDLReference();
mIsPending = true;
mWasOpened = true;
return rv;
}
NS_IMETHODIMP
GIOChannelChild::IsPending(bool* aResult) {
*aResult = mIsPending;
return NS_OK;
}
nsresult GIOChannelChild::OpenContentStream(bool aAsync,
nsIInputStream** aStream,
nsIChannel** aChannel) {
MOZ_CRASH("GIOChannel*Child* should never have OpenContentStream called!");
return NS_OK;
}
mozilla::ipc::IPCResult GIOChannelChild::RecvOnStartRequest(
const nsresult& aChannelStatus, const int64_t& aContentLength,
const nsCString& aContentType, const nsCString& aEntityID,
const URIParams& aURI) {
LOG(("GIOChannelChild::RecvOnStartRequest [this=%p]\n", this));
mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
this, [self = UnsafePtr<GIOChannelChild>(this), aChannelStatus,
aContentLength, aContentType, aEntityID, aURI]() {
self->DoOnStartRequest(aChannelStatus, aContentLength, aContentType,
aEntityID, aURI);
}));
return IPC_OK();
}
void GIOChannelChild::DoOnStartRequest(const nsresult& aChannelStatus,
const int64_t& aContentLength,
const nsCString& aContentType,
const nsCString& aEntityID,
const URIParams& aURI) {
LOG(("GIOChannelChild::DoOnStartRequest [this=%p]\n", this));
if (!mCanceled && NS_SUCCEEDED(mStatus)) {
mStatus = aChannelStatus;
}
mContentLength = aContentLength;
SetContentType(aContentType);
mEntityID = aEntityID;
nsCString spec;
nsCOMPtr<nsIURI> uri = DeserializeURI(aURI);
nsresult rv = uri->GetSpec(spec);
if (NS_SUCCEEDED(rv)) {
// Changes nsBaseChannel::URI()
rv = NS_MutateURI(mURI).SetSpec(spec).Finalize(mURI);
}
if (NS_FAILED(rv)) {
Cancel(rv);
}
AutoEventEnqueuer ensureSerialDispatch(mEventQ);
rv = mListener->OnStartRequest(this);
if (NS_FAILED(rv)) {
Cancel(rv);
}
}
mozilla::ipc::IPCResult GIOChannelChild::RecvOnDataAvailable(
const nsresult& aChannelStatus, const nsCString& aData,
const uint64_t& aOffset, const uint32_t& aCount) {
LOG(("GIOChannelChild::RecvOnDataAvailable [this=%p]\n", this));
mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
this, [self = UnsafePtr<GIOChannelChild>(this), aChannelStatus, aData,
aOffset, aCount]() {
self->DoOnDataAvailable(aChannelStatus, aData, aOffset, aCount);
}));
return IPC_OK();
}
void GIOChannelChild::DoOnDataAvailable(const nsresult& aChannelStatus,
const nsCString& aData,
const uint64_t& aOffset,
const uint32_t& aCount) {
LOG(("GIOChannelChild::DoOnDataAvailable [this=%p]\n", this));
if (!mCanceled && NS_SUCCEEDED(mStatus)) {
mStatus = aChannelStatus;
}
if (mCanceled) {
return;
}
// NOTE: the OnDataAvailable contract requires the client to read all the data
// in the inputstream. This code relies on that ('data' will go away after
// this function). Apparently the previous, non-e10s behavior was to actually
// support only reading part of the data, allowing later calls to read the
// rest.
nsCOMPtr<nsIInputStream> stringStream;
nsresult rv =
NS_NewByteInputStream(getter_AddRefs(stringStream),
Span(aData).To(aCount), NS_ASSIGNMENT_DEPEND);
if (NS_FAILED(rv)) {
Cancel(rv);
return;
}
AutoEventEnqueuer ensureSerialDispatch(mEventQ);
rv = mListener->OnDataAvailable(this, stringStream, aOffset, aCount);
if (NS_FAILED(rv)) {
Cancel(rv);
}
stringStream->Close();
}
mozilla::ipc::IPCResult GIOChannelChild::RecvOnStopRequest(
const nsresult& aChannelStatus) {
LOG(("GIOChannelChild::RecvOnStopRequest [this=%p status=%" PRIx32 "]\n",
this, static_cast<uint32_t>(aChannelStatus)));
mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
this, [self = UnsafePtr<GIOChannelChild>(this), aChannelStatus]() {
self->DoOnStopRequest(aChannelStatus);
}));
return IPC_OK();
}
void GIOChannelChild::DoOnStopRequest(const nsresult& aChannelStatus) {
LOG(("GIOChannelChild::DoOnStopRequest [this=%p status=%" PRIx32 "]\n", this,
static_cast<uint32_t>(aChannelStatus)));
if (!mCanceled) {
mStatus = aChannelStatus;
}
{ // Ensure that all queued ipdl events are dispatched before
// we initiate protocol deletion below.
mIsPending = false;
AutoEventEnqueuer ensureSerialDispatch(mEventQ);
(void)mListener->OnStopRequest(this, aChannelStatus);
mListener = nullptr;
if (mLoadGroup) {
mLoadGroup->RemoveRequest(this, nullptr, aChannelStatus);
}
}
// This calls NeckoChild::DeallocPGIOChannelChild(), which deletes |this| if
// IPDL holds the last reference. Don't rely on |this| existing after here!
Send__delete__(this);
}
mozilla::ipc::IPCResult GIOChannelChild::RecvFailedAsyncOpen(
const nsresult& aStatusCode) {
LOG(("GIOChannelChild::RecvFailedAsyncOpen [this=%p status=%" PRIx32 "]\n",
this, static_cast<uint32_t>(aStatusCode)));
mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
this, [self = UnsafePtr<GIOChannelChild>(this), aStatusCode]() {
self->DoFailedAsyncOpen(aStatusCode);
}));
return IPC_OK();
}
void GIOChannelChild::DoFailedAsyncOpen(const nsresult& aStatusCode) {
LOG(("GIOChannelChild::DoFailedAsyncOpen [this=%p status=%" PRIx32 "]\n",
this, static_cast<uint32_t>(aStatusCode)));
mStatus = aStatusCode;
if (mLoadGroup) {
mLoadGroup->RemoveRequest(this, nullptr, aStatusCode);
}
if (mListener) {
mListener->OnStartRequest(this);
mIsPending = false;
mListener->OnStopRequest(this, aStatusCode);
} else {
mIsPending = false;
}
mListener = nullptr;
if (mIPCOpen) {
Send__delete__(this);
}
}
mozilla::ipc::IPCResult GIOChannelChild::RecvDeleteSelf() {
mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
this,
[self = UnsafePtr<GIOChannelChild>(this)]() { self->DoDeleteSelf(); }));
return IPC_OK();
}
void GIOChannelChild::DoDeleteSelf() {
if (mIPCOpen) {
Send__delete__(this);
}
}
//-----------------------------------------------------------------------------
// GIOChannelChild::nsIResumableChannel
//-----------------------------------------------------------------------------
NS_IMETHODIMP
GIOChannelChild::Cancel(nsresult aStatus) {
LOG(("GIOChannelChild::Cancel [this=%p]\n", this));
if (mCanceled) {
return NS_OK;
}
mCanceled = true;
mStatus = aStatus;
if (mIPCOpen) {
SendCancel(aStatus);
}
return NS_OK;
}
NS_IMETHODIMP
GIOChannelChild::Suspend() {
NS_ENSURE_TRUE(mIPCOpen, NS_ERROR_NOT_AVAILABLE);
LOG(("GIOChannelChild::Suspend [this=%p]\n", this));
// SendSuspend only once, when suspend goes from 0 to 1.
if (!mSuspendCount++) {
SendSuspend();
mSuspendSent = true;
}
mEventQ->Suspend();
return NS_OK;
}
NS_IMETHODIMP
GIOChannelChild::Resume() {
NS_ENSURE_TRUE(mIPCOpen, NS_ERROR_NOT_AVAILABLE);
LOG(("GIOChannelChild::Resume [this=%p]\n", this));
// SendResume only once, when suspend count drops to 0.
if (!--mSuspendCount && mSuspendSent) {
SendResume();
}
mEventQ->Resume();
return NS_OK;
}
//-----------------------------------------------------------------------------
// GIOChannelChild::nsIChildChannel
//-----------------------------------------------------------------------------
NS_IMETHODIMP
GIOChannelChild::ConnectParent(uint32_t aId) {
NS_ENSURE_TRUE((gNeckoChild), NS_ERROR_FAILURE);
NS_ENSURE_TRUE(
!static_cast<ContentChild*>(gNeckoChild->Manager())->IsShuttingDown(),
NS_ERROR_FAILURE);
LOG(("GIOChannelChild::ConnectParent [this=%p]\n", this));
mozilla::dom::BrowserChild* browserChild = nullptr;
nsCOMPtr<nsIBrowserChild> iBrowserChild;
NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup,
NS_GET_IID(nsIBrowserChild),
getter_AddRefs(iBrowserChild));
GetCallback(iBrowserChild);
if (iBrowserChild) {
browserChild =
static_cast<mozilla::dom::BrowserChild*>(iBrowserChild.get());
}
// This must happen before the constructor message is sent.
SetupNeckoTarget();
// The socket transport in the chrome process now holds a logical ref to us
// until OnStopRequest, or we do a redirect, or we hit an IPDL error.
AddIPDLReference();
GIOChannelConnectArgs connectArgs(aId);
if (!gNeckoChild->SendPGIOChannelConstructor(
this, browserChild, IPC::SerializedLoadContext(this), connectArgs)) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
NS_IMETHODIMP
GIOChannelChild::CompleteRedirectSetup(nsIStreamListener* aListener) {
LOG(("GIOChannelChild::CompleteRedirectSetup [this=%p]\n", this));
NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);
mIsPending = true;
mWasOpened = true;
mListener = aListener;
// add ourselves to the load group.
if (mLoadGroup) {
mLoadGroup->AddRequest(this, nullptr);
}
// We already have an open IPDL connection to the parent. If on-modify-request
// listeners or load group observers canceled us, let the parent handle it
// and send it back to us naturally.
return NS_OK;
}
void GIOChannelChild::SetupNeckoTarget() {
if (mNeckoTarget) {
return;
}
nsCOMPtr<nsILoadInfo> loadInfo = LoadInfo();
mNeckoTarget =
nsContentUtils::GetEventTargetByLoadInfo(loadInfo, TaskCategory::Network);
if (!mNeckoTarget) {
return;
}
}
} // namespace net
} // namespace mozilla