Bug 1666725 - Make gvfs e10s compatible r=valentin,necko-reviewers

Split channel into parent/child to be able to use dbus in the content sandbox

Differential Revision: https://phabricator.services.mozilla.com/D92206
This commit is contained in:
M. Sirringhaus 2021-02-09 12:52:22 +00:00
parent f3afaa1ce8
commit 86e1612919
15 changed files with 1257 additions and 55 deletions

View File

@ -666,6 +666,8 @@ VIRTUAL_CALL_CLASSES = set(
("PColorPicker", "child"),
("PColorPicker", "parent"),
("PDataChannel", "child"),
("PGIOChannel", "child"),
("PGIOChannel", "parent"),
("PFileChannel", "child"),
("PFilePicker", "child"),
("PFunctionBroker", "child"),

View File

@ -543,5 +543,29 @@ struct SpeculativeConnectionOverriderArgs {
bool allow1918;
};
//-----------------------------------------------------------------------------
// GIO IPDL structs
//-----------------------------------------------------------------------------
struct GIOChannelOpenArgs
{
URIParams uri;
uint64_t startPos;
nsCString entityID;
IPCStream? uploadStream;
LoadInfoArgs? loadInfo;
uint32_t loadFlags;
};
struct GIOChannelConnectArgs
{
uint32_t channelId;
};
union GIOChannelCreationArgs
{
GIOChannelOpenArgs; // For AsyncOpen: the common case.
GIOChannelConnectArgs; // Used for redirected-to channels
};
} // namespace net
} // namespace mozilla

View File

