Commit dd47fd6d authored by Marijn Kruisselbrink's avatar Marijn Kruisselbrink Committed by Commit Bot

Reland "[FileSystem] Add support for selecting multiple files or directories."

This is a reland of 9594a943

Original change's description:
> [FileSystem] Add support for selecting multiple files or directories.
>
> Bug: 878581
> Change-Id: I65fc0029da2e3951495a6cd96704085e5c1b942c
> Reviewed-on: https://chromium-review.googlesource.com/1227366
> Reviewed-by: Daniel Cheng <dcheng@chromium.org>
> Reviewed-by: Daniel Murphy <dmurph@chromium.org>
> Commit-Queue: Marijn Kruisselbrink <mek@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#594132}

Tbr: dmurph@chromium.org, dcheng@chromium.org
Bug: 878581
Change-Id: Ic145a6b48279f29368fbbc802751eb3d43b4ef4c
Reviewed-on: https://chromium-review.googlesource.com/c/1265058Reviewed-by: default avatarMarijn Kruisselbrink <mek@chromium.org>
Commit-Queue: Marijn Kruisselbrink <mek@chromium.org>
Cr-Commit-Position: refs/heads/master@{#597233}
parent ccde8ca3
# FileSystem API
This directory contains part of the browser side implementation of various
filesystem related APIs.
## Related directories
[`//storage/browser/fileapi/`](../../../storage/browser/fileapi) contains the
rest of the browser side implementation, while
[`blink/renderer/modules/filesystem`](../../../third_party/blink/renderer/modules/filesystem)
contains the renderer side implementation and
[`blink/public/mojom/filesystem`](../../../third_party/blink/public/mojom/filesystem)
contains the mojom interfaces for these APIs.
## In this directory
[`FileSystemManagerImpl`](file_system_manager_impl.h) is the main entry point
for calls from the renderer, it mostly redirects incoming mojom calls to a
`storage::FileSystemContext` instance.
[`FileSystemChooser`](file_system_chooser.h) uses ui::SelectFileDialog to show
a file or directory picker, and is responsible for granting a process the right
permissions for actually accessing the files that were selected.
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
#include "content/browser/fileapi/file_system_chooser.h" #include "content/browser/fileapi/file_system_chooser.h"
#include "base/bind.h" #include "base/bind.h"
#include "base/files/file_util.h"
#include "base/task/post_task.h"
#include "content/public/browser/browser_thread.h" #include "content/public/browser/browser_thread.h"
#include "content/public/browser/child_process_security_policy.h" #include "content/public/browser/child_process_security_policy.h"
#include "content/public/browser/content_browser_client.h" #include "content/public/browser/content_browser_client.h"
...@@ -19,20 +21,37 @@ namespace content { ...@@ -19,20 +21,37 @@ namespace content {
void FileSystemChooser::CreateAndShow( void FileSystemChooser::CreateAndShow(
int render_process_id, int render_process_id,
int frame_id, int frame_id,
blink::mojom::ChooseFileSystemEntryType type,
ResultCallback callback, ResultCallback callback,
scoped_refptr<base::TaskRunner> callback_runner) { scoped_refptr<base::TaskRunner> callback_runner) {
DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK_CURRENTLY_ON(BrowserThread::UI);
RenderFrameHost* rfh = RenderFrameHost::FromID(render_process_id, frame_id); RenderFrameHost* rfh = RenderFrameHost::FromID(render_process_id, frame_id);
WebContents* web_contents = WebContents::FromRenderFrameHost(rfh); WebContents* web_contents = WebContents::FromRenderFrameHost(rfh);
auto* listener = new FileSystemChooser(render_process_id, std::move(callback), auto* listener = new FileSystemChooser(
std::move(callback_runner)); render_process_id, type, std::move(callback), std::move(callback_runner));
listener->dialog_ = ui::SelectFileDialog::Create( listener->dialog_ = ui::SelectFileDialog::Create(
listener, listener,
GetContentClient()->browser()->CreateSelectFilePolicy(web_contents)); GetContentClient()->browser()->CreateSelectFilePolicy(web_contents));
// TODO(https://crbug.com/878581): Better/more specific options to pass to // TODO(https://crbug.com/878581): Better/more specific options to pass to
// SelectFile. // SelectFile.
ui::SelectFileDialog::Type dialog_type = ui::SelectFileDialog::SELECT_NONE;
switch (type) {
case blink::mojom::ChooseFileSystemEntryType::kOpenFile:
dialog_type = ui::SelectFileDialog::SELECT_OPEN_FILE;
break;
case blink::mojom::ChooseFileSystemEntryType::kOpenMultipleFiles:
dialog_type = ui::SelectFileDialog::SELECT_OPEN_MULTI_FILE;
break;
case blink::mojom::ChooseFileSystemEntryType::kSaveFile:
dialog_type = ui::SelectFileDialog::SELECT_SAVEAS_FILE;
break;
case blink::mojom::ChooseFileSystemEntryType::kOpenDirectory:
dialog_type = ui::SelectFileDialog::SELECT_FOLDER;
break;
}
DCHECK_NE(dialog_type, ui::SelectFileDialog::SELECT_NONE);
listener->dialog_->SelectFile( listener->dialog_->SelectFile(
ui::SelectFileDialog::SELECT_OPEN_FILE, /*title=*/base::string16(), dialog_type, /*title=*/base::string16(),
/*default_path=*/base::FilePath(), /*file_types=*/nullptr, /*default_path=*/base::FilePath(), /*file_types=*/nullptr,
/*file_type_index=*/0, /*file_type_index=*/0,
/*default_extension=*/base::FilePath::StringType(), /*default_extension=*/base::FilePath::StringType(),
...@@ -42,11 +61,13 @@ void FileSystemChooser::CreateAndShow( ...@@ -42,11 +61,13 @@ void FileSystemChooser::CreateAndShow(
FileSystemChooser::FileSystemChooser( FileSystemChooser::FileSystemChooser(
int render_process_id, int render_process_id,
blink::mojom::ChooseFileSystemEntryType type,
ResultCallback callback, ResultCallback callback,
scoped_refptr<base::TaskRunner> callback_runner) scoped_refptr<base::TaskRunner> callback_runner)
: render_process_id_(render_process_id), : render_process_id_(render_process_id),
callback_(std::move(callback)), callback_(std::move(callback)),
callback_runner_(std::move(callback_runner)) {} callback_runner_(std::move(callback_runner)),
type_(type) {}
FileSystemChooser::~FileSystemChooser() { FileSystemChooser::~FileSystemChooser() {
if (dialog_) if (dialog_)
...@@ -73,21 +94,72 @@ void FileSystemChooser::MultiFilesSelected( ...@@ -73,21 +94,72 @@ void FileSystemChooser::MultiFilesSelected(
for (const auto& path : files) { for (const auto& path : files) {
auto entry = blink::mojom::FileSystemEntry::New(); auto entry = blink::mojom::FileSystemEntry::New();
entry->file_system_id = isolated_context->RegisterFileSystemForPath( entry->file_system_id = isolated_context->RegisterFileSystemForPath(
storage::kFileSystemTypeNativeForPlatformApp, std::string(), path, storage::kFileSystemTypeNativeLocal, std::string(), path,
&entry->base_name); &entry->base_name);
// TODO(https://crbug.com/878585): Determine if we always want to grant
// write permission.
security_policy->GrantReadFileSystem(render_process_id_, security_policy->GrantReadFileSystem(render_process_id_,
entry->file_system_id); entry->file_system_id);
security_policy->GrantWriteFileSystem(render_process_id_, // TODO(https://crbug.com/878585): Don't grant write/modify permissions
entry->file_system_id); // unless a website already has the appropriate permissions.
security_policy->GrantDeleteFromFileSystem(render_process_id_, if (type_ == blink::mojom::ChooseFileSystemEntryType::kOpenDirectory) {
entry->file_system_id); // Only grant Create permissions if we're opening a directory. And
// just granting Create permissions on top of Write and Delete does not
// actually grant the same permissions as calling this method.
security_policy->GrantCreateReadWriteFileSystem(render_process_id_,
entry->file_system_id);
} else {
security_policy->GrantWriteFileSystem(render_process_id_,
entry->file_system_id);
security_policy->GrantDeleteFromFileSystem(render_process_id_,
entry->file_system_id);
}
result.push_back(std::move(entry)); result.push_back(std::move(entry));
} }
if (type_ == blink::mojom::ChooseFileSystemEntryType::kSaveFile) {
// Create files if they don't yet exist.
// TODO(mek): If we change FileSystemFileHandle to be able to represent a
// file that doesn't exist on disk, we should be able to get rid of this
// step and make the whole API slightly more robust.
base::PostTaskWithTraits(
FROM_HERE, {base::TaskPriority::USER_BLOCKING, base::MayBlock()},
base::BindOnce(
[](const std::vector<base::FilePath>& files,
std::vector<blink::mojom::FileSystemEntryPtr> result,
scoped_refptr<base::TaskRunner> callback_runner,
ResultCallback callback) {
for (const auto& path : files) {
// Checking if a path exists, and then creating it if it doesn't
// is of course racy, but external applications could just as
// well be deleting the entire directory, or similar problematic
// cases, so no matter what we do there are always going to be
// race conditions and websites will just have to deal with any
// method being able to fail in unexpected ways.
if (base::PathExists(path))
continue;
int creation_flags =
base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_READ;
base::File file(path, creation_flags);
if (!file.IsValid()) {
callback_runner->PostTask(
FROM_HERE,
base::BindOnce(
std::move(callback), base::File::FILE_ERROR_FAILED,
std::vector<blink::mojom::FileSystemEntryPtr>()));
return;
}
}
callback_runner->PostTask(
FROM_HERE,
base::BindOnce(std::move(callback), base::File::FILE_OK,
std::move(result)));
},
files, std::move(result), callback_runner_, std::move(callback_)));
delete this;
return;
}
callback_runner_->PostTask( callback_runner_->PostTask(
FROM_HERE, base::BindOnce(std::move(callback_), base::File::FILE_OK, FROM_HERE, base::BindOnce(std::move(callback_), base::File::FILE_OK,
std::move(result))); std::move(result)));
......
...@@ -25,10 +25,12 @@ class FileSystemChooser : public ui::SelectFileDialog::Listener { ...@@ -25,10 +25,12 @@ class FileSystemChooser : public ui::SelectFileDialog::Listener {
static void CreateAndShow(int render_process_id, static void CreateAndShow(int render_process_id,
int frame_id, int frame_id,
blink::mojom::ChooseFileSystemEntryType type,
ResultCallback callback, ResultCallback callback,
scoped_refptr<base::TaskRunner> callback_runner); scoped_refptr<base::TaskRunner> callback_runner);
FileSystemChooser(int render_process_id, FileSystemChooser(int render_process_id,
blink::mojom::ChooseFileSystemEntryType type,
ResultCallback callback, ResultCallback callback,
scoped_refptr<base::TaskRunner> callback_runner); scoped_refptr<base::TaskRunner> callback_runner);
...@@ -46,6 +48,7 @@ class FileSystemChooser : public ui::SelectFileDialog::Listener { ...@@ -46,6 +48,7 @@ class FileSystemChooser : public ui::SelectFileDialog::Listener {
int render_process_id_; int render_process_id_;
ResultCallback callback_; ResultCallback callback_;
scoped_refptr<base::TaskRunner> callback_runner_; scoped_refptr<base::TaskRunner> callback_runner_;
blink::mojom::ChooseFileSystemEntryType type_;
scoped_refptr<ui::SelectFileDialog> dialog_; scoped_refptr<ui::SelectFileDialog> dialog_;
}; };
......
This diff is collapsed.
...@@ -592,7 +592,9 @@ void FileSystemManagerImpl::CreateWriter(const GURL& file_path, ...@@ -592,7 +592,9 @@ void FileSystemManagerImpl::CreateWriter(const GURL& file_path,
std::move(callback).Run(base::File::FILE_OK, std::move(writer)); std::move(callback).Run(base::File::FILE_OK, std::move(writer));
} }
void FileSystemManagerImpl::ChooseEntry(ChooseEntryCallback callback) { void FileSystemManagerImpl::ChooseEntry(
blink::mojom::ChooseFileSystemEntryType type,
ChooseEntryCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO); DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (!base::FeatureList::IsEnabled(blink::features::kWritableFilesAPI)) { if (!base::FeatureList::IsEnabled(blink::features::kWritableFilesAPI)) {
bindings_.ReportBadMessage("FSMI_WRITABLE_FILES_DISABLED"); bindings_.ReportBadMessage("FSMI_WRITABLE_FILES_DISABLED");
...@@ -602,7 +604,7 @@ void FileSystemManagerImpl::ChooseEntry(ChooseEntryCallback callback) { ...@@ -602,7 +604,7 @@ void FileSystemManagerImpl::ChooseEntry(ChooseEntryCallback callback) {
base::PostTaskWithTraits( base::PostTaskWithTraits(
FROM_HERE, {BrowserThread::UI}, FROM_HERE, {BrowserThread::UI},
base::BindOnce( base::BindOnce(
&FileSystemChooser::CreateAndShow, process_id_, frame_id_, &FileSystemChooser::CreateAndShow, process_id_, frame_id_, type,
std::move(callback), std::move(callback),
base::CreateSingleThreadTaskRunnerWithTraits({BrowserThread::IO}))); base::CreateSingleThreadTaskRunnerWithTraits({BrowserThread::IO})));
} }
......
...@@ -125,7 +125,8 @@ class CONTENT_EXPORT FileSystemManagerImpl ...@@ -125,7 +125,8 @@ class CONTENT_EXPORT FileSystemManagerImpl
GetPlatformPathCallback callback) override; GetPlatformPathCallback callback) override;
void CreateWriter(const GURL& file_path, void CreateWriter(const GURL& file_path,
CreateWriterCallback callback) override; CreateWriterCallback callback) override;
void ChooseEntry(ChooseEntryCallback callback) override; void ChooseEntry(blink::mojom::ChooseFileSystemEntryType type,
ChooseEntryCallback callback) override;
private: private:
class FileSystemCancellableOperationImpl; class FileSystemCancellableOperationImpl;
......
...@@ -758,6 +758,7 @@ test("content_browsertests") { ...@@ -758,6 +758,7 @@ test("content_browsertests") {
# These tests have incorrect threading (https://crbug.com/860547). # These tests have incorrect threading (https://crbug.com/860547).
#"../browser/fileapi/file_system_url_loader_factory_browsertest.cc", #"../browser/fileapi/file_system_url_loader_factory_browsertest.cc",
"../browser/fileapi/file_system_chooser_browsertest.cc",
"../browser/fileapi/fileapi_browsertest.cc", "../browser/fileapi/fileapi_browsertest.cc",
"../browser/find_request_manager_browsertest.cc", "../browser/find_request_manager_browsertest.cc",
"../browser/font_unique_name_lookup/font_unique_name_browsertest.cc", "../browser/font_unique_name_lookup/font_unique_name_browsertest.cc",
......
# FileSystem API
This directory contains part of the browser side implementation of various
filesystem related APIs, as well as interfaces to treat various types of native
and non-native filesystems mostly the same without having to worry about the
underlying implementation.
## Related directories
[`//content/browser/fileapi/`](../../../content/browser/fileapi) contains the
rest of the browser side implementation, while
[`blink/renderer/modules/filesystem`](../../../third_party/blink/renderer/modules/filesystem)
contains the renderer side implementation and
[`blink/public/mojom/filesystem`](../../../third_party/blink/public/mojom/filesystem)
contains the mojom interfaces for these APIs.
...@@ -72,6 +72,13 @@ interface ReceivedSnapshotListener { ...@@ -72,6 +72,13 @@ interface ReceivedSnapshotListener {
DidReceiveSnapshotFile(); DidReceiveSnapshotFile();
}; };
enum ChooseFileSystemEntryType {
kOpenFile,
kOpenMultipleFiles,
kSaveFile,
kOpenDirectory
};
// Interface provided by the browser to the renderer to carry out filesystem // Interface provided by the browser to the renderer to carry out filesystem
// operations. All [Sync] methods should only be called synchronously on worker // operations. All [Sync] methods should only be called synchronously on worker
// threads (and asynchronously otherwise). // threads (and asynchronously otherwise).
...@@ -219,7 +226,7 @@ interface FileSystemManager { ...@@ -219,7 +226,7 @@ interface FileSystemManager {
// success. // success.
// TODO(https://crbug.com/878581): Add more options to this method to support // TODO(https://crbug.com/878581): Add more options to this method to support
// multiple files, directories, "open" vs "save" dialogs, etc. // multiple files, directories, "open" vs "save" dialogs, etc.
ChooseEntry() => ChooseEntry(ChooseFileSystemEntryType type) =>
(mojo_base.mojom.FileError error_code, (mojo_base.mojom.FileError error_code,
array<FileSystemEntry> entries); array<FileSystemEntry> entries);
}; };
# FileSystem API
This directory contains the renderer side implementation of various filesystem
related APIs.
## Related directories
[`//storage/browser/fileapi/`](../../../storage/browser/fileapi) contains part
of the browser side implementation, while
[`//content/browser/fileapi/`](../../../content/browser/fileapi) contains the
rest of the browser side implementation and
[`blink/public/mojom/filesystem`](../../../third_party/blink/public/mojom/filesystem)
contains the mojom interfaces for these APIs.
## APIs In this directory
### File and Directory Entries API
First of all this directory contains the implementation of the
[Entries API](https://wicg.github.io/entries-api). This API consists of
types to expose read-only access to file and directory entries to the web,
primarily used by drag-and-drop and `<input type=file>`. Our implementation
doesn't match the interface names of the spec, but otherwise should be pretty
close to the spec.
TODO(mek): More details
### File API: Directories and FileSystem
Secondly this directory contains the implementation of something similar to the
deprecated [w3c file-system-api](https://www.w3.org/TR/2012/WD-file-system-api-20120417/).
This API is very similar to the previous Entries API, but it also adds support
for writing and modifying to files and directories, as well as a way to get
access to a origin scoped sandboxed filesystem.
TODO(mek): More details
### Writable Files
Finally this directory contains the implementation of the new and still under
development [Writable Files API](https://github.com/WICG/writable-files/blob/master/EXPLAINER.md).
This API is mostly implemented on top of the same backend as the previous two
APIs, but hopes to eventually replace both of those, while also adding new
functionality.
It consists of the following parts:
* `FileSystemBaseHandle`, `FileSystemFileHandle` and `FileSystemDirectoryHandle`:
these interfaces mimic the old `Entry` interfaces (and inherit from `EntryBase`
to share as much of the implementation as possible), but expose a more modern
promisified API.
* `getSystemDirectory`: An entry point (exposed via `FileSystemDirectoryHandle`)
that today only gives access to the same sandboxed filesystem as what was
available through the old API. In the future this could get extended to add
support for other directories as well.
* `FileSystemWriter`: a more modern API with similar functionality to the
old `FileWriter` API. The implementation of this actually does make use of
a different mojom interface than the old API. But since the functionality is
mostly the same, hopefully we will be able to migrate the old implementation
to the new mojom API as well.
* `chooseFileSystemEntries`: An entry point, currently on `window`, that lets
a website pop-up a file picker, prompting the user to select one or more
files or directories, to which the website than gets access.
Since the `Handle` interfaces are based on the implementation of the `Entry`
interfaces, internally and across IPC these are still represented by
`filesystem://` URLs. Hopefully in the future we will be able to change this and
turn it into a more capabilities based API (where having a mojo handle gives you
access to specific files or directories), as with the current implementation it
is very hard to properly support transferring handles to other processes via
postMessage (which is something we do want to support in the future).
// Copyright 2018 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.
// https://github.com/WICG/writable-files/blob/master/EXPLAINER.md
enum ChooseFileSystemEntriesType { "openFile", "saveFile", "openDirectory" };
dictionary ChooseFileSystemEntriesOptions {
ChooseFileSystemEntriesType type = "openFile";
boolean multiple = false;
};
...@@ -135,7 +135,8 @@ static_assert( ...@@ -135,7 +135,8 @@ static_assert(
ScriptPromise DOMWindowFileSystem::chooseFileSystemEntries( ScriptPromise DOMWindowFileSystem::chooseFileSystemEntries(
ScriptState* script_state, ScriptState* script_state,
LocalDOMWindow& window) { LocalDOMWindow& window,
const ChooseFileSystemEntriesOptions& options) {
if (!window.IsCurrentlyDisplayedInFrame()) { if (!window.IsCurrentlyDisplayedInFrame()) {
return ScriptPromise::RejectWithDOMException( return ScriptPromise::RejectWithDOMException(
script_state, DOMException::Create(DOMExceptionCode::kAbortError)); script_state, DOMException::Create(DOMExceptionCode::kAbortError));
...@@ -157,7 +158,7 @@ ScriptPromise DOMWindowFileSystem::chooseFileSystemEntries( ...@@ -157,7 +158,7 @@ ScriptPromise DOMWindowFileSystem::chooseFileSystemEntries(
auto* resolver = ScriptPromiseResolver::Create(script_state); auto* resolver = ScriptPromiseResolver::Create(script_state);
ScriptPromise result = resolver->Promise(); ScriptPromise result = resolver->Promise();
LocalFileSystem::From(*document)->ChooseEntry(resolver); LocalFileSystem::From(*document)->ChooseEntry(resolver, options);
return result; return result;
} }
......
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
namespace blink { namespace blink {
class ChooseFileSystemEntriesOptions;
class LocalDOMWindow; class LocalDOMWindow;
class ScriptPromise; class ScriptPromise;
class ScriptState; class ScriptState;
...@@ -60,7 +61,10 @@ class DOMWindowFileSystem { ...@@ -60,7 +61,10 @@ class DOMWindowFileSystem {
kPersistent, kPersistent,
}; };
static ScriptPromise chooseFileSystemEntries(ScriptState*, LocalDOMWindow&); static ScriptPromise chooseFileSystemEntries(
ScriptState*,
LocalDOMWindow&,
const ChooseFileSystemEntriesOptions&);
}; };
} // namespace blink } // namespace blink
......
...@@ -436,18 +436,20 @@ void FileSystemDispatcher::CreateFileWriter( ...@@ -436,18 +436,20 @@ void FileSystemDispatcher::CreateFileWriter(
} }
void FileSystemDispatcher::ChooseEntry( void FileSystemDispatcher::ChooseEntry(
mojom::blink::ChooseFileSystemEntryType type,
std::unique_ptr<ChooseEntryCallbacks> callbacks) { std::unique_ptr<ChooseEntryCallbacks> callbacks) {
GetFileSystemManager().ChooseEntry(WTF::Bind( GetFileSystemManager().ChooseEntry(
[](std::unique_ptr<ChooseEntryCallbacks> callbacks, type, WTF::Bind(
base::File::Error result, [](std::unique_ptr<ChooseEntryCallbacks> callbacks,
Vector<mojom::blink::FileSystemEntryPtr> entries) { base::File::Error result,
if (result != base::File::FILE_OK) { Vector<mojom::blink::FileSystemEntryPtr> entries) {
callbacks->OnError(result); if (result != base::File::FILE_OK) {
} else { callbacks->OnError(result);
callbacks->OnSuccess(std::move(entries)); } else {
} callbacks->OnSuccess(std::move(entries));
}, }
std::move(callbacks))); },
std::move(callbacks)));
} }
mojom::blink::FileSystemManager& FileSystemDispatcher::GetFileSystemManager() { mojom::blink::FileSystemManager& FileSystemDispatcher::GetFileSystemManager() {
......
...@@ -143,7 +143,8 @@ class FileSystemDispatcher ...@@ -143,7 +143,8 @@ class FileSystemDispatcher
using ChooseEntryCallbacks = using ChooseEntryCallbacks =
WebCallbacks<Vector<mojom::blink::FileSystemEntryPtr>, base::File::Error>; WebCallbacks<Vector<mojom::blink::FileSystemEntryPtr>, base::File::Error>;
void ChooseEntry(std::unique_ptr<ChooseEntryCallbacks> callbacks); void ChooseEntry(mojom::blink::ChooseFileSystemEntryType,
std::unique_ptr<ChooseEntryCallbacks> callbacks);
private: private:
class WriteListener; class WriteListener;
......
...@@ -43,6 +43,7 @@ ...@@ -43,6 +43,7 @@
#include "third_party/blink/renderer/core/frame/local_frame.h" #include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/frame/local_frame_client.h" #include "third_party/blink/renderer/core/frame/local_frame_client.h"
#include "third_party/blink/renderer/core/workers/worker_global_scope.h" #include "third_party/blink/renderer/core/workers/worker_global_scope.h"
#include "third_party/blink/renderer/modules/filesystem/choose_file_system_entries_options.h"
#include "third_party/blink/renderer/modules/filesystem/directory_entry.h" #include "third_party/blink/renderer/modules/filesystem/directory_entry.h"
#include "third_party/blink/renderer/modules/filesystem/dom_file_system.h" #include "third_party/blink/renderer/modules/filesystem/dom_file_system.h"
#include "third_party/blink/renderer/modules/filesystem/file_system_client.h" #include "third_party/blink/renderer/modules/filesystem/file_system_client.h"
...@@ -120,12 +121,13 @@ class ChooseEntryCallbacks ...@@ -120,12 +121,13 @@ class ChooseEntryCallbacks
: public WebCallbacks<Vector<mojom::blink::FileSystemEntryPtr>, : public WebCallbacks<Vector<mojom::blink::FileSystemEntryPtr>,
base::File::Error> { base::File::Error> {
public: public:
ChooseEntryCallbacks(ScriptPromiseResolver* resolver, bool return_multiple) ChooseEntryCallbacks(ScriptPromiseResolver* resolver,
: resolver_(resolver), return_multiple_(return_multiple) {} const ChooseFileSystemEntriesOptions& options)
: resolver_(resolver), options_(options) {}
void OnSuccess(Vector<mojom::blink::FileSystemEntryPtr> entries) override { void OnSuccess(Vector<mojom::blink::FileSystemEntryPtr> entries) override {
ScriptState::Scope scope(resolver_->GetScriptState()); ScriptState::Scope scope(resolver_->GetScriptState());
if (return_multiple_) { if (options_.multiple()) {
Vector<ScriptPromise> result; Vector<ScriptPromise> result;
result.ReserveInitialCapacity(SafeCast<wtf_size_t>(entries.size())); result.ReserveInitialCapacity(SafeCast<wtf_size_t>(entries.size()));
for (const auto& entry : entries) for (const auto& entry : entries)
...@@ -152,19 +154,43 @@ class ChooseEntryCallbacks ...@@ -152,19 +154,43 @@ class ChooseEntryCallbacks
resolver_->GetExecutionContext(), entry->file_system_id); resolver_->GetExecutionContext(), entry->file_system_id);
// TODO(mek): Try to create handle directly rather than having to do more // TODO(mek): Try to create handle directly rather than having to do more
// IPCs to get the actual entries. // IPCs to get the actual entries.
fs->GetFile(fs->root(), entry->base_name, FileSystemFlags(), if (options_.type() == "openDirectory") {
new EntryCallbacks::OnDidGetEntryPromiseImpl(new_resolver), fs->GetDirectory(
new PromiseErrorCallback(new_resolver)); fs->root(), entry->base_name, FileSystemFlags(),
new EntryCallbacks::OnDidGetEntryPromiseImpl(new_resolver),
new PromiseErrorCallback(new_resolver));
} else {
fs->GetFile(fs->root(), entry->base_name, FileSystemFlags(),
new EntryCallbacks::OnDidGetEntryPromiseImpl(new_resolver),
new PromiseErrorCallback(new_resolver));
}
return result; return result;
} }
Persistent<ScriptPromiseResolver> resolver_; Persistent<ScriptPromiseResolver> resolver_;
bool return_multiple_; ChooseFileSystemEntriesOptions options_;
}; };
mojom::blink::ChooseFileSystemEntryType ConvertChooserType(const String& input,
bool multiple) {
if (input == "openFile") {
return multiple
? mojom::blink::ChooseFileSystemEntryType::kOpenMultipleFiles
: mojom::blink::ChooseFileSystemEntryType::kOpenFile;
}
if (input == "saveFile")
return mojom::blink::ChooseFileSystemEntryType::kSaveFile;
if (input == "openDirectory")
return mojom::blink::ChooseFileSystemEntryType::kOpenDirectory;
NOTREACHED();
return mojom::blink::ChooseFileSystemEntryType::kOpenFile;
}
} // namespace } // namespace
void LocalFileSystem::ChooseEntry(ScriptPromiseResolver* resolver) { void LocalFileSystem::ChooseEntry(
ScriptPromiseResolver* resolver,
const ChooseFileSystemEntriesOptions& options) {
if (!base::FeatureList::IsEnabled(blink::features::kWritableFilesAPI)) { if (!base::FeatureList::IsEnabled(blink::features::kWritableFilesAPI)) {
resolver->Reject( resolver->Reject(
FileError::CreateDOMException(base::File::FILE_ERROR_ABORT)); FileError::CreateDOMException(base::File::FILE_ERROR_ABORT));
...@@ -172,7 +198,8 @@ void LocalFileSystem::ChooseEntry(ScriptPromiseResolver* resolver) { ...@@ -172,7 +198,8 @@ void LocalFileSystem::ChooseEntry(ScriptPromiseResolver* resolver) {
} }
FileSystemDispatcher::From(resolver->GetExecutionContext()) FileSystemDispatcher::From(resolver->GetExecutionContext())
.ChooseEntry(std::make_unique<ChooseEntryCallbacks>(resolver, false)); .ChooseEntry(ConvertChooserType(options.type(), options.multiple()),
std::make_unique<ChooseEntryCallbacks>(resolver, options));
} }
void LocalFileSystem::RequestFileSystemAccessInternal( void LocalFileSystem::RequestFileSystemAccessInternal(
......
...@@ -45,6 +45,7 @@ namespace blink { ...@@ -45,6 +45,7 @@ namespace blink {
class AsyncFileSystemCallbacks; class AsyncFileSystemCallbacks;
class CallbackWrapper; class CallbackWrapper;
class ChooseFileSystemEntriesOptions;
class FileSystemClient; class FileSystemClient;
class ExecutionContext; class ExecutionContext;
class KURL; class KURL;
...@@ -76,7 +77,8 @@ class LocalFileSystem final : public GarbageCollectedFinalized<LocalFileSystem>, ...@@ -76,7 +77,8 @@ class LocalFileSystem final : public GarbageCollectedFinalized<LocalFileSystem>,
std::unique_ptr<AsyncFileSystemCallbacks>, std::unique_ptr<AsyncFileSystemCallbacks>,
SynchronousType sync_type); SynchronousType sync_type);
void ChooseEntry(ScriptPromiseResolver*); void ChooseEntry(ScriptPromiseResolver*,
const ChooseFileSystemEntriesOptions& options);
FileSystemClient& Client() const { return *client_; } FileSystemClient& Client() const { return *client_; }
......
...@@ -41,5 +41,6 @@ ...@@ -41,5 +41,6 @@
// https://github.com/WICG/writable-files/blob/master/EXPLAINER.md // https://github.com/WICG/writable-files/blob/master/EXPLAINER.md
// TODO(crbug.com/878581): This needs some kind of options dictionary. // TODO(crbug.com/878581): This needs some kind of options dictionary.
[RuntimeEnabled=WritableFiles, CallWith=ScriptState, SecureContext] [RuntimeEnabled=WritableFiles, CallWith=ScriptState, SecureContext]
Promise<(FileSystemBaseHandle or sequence<FileSystemBaseHandle>)> chooseFileSystemEntries(); Promise<(FileSystemBaseHandle or sequence<FileSystemBaseHandle>)>
chooseFileSystemEntries(optional ChooseFileSystemEntriesOptions options);
}; };
...@@ -528,6 +528,7 @@ modules_dictionary_idl_files = ...@@ -528,6 +528,7 @@ modules_dictionary_idl_files =
"encryptedmedia/media_key_system_media_capability.idl", "encryptedmedia/media_key_system_media_capability.idl",
"encryptedmedia/media_keys_policy.idl", "encryptedmedia/media_keys_policy.idl",
"eventsource/event_source_init.idl", "eventsource/event_source_init.idl",
"filesystem/choose_file_system_entries_options.idl",
"filesystem/file_system_directory_iterator_entry.idl", "filesystem/file_system_directory_iterator_entry.idl",
"filesystem/file_system_flags.idl", "filesystem/file_system_flags.idl",
"filesystem/file_system_get_directory_options.idl", "filesystem/file_system_get_directory_options.idl",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment