| Index: chrome/browser/extensions/api/file_system/chrome_file_system_delegate.cc
|
| diff --git a/chrome/browser/extensions/api/file_system/file_system_api.cc b/chrome/browser/extensions/api/file_system/chrome_file_system_delegate.cc
|
| similarity index 17%
|
| copy from chrome/browser/extensions/api/file_system/file_system_api.cc
|
| copy to chrome/browser/extensions/api/file_system/chrome_file_system_delegate.cc
|
| index 58f48039b8db60a039b411781f442d05ad03f2ed..2bad956bd95a558cdd98c558a30593ae58cac8f4 100644
|
| --- a/chrome/browser/extensions/api/file_system/file_system_api.cc
|
| +++ b/chrome/browser/extensions/api/file_system/chrome_file_system_delegate.cc
|
| @@ -1,13 +1,9 @@
|
| -// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
| +// Copyright 2017 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 "chrome/browser/extensions/api/file_system/file_system_api.h"
|
| +#include "chrome/browser/extensions/api/file_system/chrome_file_system_delegate.h"
|
|
|
| -#include <stddef.h>
|
| -
|
| -#include <memory>
|
| -#include <set>
|
| #include <utility>
|
| #include <vector>
|
|
|
| @@ -15,54 +11,37 @@
|
| #include "base/bind.h"
|
| #include "base/callback.h"
|
| #include "base/files/file_path.h"
|
| -#include "base/files/file_util.h"
|
| -#include "base/macros.h"
|
| +#include "base/logging.h"
|
| #include "base/memory/ptr_util.h"
|
| #include "base/path_service.h"
|
| -#include "base/stl_util.h"
|
| -#include "base/strings/string_util.h"
|
| -#include "base/strings/stringprintf.h"
|
| -#include "base/strings/sys_string_conversions.h"
|
| -#include "base/strings/utf_string_conversions.h"
|
| -#include "base/task_scheduler/post_task.h"
|
| -#include "base/value_conversions.h"
|
| -#include "base/values.h"
|
| -#include "build/build_config.h"
|
| +#include "base/strings/string16.h"
|
| #include "chrome/browser/extensions/api/file_system/file_entry_picker.h"
|
| -#include "chrome/browser/platform_util.h"
|
| +#include "chrome/browser/extensions/chrome_extension_function_details.h"
|
| #include "chrome/browser/profiles/profile.h"
|
| #include "chrome/browser/ui/apps/directory_access_confirmation_dialog.h"
|
| -#include "chrome/browser/ui/chrome_select_file_policy.h"
|
| #include "chrome/common/chrome_paths.h"
|
| -#include "chrome/common/extensions/api/file_system.h"
|
| #include "chrome/grit/generated_resources.h"
|
| -#include "content/public/browser/browser_thread.h"
|
| +#include "content/public/browser/browser_context.h"
|
| #include "content/public/browser/child_process_security_policy.h"
|
| #include "content/public/browser/render_frame_host.h"
|
| #include "content/public/browser/render_process_host.h"
|
| #include "content/public/browser/storage_partition.h"
|
| #include "content/public/browser/web_contents.h"
|
| #include "extensions/browser/api/file_handlers/app_file_handler_util.h"
|
| -#include "extensions/browser/api/file_system/saved_file_entry.h"
|
| +#include "extensions/browser/api/file_system/saved_files_service_interface.h"
|
| #include "extensions/browser/app_window/app_window.h"
|
| #include "extensions/browser/app_window/app_window_registry.h"
|
| +#include "extensions/browser/extension_function.h"
|
| #include "extensions/browser/extension_prefs.h"
|
| #include "extensions/browser/extension_system.h"
|
| #include "extensions/browser/extension_util.h"
|
| -#include "extensions/browser/granted_file_entry.h"
|
| -#include "extensions/browser/path_util.h"
|
| -#include "extensions/common/permissions/api_permission.h"
|
| -#include "extensions/common/permissions/permissions_data.h"
|
| -#include "net/base/mime_util.h"
|
| +#include "extensions/common/api/file_system.h"
|
| +#include "extensions/common/extension.h"
|
| #include "storage/browser/fileapi/external_mount_points.h"
|
| -#include "storage/browser/fileapi/file_system_operation_runner.h"
|
| #include "storage/browser/fileapi/isolated_context.h"
|
| #include "storage/common/fileapi/file_system_types.h"
|
| #include "storage/common/fileapi/file_system_util.h"
|
| -#include "ui/base/l10n/l10n_util.h"
|
| -#include "ui/base/ui_base_types.h"
|
| #include "ui/shell_dialogs/select_file_dialog.h"
|
| -#include "ui/shell_dialogs/select_file_policy.h"
|
|
|
| #if defined(OS_MACOSX)
|
| #include <CoreFoundation/CoreFoundation.h>
|
| @@ -70,1025 +49,311 @@
|
| #endif
|
|
|
| #if defined(OS_CHROMEOS)
|
| -#include "base/strings/string16.h"
|
| -#include "chrome/browser/chromeos/file_manager/filesystem_api_util.h"
|
| #include "chrome/browser/chromeos/file_manager/volume_manager.h"
|
| +#include "chrome/browser/extensions/api/file_system/consent_provider.h"
|
| #include "extensions/browser/event_router.h"
|
| #include "extensions/browser/extension_registry.h"
|
| #include "extensions/common/constants.h"
|
| #include "url/url_constants.h"
|
| #endif
|
|
|
| -using apps::SavedFilesService;
|
| -using storage::IsolatedContext;
|
| -
|
| -const char kInvalidCallingPage[] =
|
| - "Invalid calling page. "
|
| - "This function can't be called from a background page.";
|
| -const char kUserCancelled[] = "User cancelled";
|
| -const char kWritableFileErrorFormat[] = "Error opening %s";
|
| -const char kRequiresFileSystemWriteError[] =
|
| - "Operation requires fileSystem.write permission";
|
| -const char kRequiresFileSystemDirectoryError[] =
|
| - "Operation requires fileSystem.directory permission";
|
| -const char kMultipleUnsupportedError[] =
|
| - "acceptsMultiple: true is only supported for 'openFile'";
|
| -const char kUnknownIdError[] = "Unknown id";
|
| -
|
| -#if !defined(OS_CHROMEOS)
|
| -const char kNotSupportedOnCurrentPlatformError[] =
|
| - "Operation not supported on the current platform.";
|
| -#else
|
| -const char kNotSupportedOnNonKioskSessionError[] =
|
| - "Operation only supported for kiosk apps running in a kiosk session.";
|
| -const char kVolumeNotFoundError[] = "Volume not found.";
|
| -const char kSecurityError[] = "Security error.";
|
| -const char kConsentImpossible[] =
|
| - "Impossible to ask for user consent as there is no app window visible.";
|
| -#endif
|
| -
|
| namespace extensions {
|
|
|
| namespace file_system = api::file_system;
|
| -namespace ChooseEntry = file_system::ChooseEntry;
|
| -
|
| -namespace {
|
| -
|
| -bool g_skip_picker_for_test = false;
|
| -bool g_use_suggested_path_for_test = false;
|
| -base::FilePath* g_path_to_be_picked_for_test;
|
| -std::vector<base::FilePath>* g_paths_to_be_picked_for_test;
|
| -bool g_skip_directory_confirmation_for_test = false;
|
| -bool g_allow_directory_access_for_test = false;
|
| -
|
| -// Expand the mime-types and extensions provided in an AcceptOption, returning
|
| -// them within the passed extension vector. Returns false if no valid types
|
| -// were found.
|
| -bool GetFileTypesFromAcceptOption(
|
| - const file_system::AcceptOption& accept_option,
|
| - std::vector<base::FilePath::StringType>* extensions,
|
| - base::string16* description) {
|
| - std::set<base::FilePath::StringType> extension_set;
|
| - int description_id = 0;
|
|
|
| - if (accept_option.mime_types.get()) {
|
| - std::vector<std::string>* list = accept_option.mime_types.get();
|
| - bool valid_type = false;
|
| - for (std::vector<std::string>::const_iterator iter = list->begin();
|
| - iter != list->end(); ++iter) {
|
| - std::vector<base::FilePath::StringType> inner;
|
| - std::string accept_type = base::ToLowerASCII(*iter);
|
| - net::GetExtensionsForMimeType(accept_type, &inner);
|
| - if (inner.empty())
|
| - continue;
|
| -
|
| - if (valid_type)
|
| - description_id = 0; // We already have an accept type with label; if
|
| - // we find another, give up and use the default.
|
| - else if (accept_type == "image/*")
|
| - description_id = IDS_IMAGE_FILES;
|
| - else if (accept_type == "audio/*")
|
| - description_id = IDS_AUDIO_FILES;
|
| - else if (accept_type == "video/*")
|
| - description_id = IDS_VIDEO_FILES;
|
| -
|
| - extension_set.insert(inner.begin(), inner.end());
|
| - valid_type = true;
|
| - }
|
| - }
|
| -
|
| - if (accept_option.extensions.get()) {
|
| - std::vector<std::string>* list = accept_option.extensions.get();
|
| - for (std::vector<std::string>::const_iterator iter = list->begin();
|
| - iter != list->end(); ++iter) {
|
| - std::string extension = base::ToLowerASCII(*iter);
|
| -#if defined(OS_WIN)
|
| - extension_set.insert(base::UTF8ToWide(*iter));
|
| -#else
|
| - extension_set.insert(*iter);
|
| -#endif
|
| - }
|
| - }
|
| -
|
| - extensions->assign(extension_set.begin(), extension_set.end());
|
| - if (extensions->empty())
|
| - return false;
|
| -
|
| - if (accept_option.description.get())
|
| - *description = base::UTF8ToUTF16(*accept_option.description);
|
| - else if (description_id)
|
| - *description = l10n_util::GetStringUTF16(description_id);
|
| -
|
| - return true;
|
| -}
|
| -
|
| -// Key for the path of the directory of the file last chosen by the user in
|
| -// response to a chrome.fileSystem.chooseEntry() call.
|
| -const char kLastChooseEntryDirectory[] = "last_choose_file_directory";
|
| -
|
| -const int kGraylistedPaths[] = {
|
| - base::DIR_HOME,
|
| -#if defined(OS_WIN)
|
| - base::DIR_PROGRAM_FILES, base::DIR_PROGRAM_FILESX86, base::DIR_WINDOWS,
|
| -#endif
|
| -};
|
| -
|
| -typedef base::Callback<void(std::unique_ptr<base::File::Info>)>
|
| - FileInfoOptCallback;
|
| +#if defined(OS_CHROMEOS)
|
| +using file_system_api::ConsentProvider;
|
| +using file_system_api::ConsentProviderDelegate;
|
|
|
| -// Passes optional file info to the UI thread depending on |result| and |info|.
|
| -void PassFileInfoToUIThread(const FileInfoOptCallback& callback,
|
| - base::File::Error result,
|
| - const base::File::Info& info) {
|
| - DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
| - std::unique_ptr<base::File::Info> file_info(
|
| - result == base::File::FILE_OK ? new base::File::Info(info) : NULL);
|
| - content::BrowserThread::PostTask(
|
| - content::BrowserThread::UI, FROM_HERE,
|
| - base::BindOnce(callback, base::Passed(&file_info)));
|
| -}
|
| +namespace {
|
|
|
| -// Gets a WebContents instance handle for a platform app hosted in
|
| -// |render_frame_host|. If not found, then returns NULL.
|
| -content::WebContents* GetWebContentsForRenderFrameHost(
|
| - content::BrowserContext* browser_context,
|
| - content::RenderFrameHost* render_frame_host) {
|
| - content::WebContents* web_contents =
|
| - content::WebContents::FromRenderFrameHost(render_frame_host);
|
| - // Check if there is an app window associated with the web contents; if not,
|
| - // return null.
|
| - return AppWindowRegistry::Get(browser_context)
|
| - ->GetAppWindowForWebContents(web_contents)
|
| - ? web_contents
|
| - : nullptr;
|
| -}
|
| +const char kConsentImpossible[] =
|
| + "Impossible to ask for user consent as there is no app window visible.";
|
| +const char kNotSupportedOnNonKioskSessionError[] =
|
| + "Operation only supported for kiosk apps running in a kiosk session.";
|
| +const char kRequiresFileSystemWriteError[] =
|
| + "Operation requires fileSystem.write permission";
|
| +const char kSecurityError[] = "Security error.";
|
| +const char kVolumeNotFoundError[] = "Volume not found.";
|
|
|
| -#if defined(OS_CHROMEOS)
|
| // Fills a list of volumes mounted in the system.
|
| -void FillVolumeList(Profile* profile,
|
| - std::vector<api::file_system::Volume>* result) {
|
| +void FillVolumeList(content::BrowserContext* browser_context,
|
| + std::vector<file_system::Volume>* result) {
|
| file_manager::VolumeManager* const volume_manager =
|
| - file_manager::VolumeManager::Get(profile);
|
| + file_manager::VolumeManager::Get(browser_context);
|
| DCHECK(volume_manager);
|
|
|
| const auto& volume_list = volume_manager->GetVolumeList();
|
| // Convert volume_list to result_volume_list.
|
| for (const auto& volume : volume_list) {
|
| - api::file_system::Volume result_volume;
|
| + file_system::Volume result_volume;
|
| result_volume.volume_id = volume->volume_id();
|
| result_volume.writable = !volume->is_read_only();
|
| result->push_back(std::move(result_volume));
|
| }
|
| }
|
| -#endif
|
|
|
| -// Creates and shows a SelectFileDialog, or returns false if the dialog could
|
| -// not be created.
|
| -bool ShowSelectFileDialog(
|
| - scoped_refptr<UIThreadExtensionFunction> extension_function,
|
| - ui::SelectFileDialog::Type type,
|
| - const base::FilePath& default_path,
|
| - const ui::SelectFileDialog::FileTypeInfo* file_types,
|
| - FileEntryPicker::FilesSelectedCallback files_selected_callback,
|
| - base::OnceClosure file_selection_canceled_callback) {
|
| - // TODO(asargent/benwells) - As a short term remediation for
|
| - // crbug.com/179010 we're adding the ability for a whitelisted extension to
|
| - // use this API since chrome.fileBrowserHandler.selectFile is ChromeOS-only.
|
| - // Eventually we'd like a better solution and likely this code will go back
|
| - // to being platform-app only.
|
| - content::WebContents* const web_contents =
|
| - extension_function->extension()->is_platform_app()
|
| - ? GetWebContentsForRenderFrameHost(
|
| - extension_function->browser_context(),
|
| - extension_function->render_frame_host())
|
| - : extension_function->GetAssociatedWebContents();
|
| - if (!web_contents)
|
| - return false;
|
| -
|
| - // The file picker will hold a reference to the UIThreadExtensionFunction
|
| - // instance, preventing its destruction (and subsequent sending of the
|
| - // function response) until the user has selected a file or cancelled the
|
| - // picker. At that point, the picker will delete itself, which will also free
|
| - // the function instance.
|
| - new FileEntryPicker(web_contents, default_path, *file_types, type,
|
| - std::move(files_selected_callback),
|
| - std::move(file_selection_canceled_callback));
|
| - return true;
|
| -}
|
| -
|
| -} // namespace
|
| -
|
| -namespace file_system_api {
|
| -
|
| -base::FilePath GetLastChooseEntryDirectory(const ExtensionPrefs* prefs,
|
| - const std::string& extension_id) {
|
| - base::FilePath path;
|
| - std::string string_path;
|
| - if (prefs->ReadPrefAsString(extension_id, kLastChooseEntryDirectory,
|
| - &string_path)) {
|
| - path = base::FilePath::FromUTF8Unsafe(string_path);
|
| - }
|
| - return path;
|
| -}
|
| -
|
| -void SetLastChooseEntryDirectory(ExtensionPrefs* prefs,
|
| - const std::string& extension_id,
|
| - const base::FilePath& path) {
|
| - prefs->UpdateExtensionPref(extension_id, kLastChooseEntryDirectory,
|
| - base::CreateFilePathValue(path));
|
| -}
|
| -
|
| -#if defined(OS_CHROMEOS)
|
| -void DispatchVolumeListChangeEvent(Profile* profile) {
|
| - DCHECK(profile);
|
| - EventRouter* const event_router = EventRouter::Get(profile);
|
| - if (!event_router) // Possible on shutdown.
|
| - return;
|
| +// Callback called when consent is granted or denied.
|
| +void OnConsentReceived(
|
| + content::BrowserContext* browser_context,
|
| + scoped_refptr<UIThreadExtensionFunction> requester,
|
| + const FileSystemDelegate::FileSystemCallback& success_callback,
|
| + const FileSystemDelegate::ErrorCallback& error_callback,
|
| + const std::string& extension_id,
|
| + const base::WeakPtr<file_manager::Volume>& volume,
|
| + bool writable,
|
| + ConsentProvider::Consent result) {
|
| + using file_manager::VolumeManager;
|
| + using file_manager::Volume;
|
|
|
| - ExtensionRegistry* const registry = ExtensionRegistry::Get(profile);
|
| - if (!registry) // Possible on shutdown.
|
| + // Render frame host can be gone before this callback method is executed.
|
| + if (!requester->render_frame_host()) {
|
| + error_callback.Run(std::string());
|
| return;
|
| -
|
| - ConsentProviderDelegate consent_provider_delegate(profile, nullptr);
|
| - ConsentProvider consent_provider(&consent_provider_delegate);
|
| - api::file_system::VolumeListChangedEvent event_args;
|
| - FillVolumeList(profile, &event_args.volumes);
|
| - for (const auto& extension : registry->enabled_extensions()) {
|
| - if (!consent_provider.IsGrantable(*extension.get()))
|
| - continue;
|
| - event_router->DispatchEventToExtension(
|
| - extension->id(),
|
| - base::MakeUnique<Event>(
|
| - events::FILE_SYSTEM_ON_VOLUME_LIST_CHANGED,
|
| - api::file_system::OnVolumeListChanged::kEventName,
|
| - api::file_system::OnVolumeListChanged::Create(event_args)));
|
| }
|
| -}
|
| -#endif
|
|
|
| -} // namespace file_system_api
|
| -
|
| -#if defined(OS_CHROMEOS)
|
| -using file_system_api::ConsentProvider;
|
| -#endif
|
| + switch (result) {
|
| + case ConsentProvider::CONSENT_REJECTED:
|
| + error_callback.Run(kSecurityError);
|
| + return;
|
|
|
| -ExtensionFunction::ResponseAction FileSystemGetDisplayPathFunction::Run() {
|
| - std::string filesystem_name;
|
| - std::string filesystem_path;
|
| - EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &filesystem_name));
|
| - EXTENSION_FUNCTION_VALIDATE(args_->GetString(1, &filesystem_path));
|
| + case ConsentProvider::CONSENT_IMPOSSIBLE:
|
| + error_callback.Run(kConsentImpossible);
|
| + return;
|
|
|
| - base::FilePath file_path;
|
| - std::string error;
|
| - if (!app_file_handler_util::ValidateFileEntryAndGetPath(
|
| - filesystem_name, filesystem_path,
|
| - render_frame_host()->GetProcess()->GetID(), &file_path, &error)) {
|
| - return RespondNow(Error(error));
|
| + case ConsentProvider::CONSENT_GRANTED:
|
| + break;
|
| }
|
|
|
| - file_path = path_util::PrettifyPath(file_path);
|
| - return RespondNow(
|
| - OneArgument(base::MakeUnique<base::Value>(file_path.value())));
|
| -}
|
| -
|
| -FileSystemEntryFunction::FileSystemEntryFunction()
|
| - : multiple_(false), is_directory_(false) {}
|
| -
|
| -void FileSystemEntryFunction::PrepareFilesForWritableApp(
|
| - const std::vector<base::FilePath>& paths) {
|
| - DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
| - // TODO(cmihail): Path directory set should be initialized only with the
|
| - // paths that are actually directories, but for now we will consider
|
| - // all paths directories in case is_directory_ is true, otherwise
|
| - // all paths files, as this was the previous logic.
|
| - std::set<base::FilePath> path_directory_set_ =
|
| - is_directory_ ? std::set<base::FilePath>(paths.begin(), paths.end())
|
| - : std::set<base::FilePath>{};
|
| - app_file_handler_util::PrepareFilesForWritableApp(
|
| - paths, GetProfile(), path_directory_set_,
|
| - base::Bind(&FileSystemEntryFunction::RegisterFileSystemsAndSendResponse,
|
| - this, paths),
|
| - base::Bind(&FileSystemEntryFunction::HandleWritableFileError, this));
|
| -}
|
| -
|
| -void FileSystemEntryFunction::RegisterFileSystemsAndSendResponse(
|
| - const std::vector<base::FilePath>& paths) {
|
| - DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
| - if (!render_frame_host())
|
| + if (!volume.get()) {
|
| + error_callback.Run(kVolumeNotFoundError);
|
| return;
|
| + }
|
|
|
| - std::unique_ptr<base::DictionaryValue> result = CreateResult();
|
| - for (const auto& path : paths)
|
| - AddEntryToResult(path, std::string(), result.get());
|
| - SetResult(std::move(result));
|
| - SendResponse(true);
|
| -}
|
| -
|
| -std::unique_ptr<base::DictionaryValue> FileSystemEntryFunction::CreateResult() {
|
| - std::unique_ptr<base::DictionaryValue> result(new base::DictionaryValue());
|
| - result->Set("entries", base::MakeUnique<base::ListValue>());
|
| - result->SetBoolean("multiple", multiple_);
|
| - return result;
|
| -}
|
| -
|
| -void FileSystemEntryFunction::AddEntryToResult(const base::FilePath& path,
|
| - const std::string& id_override,
|
| - base::DictionaryValue* result) {
|
| - GrantedFileEntry file_entry = app_file_handler_util::CreateFileEntry(
|
| - GetProfile(), extension(), render_frame_host()->GetProcess()->GetID(),
|
| - path, is_directory_);
|
| - base::ListValue* entries;
|
| - bool success = result->GetList("entries", &entries);
|
| - DCHECK(success);
|
| -
|
| - std::unique_ptr<base::DictionaryValue> entry(new base::DictionaryValue());
|
| - entry->SetString("fileSystemId", file_entry.filesystem_id);
|
| - entry->SetString("baseName", file_entry.registered_name);
|
| - if (id_override.empty())
|
| - entry->SetString("id", file_entry.id);
|
| - else
|
| - entry->SetString("id", id_override);
|
| - entry->SetBoolean("isDirectory", is_directory_);
|
| - entries->Append(std::move(entry));
|
| -}
|
| -
|
| -void FileSystemEntryFunction::HandleWritableFileError(
|
| - const base::FilePath& error_path) {
|
| - DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
| - error_ = base::StringPrintf(kWritableFileErrorFormat,
|
| - error_path.BaseName().AsUTF8Unsafe().c_str());
|
| - SendResponse(false);
|
| -}
|
| -
|
| -bool FileSystemGetWritableEntryFunction::RunAsync() {
|
| - std::string filesystem_name;
|
| - std::string filesystem_path;
|
| - EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &filesystem_name));
|
| - EXTENSION_FUNCTION_VALIDATE(args_->GetString(1, &filesystem_path));
|
| + const GURL site = util::GetSiteForExtensionId(extension_id, browser_context);
|
| + scoped_refptr<storage::FileSystemContext> file_system_context =
|
| + content::BrowserContext::GetStoragePartitionForSite(browser_context, site)
|
| + ->GetFileSystemContext();
|
| + storage::ExternalFileSystemBackend* const backend =
|
| + file_system_context->external_backend();
|
| + DCHECK(backend);
|
|
|
| - if (!app_file_handler_util::HasFileSystemWritePermission(extension_.get())) {
|
| - error_ = kRequiresFileSystemWriteError;
|
| - return false;
|
| + base::FilePath virtual_path;
|
| + if (!backend->GetVirtualPath(volume->mount_path(), &virtual_path)) {
|
| + error_callback.Run(kSecurityError);
|
| + return;
|
| }
|
|
|
| - if (!app_file_handler_util::ValidateFileEntryAndGetPath(
|
| - filesystem_name, filesystem_path,
|
| - render_frame_host()->GetProcess()->GetID(), &path_, &error_))
|
| - return false;
|
| -
|
| - base::PostTaskWithTraitsAndReply(
|
| - FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND},
|
| - base::BindOnce(&FileSystemGetWritableEntryFunction::SetIsDirectoryAsync,
|
| - this),
|
| - base::BindOnce(
|
| - &FileSystemGetWritableEntryFunction::CheckPermissionAndSendResponse,
|
| - this));
|
| - return true;
|
| -}
|
| + storage::IsolatedContext* const isolated_context =
|
| + storage::IsolatedContext::GetInstance();
|
| + DCHECK(isolated_context);
|
|
|
| -void FileSystemGetWritableEntryFunction::CheckPermissionAndSendResponse() {
|
| - DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
| - if (is_directory_ && !extension_->permissions_data()->HasAPIPermission(
|
| - APIPermission::kFileSystemDirectory)) {
|
| - error_ = kRequiresFileSystemDirectoryError;
|
| - SendResponse(false);
|
| - }
|
| - std::vector<base::FilePath> paths;
|
| - paths.push_back(path_);
|
| - PrepareFilesForWritableApp(paths);
|
| -}
|
| + const storage::FileSystemURL original_url =
|
| + file_system_context->CreateCrackedFileSystemURL(
|
| + GURL(std::string(kExtensionScheme) + url::kStandardSchemeSeparator +
|
| + extension_id),
|
| + storage::kFileSystemTypeExternal, virtual_path);
|
|
|
| -void FileSystemGetWritableEntryFunction::SetIsDirectoryAsync() {
|
| - if (base::DirectoryExists(path_)) {
|
| - is_directory_ = true;
|
| + // Set a fixed register name, as the automatic one would leak the mount point
|
| + // directory.
|
| + std::string register_name = "fs";
|
| + const std::string file_system_id =
|
| + isolated_context->RegisterFileSystemForPath(
|
| + storage::kFileSystemTypeNativeForPlatformApp,
|
| + std::string() /* file_system_id */, original_url.path(),
|
| + ®ister_name);
|
| + if (file_system_id.empty()) {
|
| + error_callback.Run(kSecurityError);
|
| + return;
|
| }
|
| -}
|
|
|
| -ExtensionFunction::ResponseAction FileSystemIsWritableEntryFunction::Run() {
|
| - std::string filesystem_name;
|
| - std::string filesystem_path;
|
| - EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &filesystem_name));
|
| - EXTENSION_FUNCTION_VALIDATE(args_->GetString(1, &filesystem_path));
|
| -
|
| - std::string filesystem_id;
|
| - if (!storage::CrackIsolatedFileSystemName(filesystem_name, &filesystem_id))
|
| - return RespondNow(Error(app_file_handler_util::kInvalidParameters));
|
| + backend->GrantFileAccessToExtension(extension_id, virtual_path);
|
|
|
| + // Grant file permissions to the renderer hosting component.
|
| content::ChildProcessSecurityPolicy* policy =
|
| content::ChildProcessSecurityPolicy::GetInstance();
|
| - int renderer_id = render_frame_host()->GetProcess()->GetID();
|
| - bool is_writable = policy->CanReadWriteFileSystem(renderer_id, filesystem_id);
|
| -
|
| - return RespondNow(OneArgument(base::MakeUnique<base::Value>(is_writable)));
|
| -}
|
| -
|
| -void FileSystemChooseEntryFunction::ShowPicker(
|
| - const ui::SelectFileDialog::FileTypeInfo& file_type_info,
|
| - ui::SelectFileDialog::Type picker_type) {
|
| - if (g_skip_picker_for_test) {
|
| - std::vector<base::FilePath> test_paths;
|
| - if (g_use_suggested_path_for_test)
|
| - test_paths.push_back(initial_path_);
|
| - else if (g_path_to_be_picked_for_test)
|
| - test_paths.push_back(*g_path_to_be_picked_for_test);
|
| - else if (g_paths_to_be_picked_for_test)
|
| - test_paths = *g_paths_to_be_picked_for_test;
|
| + DCHECK(policy);
|
|
|
| - content::BrowserThread::PostTask(
|
| - content::BrowserThread::UI, FROM_HERE,
|
| - test_paths.size() > 0
|
| - ? base::BindOnce(&FileSystemChooseEntryFunction::FilesSelected,
|
| - this, test_paths)
|
| - : base::BindOnce(
|
| - &FileSystemChooseEntryFunction::FileSelectionCanceled, this));
|
| - return;
|
| - }
|
| + const auto process_id = requester->render_frame_host()->GetProcess()->GetID();
|
| + // Read-only permisisons.
|
| + policy->GrantReadFile(process_id, volume->mount_path());
|
| + policy->GrantReadFileSystem(process_id, file_system_id);
|
|
|
| - // The callbacks passed to the dialog will retain references to this
|
| - // UIThreadExtenisonFunction, preventing its destruction (and subsequent
|
| - // sending of the function response) until the user has selected a file or
|
| - // cancelled the picker.
|
| - if (!ShowSelectFileDialog(
|
| - this, picker_type, initial_path_, &file_type_info,
|
| - base::BindOnce(&FileSystemChooseEntryFunction::FilesSelected, this),
|
| - base::BindOnce(&FileSystemChooseEntryFunction::FileSelectionCanceled,
|
| - this))) {
|
| - error_ = kInvalidCallingPage;
|
| - SendResponse(false);
|
| + // Additional write permissions.
|
| + if (writable) {
|
| + policy->GrantCreateReadWriteFile(process_id, volume->mount_path());
|
| + policy->GrantCopyInto(process_id, volume->mount_path());
|
| + policy->GrantWriteFileSystem(process_id, file_system_id);
|
| + policy->GrantDeleteFromFileSystem(process_id, file_system_id);
|
| + policy->GrantCreateFileForFileSystem(process_id, file_system_id);
|
| }
|
| -}
|
| -
|
| -// static
|
| -void FileSystemChooseEntryFunction::SkipPickerAndAlwaysSelectPathForTest(
|
| - base::FilePath* path) {
|
| - g_skip_picker_for_test = true;
|
| - g_use_suggested_path_for_test = false;
|
| - g_path_to_be_picked_for_test = path;
|
| - g_paths_to_be_picked_for_test = NULL;
|
| -}
|
| -
|
| -void FileSystemChooseEntryFunction::SkipPickerAndAlwaysSelectPathsForTest(
|
| - std::vector<base::FilePath>* paths) {
|
| - g_skip_picker_for_test = true;
|
| - g_use_suggested_path_for_test = false;
|
| - g_paths_to_be_picked_for_test = paths;
|
| -}
|
| -
|
| -// static
|
| -void FileSystemChooseEntryFunction::SkipPickerAndSelectSuggestedPathForTest() {
|
| - g_skip_picker_for_test = true;
|
| - g_use_suggested_path_for_test = true;
|
| - g_path_to_be_picked_for_test = NULL;
|
| - g_paths_to_be_picked_for_test = NULL;
|
| -}
|
| -
|
| -// static
|
| -void FileSystemChooseEntryFunction::SkipPickerAndAlwaysCancelForTest() {
|
| - g_skip_picker_for_test = true;
|
| - g_use_suggested_path_for_test = false;
|
| - g_path_to_be_picked_for_test = NULL;
|
| - g_paths_to_be_picked_for_test = NULL;
|
| -}
|
| -
|
| -// static
|
| -void FileSystemChooseEntryFunction::StopSkippingPickerForTest() {
|
| - g_skip_picker_for_test = false;
|
| -}
|
| -
|
| -// static
|
| -void FileSystemChooseEntryFunction::SkipDirectoryConfirmationForTest() {
|
| - g_skip_directory_confirmation_for_test = true;
|
| - g_allow_directory_access_for_test = true;
|
| -}
|
|
|
| -// static
|
| -void FileSystemChooseEntryFunction::AutoCancelDirectoryConfirmationForTest() {
|
| - g_skip_directory_confirmation_for_test = true;
|
| - g_allow_directory_access_for_test = false;
|
| + success_callback.Run(file_system_id, register_name);
|
| }
|
|
|
| -// static
|
| -void FileSystemChooseEntryFunction::StopSkippingDirectoryConfirmationForTest() {
|
| - g_skip_directory_confirmation_for_test = false;
|
| -}
|
| -
|
| -// static
|
| -void FileSystemChooseEntryFunction::RegisterTempExternalFileSystemForTest(
|
| - const std::string& name,
|
| - const base::FilePath& path) {
|
| - // For testing on Chrome OS, where to deal with remote and local paths
|
| - // smoothly, all accessed paths need to be registered in the list of
|
| - // external mount points.
|
| - storage::ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
|
| - name, storage::kFileSystemTypeNativeLocal,
|
| - storage::FileSystemMountOption(), path);
|
| -}
|
| -
|
| -void FileSystemChooseEntryFunction::FilesSelected(
|
| - const std::vector<base::FilePath>& paths) {
|
| - DCHECK(!paths.empty());
|
| - base::FilePath last_choose_directory;
|
| - if (is_directory_) {
|
| - last_choose_directory = paths[0];
|
| - } else {
|
| - last_choose_directory = paths[0].DirName();
|
| - }
|
| - file_system_api::SetLastChooseEntryDirectory(
|
| - ExtensionPrefs::Get(GetProfile()), extension()->id(),
|
| - last_choose_directory);
|
| - if (is_directory_) {
|
| - // Get the WebContents for the app window to be the parent window of the
|
| - // confirmation dialog if necessary.
|
| - content::WebContents* const web_contents =
|
| - GetWebContentsForRenderFrameHost(GetProfile(), render_frame_host());
|
| - if (!web_contents) {
|
| - error_ = kInvalidCallingPage;
|
| - SendResponse(false);
|
| - return;
|
| - }
|
| -
|
| - DCHECK_EQ(paths.size(), 1u);
|
| - bool non_native_path = false;
|
| -#if defined(OS_CHROMEOS)
|
| - non_native_path =
|
| - file_manager::util::IsUnderNonNativeLocalPath(GetProfile(), paths[0]);
|
| -#endif
|
| -
|
| - base::PostTaskWithTraits(
|
| - FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND},
|
| - base::BindOnce(
|
| - &FileSystemChooseEntryFunction::ConfirmDirectoryAccessAsync, this,
|
| - non_native_path, paths, web_contents));
|
| - return;
|
| - }
|
| -
|
| - OnDirectoryAccessConfirmed(paths);
|
| -}
|
| +} // namespace
|
|
|
| -void FileSystemChooseEntryFunction::FileSelectionCanceled() {
|
| - error_ = kUserCancelled;
|
| - SendResponse(false);
|
| -}
|
| +namespace file_system_api {
|
|
|
| -void FileSystemChooseEntryFunction::ConfirmDirectoryAccessAsync(
|
| - bool non_native_path,
|
| - const std::vector<base::FilePath>& paths,
|
| - content::WebContents* web_contents) {
|
| - const base::FilePath check_path =
|
| - non_native_path ? paths[0] : base::MakeAbsoluteFilePath(paths[0]);
|
| - if (check_path.empty()) {
|
| - content::BrowserThread::PostTask(
|
| - content::BrowserThread::UI, FROM_HERE,
|
| - base::BindOnce(&FileSystemChooseEntryFunction::FileSelectionCanceled,
|
| - this));
|
| +void DispatchVolumeListChangeEvent(content::BrowserContext* browser_context) {
|
| + DCHECK(browser_context);
|
| + EventRouter* const event_router = EventRouter::Get(browser_context);
|
| + if (!event_router) // Possible on shutdown.
|
| return;
|
| - }
|
| -
|
| - for (size_t i = 0; i < arraysize(kGraylistedPaths); i++) {
|
| - base::FilePath graylisted_path;
|
| - if (PathService::Get(kGraylistedPaths[i], &graylisted_path) &&
|
| - (check_path == graylisted_path ||
|
| - check_path.IsParent(graylisted_path))) {
|
| - if (g_skip_directory_confirmation_for_test) {
|
| - if (g_allow_directory_access_for_test) {
|
| - break;
|
| - } else {
|
| - content::BrowserThread::PostTask(
|
| - content::BrowserThread::UI, FROM_HERE,
|
| - base::BindOnce(
|
| - &FileSystemChooseEntryFunction::FileSelectionCanceled, this));
|
| - }
|
| - return;
|
| - }
|
| -
|
| - content::BrowserThread::PostTask(
|
| - content::BrowserThread::UI, FROM_HERE,
|
| - base::BindOnce(
|
| - CreateDirectoryAccessConfirmationDialog,
|
| - app_file_handler_util::HasFileSystemWritePermission(
|
| - extension_.get()),
|
| - base::UTF8ToUTF16(extension_->name()), web_contents,
|
| - base::Bind(
|
| - &FileSystemChooseEntryFunction::OnDirectoryAccessConfirmed,
|
| - this, paths),
|
| - base::Bind(&FileSystemChooseEntryFunction::FileSelectionCanceled,
|
| - this)));
|
| - return;
|
| - }
|
| - }
|
| -
|
| - content::BrowserThread::PostTask(
|
| - content::BrowserThread::UI, FROM_HERE,
|
| - base::BindOnce(&FileSystemChooseEntryFunction::OnDirectoryAccessConfirmed,
|
| - this, paths));
|
| -}
|
|
|
| -void FileSystemChooseEntryFunction::OnDirectoryAccessConfirmed(
|
| - const std::vector<base::FilePath>& paths) {
|
| - if (app_file_handler_util::HasFileSystemWritePermission(extension_.get())) {
|
| - PrepareFilesForWritableApp(paths);
|
| + ExtensionRegistry* const registry = ExtensionRegistry::Get(browser_context);
|
| + if (!registry) // Possible on shutdown.
|
| return;
|
| - }
|
| -
|
| - // Don't need to check the file, it's for reading.
|
| - RegisterFileSystemsAndSendResponse(paths);
|
| -}
|
| -
|
| -void FileSystemChooseEntryFunction::BuildFileTypeInfo(
|
| - ui::SelectFileDialog::FileTypeInfo* file_type_info,
|
| - const base::FilePath::StringType& suggested_extension,
|
| - const AcceptOptions* accepts,
|
| - const bool* acceptsAllTypes) {
|
| - file_type_info->include_all_files = true;
|
| - if (acceptsAllTypes)
|
| - file_type_info->include_all_files = *acceptsAllTypes;
|
| -
|
| - bool need_suggestion =
|
| - !file_type_info->include_all_files && !suggested_extension.empty();
|
|
|
| - if (accepts) {
|
| - for (const file_system::AcceptOption& option : *accepts) {
|
| - base::string16 description;
|
| - std::vector<base::FilePath::StringType> extensions;
|
| -
|
| - if (!GetFileTypesFromAcceptOption(option, &extensions, &description))
|
| - continue; // No extensions were found.
|
| -
|
| - file_type_info->extensions.push_back(extensions);
|
| - file_type_info->extension_description_overrides.push_back(description);
|
| -
|
| - // If we still need to find suggested_extension, hunt for it inside the
|
| - // extensions returned from GetFileTypesFromAcceptOption.
|
| - if (need_suggestion &&
|
| - base::ContainsValue(extensions, suggested_extension)) {
|
| - need_suggestion = false;
|
| - }
|
| - }
|
| + ConsentProviderDelegate consent_provider_delegate(
|
| + Profile::FromBrowserContext(browser_context));
|
| + ConsentProvider consent_provider(&consent_provider_delegate);
|
| + file_system::VolumeListChangedEvent event_args;
|
| + FillVolumeList(browser_context, &event_args.volumes);
|
| + for (const auto& extension : registry->enabled_extensions()) {
|
| + if (!consent_provider.IsGrantable(*extension.get()))
|
| + continue;
|
| + event_router->DispatchEventToExtension(
|
| + extension->id(),
|
| + base::MakeUnique<Event>(
|
| + events::FILE_SYSTEM_ON_VOLUME_LIST_CHANGED,
|
| + file_system::OnVolumeListChanged::kEventName,
|
| + file_system::OnVolumeListChanged::Create(event_args)));
|
| }
|
| -
|
| - // If there's nothing in our accepted extension list or we couldn't find the
|
| - // suggested extension required, then default to accepting all types.
|
| - if (file_type_info->extensions.empty() || need_suggestion)
|
| - file_type_info->include_all_files = true;
|
| }
|
|
|
| -void FileSystemChooseEntryFunction::BuildSuggestion(
|
| - const std::string* opt_name,
|
| - base::FilePath* suggested_name,
|
| - base::FilePath::StringType* suggested_extension) {
|
| - if (opt_name) {
|
| - *suggested_name = base::FilePath::FromUTF8Unsafe(*opt_name);
|
| +} // namespace file_system_api
|
| +#endif // defined(OS_CHROMEOS)
|
|
|
| - // Don't allow any path components; shorten to the base name. This should
|
| - // result in a relative path, but in some cases may not. Clear the
|
| - // suggestion for safety if this is the case.
|
| - *suggested_name = suggested_name->BaseName();
|
| - if (suggested_name->IsAbsolute())
|
| - *suggested_name = base::FilePath();
|
| +ChromeFileSystemDelegate::ChromeFileSystemDelegate() {}
|
|
|
| - *suggested_extension = suggested_name->Extension();
|
| - if (!suggested_extension->empty())
|
| - suggested_extension->erase(suggested_extension->begin()); // drop the .
|
| - }
|
| -}
|
| +ChromeFileSystemDelegate::~ChromeFileSystemDelegate() {}
|
|
|
| -void FileSystemChooseEntryFunction::SetInitialPathAndShowPicker(
|
| - const base::FilePath& previous_path,
|
| - const base::FilePath& suggested_name,
|
| - const ui::SelectFileDialog::FileTypeInfo& file_type_info,
|
| - ui::SelectFileDialog::Type picker_type,
|
| - bool is_previous_path_directory) {
|
| - DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
| - if (is_previous_path_directory) {
|
| - initial_path_ = previous_path.Append(suggested_name);
|
| - } else {
|
| - base::FilePath documents_dir;
|
| - if (PathService::Get(chrome::DIR_USER_DOCUMENTS, &documents_dir)) {
|
| - initial_path_ = documents_dir.Append(suggested_name);
|
| - } else {
|
| - initial_path_ = suggested_name;
|
| - }
|
| - }
|
| - ShowPicker(file_type_info, picker_type);
|
| +base::FilePath ChromeFileSystemDelegate::GetDefaultDirectory() {
|
| + base::FilePath documents_dir;
|
| + PathService::Get(chrome::DIR_USER_DOCUMENTS, &documents_dir);
|
| + return documents_dir;
|
| }
|
|
|
| -bool FileSystemChooseEntryFunction::RunAsync() {
|
| - std::unique_ptr<ChooseEntry::Params> params(
|
| - ChooseEntry::Params::Create(*args_));
|
| - EXTENSION_FUNCTION_VALIDATE(params.get());
|
| -
|
| - base::FilePath suggested_name;
|
| - ui::SelectFileDialog::FileTypeInfo file_type_info;
|
| - ui::SelectFileDialog::Type picker_type =
|
| - ui::SelectFileDialog::SELECT_OPEN_FILE;
|
| -
|
| - file_system::ChooseEntryOptions* options = params->options.get();
|
| - if (options) {
|
| - multiple_ = options->accepts_multiple && *options->accepts_multiple;
|
| - if (multiple_)
|
| - picker_type = ui::SelectFileDialog::SELECT_OPEN_MULTI_FILE;
|
| -
|
| - if (options->type == file_system::CHOOSE_ENTRY_TYPE_OPENWRITABLEFILE &&
|
| - !app_file_handler_util::HasFileSystemWritePermission(
|
| - extension_.get())) {
|
| - error_ = kRequiresFileSystemWriteError;
|
| - return false;
|
| - } else if (options->type == file_system::CHOOSE_ENTRY_TYPE_SAVEFILE) {
|
| - if (!app_file_handler_util::HasFileSystemWritePermission(
|
| - extension_.get())) {
|
| - error_ = kRequiresFileSystemWriteError;
|
| - return false;
|
| - }
|
| - if (multiple_) {
|
| - error_ = kMultipleUnsupportedError;
|
| - return false;
|
| - }
|
| - picker_type = ui::SelectFileDialog::SELECT_SAVEAS_FILE;
|
| - } else if (options->type == file_system::CHOOSE_ENTRY_TYPE_OPENDIRECTORY) {
|
| - is_directory_ = true;
|
| - if (!extension_->permissions_data()->HasAPIPermission(
|
| - APIPermission::kFileSystemDirectory)) {
|
| - error_ = kRequiresFileSystemDirectoryError;
|
| - return false;
|
| - }
|
| - if (multiple_) {
|
| - error_ = kMultipleUnsupportedError;
|
| - return false;
|
| - }
|
| - picker_type = ui::SelectFileDialog::SELECT_FOLDER;
|
| - }
|
| -
|
| - base::FilePath::StringType suggested_extension;
|
| - BuildSuggestion(options->suggested_name.get(), &suggested_name,
|
| - &suggested_extension);
|
| -
|
| - BuildFileTypeInfo(&file_type_info, suggested_extension,
|
| - options->accepts.get(), options->accepts_all_types.get());
|
| - }
|
| -
|
| - file_type_info.allowed_paths = ui::SelectFileDialog::FileTypeInfo::ANY_PATH;
|
| -
|
| - base::FilePath previous_path = file_system_api::GetLastChooseEntryDirectory(
|
| - ExtensionPrefs::Get(GetProfile()), extension()->id());
|
| -
|
| - if (previous_path.empty()) {
|
| - SetInitialPathAndShowPicker(previous_path, suggested_name, file_type_info,
|
| - picker_type, false);
|
| - return true;
|
| - }
|
| -
|
| - base::Callback<void(bool)> set_initial_path_callback = base::Bind(
|
| - &FileSystemChooseEntryFunction::SetInitialPathAndShowPicker, this,
|
| - previous_path, suggested_name, file_type_info, picker_type);
|
| -
|
| -// Check whether the |previous_path| is a non-native directory.
|
| -#if defined(OS_CHROMEOS)
|
| - if (file_manager::util::IsUnderNonNativeLocalPath(GetProfile(),
|
| - previous_path)) {
|
| - file_manager::util::IsNonNativeLocalPathDirectory(
|
| - GetProfile(), previous_path, set_initial_path_callback);
|
| - return true;
|
| - }
|
| -#endif
|
| - base::PostTaskWithTraitsAndReplyWithResult(
|
| - FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND},
|
| - base::Bind(&base::DirectoryExists, previous_path),
|
| - set_initial_path_callback);
|
| -
|
| - return true;
|
| -}
|
| +bool ChromeFileSystemDelegate::ShowSelectFileDialog(
|
| + scoped_refptr<UIThreadExtensionFunction> extension_function,
|
| + ui::SelectFileDialog::Type type,
|
| + const base::FilePath& default_path,
|
| + const ui::SelectFileDialog::FileTypeInfo* file_types,
|
| + FileSystemDelegate::FilesSelectedCallback files_selected_callback,
|
| + base::OnceClosure file_selection_canceled_callback) {
|
| + const Extension* extension = extension_function->extension();
|
| + content::WebContents* web_contents = nullptr;
|
|
|
| -bool FileSystemRetainEntryFunction::RunAsync() {
|
| - std::string entry_id;
|
| - EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &entry_id));
|
| - SavedFilesService* saved_files_service = SavedFilesService::Get(GetProfile());
|
| - // Add the file to the retain list if it is not already on there.
|
| - if (!saved_files_service->IsRegistered(extension_->id(), entry_id)) {
|
| - std::string filesystem_name;
|
| - std::string filesystem_path;
|
| - base::FilePath path;
|
| - EXTENSION_FUNCTION_VALIDATE(args_->GetString(1, &filesystem_name));
|
| - EXTENSION_FUNCTION_VALIDATE(args_->GetString(2, &filesystem_path));
|
| - if (!app_file_handler_util::ValidateFileEntryAndGetPath(
|
| - filesystem_name, filesystem_path,
|
| - render_frame_host()->GetProcess()->GetID(), &path, &error_)) {
|
| - return false;
|
| + // TODO(asargent/benwells) - As a short term remediation for
|
| + // crbug.com/179010 we're adding the ability for a whitelisted extension to
|
| + // use this API since chrome.fileBrowserHandler.selectFile is ChromeOS-only.
|
| + // Eventually we'd like a better solution and likely this code will go back
|
| + // to being platform-app only.
|
| + if (extension->is_platform_app()) {
|
| + content::WebContents* candidate = content::WebContents::FromRenderFrameHost(
|
| + extension_function->render_frame_host());
|
| + // Make sure there is an app window associated with the web contents.
|
| + if (AppWindowRegistry::Get(extension_function->browser_context())
|
| + ->GetAppWindowForWebContents(candidate)) {
|
| + web_contents = candidate;
|
| }
|
| -
|
| - std::string filesystem_id;
|
| - if (!storage::CrackIsolatedFileSystemName(filesystem_name, &filesystem_id))
|
| - return false;
|
| -
|
| - const GURL site = util::GetSiteForExtensionId(extension_id(), GetProfile());
|
| - storage::FileSystemContext* const context =
|
| - content::BrowserContext::GetStoragePartitionForSite(GetProfile(), site)
|
| - ->GetFileSystemContext();
|
| - const storage::FileSystemURL url = context->CreateCrackedFileSystemURL(
|
| - site, storage::kFileSystemTypeIsolated,
|
| - IsolatedContext::GetInstance()
|
| - ->CreateVirtualRootPath(filesystem_id)
|
| - .Append(base::FilePath::FromUTF8Unsafe(filesystem_path)));
|
| -
|
| - content::BrowserThread::PostTask(
|
| - content::BrowserThread::IO, FROM_HERE,
|
| - base::BindOnce(
|
| - base::IgnoreResult(
|
| - &storage::FileSystemOperationRunner::GetMetadata),
|
| - context->operation_runner()->AsWeakPtr(), url,
|
| - storage::FileSystemOperation::GET_METADATA_FIELD_IS_DIRECTORY,
|
| - base::Bind(
|
| - &PassFileInfoToUIThread,
|
| - base::Bind(&FileSystemRetainEntryFunction::RetainFileEntry,
|
| - this, entry_id, path))));
|
| - return true;
|
| - }
|
| -
|
| - saved_files_service->EnqueueFileEntry(extension_->id(), entry_id);
|
| - SendResponse(true);
|
| - return true;
|
| -}
|
| -
|
| -void FileSystemRetainEntryFunction::RetainFileEntry(
|
| - const std::string& entry_id,
|
| - const base::FilePath& path,
|
| - std::unique_ptr<base::File::Info> file_info) {
|
| - if (!file_info) {
|
| - SendResponse(false);
|
| - return;
|
| + } else {
|
| + // TODO(michaelpg): As a workaround for crbug.com/736930, allow this to work
|
| + // from a background page by using the the extended GetAssociatedWebContents
|
| + // function provided by ChromeExtensionFunctionDetails. This is safe because
|
| + // only whitelisted extensions can access the chrome.fileSystem API;
|
| + // platform apps cannot open the file picker from a background page.
|
| + web_contents = ChromeExtensionFunctionDetails(extension_function.get())
|
| + .GetAssociatedWebContents();
|
| }
|
|
|
| - SavedFilesService* saved_files_service = SavedFilesService::Get(GetProfile());
|
| - saved_files_service->RegisterFileEntry(extension_->id(), entry_id, path,
|
| - file_info->is_directory);
|
| - saved_files_service->EnqueueFileEntry(extension_->id(), entry_id);
|
| - SendResponse(true);
|
| -}
|
| -
|
| -ExtensionFunction::ResponseAction FileSystemIsRestorableFunction::Run() {
|
| - std::string entry_id;
|
| - EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &entry_id));
|
| - return RespondNow(OneArgument(base::MakeUnique<base::Value>(
|
| - SavedFilesService::Get(Profile::FromBrowserContext(browser_context()))
|
| - ->IsRegistered(extension_->id(), entry_id))));
|
| -}
|
| -
|
| -bool FileSystemRestoreEntryFunction::RunAsync() {
|
| - std::string entry_id;
|
| - bool needs_new_entry;
|
| - EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &entry_id));
|
| - EXTENSION_FUNCTION_VALIDATE(args_->GetBoolean(1, &needs_new_entry));
|
| - const SavedFileEntry* file_entry =
|
| - SavedFilesService::Get(GetProfile())
|
| - ->GetFileEntry(extension_->id(), entry_id);
|
| - if (!file_entry) {
|
| - error_ = kUnknownIdError;
|
| + if (!web_contents)
|
| return false;
|
| - }
|
|
|
| - SavedFilesService::Get(GetProfile())
|
| - ->EnqueueFileEntry(extension_->id(), entry_id);
|
| -
|
| - // Only create a new file entry if the renderer requests one.
|
| - // |needs_new_entry| will be false if the renderer already has an Entry for
|
| - // |entry_id|.
|
| - if (needs_new_entry) {
|
| - is_directory_ = file_entry->is_directory;
|
| - std::unique_ptr<base::DictionaryValue> result = CreateResult();
|
| - AddEntryToResult(file_entry->path, file_entry->id, result.get());
|
| - SetResult(std::move(result));
|
| - }
|
| - SendResponse(true);
|
| + // The file picker will hold a reference to the UIThreadExtensionFunction
|
| + // instance, preventing its destruction (and subsequent sending of the
|
| + // function response) until the user has selected a file or cancelled the
|
| + // picker. At that point, the picker will delete itself, which will also free
|
| + // the function instance.
|
| + new FileEntryPicker(web_contents, default_path, *file_types, type,
|
| + std::move(files_selected_callback),
|
| + std::move(file_selection_canceled_callback));
|
| return true;
|
| }
|
|
|
| -ExtensionFunction::ResponseAction FileSystemObserveDirectoryFunction::Run() {
|
| - NOTIMPLEMENTED();
|
| - return RespondNow(Error(kUnknownIdError));
|
| +void ChromeFileSystemDelegate::ConfirmSensitiveDirectoryAccess(
|
| + bool has_write_permission,
|
| + const base::string16& app_name,
|
| + content::WebContents* web_contents,
|
| + const base::Closure& on_accept,
|
| + const base::Closure& on_cancel) {
|
| + CreateDirectoryAccessConfirmationDialog(has_write_permission, app_name,
|
| + web_contents, on_accept, on_cancel);
|
| }
|
|
|
| -ExtensionFunction::ResponseAction FileSystemUnobserveEntryFunction::Run() {
|
| - NOTIMPLEMENTED();
|
| - return RespondNow(Error(kUnknownIdError));
|
| +int ChromeFileSystemDelegate::GetDescriptionIdForAcceptType(
|
| + const std::string& accept_type) {
|
| + if (accept_type == "image/*")
|
| + return IDS_IMAGE_FILES;
|
| + if (accept_type == "audio/*")
|
| + return IDS_AUDIO_FILES;
|
| + if (accept_type == "video/*")
|
| + return IDS_VIDEO_FILES;
|
| + return 0;
|
| }
|
|
|
| -ExtensionFunction::ResponseAction FileSystemGetObservedEntriesFunction::Run() {
|
| - NOTIMPLEMENTED();
|
| - return RespondNow(Error(kUnknownIdError));
|
| -}
|
| -
|
| -#if !defined(OS_CHROMEOS)
|
| -ExtensionFunction::ResponseAction FileSystemRequestFileSystemFunction::Run() {
|
| - using api::file_system::RequestFileSystem::Params;
|
| - const std::unique_ptr<Params> params(Params::Create(*args_));
|
| - EXTENSION_FUNCTION_VALIDATE(params);
|
| -
|
| - NOTIMPLEMENTED();
|
| - return RespondNow(Error(kNotSupportedOnCurrentPlatformError));
|
| -}
|
| -
|
| -ExtensionFunction::ResponseAction FileSystemGetVolumeListFunction::Run() {
|
| - NOTIMPLEMENTED();
|
| - return RespondNow(Error(kNotSupportedOnCurrentPlatformError));
|
| -}
|
| -#else
|
| -
|
| -FileSystemRequestFileSystemFunction::FileSystemRequestFileSystemFunction()
|
| - : chrome_details_(this) {}
|
| -
|
| -FileSystemRequestFileSystemFunction::~FileSystemRequestFileSystemFunction() {}
|
| -
|
| -ExtensionFunction::ResponseAction FileSystemRequestFileSystemFunction::Run() {
|
| - using api::file_system::RequestFileSystem::Params;
|
| - const std::unique_ptr<Params> params(Params::Create(*args_));
|
| - EXTENSION_FUNCTION_VALIDATE(params);
|
| -
|
| +#if defined(OS_CHROMEOS)
|
| +bool ChromeFileSystemDelegate::IsGrantable(
|
| + content::BrowserContext* browser_context,
|
| + content::RenderFrameHost* render_frame_host,
|
| + const Extension& extension) {
|
| // Only kiosk apps in kiosk sessions can use this API.
|
| // Additionally it is enabled for whitelisted component extensions and apps.
|
| - file_system_api::ConsentProviderDelegate consent_provider_delegate(
|
| - chrome_details_.GetProfile(), render_frame_host());
|
| - file_system_api::ConsentProvider consent_provider(&consent_provider_delegate);
|
| -
|
| - if (!consent_provider.IsGrantable(*extension()))
|
| - return RespondNow(Error(kNotSupportedOnNonKioskSessionError));
|
| -
|
| - using file_manager::VolumeManager;
|
| - using file_manager::Volume;
|
| - VolumeManager* const volume_manager =
|
| - VolumeManager::Get(chrome_details_.GetProfile());
|
| - DCHECK(volume_manager);
|
| -
|
| - const bool writable =
|
| - params->options.writable.get() && *params->options.writable.get();
|
| - if (writable &&
|
| - !app_file_handler_util::HasFileSystemWritePermission(extension_.get())) {
|
| - return RespondNow(Error(kRequiresFileSystemWriteError));
|
| - }
|
| -
|
| - base::WeakPtr<file_manager::Volume> volume =
|
| - volume_manager->FindVolumeById(params->options.volume_id);
|
| - if (!volume.get())
|
| - return RespondNow(Error(kVolumeNotFoundError));
|
| -
|
| - const GURL site =
|
| - util::GetSiteForExtensionId(extension_id(), chrome_details_.GetProfile());
|
| - scoped_refptr<storage::FileSystemContext> file_system_context =
|
| - content::BrowserContext::GetStoragePartitionForSite(
|
| - chrome_details_.GetProfile(), site)
|
| - ->GetFileSystemContext();
|
| - storage::ExternalFileSystemBackend* const backend =
|
| - file_system_context->external_backend();
|
| - DCHECK(backend);
|
| -
|
| - base::FilePath virtual_path;
|
| - if (!backend->GetVirtualPath(volume->mount_path(), &virtual_path))
|
| - return RespondNow(Error(kSecurityError));
|
| -
|
| - if (writable && (volume->is_read_only()))
|
| - return RespondNow(Error(kSecurityError));
|
| -
|
| - consent_provider.RequestConsent(
|
| - *extension(), volume, writable,
|
| - base::Bind(&FileSystemRequestFileSystemFunction::OnConsentReceived, this,
|
| - volume, writable));
|
| - return RespondLater();
|
| + ConsentProviderDelegate consent_provider_delegate(
|
| + Profile::FromBrowserContext(browser_context));
|
| + return ConsentProvider(&consent_provider_delegate).IsGrantable(extension);
|
| }
|
|
|
| -void FileSystemRequestFileSystemFunction::OnConsentReceived(
|
| - const base::WeakPtr<file_manager::Volume>& volume,
|
| +void ChromeFileSystemDelegate::RequestFileSystem(
|
| + content::BrowserContext* browser_context,
|
| + scoped_refptr<UIThreadExtensionFunction> requester,
|
| + const Extension& extension,
|
| + std::string volume_id,
|
| bool writable,
|
| - ConsentProvider::Consent result) {
|
| - using file_manager::VolumeManager;
|
| - using file_manager::Volume;
|
| + const FileSystemCallback& success_callback,
|
| + const ErrorCallback& error_callback) {
|
| + ConsentProviderDelegate consent_provider_delegate(
|
| + Profile::FromBrowserContext(browser_context));
|
| + ConsentProvider consent_provider(&consent_provider_delegate);
|
|
|
| - // Render frame host can be gone before this callback method is executed.
|
| - if (!render_frame_host()) {
|
| - Respond(Error(""));
|
| + if (!consent_provider.IsGrantable(extension)) {
|
| + error_callback.Run(kNotSupportedOnNonKioskSessionError);
|
| return;
|
| }
|
|
|
| - switch (result) {
|
| - case ConsentProvider::CONSENT_REJECTED:
|
| - Respond(Error(kSecurityError));
|
| - return;
|
| -
|
| - case ConsentProvider::CONSENT_IMPOSSIBLE:
|
| - Respond(Error(kConsentImpossible));
|
| - return;
|
| + using file_manager::VolumeManager;
|
| + using file_manager::Volume;
|
| + VolumeManager* const volume_manager = VolumeManager::Get(browser_context);
|
| + DCHECK(volume_manager);
|
|
|
| - case ConsentProvider::CONSENT_GRANTED:
|
| - break;
|
| + if (writable &&
|
| + !app_file_handler_util::HasFileSystemWritePermission(&extension)) {
|
| + error_callback.Run(kRequiresFileSystemWriteError);
|
| + return;
|
| }
|
|
|
| + base::WeakPtr<file_manager::Volume> volume =
|
| + volume_manager->FindVolumeById(volume_id);
|
| if (!volume.get()) {
|
| - Respond(Error(kVolumeNotFoundError));
|
| + error_callback.Run(kVolumeNotFoundError);
|
| return;
|
| }
|
|
|
| const GURL site =
|
| - util::GetSiteForExtensionId(extension_id(), chrome_details_.GetProfile());
|
| + util::GetSiteForExtensionId(extension.id(), browser_context);
|
| scoped_refptr<storage::FileSystemContext> file_system_context =
|
| - content::BrowserContext::GetStoragePartitionForSite(
|
| - chrome_details_.GetProfile(), site)
|
| + content::BrowserContext::GetStoragePartitionForSite(browser_context, site)
|
| ->GetFileSystemContext();
|
| storage::ExternalFileSystemBackend* const backend =
|
| file_system_context->external_backend();
|
| @@ -1096,87 +361,38 @@ void FileSystemRequestFileSystemFunction::OnConsentReceived(
|
|
|
| base::FilePath virtual_path;
|
| if (!backend->GetVirtualPath(volume->mount_path(), &virtual_path)) {
|
| - Respond(Error(kSecurityError));
|
| + error_callback.Run(kSecurityError);
|
| return;
|
| }
|
|
|
| - storage::IsolatedContext* const isolated_context =
|
| - storage::IsolatedContext::GetInstance();
|
| - DCHECK(isolated_context);
|
| -
|
| - const storage::FileSystemURL original_url =
|
| - file_system_context->CreateCrackedFileSystemURL(
|
| - GURL(std::string(kExtensionScheme) + url::kStandardSchemeSeparator +
|
| - extension_id()),
|
| - storage::kFileSystemTypeExternal, virtual_path);
|
| -
|
| - // Set a fixed register name, as the automatic one would leak the mount point
|
| - // directory.
|
| - std::string register_name = "fs";
|
| - const std::string file_system_id =
|
| - isolated_context->RegisterFileSystemForPath(
|
| - storage::kFileSystemTypeNativeForPlatformApp,
|
| - std::string() /* file_system_id */, original_url.path(),
|
| - ®ister_name);
|
| - if (file_system_id.empty()) {
|
| - Respond(Error(kSecurityError));
|
| + if (writable && (volume->is_read_only())) {
|
| + error_callback.Run(kSecurityError);
|
| return;
|
| }
|
|
|
| - backend->GrantFileAccessToExtension(extension_->id(), virtual_path);
|
| -
|
| - // Grant file permissions to the renderer hosting component.
|
| - content::ChildProcessSecurityPolicy* policy =
|
| - content::ChildProcessSecurityPolicy::GetInstance();
|
| - DCHECK(policy);
|
| -
|
| - // Read-only permisisons.
|
| - policy->GrantReadFile(render_frame_host()->GetProcess()->GetID(),
|
| - volume->mount_path());
|
| - policy->GrantReadFileSystem(render_frame_host()->GetProcess()->GetID(),
|
| - file_system_id);
|
| -
|
| - // Additional write permissions.
|
| - if (writable) {
|
| - policy->GrantCreateReadWriteFile(render_frame_host()->GetProcess()->GetID(),
|
| - volume->mount_path());
|
| - policy->GrantCopyInto(render_frame_host()->GetProcess()->GetID(),
|
| - volume->mount_path());
|
| - policy->GrantWriteFileSystem(render_frame_host()->GetProcess()->GetID(),
|
| - file_system_id);
|
| - policy->GrantDeleteFromFileSystem(
|
| - render_frame_host()->GetProcess()->GetID(), file_system_id);
|
| - policy->GrantCreateFileForFileSystem(
|
| - render_frame_host()->GetProcess()->GetID(), file_system_id);
|
| - }
|
| -
|
| - std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
|
| - dict->SetString("file_system_id", file_system_id);
|
| - dict->SetString("file_system_path", register_name);
|
| + const ConsentProvider::ConsentCallback& callback = base::Bind(
|
| + &OnConsentReceived, browser_context, requester, success_callback,
|
| + error_callback, extension.id(), volume, writable);
|
|
|
| - Respond(OneArgument(std::move(dict)));
|
| + consent_provider.RequestConsent(extension, requester->render_frame_host(),
|
| + volume, writable, callback);
|
| }
|
|
|
| -FileSystemGetVolumeListFunction::FileSystemGetVolumeListFunction()
|
| - : chrome_details_(this) {}
|
| -
|
| -FileSystemGetVolumeListFunction::~FileSystemGetVolumeListFunction() {}
|
| +void ChromeFileSystemDelegate::GetVolumeList(
|
| + content::BrowserContext* browser_context,
|
| + const VolumeListCallback& success_callback,
|
| + const ErrorCallback& error_callback) {
|
| + std::vector<file_system::Volume> result_volume_list;
|
| + FillVolumeList(browser_context, &result_volume_list);
|
|
|
| -ExtensionFunction::ResponseAction FileSystemGetVolumeListFunction::Run() {
|
| - // Only kiosk apps in kiosk sessions can use this API.
|
| - // Additionally it is enabled for whitelisted component extensions and apps.
|
| - file_system_api::ConsentProviderDelegate consent_provider_delegate(
|
| - chrome_details_.GetProfile(), render_frame_host());
|
| - file_system_api::ConsentProvider consent_provider(&consent_provider_delegate);
|
| + success_callback.Run(result_volume_list);
|
| +}
|
|
|
| - if (!consent_provider.IsGrantable(*extension()))
|
| - return RespondNow(Error(kNotSupportedOnNonKioskSessionError));
|
| - std::vector<api::file_system::Volume> result_volume_list;
|
| - FillVolumeList(chrome_details_.GetProfile(), &result_volume_list);
|
| +#endif // defined(OS_CHROMEOS)
|
|
|
| - return RespondNow(ArgumentList(
|
| - api::file_system::GetVolumeList::Results::Create(result_volume_list)));
|
| +SavedFilesServiceInterface* ChromeFileSystemDelegate::GetSavedFilesService(
|
| + content::BrowserContext* browser_context) {
|
| + return apps::SavedFilesService::Get(browser_context);
|
| }
|
| -#endif
|
|
|
| } // namespace extensions
|
|
|