/* -*- 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/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 parentEnd; Endpoint childEnd; nsresult rv = PWinFileDialog::CreateEndpoints(base::GetCurrentProcId(), aUtilityParent->OtherPid(), &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 static auto ConvertToFDPromise( const char (&aMethod)[N], // __func__ Ex&& extractor, RefPtr> aSrcPromise) { // The extractor must produce a `mozilla::Result<..., Error>` from `T`. using SrcResultInfo = detail::DestructureResult>; using ResolveT = typename SrcResultInfo::OkT; static_assert(std::is_same_v, "expected T to be a Result<..., Error>"); using SrcPromiseT = MozPromise; using DstPromiseT = MozPromise; RefPtr ret = aSrcPromise->Then( mozilla::GetCurrentSerialEventTarget(), aMethod, [extractor, aMethod](T&& val) { mozilla::Result 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 struct Extractor { template static auto get() { return [](Input&& res) -> Result { 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::ShowFileDialogImpl(HWND parent, const FileDialogType& type, mozilla::Span commands) { auto inner_promise = PWinFileDialogParent::SendShowFileDialog( reinterpret_cast(parent), type, std::move(commands)); return ConvertToFDPromise( __func__, Extractor>::get< FileResult::TMaybeResults, &FileResult::get_MaybeResults>(), std::move(inner_promise)); } [[nodiscard]] RefPtr WinFileDialogParent::ShowFolderDialogImpl( HWND parent, mozilla::Span commands) { auto inner_promise = PWinFileDialogParent::SendShowFolderDialog( reinterpret_cast(parent), std::move(commands)); return ConvertToFDPromise( __func__, Extractor>::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&& obj) : data(MakeRefPtr(std::move(obj))) {} ProcessProxy::Contents::Contents(RefPtr&& 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