175 lines
6 KiB
C++
175 lines
6 KiB
C++
/* -*- 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
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#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
|