fix: correctly support the webkitdirectory input attr (#18343)
Fixes #839 The implementation here was loosely inspired by the implentation in //chrome found in their FileSelectHelper. I.e. That's where the usage of net::DirectoryLister comes frome. Refs: https://cs.chromium.org/chromium/src/chrome/browser/file_select_helper.cc
This commit is contained in:
parent
1941a46825
commit
a8ff6899d4
2 changed files with 103 additions and 9 deletions
|
@ -35,7 +35,8 @@ using blink::mojom::FileChooserParams;
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
class FileSelectHelper : public base::RefCounted<FileSelectHelper>,
|
class FileSelectHelper : public base::RefCounted<FileSelectHelper>,
|
||||||
public content::WebContentsObserver {
|
public content::WebContentsObserver,
|
||||||
|
public atom::DirectoryListerHelperDelegate {
|
||||||
public:
|
public:
|
||||||
FileSelectHelper(content::RenderFrameHost* render_frame_host,
|
FileSelectHelper(content::RenderFrameHost* render_frame_host,
|
||||||
std::unique_ptr<content::FileSelectListener> listener,
|
std::unique_ptr<content::FileSelectListener> listener,
|
||||||
|
@ -71,25 +72,63 @@ class FileSelectHelper : public base::RefCounted<FileSelectHelper>,
|
||||||
isolate, base::Bind(&FileSelectHelper::OnSaveDialogDone, this)))));
|
isolate, base::Bind(&FileSelectHelper::OnSaveDialogDone, this)))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OnDirectoryListerDone(std::vector<FileChooserFileInfoPtr> file_info,
|
||||||
|
base::FilePath base_dir) override {
|
||||||
|
OnFilesSelected(std::move(file_info), base_dir);
|
||||||
|
Release();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class base::RefCounted<FileSelectHelper>;
|
friend class base::RefCounted<FileSelectHelper>;
|
||||||
|
|
||||||
~FileSelectHelper() override {}
|
~FileSelectHelper() override {}
|
||||||
|
|
||||||
|
void EnumerateDirectory(base::FilePath base_dir) {
|
||||||
|
auto* lister = new net::DirectoryLister(
|
||||||
|
base_dir, net::DirectoryLister::NO_SORT_RECURSIVE,
|
||||||
|
new atom::DirectoryListerHelper(base_dir, 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() in OnDirectoryListerDone() and at that point we run
|
||||||
|
// OnFilesSelected() which will deref the last reference held by the
|
||||||
|
// listener.
|
||||||
|
AddRef();
|
||||||
|
}
|
||||||
|
|
||||||
void OnOpenDialogDone(mate::Dictionary result) {
|
void OnOpenDialogDone(mate::Dictionary result) {
|
||||||
std::vector<FileChooserFileInfoPtr> file_info;
|
std::vector<FileChooserFileInfoPtr> file_info;
|
||||||
bool canceled = true;
|
bool canceled = true;
|
||||||
result.Get("canceled", &canceled);
|
result.Get("canceled", &canceled);
|
||||||
|
base::FilePath base_dir;
|
||||||
|
// 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) {
|
if (!canceled) {
|
||||||
std::vector<base::FilePath> paths;
|
std::vector<base::FilePath> paths;
|
||||||
if (result.Get("filePaths", &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) {
|
||||||
|
base_dir = paths[0];
|
||||||
|
|
||||||
|
// Actually enumerate soemwhere off-thread
|
||||||
|
base::SequencedTaskRunnerHandle::Get()->PostTask(
|
||||||
|
FROM_HERE, base::BindOnce(&FileSelectHelper::EnumerateDirectory,
|
||||||
|
this, base_dir));
|
||||||
|
} else {
|
||||||
for (auto& path : paths) {
|
for (auto& path : paths) {
|
||||||
file_info.push_back(FileChooserFileInfo::NewNativeFile(
|
file_info.push_back(FileChooserFileInfo::NewNativeFile(
|
||||||
blink::mojom::NativeFileInfo::New(
|
blink::mojom::NativeFileInfo::New(
|
||||||
path, path.BaseName().AsUTF16Unsafe())));
|
path, path.BaseName().AsUTF16Unsafe())));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ready_to_call_listener = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (render_frame_host_ && !paths.empty()) {
|
if (render_frame_host_ && !paths.empty()) {
|
||||||
auto* browser_context = static_cast<atom::AtomBrowserContext*>(
|
auto* browser_context = static_cast<atom::AtomBrowserContext*>(
|
||||||
render_frame_host_->GetProcess()->GetBrowserContext());
|
render_frame_host_->GetProcess()->GetBrowserContext());
|
||||||
|
@ -98,7 +137,9 @@ class FileSelectHelper : public base::RefCounted<FileSelectHelper>,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
OnFilesSelected(std::move(file_info));
|
|
||||||
|
if (ready_to_call_listener)
|
||||||
|
OnFilesSelected(std::move(file_info), base_dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnSaveDialogDone(mate::Dictionary result) {
|
void OnSaveDialogDone(mate::Dictionary result) {
|
||||||
|
@ -114,12 +155,13 @@ class FileSelectHelper : public base::RefCounted<FileSelectHelper>,
|
||||||
path, path.BaseName().AsUTF16Unsafe())));
|
path, path.BaseName().AsUTF16Unsafe())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
OnFilesSelected(std::move(file_info));
|
OnFilesSelected(std::move(file_info), base::FilePath());
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnFilesSelected(std::vector<FileChooserFileInfoPtr> file_info) {
|
void OnFilesSelected(std::vector<FileChooserFileInfoPtr> file_info,
|
||||||
|
base::FilePath base_dir) {
|
||||||
if (listener_) {
|
if (listener_) {
|
||||||
listener_->FileSelected(std::move(file_info), base::FilePath(), mode_);
|
listener_->FileSelected(std::move(file_info), base_dir, mode_);
|
||||||
listener_.reset();
|
listener_.reset();
|
||||||
}
|
}
|
||||||
render_frame_host_ = nullptr;
|
render_frame_host_ = nullptr;
|
||||||
|
@ -216,6 +258,30 @@ file_dialog::Filters GetFileTypesFromAcceptType(
|
||||||
|
|
||||||
namespace atom {
|
namespace atom {
|
||||||
|
|
||||||
|
DirectoryListerHelper::DirectoryListerHelper(
|
||||||
|
base::FilePath base,
|
||||||
|
DirectoryListerHelperDelegate* helper)
|
||||||
|
: base_dir_(base), delegate_(helper) {}
|
||||||
|
DirectoryListerHelper::~DirectoryListerHelper() {}
|
||||||
|
|
||||||
|
void DirectoryListerHelper::OnListFile(
|
||||||
|
const net::DirectoryLister::DirectoryListerData& data) {
|
||||||
|
// We don't want to return directory paths, only file paths
|
||||||
|
if (data.info.IsDirectory())
|
||||||
|
return;
|
||||||
|
|
||||||
|
paths_.push_back(data.path);
|
||||||
|
}
|
||||||
|
void DirectoryListerHelper::OnListDone(int error) {
|
||||||
|
std::vector<FileChooserFileInfoPtr> file_info;
|
||||||
|
for (auto path : paths_)
|
||||||
|
file_info.push_back(FileChooserFileInfo::NewNativeFile(
|
||||||
|
blink::mojom::NativeFileInfo::New(path, base::string16())));
|
||||||
|
|
||||||
|
delegate_->OnDirectoryListerDone(std::move(file_info), base_dir_);
|
||||||
|
delete this;
|
||||||
|
}
|
||||||
|
|
||||||
WebDialogHelper::WebDialogHelper(NativeWindow* window, bool offscreen)
|
WebDialogHelper::WebDialogHelper(NativeWindow* window, bool offscreen)
|
||||||
: window_(window), offscreen_(offscreen), weak_factory_(this) {}
|
: window_(window), offscreen_(offscreen), weak_factory_(this) {}
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,10 @@
|
||||||
#define ATOM_BROWSER_WEB_DIALOG_HELPER_H_
|
#define ATOM_BROWSER_WEB_DIALOG_HELPER_H_
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "base/memory/weak_ptr.h"
|
#include "base/memory/weak_ptr.h"
|
||||||
|
#include "net/base/directory_lister.h"
|
||||||
#include "third_party/blink/public/mojom/choosers/file_chooser.mojom.h"
|
#include "third_party/blink/public/mojom/choosers/file_chooser.mojom.h"
|
||||||
|
|
||||||
namespace base {
|
namespace base {
|
||||||
|
@ -22,6 +24,32 @@ class WebContents;
|
||||||
|
|
||||||
namespace atom {
|
namespace atom {
|
||||||
|
|
||||||
|
class DirectoryListerHelperDelegate {
|
||||||
|
public:
|
||||||
|
virtual void OnDirectoryListerDone(
|
||||||
|
std::vector<blink::mojom::FileChooserFileInfoPtr> file_info,
|
||||||
|
base::FilePath base_dir) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class DirectoryListerHelper
|
||||||
|
: public net::DirectoryLister::DirectoryListerDelegate {
|
||||||
|
public:
|
||||||
|
DirectoryListerHelper(base::FilePath base,
|
||||||
|
DirectoryListerHelperDelegate* helper);
|
||||||
|
~DirectoryListerHelper() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void OnListFile(
|
||||||
|
const net::DirectoryLister::DirectoryListerData& data) override;
|
||||||
|
void OnListDone(int error) override;
|
||||||
|
|
||||||
|
base::FilePath base_dir_;
|
||||||
|
DirectoryListerHelperDelegate* delegate_;
|
||||||
|
std::vector<base::FilePath> paths_;
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(DirectoryListerHelper);
|
||||||
|
};
|
||||||
|
|
||||||
class NativeWindow;
|
class NativeWindow;
|
||||||
|
|
||||||
class WebDialogHelper {
|
class WebDialogHelper {
|
||||||
|
|
Loading…
Reference in a new issue