Source code
Revision control
Copy as Markdown
Other Tools
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 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
#include "mozilla/widget/filedialog/WinFileDialogParent.h"
#include "mozilla/Logging.h"
#include "mozilla/Result.h"
#include "mozilla/SpinEventLoopUntil.h"
#include "mozilla/StaticString.h"
#include "mozilla/ipc/UtilityProcessManager.h"
#include "nsISupports.h"
namespace mozilla::widget::filedialog {
// Count of currently-open file dialogs (not just open-file dialogs).
static size_t sOpenDialogActors = 0;
WinFileDialogParent::WinFileDialogParent() {
MOZ_LOG(sLogFileDialog, LogLevel::Debug,
("%s %p", __PRETTY_FUNCTION__, this));
}
WinFileDialogParent::~WinFileDialogParent() {
MOZ_LOG(sLogFileDialog, LogLevel::Debug,
("%s %p", __PRETTY_FUNCTION__, this));
}
PWinFileDialogParent::nsresult WinFileDialogParent::BindToUtilityProcess(
mozilla::ipc::UtilityProcessParent* aUtilityParent) {
Endpoint<PWinFileDialogParent> parentEnd;
Endpoint<PWinFileDialogChild> childEnd;
nsresult rv = PWinFileDialog::CreateEndpoints(
mozilla::ipc::EndpointProcInfo::Current(),
aUtilityParent->OtherEndpointProcInfo(), &parentEnd, &childEnd);
if (NS_FAILED(rv)) {
MOZ_ASSERT(false, "Protocol endpoints failure");
return NS_ERROR_FAILURE;
}
if (!aUtilityParent->SendStartWinFileDialogService(std::move(childEnd))) {
MOZ_ASSERT(false, "SendStartWinFileDialogService failed");
return NS_ERROR_FAILURE;
}
if (!parentEnd.Bind(this)) {
MOZ_ASSERT(false, "parentEnd.Bind failed");
return NS_ERROR_FAILURE;
}
sOpenDialogActors++;
return NS_OK;
}
// Convert the raw IPC promise-type to a filedialog::Promise.
template <typename T, typename Ex>
static auto ConvertToFDPromise(
StaticString aMethod, // __func__
Ex&& extractor,
RefPtr<MozPromise<T, mozilla::ipc::ResponseRejectReason, true>>
aSrcPromise) {
// The extractor must produce a `mozilla::Result<..., Error>` from `T`.
using SrcResultInfo = detail::DestructureResult<std::invoke_result_t<Ex, T>>;
using ResolveT = typename SrcResultInfo::OkT;
static_assert(std::is_same_v<typename SrcResultInfo::ErrorT, Error>,
"expected T to be a Result<..., Error>");
using SrcPromiseT = MozPromise<T, mozilla::ipc::ResponseRejectReason, true>;
using DstPromiseT = MozPromise<ResolveT, Error, true>;
RefPtr<DstPromiseT> ret = aSrcPromise->Then(
mozilla::GetCurrentSerialEventTarget(), aMethod,
[extractor, aMethod](T&& val) {
mozilla::Result<ResolveT, Error> result = extractor(std::move(val));
if (result.isOk()) {
return DstPromiseT::CreateAndResolve(result.unwrap(), aMethod);
}
return DstPromiseT::CreateAndReject(result.unwrapErr(), aMethod);
},
[aMethod](typename mozilla::ipc::ResponseRejectReason&& val) {
return DstPromiseT::CreateAndReject(
MOZ_FD_ERROR(IPCError, "IPC", (uint32_t)val), aMethod);
});
return ret;
}
template <typename Input, typename Output>
struct Extractor {
template <typename Input::Type tag_, Output const& (Input::*getter_)() const>
static auto get() {
return [](Input&& res) -> Result<Output, Error> {
if (res.type() == tag_) {
return (res.*getter_)();
}
if (res.type() == Input::TRemoteError) {
RemoteError err = res.get_RemoteError();
return Err(Error{.kind = Error::RemoteError,
.where = Error::Location::Deserialize(err.where()),
.why = err.why()});
}
MOZ_ASSERT_UNREACHABLE("internal IPC failure?");
return Err(MOZ_FD_ERROR(IPCError, "internal IPC failure?", E_FAIL));
};
}
};
[[nodiscard]] RefPtr<WinFileDialogParent::ShowFileDialogPromise>
WinFileDialogParent::ShowFileDialogImpl(HWND parent, const FileDialogType& type,
mozilla::Span<Command const> commands) {
auto inner_promise = PWinFileDialogParent::SendShowFileDialog(
reinterpret_cast<WindowsHandle>(parent), type, std::move(commands));
return ConvertToFDPromise(
__func__,
Extractor<FileResult, Maybe<Results>>::get<
FileResult::TMaybeResults, &FileResult::get_MaybeResults>(),
std::move(inner_promise));
}
[[nodiscard]] RefPtr<WinFileDialogParent::ShowFolderDialogPromise>
WinFileDialogParent::ShowFolderDialogImpl(
HWND parent, mozilla::Span<Command const> commands) {
auto inner_promise = PWinFileDialogParent::SendShowFolderDialog(
reinterpret_cast<WindowsHandle>(parent), std::move(commands));
return ConvertToFDPromise(
__func__,
Extractor<FolderResult, Maybe<nsString>>::get<
FolderResult::TMaybensString, &FolderResult::get_MaybensString>(),
std::move(inner_promise));
}
void WinFileDialogParent::ProcessingError(Result aCode, const char* aReason) {
detail::LogProcessingError(sLogFileDialog, this, aCode, aReason);
}
ProcessProxy::ProcessProxy(RefPtr<WFDP>&& obj)
: data(MakeRefPtr<Contents>(std::move(obj))) {}
ProcessProxy::Contents::Contents(RefPtr<WFDP>&& obj) : ptr(std::move(obj)) {}
ProcessProxy::Contents::~Contents() {
AssertIsOnMainThread();
// destroy the actor...
ptr->Close();
// ... and possibly the process
if (!--sOpenDialogActors) {
StopProcess();
}
}
void ProcessProxy::Contents::StopProcess() {
auto const upm = ipc::UtilityProcessManager::GetSingleton();
if (!upm) {
// This is only possible when the UtilityProcessManager has shut down -- in
// which case the file-dialog process has also already been directed to shut
// down, and there's nothing we need to do here.
return;
}
MOZ_LOG(sLogFileDialog, LogLevel::Debug,
("%s: killing the WINDOWS_FILE_DIALOG process (no more live "
"actors)",
__PRETTY_FUNCTION__));
upm->CleanShutdown(ipc::SandboxingKind::WINDOWS_FILE_DIALOG);
}
} // namespace mozilla::widget::filedialog