@ -13,6 +13,7 @@
#include "mozilla/net/CookieServiceChild.h"
#include "mozilla/net/FTPChannelChild.h"
#include "mozilla/net/DataChannelChild.h"
#include "mozilla/net/GIOChannelChild.h"
#include "mozilla/net/FileChannelChild.h"
#include "mozilla/net/WebSocketChannelChild.h"
#include "mozilla/net/WebSocketEventListenerChild.h"
@ -141,6 +142,22 @@ bool NeckoChild::DeallocPFTPChannelChild(PFTPChannelChild* channel) {
return true;
}
PGIOChannelChild* NeckoChild::AllocPGIOChannelChild(
PBrowserChild* aBrowser, const SerializedLoadContext& aSerialized,
const GIOChannelCreationArgs& aOpenArgs) {
// We don't allocate here: see GIOChannelChild::AsyncOpen()
MOZ_CRASH("AllocPGIOChannelChild should not be called");
return nullptr;
}
bool NeckoChild::DeallocPGIOChannelChild(PGIOChannelChild* channel) {
MOZ_ASSERT(IsNeckoChild(), "DeallocPGIOChannelChild called by non-child!");
GIOChannelChild* child = static_cast<GIOChannelChild*>(channel);
child->ReleaseIPDLReference();
return true;
}
PCookieServiceChild* NeckoChild::AllocPCookieServiceChild() {
// We don't allocate here: see CookieService::GetSingleton()
MOZ_ASSERT_UNREACHABLE("AllocPCookieServiceChild should not be called");

View File

@ -42,6 +42,12 @@ class NeckoChild : public PNeckoChild {
PBrowserChild* aBrowser, const SerializedLoadContext& aSerialized,
const FTPChannelCreationArgs& aOpenArgs);
bool DeallocPFTPChannelChild(PFTPChannelChild*);
PGIOChannelChild* AllocPGIOChannelChild(
PBrowserChild* aBrowser, const SerializedLoadContext& aSerialized,
const GIOChannelCreationArgs& aOpenArgs);
bool DeallocPGIOChannelChild(PGIOChannelChild*);
PWebSocketChild* AllocPWebSocketChild(PBrowserChild*,
const SerializedLoadContext&,
const uint32_t&);
@ -81,7 +87,7 @@ class NeckoChild : public PNeckoChild {
const Maybe<LoadInfoArgs>& aLoadInfo);
bool DeallocPClassifierDummyChannelChild(
PClassifierDummyChannelChild* aChannel);
PClassifierDummyChannelChild* aActor);
};
/**

View File

@ -18,6 +18,7 @@
#include "mozilla/net/WebSocketChannelParent.h"
#include "mozilla/net/WebSocketEventListenerParent.h"
#include "mozilla/net/DataChannelParent.h"
#include "mozilla/net/GIOChannelParent.h"
#include "mozilla/net/DocumentChannelParent.h"
#include "mozilla/net/SimpleChannelParent.h"
#include "mozilla/net/AltDataOutputStreamParent.h"
@ -408,6 +409,56 @@ mozilla::ipc::IPCResult NeckoParent::RecvPDataChannelConstructor(
return IPC_OK();
}
static already_AddRefed<nsIPrincipal> GetRequestingPrincipal(
const GIOChannelCreationArgs& aArgs) {
if (aArgs.type() != GIOChannelCreationArgs::TGIOChannelOpenArgs) {
return nullptr;
}
const GIOChannelOpenArgs& args = aArgs.get_GIOChannelOpenArgs();
return GetRequestingPrincipal(args.loadInfo());
}
PGIOChannelParent* NeckoParent::AllocPGIOChannelParent(
PBrowserParent* aBrowser, const SerializedLoadContext& aSerialized,
const GIOChannelCreationArgs& aOpenArgs) {
nsCOMPtr<nsIPrincipal> requestingPrincipal =
GetRequestingPrincipal(aOpenArgs);
nsCOMPtr<nsILoadContext> loadContext;
const char* error = CreateChannelLoadContext(
aBrowser, Manager(), aSerialized, requestingPrincipal, loadContext);
if (error) {
printf_stderr(
"NeckoParent::AllocPGIOChannelParent: "
"FATAL error: %s: KILLING CHILD PROCESS\n",
error);
return nullptr;
}
PBOverrideStatus overrideStatus =
PBOverrideStatusFromLoadContext(aSerialized);
GIOChannelParent* p = new GIOChannelParent(BrowserParent::GetFrom(aBrowser),
loadContext, overrideStatus);
p->AddRef();
return p;
}
bool NeckoParent::DeallocPGIOChannelParent(PGIOChannelParent* channel) {
GIOChannelParent* p = static_cast<GIOChannelParent*>(channel);
p->Release();
return true;
}
mozilla::ipc::IPCResult NeckoParent::RecvPGIOChannelConstructor(
PGIOChannelParent* actor, PBrowserParent* aBrowser,
const SerializedLoadContext& aSerialized,
const GIOChannelCreationArgs& aOpenArgs) {
GIOChannelParent* p = static_cast<GIOChannelParent*>(actor);
DebugOnly<bool> rv = p->Init(aOpenArgs);
MOZ_ASSERT(rv);
return IPC_OK();
}
PSimpleChannelParent* NeckoParent::AllocPSimpleChannelParent(
const uint32_t& channelId) {
RefPtr<SimpleChannelParent> p = new SimpleChannelParent();

View File

@ -153,7 +153,7 @@ class NeckoParent : public PNeckoParent {
const uint16_t& aType, const OriginAttributes& aOriginAttributes,
const uint32_t& aFlags);
virtual mozilla::ipc::IPCResult RecvPDNSRequestConstructor(
PDNSRequestParent* actor, const nsCString& hostName,
PDNSRequestParent* actor, const nsCString& aHost,
const nsCString& trrServer, const uint16_t& type,
const OriginAttributes& aOriginAttributes,
const uint32_t& flags) override;
@ -177,8 +177,18 @@ class NeckoParent : public PNeckoParent {
virtual mozilla::ipc::IPCResult RecvPDataChannelConstructor(
PDataChannelParent* aActor, const uint32_t& channelId) override;
PGIOChannelParent* AllocPGIOChannelParent(
PBrowserParent* aBrowser, const SerializedLoadContext& aSerialized,
const GIOChannelCreationArgs& aOpenArgs);
bool DeallocPGIOChannelParent(PGIOChannelParent* channel);
virtual mozilla::ipc::IPCResult RecvPGIOChannelConstructor(
PGIOChannelParent* aActor, PBrowserParent* aBrowser,
const SerializedLoadContext& aSerialized,
const GIOChannelCreationArgs& aOpenArgs) override;
PSimpleChannelParent* AllocPSimpleChannelParent(const uint32_t& channelId);
bool DeallocPSimpleChannelParent(PSimpleChannelParent* parent);
bool DeallocPSimpleChannelParent(PSimpleChannelParent* actor);
virtual mozilla::ipc::IPCResult RecvPSimpleChannelConstructor(
PSimpleChannelParent* aActor, const uint32_t& channelId) override;
@ -232,7 +242,7 @@ class NeckoParent : public PNeckoParent {
const Maybe<LoadInfoArgs>& aLoadInfo);
bool DeallocPClassifierDummyChannelParent(
PClassifierDummyChannelParent* aParent);
PClassifierDummyChannelParent* aActor);
virtual mozilla::ipc::IPCResult RecvPClassifierDummyChannelConstructor(
PClassifierDummyChannelParent* aActor, nsIURI* aURI,

View File

@ -10,6 +10,7 @@ include protocol PHttpChannel;
include protocol PCookieService;
include protocol PBrowser;
include protocol PFTPChannel;
include protocol PGIOChannel;
include protocol PWebSocket;
include protocol PWebSocketEventListener;
include protocol PTCPSocket;
@ -60,6 +61,7 @@ nested(upto inside_cpow) sync protocol PNecko
manages PUDPSocket;
manages PDNSRequest;
manages PDataChannel;
manages PGIOChannel;
manages PSimpleChannel;
manages PFileChannel;
manages PTransportProvider;
@ -112,6 +114,7 @@ parent:
* the parent and the child when we're redirecting to a data: URI.
*/
async PDataChannel(uint32_t channelId);
async PGIOChannel(nullable PBrowser browser, SerializedLoadContext loadContext, GIOChannelCreationArgs args);
async PSimpleChannel(uint32_t channelId);
async PFileChannel(uint32_t channelId);

