Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright 2017 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/extensions/api/file_system/file_system_api.h" | 5 #include "chrome/browser/extensions/api/file_system/consent_provider.h" |
| 6 | |
| 7 #include <stddef.h> | |
| 8 | 6 |
| 9 #include <memory> | 7 #include <memory> |
| 10 #include <set> | 8 #include <string> |
| 11 #include <utility> | |
| 12 #include <vector> | |
| 13 | 9 |
| 14 #include "apps/saved_files_service.h" | |
| 15 #include "base/bind.h" | 10 #include "base/bind.h" |
| 16 #include "base/files/file_path.h" | |
| 17 #include "base/files/file_util.h" | |
| 18 #include "base/logging.h" | 11 #include "base/logging.h" |
| 19 #include "base/macros.h" | |
| 20 #include "base/memory/ptr_util.h" | 12 #include "base/memory/ptr_util.h" |
| 21 #include "base/path_service.h" | |
| 22 #include "base/strings/string_util.h" | |
| 23 #include "base/strings/stringprintf.h" | |
| 24 #include "base/strings/sys_string_conversions.h" | |
| 25 #include "base/strings/utf_string_conversions.h" | |
| 26 #include "base/task_scheduler/post_task.h" | |
| 27 #include "base/value_conversions.h" | |
| 28 #include "base/values.h" | |
| 29 #include "build/build_config.h" | |
| 30 #include "chrome/browser/platform_util.h" | |
| 31 #include "chrome/browser/profiles/profile.h" | |
| 32 #include "chrome/browser/ui/apps/directory_access_confirmation_dialog.h" | |
| 33 #include "chrome/browser/ui/chrome_select_file_policy.h" | |
| 34 #include "chrome/common/chrome_paths.h" | |
| 35 #include "chrome/common/extensions/api/file_system.h" | |
| 36 #include "chrome/grit/generated_resources.h" | |
| 37 #include "content/public/browser/browser_thread.h" | |
| 38 #include "content/public/browser/child_process_security_policy.h" | |
| 39 #include "content/public/browser/render_frame_host.h" | |
| 40 #include "content/public/browser/render_process_host.h" | |
| 41 #include "content/public/browser/storage_partition.h" | |
| 42 #include "content/public/browser/web_contents.h" | |
| 43 #include "extensions/browser/api/file_handlers/app_file_handler_util.h" | |
| 44 #include "extensions/browser/app_window/app_window.h" | |
| 45 #include "extensions/browser/app_window/app_window_registry.h" | |
| 46 #include "extensions/browser/extension_prefs.h" | |
| 47 #include "extensions/browser/extension_system.h" | |
| 48 #include "extensions/browser/extension_util.h" | |
| 49 #include "extensions/browser/granted_file_entry.h" | |
| 50 #include "extensions/browser/path_util.h" | |
| 51 #include "extensions/common/permissions/api_permission.h" | |
| 52 #include "extensions/common/permissions/permissions_data.h" | |
| 53 #include "net/base/mime_util.h" | |
| 54 #include "storage/browser/fileapi/external_mount_points.h" | |
| 55 #include "storage/browser/fileapi/file_system_operation_runner.h" | |
| 56 #include "storage/browser/fileapi/isolated_context.h" | |
| 57 #include "storage/common/fileapi/file_system_types.h" | |
| 58 #include "storage/common/fileapi/file_system_util.h" | |
| 59 #include "ui/base/l10n/l10n_util.h" | |
| 60 #include "ui/base/ui_base_types.h" | |
| 61 #include "ui/shell_dialogs/select_file_dialog.h" | |
| 62 #include "ui/shell_dialogs/selected_file_info.h" | |
| 63 | |
| 64 #if defined(OS_MACOSX) | |
| 65 #include <CoreFoundation/CoreFoundation.h> | |
| 66 #include "base/mac/foundation_util.h" | |
| 67 #endif | |
| 68 | |
| 69 #if defined(OS_CHROMEOS) | |
| 70 #include "base/strings/string16.h" | |
| 71 #include "base/threading/thread_task_runner_handle.h" | 13 #include "base/threading/thread_task_runner_handle.h" |
| 72 #include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h" | 14 #include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h" |
| 73 #include "chrome/browser/chromeos/file_manager/app_id.h" | 15 #include "chrome/browser/chromeos/file_manager/app_id.h" |
| 74 #include "chrome/browser/chromeos/file_manager/filesystem_api_util.h" | |
| 75 #include "chrome/browser/chromeos/file_manager/volume_manager.h" | 16 #include "chrome/browser/chromeos/file_manager/volume_manager.h" |
| 76 #include "chrome/browser/extensions/api/file_system/request_file_system_notifica tion.h" | 17 #include "chrome/browser/extensions/api/file_system/request_file_system_notifica tion.h" |
| 77 #include "chrome/browser/ui/simple_message_box.h" | 18 #include "chrome/browser/profiles/profile.h" |
| 78 #include "chrome/browser/ui/views/extensions/request_file_system_dialog_view.h" | 19 #include "chrome/browser/ui/views/extensions/request_file_system_dialog_view.h" |
| 20 #include "chrome/common/extensions/api/file_system.h" | |
| 79 #include "components/user_manager/user_manager.h" | 21 #include "components/user_manager/user_manager.h" |
| 80 #include "extensions/browser/event_router.h" | 22 #include "content/public/browser/render_frame_host.h" |
| 81 #include "extensions/browser/extension_registry.h" | 23 #include "content/public/browser/web_contents.h" |
| 82 #include "extensions/common/constants.h" | 24 #include "extensions/browser/app_window/app_window.h" |
| 25 #include "extensions/browser/app_window/app_window_registry.h" | |
| 83 #include "extensions/common/manifest_handlers/kiosk_mode_info.h" | 26 #include "extensions/common/manifest_handlers/kiosk_mode_info.h" |
| 84 #include "url/url_constants.h" | |
| 85 #endif | |
| 86 | 27 |
| 87 using apps::SavedFileEntry; | 28 namespace extensions { |
| 88 using apps::SavedFilesService; | |
| 89 using storage::IsolatedContext; | |
| 90 | 29 |
| 91 const char kInvalidCallingPage[] = "Invalid calling page. This function can't " | 30 namespace { |
| 92 "be called from a background page."; | |
| 93 const char kUserCancelled[] = "User cancelled"; | |
| 94 const char kWritableFileErrorFormat[] = "Error opening %s"; | |
| 95 const char kRequiresFileSystemWriteError[] = | |
| 96 "Operation requires fileSystem.write permission"; | |
| 97 const char kRequiresFileSystemDirectoryError[] = | |
| 98 "Operation requires fileSystem.directory permission"; | |
| 99 const char kMultipleUnsupportedError[] = | |
| 100 "acceptsMultiple: true is only supported for 'openFile'"; | |
| 101 const char kUnknownIdError[] = "Unknown id"; | |
| 102 | |
| 103 #if !defined(OS_CHROMEOS) | |
| 104 const char kNotSupportedOnCurrentPlatformError[] = | |
| 105 "Operation not supported on the current platform."; | |
| 106 #else | |
| 107 const char kNotSupportedOnNonKioskSessionError[] = | |
| 108 "Operation only supported for kiosk apps running in a kiosk session."; | |
| 109 const char kVolumeNotFoundError[] = "Volume not found."; | |
| 110 const char kSecurityError[] = "Security error."; | |
| 111 const char kConsentImpossible[] = | |
| 112 "Impossible to ask for user consent as there is no app window visible."; | |
| 113 | 31 |
| 114 // List of whitelisted component apps and extensions by their ids for | 32 // List of whitelisted component apps and extensions by their ids for |
| 115 // chrome.fileSystem.requestFileSystem. | 33 // chrome.fileSystem.requestFileSystem. |
| 116 const char* const kRequestFileSystemComponentWhitelist[] = { | 34 const char* const kRequestFileSystemComponentWhitelist[] = { |
| 117 file_manager::kFileManagerAppId, | 35 file_manager::kFileManagerAppId, |
| 118 file_manager::kVideoPlayerAppId, | 36 file_manager::kVideoPlayerAppId, |
| 119 file_manager::kGalleryAppId, | 37 file_manager::kGalleryAppId, |
| 120 file_manager::kAudioPlayerAppId, | 38 file_manager::kAudioPlayerAppId, |
| 121 file_manager::kImageLoaderExtensionId, | 39 file_manager::kImageLoaderExtensionId, |
| 122 // TODO(mtomasz): Remove this extension id, and add it only for tests. | 40 // TODO(mtomasz): Remove this extension id, and add it only for tests. |
| 123 "pkplfbidichfdicaijlchgnapepdginl" // Testing extensions. | 41 "pkplfbidichfdicaijlchgnapepdginl" // Testing extensions. |
| 124 }; | 42 }; |
| 125 #endif | |
| 126 | 43 |
| 127 namespace extensions { | 44 ui::DialogButton g_auto_dialog_button_for_test = ui::DIALOG_BUTTON_NONE; |
| 128 | 45 |
| 129 namespace file_system = api::file_system; | |
| 130 namespace ChooseEntry = file_system::ChooseEntry; | |
| 131 | |
| 132 namespace { | |
| 133 | |
| 134 bool g_skip_picker_for_test = false; | |
| 135 bool g_use_suggested_path_for_test = false; | |
| 136 base::FilePath* g_path_to_be_picked_for_test; | |
| 137 std::vector<base::FilePath>* g_paths_to_be_picked_for_test; | |
| 138 bool g_skip_directory_confirmation_for_test = false; | |
| 139 bool g_allow_directory_access_for_test = false; | |
| 140 | |
| 141 #if defined(OS_CHROMEOS) | |
| 142 ui::DialogButton g_auto_dialog_button_for_test = ui::DIALOG_BUTTON_NONE; | |
| 143 #endif | |
| 144 | |
| 145 // Expand the mime-types and extensions provided in an AcceptOption, returning | |
| 146 // them within the passed extension vector. Returns false if no valid types | |
| 147 // were found. | |
| 148 bool GetFileTypesFromAcceptOption( | |
| 149 const file_system::AcceptOption& accept_option, | |
| 150 std::vector<base::FilePath::StringType>* extensions, | |
| 151 base::string16* description) { | |
| 152 std::set<base::FilePath::StringType> extension_set; | |
| 153 int description_id = 0; | |
| 154 | |
| 155 if (accept_option.mime_types.get()) { | |
| 156 std::vector<std::string>* list = accept_option.mime_types.get(); | |
| 157 bool valid_type = false; | |
| 158 for (std::vector<std::string>::const_iterator iter = list->begin(); | |
| 159 iter != list->end(); ++iter) { | |
| 160 std::vector<base::FilePath::StringType> inner; | |
| 161 std::string accept_type = base::ToLowerASCII(*iter); | |
| 162 net::GetExtensionsForMimeType(accept_type, &inner); | |
| 163 if (inner.empty()) | |
| 164 continue; | |
| 165 | |
| 166 if (valid_type) | |
| 167 description_id = 0; // We already have an accept type with label; if | |
| 168 // we find another, give up and use the default. | |
| 169 else if (accept_type == "image/*") | |
| 170 description_id = IDS_IMAGE_FILES; | |
| 171 else if (accept_type == "audio/*") | |
| 172 description_id = IDS_AUDIO_FILES; | |
| 173 else if (accept_type == "video/*") | |
| 174 description_id = IDS_VIDEO_FILES; | |
| 175 | |
| 176 extension_set.insert(inner.begin(), inner.end()); | |
| 177 valid_type = true; | |
| 178 } | |
| 179 } | |
| 180 | |
| 181 if (accept_option.extensions.get()) { | |
| 182 std::vector<std::string>* list = accept_option.extensions.get(); | |
| 183 for (std::vector<std::string>::const_iterator iter = list->begin(); | |
| 184 iter != list->end(); ++iter) { | |
| 185 std::string extension = base::ToLowerASCII(*iter); | |
| 186 #if defined(OS_WIN) | |
| 187 extension_set.insert(base::UTF8ToWide(*iter)); | |
| 188 #else | |
| 189 extension_set.insert(*iter); | |
| 190 #endif | |
| 191 } | |
| 192 } | |
| 193 | |
| 194 extensions->assign(extension_set.begin(), extension_set.end()); | |
| 195 if (extensions->empty()) | |
| 196 return false; | |
| 197 | |
| 198 if (accept_option.description.get()) | |
| 199 *description = base::UTF8ToUTF16(*accept_option.description); | |
| 200 else if (description_id) | |
| 201 *description = l10n_util::GetStringUTF16(description_id); | |
| 202 | |
| 203 return true; | |
| 204 } | |
| 205 | |
| 206 // Key for the path of the directory of the file last chosen by the user in | |
| 207 // response to a chrome.fileSystem.chooseEntry() call. | |
| 208 const char kLastChooseEntryDirectory[] = "last_choose_file_directory"; | |
| 209 | |
| 210 const int kGraylistedPaths[] = { | |
| 211 base::DIR_HOME, | |
| 212 #if defined(OS_WIN) | |
| 213 base::DIR_PROGRAM_FILES, | |
| 214 base::DIR_PROGRAM_FILESX86, | |
| 215 base::DIR_WINDOWS, | |
| 216 #endif | |
| 217 }; | |
| 218 | |
| 219 typedef base::Callback<void(std::unique_ptr<base::File::Info>)> | |
| 220 FileInfoOptCallback; | |
| 221 | |
| 222 // Passes optional file info to the UI thread depending on |result| and |info|. | |
| 223 void PassFileInfoToUIThread(const FileInfoOptCallback& callback, | |
| 224 base::File::Error result, | |
| 225 const base::File::Info& info) { | |
| 226 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); | |
| 227 std::unique_ptr<base::File::Info> file_info( | |
| 228 result == base::File::FILE_OK ? new base::File::Info(info) : NULL); | |
| 229 content::BrowserThread::PostTask( | |
| 230 content::BrowserThread::UI, FROM_HERE, | |
| 231 base::BindOnce(callback, base::Passed(&file_info))); | |
| 232 } | |
| 233 | |
| 234 // Gets a WebContents instance handle for a platform app hosted in | |
| 235 // |render_frame_host|. If not found, then returns NULL. | |
| 236 content::WebContents* GetWebContentsForRenderFrameHost( | |
| 237 Profile* profile, | |
| 238 content::RenderFrameHost* render_frame_host) { | |
| 239 content::WebContents* web_contents = | |
| 240 content::WebContents::FromRenderFrameHost(render_frame_host); | |
| 241 // Check if there is an app window associated with the web contents; if not, | |
| 242 // return null. | |
| 243 return AppWindowRegistry::Get(profile) | |
| 244 ->GetAppWindowForWebContents(web_contents) | |
| 245 ? web_contents | |
| 246 : nullptr; | |
| 247 } | |
| 248 | |
| 249 #if defined(OS_CHROMEOS) | |
| 250 // Gets a WebContents instance handle for a current window of a platform app | 46 // Gets a WebContents instance handle for a current window of a platform app |
| 251 // with |app_id|. If not found, then returns NULL. | 47 // with |app_id|. If not found, then returns NULL. |
| 252 content::WebContents* GetWebContentsForAppId(Profile* profile, | 48 content::WebContents* GetWebContentsForAppId(Profile* profile, |
| 253 const std::string& app_id) { | 49 const std::string& app_id) { |
| 254 AppWindowRegistry* const registry = AppWindowRegistry::Get(profile); | 50 AppWindowRegistry* const registry = AppWindowRegistry::Get(profile); |
| 255 DCHECK(registry); | 51 DCHECK(registry); |
| 256 AppWindow* const app_window = registry->GetCurrentAppWindowForApp(app_id); | 52 AppWindow* const app_window = registry->GetCurrentAppWindowForApp(app_id); |
| 257 return app_window ? app_window->web_contents() : nullptr; | 53 return app_window ? app_window->web_contents() : nullptr; |
| 258 } | 54 } |
| 259 | 55 |
| 260 // Fills a list of volumes mounted in the system. | |
| 261 void FillVolumeList(Profile* profile, | |
| 262 std::vector<api::file_system::Volume>* result) { | |
| 263 file_manager::VolumeManager* const volume_manager = | |
| 264 file_manager::VolumeManager::Get(profile); | |
| 265 DCHECK(volume_manager); | |
| 266 | |
| 267 const auto& volume_list = volume_manager->GetVolumeList(); | |
| 268 // Convert volume_list to result_volume_list. | |
| 269 for (const auto& volume : volume_list) { | |
| 270 api::file_system::Volume result_volume; | |
| 271 result_volume.volume_id = volume->volume_id(); | |
| 272 result_volume.writable = !volume->is_read_only(); | |
| 273 result->push_back(std::move(result_volume)); | |
| 274 } | |
| 275 } | |
| 276 | |
| 277 // Converts the clicked button to a consent result and passes it via the | 56 // Converts the clicked button to a consent result and passes it via the |
| 278 // |callback|. | 57 // |callback|. |
| 279 void DialogResultToConsent( | 58 void DialogResultToConsent( |
| 280 const file_system_api::ConsentProvider::ConsentCallback& callback, | 59 const file_system_api::ConsentProvider::ConsentCallback& callback, |
| 281 ui::DialogButton button) { | 60 ui::DialogButton button) { |
| 282 switch (button) { | 61 switch (button) { |
| 283 case ui::DIALOG_BUTTON_NONE: | 62 case ui::DIALOG_BUTTON_NONE: |
| 284 callback.Run(file_system_api::ConsentProvider::CONSENT_IMPOSSIBLE); | 63 callback.Run(file_system_api::ConsentProvider::CONSENT_IMPOSSIBLE); |
| 285 break; | 64 break; |
| 286 case ui::DIALOG_BUTTON_OK: | 65 case ui::DIALOG_BUTTON_OK: |
| 287 callback.Run(file_system_api::ConsentProvider::CONSENT_GRANTED); | 66 callback.Run(file_system_api::ConsentProvider::CONSENT_GRANTED); |
| 288 break; | 67 break; |
| 289 case ui::DIALOG_BUTTON_CANCEL: | 68 case ui::DIALOG_BUTTON_CANCEL: |
| 290 callback.Run(file_system_api::ConsentProvider::CONSENT_REJECTED); | 69 callback.Run(file_system_api::ConsentProvider::CONSENT_REJECTED); |
| 291 break; | 70 break; |
| 292 } | 71 } |
| 293 } | 72 } |
| 294 #endif | |
| 295 | 73 |
| 296 } // namespace | 74 } // namespace |
| 297 | 75 |
| 298 namespace file_system_api { | 76 namespace file_system_api { |
| 299 | 77 |
| 300 base::FilePath GetLastChooseEntryDirectory(const ExtensionPrefs* prefs, | |
| 301 const std::string& extension_id) { | |
| 302 base::FilePath path; | |
| 303 std::string string_path; | |
| 304 if (prefs->ReadPrefAsString(extension_id, | |
| 305 kLastChooseEntryDirectory, | |
| 306 &string_path)) { | |
| 307 path = base::FilePath::FromUTF8Unsafe(string_path); | |
| 308 } | |
| 309 return path; | |
| 310 } | |
| 311 | |
| 312 void SetLastChooseEntryDirectory(ExtensionPrefs* prefs, | |
| 313 const std::string& extension_id, | |
| 314 const base::FilePath& path) { | |
| 315 prefs->UpdateExtensionPref(extension_id, kLastChooseEntryDirectory, | |
| 316 base::CreateFilePathValue(path)); | |
| 317 } | |
| 318 | |
| 319 #if defined(OS_CHROMEOS) | |
| 320 void DispatchVolumeListChangeEvent(Profile* profile) { | |
| 321 DCHECK(profile); | |
| 322 EventRouter* const event_router = EventRouter::Get(profile); | |
| 323 if (!event_router) // Possible on shutdown. | |
| 324 return; | |
| 325 | |
| 326 ExtensionRegistry* const registry = ExtensionRegistry::Get(profile); | |
| 327 if (!registry) // Possible on shutdown. | |
| 328 return; | |
| 329 | |
| 330 ConsentProviderDelegate consent_provider_delegate(profile, nullptr); | |
| 331 ConsentProvider consent_provider(&consent_provider_delegate); | |
| 332 api::file_system::VolumeListChangedEvent event_args; | |
| 333 FillVolumeList(profile, &event_args.volumes); | |
| 334 for (const auto& extension : registry->enabled_extensions()) { | |
| 335 if (!consent_provider.IsGrantable(*extension.get())) | |
| 336 continue; | |
| 337 event_router->DispatchEventToExtension( | |
| 338 extension->id(), | |
| 339 base::MakeUnique<Event>( | |
| 340 events::FILE_SYSTEM_ON_VOLUME_LIST_CHANGED, | |
| 341 api::file_system::OnVolumeListChanged::kEventName, | |
| 342 api::file_system::OnVolumeListChanged::Create(event_args))); | |
| 343 } | |
| 344 } | |
| 345 | |
| 346 ConsentProvider::ConsentProvider(DelegateInterface* delegate) | 78 ConsentProvider::ConsentProvider(DelegateInterface* delegate) |
| 347 : delegate_(delegate) { | 79 : delegate_(delegate) { |
| 348 DCHECK(delegate_); | 80 DCHECK(delegate_); |
| 349 } | 81 } |
| 350 | 82 |
| 351 ConsentProvider::~ConsentProvider() { | 83 ConsentProvider::~ConsentProvider() { |
| 352 } | 84 } |
| 353 | 85 |
| 354 void ConsentProvider::RequestConsent( | 86 void ConsentProvider::RequestConsent( |
| 355 const Extension& extension, | 87 const Extension& extension, |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 412 ui::DialogButton button) { | 144 ui::DialogButton button) { |
| 413 g_auto_dialog_button_for_test = button; | 145 g_auto_dialog_button_for_test = button; |
| 414 } | 146 } |
| 415 | 147 |
| 416 void ConsentProviderDelegate::ShowDialog( | 148 void ConsentProviderDelegate::ShowDialog( |
| 417 const Extension& extension, | 149 const Extension& extension, |
| 418 const base::WeakPtr<file_manager::Volume>& volume, | 150 const base::WeakPtr<file_manager::Volume>& volume, |
| 419 bool writable, | 151 bool writable, |
| 420 const file_system_api::ConsentProvider::ShowDialogCallback& callback) { | 152 const file_system_api::ConsentProvider::ShowDialogCallback& callback) { |
| 421 DCHECK(host_); | 153 DCHECK(host_); |
| 154 content::WebContents* web_contents = nullptr; | |
|
michaelpg
2017/06/07 06:28:04
This is the only logic I actually changed in this
| |
| 155 | |
| 156 // Find an app window to host the dialog. | |
| 422 content::WebContents* const foreground_contents = | 157 content::WebContents* const foreground_contents = |
| 423 GetWebContentsForRenderFrameHost(profile_, host_); | 158 content::WebContents::FromRenderFrameHost(host_); |
| 159 if (AppWindowRegistry::Get(profile_)->GetAppWindowForWebContents( | |
| 160 foreground_contents)) { | |
| 161 web_contents = foreground_contents; | |
| 162 } | |
| 163 | |
| 424 // If there is no web contents handle, then the method is most probably | 164 // If there is no web contents handle, then the method is most probably |
| 425 // executed from a background page. Find an app window to host the dialog. | 165 // executed from a background page. |
| 426 content::WebContents* const web_contents = | 166 if (!web_contents) |
| 427 foreground_contents ? foreground_contents | 167 web_contents = GetWebContentsForAppId(profile_, extension.id()); |
| 428 : GetWebContentsForAppId(profile_, extension.id()); | 168 |
| 429 if (!web_contents) { | 169 if (!web_contents) { |
| 430 base::ThreadTaskRunnerHandle::Get()->PostTask( | 170 base::ThreadTaskRunnerHandle::Get()->PostTask( |
| 431 FROM_HERE, base::Bind(callback, ui::DIALOG_BUTTON_NONE)); | 171 FROM_HERE, base::Bind(callback, ui::DIALOG_BUTTON_NONE)); |
| 432 return; | 172 return; |
| 433 } | 173 } |
| 434 | 174 |
| 435 // Short circuit the user consent dialog for tests. This is far from a pretty | 175 // Short circuit the user consent dialog for tests. This is far from a pretty |
| 436 // code design. | 176 // code design. |
| 437 if (g_auto_dialog_button_for_test != ui::DIALOG_BUTTON_NONE) { | 177 if (g_auto_dialog_button_for_test != ui::DIALOG_BUTTON_NONE) { |
| 438 base::ThreadTaskRunnerHandle::Get()->PostTask( | 178 base::ThreadTaskRunnerHandle::Get()->PostTask( |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 471 | 211 |
| 472 bool ConsentProviderDelegate::IsWhitelistedComponent( | 212 bool ConsentProviderDelegate::IsWhitelistedComponent( |
| 473 const Extension& extension) { | 213 const Extension& extension) { |
| 474 for (auto* whitelisted_id : kRequestFileSystemComponentWhitelist) { | 214 for (auto* whitelisted_id : kRequestFileSystemComponentWhitelist) { |
| 475 if (extension.id().compare(whitelisted_id) == 0) | 215 if (extension.id().compare(whitelisted_id) == 0) |
| 476 return true; | 216 return true; |
| 477 } | 217 } |
| 478 return false; | 218 return false; |
| 479 } | 219 } |
| 480 | 220 |
| 481 #endif | |
| 482 | |
| 483 } // namespace file_system_api | 221 } // namespace file_system_api |
| 484 | |
| 485 #if defined(OS_CHROMEOS) | |
| 486 using file_system_api::ConsentProvider; | |
| 487 #endif | |
| 488 | |
| 489 ExtensionFunction::ResponseAction FileSystemGetDisplayPathFunction::Run() { | |
| 490 std::string filesystem_name; | |
| 491 std::string filesystem_path; | |
| 492 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &filesystem_name)); | |
| 493 EXTENSION_FUNCTION_VALIDATE(args_->GetString(1, &filesystem_path)); | |
| 494 | |
| 495 base::FilePath file_path; | |
| 496 std::string error; | |
| 497 if (!app_file_handler_util::ValidateFileEntryAndGetPath( | |
| 498 filesystem_name, filesystem_path, | |
| 499 render_frame_host()->GetProcess()->GetID(), &file_path, &error)) { | |
| 500 return RespondNow(Error(error)); | |
| 501 } | |
| 502 | |
| 503 file_path = path_util::PrettifyPath(file_path); | |
| 504 return RespondNow( | |
| 505 OneArgument(base::MakeUnique<base::Value>(file_path.value()))); | |
| 506 } | |
| 507 | |
| 508 FileSystemEntryFunction::FileSystemEntryFunction() | |
| 509 : multiple_(false), is_directory_(false) {} | |
| 510 | |
| 511 void FileSystemEntryFunction::PrepareFilesForWritableApp( | |
| 512 const std::vector<base::FilePath>& paths) { | |
| 513 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | |
| 514 // TODO(cmihail): Path directory set should be initialized only with the | |
| 515 // paths that are actually directories, but for now we will consider | |
| 516 // all paths directories in case is_directory_ is true, otherwise | |
| 517 // all paths files, as this was the previous logic. | |
| 518 std::set<base::FilePath> path_directory_set_ = | |
| 519 is_directory_ ? std::set<base::FilePath>(paths.begin(), paths.end()) | |
| 520 : std::set<base::FilePath>{}; | |
| 521 app_file_handler_util::PrepareFilesForWritableApp( | |
| 522 paths, GetProfile(), path_directory_set_, | |
| 523 base::Bind(&FileSystemEntryFunction::RegisterFileSystemsAndSendResponse, | |
| 524 this, paths), | |
| 525 base::Bind(&FileSystemEntryFunction::HandleWritableFileError, this)); | |
| 526 } | |
| 527 | |
| 528 void FileSystemEntryFunction::RegisterFileSystemsAndSendResponse( | |
| 529 const std::vector<base::FilePath>& paths) { | |
| 530 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | |
| 531 if (!render_frame_host()) | |
| 532 return; | |
| 533 | |
| 534 std::unique_ptr<base::DictionaryValue> result = CreateResult(); | |
| 535 for (const auto& path : paths) | |
| 536 AddEntryToResult(path, std::string(), result.get()); | |
| 537 SetResult(std::move(result)); | |
| 538 SendResponse(true); | |
| 539 } | |
| 540 | |
| 541 std::unique_ptr<base::DictionaryValue> FileSystemEntryFunction::CreateResult() { | |
| 542 std::unique_ptr<base::DictionaryValue> result(new base::DictionaryValue()); | |
| 543 result->Set("entries", base::MakeUnique<base::ListValue>()); | |
| 544 result->SetBoolean("multiple", multiple_); | |
| 545 return result; | |
| 546 } | |
| 547 | |
| 548 void FileSystemEntryFunction::AddEntryToResult(const base::FilePath& path, | |
| 549 const std::string& id_override, | |
| 550 base::DictionaryValue* result) { | |
| 551 GrantedFileEntry file_entry = app_file_handler_util::CreateFileEntry( | |
| 552 GetProfile(), | |
| 553 extension(), | |
| 554 render_frame_host()->GetProcess()->GetID(), | |
| 555 path, | |
| 556 is_directory_); | |
| 557 base::ListValue* entries; | |
| 558 bool success = result->GetList("entries", &entries); | |
| 559 DCHECK(success); | |
| 560 | |
| 561 std::unique_ptr<base::DictionaryValue> entry(new base::DictionaryValue()); | |
| 562 entry->SetString("fileSystemId", file_entry.filesystem_id); | |
| 563 entry->SetString("baseName", file_entry.registered_name); | |
| 564 if (id_override.empty()) | |
| 565 entry->SetString("id", file_entry.id); | |
| 566 else | |
| 567 entry->SetString("id", id_override); | |
| 568 entry->SetBoolean("isDirectory", is_directory_); | |
| 569 entries->Append(std::move(entry)); | |
| 570 } | |
| 571 | |
| 572 void FileSystemEntryFunction::HandleWritableFileError( | |
| 573 const base::FilePath& error_path) { | |
| 574 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | |
| 575 error_ = base::StringPrintf(kWritableFileErrorFormat, | |
| 576 error_path.BaseName().AsUTF8Unsafe().c_str()); | |
| 577 SendResponse(false); | |
| 578 } | |
| 579 | |
| 580 bool FileSystemGetWritableEntryFunction::RunAsync() { | |
| 581 std::string filesystem_name; | |
| 582 std::string filesystem_path; | |
| 583 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &filesystem_name)); | |
| 584 EXTENSION_FUNCTION_VALIDATE(args_->GetString(1, &filesystem_path)); | |
| 585 | |
| 586 if (!app_file_handler_util::HasFileSystemWritePermission(extension_.get())) { | |
| 587 error_ = kRequiresFileSystemWriteError; | |
| 588 return false; | |
| 589 } | |
| 590 | |
| 591 if (!app_file_handler_util::ValidateFileEntryAndGetPath( | |
| 592 filesystem_name, filesystem_path, | |
| 593 render_frame_host()->GetProcess()->GetID(), &path_, &error_)) | |
| 594 return false; | |
| 595 | |
| 596 base::PostTaskWithTraitsAndReply( | |
| 597 FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND}, | |
| 598 base::BindOnce(&FileSystemGetWritableEntryFunction::SetIsDirectoryAsync, | |
| 599 this), | |
| 600 base::BindOnce( | |
| 601 &FileSystemGetWritableEntryFunction::CheckPermissionAndSendResponse, | |
| 602 this)); | |
| 603 return true; | |
| 604 } | |
| 605 | |
| 606 void FileSystemGetWritableEntryFunction::CheckPermissionAndSendResponse() { | |
| 607 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | |
| 608 if (is_directory_ && | |
| 609 !extension_->permissions_data()->HasAPIPermission( | |
| 610 APIPermission::kFileSystemDirectory)) { | |
| 611 error_ = kRequiresFileSystemDirectoryError; | |
| 612 SendResponse(false); | |
| 613 } | |
| 614 std::vector<base::FilePath> paths; | |
| 615 paths.push_back(path_); | |
| 616 PrepareFilesForWritableApp(paths); | |
| 617 } | |
| 618 | |
| 619 void FileSystemGetWritableEntryFunction::SetIsDirectoryAsync() { | |
| 620 if (base::DirectoryExists(path_)) { | |
| 621 is_directory_ = true; | |
| 622 } | |
| 623 } | |
| 624 | |
| 625 ExtensionFunction::ResponseAction FileSystemIsWritableEntryFunction::Run() { | |
| 626 std::string filesystem_name; | |
| 627 std::string filesystem_path; | |
| 628 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &filesystem_name)); | |
| 629 EXTENSION_FUNCTION_VALIDATE(args_->GetString(1, &filesystem_path)); | |
| 630 | |
| 631 std::string filesystem_id; | |
| 632 if (!storage::CrackIsolatedFileSystemName(filesystem_name, &filesystem_id)) | |
| 633 return RespondNow(Error(app_file_handler_util::kInvalidParameters)); | |
| 634 | |
| 635 content::ChildProcessSecurityPolicy* policy = | |
| 636 content::ChildProcessSecurityPolicy::GetInstance(); | |
| 637 int renderer_id = render_frame_host()->GetProcess()->GetID(); | |
| 638 bool is_writable = policy->CanReadWriteFileSystem(renderer_id, | |
| 639 filesystem_id); | |
| 640 | |
| 641 return RespondNow(OneArgument(base::MakeUnique<base::Value>(is_writable))); | |
| 642 } | |
| 643 | |
| 644 // Handles showing a dialog to the user to ask for the filename for a file to | |
| 645 // save or open. | |
| 646 class FileSystemChooseEntryFunction::FilePicker | |
| 647 : public ui::SelectFileDialog::Listener { | |
| 648 public: | |
| 649 FilePicker(FileSystemChooseEntryFunction* function, | |
| 650 content::WebContents* web_contents, | |
| 651 const base::FilePath& suggested_name, | |
| 652 const ui::SelectFileDialog::FileTypeInfo& file_type_info, | |
| 653 ui::SelectFileDialog::Type picker_type) | |
| 654 : function_(function) { | |
| 655 select_file_dialog_ = ui::SelectFileDialog::Create( | |
| 656 this, new ChromeSelectFilePolicy(web_contents)); | |
| 657 gfx::NativeWindow owning_window = web_contents ? | |
| 658 platform_util::GetTopLevel(web_contents->GetNativeView()) : | |
| 659 NULL; | |
| 660 | |
| 661 if (g_skip_picker_for_test) { | |
| 662 if (g_use_suggested_path_for_test) { | |
| 663 content::BrowserThread::PostTask( | |
| 664 content::BrowserThread::UI, FROM_HERE, | |
| 665 base::BindOnce( | |
| 666 &FileSystemChooseEntryFunction::FilePicker::FileSelected, | |
| 667 base::Unretained(this), suggested_name, 1, | |
| 668 static_cast<void*>(NULL))); | |
| 669 } else if (g_path_to_be_picked_for_test) { | |
| 670 content::BrowserThread::PostTask( | |
| 671 content::BrowserThread::UI, FROM_HERE, | |
| 672 base::BindOnce( | |
| 673 &FileSystemChooseEntryFunction::FilePicker::FileSelected, | |
| 674 base::Unretained(this), *g_path_to_be_picked_for_test, 1, | |
| 675 static_cast<void*>(NULL))); | |
| 676 } else if (g_paths_to_be_picked_for_test) { | |
| 677 content::BrowserThread::PostTask( | |
| 678 content::BrowserThread::UI, FROM_HERE, | |
| 679 base::BindOnce( | |
| 680 &FileSystemChooseEntryFunction::FilePicker::MultiFilesSelected, | |
| 681 base::Unretained(this), *g_paths_to_be_picked_for_test, | |
| 682 static_cast<void*>(NULL))); | |
| 683 } else { | |
| 684 content::BrowserThread::PostTask( | |
| 685 content::BrowserThread::UI, FROM_HERE, | |
| 686 base::BindOnce(&FileSystemChooseEntryFunction::FilePicker:: | |
| 687 FileSelectionCanceled, | |
| 688 base::Unretained(this), static_cast<void*>(NULL))); | |
| 689 } | |
| 690 return; | |
| 691 } | |
| 692 | |
| 693 select_file_dialog_->SelectFile(picker_type, | |
| 694 base::string16(), | |
| 695 suggested_name, | |
| 696 &file_type_info, | |
| 697 0, | |
| 698 base::FilePath::StringType(), | |
| 699 owning_window, | |
| 700 NULL); | |
| 701 } | |
| 702 | |
| 703 ~FilePicker() override {} | |
| 704 | |
| 705 private: | |
| 706 // ui::SelectFileDialog::Listener implementation. | |
| 707 void FileSelected(const base::FilePath& path, | |
| 708 int index, | |
| 709 void* params) override { | |
| 710 std::vector<base::FilePath> paths; | |
| 711 paths.push_back(path); | |
| 712 MultiFilesSelected(paths, params); | |
| 713 } | |
| 714 | |
| 715 void FileSelectedWithExtraInfo(const ui::SelectedFileInfo& file, | |
| 716 int index, | |
| 717 void* params) override { | |
| 718 // Normally, file.local_path is used because it is a native path to the | |
| 719 // local read-only cached file in the case of remote file system like | |
| 720 // Chrome OS's Google Drive integration. Here, however, |file.file_path| is | |
| 721 // necessary because we need to create a FileEntry denoting the remote file, | |
| 722 // not its cache. On other platforms than Chrome OS, they are the same. | |
| 723 // | |
| 724 // TODO(kinaba): remove this, once after the file picker implements proper | |
| 725 // switch of the path treatment depending on the |allowed_paths|. | |
| 726 FileSelected(file.file_path, index, params); | |
| 727 } | |
| 728 | |
| 729 void MultiFilesSelected(const std::vector<base::FilePath>& files, | |
| 730 void* params) override { | |
| 731 function_->FilesSelected(files); | |
| 732 delete this; | |
| 733 } | |
| 734 | |
| 735 void MultiFilesSelectedWithExtraInfo( | |
| 736 const std::vector<ui::SelectedFileInfo>& files, | |
| 737 void* params) override { | |
| 738 std::vector<base::FilePath> paths; | |
| 739 for (std::vector<ui::SelectedFileInfo>::const_iterator it = files.begin(); | |
| 740 it != files.end(); ++it) { | |
| 741 paths.push_back(it->file_path); | |
| 742 } | |
| 743 MultiFilesSelected(paths, params); | |
| 744 } | |
| 745 | |
| 746 void FileSelectionCanceled(void* params) override { | |
| 747 function_->FileSelectionCanceled(); | |
| 748 delete this; | |
| 749 } | |
| 750 | |
| 751 scoped_refptr<ui::SelectFileDialog> select_file_dialog_; | |
| 752 scoped_refptr<FileSystemChooseEntryFunction> function_; | |
| 753 | |
| 754 DISALLOW_COPY_AND_ASSIGN(FilePicker); | |
| 755 }; | |
| 756 | |
| 757 void FileSystemChooseEntryFunction::ShowPicker( | |
| 758 const ui::SelectFileDialog::FileTypeInfo& file_type_info, | |
| 759 ui::SelectFileDialog::Type picker_type) { | |
| 760 // TODO(asargent/benwells) - As a short term remediation for crbug.com/179010 | |
| 761 // we're adding the ability for a whitelisted extension to use this API since | |
| 762 // chrome.fileBrowserHandler.selectFile is ChromeOS-only. Eventually we'd | |
| 763 // like a better solution and likely this code will go back to being | |
| 764 // platform-app only. | |
| 765 content::WebContents* const web_contents = | |
| 766 extension_->is_platform_app() | |
| 767 ? GetWebContentsForRenderFrameHost(GetProfile(), render_frame_host()) | |
| 768 : GetAssociatedWebContents(); | |
| 769 if (!web_contents) { | |
| 770 error_ = kInvalidCallingPage; | |
| 771 SendResponse(false); | |
| 772 return; | |
| 773 } | |
| 774 | |
| 775 // The file picker will hold a reference to this function instance, preventing | |
| 776 // its destruction (and subsequent sending of the function response) until the | |
| 777 // user has selected a file or cancelled the picker. At that point, the picker | |
| 778 // will delete itself, which will also free the function instance. | |
| 779 new FilePicker( | |
| 780 this, web_contents, initial_path_, file_type_info, picker_type); | |
| 781 } | |
| 782 | |
| 783 // static | |
| 784 void FileSystemChooseEntryFunction::SkipPickerAndAlwaysSelectPathForTest( | |
| 785 base::FilePath* path) { | |
| 786 g_skip_picker_for_test = true; | |
| 787 g_use_suggested_path_for_test = false; | |
| 788 g_path_to_be_picked_for_test = path; | |
| 789 g_paths_to_be_picked_for_test = NULL; | |
| 790 } | |
| 791 | |
| 792 void FileSystemChooseEntryFunction::SkipPickerAndAlwaysSelectPathsForTest( | |
| 793 std::vector<base::FilePath>* paths) { | |
| 794 g_skip_picker_for_test = true; | |
| 795 g_use_suggested_path_for_test = false; | |
| 796 g_paths_to_be_picked_for_test = paths; | |
| 797 } | |
| 798 | |
| 799 // static | |
| 800 void FileSystemChooseEntryFunction::SkipPickerAndSelectSuggestedPathForTest() { | |
| 801 g_skip_picker_for_test = true; | |
| 802 g_use_suggested_path_for_test = true; | |
| 803 g_path_to_be_picked_for_test = NULL; | |
| 804 g_paths_to_be_picked_for_test = NULL; | |
| 805 } | |
| 806 | |
| 807 // static | |
| 808 void FileSystemChooseEntryFunction::SkipPickerAndAlwaysCancelForTest() { | |
| 809 g_skip_picker_for_test = true; | |
| 810 g_use_suggested_path_for_test = false; | |
| 811 g_path_to_be_picked_for_test = NULL; | |
| 812 g_paths_to_be_picked_for_test = NULL; | |
| 813 } | |
| 814 | |
| 815 // static | |
| 816 void FileSystemChooseEntryFunction::StopSkippingPickerForTest() { | |
| 817 g_skip_picker_for_test = false; | |
| 818 } | |
| 819 | |
| 820 // static | |
| 821 void FileSystemChooseEntryFunction::SkipDirectoryConfirmationForTest() { | |
| 822 g_skip_directory_confirmation_for_test = true; | |
| 823 g_allow_directory_access_for_test = true; | |
| 824 } | |
| 825 | |
| 826 // static | |
| 827 void FileSystemChooseEntryFunction::AutoCancelDirectoryConfirmationForTest() { | |
| 828 g_skip_directory_confirmation_for_test = true; | |
| 829 g_allow_directory_access_for_test = false; | |
| 830 } | |
| 831 | |
| 832 // static | |
| 833 void FileSystemChooseEntryFunction::StopSkippingDirectoryConfirmationForTest() { | |
| 834 g_skip_directory_confirmation_for_test = false; | |
| 835 } | |
| 836 | |
| 837 // static | |
| 838 void FileSystemChooseEntryFunction::RegisterTempExternalFileSystemForTest( | |
| 839 const std::string& name, const base::FilePath& path) { | |
| 840 // For testing on Chrome OS, where to deal with remote and local paths | |
| 841 // smoothly, all accessed paths need to be registered in the list of | |
| 842 // external mount points. | |
| 843 storage::ExternalMountPoints::GetSystemInstance()->RegisterFileSystem( | |
| 844 name, | |
| 845 storage::kFileSystemTypeNativeLocal, | |
| 846 storage::FileSystemMountOption(), | |
| 847 path); | |
| 848 } | |
| 849 | |
| 850 void FileSystemChooseEntryFunction::FilesSelected( | |
| 851 const std::vector<base::FilePath>& paths) { | |
| 852 DCHECK(!paths.empty()); | |
| 853 base::FilePath last_choose_directory; | |
| 854 if (is_directory_) { | |
| 855 last_choose_directory = paths[0]; | |
| 856 } else { | |
| 857 last_choose_directory = paths[0].DirName(); | |
| 858 } | |
| 859 file_system_api::SetLastChooseEntryDirectory( | |
| 860 ExtensionPrefs::Get(GetProfile()), | |
| 861 extension()->id(), | |
| 862 last_choose_directory); | |
| 863 if (is_directory_) { | |
| 864 // Get the WebContents for the app window to be the parent window of the | |
| 865 // confirmation dialog if necessary. | |
| 866 content::WebContents* const web_contents = | |
| 867 GetWebContentsForRenderFrameHost(GetProfile(), render_frame_host()); | |
| 868 if (!web_contents) { | |
| 869 error_ = kInvalidCallingPage; | |
| 870 SendResponse(false); | |
| 871 return; | |
| 872 } | |
| 873 | |
| 874 DCHECK_EQ(paths.size(), 1u); | |
| 875 bool non_native_path = false; | |
| 876 #if defined(OS_CHROMEOS) | |
| 877 non_native_path = | |
| 878 file_manager::util::IsUnderNonNativeLocalPath(GetProfile(), paths[0]); | |
| 879 #endif | |
| 880 | |
| 881 base::PostTaskWithTraits( | |
| 882 FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND}, | |
| 883 base::BindOnce( | |
| 884 &FileSystemChooseEntryFunction::ConfirmDirectoryAccessAsync, this, | |
| 885 non_native_path, paths, web_contents)); | |
| 886 return; | |
| 887 } | |
| 888 | |
| 889 OnDirectoryAccessConfirmed(paths); | |
| 890 } | |
| 891 | |
| 892 void FileSystemChooseEntryFunction::FileSelectionCanceled() { | |
| 893 error_ = kUserCancelled; | |
| 894 SendResponse(false); | |
| 895 } | |
| 896 | |
| 897 void FileSystemChooseEntryFunction::ConfirmDirectoryAccessAsync( | |
| 898 bool non_native_path, | |
| 899 const std::vector<base::FilePath>& paths, | |
| 900 content::WebContents* web_contents) { | |
| 901 const base::FilePath check_path = | |
| 902 non_native_path ? paths[0] : base::MakeAbsoluteFilePath(paths[0]); | |
| 903 if (check_path.empty()) { | |
| 904 content::BrowserThread::PostTask( | |
| 905 content::BrowserThread::UI, FROM_HERE, | |
| 906 base::BindOnce(&FileSystemChooseEntryFunction::FileSelectionCanceled, | |
| 907 this)); | |
| 908 return; | |
| 909 } | |
| 910 | |
| 911 for (size_t i = 0; i < arraysize(kGraylistedPaths); i++) { | |
| 912 base::FilePath graylisted_path; | |
| 913 if (PathService::Get(kGraylistedPaths[i], &graylisted_path) && | |
| 914 (check_path == graylisted_path || | |
| 915 check_path.IsParent(graylisted_path))) { | |
| 916 if (g_skip_directory_confirmation_for_test) { | |
| 917 if (g_allow_directory_access_for_test) { | |
| 918 break; | |
| 919 } else { | |
| 920 content::BrowserThread::PostTask( | |
| 921 content::BrowserThread::UI, FROM_HERE, | |
| 922 base::BindOnce( | |
| 923 &FileSystemChooseEntryFunction::FileSelectionCanceled, this)); | |
| 924 } | |
| 925 return; | |
| 926 } | |
| 927 | |
| 928 content::BrowserThread::PostTask( | |
| 929 content::BrowserThread::UI, FROM_HERE, | |
| 930 base::BindOnce( | |
| 931 CreateDirectoryAccessConfirmationDialog, | |
| 932 app_file_handler_util::HasFileSystemWritePermission( | |
| 933 extension_.get()), | |
| 934 base::UTF8ToUTF16(extension_->name()), web_contents, | |
| 935 base::Bind( | |
| 936 &FileSystemChooseEntryFunction::OnDirectoryAccessConfirmed, | |
| 937 this, paths), | |
| 938 base::Bind(&FileSystemChooseEntryFunction::FileSelectionCanceled, | |
| 939 this))); | |
| 940 return; | |
| 941 } | |
| 942 } | |
| 943 | |
| 944 content::BrowserThread::PostTask( | |
| 945 content::BrowserThread::UI, FROM_HERE, | |
| 946 base::BindOnce(&FileSystemChooseEntryFunction::OnDirectoryAccessConfirmed, | |
| 947 this, paths)); | |
| 948 } | |
| 949 | |
| 950 void FileSystemChooseEntryFunction::OnDirectoryAccessConfirmed( | |
| 951 const std::vector<base::FilePath>& paths) { | |
| 952 if (app_file_handler_util::HasFileSystemWritePermission(extension_.get())) { | |
| 953 PrepareFilesForWritableApp(paths); | |
| 954 return; | |
| 955 } | |
| 956 | |
| 957 // Don't need to check the file, it's for reading. | |
| 958 RegisterFileSystemsAndSendResponse(paths); | |
| 959 } | |
| 960 | |
| 961 void FileSystemChooseEntryFunction::BuildFileTypeInfo( | |
| 962 ui::SelectFileDialog::FileTypeInfo* file_type_info, | |
| 963 const base::FilePath::StringType& suggested_extension, | |
| 964 const AcceptOptions* accepts, | |
| 965 const bool* acceptsAllTypes) { | |
| 966 file_type_info->include_all_files = true; | |
| 967 if (acceptsAllTypes) | |
| 968 file_type_info->include_all_files = *acceptsAllTypes; | |
| 969 | |
| 970 bool need_suggestion = !file_type_info->include_all_files && | |
| 971 !suggested_extension.empty(); | |
| 972 | |
| 973 if (accepts) { | |
| 974 for (const file_system::AcceptOption& option : *accepts) { | |
| 975 base::string16 description; | |
| 976 std::vector<base::FilePath::StringType> extensions; | |
| 977 | |
| 978 if (!GetFileTypesFromAcceptOption(option, &extensions, &description)) | |
| 979 continue; // No extensions were found. | |
| 980 | |
| 981 file_type_info->extensions.push_back(extensions); | |
| 982 file_type_info->extension_description_overrides.push_back(description); | |
| 983 | |
| 984 // If we still need to find suggested_extension, hunt for it inside the | |
| 985 // extensions returned from GetFileTypesFromAcceptOption. | |
| 986 if (need_suggestion && std::find(extensions.begin(), | |
| 987 extensions.end(), suggested_extension) != extensions.end()) { | |
| 988 need_suggestion = false; | |
| 989 } | |
| 990 } | |
| 991 } | |
| 992 | |
| 993 // If there's nothing in our accepted extension list or we couldn't find the | |
| 994 // suggested extension required, then default to accepting all types. | |
| 995 if (file_type_info->extensions.empty() || need_suggestion) | |
| 996 file_type_info->include_all_files = true; | |
| 997 } | |
| 998 | |
| 999 void FileSystemChooseEntryFunction::BuildSuggestion( | |
| 1000 const std::string *opt_name, | |
| 1001 base::FilePath* suggested_name, | |
| 1002 base::FilePath::StringType* suggested_extension) { | |
| 1003 if (opt_name) { | |
| 1004 *suggested_name = base::FilePath::FromUTF8Unsafe(*opt_name); | |
| 1005 | |
| 1006 // Don't allow any path components; shorten to the base name. This should | |
| 1007 // result in a relative path, but in some cases may not. Clear the | |
| 1008 // suggestion for safety if this is the case. | |
| 1009 *suggested_name = suggested_name->BaseName(); | |
| 1010 if (suggested_name->IsAbsolute()) | |
| 1011 *suggested_name = base::FilePath(); | |
| 1012 | |
| 1013 *suggested_extension = suggested_name->Extension(); | |
| 1014 if (!suggested_extension->empty()) | |
| 1015 suggested_extension->erase(suggested_extension->begin()); // drop the . | |
| 1016 } | |
| 1017 } | |
| 1018 | |
| 1019 void FileSystemChooseEntryFunction::SetInitialPathAndShowPicker( | |
| 1020 const base::FilePath& previous_path, | |
| 1021 const base::FilePath& suggested_name, | |
| 1022 const ui::SelectFileDialog::FileTypeInfo& file_type_info, | |
| 1023 ui::SelectFileDialog::Type picker_type, | |
| 1024 bool is_previous_path_directory) { | |
| 1025 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | |
| 1026 if (is_previous_path_directory) { | |
| 1027 initial_path_ = previous_path.Append(suggested_name); | |
| 1028 } else { | |
| 1029 base::FilePath documents_dir; | |
| 1030 if (PathService::Get(chrome::DIR_USER_DOCUMENTS, &documents_dir)) { | |
| 1031 initial_path_ = documents_dir.Append(suggested_name); | |
| 1032 } else { | |
| 1033 initial_path_ = suggested_name; | |
| 1034 } | |
| 1035 } | |
| 1036 ShowPicker(file_type_info, picker_type); | |
| 1037 } | |
| 1038 | |
| 1039 bool FileSystemChooseEntryFunction::RunAsync() { | |
| 1040 std::unique_ptr<ChooseEntry::Params> params( | |
| 1041 ChooseEntry::Params::Create(*args_)); | |
| 1042 EXTENSION_FUNCTION_VALIDATE(params.get()); | |
| 1043 | |
| 1044 base::FilePath suggested_name; | |
| 1045 ui::SelectFileDialog::FileTypeInfo file_type_info; | |
| 1046 ui::SelectFileDialog::Type picker_type = | |
| 1047 ui::SelectFileDialog::SELECT_OPEN_FILE; | |
| 1048 | |
| 1049 file_system::ChooseEntryOptions* options = params->options.get(); | |
| 1050 if (options) { | |
| 1051 multiple_ = options->accepts_multiple && *options->accepts_multiple; | |
| 1052 if (multiple_) | |
| 1053 picker_type = ui::SelectFileDialog::SELECT_OPEN_MULTI_FILE; | |
| 1054 | |
| 1055 if (options->type == file_system::CHOOSE_ENTRY_TYPE_OPENWRITABLEFILE && | |
| 1056 !app_file_handler_util::HasFileSystemWritePermission( | |
| 1057 extension_.get())) { | |
| 1058 error_ = kRequiresFileSystemWriteError; | |
| 1059 return false; | |
| 1060 } else if (options->type == file_system::CHOOSE_ENTRY_TYPE_SAVEFILE) { | |
| 1061 if (!app_file_handler_util::HasFileSystemWritePermission( | |
| 1062 extension_.get())) { | |
| 1063 error_ = kRequiresFileSystemWriteError; | |
| 1064 return false; | |
| 1065 } | |
| 1066 if (multiple_) { | |
| 1067 error_ = kMultipleUnsupportedError; | |
| 1068 return false; | |
| 1069 } | |
| 1070 picker_type = ui::SelectFileDialog::SELECT_SAVEAS_FILE; | |
| 1071 } else if (options->type == file_system::CHOOSE_ENTRY_TYPE_OPENDIRECTORY) { | |
| 1072 is_directory_ = true; | |
| 1073 if (!extension_->permissions_data()->HasAPIPermission( | |
| 1074 APIPermission::kFileSystemDirectory)) { | |
| 1075 error_ = kRequiresFileSystemDirectoryError; | |
| 1076 return false; | |
| 1077 } | |
| 1078 if (multiple_) { | |
| 1079 error_ = kMultipleUnsupportedError; | |
| 1080 return false; | |
| 1081 } | |
| 1082 picker_type = ui::SelectFileDialog::SELECT_FOLDER; | |
| 1083 } | |
| 1084 | |
| 1085 base::FilePath::StringType suggested_extension; | |
| 1086 BuildSuggestion(options->suggested_name.get(), &suggested_name, | |
| 1087 &suggested_extension); | |
| 1088 | |
| 1089 BuildFileTypeInfo(&file_type_info, suggested_extension, | |
| 1090 options->accepts.get(), options->accepts_all_types.get()); | |
| 1091 } | |
| 1092 | |
| 1093 file_type_info.allowed_paths = ui::SelectFileDialog::FileTypeInfo::ANY_PATH; | |
| 1094 | |
| 1095 base::FilePath previous_path = file_system_api::GetLastChooseEntryDirectory( | |
| 1096 ExtensionPrefs::Get(GetProfile()), extension()->id()); | |
| 1097 | |
| 1098 if (previous_path.empty()) { | |
| 1099 SetInitialPathAndShowPicker(previous_path, suggested_name, file_type_info, | |
| 1100 picker_type, false); | |
| 1101 return true; | |
| 1102 } | |
| 1103 | |
| 1104 base::Callback<void(bool)> set_initial_path_callback = base::Bind( | |
| 1105 &FileSystemChooseEntryFunction::SetInitialPathAndShowPicker, this, | |
| 1106 previous_path, suggested_name, file_type_info, picker_type); | |
| 1107 | |
| 1108 // Check whether the |previous_path| is a non-native directory. | |
| 1109 #if defined(OS_CHROMEOS) | |
| 1110 if (file_manager::util::IsUnderNonNativeLocalPath(GetProfile(), | |
| 1111 previous_path)) { | |
| 1112 file_manager::util::IsNonNativeLocalPathDirectory( | |
| 1113 GetProfile(), previous_path, set_initial_path_callback); | |
| 1114 return true; | |
| 1115 } | |
| 1116 #endif | |
| 1117 base::PostTaskWithTraitsAndReplyWithResult( | |
| 1118 FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND}, | |
| 1119 base::Bind(&base::DirectoryExists, previous_path), | |
| 1120 set_initial_path_callback); | |
| 1121 | |
| 1122 return true; | |
| 1123 } | |
| 1124 | |
| 1125 bool FileSystemRetainEntryFunction::RunAsync() { | |
| 1126 std::string entry_id; | |
| 1127 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &entry_id)); | |
| 1128 SavedFilesService* saved_files_service = SavedFilesService::Get(GetProfile()); | |
| 1129 // Add the file to the retain list if it is not already on there. | |
| 1130 if (!saved_files_service->IsRegistered(extension_->id(), entry_id)) { | |
| 1131 std::string filesystem_name; | |
| 1132 std::string filesystem_path; | |
| 1133 base::FilePath path; | |
| 1134 EXTENSION_FUNCTION_VALIDATE(args_->GetString(1, &filesystem_name)); | |
| 1135 EXTENSION_FUNCTION_VALIDATE(args_->GetString(2, &filesystem_path)); | |
| 1136 if (!app_file_handler_util::ValidateFileEntryAndGetPath( | |
| 1137 filesystem_name, filesystem_path, | |
| 1138 render_frame_host()->GetProcess()->GetID(), &path, &error_)) { | |
| 1139 return false; | |
| 1140 } | |
| 1141 | |
| 1142 std::string filesystem_id; | |
| 1143 if (!storage::CrackIsolatedFileSystemName(filesystem_name, &filesystem_id)) | |
| 1144 return false; | |
| 1145 | |
| 1146 const GURL site = util::GetSiteForExtensionId(extension_id(), GetProfile()); | |
| 1147 storage::FileSystemContext* const context = | |
| 1148 content::BrowserContext::GetStoragePartitionForSite(GetProfile(), site) | |
| 1149 ->GetFileSystemContext(); | |
| 1150 const storage::FileSystemURL url = context->CreateCrackedFileSystemURL( | |
| 1151 site, | |
| 1152 storage::kFileSystemTypeIsolated, | |
| 1153 IsolatedContext::GetInstance() | |
| 1154 ->CreateVirtualRootPath(filesystem_id) | |
| 1155 .Append(base::FilePath::FromUTF8Unsafe(filesystem_path))); | |
| 1156 | |
| 1157 content::BrowserThread::PostTask( | |
| 1158 content::BrowserThread::IO, FROM_HERE, | |
| 1159 base::BindOnce( | |
| 1160 base::IgnoreResult( | |
| 1161 &storage::FileSystemOperationRunner::GetMetadata), | |
| 1162 context->operation_runner()->AsWeakPtr(), url, | |
| 1163 storage::FileSystemOperation::GET_METADATA_FIELD_IS_DIRECTORY, | |
| 1164 base::Bind( | |
| 1165 &PassFileInfoToUIThread, | |
| 1166 base::Bind(&FileSystemRetainEntryFunction::RetainFileEntry, | |
| 1167 this, entry_id, path)))); | |
| 1168 return true; | |
| 1169 } | |
| 1170 | |
| 1171 saved_files_service->EnqueueFileEntry(extension_->id(), entry_id); | |
| 1172 SendResponse(true); | |
| 1173 return true; | |
| 1174 } | |
| 1175 | |
| 1176 void FileSystemRetainEntryFunction::RetainFileEntry( | |
| 1177 const std::string& entry_id, | |
| 1178 const base::FilePath& path, | |
| 1179 std::unique_ptr<base::File::Info> file_info) { | |
| 1180 if (!file_info) { | |
| 1181 SendResponse(false); | |
| 1182 return; | |
| 1183 } | |
| 1184 | |
| 1185 SavedFilesService* saved_files_service = SavedFilesService::Get(GetProfile()); | |
| 1186 saved_files_service->RegisterFileEntry( | |
| 1187 extension_->id(), entry_id, path, file_info->is_directory); | |
| 1188 saved_files_service->EnqueueFileEntry(extension_->id(), entry_id); | |
| 1189 SendResponse(true); | |
| 1190 } | |
| 1191 | |
| 1192 ExtensionFunction::ResponseAction FileSystemIsRestorableFunction::Run() { | |
| 1193 std::string entry_id; | |
| 1194 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &entry_id)); | |
| 1195 return RespondNow(OneArgument(base::MakeUnique<base::Value>( | |
| 1196 SavedFilesService::Get(Profile::FromBrowserContext(browser_context())) | |
| 1197 ->IsRegistered(extension_->id(), entry_id)))); | |
| 1198 } | |
| 1199 | |
| 1200 bool FileSystemRestoreEntryFunction::RunAsync() { | |
| 1201 std::string entry_id; | |
| 1202 bool needs_new_entry; | |
| 1203 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &entry_id)); | |
| 1204 EXTENSION_FUNCTION_VALIDATE(args_->GetBoolean(1, &needs_new_entry)); | |
| 1205 const SavedFileEntry* file_entry = SavedFilesService::Get( | |
| 1206 GetProfile())->GetFileEntry(extension_->id(), entry_id); | |
| 1207 if (!file_entry) { | |
| 1208 error_ = kUnknownIdError; | |
| 1209 return false; | |
| 1210 } | |
| 1211 | |
| 1212 SavedFilesService::Get(GetProfile()) | |
| 1213 ->EnqueueFileEntry(extension_->id(), entry_id); | |
| 1214 | |
| 1215 // Only create a new file entry if the renderer requests one. | |
| 1216 // |needs_new_entry| will be false if the renderer already has an Entry for | |
| 1217 // |entry_id|. | |
| 1218 if (needs_new_entry) { | |
| 1219 is_directory_ = file_entry->is_directory; | |
| 1220 std::unique_ptr<base::DictionaryValue> result = CreateResult(); | |
| 1221 AddEntryToResult(file_entry->path, file_entry->id, result.get()); | |
| 1222 SetResult(std::move(result)); | |
| 1223 } | |
| 1224 SendResponse(true); | |
| 1225 return true; | |
| 1226 } | |
| 1227 | |
| 1228 ExtensionFunction::ResponseAction FileSystemObserveDirectoryFunction::Run() { | |
| 1229 NOTIMPLEMENTED(); | |
| 1230 return RespondNow(Error(kUnknownIdError)); | |
| 1231 } | |
| 1232 | |
| 1233 ExtensionFunction::ResponseAction FileSystemUnobserveEntryFunction::Run() { | |
| 1234 NOTIMPLEMENTED(); | |
| 1235 return RespondNow(Error(kUnknownIdError)); | |
| 1236 } | |
| 1237 | |
| 1238 ExtensionFunction::ResponseAction FileSystemGetObservedEntriesFunction::Run() { | |
| 1239 NOTIMPLEMENTED(); | |
| 1240 return RespondNow(Error(kUnknownIdError)); | |
| 1241 } | |
| 1242 | |
| 1243 #if !defined(OS_CHROMEOS) | |
| 1244 ExtensionFunction::ResponseAction FileSystemRequestFileSystemFunction::Run() { | |
| 1245 using api::file_system::RequestFileSystem::Params; | |
| 1246 const std::unique_ptr<Params> params(Params::Create(*args_)); | |
| 1247 EXTENSION_FUNCTION_VALIDATE(params); | |
| 1248 | |
| 1249 NOTIMPLEMENTED(); | |
| 1250 return RespondNow(Error(kNotSupportedOnCurrentPlatformError)); | |
| 1251 } | |
| 1252 | |
| 1253 ExtensionFunction::ResponseAction FileSystemGetVolumeListFunction::Run() { | |
| 1254 NOTIMPLEMENTED(); | |
| 1255 return RespondNow(Error(kNotSupportedOnCurrentPlatformError)); | |
| 1256 } | |
| 1257 #else | |
| 1258 | |
| 1259 FileSystemRequestFileSystemFunction::FileSystemRequestFileSystemFunction() | |
| 1260 : chrome_details_(this) { | |
| 1261 } | |
| 1262 | |
| 1263 FileSystemRequestFileSystemFunction::~FileSystemRequestFileSystemFunction() { | |
| 1264 } | |
| 1265 | |
| 1266 ExtensionFunction::ResponseAction FileSystemRequestFileSystemFunction::Run() { | |
| 1267 using api::file_system::RequestFileSystem::Params; | |
| 1268 const std::unique_ptr<Params> params(Params::Create(*args_)); | |
| 1269 EXTENSION_FUNCTION_VALIDATE(params); | |
| 1270 | |
| 1271 // Only kiosk apps in kiosk sessions can use this API. | |
| 1272 // Additionally it is enabled for whitelisted component extensions and apps. | |
| 1273 file_system_api::ConsentProviderDelegate consent_provider_delegate( | |
| 1274 chrome_details_.GetProfile(), render_frame_host()); | |
| 1275 file_system_api::ConsentProvider consent_provider(&consent_provider_delegate); | |
| 1276 | |
| 1277 if (!consent_provider.IsGrantable(*extension())) | |
| 1278 return RespondNow(Error(kNotSupportedOnNonKioskSessionError)); | |
| 1279 | |
| 1280 using file_manager::VolumeManager; | |
| 1281 using file_manager::Volume; | |
| 1282 VolumeManager* const volume_manager = | |
| 1283 VolumeManager::Get(chrome_details_.GetProfile()); | |
| 1284 DCHECK(volume_manager); | |
| 1285 | |
| 1286 const bool writable = | |
| 1287 params->options.writable.get() && *params->options.writable.get(); | |
| 1288 if (writable && | |
| 1289 !app_file_handler_util::HasFileSystemWritePermission(extension_.get())) { | |
| 1290 return RespondNow(Error(kRequiresFileSystemWriteError)); | |
| 1291 } | |
| 1292 | |
| 1293 base::WeakPtr<file_manager::Volume> volume = | |
| 1294 volume_manager->FindVolumeById(params->options.volume_id); | |
| 1295 if (!volume.get()) | |
| 1296 return RespondNow(Error(kVolumeNotFoundError)); | |
| 1297 | |
| 1298 const GURL site = | |
| 1299 util::GetSiteForExtensionId(extension_id(), chrome_details_.GetProfile()); | |
| 1300 scoped_refptr<storage::FileSystemContext> file_system_context = | |
| 1301 content::BrowserContext::GetStoragePartitionForSite( | |
| 1302 chrome_details_.GetProfile(), site)->GetFileSystemContext(); | |
| 1303 storage::ExternalFileSystemBackend* const backend = | |
| 1304 file_system_context->external_backend(); | |
| 1305 DCHECK(backend); | |
| 1306 | |
| 1307 base::FilePath virtual_path; | |
| 1308 if (!backend->GetVirtualPath(volume->mount_path(), &virtual_path)) | |
| 1309 return RespondNow(Error(kSecurityError)); | |
| 1310 | |
| 1311 if (writable && (volume->is_read_only())) | |
| 1312 return RespondNow(Error(kSecurityError)); | |
| 1313 | |
| 1314 consent_provider.RequestConsent( | |
| 1315 *extension(), volume, writable, | |
| 1316 base::Bind(&FileSystemRequestFileSystemFunction::OnConsentReceived, this, | |
| 1317 volume, writable)); | |
| 1318 return RespondLater(); | |
| 1319 } | |
| 1320 | |
| 1321 void FileSystemRequestFileSystemFunction::OnConsentReceived( | |
| 1322 const base::WeakPtr<file_manager::Volume>& volume, | |
| 1323 bool writable, | |
| 1324 ConsentProvider::Consent result) { | |
| 1325 using file_manager::VolumeManager; | |
| 1326 using file_manager::Volume; | |
| 1327 | |
| 1328 // Render frame host can be gone before this callback method is executed. | |
| 1329 if (!render_frame_host()) { | |
| 1330 Respond(Error("")); | |
| 1331 return; | |
| 1332 } | |
| 1333 | |
| 1334 switch (result) { | |
| 1335 case ConsentProvider::CONSENT_REJECTED: | |
| 1336 Respond(Error(kSecurityError)); | |
| 1337 return; | |
| 1338 | |
| 1339 case ConsentProvider::CONSENT_IMPOSSIBLE: | |
| 1340 Respond(Error(kConsentImpossible)); | |
| 1341 return; | |
| 1342 | |
| 1343 case ConsentProvider::CONSENT_GRANTED: | |
| 1344 break; | |
| 1345 } | |
| 1346 | |
| 1347 if (!volume.get()) { | |
| 1348 Respond(Error(kVolumeNotFoundError)); | |
| 1349 return; | |
| 1350 } | |
| 1351 | |
| 1352 const GURL site = | |
| 1353 util::GetSiteForExtensionId(extension_id(), chrome_details_.GetProfile()); | |
| 1354 scoped_refptr<storage::FileSystemContext> file_system_context = | |
| 1355 content::BrowserContext::GetStoragePartitionForSite( | |
| 1356 chrome_details_.GetProfile(), site)->GetFileSystemContext(); | |
| 1357 storage::ExternalFileSystemBackend* const backend = | |
| 1358 file_system_context->external_backend(); | |
| 1359 DCHECK(backend); | |
| 1360 | |
| 1361 base::FilePath virtual_path; | |
| 1362 if (!backend->GetVirtualPath(volume->mount_path(), &virtual_path)) { | |
| 1363 Respond(Error(kSecurityError)); | |
| 1364 return; | |
| 1365 } | |
| 1366 | |
| 1367 storage::IsolatedContext* const isolated_context = | |
| 1368 storage::IsolatedContext::GetInstance(); | |
| 1369 DCHECK(isolated_context); | |
| 1370 | |
| 1371 const storage::FileSystemURL original_url = | |
| 1372 file_system_context->CreateCrackedFileSystemURL( | |
| 1373 GURL(std::string(kExtensionScheme) + url::kStandardSchemeSeparator + | |
| 1374 extension_id()), | |
| 1375 storage::kFileSystemTypeExternal, virtual_path); | |
| 1376 | |
| 1377 // Set a fixed register name, as the automatic one would leak the mount point | |
| 1378 // directory. | |
| 1379 std::string register_name = "fs"; | |
| 1380 const std::string file_system_id = | |
| 1381 isolated_context->RegisterFileSystemForPath( | |
| 1382 storage::kFileSystemTypeNativeForPlatformApp, | |
| 1383 std::string() /* file_system_id */, original_url.path(), | |
| 1384 ®ister_name); | |
| 1385 if (file_system_id.empty()) { | |
| 1386 Respond(Error(kSecurityError)); | |
| 1387 return; | |
| 1388 } | |
| 1389 | |
| 1390 backend->GrantFileAccessToExtension(extension_->id(), virtual_path); | |
| 1391 | |
| 1392 // Grant file permissions to the renderer hosting component. | |
| 1393 content::ChildProcessSecurityPolicy* policy = | |
| 1394 content::ChildProcessSecurityPolicy::GetInstance(); | |
| 1395 DCHECK(policy); | |
| 1396 | |
| 1397 // Read-only permisisons. | |
| 1398 policy->GrantReadFile(render_frame_host()->GetProcess()->GetID(), | |
| 1399 volume->mount_path()); | |
| 1400 policy->GrantReadFileSystem(render_frame_host()->GetProcess()->GetID(), | |
| 1401 file_system_id); | |
| 1402 | |
| 1403 // Additional write permissions. | |
| 1404 if (writable) { | |
| 1405 policy->GrantCreateReadWriteFile(render_frame_host()->GetProcess()->GetID(), | |
| 1406 volume->mount_path()); | |
| 1407 policy->GrantCopyInto(render_frame_host()->GetProcess()->GetID(), | |
| 1408 volume->mount_path()); | |
| 1409 policy->GrantWriteFileSystem(render_frame_host()->GetProcess()->GetID(), | |
| 1410 file_system_id); | |
| 1411 policy->GrantDeleteFromFileSystem( | |
| 1412 render_frame_host()->GetProcess()->GetID(), file_system_id); | |
| 1413 policy->GrantCreateFileForFileSystem( | |
| 1414 render_frame_host()->GetProcess()->GetID(), file_system_id); | |
| 1415 } | |
| 1416 | |
| 1417 std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue()); | |
| 1418 dict->SetString("file_system_id", file_system_id); | |
| 1419 dict->SetString("file_system_path", register_name); | |
| 1420 | |
| 1421 Respond(OneArgument(std::move(dict))); | |
| 1422 } | |
| 1423 | |
| 1424 FileSystemGetVolumeListFunction::FileSystemGetVolumeListFunction() | |
| 1425 : chrome_details_(this) { | |
| 1426 } | |
| 1427 | |
| 1428 FileSystemGetVolumeListFunction::~FileSystemGetVolumeListFunction() { | |
| 1429 } | |
| 1430 | |
| 1431 ExtensionFunction::ResponseAction FileSystemGetVolumeListFunction::Run() { | |
| 1432 // Only kiosk apps in kiosk sessions can use this API. | |
| 1433 // Additionally it is enabled for whitelisted component extensions and apps. | |
| 1434 file_system_api::ConsentProviderDelegate consent_provider_delegate( | |
| 1435 chrome_details_.GetProfile(), render_frame_host()); | |
| 1436 file_system_api::ConsentProvider consent_provider(&consent_provider_delegate); | |
| 1437 | |
| 1438 if (!consent_provider.IsGrantable(*extension())) | |
| 1439 return RespondNow(Error(kNotSupportedOnNonKioskSessionError)); | |
| 1440 std::vector<api::file_system::Volume> result_volume_list; | |
| 1441 FillVolumeList(chrome_details_.GetProfile(), &result_volume_list); | |
| 1442 | |
| 1443 return RespondNow(ArgumentList( | |
| 1444 api::file_system::GetVolumeList::Results::Create(result_volume_list))); | |
| 1445 } | |
| 1446 #endif | |
| 1447 | |
| 1448 } // namespace extensions | 222 } // namespace extensions |
| OLD | NEW |