/* -*- 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 struct IFileDialog; struct IFileOpenDialog; namespace mozilla::widget::filedialog { namespace detail { template struct PromiseInfo { using ResolveT = T; using RejectT = E; constexpr static const bool IsExclusive = B; using Promise = MozPromise; }; template auto DestructurePromiseImpl(P&&) { // Debugging hint: A type in the instantiation chain (here named `P`) was // expected to be a `RefPtr>`, but was some other type. static_assert(false, "expected P = RefPtr>"); } template auto DestructurePromiseImpl(RefPtr>&&) -> PromiseInfo; template using DestructurePromise = std::decay_t()))>; template struct ResultInfo { using OkT = T; using ErrorT = E; }; template 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 auto DestructureResultImpl(mozilla::Result&&) -> ResultInfo; template using DestructureResult = std::decay_t()))>; #define MOZ_ASSERT_SAME_TYPE(T1, T2, ...) \ static_assert(std::is_same_v, ##__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; // 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] : ""; } 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 using Promise = MozPromise; enum class FileDialogType : uint8_t { Open, Save }; // Create a file-dialog of the relevant type. Requires MSCOM to be initialized. mozilla::Result, 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 ApplyCommands(::IFileDialog*, nsTArray 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 GetFileResults(::IFileDialog*); // Extract the chosen folder from the folder-picker dialog. // // Requires that Show() has been called and has returned S_OK. mozilla::Result 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>> SpawnFilePicker(HWND parent, FileDialogType type, nsTArray commands); // Show a folder-picker on another thread in the current process. RefPtr>> SpawnFolderPicker(HWND parent, nsTArray commands); } // namespace mozilla::widget::filedialog namespace IPC { template <> struct ParamTraits : public ContiguousEnumSerializerInclusive< mozilla::widget::filedialog::FileDialogType, mozilla::widget::filedialog::FileDialogType::Open, mozilla::widget::filedialog::FileDialogType::Save> {}; } // namespace IPC #endif // widget_windows_filedialog_WinFileDialogCommands_h__