View File

@ -0,0 +1,468 @@
/* -*- 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)
: mIPCOpen(false),
mEventQ(new ChannelEventQueue(static_cast<nsIChildChannel*>(this))),
mCanceled(false),
mSuspendCount(0),
mIsPending(false),
mStartPos(0),
mSuspendSent(false) {
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;
}
gNeckoChild->SetEventTargetForActor(this, mNeckoTarget);
}
} // namespace net
} // namespace mozilla

View File

@ -0,0 +1,109 @@
/* -*- 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/. */
#ifndef NS_GIOCHANNELCHILD_H
#define NS_GIOCHANNELCHILD_H
#include "mozilla/net/PGIOChannelChild.h"
#include "mozilla/net/ChannelEventQueue.h"
#include "nsBaseChannel.h"
#include "nsIUploadChannel.h"
#include "nsIProxiedChannel.h"
#include "nsIResumableChannel.h"
#include "nsIChildChannel.h"
#include "nsIEventTarget.h"
#include "nsIStreamListener.h"
class nsIEventTarget;
namespace mozilla {
namespace net {
class GIOChannelChild final : public PGIOChannelChild,
public nsBaseChannel,
public nsIChildChannel {
public:
using nsIStreamListener = ::nsIStreamListener;
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSICHILDCHANNEL
NS_IMETHOD Cancel(nsresult aStatus) override;
NS_IMETHOD Suspend() override;
NS_IMETHOD Resume() override;
explicit GIOChannelChild(nsIURI* uri);
void AddIPDLReference();
void ReleaseIPDLReference();
NS_IMETHOD AsyncOpen(nsIStreamListener* aListener) override;
// Note that we handle this ourselves, overriding the nsBaseChannel
// default behavior, in order to be e10s-friendly.
NS_IMETHOD IsPending(bool* aResult) override;
nsresult OpenContentStream(bool aAsync, nsIInputStream** aStream,
nsIChannel** aChannel) override;
bool IsSuspended() const;
protected:
virtual ~GIOChannelChild() = default;
mozilla::ipc::IPCResult RecvOnStartRequest(const nsresult& aChannelStatus,
const int64_t& aContentLength,
const nsCString& aContentType,
const nsCString& aEntityID,
const URIParams& aURI) override;
mozilla::ipc::IPCResult RecvOnDataAvailable(const nsresult& aChannelStatus,
const nsCString& aData,
const uint64_t& aOffset,
const uint32_t& aCount) override;
mozilla::ipc::IPCResult RecvOnStopRequest(
const nsresult& aChannelStatus) override;
mozilla::ipc::IPCResult RecvFailedAsyncOpen(
const nsresult& aStatusCode) override;
mozilla::ipc::IPCResult RecvDeleteSelf() override;
void DoOnStartRequest(const nsresult& aChannelStatus,
const int64_t& aContentLength,
const nsCString& aContentType,
const nsCString& aEntityID, const URIParams& aURI);
void DoOnDataAvailable(const nsresult& aChannelStatus, const nsCString& aData,
const uint64_t& aOffset, const uint32_t& aCount);
void DoOnStopRequest(const nsresult& aChannelStatus);
void DoFailedAsyncOpen(const nsresult& aStatusCode);
void DoDeleteSelf();
void SetupNeckoTarget() override;
friend class NeckoTargetChannelFunctionEvent;
private:
nsCOMPtr<nsIInputStream> mUploadStream;
bool mIPCOpen;
const RefPtr<ChannelEventQueue> mEventQ;
bool mCanceled = false;
uint32_t mSuspendCount;
bool mIsPending = false;
uint64_t mStartPos;
nsCString mEntityID;
// Set if SendSuspend is called. Determines if SendResume is needed when
// diverting callbacks to parent.
bool mSuspendSent;
};
inline bool GIOChannelChild::IsSuspended() const { return mSuspendCount != 0; }
} // namespace net
} // namespace mozilla
#endif /* NS_GIOCHANNELCHILD_H */

View File

