summaryrefslogtreecommitdiffstats
path: root/widget/windows/filedialog
diff options
context:
space:
mode:
Diffstat (limited to 'widget/windows/filedialog')
-rw-r--r--widget/windows/filedialog/WinFileDialogCommands.cpp196
-rw-r--r--widget/windows/filedialog/WinFileDialogCommands.h32
-rw-r--r--widget/windows/filedialog/WinFileDialogCommandsDefn.ipdlh49
-rw-r--r--widget/windows/filedialog/moz.build22
4 files changed, 299 insertions, 0 deletions
diff --git a/widget/windows/filedialog/WinFileDialogCommands.cpp b/widget/windows/filedialog/WinFileDialogCommands.cpp
new file mode 100644
index 0000000000..756e6b1cf3
--- /dev/null
+++ b/widget/windows/filedialog/WinFileDialogCommands.cpp
@@ -0,0 +1,196 @@
+/* -*- 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/WinFileDialogCommands.h"
+
+#include <shobjidl.h>
+#include <shtypes.h>
+#include <winerror.h>
+#include "WinUtils.h"
+
+namespace mozilla::widget::filedialog {
+
+// Visitor to apply commands to the dialog.
+struct Applicator {
+ IFileDialog* dialog = nullptr;
+
+ HRESULT Visit(Command const& c) {
+ switch (c.type()) {
+ default:
+ case Command::T__None:
+ return E_INVALIDARG;
+
+ case Command::TSetOptions:
+ return Apply(c.get_SetOptions());
+ case Command::TSetTitle:
+ return Apply(c.get_SetTitle());
+ case Command::TSetOkButtonLabel:
+ return Apply(c.get_SetOkButtonLabel());
+ case Command::TSetFolder:
+ return Apply(c.get_SetFolder());
+ case Command::TSetFileName:
+ return Apply(c.get_SetFileName());
+ case Command::TSetDefaultExtension:
+ return Apply(c.get_SetDefaultExtension());
+ case Command::TSetFileTypes:
+ return Apply(c.get_SetFileTypes());
+ case Command::TSetFileTypeIndex:
+ return Apply(c.get_SetFileTypeIndex());
+ }
+ }
+
+ HRESULT Apply(SetOptions const& c) { return dialog->SetOptions(c.options()); }
+ HRESULT Apply(SetTitle const& c) { return dialog->SetTitle(c.title().get()); }
+ HRESULT Apply(SetOkButtonLabel const& c) {
+ return dialog->SetOkButtonLabel(c.label().get());
+ }
+ HRESULT Apply(SetFolder const& c) {
+ RefPtr<IShellItem> folder;
+ if (SUCCEEDED(SHCreateItemFromParsingName(
+ c.path().get(), nullptr, IID_IShellItem, getter_AddRefs(folder)))) {
+ return dialog->SetFolder(folder);
+ }
+ // graciously accept that the provided path may have been nonsense
+ return S_OK;
+ }
+ HRESULT Apply(SetFileName const& c) {
+ return dialog->SetFileName(c.filename().get());
+ }
+ HRESULT Apply(SetDefaultExtension const& c) {
+ return dialog->SetDefaultExtension(c.extension().get());
+ }
+ HRESULT Apply(SetFileTypes const& c) {
+ std::vector<COMDLG_FILTERSPEC> vec;
+ for (auto const& filter : c.filterList()) {
+ vec.push_back(
+ {.pszName = filter.name().get(), .pszSpec = filter.spec().get()});
+ }
+ return dialog->SetFileTypes(vec.size(), vec.data());
+ }
+ HRESULT Apply(SetFileTypeIndex const& c) {
+ return dialog->SetFileTypeIndex(c.index());
+ }
+};
+
+namespace {
+static HRESULT GetShellItemPath(IShellItem* aItem, nsString& aResultString) {
+ NS_ENSURE_TRUE(aItem, E_INVALIDARG);
+
+ LPWSTR str = nullptr;
+ auto const onExit = MakeScopeExit([&]() { CoTaskMemFree(str); });
+
+ HRESULT const hr = aItem->GetDisplayName(SIGDN_FILESYSPATH, &str);
+ if (SUCCEEDED(hr)) {
+ aResultString.Assign(str);
+ }
+ return hr;
+}
+} // namespace
+
+#define MOZ_ENSURE_HRESULT_OK(call_) \
+ do { \
+ HRESULT const hr = (call_); \
+ if (FAILED(hr)) return Err(nsresult(hr)); \
+ } while (0)
+
+nsresult ApplyCommands(::IFileDialog* dialog,
+ nsTArray<Command> const& commands) {
+ Applicator applicator{.dialog = dialog};
+ for (auto const& cmd : commands) {
+ MOZ_ENSURE_HRESULT_OK(applicator.Visit(cmd));
+ }
+ return NS_OK;
+}
+
+mozilla::Result<Results, nsresult> GetFileResults(::IFileDialog* dialog) {
+ FILEOPENDIALOGOPTIONS fos;
+ MOZ_ENSURE_HRESULT_OK(dialog->GetOptions(&fos));
+
+ using widget::WinUtils;
+
+ // Extract which filter type the user selected
+ UINT index;
+ MOZ_ENSURE_HRESULT_OK(dialog->GetFileTypeIndex(&index));
+
+ // single selection
+ if ((fos & FOS_ALLOWMULTISELECT) == 0) {
+ RefPtr<IShellItem> item;
+ MOZ_ENSURE_HRESULT_OK(dialog->GetResult(getter_AddRefs(item)));
+ if (!item) {
+ return Err(nsresult::NS_ERROR_FAILURE);
+ }
+
+ nsAutoString path;
+ MOZ_ENSURE_HRESULT_OK(GetShellItemPath(item, path));
+
+ return Results({path}, index);
+ }
+
+ // multiple selection
+ RefPtr<IFileOpenDialog> openDlg;
+ dialog->QueryInterface(IID_IFileOpenDialog, getter_AddRefs(openDlg));
+ if (!openDlg) {
+ MOZ_ASSERT(false, "a file-save dialog was given FOS_ALLOWMULTISELECT?");
+ return Err(NS_ERROR_UNEXPECTED);
+ }
+
+ RefPtr<IShellItemArray> items;
+ MOZ_ENSURE_HRESULT_OK(openDlg->GetResults(getter_AddRefs(items)));
+ if (!items) {
+ return Err(NS_ERROR_FAILURE);
+ }
+
+ nsTArray<nsString> paths;
+
+ DWORD count = 0;
+ MOZ_ENSURE_HRESULT_OK(items->GetCount(&count));
+ for (DWORD idx = 0; idx < count; idx++) {
+ RefPtr<IShellItem> item;
+ MOZ_ENSURE_HRESULT_OK(items->GetItemAt(idx, getter_AddRefs(item)));
+
+ nsAutoString str;
+ MOZ_ENSURE_HRESULT_OK(GetShellItemPath(item, str));
+
+ paths.EmplaceBack(str);
+ }
+
+ return Results(std::move(paths), std::move(index));
+}
+
+mozilla::Result<nsString, nsresult> GetFolderResults(::IFileDialog* dialog) {
+ RefPtr<IShellItem> item;
+ MOZ_ENSURE_HRESULT_OK(dialog->GetResult(getter_AddRefs(item)));
+ if (!item) {
+ // shouldn't happen -- probably a precondition failure on our part, but
+ // might be due to misbehaving shell extensions?
+ MOZ_ASSERT(false,
+ "unexpected lack of item: was `Show`'s return value checked?");
+ return Err(NS_ERROR_FAILURE);
+ }
+
+ // If the user chose a Win7 Library, resolve to the library's
+ // default save folder.
+ RefPtr<IShellLibrary> shellLib;
+ RefPtr<IShellItem> folderPath;
+ MOZ_ENSURE_HRESULT_OK(
+ CoCreateInstance(CLSID_ShellLibrary, nullptr, CLSCTX_INPROC_SERVER,
+ IID_IShellLibrary, getter_AddRefs(shellLib)));
+
+ if (shellLib && SUCCEEDED(shellLib->LoadLibraryFromItem(item, STGM_READ)) &&
+ SUCCEEDED(shellLib->GetDefaultSaveFolder(DSFT_DETECT, IID_IShellItem,
+ getter_AddRefs(folderPath)))) {
+ item.swap(folderPath);
+ }
+
+ // get the folder's file system path
+ nsAutoString str;
+ MOZ_ENSURE_HRESULT_OK(GetShellItemPath(item, str));
+ return str;
+}
+
+#undef MOZ_ENSURE_HRESULT_OK
+
+} // namespace mozilla::widget::filedialog
diff --git a/widget/windows/filedialog/WinFileDialogCommands.h b/widget/windows/filedialog/WinFileDialogCommands.h
new file mode 100644
index 0000000000..fcb93eb0f1
--- /dev/null
+++ b/widget/windows/filedialog/WinFileDialogCommands.h
@@ -0,0 +1,32 @@
+/* -*- 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 "mozilla/widget/filedialog/WinFileDialogCommandsDefn.h"
+
+// Windows interface type, defined in <shobjidl_core.h>
+struct IFileDialog;
+
+namespace mozilla::widget::filedialog {
+// Apply the selected commands to the IFileDialog, in preparation for showing
+// it. (The actual showing step is left to the caller.)
+[[nodiscard]] nsresult 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, nsresult> 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, nsresult> GetFolderResults(::IFileDialog*);
+} // namespace mozilla::widget::filedialog
+
+#endif // widget_windows_filedialog_WinFileDialogCommands_h__
diff --git a/widget/windows/filedialog/WinFileDialogCommandsDefn.ipdlh b/widget/windows/filedialog/WinFileDialogCommandsDefn.ipdlh
new file mode 100644
index 0000000000..dd85942f24
--- /dev/null
+++ b/widget/windows/filedialog/WinFileDialogCommandsDefn.ipdlh
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et ft=ipdl : */
+/* 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/. */
+
+namespace mozilla {
+namespace widget {
+namespace filedialog {
+
+// Commands corresponding to the various functions in IFileDialog (or at least
+// the ones we actually make use of).
+//
+// All commands' semantics are direct parallels of their equivalently-named
+// functions on IFileDialog, with the only changes being those necessary to use
+// IPDLable representation-datatypes. (Thus, e.g., `SetOptions` effectively
+// takes a `FILEOPENDIALOGOPTIONS`, and `SetFileTypeIndex` is 1-based.)
+struct SetOptions { uint32_t options; };
+struct SetTitle { nsString title; };
+struct SetOkButtonLabel { nsString label; };
+struct SetFolder { nsString path; };
+struct SetFileName { nsString filename; };
+struct SetDefaultExtension { nsString extension; };
+struct ComDlgFilterSpec { nsString name; nsString spec; };
+struct SetFileTypes { ComDlgFilterSpec[] filterList; };
+struct SetFileTypeIndex { uint32_t index; };
+
+// Union of the above.
+union Command {
+ SetOptions;
+ SetTitle;
+ SetOkButtonLabel;
+ SetFolder;
+ SetFileName;
+ SetDefaultExtension;
+ SetFileTypes;
+ SetFileTypeIndex;
+};
+
+// The results from opening a file dialog. (Note that folder selection only
+// returns an nsString.)
+struct Results {
+ nsString[] paths;
+ uint32_t selectedFileTypeIndex;
+};
+
+} // namespace filedialog
+} // namespace widget
+} // namespace mozilla
diff --git a/widget/windows/filedialog/moz.build b/widget/windows/filedialog/moz.build
new file mode 100644
index 0000000000..e6b1b478bb
--- /dev/null
+++ b/widget/windows/filedialog/moz.build
@@ -0,0 +1,22 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+IPDL_SOURCES += [
+ "WinFileDialogCommandsDefn.ipdlh",
+]
+
+UNIFIED_SOURCES += [
+ "WinFileDialogCommands.cpp",
+]
+
+EXPORTS.mozilla.widget.filedialog += [
+ "WinFileDialogCommands.h",
+]
+
+# needed for IPC header files
+include("/ipc/chromium/chromium-config.mozbuild")
+
+FINAL_LIBRARY = "xul"