Bug 1837079 - [3/10] Refactor Windows nsFilePicker dialog creation r=gstoll,handyman,win-reviewers,mhowell

Extract the creation of Windows file dialogs into a separate function,
as preparation for (sometimes) performing that remotely (that is, out-
of-process).

No functional changes.

Differential Revision: https://phabricator.services.mozilla.com/D180340
This commit is contained in:
Ray Kraesig 2023-10-26 18:21:27 +00:00
parent f72bfe06f7
commit c5c346391a
4 changed files with 133 additions and 47 deletions

View File

@ -97,6 +97,19 @@ static HRESULT GetShellItemPath(IShellItem* aItem, nsString& aResultString) {
if (FAILED(_tmp_hr_)) return Err(_tmp_hr_); \
} while (0)
mozilla::Result<RefPtr<IFileDialog>, HRESULT> MakeFileDialog(
FileDialogType type) {
RefPtr<IFileDialog> dialog;
CLSID const clsid = type == FileDialogType::Open ? CLSID_FileOpenDialog
: CLSID_FileSaveDialog;
HRESULT const hr = CoCreateInstance(clsid, nullptr, CLSCTX_INPROC_SERVER,
IID_IFileDialog, getter_AddRefs(dialog));
MOZ_ENSURE_HRESULT_OK(hr);
return std::move(dialog);
}
HRESULT ApplyCommands(::IFileDialog* dialog,
nsTArray<Command> const& commands) {
Applicator applicator{.dialog = dialog};

View File

@ -9,10 +9,17 @@
#include "mozilla/widget/filedialog/WinFileDialogCommandsDefn.h"
// Windows interface type, defined in <shobjidl_core.h>
// Windows interface types, defined in <shobjidl.h>
struct IFileDialog;
struct IFileOpenDialog;
namespace mozilla::widget::filedialog {
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);
// 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*,

View File

@ -27,6 +27,8 @@
#include "mozilla/widget/filedialog/WinFileDialogCommands.h"
using mozilla::Maybe;
using mozilla::Result;
using mozilla::UniquePtr;
using namespace mozilla::widget;
@ -78,6 +80,62 @@ NS_IMETHODIMP nsFilePicker::Init(mozIDOMWindowProxy* aParent,
return nsBaseFilePicker::Init(aParent, aTitle, aMode);
}
/* static */
Result<Maybe<filedialog::Results>, HRESULT> nsFilePicker::ShowFilePickerLocal(
HWND parent, filedialog::FileDialogType type,
nsTArray<filedialog::Command> const& commands) {
using mozilla::Err;
using mozilla::Nothing;
using mozilla::Some;
namespace fd = filedialog;
RefPtr<IFileDialog> dialog;
MOZ_TRY_VAR(dialog, fd::MakeFileDialog(type));
if (auto const res = fd::ApplyCommands(dialog.get(), commands); FAILED(res)) {
return Err(res);
}
// synchronously show the dialog
auto const ret = dialog->Show(parent);
if (ret == HRESULT_FROM_WIN32(ERROR_CANCELLED)) {
return Maybe<fd::Results>(Nothing());
}
if (FAILED(ret)) {
return Err(ret);
}
return fd::GetFileResults(dialog.get()).map(mozilla::Some<fd::Results>);
}
/* static */
Result<Maybe<nsString>, HRESULT> nsFilePicker::ShowFolderPickerLocal(
HWND parent, nsTArray<filedialog::Command> const& commands) {
using mozilla::Err;
using mozilla::Nothing;
using mozilla::Some;
namespace fd = filedialog;
RefPtr<IFileDialog> dialog;
MOZ_TRY_VAR(dialog, fd::MakeFileDialog(fd::FileDialogType::Open));
if (auto const res = fd::ApplyCommands(dialog.get(), commands); FAILED(res)) {
return Err(res);
}
// synchronously show the dialog
auto const ret = dialog->Show(parent);
if (ret == HRESULT_FROM_WIN32(ERROR_CANCELLED)) {
return Maybe<nsString>(Nothing());
}
if (FAILED(ret)) {
return Err(ret);
}
return fd::GetFolderResults(dialog.get()).map(mozilla::Some<nsString>);
}
/*
* Folder picker invocation
*/
@ -90,13 +148,6 @@ NS_IMETHODIMP nsFilePicker::Init(mozIDOMWindowProxy* aParent,
* @return true if a file was selected successfully.
*/
bool nsFilePicker::ShowFolderPicker(const nsString& aInitialDir) {
RefPtr<IFileOpenDialog> dialog;
if (FAILED(CoCreateInstance(CLSID_FileOpenDialog, nullptr,
CLSCTX_INPROC_SERVER, IID_IFileOpenDialog,
getter_AddRefs(dialog)))) {
return false;
}
namespace fd = ::mozilla::widget::filedialog;
nsTArray<fd::Command> commands = {
fd::SetOptions(FOS_PICKFOLDERS),
@ -111,25 +162,28 @@ bool nsFilePicker::ShowFolderPicker(const nsString& aInitialDir) {
commands.AppendElement(fd::SetFolder(aInitialDir));
}
nsString result;
{
if (FAILED(fd::ApplyCommands(dialog, commands))) {
return false;
}
ScopedRtlShimWindow shim(mParentWidget.get());
mozilla::BackgroundHangMonitor().NotifyWait();
AutoWidgetPickerState awps(mParentWidget);
if (FAILED(dialog->Show(shim.get()))) {
mozilla::BackgroundHangMonitor().NotifyWait();
auto res = ShowFolderPickerLocal(shim.get(), commands);
if (res.isErr()) {
NS_WARNING("ShowFolderPickerImpl failed");
return false;
}
auto optResults = res.unwrap();
if (!optResults) {
// cancellation, not error
return false;
}
result = optResults.extract();
}
auto result = fd::GetFolderResults(dialog.get());
if (result.isErr()) {
return false;
}
mUnicodeFile = result.unwrap();
mUnicodeFile = result;
return true;
}
@ -147,21 +201,6 @@ bool nsFilePicker::ShowFolderPicker(const nsString& aInitialDir) {
bool nsFilePicker::ShowFilePicker(const nsString& aInitialDir) {
AUTO_PROFILER_LABEL("nsFilePicker::ShowFilePicker", OTHER);
RefPtr<IFileDialog> dialog;
if (mMode != modeSave) {
if (FAILED(CoCreateInstance(CLSID_FileOpenDialog, nullptr,
CLSCTX_INPROC_SERVER, IID_IFileOpenDialog,
getter_AddRefs(dialog)))) {
return false;
}
} else {
if (FAILED(CoCreateInstance(CLSID_FileSaveDialog, nullptr,
CLSCTX_INPROC_SERVER, IID_IFileSaveDialog,
getter_AddRefs(dialog)))) {
return false;
}
}
namespace fd = ::mozilla::widget::filedialog;
nsTArray<fd::Command> commands;
// options
@ -241,29 +280,33 @@ bool nsFilePicker::ShowFilePicker(const nsString& aInitialDir) {
}
// display
fd::Results result;
{
if (FAILED(fd::ApplyCommands(dialog, commands))) {
return false;
}
ScopedRtlShimWindow shim(mParentWidget.get());
AutoWidgetPickerState awps(mParentWidget);
mozilla::BackgroundHangMonitor().NotifyWait();
if (FAILED(dialog->Show(shim.get()))) {
auto res = ShowFilePickerLocal(
shim.get(),
mMode == modeSave ? FileDialogType::Save : FileDialogType::Open,
commands);
if (res.isErr()) {
NS_WARNING("ShowFilePickerImpl failed");
return false;
}
}
// results
auto result_ = fd::GetFileResults(dialog.get());
if (result_.isErr()) {
return false;
auto optResults = res.unwrap();
if (!optResults) {
// cancellation, not error
return false;
}
result = optResults.extract();
}
auto result = result_.unwrap();
// Remember what filter type the user selected
mSelectedType = result.selectedFileTypeIndex();
mSelectedType = int32_t(result.selectedFileTypeIndex());
auto const& paths = result.paths();

View File

@ -21,6 +21,12 @@
class nsILoadContext;
namespace mozilla::widget::filedialog {
class Command;
class Results;
enum class FileDialogType : uint8_t;
} // namespace mozilla::widget::filedialog
class nsBaseWinFilePicker : public nsBaseFilePicker {
public:
NS_IMETHOD GetDefaultString(nsAString& aDefaultString) override;
@ -41,6 +47,15 @@ class nsBaseWinFilePicker : public nsBaseFilePicker {
class nsFilePicker : public nsBaseWinFilePicker {
virtual ~nsFilePicker() = default;
template <typename T>
using Maybe = mozilla::Maybe<T>;
template <typename T>
using Result = mozilla::Result<T, HRESULT>;
using Command = mozilla::widget::filedialog::Command;
using Results = mozilla::widget::filedialog::Results;
using FileDialogType = mozilla::widget::filedialog::FileDialogType;
public:
nsFilePicker();
@ -66,6 +81,14 @@ class nsFilePicker : public nsBaseWinFilePicker {
void GetFilterListArray(nsString& aFilterList);
bool ShowFolderPicker(const nsString& aInitialDir);
bool ShowFilePicker(const nsString& aInitialDir);
private:
static Result<Maybe<Results>> ShowFilePickerLocal(
HWND aParent, FileDialogType type, nsTArray<Command> const& commands);
static Result<Maybe<nsString>> ShowFolderPickerLocal(
HWND aParent, nsTArray<Command> const& commands);
protected:
void RememberLastUsedDirectory();
bool IsPrivacyModeEnabled();
bool IsDefaultPathLink();