@ -0,0 +1,323 @@
/* -*- 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 "GIOChannelParent.h"
#include "nsGIOProtocolHandler.h"
#include "mozilla/Assertions.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/BrowserParent.h"
#include "mozilla/net/NeckoParent.h"
#include "nsNetUtil.h"
#include "nsIChannel.h"
#include "mozilla/net/NeckoChannelParams.h"
#include "nsIAuthPrompt.h"
#include "nsIAuthPromptProvider.h"
#include "nsISecureBrowserUI.h"
#include "nsQueryObject.h"
#include "mozilla/Logging.h"
using namespace mozilla::dom;
using namespace mozilla::ipc;
namespace mozilla {
#undef LOG
#define LOG(args) MOZ_LOG(gGIOLog, mozilla::LogLevel::Debug, args)
namespace net {
GIOChannelParent::GIOChannelParent(dom::BrowserParent* aIframeEmbedding,
nsILoadContext* aLoadContext,
PBOverrideStatus aOverrideStatus)
: mLoadContext(aLoadContext),
mPBOverride(aOverrideStatus),
mBrowserParent(aIframeEmbedding) {
mEventQ = new ChannelEventQueue(static_cast<nsIParentChannel*>(this));
}
void GIOChannelParent::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;
}
//-----------------------------------------------------------------------------
// GIOChannelParent::nsISupports
//-----------------------------------------------------------------------------
NS_IMPL_ISUPPORTS(GIOChannelParent, nsIStreamListener, nsIParentChannel,
nsIInterfaceRequestor, nsIRequestObserver)
//-----------------------------------------------------------------------------
// GIOChannelParent methods
//-----------------------------------------------------------------------------
bool GIOChannelParent::Init(const GIOChannelCreationArgs& aOpenArgs) {
switch (aOpenArgs.type()) {
case GIOChannelCreationArgs::TGIOChannelOpenArgs: {
const GIOChannelOpenArgs& a = aOpenArgs.get_GIOChannelOpenArgs();
return DoAsyncOpen(a.uri(), a.startPos(), a.entityID(), a.uploadStream(),
a.loadInfo(), a.loadFlags());
}
case GIOChannelCreationArgs::TGIOChannelConnectArgs: {
const GIOChannelConnectArgs& cArgs =
aOpenArgs.get_GIOChannelConnectArgs();
return ConnectChannel(cArgs.channelId());
}
default:
MOZ_ASSERT_UNREACHABLE("unknown open type");
return false;
}
}
bool GIOChannelParent::DoAsyncOpen(const URIParams& aURI,
const uint64_t& aStartPos,
const nsCString& aEntityID,
const Maybe<IPCStream>& aUploadStream,
const Maybe<LoadInfoArgs>& aLoadInfoArgs,
const uint32_t& aLoadFlags) {
nsresult rv;
nsCOMPtr<nsIURI> uri = DeserializeURI(aURI);
if (!uri) {
return false;
}
#ifdef DEBUG
LOG(("GIOChannelParent 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;
nsIChannel* gioChan = static_cast<nsIChannel*>(mChannel.get());
rv = gioChan->AsyncOpen(this);
if (NS_FAILED(rv)) {
return SendFailedAsyncOpen(rv);
}
return true;
}
bool GIOChannelParent::ConnectChannel(const uint64_t& channelId) {
nsresult rv;
LOG(("Looking for a registered channel [this=%p, id=%" PRIx64 "]", 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 GIOChannelParent::RecvCancel(const nsresult& status) {
if (mChannel) {
mChannel->Cancel(status);
}
return IPC_OK();
}
mozilla::ipc::IPCResult GIOChannelParent::RecvSuspend() {
if (mChannel) {
mChannel->Suspend();
}
return IPC_OK();
}
mozilla::ipc::IPCResult GIOChannelParent::RecvResume() {
if (mChannel) {
mChannel->Resume();
}
return IPC_OK();
}
//-----------------------------------------------------------------------------
// GIOChannelParent::nsIRequestObserver
//-----------------------------------------------------------------------------
NS_IMETHODIMP
GIOChannelParent::OnStartRequest(nsIRequest* aRequest) {
LOG(("GIOChannelParent::OnStartRequest [this=%p]\n", this));
nsCOMPtr<nsIChannel> chan = do_QueryInterface(aRequest);
MOZ_ASSERT(chan);
NS_ENSURE_TRUE(chan, NS_ERROR_UNEXPECTED);
int64_t contentLength;
chan->GetContentLength(&contentLength);
nsCString contentType;
chan->GetContentType(contentType);
nsresult channelStatus = NS_OK;
chan->GetStatus(&channelStatus);
nsCString entityID;
URIParams uriparam;
nsCOMPtr<nsIURI> uri;
chan->GetURI(getter_AddRefs(uri));
SerializeURI(uri, uriparam);
if (mIPCClosed || !SendOnStartRequest(channelStatus, contentLength,
contentType, entityID, uriparam)) {
return NS_ERROR_UNEXPECTED;
}
return NS_OK;
}
NS_IMETHODIMP
GIOChannelParent::OnStopRequest(nsIRequest* aRequest, nsresult aStatusCode) {
LOG(("GIOChannelParent::OnStopRequest: [this=%p status=%" PRIu32 "]\n", this,
static_cast<uint32_t>(aStatusCode)));
if (mIPCClosed || !SendOnStopRequest(aStatusCode)) {
return NS_ERROR_UNEXPECTED;
}
return NS_OK;
}
//-----------------------------------------------------------------------------
// GIOChannelParent::nsIStreamListener
//-----------------------------------------------------------------------------
NS_IMETHODIMP
GIOChannelParent::OnDataAvailable(nsIRequest* aRequest,
nsIInputStream* aInputStream,
uint64_t aOffset, uint32_t aCount) {
LOG(("GIOChannelParent::OnDataAvailable [this=%p]\n", this));
nsCString data;
nsresult rv = NS_ReadInputStreamToString(aInputStream, data, aCount);
if (NS_FAILED(rv)) {
return rv;
}
nsresult channelStatus = NS_OK;
mChannel->GetStatus(&channelStatus);
if (mIPCClosed ||
!SendOnDataAvailable(channelStatus, data, aOffset, aCount)) {
return NS_ERROR_UNEXPECTED;
}
return NS_OK;
}
//-----------------------------------------------------------------------------
// GIOChannelParent::nsIParentChannel
//-----------------------------------------------------------------------------
NS_IMETHODIMP
GIOChannelParent::SetParentListener(ParentChannelListener* aListener) {
// Do not need ptr to ParentChannelListener.
return NS_OK;
}
NS_IMETHODIMP
GIOChannelParent::NotifyClassificationFlags(uint32_t aClassificationFlags,
bool aIsThirdParty) {
// Nothing to do.
return NS_OK;
}
NS_IMETHODIMP
GIOChannelParent::NotifyFlashPluginStateChanged(
nsIHttpChannel::FlashPluginState aState) {
// Nothing to do.
return NS_OK;
}
NS_IMETHODIMP
GIOChannelParent::SetClassifierMatchedInfo(const nsACString& aList,
const nsACString& aProvider,
const nsACString& aFullHash) {
// nothing to do
return NS_OK;
}
NS_IMETHODIMP
GIOChannelParent::SetClassifierMatchedTrackingInfo(
const nsACString& aLists, const nsACString& aFullHashes) {
// nothing to do
return NS_OK;
}
NS_IMETHODIMP
GIOChannelParent::Delete() {
if (mIPCClosed || !SendDeleteSelf()) {
return NS_ERROR_UNEXPECTED;
}
return NS_OK;
}
NS_IMETHODIMP
GIOChannelParent::GetRemoteType(nsACString& aRemoteType) {
if (!CanSend()) {
return NS_ERROR_UNEXPECTED;
}
dom::PContentParent* pcp = Manager()->Manager();
aRemoteType = static_cast<dom::ContentParent*>(pcp)->GetRemoteType();
return NS_OK;
}
//-----------------------------------------------------------------------------
// GIOChannelParent::nsIInterfaceRequestor
//-----------------------------------------------------------------------------
NS_IMETHODIMP
GIOChannelParent::GetInterface(const nsIID& uuid, void** result) {
if (uuid.Equals(NS_GET_IID(nsIAuthPromptProvider)) ||
uuid.Equals(NS_GET_IID(nsISecureBrowserUI))) {
if (mBrowserParent) {
return mBrowserParent->QueryInterface(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);
}
//---------------------
} // namespace net
} // namespace mozilla

View File

@ -0,0 +1,80 @@
/* -*- 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/. */
#ifndef NS_GIOCHANNELPARENT_H
#define NS_GIOCHANNELPARENT_H
#include "mozilla/net/PGIOChannelParent.h"
#include "mozilla/net/NeckoParent.h"
#include "nsIParentChannel.h"
#include "nsIInterfaceRequestor.h"
class nsILoadContext;
namespace mozilla {
namespace dom {
class BrowserParent;
} // namespace dom
namespace net {
class ChannelEventQueue;
class GIOChannelParent final : public PGIOChannelParent,
public nsIParentChannel,
public nsIInterfaceRequestor {
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIREQUESTOBSERVER
NS_DECL_NSISTREAMLISTENER
NS_DECL_NSIPARENTCHANNEL
NS_DECL_NSIINTERFACEREQUESTOR
GIOChannelParent(dom::BrowserParent* aIframeEmbedding,
nsILoadContext* aLoadContext,
PBOverrideStatus aOverrideStatus);
bool Init(const GIOChannelCreationArgs& aOpenArgs);
protected:
virtual ~GIOChannelParent() = default;
bool DoAsyncOpen(const URIParams& aURI, const uint64_t& aStartPos,
const nsCString& aEntityID,
const Maybe<IPCStream>& aUploadStream,
const Maybe<LoadInfoArgs>& aLoadInfoArgs,
const uint32_t& aLoadFlags);
// used to connect redirected-to channel in parent with just created
// ChildChannel. Used during HTTP->FTP redirects.
bool ConnectChannel(const uint64_t& channelId);
virtual mozilla::ipc::IPCResult RecvCancel(const nsresult& status) override;
virtual mozilla::ipc::IPCResult RecvSuspend() override;
virtual mozilla::ipc::IPCResult RecvResume() override;
virtual void ActorDestroy(ActorDestroyReason why) override;
nsCOMPtr<nsIChannel> mChannel;
bool mIPCClosed = false;
nsCOMPtr<nsILoadContext> mLoadContext;
PBOverrideStatus mPBOverride;
// Set to the canceled status value if the main channel was canceled.
nsresult mStatus = NS_OK;
RefPtr<mozilla::dom::BrowserParent> mBrowserParent;
RefPtr<ChannelEventQueue> mEventQ;
};
} // namespace net
} // namespace mozilla
#endif /* NS_GIOCHANNELPARENT_H */

