282 lines
10 KiB
C++
282 lines
10 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/. */
|
|
|
|
#ifndef widget_windows_filedialog_WinFileDialogCommands_h__
|
|
#define widget_windows_filedialog_WinFileDialogCommands_h__
|
|
|
|
#include "ipc/EnumSerializer.h"
|
|
#include "mozilla/Logging.h"
|
|
#include "mozilla/MozPromise.h"
|
|
#include "mozilla/ipc/MessageLink.h"
|
|
#include "mozilla/widget/filedialog/WinFileDialogCommandsDefn.h"
|
|
|
|
// Windows interface types, defined in <shobjidl.h>
|
|
struct IFileDialog;
|
|
struct IFileOpenDialog;
|
|
|
|
namespace mozilla::ipc {
|
|
class LaunchError;
|
|
}
|
|
|
|
namespace mozilla::widget::filedialog {
|
|
|
|
namespace detail {
|
|
|
|
template <typename T, typename E, bool B>
|
|
struct PromiseInfo {
|
|
using ResolveT = T;
|
|
using RejectT = E;
|
|
constexpr static const bool IsExclusive = B;
|
|
using Promise = MozPromise<T, E, B>;
|
|
};
|
|
|
|
template <typename P>
|
|
auto DestructurePromiseImpl(P&&) {
|
|
// Debugging hint: A type in the instantiation chain (here named `P`) was
|
|
// expected to be a `RefPtr<MozPromise<...>>`, but was some other type.
|
|
static_assert(false, "expected P = RefPtr<MozPromise< ... >>");
|
|
}
|
|
|
|
template <typename T, typename E, bool B>
|
|
auto DestructurePromiseImpl(RefPtr<MozPromise<T, E, B>>&&)
|
|
-> PromiseInfo<T, E, B>;
|
|
|
|
template <typename P>
|
|
using DestructurePromise =
|
|
std::decay_t<decltype(DestructurePromiseImpl(std::declval<P>()))>;
|
|
|
|
template <typename T, typename E>
|
|
struct ResultInfo {
|
|
using OkT = T;
|
|
using ErrorT = E;
|
|
};
|
|
|
|
template <typename R>
|
|
auto DestructureResultImpl(R&&) {
|
|
// Debugging hint: A type in the instantiation chain (here named `R`) was
|
|
// expected to be a `mozilla::Result<...>`, but was some other type.
|
|
static_assert(false, "expected R = mozilla::Result< ... >");
|
|
}
|
|
|
|
template <typename T, typename E>
|
|
auto DestructureResultImpl(mozilla::Result<T, E>&&) -> ResultInfo<T, E>;
|
|
|
|
template <typename R>
|
|
using DestructureResult =
|
|
std::decay_t<decltype(DestructureResultImpl(std::declval<R>()))>;
|
|
|
|
#define MOZ_ASSERT_SAME_TYPE(T1, T2, ...) \
|
|
static_assert(std::is_same_v<T1, T2>, ##__VA_ARGS__)
|
|
} // namespace detail
|
|
|
|
extern LazyLogModule sLogFileDialog;
|
|
|
|
// Simple struct for reporting errors.
|
|
struct Error {
|
|
enum Kind {
|
|
// This error's source was within the local (current) process.
|
|
LocalError,
|
|
// This error's source was within the remote host process, and it was
|
|
// reported via noncatastrophic channels.
|
|
RemoteError,
|
|
// This error was reported via the IPC subsystem. (This includes unexpected
|
|
// remote host-process crashes.)
|
|
IPCError,
|
|
};
|
|
|
|
constexpr static const StaticString kInvalidValue{
|
|
"<bad filedialog::Error::Location?>"};
|
|
|
|
// "Enum" denoting error-location. Members are in `VALID_STRINGS`, and have no
|
|
// name other than their string.
|
|
//
|
|
// The "enum" also supports importing "dynamic" strings from ipc::LaunchError.
|
|
// These strings will not be present in the VALID_STRINGS set and so cannot be
|
|
// faithfully serialized for IPC -- which is fine, since definitionally if you
|
|
// get one you won't have an IPC connection to worry about passing it over
|
|
// anyway.
|
|
class Location {
|
|
// The printable form of the location.
|
|
StaticString text;
|
|
// The numeric value for the location.
|
|
//
|
|
// This will be less than `VALID_STRINGS_COUNT` iff the text of `text` is
|
|
// equal to that of some string in `VALID_STRINGS`; other values have no
|
|
// individual significance.
|
|
uint32_t value;
|
|
|
|
constexpr explicit Location(uint32_t value)
|
|
: text(value < VALID_STRINGS_COUNT ? StaticString(VALID_STRINGS[value])
|
|
: kInvalidValue),
|
|
value(value) {}
|
|
|
|
// Valid locations for errors. (Indices do not need to remain stable between
|
|
// releases; but -- where meaningful -- string values themselves should, for
|
|
// ease of telemetry-aggregation.)
|
|
constexpr static nsLiteralCString const VALID_STRINGS[] = {
|
|
"ApplyCommands"_ns,
|
|
"CoCreateInstance(CLSID_ShellLibrary)"_ns,
|
|
"GetFileResults: GetShellItemPath (1)"_ns,
|
|
"GetFileResults: GetShellItemPath (2)"_ns,
|
|
"GetShellItemPath"_ns,
|
|
"IFileDialog::GetFileTypeIndex"_ns,
|
|
"IFileDialog::GetOptions"_ns,
|
|
"IFileDialog::GetResult"_ns,
|
|
"IFileDialog::GetResult: item"_ns,
|
|
"IFileDialog::Show"_ns,
|
|
"IFileOpenDialog::GetResults"_ns,
|
|
"IFileOpenDialog::GetResults: items"_ns,
|
|
"IPC"_ns,
|
|
"IShellItemArray::GetCount"_ns,
|
|
"IShellItemArray::GetItemAt"_ns,
|
|
"MakeFileDialog"_ns,
|
|
"NS_NewNamedThread"_ns,
|
|
"Save + FOS_ALLOWMULTISELECT"_ns,
|
|
"ShowFilePicker"_ns,
|
|
"ShowFolderPicker"_ns,
|
|
"ShowRemote: UtilityProcessManager::GetSingleton"_ns,
|
|
"ShowRemote: invocation of CreateWinFileDialogActor"_ns,
|
|
"UtilityProcessManager::CreateWinFileDialogActor"_ns,
|
|
"file-picker opened in directory-picker mode"_ns,
|
|
"internal IPC failure?"_ns,
|
|
};
|
|
constexpr static size_t VALID_STRINGS_COUNT =
|
|
std::extent_v<decltype(VALID_STRINGS)>;
|
|
|
|
// Prevent duplicates from occurring in VALID_STRINGS by forcing it to be
|
|
// sorted. (Note that std::is_sorted is not constexpr until C++20.)
|
|
static_assert(
|
|
[]() {
|
|
for (size_t i = 0; i + 1 < VALID_STRINGS_COUNT; ++i) {
|
|
if (!(std::string_view{VALID_STRINGS[i]} <
|
|
std::string_view{VALID_STRINGS[i + 1]})) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}(),
|
|
"VALID_STRINGS should be ASCIIbetically sorted");
|
|
|
|
public:
|
|
// A LaunchError's carried location won't be in the list and so is not
|
|
// IPDL-serializable -- but it's still telemetry-safe.
|
|
constexpr explicit Location(mozilla::ipc::LaunchError const& err);
|
|
|
|
constexpr uint32_t Serialize() const { return value; }
|
|
constexpr static Location Deserialize(uint32_t val) {
|
|
MOZ_ASSERT(val < VALID_STRINGS_COUNT);
|
|
return Location(val);
|
|
}
|
|
|
|
public:
|
|
constexpr static Location npos() { return Location(~uint32_t(0)); }
|
|
|
|
// True iff this Location was formed from a string in VALID_STRINGS.
|
|
constexpr bool IsSerializable() const {
|
|
return value < VALID_STRINGS_COUNT;
|
|
}
|
|
|
|
constexpr StaticString ToString() const { return text; }
|
|
|
|
constexpr static Location FromString(StaticString str) {
|
|
std::string_view val(str.get());
|
|
for (uint32_t i = 0; i < VALID_STRINGS_COUNT; ++i) {
|
|
if (val == VALID_STRINGS[i]) {
|
|
return Location(i);
|
|
}
|
|
}
|
|
// TODO(C++20): make this `consteval` and fail here, eliminating any need
|
|
// for a return value for the fallthrough case.
|
|
return npos();
|
|
}
|
|
|
|
constexpr char const* c_str() const { return ToString().get(); }
|
|
}; // class Error::Location
|
|
|
|
// Where and how (run-time) this error occurred.
|
|
Kind kind;
|
|
// Where (compile-time) this error occurred.
|
|
Location where;
|
|
// Why (run-time) this error occurred. Usually an HRESULT, but its
|
|
// interpretation is necessarily dependent on the value of `where`.
|
|
uint32_t why;
|
|
|
|
// `impl Debug for Kind`
|
|
static const char* KindName(Kind);
|
|
|
|
// Constructs an error of Kind "LocalError" (since launch-errors are always
|
|
// considered local).
|
|
static Error From(mozilla::ipc::LaunchError const&);
|
|
};
|
|
|
|
// Create a filedialog::Error from a where-string, confirming at compile-time
|
|
// that the supplied where-string is validly serializable.
|
|
#define MOZ_FD_ERROR(kind_, where_, why_) \
|
|
([](HRESULT why_arg_) -> ::mozilla::widget::filedialog::Error { \
|
|
using Error = ::mozilla::widget::filedialog::Error; \
|
|
constexpr static const Error::Location loc = \
|
|
Error::Location::FromString(where_); \
|
|
static_assert( \
|
|
loc.IsSerializable(), \
|
|
"filedialog::Error: location not found in Error::VALID_STRINGS"); \
|
|
return Error{ \
|
|
.kind = Error::kind_, .where = loc, .why = (uint32_t)why_arg_}; \
|
|
}(why_))
|
|
|
|
// Create a filedialog::Error of kind LocalError (the usual case).
|
|
#define MOZ_FD_LOCAL_ERROR(where_, why_) MOZ_FD_ERROR(LocalError, where_, why_)
|
|
|
|
template <typename R>
|
|
using Promise = MozPromise<R, Error, true>;
|
|
|
|
enum class FileDialogType : uint8_t { Open, Save };
|
|
|
|
// Create a file-dialog of the relevant type. Requires MSCOM to be initialized.
|
|
mozilla::Result<RefPtr<IFileDialog>, Error> MakeFileDialog(FileDialogType);
|
|
|
|
// Apply the selected commands to the IFileDialog, in preparation for showing
|
|
// it. (The actual showing step is left to the caller.)
|
|
mozilla::Result<Ok, Error> ApplyCommands(::IFileDialog*,
|
|
nsTArray<Command> const& commands);
|
|
|
|
// Extract one or more results from the file-picker dialog.
|
|
//
|
|
// Requires that Show() has been called and has returned S_OK.
|
|
mozilla::Result<Results, Error> GetFileResults(::IFileDialog*);
|
|
|
|
// Extract the chosen folder from the folder-picker dialog.
|
|
//
|
|
// Requires that Show() has been called and has returned S_OK.
|
|
mozilla::Result<nsString, Error> GetFolderResults(::IFileDialog*);
|
|
|
|
namespace detail {
|
|
// Log the error. If it's a notable error, kill the child process.
|
|
void LogProcessingError(LogModule* aModule, ipc::IProtocol* aCaller,
|
|
ipc::HasResultCodes::Result aCode, const char* aReason);
|
|
|
|
} // namespace detail
|
|
|
|
// Show a file-picker on another thread in the current process.
|
|
RefPtr<Promise<Maybe<Results>>> SpawnFilePicker(HWND parent,
|
|
FileDialogType type,
|
|
nsTArray<Command> commands);
|
|
|
|
// Show a folder-picker on another thread in the current process.
|
|
RefPtr<Promise<Maybe<nsString>>> SpawnFolderPicker(HWND parent,
|
|
nsTArray<Command> commands);
|
|
|
|
} // namespace mozilla::widget::filedialog
|
|
|
|
namespace IPC {
|
|
template <>
|
|
struct ParamTraits<mozilla::widget::filedialog::FileDialogType>
|
|
: public ContiguousEnumSerializerInclusive<
|
|
mozilla::widget::filedialog::FileDialogType,
|
|
mozilla::widget::filedialog::FileDialogType::Open,
|
|
mozilla::widget::filedialog::FileDialogType::Save> {};
|
|
} // namespace IPC
|
|
|
|
#endif // widget_windows_filedialog_WinFileDialogCommands_h__
|