fune/netwerk/protocol/ftp/FTPChannelParent.cpp
Francois Marier 309a23afc3 Bug 1482950 - Use the correct 3rdparty check in tracking annotations. r=dimi,Ehsan,mayhemer!,ehsan!
The mIsTrackingResource flag on nsIHttpChannel was split into two separate
flags depending on whether or not the resource is third-party. The correct
flag will be set by the channel classifier. Similarly, a new function was
introduced, GetIsThirdPartyTrackingResource(), for those consumers (like TP)
who only care about third-party trackers.

The existing function, GetIsTracking(), will continue to look at both
first-party and third-party trackers (the behavior since first party
tracking was added to annotations in bug 1476324).

The OverrideTrackingResource() function now allows nsHTMLDocument to
override both mIsFirstPartyTrackingResource and
mIsThirdPartyTrackingResource, but since this function is a little dangerous
and only has a single user, I added an assert to make future callers think
twice about using it to opt out of tracking annotations.

Currently, only the default storage restrictions need to look at first-party
trackers so every other consumer has been moved to
mIsThirdPartyTrackingResource or GetIsThirdPartyTrackingResource().

This effectively reverts the third-party checks added in bug 1476715 and
replaces them with the more complicated check that was added in bug 1108017.
It follows the approach that Ehsan initially suggested in bug 1476715. It
also reverts the changes in the expected values of the tracking annotation
test since these were, in hindsight, a warning about this regression.

Depends on D3722

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

--HG--
extra : moz-landing-system : lando
2018-08-20 23:53:45 +00:00

937 lines
26 KiB
C++

