| 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 | 
|  |