forked from mirrors/gecko-dev
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
460 lines
14 KiB
C++
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
|