/* -*- 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/. */
#include "mozilla/net/FTPChannelParent.h"
#include "nsStringStream.h"
#include "mozilla/net/ChannelEventQueue.h"
#include "mozilla/dom/TabParent.h"
#include "nsFTPChannel.h"
#include "nsNetCID.h"
#include "nsNetUtil.h"
#include "nsQueryObject.h"
#include "nsFtpProtocolHandler.h"
#include "nsIAuthPrompt.h"
#include "nsIAuthPromptProvider.h"
#include "nsIEncodedChannel.h"
#include "nsIHttpChannelInternal.h"
#include "nsIForcePendingChannel.h"
#include "mozilla/ipc/IPCStreamUtils.h"
#include "mozilla/ipc/URIUtils.h"
#include "mozilla/Unused.h"
#include "SerializedLoadContext.h"
#include "nsIContentPolicy.h"
#include "mozilla/ipc/BackgroundUtils.h"
#include "mozilla/LoadInfo.h"
#include "mozilla/dom/ContentParent.h"
using namespace mozilla::dom;
using namespace mozilla::ipc;
#undef LOG
#define LOG(args) MOZ_LOG(gFTPLog, mozilla::LogLevel::Debug, args)
namespace mozilla {
namespace net {
FTPChannelParent::FTPChannelParent(const PBrowserOrId& aIframeEmbedding,
nsILoadContext* aLoadContext,
PBOverrideStatus aOverrideStatus)
: mIPCClosed(false)
, mLoadContext(aLoadContext)
, mPBOverride(aOverrideStatus)
, mStatus(NS_OK)
, mDivertingFromChild(false)
, mDivertedOnStartRequest(false)
, mSuspendedForDiversion(false)
, mUseUTF8(false)
{
nsIProtocolHandler* handler;
CallGetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "ftp", &handler);
MOZ_ASSERT(handler, "no ftp handler");
if (aIframeEmbedding.type() == PBrowserOrId::TPBrowserParent) {
mTabParent = static_cast<dom::TabParent*>(aIframeEmbedding.get_PBrowserParent());
}
mEventQ = new ChannelEventQueue(static_cast<nsIParentChannel*>(this));
}
FTPChannelParent::~FTPChannelParent()
{
gFtpHandler->Release();
}
void
FTPChannelParent::ActorDestroy(ActorDestroyReason why)
{
// We may still have refcount>0 if the channel hasn't called OnStopRequest
// yet, but we must not send any more msgs to child.
mIPCClosed = true;
}
//-----------------------------------------------------------------------------
// FTPChannelParent::nsISupports
//-----------------------------------------------------------------------------
NS_IMPL_ISUPPORTS(FTPChannelParent,
nsIStreamListener,
nsIParentChannel,
nsIInterfaceRequestor,
nsIRequestObserver,
nsIChannelEventSink,
nsIFTPChannelParentInternal)
//-----------------------------------------------------------------------------
// FTPChannelParent::PFTPChannelParent
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// FTPChannelParent methods
//-----------------------------------------------------------------------------
bool
FTPChannelParent::Init(const FTPChannelCreationArgs& aArgs)
{
switch (aArgs.type()) {
case FTPChannelCreationArgs::TFTPChannelOpenArgs:
{
const FTPChannelOpenArgs& a = aArgs.get_FTPChannelOpenArgs();
return DoAsyncOpen(a.uri(), a.startPos(), a.entityID(), a.uploadStream(),
a.loadInfo(), a.loadFlags());
}
case FTPChannelCreationArgs::TFTPChannelConnectArgs:
{
const FTPChannelConnectArgs& cArgs = aArgs.get_FTPChannelConnectArgs();
return ConnectChannel(cArgs.channelId());
}
default:
MOZ_ASSERT_UNREACHABLE("unknown open type");
return false;
}
}
bool
FTPChannelParent::DoAsyncOpen(const URIParams& aURI,
const uint64_t& aStartPos,
const nsCString& aEntityID,
const OptionalIPCStream& aUploadStream,
const OptionalLoadInfoArgs& aLoadInfoArgs,
const uint32_t& aLoadFlags)
{
nsresult rv;
nsCOMPtr<nsIURI> uri = DeserializeURI(aURI);
if (!uri)
return false;
#ifdef DEBUG
LOG(("FTPChannelParent DoAsyncOpen [this=%p uri=%s]\n",
this, uri->GetSpecOrDefault().get()));
#endif
nsCOMPtr<nsIIOService> ios(do_GetIOService(&rv));
if (NS_FAILED(rv)) {
return SendFailedAsyncOpen(rv);
}
nsCOMPtr<nsILoadInfo> loadInfo;
rv = mozilla::ipc::LoadInfoArgsToLoadInfo(aLoadInfoArgs,
getter_AddRefs(loadInfo));
if (NS_FAILED(rv)) {
return SendFailedAsyncOpen(rv);
}
OriginAttributes attrs;
rv = loadInfo->GetOriginAttributes(&attrs);
if (NS_FAILED(rv)) {
return SendFailedAsyncOpen(rv);
}
nsCOMPtr<nsIChannel> chan;
rv = NS_NewChannelInternal(getter_AddRefs(chan), uri, loadInfo,
nullptr, nullptr, nullptr,
aLoadFlags, ios);
if (NS_FAILED(rv))
return SendFailedAsyncOpen(rv);
mChannel = chan;
// later on mChannel may become an HTTP channel (we'll be redirected to one
// if we're using a proxy), but for now this is safe
nsFtpChannel* ftpChan = static_cast<nsFtpChannel*>(mChannel.get());
if (mPBOverride != kPBOverride_Unset) {
ftpChan->SetPrivate(mPBOverride == kPBOverride_Private ? true : false);
}
rv = ftpChan->SetNotificationCallbacks(this);
if (NS_FAILED(rv))
return SendFailedAsyncOpen(rv);
nsCOMPtr<nsIInputStream> upload = DeserializeIPCStream(aUploadStream);
if (upload) {
// contentType and contentLength are ignored
rv = ftpChan->SetUploadStream(upload, EmptyCString(), 0);
if (NS_FAILED(rv))
return SendFailedAsyncOpen(rv);
}
rv = ftpChan->ResumeAt(aStartPos, aEntityID);
if (NS_FAILED(rv))
return SendFailedAsyncOpen(rv);
if (loadInfo && loadInfo->GetEnforceSecurity()) {
rv = ftpChan->AsyncOpen2(this);
}
else {
rv = ftpChan->AsyncOpen(this, nullptr);
}
if (NS_FAILED(rv))
return SendFailedAsyncOpen(rv);
return true;
}
bool
FTPChannelParent::ConnectChannel(const uint32_t& channelId)
{
nsresult rv;
LOG(("Looking for a registered channel [this=%p, id=%d]", this, channelId));
nsCOMPtr<nsIChannel> channel;
rv = NS_LinkRedirectChannels(channelId, this, getter_AddRefs(channel));
if (NS_SUCCEEDED(rv))
mChannel = channel;
LOG((" found channel %p, rv=%08" PRIx32, mChannel.get(), static_cast<uint32_t>(rv)));
return true;
}
mozilla::ipc::IPCResult
FTPChannelParent::RecvCancel(const nsresult& status)
{
if (mChannel)
mChannel->Cancel(status);
return IPC_OK();
}
mozilla::ipc::IPCResult
FTPChannelParent::RecvSuspend()
{
if (mChannel) {
mChannel->Suspend();
}
return IPC_OK();
}
mozilla::ipc::IPCResult
FTPChannelParent::RecvResume()
{
if (mChannel) {
mChannel->Resume();
}
return IPC_OK();
}
class FTPDivertDataAvailableEvent : public MainThreadChannelEvent
{
public:
FTPDivertDataAvailableEvent(FTPChannelParent* aParent,
const nsCString& data,
const uint64_t& offset,
const uint32_t& count)
: mParent(aParent)
, mData(data)
, mOffset(offset)
, mCount(count)
{
}
void Run() override
{
mParent->DivertOnDataAvailable(mData, mOffset, mCount);
}
private:
FTPChannelParent* mParent;
nsCString mData;
uint64_t mOffset;
uint32_t mCount;
};
mozilla::ipc::IPCResult
FTPChannelParent::RecvDivertOnDataAvailable(const nsCString& data,
const uint64_t& offset,
const uint32_t& count)
{
if (NS_WARN_IF(!mDivertingFromChild)) {
MOZ_ASSERT(mDivertingFromChild,
"Cannot RecvDivertOnDataAvailable if diverting is not set!");
FailDiversion(NS_ERROR_UNEXPECTED);
return IPC_FAIL_NO_REASON(this);
}
// Drop OnDataAvailables if the parent was canceled already.
if (NS_FAILED(mStatus)) {
return IPC_OK();
}
mEventQ->RunOrEnqueue(new FTPDivertDataAvailableEvent(this, data, offset,
count));
return IPC_OK();
}
void
FTPChannelParent::DivertOnDataAvailable(const nsCString& data,
const uint64_t& offset,
const uint32_t& count)
{
LOG(("FTPChannelParent::DivertOnDataAvailable [this=%p]\n", this));
if (NS_WARN_IF(!mDivertingFromChild)) {
MOZ_ASSERT(mDivertingFromChild,
"Cannot DivertOnDataAvailable if diverting is not set!");
FailDiversion(NS_ERROR_UNEXPECTED);
return;
}
// Drop OnDataAvailables if the parent was canceled already.
if (NS_FAILED(mStatus)) {
return;
}
nsCOMPtr<nsIInputStream> stringStream;
nsresult rv = NS_NewByteInputStream(getter_AddRefs(stringStream), data.get(),
count, NS_ASSIGNMENT_DEPEND);
if (NS_FAILED(rv)) {
if (mChannel) {
mChannel->Cancel(rv);
}
mStatus = rv;
return;
}
AutoEventEnqueuer ensureSerialDispatch(mEventQ);
rv = OnDataAvailable(mChannel, nullptr, stringStream, offset, count);
stringStream->Close();
if (NS_FAILED(rv)) {
if (mChannel) {
mChannel->Cancel(rv);
}
mStatus = rv;
}
}
class FTPDivertStopRequestEvent : public MainThreadChannelEvent
{
public:
FTPDivertStopRequestEvent(FTPChannelParent* aParent,
const nsresult& statusCode)
: mParent(aParent)
, mStatusCode(statusCode)
{
}
void Run() override
{
mParent->DivertOnStopRequest(mStatusCode);
}
private:
FTPChannelParent* mParent;
nsresult mStatusCode;
};
mozilla::ipc::IPCResult
FTPChannelParent::RecvDivertOnStopRequest(const nsresult& statusCode)
{
if (NS_WARN_IF(!mDivertingFromChild)) {
MOZ_ASSERT(mDivertingFromChild,
"Cannot RecvDivertOnStopRequest if diverting is not set!");
FailDiversion(NS_ERROR_UNEXPECTED);
return IPC_FAIL_NO_REASON(this);
}
mEventQ->RunOrEnqueue(new FTPDivertStopRequestEvent(this, statusCode));
return IPC_OK();
}
void
FTPChannelParent::DivertOnStopRequest(const nsresult& statusCode)
{
LOG(("FTPChannelParent::DivertOnStopRequest [this=%p]\n", this));
if (NS_WARN_IF(!mDivertingFromChild)) {
MOZ_ASSERT(mDivertingFromChild,
"Cannot DivertOnStopRequest if diverting is not set!");
FailDiversion(NS_ERROR_UNEXPECTED);
return;
}
// Honor the channel's status even if the underlying transaction completed.
nsresult status = NS_FAILED(mStatus) ? mStatus : statusCode;
// Reset fake pending status in case OnStopRequest has already been called.
if (mChannel) {
nsCOMPtr<nsIForcePendingChannel> forcePendingIChan = do_QueryInterface(mChannel);
if (forcePendingIChan) {
forcePendingIChan->ForcePending(false);
}
}
AutoEventEnqueuer ensureSerialDispatch(mEventQ);
OnStopRequest(mChannel, nullptr, status);
}
class FTPDivertCompleteEvent : public MainThreadChannelEvent
{
public:
explicit FTPDivertCompleteEvent(FTPChannelParent* aParent)
: mParent(aParent)
{
}
void Run() override
{
mParent->DivertComplete();
}
private:
FTPChannelParent* mParent;
};
mozilla::ipc::IPCResult
FTPChannelParent::RecvDivertComplete()
{
if (NS_WARN_IF(!mDivertingFromChild)) {
MOZ_ASSERT(mDivertingFromChild,
"Cannot RecvDivertComplete if diverting is not set!");
FailDiversion(NS_ERROR_UNEXPECTED);
return IPC_FAIL_NO_REASON(this);
}
mEventQ->RunOrEnqueue(new FTPDivertCompleteEvent(this));
return IPC_OK();
}
void
FTPChannelParent::DivertComplete()
{
LOG(("FTPChannelParent::DivertComplete [this=%p]\n", this));
if (NS_WARN_IF(!mDivertingFromChild)) {
MOZ_ASSERT(mDivertingFromChild,
"Cannot DivertComplete if diverting is not set!");
FailDiversion(NS_ERROR_UNEXPECTED);
return;
}
nsresult rv = ResumeForDiversion();
if (NS_WARN_IF(NS_FAILED(rv))) {
FailDiversion(NS_ERROR_UNEXPECTED);
}
}
//-----------------------------------------------------------------------------
// FTPChannelParent::nsIRequestObserver
//-----------------------------------------------------------------------------
NS_IMETHODIMP
FTPChannelParent::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
{
LOG(("FTPChannelParent::OnStartRequest [this=%p]\n", this));
if (mDivertingFromChild) {
MOZ_RELEASE_ASSERT(mDivertToListener,
"Cannot divert if listener is unset!");
return mDivertToListener->OnStartRequest(aRequest, aContext);
}
nsCOMPtr<nsIChannel> chan = do_QueryInterface(aRequest);
MOZ_ASSERT(chan);
NS_ENSURE_TRUE(chan, NS_ERROR_UNEXPECTED);
// Send down any permissions which are relevant to this URL if we are
// performing a document load.
if (!mIPCClosed) {
PContentParent* pcp = Manager()->Manager();
MOZ_ASSERT(pcp, "We should have a manager if our IPC isn't closed");
DebugOnly<nsresult> rv =
static_cast<ContentParent*>(pcp)->AboutToLoadHttpFtpWyciwygDocumentForChild(chan);
MOZ_ASSERT(NS_SUCCEEDED(rv));
}
int64_t contentLength;
chan->GetContentLength(&contentLength);
nsCString contentType;
chan->GetContentType(contentType);
nsCString entityID;
nsCOMPtr<nsIResumableChannel> resChan = do_QueryInterface(aRequest);
MOZ_ASSERT(resChan); // both FTP and HTTP should implement nsIResumableChannel
if (resChan) {
resChan->GetEntityID(entityID);
}
PRTime lastModified = 0;
nsCOMPtr<nsIFTPChannel> ftpChan = do_QueryInterface(aRequest);
if (ftpChan) {
ftpChan->GetLastModifiedTime(&lastModified);
}
nsCOMPtr<nsIHttpChannelInternal> httpChan = do_QueryInterface(aRequest);
if (httpChan) {
Unused << httpChan->GetLastModifiedTime(&lastModified);
}
URIParams uriparam;
nsCOMPtr<nsIURI> uri;
chan->GetURI(getter_AddRefs(uri));
SerializeURI(uri, uriparam);
if (mIPCClosed || !SendOnStartRequest(mStatus, contentLength, contentType,
lastModified, entityID, uriparam)) {
return NS_ERROR_UNEXPECTED;
}
return NS_OK;
}
NS_IMETHODIMP
FTPChannelParent::OnStopRequest(nsIRequest* aRequest,
nsISupports* aContext,
nsresult aStatusCode)
{
LOG(("FTPChannelParent::OnStopRequest: [this=%p status=%" PRIu32 "]\n",
this, static_cast<uint32_t>(aStatusCode)));
if (mDivertingFromChild) {
MOZ_RELEASE_ASSERT(mDivertToListener,
"Cannot divert if listener is unset!");
return mDivertToListener->OnStopRequest(aRequest, aContext, aStatusCode);
}
if (mIPCClosed || !SendOnStopRequest(aStatusCode, mErrorMsg, mUseUTF8)) {
return NS_ERROR_UNEXPECTED;
}
return NS_OK;
}
//-----------------------------------------------------------------------------
// FTPChannelParent::nsIStreamListener
//-----------------------------------------------------------------------------
NS_IMETHODIMP
FTPChannelParent::OnDataAvailable(nsIRequest* aRequest,
nsISupports* aContext,
nsIInputStream* aInputStream,
uint64_t aOffset,
uint32_t aCount)
{
LOG(("FTPChannelParent::OnDataAvailable [this=%p]\n", this));
if (mDivertingFromChild) {
MOZ_RELEASE_ASSERT(mDivertToListener,
"Cannot divert if listener is unset!");
return mDivertToListener->OnDataAvailable(aRequest, aContext, aInputStream,
aOffset, aCount);
}
nsCString data;
nsresult rv = NS_ReadInputStreamToString(aInputStream, data, aCount);
if (NS_FAILED(rv))
return rv;
if (mIPCClosed || !SendOnDataAvailable(mStatus, data, aOffset, aCount))
return NS_ERROR_UNEXPECTED;
return NS_OK;
}
//-----------------------------------------------------------------------------
// FTPChannelParent::nsIParentChannel
//-----------------------------------------------------------------------------
NS_IMETHODIMP
FTPChannelParent::SetParentListener(HttpChannelParentListener* aListener)
{
// Do not need ptr to HttpChannelParentListener.
return NS_OK;
}
NS_IMETHODIMP
FTPChannelParent::NotifyTrackingProtectionDisabled()
{
// One day, this should probably be filled in.
return NS_OK;
}
NS_IMETHODIMP
FTPChannelParent::NotifyTrackingResource(bool aIsThirdParty)
{
// One day, this should probably be filled in.
return NS_OK;
}
NS_IMETHODIMP
FTPChannelParent::SetClassifierMatchedInfo(const nsACString& aList,
const nsACString& aProvider,
const nsACString& aFullHash)
{
// One day, this should probably be filled in.
return NS_OK;
}
NS_IMETHODIMP
FTPChannelParent::Delete()
{
if (mIPCClosed || !SendDeleteSelf())
return NS_ERROR_UNEXPECTED;
return NS_OK;
}
//-----------------------------------------------------------------------------
// FTPChannelParent::nsIInterfaceRequestor
//-----------------------------------------------------------------------------
NS_IMETHODIMP
FTPChannelParent::GetInterface(const nsIID& uuid, void** result)
{
if (uuid.Equals(NS_GET_IID(nsIAuthPromptProvider)) ||
uuid.Equals(NS_GET_IID(nsISecureBrowserUI))) {
if (mTabParent) {
return mTabParent->QueryInterface(uuid, result);
}
} else if (uuid.Equals(NS_GET_IID(nsIAuthPrompt)) ||
uuid.Equals(NS_GET_IID(nsIAuthPrompt2))) {
nsCOMPtr<nsIAuthPromptProvider> provider(do_QueryObject(mTabParent));
if (provider) {
return provider->GetAuthPrompt(nsIAuthPromptProvider::PROMPT_NORMAL,
uuid,
result);
}
}
// Only support nsILoadContext if child channel's callbacks did too
if (uuid.Equals(NS_GET_IID(nsILoadContext)) && mLoadContext) {
nsCOMPtr<nsILoadContext> copy = mLoadContext;
copy.forget(result);
return NS_OK;
}
return QueryInterface(uuid, result);
}
nsresult
FTPChannelParent::ResumeChannelInternalIfPossible()
{
nsCOMPtr<nsIChannelWithDivertableParentListener> chan =
do_QueryInterface(mChannel);
if (chan) {
return chan->ResumeInternal();
}
return mChannel->Resume();
}
//-----------------------------------------------------------------------------
// FTPChannelParent::ADivertableParentChannel
//-----------------------------------------------------------------------------
nsresult
FTPChannelParent::SuspendForDiversion()
{
MOZ_ASSERT(mChannel);
if (NS_WARN_IF(mDivertingFromChild)) {
MOZ_ASSERT(!mDivertingFromChild, "Already suspended for diversion!");
return NS_ERROR_UNEXPECTED;
}
// MessageDiversionStarted call will suspend mEventQ as many times as the
// channel has been suspended, so that channel and this queue are in sync.
nsCOMPtr<nsIChannelWithDivertableParentListener> chan =
do_QueryInterface(mChannel);
if (chan) {
chan->MessageDiversionStarted(this);
}
// We need to suspend only nsHttp/FTPChannel (i.e. we should not suspend
// mEventQ). Therefore we call mChannel->SuspendInternal() and not
// mChannel->Suspend().
// We are suspending only nsHttp/FTPChannel here because we want to stop
// OnDataAvailable until diversion is over. At the same time we should
// send the diverted OnDataAvailable-s to the listeners and not queue them
// in mEventQ.
// Try suspending the channel. Allow it to fail, since OnStopRequest may have
// been called and thus the channel may not be pending.
nsresult rv;
if (chan) {
rv = chan->SuspendInternal();
} else {
rv = mChannel->Suspend();
}
MOZ_ASSERT(NS_SUCCEEDED(rv) || rv == NS_ERROR_NOT_AVAILABLE);
mSuspendedForDiversion = NS_SUCCEEDED(rv);
// Once this is set, no more OnStart/OnData/OnStop callbacks should be sent
// to the child.
mDivertingFromChild = true;
return NS_OK;
}
/* private, supporting function for ADivertableParentChannel */
nsresult
FTPChannelParent::ResumeForDiversion()
{
MOZ_ASSERT(mChannel);
MOZ_ASSERT(mDivertToListener);
if (NS_WARN_IF(!mDivertingFromChild)) {
MOZ_ASSERT(mDivertingFromChild,
"Cannot ResumeForDiversion if not diverting!");
return NS_ERROR_UNEXPECTED;
}
nsCOMPtr<nsIChannelWithDivertableParentListener> chan =
do_QueryInterface(mChannel);
if (chan) {
chan->MessageDiversionStop();
}
if (mSuspendedForDiversion) {
nsresult rv = ResumeChannelInternalIfPossible();
if (NS_WARN_IF(NS_FAILED(rv))) {
FailDiversion(NS_ERROR_UNEXPECTED, true);
return rv;
}
mSuspendedForDiversion = false;
}
// Delete() will tear down IPDL, but ref from underlying nsFTPChannel will
// keep us alive if there's more data to be delivered to listener.
if (NS_WARN_IF(NS_FAILED(Delete()))) {
FailDiversion(NS_ERROR_UNEXPECTED);
return NS_ERROR_UNEXPECTED;
}
return NS_OK;
}
nsresult
FTPChannelParent::SuspendMessageDiversion()
{
// This only need to suspend message queue.
mEventQ->Suspend();
return NS_OK;
}
nsresult
FTPChannelParent::ResumeMessageDiversion()
{
// This only need to resumes message queue.
mEventQ->Resume();
return NS_OK;
}
nsresult
FTPChannelParent::CancelDiversion()
{
// Only HTTP channels can have child-process-sourced-data that's long-lived
// so this isn't currently relevant for FTP channels and there is nothing to
// do.
return NS_OK;
}
void
FTPChannelParent::DivertTo(nsIStreamListener *aListener)
{
MOZ_ASSERT(aListener);
if (NS_WARN_IF(!mDivertingFromChild)) {
MOZ_ASSERT(mDivertingFromChild,
"Cannot DivertTo new listener if diverting is not set!");
return;
}
if (NS_WARN_IF(mIPCClosed || !SendFlushedForDiversion())) {
FailDiversion(NS_ERROR_UNEXPECTED);
return;
}
mDivertToListener = aListener;
// Call OnStartRequest and SendDivertMessages asynchronously to avoid
// reentering client context.
NS_DispatchToCurrentThread(
NewRunnableMethod("net::FTPChannelParent::StartDiversion",
this,
&FTPChannelParent::StartDiversion));
}
void
FTPChannelParent::StartDiversion()
{
if (NS_WARN_IF(!mDivertingFromChild)) {
MOZ_ASSERT(mDivertingFromChild,
"Cannot StartDiversion if diverting is not set!");
return;
}
// Fake pending status in case OnStopRequest has already been called.
if (mChannel) {
nsCOMPtr<nsIForcePendingChannel> forcePendingIChan = do_QueryInterface(mChannel);
if (forcePendingIChan) {
forcePendingIChan->ForcePending(true);
}
}
{
AutoEventEnqueuer ensureSerialDispatch(mEventQ);
// Call OnStartRequest for the "DivertTo" listener.
nsresult rv = OnStartRequest(mChannel, nullptr);
if (NS_FAILED(rv)) {
if (mChannel) {
mChannel->Cancel(rv);
}
mStatus = rv;
return;
}
}
// After OnStartRequest has been called, tell FTPChannelChild to divert the
// OnDataAvailables and OnStopRequest to this FTPChannelParent.
if (NS_WARN_IF(mIPCClosed || !SendDivertMessages())) {
FailDiversion(NS_ERROR_UNEXPECTED);
return;
}
}
class FTPFailDiversionEvent : public Runnable
{
public:
FTPFailDiversionEvent(FTPChannelParent* aChannelParent,
nsresult aErrorCode,
bool aSkipResume)
: Runnable("net::FTPFailDiversionEvent")
, mChannelParent(aChannelParent)
, mErrorCode(aErrorCode)
, mSkipResume(aSkipResume)
{
MOZ_RELEASE_ASSERT(aChannelParent);
MOZ_RELEASE_ASSERT(NS_FAILED(aErrorCode));
}
NS_IMETHOD Run() override
{
mChannelParent->NotifyDiversionFailed(mErrorCode, mSkipResume);
return NS_OK;
}
private:
RefPtr<FTPChannelParent> mChannelParent;
nsresult mErrorCode;
bool mSkipResume;
};
void
FTPChannelParent::FailDiversion(nsresult aErrorCode,
bool aSkipResume)
{
MOZ_RELEASE_ASSERT(NS_FAILED(aErrorCode));
MOZ_RELEASE_ASSERT(mDivertingFromChild);
MOZ_RELEASE_ASSERT(mDivertToListener);
MOZ_RELEASE_ASSERT(mChannel);
NS_DispatchToCurrentThread(
new FTPFailDiversionEvent(this, aErrorCode, aSkipResume));
}
void
FTPChannelParent::NotifyDiversionFailed(nsresult aErrorCode,
bool aSkipResume)
{
MOZ_RELEASE_ASSERT(NS_FAILED(aErrorCode));
MOZ_RELEASE_ASSERT(mDivertingFromChild);
MOZ_RELEASE_ASSERT(mDivertToListener);
MOZ_RELEASE_ASSERT(mChannel);
mChannel->Cancel(aErrorCode);
nsCOMPtr<nsIForcePendingChannel> forcePendingIChan = do_QueryInterface(mChannel);
if (forcePendingIChan) {
forcePendingIChan->ForcePending(false);
}
bool isPending = false;
nsresult rv = mChannel->IsPending(&isPending);
MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
// Resume only we suspended earlier.
if (mSuspendedForDiversion) {
ResumeChannelInternalIfPossible();
}
// Channel has already sent OnStartRequest to the child, so ensure that we
// call it here if it hasn't already been called.
if (!mDivertedOnStartRequest) {
nsCOMPtr<nsIForcePendingChannel> forcePendingIChan = do_QueryInterface(mChannel);
if (forcePendingIChan) {
forcePendingIChan->ForcePending(true);
}
mDivertToListener->OnStartRequest(mChannel, nullptr);
if (forcePendingIChan) {
forcePendingIChan->ForcePending(false);
}
}
// If the channel is pending, it will call OnStopRequest itself; otherwise, do
// it here.
if (!isPending) {
mDivertToListener->OnStopRequest(mChannel, nullptr, aErrorCode);
}
mDivertToListener = nullptr;
mChannel = nullptr;
if (!mIPCClosed) {
Unused << SendDeleteSelf();
}
}
//-----------------------------------------------------------------------------
// FTPChannelParent::nsIChannelEventSink
//-----------------------------------------------------------------------------
NS_IMETHODIMP
FTPChannelParent::AsyncOnChannelRedirect(
nsIChannel *oldChannel,
nsIChannel *newChannel,
uint32_t redirectFlags,
nsIAsyncVerifyRedirectCallback* callback)
{
nsCOMPtr<nsIFTPChannel> ftpChan = do_QueryInterface(newChannel);
if (!ftpChan) {
// when FTP is set to use HTTP proxying, we wind up getting redirected to an HTTP channel.
nsCOMPtr<nsIHttpChannel> httpChan = do_QueryInterface(newChannel);
if (!httpChan)
return NS_ERROR_UNEXPECTED;
}
mChannel = newChannel;
callback->OnRedirectVerifyCallback(NS_OK);
return NS_OK;
}
NS_IMETHODIMP
FTPChannelParent::SetErrorMsg(const char *aMsg, bool aUseUTF8)
{
mErrorMsg = aMsg;
mUseUTF8 = aUseUTF8;
return NS_OK;
}
//---------------------
} // namespace net
} // namespace mozilla