Instead of passing potentially-dynamic strings over the IPDL interface, pass an integer which corresponds to one of a fixed set of static strings. (This is basically an enum with more steps -- or with fewer steps, depending on which side of it you're standing on.) This ensures that the set of possible location-strings is fixed, which in turn both ensures that aggregation of telemetry stays relatively simple and guards against accidental leakage of PII thereinto. (Or, alternatively and equivalently, this minimizes the code region that data-review needs to consider once the location string is reported to telemetry.) Note that we do not yet actually report the location-string via telemetry; that's planned to occur in bug 1884221. Differential Revision: https://phabricator.services.mozilla.com/D205169
175 lines
6.1 KiB
C++
175 lines
6.1 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/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(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 <typename T, typename Ex, size_t N>
|
|
static auto ConvertToFDPromise(
|
|
const char (&aMethod)[N], // __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
|