View File

@ -0,0 +1,50 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
/* 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 protocol PNecko;
include URIParams;
//FIXME: bug #792908 (NeckoChannelParams already included by PNecko)
include NeckoChannelParams;
using PRTime from "prtime.h";
namespace mozilla {
namespace net {
async protocol PGIOChannel
{
manager PNecko;
parent:
// Note: channels are opened during construction, so no open method here:
// see PNecko.ipdl
async __delete__();
async Cancel(nsresult status);
async Suspend();
async Resume();
child:
async OnStartRequest(nsresult aChannelStatus,
int64_t aContentLength,
nsCString aContentType,
nsCString aEntityID,
URIParams aURI);
async OnDataAvailable(nsresult channelStatus,
nsCString data,
uint64_t offset,
uint32_t count);
async OnStopRequest(nsresult channelStatus);
async FailedAsyncOpen(nsresult statusCode);
async DeleteSelf();
};
} // namespace net
} // namespace mozilla

View File

@ -4,10 +4,6 @@
# 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/.
SOURCES += [
"nsGIOProtocolHandler.cpp",
]
XPCOM_MANIFESTS += [
"components.conf",
]
@ -16,6 +12,27 @@ EXPORTS += [
"nsGIOProtocolHandler.h",
]
EXPORTS.mozilla.net += [
"GIOChannelChild.h",
"GIOChannelParent.h",
]
UNIFIED_SOURCES += [
"GIOChannelChild.cpp",
"GIOChannelParent.cpp",
"nsGIOProtocolHandler.cpp",
]
IPDL_SOURCES = [
"PGIOChannel.ipdl",
]
include("/ipc/chromium/chromium-config.mozbuild")
LOCAL_INCLUDES += [
"/netwerk/base",
]
FINAL_LIBRARY = "xul"
CXXFLAGS += CONFIG["TK_CFLAGS"]

View File

@ -8,9 +8,11 @@
* input stream provided by GVFS/GIO.
*/
#include "nsGIOProtocolHandler.h"
#include "GIOChannelChild.h"
#include "mozilla/Components.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/Logging.h"
#include "mozilla/net/NeckoChild.h"
#include "mozilla/NullPrincipal.h"
#include "nsIPrefBranch.h"
#include "nsIPrefService.h"
@ -41,8 +43,9 @@ using namespace mozilla;
//-----------------------------------------------------------------------------
// NSPR_LOG_MODULES=gio:5
static mozilla::LazyLogModule sGIOLog("gio");
#define LOG(args) MOZ_LOG(sGIOLog, mozilla::LogLevel::Debug, args)
LazyLogModule gGIOLog("gio");
#undef LOG
#define LOG(args) MOZ_LOG(gGIOLog, mozilla::LogLevel::Debug, args)
//-----------------------------------------------------------------------------
static nsresult MapGIOResult(gint code) {
@ -108,17 +111,21 @@ static nsresult MapGIOResult(gint code) {
}
static nsresult MapGIOResult(GError* result) {
if (!result) return NS_OK;
if (!result) {
return NS_OK;
}
return MapGIOResult(result->code);
}
/** Return values for mount operation.
* These enums are used as mount operation return values.
*/
typedef enum {
enum class MountOperationResult {
MOUNT_OPERATION_IN_PROGRESS, /** \enum operation in progress */
MOUNT_OPERATION_SUCCESS, /** \enum operation successful */
MOUNT_OPERATION_FAILED /** \enum operation not successful */
} MountOperationResult;
};
//-----------------------------------------------------------------------------
/**
* Sort function compares according to file type (directory/file)
@ -132,11 +139,13 @@ static gint FileInfoComparator(gconstpointer a, gconstpointer b) {
GFileInfo* ia = (GFileInfo*)a;
GFileInfo* ib = (GFileInfo*)b;
if (g_file_info_get_file_type(ia) == G_FILE_TYPE_DIRECTORY &&
g_file_info_get_file_type(ib) != G_FILE_TYPE_DIRECTORY)
g_file_info_get_file_type(ib) != G_FILE_TYPE_DIRECTORY) {
return -1;
}
if (g_file_info_get_file_type(ib) == G_FILE_TYPE_DIRECTORY &&
g_file_info_get_file_type(ia) != G_FILE_TYPE_DIRECTORY)
g_file_info_get_file_type(ia) != G_FILE_TYPE_DIRECTORY) {
return 1;
}
return strcasecmp(g_file_info_get_name(ia), g_file_info_get_name(ib));
}
@ -149,10 +158,7 @@ static void mount_operation_ask_password(
GMountOperation* mount_op, const char* message, const char* default_user,
const char* default_domain, GAskPasswordFlags flags, gpointer user_data);
//-----------------------------------------------------------------------------
class nsGIOInputStream final : public nsIInputStream {
~nsGIOInputStream() { Close(); }
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIINPUTSTREAM
@ -189,6 +195,7 @@ class nsGIOInputStream final : public nsIInputStream {
void SetMountResult(MountOperationResult result, gint error_code);
private:
~nsGIOInputStream() { Close(); }
nsresult DoOpen();
nsresult DoRead(char* aBuf, uint32_t aCount, uint32_t* aCountRead);
nsresult SetContentTypeOfChannel(const char* contentType);
@ -206,10 +213,12 @@ class nsGIOInputStream final : public nsIInputStream {
nsCString mDirBuf;
uint32_t mDirBufCursor;
bool mDirOpen;
MountOperationResult mMountRes;
MountOperationResult mMountRes =
MountOperationResult::MOUNT_OPERATION_SUCCESS;
mozilla::Monitor mMonitorMountInProgress;
gint mMountErrorCode;
gint mMountErrorCode{};
};
/**
* Set result of mount operation and notify monitor waiting for results.
* This method is called in main thread as long as it is used only
@ -232,7 +241,7 @@ nsresult nsGIOInputStream::MountVolume() {
GMountOperation* mount_op = g_mount_operation_new();
g_signal_connect(mount_op, "ask-password",
G_CALLBACK(mount_operation_ask_password), mChannel);
mMountRes = MOUNT_OPERATION_IN_PROGRESS;
mMountRes = MountOperationResult::MOUNT_OPERATION_IN_PROGRESS;
/* g_file_mount_enclosing_volume uses a dbus request to mount the volume.
Callback mount_enclosing_volume_finished is called in main thread
(not this thread on which this method is called). */
@ -240,11 +249,13 @@ nsresult nsGIOInputStream::MountVolume() {
mount_enclosing_volume_finished, this);
mozilla::MonitorAutoLock mon(mMonitorMountInProgress);
/* Waiting for finish of mount operation thread */
while (mMountRes == MOUNT_OPERATION_IN_PROGRESS) mon.Wait();
while (mMountRes == MountOperationResult::MOUNT_OPERATION_IN_PROGRESS) {
mon.Wait();
}
g_object_unref(mount_op);
if (mMountRes == MOUNT_OPERATION_FAILED) {
if (mMountRes == MountOperationResult::MOUNT_OPERATION_FAILED) {
return MapGIOResult(mMountErrorCode);
}
return NS_OK;
@ -288,7 +299,9 @@ nsresult nsGIOInputStream::DoOpenDirectory() {
// Write base URL (make sure it ends with a '/')
mDirBuf.AppendLiteral("300: ");
mDirBuf.Append(mSpec);
if (mSpec.get()[mSpec.Length() - 1] != '/') mDirBuf.Append('/');
if (mSpec.get()[mSpec.Length() - 1] != '/') {
mDirBuf.Append('/');
}
mDirBuf.Append('\n');
// Write column names
@ -360,7 +373,9 @@ nsresult nsGIOInputStream::DoOpen() {
if (error->domain == G_IO_ERROR && error->code == G_IO_ERROR_NOT_MOUNTED) {
// location is not yet mounted, try to mount
g_error_free(error);
if (NS_IsMainThread()) return NS_ERROR_NOT_CONNECTED;
if (NS_IsMainThread()) {
return NS_ERROR_NOT_CONNECTED;
}
error = nullptr;
rv = MountVolume();
if (rv != NS_OK) {
@ -395,7 +410,9 @@ nsresult nsGIOInputStream::DoOpen() {
g_warning("Unable to get file type.");
rv = NS_ERROR_FILE_NOT_FOUND;
}
if (info) g_object_unref(info);
if (info) {
g_object_unref(info);
}
return rv;
}
@ -590,7 +607,9 @@ nsGIOInputStream::Close() {
mSpec.Truncate(); // free memory
// Prevent future reads from re-opening the handle.
if (NS_SUCCEEDED(mStatus)) mStatus = NS_BASE_STREAM_CLOSED;
if (NS_SUCCEEDED(mStatus)) {
mStatus = NS_BASE_STREAM_CLOSED;
}
return NS_OK;
}
@ -601,7 +620,9 @@ nsGIOInputStream::Close() {
*/
NS_IMETHODIMP
nsGIOInputStream::Available(uint64_t* aResult) {
if (NS_FAILED(mStatus)) return mStatus;
if (NS_FAILED(mStatus)) {
return mStatus;
}
*aResult = mBytesRemaining;
@ -628,7 +649,9 @@ nsGIOInputStream::Read(char* aBuf, uint32_t aCount, uint32_t* aCountRead) {
mStatus = DoRead(aBuf, aCount, aCountRead);
// Check if all data has been read
if (mStatus == NS_BASE_STREAM_CLOSED) return NS_OK;
if (mStatus == NS_BASE_STREAM_CLOSED) {
return NS_OK;
}
// Check whenever any error appears while reading
return mStatus;
@ -671,10 +694,11 @@ static void mount_enclosing_volume_finished(GObject* source_object,
if (error) {
g_warning("Mount failed: %s %d", error->message, error->code);
istream->SetMountResult(MOUNT_OPERATION_FAILED, error->code);
istream->SetMountResult(MountOperationResult::MOUNT_OPERATION_FAILED,
error->code);
g_error_free(error);
} else {
istream->SetMountResult(MOUNT_OPERATION_SUCCESS, 0);
istream->SetMountResult(MountOperationResult::MOUNT_OPERATION_SUCCESS, 0);
}
}
@ -840,6 +864,9 @@ already_AddRefed<nsGIOProtocolHandler> nsGIOProtocolHandler::GetSingleton() {
NS_IMPL_ISUPPORTS(nsGIOProtocolHandler, nsIProtocolHandler, nsIObserver)
nsresult nsGIOProtocolHandler::Init() {
if (net::IsNeckoChild()) {
net::NeckoChild::InitNeckoChild();
}
nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
if (prefs) {
InitSupportedProtocolsPref(prefs);
@ -865,29 +892,21 @@ void nsGIOProtocolHandler::InitSupportedProtocolsPref(nsIPrefBranch* prefs) {
#ifdef MOZ_PROXY_BYPASS_PROTECTION
"" // use none
#else
"smb:,sftp:" // use defaults
"sftp:" // use defaults (comma separated list)
#endif
);
}
LOG(("gio: supported protocols \"%s\"\n", mSupportedProtocols.get()));
}
bool nsGIOProtocolHandler::IsSupportedProtocol(const nsCString& aSpec) {
const char* specString = aSpec.get();
const char* colon = strchr(specString, ':');
if (!colon) return false;
uint32_t length = colon - specString + 1;
// <scheme> + ':'
nsCString scheme(specString, length);
char* found = PL_strcasestr(mSupportedProtocols.get(), scheme.get());
if (!found) return false;
if (found[length] != ',' && found[length] != '\0') return false;
return true;
bool nsGIOProtocolHandler::IsSupportedProtocol(const nsCString& aScheme) {
nsAutoCString schemeWithColon = aScheme + ":"_ns;
for (const auto& protocol : mSupportedProtocols.Split(',')) {
if (schemeWithColon.Equals(protocol, nsCaseInsensitiveCStringComparator)) {
return true;
}
}
return false;
}
NS_IMETHODIMP
@ -940,10 +959,8 @@ nsGIOProtocolHandler::NewChannel(nsIURI* aURI, nsILoadInfo* aLoadInfo,
nsAutoCString spec;
rv = aURI->GetSpec(spec);
if (NS_FAILED(rv)) return rv;
if (!IsSupportedProtocol(spec)) {
return NS_ERROR_UNKNOWN_PROTOCOL;
if (NS_FAILED(rv)) {
return rv;
}
nsAutoCString scheme;
@ -952,10 +969,29 @@ nsGIOProtocolHandler::NewChannel(nsIURI* aURI, nsILoadInfo* aLoadInfo,
return rv;
}
if (!IsValidGIOScheme(scheme)) {
if (!IsSupportedProtocol(scheme)) {
return NS_ERROR_UNKNOWN_PROTOCOL;
}
// g_vfs_get_supported_uri_schemes() returns a very limited list in the
// child due to the sandbox, so we only check if its valid for the parent.
if (XRE_IsParentProcess() && !IsValidGIOScheme(scheme)) {
return NS_ERROR_UNKNOWN_PROTOCOL;
}
RefPtr<nsBaseChannel> channel;
if (net::IsNeckoChild()) {
channel = new mozilla::net::GIOChannelChild(aURI);
// set the loadInfo on the new channel
channel->SetLoadInfo(aLoadInfo);
rv = channel->SetContentType(nsLiteralCString(UNKNOWN_CONTENT_TYPE));
NS_ENSURE_SUCCESS(rv, rv);
channel.forget(aResult);
return NS_OK;
}
RefPtr<nsGIOInputStream> stream = new nsGIOInputStream(spec);
if (!stream) {
return NS_ERROR_OUT_OF_MEMORY;
@ -969,6 +1005,7 @@ nsGIOProtocolHandler::NewChannel(nsIURI* aURI, nsILoadInfo* aLoadInfo,
if (NS_SUCCEEDED(rv)) {
stream->SetChannel(*aResult);
}
return rv;
}

View File

@ -10,6 +10,9 @@
#include "nsIPrefBranch.h"
#include "nsStringFwd.h"
#include "mozilla/Logging.h"
extern mozilla::LazyLogModule gGIOLog;
class nsGIOProtocolHandler final : public nsIProtocolHandler,
public nsIObserver {
public:
@ -18,11 +21,13 @@ class nsGIOProtocolHandler final : public nsIProtocolHandler,
NS_DECL_NSIOBSERVER
static already_AddRefed<nsGIOProtocolHandler> GetSingleton();
bool IsSupportedProtocol(const nsCString& spec);
bool IsSupportedProtocol(const nsCString& aScheme);
protected:
~nsGIOProtocolHandler() = default;
private:
nsresult Init();
~nsGIOProtocolHandler() = default;
void InitSupportedProtocolsPref(nsIPrefBranch* prefs);