summaryrefslogtreecommitdiffstats
path: root/widget/windows/filedialog/WinFileDialogCommands.h
diff options
context:
space:
mode:
Diffstat (limited to 'widget/windows/filedialog/WinFileDialogCommands.h')
-rw-r--r--widget/windows/filedialog/WinFileDialogCommands.h186
1 files changed, 178 insertions, 8 deletions
diff --git a/widget/windows/filedialog/WinFileDialogCommands.h b/widget/windows/filedialog/WinFileDialogCommands.h
index ca4561a8f2..800ce832d5 100644
--- a/widget/windows/filedialog/WinFileDialogCommands.h
+++ b/widget/windows/filedialog/WinFileDialogCommands.h
@@ -19,27 +19,200 @@ struct IFileOpenDialog;
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,
+ };
+
+ // "Enum" denoting error-location. Members are in `VALID_STRINGS`, and have no
+ // name other than their string.
+ //
+ // (Note: under C++20, this could reasonably be replaced with an `nsString`
+ // alongside a check that all constructors are either a) consteval or b) from
+ // IPC.)
+ class Location {
+ uint32_t value;
+ constexpr explicit Location(uint32_t value) : 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 std::string_view const VALID_STRINGS[] = {
+ "ApplyCommands",
+ "CoCreateInstance(CLSID_ShellLibrary)",
+ "GetFileResults: GetShellItemPath (1)",
+ "GetFileResults: GetShellItemPath (2)",
+ "GetShellItemPath",
+ "IFileDialog::GetFileTypeIndex",
+ "IFileDialog::GetOptions",
+ "IFileDialog::GetResult",
+ "IFileDialog::GetResult: item",
+ "IFileDialog::Show",
+ "IFileOpenDialog::GetResults",
+ "IFileOpenDialog::GetResults: items",
+ "IPC",
+ "IShellItemArray::GetCount",
+ "IShellItemArray::GetItemAt",
+ "MakeFileDialog",
+ "NS_NewNamedThread",
+ "Save + FOS_ALLOWMULTISELECT",
+ "ShowFilePicker",
+ "ShowFolderPicker",
+ "ShowRemote: UtilityProcessManager::GetSingleton",
+ "ShowRemote: invocation of CreateWinFileDialogActor",
+ "UtilityProcessManager::CreateWinFileDialogActor",
+ "internal IPC failure?",
+ };
+ 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 (!(VALID_STRINGS[i] < VALID_STRINGS[i + 1])) {
+ return false;
+ }
+ }
+ return true;
+ }(),
+ "VALID_STRINGS should be ASCIIbetically sorted");
+
+ public:
+ constexpr uint32_t Serialize() const { return value; }
+ constexpr static Location Deserialize(uint32_t val) {
+ return Location{val};
+ }
+
+ public:
+ constexpr static Location npos() { return Location{~uint32_t(0)}; }
+
+ constexpr bool IsValid() const { return value < VALID_STRINGS_COUNT; }
+
+ constexpr std::string_view ToString() const {
+ return value < VALID_STRINGS_COUNT ? VALID_STRINGS[value]
+ : "<bad filedialog::Error::Location?>";
+ }
+ constexpr static Location FromString(std::string_view str) {
+ for (uint32_t i = 0; i < VALID_STRINGS_COUNT; ++i) {
+ if (str == VALID_STRINGS[i]) return Location{i};
+ }
+ return npos();
+ }
+
+ constexpr char const* c_str() const { return ToString().data(); }
+ };
+
+ // Where and how (run-time) this error occurred.
+ Kind kind;
+ // Where (compile-time) this error occurred.
+ Location where;
+ // Why (run-time) this error occurred. Probably an HRESULT.
+ uint32_t why;
+
+ // `impl Debug for Kind`
+ static const char* KindName(Kind);
+};
+
+// Create a filedialog::Error, confirming at compile-time that the supplied
+// where-string is valid.
+#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.IsValid(), \
+ "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>, HRESULT> MakeFileDialog(FileDialogType);
+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.)
-[[nodiscard]] HRESULT ApplyCommands(::IFileDialog*,
- nsTArray<Command> const& commands);
+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, HRESULT> GetFileResults(::IFileDialog*);
+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, HRESULT> GetFolderResults(::IFileDialog*);
+mozilla::Result<nsString, Error> GetFolderResults(::IFileDialog*);
namespace detail {
// Log the error. If it's a notable error, kill the child process.
@@ -48,9 +221,6 @@ void LogProcessingError(LogModule* aModule, ipc::IProtocol* aCaller,
} // namespace detail
-template <typename R>
-using Promise = MozPromise<R, HRESULT, true>;
-
// Show a file-picker on another thread in the current process.
RefPtr<Promise<Maybe<Results>>> SpawnFilePicker(HWND parent,
FileDialogType type,