summaryrefslogtreecommitdiffstats
path: root/widget/windows/nsFilePicker.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'widget/windows/nsFilePicker.cpp')
-rw-r--r--widget/windows/nsFilePicker.cpp344
1 files changed, 184 insertions, 160 deletions
diff --git a/widget/windows/nsFilePicker.cpp b/widget/windows/nsFilePicker.cpp
index 2a45937988..89b10ce4ee 100644
--- a/widget/windows/nsFilePicker.cpp
+++ b/widget/windows/nsFilePicker.cpp
@@ -44,15 +44,17 @@
#include "mozilla/widget/filedialog/WinFileDialogCommands.h"
#include "mozilla/widget/filedialog/WinFileDialogParent.h"
+using mozilla::LogLevel;
using mozilla::UniquePtr;
using namespace mozilla::widget;
+template <typename Res>
+using FDPromise = filedialog::Promise<Res>;
+
UniquePtr<char16_t[], nsFilePicker::FreeDeleter>
nsFilePicker::sLastUsedUnicodeDirectory;
-using mozilla::LogLevel;
-
#define MAX_EXTENSION_LENGTH 10
///////////////////////////////////////////////////////////////////////////////
@@ -106,27 +108,29 @@ NS_IMETHODIMP nsFilePicker::Init(
}
namespace mozilla::detail {
+using Error = mozilla::widget::filedialog::Error;
+
// Boilerplate for remotely showing a file dialog.
template <typename ActionType,
typename ReturnType = typename decltype(std::declval<ActionType>()(
nullptr))::element_type::ResolveValueType>
-static auto ShowRemote(ActionType&& action)
- -> RefPtr<MozPromise<ReturnType, HRESULT, true>> {
- using RetPromise = MozPromise<ReturnType, HRESULT, true>;
+static auto ShowRemote(ActionType&& action) -> RefPtr<FDPromise<ReturnType>> {
+ using RetPromise = FDPromise<ReturnType>;
- constexpr static const auto fail = []() {
- return RetPromise::CreateAndReject(E_FAIL, __PRETTY_FUNCTION__);
- };
+// "function-local" #define
+#define FAIL(where_, why_) \
+ return RetPromise::CreateAndReject(MOZ_FD_LOCAL_ERROR(where_, why_), \
+ __PRETTY_FUNCTION__)
auto mgr = mozilla::ipc::UtilityProcessManager::GetSingleton();
if (!mgr) {
MOZ_ASSERT(false);
- return fail();
+ FAIL("ShowRemote: UtilityProcessManager::GetSingleton", E_POINTER);
}
auto wfda = mgr->CreateWinFileDialogActor();
if (!wfda) {
- return fail();
+ FAIL("ShowRemote: invocation of CreateWinFileDialogActor", E_POINTER);
}
using mozilla::widget::filedialog::sLogFileDialog;
@@ -135,32 +139,70 @@ static auto ShowRemote(ActionType&& action)
mozilla::GetMainThreadSerialEventTarget(),
"nsFilePicker ShowRemote acquire",
[action = std::forward<ActionType>(action)](
- filedialog::ProcessProxy const& p) -> RefPtr<RetPromise> {
+ filedialog::ProcessProxy p) -> RefPtr<RetPromise> {
MOZ_LOG(sLogFileDialog, LogLevel::Info,
("nsFilePicker ShowRemote first callback: p = [%p]", p.get()));
// false positive: not actually redundant
// NOLINTNEXTLINE(readability-redundant-smartptr-get)
- return action(p.get())->Then(
- mozilla::GetMainThreadSerialEventTarget(),
- "nsFilePicker ShowRemote call",
- [p](ReturnType ret) {
- return RetPromise::CreateAndResolve(std::move(ret),
- __PRETTY_FUNCTION__);
- },
- [](mozilla::ipc::ResponseRejectReason error) {
- MOZ_LOG(sLogFileDialog, LogLevel::Error,
- ("IPC call rejected: %zu", size_t(error)));
- return fail();
+ auto promise = action(p.get());
+ return promise->Map(
+ mozilla::GetMainThreadSerialEventTarget(), __func__,
+ [p = std::move(p)](typename RetPromise::ResolveValueType&& val) {
+ // explicitly retain the ProcessProxy until at least this point
+ return std::move(val);
});
},
[](nsresult error) -> RefPtr<RetPromise> {
MOZ_LOG(sLogFileDialog, LogLevel::Error,
("could not acquire WinFileDialog: %zu", size_t(error)));
- return fail();
+ // TODO: pipe more data up from utility-process creation
+ FAIL("UtilityProcessManager::CreateWinFileDialogActor",
+ (uint32_t)error);
+ });
+
+#undef FAIL
+}
+
+namespace {
+
+static RefPtr<FDPromise<Maybe<filedialog::Results>>> ShowFilePickerRemote(
+ HWND parent, filedialog::FileDialogType type,
+ nsTArray<filedialog::Command> const& commands) {
+ using mozilla::widget::filedialog::sLogFileDialog;
+ return mozilla::detail::ShowRemote(
+ [parent, type,
+ commands = commands.Clone()](filedialog::WinFileDialogParent* p) {
+ MOZ_LOG(sLogFileDialog, LogLevel::Info,
+ ("%s: p = [%p]", __PRETTY_FUNCTION__, p));
+ return p->ShowFileDialogImpl(parent, type, commands);
});
}
+static RefPtr<FDPromise<Maybe<nsString>>> ShowFolderPickerRemote(
+ HWND parent, nsTArray<filedialog::Command> const& commands) {
+ using mozilla::widget::filedialog::sLogFileDialog;
+ return mozilla::detail::ShowRemote([parent, commands = commands.Clone()](
+ filedialog::WinFileDialogParent* p) {
+ MOZ_LOG(sLogFileDialog, LogLevel::Info,
+ ("%s: p = [%p]", __PRETTY_FUNCTION__, p));
+ return p->ShowFolderDialogImpl(parent, commands);
+ });
+}
+
+static RefPtr<FDPromise<Maybe<filedialog::Results>>> ShowFilePickerLocal(
+ HWND parent, filedialog::FileDialogType type,
+ nsTArray<filedialog::Command> const& commands) {
+ return filedialog::SpawnFilePicker(parent, type, commands.Clone());
+}
+
+static RefPtr<FDPromise<Maybe<nsString>>> ShowFolderPickerLocal(
+ HWND parent, nsTArray<filedialog::Command> const& commands) {
+ return filedialog::SpawnFolderPicker(parent, commands.Clone());
+}
+
+} // namespace
+
// fd_async
//
// Wrapper-namespace for the AsyncExecute() and AsyncAll() functions.
@@ -296,31 +338,83 @@ static void RecordFailure(uint64_t (&&time)[3], HRESULT hrRemote,
}
} // namespace telemetry
+
+/* N.B.: L and R stand for Local and Remote, not just Left and Right */
+template <typename FnL, typename FnR, typename... Args>
+struct AsyncExecuteInfo {
+ template <typename T>
+ using DestructurePromise = widget::filedialog::detail::DestructurePromise<T>;
+
+ using Unit = ::mozilla::Ok;
+
+ using RetL = std::invoke_result_t<FnL, Args...>;
+ using RetR = std::invoke_result_t<FnR, Args...>;
+
+ using InfoL = DestructurePromise<RetL>;
+ using InfoR = DestructurePromise<RetR>;
+
+ MOZ_ASSERT_SAME_TYPE(
+ typename InfoL::ResolveT, typename InfoR::ResolveT,
+ "local and remote promises must have identical resolve-types");
+
+ // At present, the local and remote promises have the same type, but this
+ // isn't logically necessary. (In particular, a future refactor may remove the
+ // redundant `.kind` from the local promises' return types.)
+ MOZ_ASSERT_SAME_TYPE(typename InfoL::RejectT, filedialog::Error,
+ "local promise must reject with a filedialog::Error");
+
+ MOZ_ASSERT_SAME_TYPE(typename InfoR::RejectT, filedialog::Error,
+ "remote promise must reject with a filedialog::Error");
+
+ using ResolveT = typename InfoL::ResolveT;
+ using PromiseT = MozPromise<ResolveT, Unit, true>;
+
+ using RetT = RefPtr<PromiseT>;
+};
+
} // namespace details
-// Invoke either or both of a "do locally" and "do remotely" function with the
-// provided arguments, depending on the relevant preference-value and whether
-// or not the remote version fails.
+// Invoke either or both of a promise-returning "do locally" and "do remotely"
+// function with the provided arguments, depending on the relevant preference's
+// value and on whether or not the remote version fails (returns a rejection-
+// promise).
//
-// Both functions must be asynchronous, returning a `RefPtr<MozPromise<...>>`.
-// "Failure" is defined as the promise being rejected.
+// Both provided functions must return a `RefPtr<filedialog::MozPromise<T>>`. As
+// `AsyncExecute` reports failures itself, its rejection-type is `()`.
template <typename Fn1, typename Fn2, typename... Args>
-static auto AsyncExecute(Fn1 local, Fn2 remote, Args const&... args)
- -> std::invoke_result_t<Fn1, Args...> {
+static auto AsyncExecute(Fn1 local, Fn2 remote, Args const&... args) ->
+ typename details::AsyncExecuteInfo<Fn1, Fn2, Args...>::RetT {
using namespace details;
+ using Info = AsyncExecuteInfo<Fn1, Fn2, Args...>;
- static_assert(std::is_same_v<std::invoke_result_t<Fn1, Args...>,
- std::invoke_result_t<Fn2, Args...>>);
- using PromiseT = typename std::invoke_result_t<Fn1, Args...>::element_type;
+ using ResolveT = typename Info::ResolveT;
+ using PromiseT = typename Info::PromiseT;
+ using LPromiseT = typename Info::InfoL::Promise;
+ using RPromiseT = typename Info::InfoR::Promise;
constexpr static char kFunctionName[] = "LocalAndOrRemote::AsyncExecute";
switch (GetStrategy()) {
- case Local:
- return local(args...);
+ case Local: {
+ return local(args...)->MapErr(
+ NS_GetCurrentThread(), __func__, [](Error const& err) {
+ MOZ_ASSERT(err.kind == Error::LocalError);
+ MOZ_LOG(filedialog::sLogFileDialog, LogLevel::Info,
+ ("local file-dialog failed: where=%s, why=%08" PRIX32,
+ err.where.c_str(), err.why));
+ return Ok();
+ });
+ }
case Remote:
- return remote(args...);
+ return remote(args...)->MapErr(
+ NS_GetCurrentThread(), __func__, [](Error const& err) {
+ MOZ_LOG(
+ filedialog::sLogFileDialog, LogLevel::Info,
+ ("remote file-dialog failed: kind=%s, where=%s, why=%08" PRIX32,
+ Error::KindName(err.kind), err.where.c_str(), err.why));
+ return Ok();
+ });
case RemoteWithFallback:
// more complicated; continue below
@@ -337,34 +431,40 @@ static auto AsyncExecute(Fn1 local, Fn2 remote, Args const&... args)
return remote(args...)->Then(
NS_GetCurrentThread(), kFunctionName,
- [t0](typename PromiseT::ResolveValueType result) -> RefPtr<PromiseT> {
+ [t0](typename RPromiseT::ResolveValueType result) -> RefPtr<PromiseT> {
// success; stop here
auto const t1 = GetTime();
// record success
telemetry::RecordSuccess({t0, t1});
- return PromiseT::CreateAndResolve(result, kFunctionName);
+ return PromiseT::CreateAndResolve(std::move(result), kFunctionName);
},
// initialized lambda pack captures are C++20 (clang 9, gcc 9);
// `make_tuple` is just a C++17 workaround
[=, tuple = std::make_tuple(Copy(args)...)](
- typename PromiseT::RejectValueType err) mutable -> RefPtr<PromiseT> {
+ typename RPromiseT::RejectValueType err) mutable -> RefPtr<PromiseT> {
// failure; record time
auto const t1 = GetTime();
- HRESULT const hrRemote = err;
+ // TODO: also propagate `err.where` into telemetry
+ HRESULT const hrRemote = err.why;
// retry locally...
auto p0 = std::apply(local, std::move(tuple));
// ...then record the telemetry event
return p0->Then(
NS_GetCurrentThread(), kFunctionName,
- [t0, t1,
- hrRemote](typename PromiseT::ResolveOrRejectValue const& val)
+ [t0, t1, hrRemote](typename LPromiseT::ResolveOrRejectValue&& val)
-> RefPtr<PromiseT> {
auto const t2 = GetTime();
- HRESULT const hrLocal = val.IsReject() ? val.RejectValue() : S_OK;
+ HRESULT const hrLocal =
+ val.IsReject() ? (HRESULT)val.RejectValue().why : S_OK;
telemetry::RecordFailure({t0, t1, t2}, hrRemote, hrLocal);
- return PromiseT::CreateAndResolveOrReject(val, kFunctionName);
+ using V = typename PromiseT::ResolveOrRejectValue;
+ return PromiseT::CreateAndResolveOrReject(
+ val.IsResolve()
+ ? V::MakeResolve(std::move(val).ResolveValue())
+ : V::MakeReject(Ok{}),
+ kFunctionName);
});
});
}
@@ -393,45 +493,6 @@ using fd_async::AsyncExecute;
} // namespace mozilla::detail
-/* static */
-nsFilePicker::FPPromise<filedialog::Results> nsFilePicker::ShowFilePickerRemote(
- HWND parent, filedialog::FileDialogType type,
- nsTArray<filedialog::Command> const& commands) {
- using mozilla::widget::filedialog::sLogFileDialog;
- return mozilla::detail::ShowRemote(
- [parent, type,
- commands = commands.Clone()](filedialog::WinFileDialogParent* p) {
- MOZ_LOG(sLogFileDialog, LogLevel::Info,
- ("%s: p = [%p]", __PRETTY_FUNCTION__, p));
- return p->SendShowFileDialog((uintptr_t)parent, type, commands);
- });
-}
-
-/* static */
-nsFilePicker::FPPromise<nsString> nsFilePicker::ShowFolderPickerRemote(
- HWND parent, nsTArray<filedialog::Command> const& commands) {
- using mozilla::widget::filedialog::sLogFileDialog;
- return mozilla::detail::ShowRemote([parent, commands = commands.Clone()](
- filedialog::WinFileDialogParent* p) {
- MOZ_LOG(sLogFileDialog, LogLevel::Info,
- ("%s: p = [%p]", __PRETTY_FUNCTION__, p));
- return p->SendShowFolderDialog((uintptr_t)parent, commands);
- });
-}
-
-/* static */
-nsFilePicker::FPPromise<filedialog::Results> nsFilePicker::ShowFilePickerLocal(
- HWND parent, filedialog::FileDialogType type,
- nsTArray<filedialog::Command> const& commands) {
- return filedialog::SpawnFilePicker(parent, type, commands.Clone());
-}
-
-/* static */
-nsFilePicker::FPPromise<nsString> nsFilePicker::ShowFolderPickerLocal(
- HWND parent, nsTArray<filedialog::Command> const& commands) {
- return filedialog::SpawnFolderPicker(parent, commands.Clone());
-}
-
/*
* Folder picker invocation
*/
@@ -447,16 +508,8 @@ nsFilePicker::FPPromise<nsString> nsFilePicker::ShowFolderPickerLocal(
* - resolves to false if the dialog was cancelled by the user;
* - is rejected with the associated HRESULT if some error occurred.
*/
-RefPtr<mozilla::MozPromise<bool, HRESULT, true>> nsFilePicker::ShowFolderPicker(
- const nsString& aInitialDir) {
- using Promise = mozilla::MozPromise<bool, HRESULT, true>;
- constexpr static auto Ok = [](bool val) {
- return Promise::CreateAndResolve(val, "nsFilePicker::ShowFolderPicker");
- };
- constexpr static auto NotOk = [](HRESULT val = E_FAIL) {
- return Promise::CreateAndReject(val, "nsFilePicker::ShowFolderPicker");
- };
-
+RefPtr<mozilla::MozPromise<bool, nsFilePicker::Unit, true>>
+nsFilePicker::ShowFolderPicker(const nsString& aInitialDir) {
namespace fd = ::mozilla::widget::filedialog;
nsTArray<fd::Command> commands = {
fd::SetOptions(FOS_PICKFOLDERS),
@@ -474,23 +527,18 @@ RefPtr<mozilla::MozPromise<bool, HRESULT, true>> nsFilePicker::ShowFolderPicker(
ScopedRtlShimWindow shim(mParentWidget.get());
AutoWidgetPickerState awps(mParentWidget);
- return mozilla::detail::AsyncExecute(&ShowFolderPickerLocal,
- &ShowFolderPickerRemote, shim.get(),
- commands)
- ->Then(
- NS_GetCurrentThread(), __PRETTY_FUNCTION__,
- [self = RefPtr(this), shim = std::move(shim),
- awps = std::move(awps)](Maybe<nsString> val) {
- if (val) {
- self->mUnicodeFile = val.extract();
- return Ok(true);
- }
- return Ok(false);
- },
- [](HRESULT err) {
- NS_WARNING("ShowFolderPicker failed");
- return NotOk(err);
- });
+ return mozilla::detail::AsyncExecute(&mozilla::detail::ShowFolderPickerLocal,
+ &mozilla::detail::ShowFolderPickerRemote,
+ shim.get(), commands)
+ ->Map(NS_GetCurrentThread(), __PRETTY_FUNCTION__,
+ [self = RefPtr(this), shim = std::move(shim),
+ awps = std::move(awps)](Maybe<nsString> val) {
+ if (val) {
+ self->mUnicodeFile = val.extract();
+ return true;
+ }
+ return false;
+ });
}
/*
@@ -508,16 +556,16 @@ RefPtr<mozilla::MozPromise<bool, HRESULT, true>> nsFilePicker::ShowFolderPicker(
* - resolves to false if the dialog was cancelled by the user;
* - is rejected with the associated HRESULT if some error occurred.
*/
-RefPtr<mozilla::MozPromise<bool, HRESULT, true>> nsFilePicker::ShowFilePicker(
- const nsString& aInitialDir) {
+RefPtr<mozilla::MozPromise<bool, nsFilePicker::Unit, true>>
+nsFilePicker::ShowFilePicker(const nsString& aInitialDir) {
AUTO_PROFILER_LABEL("nsFilePicker::ShowFilePicker", OTHER);
- using Promise = mozilla::MozPromise<bool, HRESULT, true>;
+ using Promise = mozilla::MozPromise<bool, Unit, true>;
constexpr static auto Ok = [](bool val) {
return Promise::CreateAndResolve(val, "nsFilePicker::ShowFilePicker");
};
- constexpr static auto NotOk = [](HRESULT val = E_FAIL) {
- return Promise::CreateAndReject(val, "nsFilePicker::ShowFilePicker");
+ constexpr static auto NotOk = []() {
+ return Promise::CreateAndReject(Unit(), "nsFilePicker::ShowFilePicker");
};
namespace fd = ::mozilla::widget::filedialog;
@@ -551,7 +599,7 @@ RefPtr<mozilla::MozPromise<bool, HRESULT, true>> nsFilePicker::ShowFilePicker(
case modeGetFolder:
MOZ_ASSERT(false, "file-picker opened in directory-picker mode");
- return NotOk(E_FAIL);
+ return NotOk();
}
commands.AppendElement(fd::SetOptions(fos));
@@ -605,7 +653,8 @@ RefPtr<mozilla::MozPromise<bool, HRESULT, true>> nsFilePicker::ShowFilePicker(
auto type = mMode == modeSave ? FileDialogType::Save : FileDialogType::Open;
auto promise = mozilla::detail::AsyncExecute(
- &ShowFilePickerLocal, &ShowFilePickerRemote, shim.get(), type, commands);
+ &mozilla::detail::ShowFilePickerLocal,
+ &mozilla::detail::ShowFilePickerRemote, shim.get(), type, commands);
return promise->Then(
mozilla::GetMainThreadSerialEventTarget(), __PRETTY_FUNCTION__,
@@ -641,9 +690,9 @@ RefPtr<mozilla::MozPromise<bool, HRESULT, true>> nsFilePicker::ShowFilePicker(
return Ok(true);
},
- [](HRESULT err) {
+ [](Unit err) {
NS_WARNING("ShowFilePicker failed");
- return NotOk(err);
+ return NotOk();
});
}
@@ -752,43 +801,29 @@ nsFilePicker::CheckContentAnalysisService() {
return promise;
};
- // Since getting the files to analyze might be asynchronous, use a MozPromise
- // to unify the logic below.
- auto getFilesToAnalyzePromise = mozilla::MakeRefPtr<mozilla::MozPromise<
- nsTArray<mozilla::PathString>, nsresult, true>::Private>(__func__);
+ nsCOMArray<nsIFile> files;
if (mMode == modeGetFolder) {
- nsCOMPtr<nsISupports> tmp;
- nsresult rv = GetDomFileOrDirectory(getter_AddRefs(tmp));
+ nsCOMPtr<nsIFile> file;
+ nsresult rv = GetFile(getter_AddRefs(file));
if (NS_WARN_IF(NS_FAILED(rv))) {
- getFilesToAnalyzePromise->Reject(rv, __func__);
return nsFilePicker::ContentAnalysisResponse::CreateAndReject(rv,
__func__);
}
- auto* directory = static_cast<mozilla::dom::Directory*>(tmp.get());
- mozilla::dom::OwningFileOrDirectory owningDirectory;
- owningDirectory.SetAsDirectory() = directory;
- nsTArray<mozilla::dom::OwningFileOrDirectory> directoryArray{
- std::move(owningDirectory)};
-
- mozilla::ErrorResult error;
- RefPtr<mozilla::dom::GetFilesHelper> helper =
- mozilla::dom::GetFilesHelper::Create(directoryArray, true, error);
- rv = error.StealNSResult();
+ nsCOMPtr<nsIDirectoryEnumerator> iter;
+ rv = file->GetDirectoryEntries(getter_AddRefs(iter));
if (NS_WARN_IF(NS_FAILED(rv))) {
- getFilesToAnalyzePromise->Reject(rv, __func__);
return nsFilePicker::ContentAnalysisResponse::CreateAndReject(rv,
__func__);
}
- auto getFilesCallback = mozilla::MakeRefPtr<GetFilesInDirectoryCallback>(
- getFilesToAnalyzePromise);
- helper->AddCallback(getFilesCallback);
+ nsCOMPtr<nsIFile> entry;
+ while (NS_SUCCEEDED(iter->GetNextFile(getter_AddRefs(entry))) && entry) {
+ files.AppendElement(entry);
+ }
} else {
- nsCOMArray<nsIFile> files;
if (!mUnicodeFile.IsEmpty()) {
nsCOMPtr<nsIFile> file;
rv = GetFile(getter_AddRefs(file));
if (NS_WARN_IF(NS_FAILED(rv))) {
- getFilesToAnalyzePromise->Reject(rv, __func__);
return nsFilePicker::ContentAnalysisResponse::CreateAndReject(rv,
__func__);
}
@@ -796,22 +831,13 @@ nsFilePicker::CheckContentAnalysisService() {
} else {
files.AppendElements(mFiles);
}
- nsTArray<mozilla::PathString> paths(files.Length());
- std::transform(files.begin(), files.end(), MakeBackInserter(paths),
- [](auto* entry) { return entry->NativePath(); });
- getFilesToAnalyzePromise->Resolve(std::move(paths), __func__);
}
+ nsTArray<mozilla::PathString> paths(files.Length());
+ std::transform(files.begin(), files.end(), MakeBackInserter(paths),
+ [](auto* entry) { return entry->NativePath(); });
- return getFilesToAnalyzePromise->Then(
- mozilla::GetMainThreadSerialEventTarget(), __func__,
- [processOneItem](nsTArray<mozilla::PathString> aPaths) mutable {
- return mozilla::detail::AsyncAll<mozilla::PathString>(std::move(aPaths),
- processOneItem);
- },
- [](nsresult aError) {
- return nsFilePicker::ContentAnalysisResponse::CreateAndReject(aError,
- __func__);
- });
+ return mozilla::detail::AsyncAll<mozilla::PathString>(std::move(paths),
+ processOneItem);
};
///////////////////////////////////////////////////////////////////////////////
@@ -891,10 +917,8 @@ nsresult nsFilePicker::Open(nsIFilePickerShownCallback* aCallback) {
callback->Done(retValue);
},
- [callback = RefPtr(aCallback)](HRESULT err) {
- using mozilla::widget::filedialog::sLogFileDialog;
- MOZ_LOG(sLogFileDialog, LogLevel::Error,
- ("nsFilePicker: Show failed with hr=0x%08lX", err));
+ [callback = RefPtr(aCallback)](Unit _) {
+ // logging already handled
callback->Done(ResultCode::returnCancel);
});