From 9900e697b591b524b6647a15962bb39c5b34931d Mon Sep 17 00:00:00 2001 From: Shelley Vohr Date: Wed, 18 Nov 2020 05:13:57 -0800 Subject: [PATCH] fix: transparently package bundles as zip archives (#26555) --- BUILD.gn | 1 + filenames.gni | 3 + shell/browser/file_select_helper.cc | 270 ++++++++++++++++++++++++ shell/browser/file_select_helper.h | 118 +++++++++++ shell/browser/file_select_helper_mac.mm | 146 +++++++++++++ shell/browser/web_dialog_helper.cc | 211 +----------------- 6 files changed, 548 insertions(+), 201 deletions(-) create mode 100644 shell/browser/file_select_helper.cc create mode 100644 shell/browser/file_select_helper.h create mode 100644 shell/browser/file_select_helper_mac.mm diff --git a/BUILD.gn b/BUILD.gn index 4c14dcf32319f..da7dd03738763 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -370,6 +370,7 @@ source_set("electron_lib") { "//third_party/libyuv", "//third_party/webrtc_overrides:webrtc_component", "//third_party/widevine/cdm:headers", + "//third_party/zlib/google:zip", "//ui/base/idle", "//ui/events:dom_keycode_converter", "//ui/gl", diff --git a/filenames.gni b/filenames.gni index 7347c2fe3454c..8019dfcee4aac 100644 --- a/filenames.gni +++ b/filenames.gni @@ -187,6 +187,9 @@ filenames = { "shell/browser/extended_web_contents_observer.h", "shell/browser/feature_list.cc", "shell/browser/feature_list.h", + "shell/browser/file_select_helper.cc", + "shell/browser/file_select_helper.h", + "shell/browser/file_select_helper_mac.mm", "shell/browser/font_defaults.cc", "shell/browser/font_defaults.h", "shell/browser/javascript_environment.cc", diff --git a/shell/browser/file_select_helper.cc b/shell/browser/file_select_helper.cc new file mode 100644 index 0000000000000..dbd98da2a7a96 --- /dev/null +++ b/shell/browser/file_select_helper.cc @@ -0,0 +1,270 @@ +// Copyright (c) 2020 Microsoft, Inc. All rights reserved. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include + +#include +#include +#include + +#include "shell/browser/file_select_helper.h" + +#include "base/bind.h" +#include "base/files/file_enumerator.h" +#include "base/files/file_util.h" +#include "base/strings/utf_string_conversions.h" +#include "chrome/common/pref_names.h" +#include "components/prefs/pref_service.h" +#include "shell/browser/electron_browser_context.h" +#include "shell/browser/javascript_environment.h" +#include "shell/browser/ui/file_dialog.h" +#include "shell/common/gin_converters/callback_converter.h" +#include "shell/common/gin_converters/file_path_converter.h" + +using blink::mojom::FileChooserFileInfo; +using blink::mojom::FileChooserFileInfoPtr; +using blink::mojom::FileChooserParams; +using blink::mojom::NativeFileInfo; + +namespace { +void DeleteFiles(std::vector paths) { + for (auto& file_path : paths) + base::DeleteFile(file_path); +} +} // namespace + +FileSelectHelper::FileSelectHelper( + content::RenderFrameHost* render_frame_host, + std::unique_ptr listener, + FileChooserParams::Mode mode) + : render_frame_host_(render_frame_host), + listener_(std::move(listener)), + mode_(mode) { + DCHECK(render_frame_host_); + DCHECK(listener_); + + web_contents_ = content::WebContents::FromRenderFrameHost(render_frame_host); + DCHECK(web_contents_); + + content::WebContentsObserver::Observe(web_contents_); + observer_.Add(render_frame_host_->GetRenderViewHost()->GetWidget()); +} + +FileSelectHelper::~FileSelectHelper() = default; + +void FileSelectHelper::ShowOpenDialog( + const file_dialog::DialogSettings& settings) { + v8::Isolate* isolate = electron::JavascriptEnvironment::GetIsolate(); + v8::HandleScope scope(isolate); + gin_helper::Promise promise(isolate); + + auto callback = base::BindOnce(&FileSelectHelper::OnOpenDialogDone, + weak_ptr_factory_.GetWeakPtr()); + ignore_result(promise.Then(std::move(callback))); + + file_dialog::ShowOpenDialog(settings, std::move(promise)); +} + +void FileSelectHelper::ShowSaveDialog( + const file_dialog::DialogSettings& settings) { + v8::Isolate* isolate = electron::JavascriptEnvironment::GetIsolate(); + v8::HandleScope scope(isolate); + gin_helper::Promise promise(isolate); + + auto callback = base::BindOnce(&FileSelectHelper::OnSaveDialogDone, + weak_ptr_factory_.GetWeakPtr()); + ignore_result(promise.Then(std::move(callback))); + + file_dialog::ShowSaveDialog(settings, std::move(promise)); +} + +// net::DirectoryLister::DirectoryListerDelegate +void FileSelectHelper::OnListFile( + const net::DirectoryLister::DirectoryListerData& data) { + if (!render_frame_host_ || !web_contents_) { + // If the frame or webcontents was destroyed under us. We + // must notify |listener_| and release our reference to + // ourself. RunFileChooserEnd() performs this. + RunFileChooserEnd(); + return; + } + // We don't want to return directory paths, only file paths + if (data.info.IsDirectory()) + return; + + lister_paths_.push_back(data.path); +} + +void FileSelectHelper::RunFileChooserEnd() { + // If there are temporary files, then this instance needs to stick around + // until web_contents_ is destroyed, so that this instance can delete the + // temporary files. + if (!temporary_files_.empty()) + return; + + if (listener_) + listener_->FileSelectionCanceled(); + + render_frame_host_ = nullptr; + web_contents_ = nullptr; + + delete this; +} + +// net::DirectoryLister::DirectoryListerDelegate +void FileSelectHelper::OnListDone(int error) { + if (!render_frame_host_ || !web_contents_) { + // If the frame or webcontents was destroyed under us. We + // must notify |listener_| and release our reference to + // ourself. RunFileChooserEnd() performs this. + RunFileChooserEnd(); + return; + } + + std::vector file_info; + for (const auto& path : lister_paths_) + file_info.push_back(FileChooserFileInfo::NewNativeFile( + NativeFileInfo::New(path, base::string16()))); + + OnFilesSelected(std::move(file_info), lister_base_dir_); +} + +void FileSelectHelper::DeleteTemporaryFiles() { + base::ThreadPool::PostTask( + FROM_HERE, + {base::MayBlock(), base::TaskPriority::BEST_EFFORT, + base::TaskShutdownBehavior::BLOCK_SHUTDOWN}, + base::BindOnce(&DeleteFiles, std::move(temporary_files_))); +} + +void FileSelectHelper::EnumerateDirectory() { + // Ensure that this fn is only called once + DCHECK(!lister_); + DCHECK(!lister_base_dir_.empty()); + DCHECK(lister_paths_.empty()); + + lister_ = std::make_unique( + lister_base_dir_, net::DirectoryLister::NO_SORT_RECURSIVE, this); + lister_->Start(); +} + +void FileSelectHelper::OnOpenDialogDone(gin_helper::Dictionary result) { + bool canceled = true; + result.Get("canceled", &canceled); + + if (!render_frame_host_ || canceled) { + RunFileChooserEnd(); + } else { + std::vector paths; + if (result.Get("filePaths", &paths)) { + std::vector files = + ui::FilePathListToSelectedFileInfoList(paths); + // If we are uploading a folder we need to enumerate its contents + if (mode_ == FileChooserParams::Mode::kUploadFolder && + paths.size() >= 1) { + lister_base_dir_ = paths[0]; + EnumerateDirectory(); + } else { +#if defined(OS_MACOSX) + base::ThreadPool::PostTask( + FROM_HERE, + {base::MayBlock(), + base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}, + base::BindOnce(&FileSelectHelper::ProcessSelectedFilesMac, + base::Unretained(this), files)); +#else + ConvertToFileChooserFileInfoList(files); +#endif + } + + if (render_frame_host_ && !paths.empty()) { + auto* browser_context = static_cast( + render_frame_host_->GetProcess()->GetBrowserContext()); + browser_context->prefs()->SetFilePath(prefs::kSelectFileLastDirectory, + paths[0].DirName()); + } + } + } +} + +void FileSelectHelper::ConvertToFileChooserFileInfoList( + const std::vector& files) { + std::vector file_info; + + for (const auto& file : files) { + file_info.push_back(FileChooserFileInfo::NewNativeFile(NativeFileInfo::New( + file.local_path, base::FilePath(file.display_name).AsUTF16Unsafe()))); + } + + OnFilesSelected(std::move(file_info), lister_base_dir_); +} + +void FileSelectHelper::OnSaveDialogDone(gin_helper::Dictionary result) { + std::vector file_info; + bool canceled = true; + result.Get("canceled", &canceled); + + if (!render_frame_host_ || canceled) { + RunFileChooserEnd(); + } else { + base::FilePath path; + if (result.Get("filePath", &path)) { + file_info.push_back(FileChooserFileInfo::NewNativeFile( + NativeFileInfo::New(path, path.BaseName().AsUTF16Unsafe()))); + } + // We should only call this if we have not cancelled the dialog. + OnFilesSelected(std::move(file_info), base::FilePath()); + } +} + +void FileSelectHelper::OnFilesSelected( + std::vector file_info, + base::FilePath base_dir) { + if (listener_) { + listener_->FileSelected(std::move(file_info), base_dir, mode_); + listener_.reset(); + } + + render_frame_host_ = nullptr; + + delete this; +} + +void FileSelectHelper::RenderWidgetHostDestroyed( + content::RenderWidgetHost* widget_host) { + render_frame_host_ = nullptr; + observer_.Remove(widget_host); +} + +// content::WebContentsObserver: +void FileSelectHelper::RenderFrameHostChanged( + content::RenderFrameHost* old_host, + content::RenderFrameHost* new_host) { + if (!render_frame_host_) + return; + // The |old_host| and its children are now pending deletion. Do not give + // them file access past this point. + if (render_frame_host_ == old_host || + render_frame_host_->IsDescendantOf(old_host)) { + render_frame_host_ = nullptr; + } +} + +// content::WebContentsObserver: +void FileSelectHelper::RenderFrameDeleted( + content::RenderFrameHost* deleted_host) { + if (deleted_host == render_frame_host_) + render_frame_host_ = nullptr; +} + +// content::WebContentsObserver: +void FileSelectHelper::WebContentsDestroyed() { + render_frame_host_ = nullptr; + web_contents_ = nullptr; + + DeleteTemporaryFiles(); + + if (!lister_) + delete this; +} diff --git a/shell/browser/file_select_helper.h b/shell/browser/file_select_helper.h new file mode 100644 index 0000000000000..15ca4ed9bc67d --- /dev/null +++ b/shell/browser/file_select_helper.h @@ -0,0 +1,118 @@ +// Copyright (c) 2020 Microsoft, Inc. All rights reserved. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef SHELL_BROWSER_FILE_SELECT_HELPER_H_ +#define SHELL_BROWSER_FILE_SELECT_HELPER_H_ + +#include + +#include +#include +#include + +#include "base/files/file_path.h" +#include "chrome/common/pref_names.h" +#include "content/public/browser/file_select_listener.h" +#include "content/public/browser/render_frame_host.h" +#include "content/public/browser/render_process_host.h" +#include "content/public/browser/render_view_host.h" +#include "content/public/browser/render_widget_host.h" +#include "content/public/browser/render_widget_host_observer.h" +#include "content/public/browser/web_contents.h" +#include "content/public/browser/web_contents_observer.h" +#include "gin/dictionary.h" +#include "net/base/directory_lister.h" +#include "shell/browser/electron_browser_context.h" +#include "shell/browser/ui/file_dialog.h" +#include "shell/common/gin_helper/dictionary.h" +#include "ui/shell_dialogs/selected_file_info.h" + +using blink::mojom::FileChooserParams; + +class FileSelectHelper : public content::WebContentsObserver, + public content::RenderWidgetHostObserver, + public net::DirectoryLister::DirectoryListerDelegate { + public: + FileSelectHelper(content::RenderFrameHost* render_frame_host, + std::unique_ptr listener, + FileChooserParams::Mode mode); + ~FileSelectHelper() override; + + // WebDialogHelper::RunFileChooser + + void ShowOpenDialog(const file_dialog::DialogSettings& settings); + + void ShowSaveDialog(const file_dialog::DialogSettings& settings); + + private: + // net::DirectoryLister::DirectoryListerDelegate overrides. + void OnListFile( + const net::DirectoryLister::DirectoryListerData& data) override; + void OnListDone(int error) override; + + void DeleteTemporaryFiles(); + + void EnumerateDirectory(); + + void OnOpenDialogDone(gin_helper::Dictionary result); + void OnSaveDialogDone(gin_helper::Dictionary result); + + void OnFilesSelected( + std::vector file_info, + base::FilePath base_dir); + + void RunFileChooserEnd(); + + void ConvertToFileChooserFileInfoList( + const std::vector& files); + +#if defined(OS_MACOSX) + // Must be called from a MayBlock() task. Each selected file that is a package + // will be zipped, and the zip will be passed to the render view host in place + // of the package. + void ProcessSelectedFilesMac(const std::vector& files); + + // Saves the paths of |zipped_files| for later deletion. Passes |files| to the + // render view host. + void ProcessSelectedFilesMacOnUIThread( + const std::vector& files, + const std::vector& zipped_files); + + // Zips the package at |path| into a temporary destination. Returns the + // temporary destination, if the zip was successful. Otherwise returns an + // empty path. + static base::FilePath ZipPackage(const base::FilePath& path); +#endif // defined(OS_MAC) + + // content::RenderWidgetHostObserver: + void RenderWidgetHostDestroyed( + content::RenderWidgetHost* widget_host) override; + + // content::WebContentsObserver: + void RenderFrameHostChanged(content::RenderFrameHost* old_host, + content::RenderFrameHost* new_host) override; + void RenderFrameDeleted(content::RenderFrameHost* deleted_host) override; + void WebContentsDestroyed() override; + + content::RenderFrameHost* render_frame_host_; + content::WebContents* web_contents_; + std::unique_ptr listener_; + FileChooserParams::Mode mode_; + + ScopedObserver + observer_{this}; + + // Temporary files only used on OSX. This class is responsible for deleting + // these files when they are no longer needed. + std::vector temporary_files_; + + // DirectoryLister-specific members + std::unique_ptr lister_; + base::FilePath lister_base_dir_; + std::vector lister_paths_; + + base::WeakPtrFactory weak_ptr_factory_{this}; +}; + +#endif // SHELL_BROWSER_FILE_SELECT_HELPER_H_ diff --git a/shell/browser/file_select_helper_mac.mm b/shell/browser/file_select_helper_mac.mm new file mode 100644 index 0000000000000..55dc03525a45f --- /dev/null +++ b/shell/browser/file_select_helper_mac.mm @@ -0,0 +1,146 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "shell/browser/file_select_helper.h" + +#include +#include + +#include + +#include "base/bind.h" +#include "base/files/file.h" +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/mac/foundation_util.h" +#include "content/public/browser/browser_task_traits.h" +#include "content/public/browser/browser_thread.h" +#include "third_party/zlib/google/zip.h" +#include "ui/shell_dialogs/selected_file_info.h" + +namespace { + +// Given the |path| of a package, returns the destination that the package +// should be zipped to. Returns an empty path on any errors. +base::FilePath ZipDestination(const base::FilePath& path) { + base::FilePath dest; + + if (!base::GetTempDir(&dest)) { + // Couldn't get the temporary directory. + return base::FilePath(); + } + + // TMPDIR//zip_cache/ + + NSString* bundleID = [[NSBundle mainBundle] bundleIdentifier]; + dest = dest.Append([bundleID fileSystemRepresentation]); + + dest = dest.Append("zip_cache"); + + NSString* guid = [[NSProcessInfo processInfo] globallyUniqueString]; + dest = dest.Append([guid fileSystemRepresentation]); + + return dest; +} + +// Returns the path of the package and its components relative to the package's +// parent directory. +std::vector RelativePathsForPackage( + const base::FilePath& package) { + // Get the base directory. + base::FilePath base_dir = package.DirName(); + + // Add the package as the first relative path. + std::vector relative_paths; + relative_paths.push_back(package.BaseName()); + + // Add the components of the package as relative paths. + base::FileEnumerator file_enumerator( + package, true /* recursive */, + base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES); + for (base::FilePath path = file_enumerator.Next(); !path.empty(); + path = file_enumerator.Next()) { + base::FilePath relative_path; + bool success = base_dir.AppendRelativePath(path, &relative_path); + if (success) + relative_paths.push_back(relative_path); + } + + return relative_paths; +} + +} // namespace + +base::FilePath FileSelectHelper::ZipPackage(const base::FilePath& path) { + base::FilePath dest(ZipDestination(path)); + if (dest.empty()) + return dest; + + if (!base::CreateDirectory(dest.DirName())) + return base::FilePath(); + + base::File file(dest, base::File::FLAG_CREATE | base::File::FLAG_WRITE); + if (!file.IsValid()) + return base::FilePath(); + + std::vector files_to_zip(RelativePathsForPackage(path)); + base::FilePath base_dir = path.DirName(); + bool success = zip::ZipFiles(base_dir, files_to_zip, file.GetPlatformFile()); + + int result = -1; + if (success) + result = fchmod(file.GetPlatformFile(), S_IRUSR); + + return result >= 0 ? dest : base::FilePath(); +} + +void FileSelectHelper::ProcessSelectedFilesMac( + const std::vector& files) { + // Make a mutable copy of the input files. + std::vector files_out(files); + std::vector temporary_files; + + for (auto& file_info : files_out) { + NSString* filename = base::mac::FilePathToNSString(file_info.local_path); + BOOL isPackage = + [[NSWorkspace sharedWorkspace] isFilePackageAtPath:filename]; + if (isPackage && base::DirectoryExists(file_info.local_path)) { + base::FilePath result = ZipPackage(file_info.local_path); + + if (!result.empty()) { + temporary_files.push_back(result); + file_info.local_path = result; + file_info.file_path = result; + file_info.display_name.append(".zip"); + } + } + } + + content::GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, + base::BindOnce(&FileSelectHelper::ProcessSelectedFilesMacOnUIThread, + base::Unretained(this), files_out, temporary_files)); +} + +void FileSelectHelper::ProcessSelectedFilesMacOnUIThread( + const std::vector& files, + const std::vector& temporary_files) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + + if (!temporary_files.empty()) { + temporary_files_.insert(temporary_files_.end(), temporary_files.begin(), + temporary_files.end()); + + // Typically, |temporary_files| are deleted after |web_contents_| is + // destroyed. If |web_contents_| is already NULL, then the temporary files + // need to be deleted now. + if (!web_contents_) { + DeleteTemporaryFiles(); + RunFileChooserEnd(); + return; + } + } + + ConvertToFileChooserFileInfoList(files); +} diff --git a/shell/browser/web_dialog_helper.cc b/shell/browser/web_dialog_helper.cc index 9a72ef97a11b5..37ba8af752074 100644 --- a/shell/browser/web_dialog_helper.cc +++ b/shell/browser/web_dialog_helper.cc @@ -21,211 +21,20 @@ #include "content/public/browser/render_process_host.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/web_contents.h" -#include "content/public/browser/web_contents_observer.h" -#include "gin/dictionary.h" -#include "net/base/directory_lister.h" #include "net/base/mime_util.h" #include "shell/browser/electron_browser_context.h" +#include "shell/browser/file_select_helper.h" +#include "shell/browser/javascript_environment.h" #include "shell/browser/native_window.h" #include "shell/browser/ui/file_dialog.h" -#include "shell/common/gin_converters/callback_converter.h" -#include "shell/common/gin_converters/file_path_converter.h" -#include "shell/common/gin_helper/dictionary.h" -#include "ui/shell_dialogs/selected_file_info.h" using blink::mojom::FileChooserFileInfo; using blink::mojom::FileChooserFileInfoPtr; using blink::mojom::FileChooserParams; +using blink::mojom::NativeFileInfo; namespace { -class FileSelectHelper : public base::RefCounted, - public content::WebContentsObserver, - public net::DirectoryLister::DirectoryListerDelegate { - public: - REQUIRE_ADOPTION_FOR_REFCOUNTED_TYPE(); - - FileSelectHelper(content::RenderFrameHost* render_frame_host, - std::unique_ptr listener, - blink::mojom::FileChooserParams::Mode mode) - : render_frame_host_(render_frame_host), - listener_(std::move(listener)), - mode_(mode) { - auto* web_contents = - content::WebContents::FromRenderFrameHost(render_frame_host); - content::WebContentsObserver::Observe(web_contents); - } - - void ShowOpenDialog(const file_dialog::DialogSettings& settings) { - v8::Isolate* isolate = v8::Isolate::GetCurrent(); - v8::HandleScope scope(isolate); - gin_helper::Promise promise(isolate); - - auto callback = base::BindOnce(&FileSelectHelper::OnOpenDialogDone, this); - ignore_result(promise.Then(std::move(callback))); - - file_dialog::ShowOpenDialog(settings, std::move(promise)); - } - - void ShowSaveDialog(const file_dialog::DialogSettings& settings) { - v8::Isolate* isolate = v8::Isolate::GetCurrent(); - v8::HandleScope scope(isolate); - gin_helper::Promise promise(isolate); - - auto callback = base::BindOnce(&FileSelectHelper::OnSaveDialogDone, this); - ignore_result(promise.Then(std::move(callback))); - - file_dialog::ShowSaveDialog(settings, std::move(promise)); - } - - private: - friend class base::RefCounted; - - ~FileSelectHelper() override = default; - - // net::DirectoryLister::DirectoryListerDelegate - void OnListFile( - const net::DirectoryLister::DirectoryListerData& data) override { - // We don't want to return directory paths, only file paths - if (data.info.IsDirectory()) - return; - - lister_paths_.push_back(data.path); - } - - // net::DirectoryLister::DirectoryListerDelegate - void OnListDone(int error) override { - std::vector file_info; - for (const auto& path : lister_paths_) - file_info.push_back(FileChooserFileInfo::NewNativeFile( - blink::mojom::NativeFileInfo::New(path, base::string16()))); - - OnFilesSelected(std::move(file_info), lister_base_dir_); - Release(); - } - - void EnumerateDirectory() { - // Ensure that this fn is only called once - DCHECK(!lister_); - DCHECK(!lister_base_dir_.empty()); - DCHECK(lister_paths_.empty()); - - lister_ = std::make_unique( - lister_base_dir_, net::DirectoryLister::NO_SORT_RECURSIVE, this); - lister_->Start(); - // It is difficult for callers to know how long to keep a reference to - // this instance. We AddRef() here to keep the instance alive after we - // return to the caller. Once the directory lister is complete we - // Release() & at that point we run OnFilesSelected() which will - // deref the last reference held by the listener. - AddRef(); - } - - void OnOpenDialogDone(gin_helper::Dictionary result) { - std::vector file_info; - bool canceled = true; - result.Get("canceled", &canceled); - // For certain file chooser modes (kUploadFolder) we need to do some async - // work before calling back to the listener. In that particular case the - // listener is called from the directory enumerator. - bool ready_to_call_listener = false; - - if (canceled) { - OnSelectionCancelled(); - } else { - std::vector paths; - if (result.Get("filePaths", &paths)) { - // If we are uploading a folder we need to enumerate its contents - if (mode_ == FileChooserParams::Mode::kUploadFolder && - paths.size() >= 1) { - lister_base_dir_ = paths[0]; - EnumerateDirectory(); - } else { - for (auto& path : paths) { - file_info.push_back(FileChooserFileInfo::NewNativeFile( - blink::mojom::NativeFileInfo::New( - path, path.BaseName().AsUTF16Unsafe()))); - } - - ready_to_call_listener = true; - } - - if (render_frame_host_ && !paths.empty()) { - auto* browser_context = - static_cast( - render_frame_host_->GetProcess()->GetBrowserContext()); - browser_context->prefs()->SetFilePath(prefs::kSelectFileLastDirectory, - paths[0].DirName()); - } - } - // We should only call this if we have not cancelled the dialog - if (ready_to_call_listener) - OnFilesSelected(std::move(file_info), lister_base_dir_); - } - } - - void OnSaveDialogDone(gin_helper::Dictionary result) { - std::vector file_info; - bool canceled = true; - result.Get("canceled", &canceled); - - if (canceled) { - OnSelectionCancelled(); - } else { - base::FilePath path; - if (result.Get("filePath", &path)) { - file_info.push_back(FileChooserFileInfo::NewNativeFile( - blink::mojom::NativeFileInfo::New( - path, path.BaseName().AsUTF16Unsafe()))); - } - // We should only call this if we have not cancelled the dialog - OnFilesSelected(std::move(file_info), base::FilePath()); - } - } - - void OnFilesSelected(std::vector file_info, - base::FilePath base_dir) { - if (listener_) { - listener_->FileSelected(std::move(file_info), base_dir, mode_); - listener_.reset(); - } - render_frame_host_ = nullptr; - } - - void OnSelectionCancelled() { - if (listener_) { - listener_->FileSelectionCanceled(); - listener_.reset(); - } - render_frame_host_ = nullptr; - } - - // content::WebContentsObserver: - void RenderFrameHostChanged(content::RenderFrameHost* old_host, - content::RenderFrameHost* new_host) override { - if (old_host == render_frame_host_) - render_frame_host_ = nullptr; - } - - // content::WebContentsObserver: - void RenderFrameDeleted(content::RenderFrameHost* deleted_host) override { - if (deleted_host == render_frame_host_) - render_frame_host_ = nullptr; - } - - // content::WebContentsObserver: - void WebContentsDestroyed() override { render_frame_host_ = nullptr; } - - content::RenderFrameHost* render_frame_host_; - std::unique_ptr listener_; - blink::mojom::FileChooserParams::Mode mode_; - - // DirectoryLister-specific members - std::unique_ptr lister_; - base::FilePath lister_base_dir_; - std::vector lister_paths_; -}; - file_dialog::Filters GetFileTypesFromAcceptType( const std::vector& accept_types) { file_dialog::Filters filters; @@ -304,18 +113,19 @@ WebDialogHelper::~WebDialogHelper() = default; void WebDialogHelper::RunFileChooser( content::RenderFrameHost* render_frame_host, std::unique_ptr listener, - const blink::mojom::FileChooserParams& params) { + const FileChooserParams& params) { file_dialog::DialogSettings settings; settings.force_detached = offscreen_; settings.filters = GetFileTypesFromAcceptType(params.accept_types); settings.parent_window = window_; settings.title = base::UTF16ToUTF8(params.title); - auto file_select_helper = base::MakeRefCounted( - render_frame_host, std::move(listener), params.mode); + auto* fsc = + new FileSelectHelper(render_frame_host, std::move(listener), params.mode); + if (params.mode == FileChooserParams::Mode::kSave) { settings.default_path = params.default_file_name; - file_select_helper->ShowSaveDialog(settings); + fsc->ShowSaveDialog(settings); } else { int flags = file_dialog::OPEN_DIALOG_CREATE_DIRECTORY; switch (params.mode) { @@ -324,7 +134,6 @@ void WebDialogHelper::RunFileChooser( FALLTHROUGH; case FileChooserParams::Mode::kOpen: flags |= file_dialog::OPEN_DIALOG_OPEN_FILE; - flags |= file_dialog::OPEN_DIALOG_TREAT_PACKAGE_APP_AS_DIRECTORY; break; case FileChooserParams::Mode::kUploadFolder: flags |= file_dialog::OPEN_DIALOG_OPEN_DIRECTORY; @@ -339,7 +148,7 @@ void WebDialogHelper::RunFileChooser( ->GetFilePath(prefs::kSelectFileLastDirectory) .Append(params.default_file_name); settings.properties = flags; - file_select_helper->ShowOpenDialog(settings); + fsc->ShowOpenDialog(settings); } } @@ -355,7 +164,7 @@ void WebDialogHelper::EnumerateDirectory( std::vector file_info; while (!(path = file_enum.Next()).empty()) { file_info.push_back(FileChooserFileInfo::NewNativeFile( - blink::mojom::NativeFileInfo::New(path, base::string16()))); + NativeFileInfo::New(path, base::string16()))); } listener->FileSelected(std::move(file_info), dir,