Browse Source

refactor: reuse upstream //shell_dialogs (#30663)

Shelley Vohr 3 years ago
parent
commit
00d0265782

+ 2 - 0
chromium_src/BUILD.gn

@@ -48,6 +48,8 @@ static_library("chrome") {
     "//chrome/browser/predictors/resolve_host_client_impl.cc",
     "//chrome/browser/predictors/resolve_host_client_impl.h",
     "//chrome/browser/process_singleton.h",
+    "//chrome/browser/ui/browser_dialogs.cc",
+    "//chrome/browser/ui/browser_dialogs.h",
     "//chrome/browser/ui/views/autofill/autofill_popup_view_utils.cc",
     "//chrome/browser/ui/views/autofill/autofill_popup_view_utils.h",
     "//chrome/browser/ui/views/eye_dropper/eye_dropper.cc",

+ 0 - 2
filenames.gni

@@ -494,8 +494,6 @@ filenames = {
     "shell/browser/web_contents_preferences.h",
     "shell/browser/web_contents_zoom_controller.cc",
     "shell/browser/web_contents_zoom_controller.h",
-    "shell/browser/web_dialog_helper.cc",
-    "shell/browser/web_dialog_helper.h",
     "shell/browser/web_view_guest_delegate.cc",
     "shell/browser/web_view_guest_delegate.h",
     "shell/browser/web_view_manager.cc",

+ 5 - 11
shell/browser/api/electron_api_web_contents.cc

@@ -86,6 +86,7 @@
 #include "shell/browser/electron_browser_main_parts.h"
 #include "shell/browser/electron_javascript_dialog_manager.h"
 #include "shell/browser/electron_navigation_throttle.h"
+#include "shell/browser/file_select_helper.h"
 #include "shell/browser/native_window.h"
 #include "shell/browser/session_preferences.h"
 #include "shell/browser/ui/drag_util.h"
@@ -95,7 +96,6 @@
 #include "shell/browser/web_contents_permission_helper.h"
 #include "shell/browser/web_contents_preferences.h"
 #include "shell/browser/web_contents_zoom_controller.h"
-#include "shell/browser/web_dialog_helper.h"
 #include "shell/browser/web_view_guest_delegate.h"
 #include "shell/browser/web_view_manager.h"
 #include "shell/common/api/electron_api_native_image.h"
@@ -3255,21 +3255,15 @@ void WebContents::RunFileChooser(
     content::RenderFrameHost* render_frame_host,
     scoped_refptr<content::FileSelectListener> listener,
     const blink::mojom::FileChooserParams& params) {
-  if (!web_dialog_helper_)
-    web_dialog_helper_ =
-        std::make_unique<WebDialogHelper>(owner_window(), offscreen_);
-  web_dialog_helper_->RunFileChooser(render_frame_host, std::move(listener),
-                                     params);
+  FileSelectHelper::RunFileChooser(render_frame_host, std::move(listener),
+                                   params);
 }
 
 void WebContents::EnumerateDirectory(
-    content::WebContents* guest,
+    content::WebContents* web_contents,
     scoped_refptr<content::FileSelectListener> listener,
     const base::FilePath& path) {
-  if (!web_dialog_helper_)
-    web_dialog_helper_ =
-        std::make_unique<WebDialogHelper>(owner_window(), offscreen_);
-  web_dialog_helper_->EnumerateDirectory(guest, std::move(listener), path);
+  FileSelectHelper::EnumerateDirectory(web_contents, std::move(listener), path);
 }
 
 bool WebContents::IsFullscreenForTabOrPending(

+ 0 - 3
shell/browser/api/electron_api_web_contents.h

@@ -731,9 +731,6 @@ class WebContents : public gin::Wrappable<WebContents>,
   // Whether window is fullscreened by window api.
   bool native_fullscreen_ = false;
 
-  // UI related helper classes.
-  std::unique_ptr<WebDialogHelper> web_dialog_helper_;
-
   scoped_refptr<DevToolsFileSystemIndexer> devtools_file_system_indexer_;
 
   std::unique_ptr<DevToolsEyeDropper> eye_dropper_;

+ 465 - 158
shell/browser/file_select_helper.cc

@@ -1,133 +1,243 @@
-// Copyright (c) 2020 Microsoft, Inc. All rights reserved.
+// Copyright (c) 2021 Microsoft. All rights reserved.
 // Use of this source code is governed by the MIT license that can be
 // found in the LICENSE file.
 
-#include <memory>
+#include "shell/browser/file_select_helper.h"
 
-#include <utility>
-#include <vector>
+#include <stddef.h>
 
-#include "shell/browser/file_select_helper.h"
+#include <memory>
+#include <string>
+#include <utility>
 
 #include "base/bind.h"
-#include "base/files/file_enumerator.h"
 #include "base/files/file_util.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/task/thread_pool.h"
+#include "base/threading/hang_watcher.h"
+#include "build/build_config.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/platform_util.h"
+#include "chrome/browser/ui/browser_dialogs.h"
 #include "chrome/common/pref_names.h"
+#include "chrome/grit/generated_resources.h"
 #include "components/prefs/pref_service.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.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_view.h"
+#include "content/public/browser/web_contents.h"
+#include "net/base/filename_util.h"
+#include "net/base/mime_util.h"
+#include "shell/browser/api/electron_api_web_contents.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"
+#include "shell/browser/native_window.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/shell_dialogs/select_file_policy.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;
+using blink::mojom::FileChooserParamsPtr;
+using content::BrowserThread;
+using content::RenderViewHost;
+using content::RenderWidgetHost;
+using content::WebContents;
 
 namespace {
+
 void DeleteFiles(std::vector<base::FilePath> paths) {
   for (auto& file_path : paths)
     base::DeleteFile(file_path);
 }
-}  // namespace
 
-FileSelectHelper::FileSelectHelper(
-    content::RenderFrameHost* render_frame_host,
-    scoped_refptr<content::FileSelectListener> listener,
-    FileChooserParams::Mode mode)
-    : render_frame_host_(render_frame_host),
-      listener_(std::move(listener)),
-      mode_(mode) {
-  DCHECK(render_frame_host_);
-  DCHECK(listener_);
+}  // namespace
 
-  web_contents_ = content::WebContents::FromRenderFrameHost(render_frame_host);
-  DCHECK(web_contents_);
+struct FileSelectHelper::ActiveDirectoryEnumeration {
+  explicit ActiveDirectoryEnumeration(const base::FilePath& path)
+      : path_(path) {}
+
+  std::unique_ptr<net::DirectoryLister> lister_;
+  const base::FilePath path_;
+  std::vector<base::FilePath> results_;
+};
+
+FileSelectHelper::FileSelectHelper()
+    : render_frame_host_(nullptr),
+      web_contents_(nullptr),
+      select_file_dialog_(),
+      select_file_types_(),
+      dialog_type_(ui::SelectFileDialog::SELECT_OPEN_FILE),
+      dialog_mode_(FileChooserParams::Mode::kOpen) {}
+
+FileSelectHelper::~FileSelectHelper() {
+  // There may be pending file dialogs, we need to tell them that we've gone
+  // away so they don't try and call back to us.
+  if (select_file_dialog_.get())
+    select_file_dialog_->ListenerDestroyed();
+}
 
-  content::WebContentsObserver::Observe(web_contents_);
-  observation_.Observe(render_frame_host_->GetRenderViewHost()->GetWidget());
+void FileSelectHelper::FileSelected(const base::FilePath& path,
+                                    int index,
+                                    void* params) {
+  FileSelectedWithExtraInfo(ui::SelectedFileInfo(path, path), index, params);
 }
 
-FileSelectHelper::~FileSelectHelper() = default;
+void FileSelectHelper::FileSelectedWithExtraInfo(
+    const ui::SelectedFileInfo& file,
+    int index,
+    void* params) {
+  if (!render_frame_host_) {
+    RunFileChooserEnd();
+    return;
+  }
+
+  const base::FilePath& path = file.local_path;
+  if (dialog_type_ == ui::SelectFileDialog::SELECT_UPLOAD_FOLDER) {
+    StartNewEnumeration(path);
+    return;
+  }
 
-void FileSelectHelper::ShowOpenDialog(
-    const file_dialog::DialogSettings& settings) {
-  v8::Isolate* isolate = electron::JavascriptEnvironment::GetIsolate();
-  v8::HandleScope scope(isolate);
-  gin_helper::Promise<gin_helper::Dictionary> promise(isolate);
+  std::vector<ui::SelectedFileInfo> files;
+  files.push_back(file);
 
-  auto callback = base::BindOnce(&FileSelectHelper::OnOpenDialogDone,
-                                 weak_ptr_factory_.GetWeakPtr());
-  ignore_result(promise.Then(std::move(callback)));
+#if defined(OS_MAC)
+  base::ThreadPool::PostTask(
+      FROM_HERE,
+      {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
+      base::BindOnce(&FileSelectHelper::ProcessSelectedFilesMac, this, files));
+#else
+  ConvertToFileChooserFileInfoList(files);
+#endif  // defined(OS_MAC)
+}
 
-  file_dialog::ShowOpenDialog(settings, std::move(promise));
+void FileSelectHelper::MultiFilesSelected(
+    const std::vector<base::FilePath>& files,
+    void* params) {
+  std::vector<ui::SelectedFileInfo> selected_files =
+      ui::FilePathListToSelectedFileInfoList(files);
+
+  MultiFilesSelectedWithExtraInfo(selected_files, params);
 }
 
-void FileSelectHelper::ShowSaveDialog(
-    const file_dialog::DialogSettings& settings) {
-  v8::Isolate* isolate = electron::JavascriptEnvironment::GetIsolate();
-  v8::HandleScope scope(isolate);
-  gin_helper::Promise<gin_helper::Dictionary> promise(isolate);
+void FileSelectHelper::MultiFilesSelectedWithExtraInfo(
+    const std::vector<ui::SelectedFileInfo>& files,
+    void* params) {
+#if defined(OS_MAC)
+  base::ThreadPool::PostTask(
+      FROM_HERE,
+      {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
+      base::BindOnce(&FileSelectHelper::ProcessSelectedFilesMac, this, files));
+#else
+  ConvertToFileChooserFileInfoList(files);
+#endif  // defined(OS_MAC)
+}
 
-  auto callback = base::BindOnce(&FileSelectHelper::OnSaveDialogDone,
-                                 weak_ptr_factory_.GetWeakPtr());
-  ignore_result(promise.Then(std::move(callback)));
+void FileSelectHelper::FileSelectionCanceled(void* params) {
+  RunFileChooserEnd();
+}
 
-  file_dialog::ShowSaveDialog(settings, std::move(promise));
+void FileSelectHelper::StartNewEnumeration(const base::FilePath& path) {
+  base_dir_ = path;
+  auto entry = std::make_unique<ActiveDirectoryEnumeration>(path);
+  entry->lister_ = base::WrapUnique(new net::DirectoryLister(
+      path, net::DirectoryLister::NO_SORT_RECURSIVE, this));
+  entry->lister_->Start();
+  directory_enumeration_ = std::move(entry);
 }
 
-// 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
+  // Directory upload only cares about files.
+  if (data.info.IsDirectory())
+    return;
+
+  directory_enumeration_->results_.push_back(data.path);
+}
+
+void FileSelectHelper::LaunchConfirmationDialog(
+    const base::FilePath& path,
+    std::vector<ui::SelectedFileInfo> selected_files) {
+  ShowFolderUploadConfirmationDialog(
+      path,
+      base::BindOnce(&FileSelectHelper::ConvertToFileChooserFileInfoList, this),
+      std::move(selected_files), web_contents_);
+}
+
+void FileSelectHelper::OnListDone(int error) {
+  if (!web_contents_) {
+    // Web contents was destroyed under us (probably by closing the tab). 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())
+
+  // This entry needs to be cleaned up when this function is done.
+  std::unique_ptr<ActiveDirectoryEnumeration> entry =
+      std::move(directory_enumeration_);
+  if (error) {
+    FileSelectionCanceled(NULL);
     return;
+  }
+
+  std::vector<ui::SelectedFileInfo> selected_files =
+      ui::FilePathListToSelectedFileInfoList(entry->results_);
+
+  if (dialog_type_ == ui::SelectFileDialog::SELECT_UPLOAD_FOLDER) {
+    LaunchConfirmationDialog(entry->path_, std::move(selected_files));
+  } else {
+    std::vector<FileChooserFileInfoPtr> chooser_files;
+    for (const auto& file_path : entry->results_) {
+      chooser_files.push_back(FileChooserFileInfo::NewNativeFile(
+          blink::mojom::NativeFileInfo::New(file_path, std::u16string())));
+    }
 
-  lister_paths_.push_back(data.path);
+    listener_->FileSelected(std::move(chooser_files), base_dir_,
+                            FileChooserParams::Mode::kUploadFolder);
+    listener_.reset();
+    EnumerateDirectoryEnd();
+  }
 }
 
-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())
+void FileSelectHelper::ConvertToFileChooserFileInfoList(
+    const std::vector<ui::SelectedFileInfo>& files) {
+  if (AbortIfWebContentsDestroyed())
     return;
 
-  if (listener_)
-    listener_->FileSelectionCanceled();
-
-  render_frame_host_ = nullptr;
-  web_contents_ = nullptr;
+  std::vector<FileChooserFileInfoPtr> chooser_files;
+  for (const auto& file : files) {
+    chooser_files.push_back(
+        FileChooserFileInfo::NewNativeFile(blink::mojom::NativeFileInfo::New(
+            file.local_path,
+            base::FilePath(file.display_name).AsUTF16Unsafe())));
+  }
 
-  delete this;
+  PerformContentAnalysisIfNeeded(std::move(chooser_files));
 }
 
-// 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();
+void FileSelectHelper::PerformContentAnalysisIfNeeded(
+    std::vector<FileChooserFileInfoPtr> list) {
+  if (AbortIfWebContentsDestroyed())
     return;
-  }
 
-  std::vector<FileChooserFileInfoPtr> file_info;
-  for (const auto& path : lister_paths_)
-    file_info.push_back(FileChooserFileInfo::NewNativeFile(
-        NativeFileInfo::New(path, std::u16string())));
+  NotifyListenerAndEnd(std::move(list));
+}
+
+void FileSelectHelper::NotifyListenerAndEnd(
+    std::vector<blink::mojom::FileChooserFileInfoPtr> list) {
+  listener_->FileSelected(std::move(list), base_dir_, dialog_mode_);
+  listener_.reset();
 
-  OnFilesSelected(std::move(file_info), lister_base_dir_);
+  // No members should be accessed from here on.
+  RunFileChooserEnd();
 }
 
 void FileSelectHelper::DeleteTemporaryFiles() {
@@ -138,96 +248,275 @@ void FileSelectHelper::DeleteTemporaryFiles() {
       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());
+void FileSelectHelper::CleanUp() {
+  if (!temporary_files_.empty()) {
+    DeleteTemporaryFiles();
+
+    // Now that the temporary files have been scheduled for deletion, there
+    // is no longer any reason to keep this instance around.
+    Release();
+  }
+}
+
+bool FileSelectHelper::AbortIfWebContentsDestroyed() {
+  if (render_frame_host_ == nullptr || web_contents_ == nullptr) {
+    RunFileChooserEnd();
+    return true;
+  }
 
-  lister_ = std::make_unique<net::DirectoryLister>(
-      lister_base_dir_, net::DirectoryLister::NO_SORT_RECURSIVE, this);
-  lister_->Start();
+  return false;
 }
 
-void FileSelectHelper::OnOpenDialogDone(gin_helper::Dictionary result) {
-  bool canceled = true;
-  result.Get("canceled", &canceled);
+void FileSelectHelper::SetFileSelectListenerForTesting(
+    scoped_refptr<content::FileSelectListener> listener) {
+  DCHECK(listener);
+  DCHECK(!listener_);
+  listener_ = std::move(listener);
+}
 
-  if (!render_frame_host_ || canceled) {
-    RunFileChooserEnd();
-  } else {
-    std::vector<base::FilePath> paths;
-    if (result.Get("filePaths", &paths)) {
-      std::vector<ui::SelectedFileInfo> files =
-          ui::FilePathListToSelectedFileInfoList(paths);
-      // If we are uploading a folder we need to enumerate its contents
-      if (mode_ == FileChooserParams::Mode::kUploadFolder && !paths.empty()) {
-        lister_base_dir_ = paths[0];
-        EnumerateDirectory();
-      } else {
-#if defined(OS_MAC)
-        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<electron::ElectronBrowserContext*>(
-            render_frame_host_->GetProcess()->GetBrowserContext());
-        browser_context->prefs()->SetFilePath(prefs::kSelectFileLastDirectory,
-                                              paths[0].DirName());
-      }
-#if !defined(OS_MAC)
-      RunFileChooserEnd();
-#endif
+std::unique_ptr<ui::SelectFileDialog::FileTypeInfo>
+FileSelectHelper::GetFileTypesFromAcceptType(
+    const std::vector<std::u16string>& accept_types) {
+  std::unique_ptr<ui::SelectFileDialog::FileTypeInfo> base_file_type(
+      new ui::SelectFileDialog::FileTypeInfo());
+  if (accept_types.empty())
+    return base_file_type;
+
+  // Create FileTypeInfo and pre-allocate for the first extension list.
+  std::unique_ptr<ui::SelectFileDialog::FileTypeInfo> file_type(
+      new ui::SelectFileDialog::FileTypeInfo(*base_file_type));
+  file_type->include_all_files = true;
+  file_type->extensions.resize(1);
+  std::vector<base::FilePath::StringType>* extensions =
+      &file_type->extensions.back();
+
+  // Find the corresponding extensions.
+  int valid_type_count = 0;
+  int description_id = 0;
+  for (const auto& accept_type : accept_types) {
+    size_t old_extension_size = extensions->size();
+    if (accept_type[0] == '.') {
+      // If the type starts with a period it is assumed to be a file extension
+      // so we just have to add it to the list.
+      base::FilePath::StringType ext =
+          base::FilePath::FromUTF16Unsafe(accept_type).value();
+      extensions->push_back(ext.substr(1));
+    } else {
+      if (!base::IsStringASCII(accept_type))
+        continue;
+      std::string ascii_type = base::UTF16ToASCII(accept_type);
+      if (ascii_type == "image/*")
+        description_id = IDS_IMAGE_FILES;
+      else if (ascii_type == "audio/*")
+        description_id = IDS_AUDIO_FILES;
+      else if (ascii_type == "video/*")
+        description_id = IDS_VIDEO_FILES;
+
+      net::GetExtensionsForMimeType(ascii_type, extensions);
     }
+
+    if (extensions->size() > old_extension_size)
+      valid_type_count++;
   }
+
+  // If no valid extension is added, bail out.
+  if (valid_type_count == 0)
+    return base_file_type;
+
+  // Use a generic description "Custom Files" if either of the following is
+  // true:
+  // 1) There're multiple types specified, like "audio/*,video/*"
+  // 2) There're multiple extensions for a MIME type without parameter, like
+  //    "ehtml,shtml,htm,html" for "text/html". On Windows, the select file
+  //    dialog uses the first extension in the list to form the description,
+  //    like "EHTML Files". This is not what we want.
+  if (valid_type_count > 1 ||
+      (valid_type_count == 1 && description_id == 0 && extensions->size() > 1))
+    description_id = IDS_CUSTOM_FILES;
+
+  if (description_id) {
+    file_type->extension_description_overrides.push_back(
+        l10n_util::GetStringUTF16(description_id));
+  }
+
+  return file_type;
 }
 
-void FileSelectHelper::ConvertToFileChooserFileInfoList(
-    const std::vector<ui::SelectedFileInfo>& files) {
-  std::vector<FileChooserFileInfoPtr> file_info;
+// static
+void FileSelectHelper::RunFileChooser(
+    content::RenderFrameHost* render_frame_host,
+    scoped_refptr<content::FileSelectListener> listener,
+    const FileChooserParams& params) {
+  // FileSelectHelper will keep itself alive until it sends the result
+  // message.
+  scoped_refptr<FileSelectHelper> file_select_helper(new FileSelectHelper());
+  file_select_helper->RunFileChooser(render_frame_host, std::move(listener),
+                                     params.Clone());
+}
 
-  for (const auto& file : files) {
-    file_info.push_back(FileChooserFileInfo::NewNativeFile(NativeFileInfo::New(
-        file.local_path, base::FilePath(file.display_name).AsUTF16Unsafe())));
-  }
+// static
+void FileSelectHelper::EnumerateDirectory(
+    content::WebContents* tab,
+    scoped_refptr<content::FileSelectListener> listener,
+    const base::FilePath& path) {
+  // FileSelectHelper will keep itself alive until it sends the result
+  // message.
+  scoped_refptr<FileSelectHelper> file_select_helper(new FileSelectHelper());
+  file_select_helper->EnumerateDirectoryImpl(tab, std::move(listener), path);
+}
+
+void FileSelectHelper::RunFileChooser(
+    content::RenderFrameHost* render_frame_host,
+    scoped_refptr<content::FileSelectListener> listener,
+    FileChooserParamsPtr params) {
+  DCHECK(!render_frame_host_);
+  DCHECK(!web_contents_);
+  DCHECK(listener);
+  DCHECK(!listener_);
+  DCHECK(params->default_file_name.empty() ||
+         params->mode == FileChooserParams::Mode::kSave)
+      << "The default_file_name parameter should only be specified for Save "
+         "file choosers";
+  DCHECK(params->default_file_name == params->default_file_name.BaseName())
+      << "The default_file_name parameter should not contain path separators";
+
+  render_frame_host_ = render_frame_host;
+  web_contents_ = WebContents::FromRenderFrameHost(render_frame_host);
+  listener_ = std::move(listener);
+  observation_.Reset();
+  content::WebContentsObserver::Observe(web_contents_);
+  observation_.Observe(render_frame_host_->GetRenderViewHost()->GetWidget());
 
-  OnFilesSelected(std::move(file_info), lister_base_dir_);
+  base::ThreadPool::PostTask(
+      FROM_HERE, {base::MayBlock()},
+      base::BindOnce(&FileSelectHelper::GetFileTypesInThreadPool, this,
+                     std::move(params)));
+
+  // Because this class returns notifications to the RenderViewHost, 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, until the last callback is received from the file dialog.
+  // At that point, we must call RunFileChooserEnd().
+  AddRef();
 }
 
-void FileSelectHelper::OnSaveDialogDone(gin_helper::Dictionary result) {
-  std::vector<FileChooserFileInfoPtr> file_info;
-  bool canceled = true;
-  result.Get("canceled", &canceled);
+void FileSelectHelper::GetFileTypesInThreadPool(FileChooserParamsPtr params) {
+  select_file_types_ = GetFileTypesFromAcceptType(params->accept_types);
+  select_file_types_->allowed_paths =
+      params->need_local_path ? ui::SelectFileDialog::FileTypeInfo::NATIVE_PATH
+                              : ui::SelectFileDialog::FileTypeInfo::ANY_PATH;
 
-  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());
-    RunFileChooserEnd();
-  }
+  content::GetUIThreadTaskRunner({})->PostTask(
+      FROM_HERE,
+      base::BindOnce(&FileSelectHelper::GetSanitizedFilenameOnUIThread, this,
+                     std::move(params)));
 }
 
-void FileSelectHelper::OnFilesSelected(
-    std::vector<FileChooserFileInfoPtr> file_info,
-    base::FilePath base_dir) {
-  if (listener_) {
-    listener_->FileSelected(std::move(file_info), base_dir, mode_);
-    listener_.reset();
+void FileSelectHelper::GetSanitizedFilenameOnUIThread(
+    FileChooserParamsPtr params) {
+  if (AbortIfWebContentsDestroyed())
+    return;
+
+  auto* browser_context = static_cast<electron::ElectronBrowserContext*>(
+      render_frame_host_->GetProcess()->GetBrowserContext());
+  base::FilePath default_file_path =
+      browser_context->prefs()
+          ->GetFilePath(prefs::kSelectFileLastDirectory)
+          .Append(params->default_file_name);
+
+  RunFileChooserOnUIThread(default_file_path, std::move(params));
+}
+
+void FileSelectHelper::RunFileChooserOnUIThread(
+    const base::FilePath& default_file_path,
+    FileChooserParamsPtr params) {
+  DCHECK(params);
+
+  select_file_dialog_ = ui::SelectFileDialog::Create(this, nullptr);
+  if (!select_file_dialog_.get())
+    return;
+
+  dialog_mode_ = params->mode;
+  switch (params->mode) {
+    case FileChooserParams::Mode::kOpen:
+      dialog_type_ = ui::SelectFileDialog::SELECT_OPEN_FILE;
+      break;
+    case FileChooserParams::Mode::kOpenMultiple:
+      dialog_type_ = ui::SelectFileDialog::SELECT_OPEN_MULTI_FILE;
+      break;
+    case FileChooserParams::Mode::kUploadFolder:
+      dialog_type_ = ui::SelectFileDialog::SELECT_UPLOAD_FOLDER;
+      break;
+    case FileChooserParams::Mode::kSave:
+      dialog_type_ = ui::SelectFileDialog::SELECT_SAVEAS_FILE;
+      break;
+    default:
+      // Prevent warning.
+      dialog_type_ = ui::SelectFileDialog::SELECT_OPEN_FILE;
+      NOTREACHED();
   }
+
+  auto* web_contents = electron::api::WebContents::From(
+      content::WebContents::FromRenderFrameHost(render_frame_host_));
+  if (!web_contents || !web_contents->owner_window())
+    return;
+
+  // Never consider the current scope as hung. The hang watching deadline (if
+  // any) is not valid since the user can take unbounded time to choose the
+  // file.
+  base::HangWatcher::InvalidateActiveExpectations();
+
+  select_file_dialog_->SelectFile(
+      dialog_type_, params->title, default_file_path, select_file_types_.get(),
+      select_file_types_.get() && !select_file_types_->extensions.empty()
+          ? 1
+          : 0,  // 1-based index of default extension to show.
+      base::FilePath::StringType(),
+      web_contents->owner_window()->GetNativeWindow(), NULL);
+
+  select_file_types_.reset();
+}
+
+// This method is called when we receive the last callback from the file chooser
+// dialog or if the renderer was destroyed. Perform any cleanup and release the
+// reference we added in RunFileChooser().
+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;
+  Release();
+}
+
+void FileSelectHelper::EnumerateDirectoryImpl(
+    content::WebContents* tab,
+    scoped_refptr<content::FileSelectListener> listener,
+    const base::FilePath& path) {
+  DCHECK(listener);
+  DCHECK(!listener_);
+  dialog_type_ = ui::SelectFileDialog::SELECT_NONE;
+  web_contents_ = tab;
+  listener_ = std::move(listener);
+  // Because this class returns notifications to the RenderViewHost, 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, until the last callback is received from the enumeration
+  // code. At that point, we must call EnumerateDirectoryEnd().
+  AddRef();
+  StartNewEnumeration(path);
+}
+
+// This method is called when we receive the last callback from the enumeration
+// code. Perform any cleanup and release the reference we added in
+// EnumerateDirectoryImpl().
+void FileSelectHelper::EnumerateDirectoryEnd() {
+  Release();
 }
 
 void FileSelectHelper::RenderWidgetHostDestroyed(
@@ -237,34 +526,52 @@ void FileSelectHelper::RenderWidgetHostDestroyed(
   observation_.Reset();
 }
 
-// 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.
+  // 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_)
+    content::RenderFrameHost* render_frame_host) {
+  if (render_frame_host == render_frame_host_)
     render_frame_host_ = nullptr;
 }
 
-// content::WebContentsObserver:
 void FileSelectHelper::WebContentsDestroyed() {
   render_frame_host_ = nullptr;
   web_contents_ = nullptr;
+  CleanUp();
+}
 
-  DeleteTemporaryFiles();
+// static
+bool FileSelectHelper::IsAcceptTypeValid(const std::string& accept_type) {
+  // TODO(raymes): This only does some basic checks, extend to test more cases.
+  // A 1 character accept type will always be invalid (either a "." in the case
+  // of an extension or a "/" in the case of a MIME type).
+  std::string unused;
+  if (accept_type.length() <= 1 ||
+      base::ToLowerASCII(accept_type) != accept_type ||
+      base::TrimWhitespaceASCII(accept_type, base::TRIM_ALL, &unused) !=
+          base::TRIM_NONE) {
+    return false;
+  }
+  return true;
+}
 
-  if (!lister_)
-    delete this;
+// static
+base::FilePath FileSelectHelper::GetSanitizedFileName(
+    const base::FilePath& suggested_filename) {
+  if (suggested_filename.empty())
+    return base::FilePath();
+  return net::GenerateFileName(
+      GURL(), std::string(), std::string(), suggested_filename.AsUTF8Unsafe(),
+      std::string(), l10n_util::GetStringUTF8(IDS_DEFAULT_DOWNLOAD_FILENAME));
 }

+ 171 - 57
shell/browser/file_select_helper.h

@@ -1,69 +1,130 @@
-// Copyright (c) 2020 Microsoft, Inc. All rights reserved.
+// Copyright (c) 2021 Microsoft. 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 <map>
 #include <memory>
+#include <string>
 #include <vector>
 
-#include "base/files/file_path.h"
+#include "base/compiler_specific.h"
+#include "base/macros.h"
 #include "base/scoped_observation.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 "build/build_config.h"
+#include "content/public/browser/browser_thread.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,
+#include "third_party/blink/public/mojom/choosers/file_chooser.mojom.h"
+#include "ui/shell_dialogs/select_file_dialog.h"
+
+namespace content {
+class FileSelectListener;
+class WebContents;
+}  // namespace content
+
+namespace ui {
+struct SelectedFileInfo;
+}
+
+// This class handles file-selection requests coming from renderer processes.
+// It implements both the initialisation and listener functions for
+// file-selection dialogs.
+//
+// Since FileSelectHelper listens to observations of a widget, it needs to live
+// on and be destroyed on the UI thread. References to FileSelectHelper may be
+// passed on to other threads.
+class FileSelectHelper : public base::RefCountedThreadSafe<
+                             FileSelectHelper,
+                             content::BrowserThread::DeleteOnUIThread>,
+                         public ui::SelectFileDialog::Listener,
+                         public content::WebContentsObserver,
                          public content::RenderWidgetHostObserver,
-                         public net::DirectoryLister::DirectoryListerDelegate {
+                         private net::DirectoryLister::DirectoryListerDelegate {
  public:
-  FileSelectHelper(content::RenderFrameHost* render_frame_host,
-                   scoped_refptr<content::FileSelectListener> listener,
-                   FileChooserParams::Mode mode);
+  // Show the file chooser dialog.
+  static void RunFileChooser(
+      content::RenderFrameHost* render_frame_host,
+      scoped_refptr<content::FileSelectListener> listener,
+      const blink::mojom::FileChooserParams& params);
+
+  // Enumerates all the files in directory.
+  static void EnumerateDirectory(
+      content::WebContents* tab,
+      scoped_refptr<content::FileSelectListener> listener,
+      const base::FilePath& path);
+
+ private:
+  friend class base::RefCountedThreadSafe<FileSelectHelper>;
+  friend class base::DeleteHelper<FileSelectHelper>;
+  friend struct content::BrowserThread::DeleteOnThread<
+      content::BrowserThread::UI>;
+
+  FileSelectHelper();
   ~FileSelectHelper() override;
 
-  // WebDialogHelper::RunFileChooser
+  void RunFileChooser(content::RenderFrameHost* render_frame_host,
+                      scoped_refptr<content::FileSelectListener> listener,
+                      blink::mojom::FileChooserParamsPtr params);
+  void GetFileTypesInThreadPool(blink::mojom::FileChooserParamsPtr params);
+  void GetSanitizedFilenameOnUIThread(
+      blink::mojom::FileChooserParamsPtr params);
 
-  void ShowOpenDialog(const file_dialog::DialogSettings& settings);
+  void RunFileChooserOnUIThread(const base::FilePath& default_path,
+                                blink::mojom::FileChooserParamsPtr params);
 
-  void ShowSaveDialog(const file_dialog::DialogSettings& settings);
+  // Cleans up and releases this instance. This must be called after the last
+  // callback is received from the file chooser dialog.
+  void RunFileChooserEnd();
 
- private:
-  // net::DirectoryLister::DirectoryListerDelegate overrides.
-  void OnListFile(
-      const net::DirectoryLister::DirectoryListerData& data) override;
-  void OnListDone(int error) override;
+  // SelectFileDialog::Listener overrides.
+  void FileSelected(const base::FilePath& path,
+                    int index,
+                    void* params) override;
+  void FileSelectedWithExtraInfo(const ui::SelectedFileInfo& file,
+                                 int index,
+                                 void* params) override;
+  void MultiFilesSelected(const std::vector<base::FilePath>& files,
+                          void* params) override;
+  void MultiFilesSelectedWithExtraInfo(
+      const std::vector<ui::SelectedFileInfo>& files,
+      void* params) override;
+  void FileSelectionCanceled(void* params) override;
 
-  void DeleteTemporaryFiles();
+  // content::RenderWidgetHostObserver overrides.
+  void RenderWidgetHostDestroyed(
+      content::RenderWidgetHost* widget_host) override;
 
-  void EnumerateDirectory();
+  // content::WebContentsObserver overrides.
+  void RenderFrameHostChanged(content::RenderFrameHost* old_host,
+                              content::RenderFrameHost* new_host) override;
+  void RenderFrameDeleted(content::RenderFrameHost* render_frame_host) override;
+  void WebContentsDestroyed() override;
 
-  void OnOpenDialogDone(gin_helper::Dictionary result);
-  void OnSaveDialogDone(gin_helper::Dictionary result);
+  void EnumerateDirectoryImpl(
+      content::WebContents* tab,
+      scoped_refptr<content::FileSelectListener> listener,
+      const base::FilePath& path);
 
-  void OnFilesSelected(
-      std::vector<blink::mojom::FileChooserFileInfoPtr> file_info,
-      base::FilePath base_dir);
+  // Kicks off a new directory enumeration.
+  void StartNewEnumeration(const base::FilePath& path);
 
-  void RunFileChooserEnd();
+  // net::DirectoryLister::DirectoryListerDelegate overrides.
+  void OnListFile(
+      const net::DirectoryLister::DirectoryListerData& data) override;
+  void OnListDone(int error) override;
 
-  void ConvertToFileChooserFileInfoList(
-      const std::vector<ui::SelectedFileInfo>& files);
+  void LaunchConfirmationDialog(
+      const base::FilePath& path,
+      std::vector<ui::SelectedFileInfo> selected_files);
+
+  // Cleans up and releases this instance. This must be called after the last
+  // callback is received from the enumeration code.
+  void EnumerateDirectoryEnd();
 
 #if defined(OS_MAC)
   // Must be called from a MayBlock() task. Each selected file that is a package
@@ -71,11 +132,11 @@ class FileSelectHelper : public content::WebContentsObserver,
   // of the package.
   void ProcessSelectedFilesMac(const std::vector<ui::SelectedFileInfo>& files);
 
-  // Saves the paths of |temporary_files| for later deletion. Passes |files| to
-  // the render view host.
+  // Saves the paths of |zipped_files| for later deletion. Passes |files| to the
+  // render view host.
   void ProcessSelectedFilesMacOnUIThread(
       const std::vector<ui::SelectedFileInfo>& files,
-      const std::vector<base::FilePath>& temporary_files);
+      const std::vector<base::FilePath>& zipped_files);
 
   // Zips the package at |path| into a temporary destination. Returns the
   // temporary destination, if the zip was successful. Otherwise returns an
@@ -83,20 +144,78 @@ class FileSelectHelper : public content::WebContentsObserver,
   static base::FilePath ZipPackage(const base::FilePath& path);
 #endif  // defined(OS_MAC)
 
-  // content::RenderWidgetHostObserver:
-  void RenderWidgetHostDestroyed(
-      content::RenderWidgetHost* widget_host) override;
+  void ConvertToFileChooserFileInfoList(
+      const std::vector<ui::SelectedFileInfo>& files);
 
-  // content::WebContentsObserver:
-  void RenderFrameHostChanged(content::RenderFrameHost* old_host,
-                              content::RenderFrameHost* new_host) override;
-  void RenderFrameDeleted(content::RenderFrameHost* deleted_host) override;
-  void WebContentsDestroyed() override;
+  // Checks to see if scans are required for the specified files.
+  void PerformContentAnalysisIfNeeded(
+      std::vector<blink::mojom::FileChooserFileInfoPtr> list);
+
+  // Finish the PerformContentAnalysisIfNeeded() handling after the
+  // deep scanning checks have been performed.  Deep scanning may change the
+  // list of files chosen by the user, so the list of files passed here may be
+  // a subset of of the files passed to PerformContentAnalysisIfNeeded().
+  void NotifyListenerAndEnd(
+      std::vector<blink::mojom::FileChooserFileInfoPtr> list);
+
+  // Schedules the deletion of the files in |temporary_files_| and clears the
+  // vector.
+  void DeleteTemporaryFiles();
+
+  // Cleans up when the initiator of the file chooser is no longer valid.
+  void CleanUp();
 
+  // Calls RunFileChooserEnd() if the webcontents was destroyed. Returns true
+  // if the file chooser operation shouldn't proceed.
+  bool AbortIfWebContentsDestroyed();
+
+  void SetFileSelectListenerForTesting(
+      scoped_refptr<content::FileSelectListener> listener);
+
+  // Helper method to get allowed extensions for select file dialog from
+  // the specified accept types as defined in the spec:
+  //   http://whatwg.org/html/number-state.html#attr-input-accept
+  // |accept_types| contains only valid lowercased MIME types or file extensions
+  // beginning with a period (.).
+  static std::unique_ptr<ui::SelectFileDialog::FileTypeInfo>
+  GetFileTypesFromAcceptType(const std::vector<std::u16string>& accept_types);
+
+  // Check the accept type is valid. It is expected to be all lower case with
+  // no whitespace.
+  static bool IsAcceptTypeValid(const std::string& accept_type);
+
+  // Get a sanitized filename suitable for use as a default filename.
+  static base::FilePath GetSanitizedFileName(
+      const base::FilePath& suggested_path);
+
+  // The RenderFrameHost and WebContents for the page showing a file dialog
+  // (may only be one such dialog).
   content::RenderFrameHost* render_frame_host_;
   content::WebContents* web_contents_;
+
+  // |listener_| receives the result of the FileSelectHelper.
   scoped_refptr<content::FileSelectListener> listener_;
-  FileChooserParams::Mode mode_;
+
+  // Dialog box used for choosing files to upload from file form fields.
+  scoped_refptr<ui::SelectFileDialog> select_file_dialog_;
+  std::unique_ptr<ui::SelectFileDialog::FileTypeInfo> select_file_types_;
+
+  // The type of file dialog last shown. This is SELECT_NONE if an
+  // instance is created through the public EnumerateDirectory().
+  ui::SelectFileDialog::Type dialog_type_;
+
+  // The mode of file dialog last shown.
+  blink::mojom::FileChooserParams::Mode dialog_mode_;
+
+  // The enumeration root directory for EnumerateDirectory() and
+  // RunFileChooser with kUploadFolder.
+  base::FilePath base_dir_;
+
+  // Maintain an active directory enumeration.  These could come from the file
+  // select dialog or from drag-and-drop of directories.  There could not be
+  // more than one going on at a time.
+  struct ActiveDirectoryEnumeration;
+  std::unique_ptr<ActiveDirectoryEnumeration> directory_enumeration_;
 
   base::ScopedObservation<content::RenderWidgetHost,
                           content::RenderWidgetHostObserver>
@@ -106,12 +225,7 @@ class FileSelectHelper : public content::WebContentsObserver,
   // these files when they are no longer needed.
   std::vector<base::FilePath> temporary_files_;
 
-  // DirectoryLister-specific members
-  std::unique_ptr<net::DirectoryLister> lister_;
-  base::FilePath lister_base_dir_;
-  std::vector<base::FilePath> lister_paths_;
-
-  base::WeakPtrFactory<FileSelectHelper> weak_ptr_factory_{this};
+  DISALLOW_COPY_AND_ASSIGN(FileSelectHelper);
 };
 
 #endif  // SHELL_BROWSER_FILE_SELECT_HELPER_H_

+ 0 - 171
shell/browser/web_dialog_helper.cc

@@ -1,171 +0,0 @@
-// Copyright (c) 2014 GitHub, Inc. All rights reserved.
-// Use of this source code is governed by the MIT license that can be
-// found in the LICENSE file.
-
-#include "shell/browser/web_dialog_helper.h"
-
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "base/bind.h"
-#include "base/files/file_enumerator.h"
-#include "base/files/file_path.h"
-#include "base/strings/utf_string_conversions.h"
-#include "chrome/common/pref_names.h"
-#include "components/prefs/pref_service.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/web_contents.h"
-#include "net/base/mime_util.h"
-#include "shell/browser/electron_browser_context.h"
-#include "shell/browser/file_select_helper.h"
-#include "shell/browser/native_window.h"
-#include "shell/browser/ui/file_dialog.h"
-
-using blink::mojom::FileChooserFileInfo;
-using blink::mojom::FileChooserFileInfoPtr;
-using blink::mojom::FileChooserParams;
-using blink::mojom::NativeFileInfo;
-
-namespace {
-
-file_dialog::Filters GetFileTypesFromAcceptType(
-    const std::vector<std::u16string>& accept_types) {
-  file_dialog::Filters filters;
-  if (accept_types.empty())
-    return filters;
-
-  std::vector<base::FilePath::StringType> extensions;
-
-  int valid_type_count = 0;
-  std::string description;
-
-  for (const auto& accept_type : accept_types) {
-    std::string ascii_type = base::UTF16ToASCII(accept_type);
-    auto old_extension_size = extensions.size();
-
-    if (ascii_type[0] == '.') {
-      // If the type starts with a period it is assumed to be a file extension,
-      // like `.txt`, // so we just have to add it to the list.
-      base::FilePath::StringType extension(ascii_type.begin(),
-                                           ascii_type.end());
-      // Skip the first character.
-      extensions.push_back(extension.substr(1));
-    } else {
-      if (ascii_type == "image/*")
-        description = "Image Files";
-      else if (ascii_type == "audio/*")
-        description = "Audio Files";
-      else if (ascii_type == "video/*")
-        description = "Video Files";
-
-      // For MIME Type, `audio/*, video/*, image/*
-      net::GetExtensionsForMimeType(ascii_type, &extensions);
-    }
-
-    if (extensions.size() > old_extension_size)
-      valid_type_count++;
-  }
-
-  // If no valid extension is added, return empty filters.
-  if (extensions.empty())
-    return filters;
-
-  filters.push_back(file_dialog::Filter());
-
-  if (valid_type_count > 1 || (valid_type_count == 1 && description.empty()))
-    description = "Custom Files";
-
-  DCHECK(!description.empty());
-  filters[0].first = description;
-
-  for (const auto& extension : extensions) {
-#if defined(OS_WIN)
-    filters[0].second.push_back(base::WideToASCII(extension));
-#else
-    filters[0].second.push_back(extension);
-#endif
-  }
-
-  // Allow all files when extension is specified.
-  filters.push_back(file_dialog::Filter());
-  filters.back().first = "All Files";
-  filters.back().second.emplace_back("*");
-
-  return filters;
-}
-
-}  // namespace
-
-namespace electron {
-
-WebDialogHelper::WebDialogHelper(NativeWindow* window, bool offscreen)
-    : window_(window), offscreen_(offscreen) {}
-
-WebDialogHelper::~WebDialogHelper() = default;
-
-void WebDialogHelper::RunFileChooser(
-    content::RenderFrameHost* render_frame_host,
-    scoped_refptr<content::FileSelectListener> listener,
-    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* fsc =
-      new FileSelectHelper(render_frame_host, std::move(listener), params.mode);
-
-  if (params.mode == FileChooserParams::Mode::kSave) {
-    settings.default_path = params.default_file_name;
-    fsc->ShowSaveDialog(settings);
-  } else {
-    int flags = file_dialog::OPEN_DIALOG_CREATE_DIRECTORY;
-    switch (params.mode) {
-      case FileChooserParams::Mode::kOpenMultiple:
-        flags |= file_dialog::OPEN_DIALOG_MULTI_SELECTIONS;
-        FALLTHROUGH;
-      case FileChooserParams::Mode::kOpen:
-        flags |= file_dialog::OPEN_DIALOG_OPEN_FILE;
-        break;
-      case FileChooserParams::Mode::kUploadFolder:
-        flags |= file_dialog::OPEN_DIALOG_OPEN_DIRECTORY;
-        break;
-      default:
-        NOTREACHED();
-    }
-
-    auto* browser_context = static_cast<electron::ElectronBrowserContext*>(
-        render_frame_host->GetProcess()->GetBrowserContext());
-    settings.default_path = browser_context->prefs()
-                                ->GetFilePath(prefs::kSelectFileLastDirectory)
-                                .Append(params.default_file_name);
-    settings.properties = flags;
-    fsc->ShowOpenDialog(settings);
-  }
-}
-
-void WebDialogHelper::EnumerateDirectory(
-    content::WebContents* web_contents,
-    scoped_refptr<content::FileSelectListener> listener,
-    const base::FilePath& dir) {
-  int types = base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES |
-              base::FileEnumerator::INCLUDE_DOT_DOT;
-  base::FileEnumerator file_enum(dir, false, types);
-
-  base::FilePath path;
-  std::vector<FileChooserFileInfoPtr> file_info;
-  while (!(path = file_enum.Next()).empty()) {
-    file_info.push_back(FileChooserFileInfo::NewNativeFile(
-        NativeFileInfo::New(path, std::u16string())));
-  }
-
-  listener->FileSelected(std::move(file_info), dir,
-                         FileChooserParams::Mode::kUploadFolder);
-}
-
-}  // namespace electron

+ 0 - 48
shell/browser/web_dialog_helper.h

@@ -1,48 +0,0 @@
-// Copyright (c) 2014 GitHub, 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_WEB_DIALOG_HELPER_H_
-#define SHELL_BROWSER_WEB_DIALOG_HELPER_H_
-
-#include "base/memory/weak_ptr.h"
-#include "third_party/blink/public/mojom/choosers/file_chooser.mojom.h"
-
-namespace base {
-class FilePath;
-}
-
-namespace content {
-class FileSelectListener;
-class RenderFrameHost;
-class WebContents;
-}  // namespace content
-
-namespace electron {
-
-class NativeWindow;
-
-class WebDialogHelper {
- public:
-  WebDialogHelper(NativeWindow* window, bool offscreen);
-  ~WebDialogHelper();
-
-  void RunFileChooser(content::RenderFrameHost* render_frame_host,
-                      scoped_refptr<content::FileSelectListener> listener,
-                      const blink::mojom::FileChooserParams& params);
-  void EnumerateDirectory(content::WebContents* web_contents,
-                          scoped_refptr<content::FileSelectListener> listener,
-                          const base::FilePath& dir);
-
- private:
-  NativeWindow* window_;
-  bool offscreen_;
-
-  base::WeakPtrFactory<WebDialogHelper> weak_factory_{this};
-
-  DISALLOW_COPY_AND_ASSIGN(WebDialogHelper);
-};
-
-}  // namespace electron
-
-#endif  // SHELL_BROWSER_WEB_DIALOG_HELPER_H_