| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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/file_system_api.h" |
| 6 | 6 |
| 7 #include <stddef.h> | 7 #include <stddef.h> |
| 8 | 8 |
| 9 #include <memory> | 9 #include <memory> |
| 10 #include <set> | 10 #include <set> |
| 11 #include <utility> | 11 #include <utility> |
| 12 #include <vector> | 12 #include <vector> |
| 13 | 13 |
| 14 #include "apps/saved_files_service.h" | 14 #include "apps/saved_files_service.h" |
| 15 #include "base/bind.h" | 15 #include "base/bind.h" |
| 16 #include "base/files/file_path.h" | 16 #include "base/files/file_path.h" |
| 17 #include "base/files/file_util.h" | 17 #include "base/files/file_util.h" |
| 18 #include "base/logging.h" | |
| 19 #include "base/macros.h" | 18 #include "base/macros.h" |
| 20 #include "base/memory/ptr_util.h" | 19 #include "base/memory/ptr_util.h" |
| 21 #include "base/path_service.h" | 20 #include "base/path_service.h" |
| 22 #include "base/strings/string_util.h" | 21 #include "base/strings/string_util.h" |
| 23 #include "base/strings/stringprintf.h" | 22 #include "base/strings/stringprintf.h" |
| 24 #include "base/strings/sys_string_conversions.h" | 23 #include "base/strings/sys_string_conversions.h" |
| 25 #include "base/strings/utf_string_conversions.h" | 24 #include "base/strings/utf_string_conversions.h" |
| 26 #include "base/task_scheduler/post_task.h" | 25 #include "base/task_scheduler/post_task.h" |
| 27 #include "base/value_conversions.h" | 26 #include "base/value_conversions.h" |
| 28 #include "base/values.h" | 27 #include "base/values.h" |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 61 #include "ui/shell_dialogs/select_file_dialog.h" | 60 #include "ui/shell_dialogs/select_file_dialog.h" |
| 62 #include "ui/shell_dialogs/selected_file_info.h" | 61 #include "ui/shell_dialogs/selected_file_info.h" |
| 63 | 62 |
| 64 #if defined(OS_MACOSX) | 63 #if defined(OS_MACOSX) |
| 65 #include <CoreFoundation/CoreFoundation.h> | 64 #include <CoreFoundation/CoreFoundation.h> |
| 66 #include "base/mac/foundation_util.h" | 65 #include "base/mac/foundation_util.h" |
| 67 #endif | 66 #endif |
| 68 | 67 |
| 69 #if defined(OS_CHROMEOS) | 68 #if defined(OS_CHROMEOS) |
| 70 #include "base/strings/string16.h" | 69 #include "base/strings/string16.h" |
| 71 #include "base/threading/thread_task_runner_handle.h" | |
| 72 #include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h" | |
| 73 #include "chrome/browser/chromeos/file_manager/app_id.h" | |
| 74 #include "chrome/browser/chromeos/file_manager/filesystem_api_util.h" | 70 #include "chrome/browser/chromeos/file_manager/filesystem_api_util.h" |
| 75 #include "chrome/browser/chromeos/file_manager/volume_manager.h" | 71 #include "chrome/browser/chromeos/file_manager/volume_manager.h" |
| 76 #include "chrome/browser/extensions/api/file_system/request_file_system_notifica
tion.h" | |
| 77 #include "chrome/browser/ui/simple_message_box.h" | |
| 78 #include "chrome/browser/ui/views/extensions/request_file_system_dialog_view.h" | |
| 79 #include "components/user_manager/user_manager.h" | |
| 80 #include "extensions/browser/event_router.h" | 72 #include "extensions/browser/event_router.h" |
| 81 #include "extensions/browser/extension_registry.h" | 73 #include "extensions/browser/extension_registry.h" |
| 82 #include "extensions/common/constants.h" | 74 #include "extensions/common/constants.h" |
| 83 #include "extensions/common/manifest_handlers/kiosk_mode_info.h" | |
| 84 #include "url/url_constants.h" | 75 #include "url/url_constants.h" |
| 85 #endif | 76 #endif |
| 86 | 77 |
| 87 using apps::SavedFileEntry; | 78 using apps::SavedFileEntry; |
| 88 using apps::SavedFilesService; | 79 using apps::SavedFilesService; |
| 89 using storage::IsolatedContext; | 80 using storage::IsolatedContext; |
| 90 | 81 |
| 91 const char kInvalidCallingPage[] = "Invalid calling page. This function can't " | 82 const char kInvalidCallingPage[] = "Invalid calling page. This function can't " |
| 92 "be called from a background page."; | 83 "be called from a background page."; |
| 93 const char kUserCancelled[] = "User cancelled"; | 84 const char kUserCancelled[] = "User cancelled"; |
| 94 const char kWritableFileErrorFormat[] = "Error opening %s"; | 85 const char kWritableFileErrorFormat[] = "Error opening %s"; |
| 95 const char kRequiresFileSystemWriteError[] = | 86 const char kRequiresFileSystemWriteError[] = |
| 96 "Operation requires fileSystem.write permission"; | 87 "Operation requires fileSystem.write permission"; |
| 97 const char kRequiresFileSystemDirectoryError[] = | 88 const char kRequiresFileSystemDirectoryError[] = |
| 98 "Operation requires fileSystem.directory permission"; | 89 "Operation requires fileSystem.directory permission"; |
| 99 const char kMultipleUnsupportedError[] = | 90 const char kMultipleUnsupportedError[] = |
| 100 "acceptsMultiple: true is only supported for 'openFile'"; | 91 "acceptsMultiple: true is only supported for 'openFile'"; |
| 101 const char kUnknownIdError[] = "Unknown id"; | 92 const char kUnknownIdError[] = "Unknown id"; |
| 102 | 93 |
| 103 #if !defined(OS_CHROMEOS) | 94 #if !defined(OS_CHROMEOS) |
| 104 const char kNotSupportedOnCurrentPlatformError[] = | 95 const char kNotSupportedOnCurrentPlatformError[] = |
| 105 "Operation not supported on the current platform."; | 96 "Operation not supported on the current platform."; |
| 106 #else | 97 #else |
| 107 const char kNotSupportedOnNonKioskSessionError[] = | 98 const char kNotSupportedOnNonKioskSessionError[] = |
| 108 "Operation only supported for kiosk apps running in a kiosk session."; | 99 "Operation only supported for kiosk apps running in a kiosk session."; |
| 109 const char kVolumeNotFoundError[] = "Volume not found."; | 100 const char kVolumeNotFoundError[] = "Volume not found."; |
| 110 const char kSecurityError[] = "Security error."; | 101 const char kSecurityError[] = "Security error."; |
| 111 const char kConsentImpossible[] = | 102 const char kConsentImpossible[] = |
| 112 "Impossible to ask for user consent as there is no app window visible."; | 103 "Impossible to ask for user consent as there is no app window visible."; |
| 113 | |
| 114 // List of whitelisted component apps and extensions by their ids for | |
| 115 // chrome.fileSystem.requestFileSystem. | |
| 116 const char* const kRequestFileSystemComponentWhitelist[] = { | |
| 117 file_manager::kFileManagerAppId, | |
| 118 file_manager::kVideoPlayerAppId, | |
| 119 file_manager::kGalleryAppId, | |
| 120 file_manager::kAudioPlayerAppId, | |
| 121 file_manager::kImageLoaderExtensionId, | |
| 122 // TODO(mtomasz): Remove this extension id, and add it only for tests. | |
| 123 "pkplfbidichfdicaijlchgnapepdginl" // Testing extensions. | |
| 124 }; | |
| 125 #endif | 104 #endif |
| 126 | 105 |
| 127 namespace extensions { | 106 namespace extensions { |
| 128 | 107 |
| 129 namespace file_system = api::file_system; | 108 namespace file_system = api::file_system; |
| 130 namespace ChooseEntry = file_system::ChooseEntry; | 109 namespace ChooseEntry = file_system::ChooseEntry; |
| 131 | 110 |
| 132 namespace { | 111 namespace { |
| 133 | 112 |
| 134 bool g_skip_picker_for_test = false; | 113 bool g_skip_picker_for_test = false; |
| 135 bool g_use_suggested_path_for_test = false; | 114 bool g_use_suggested_path_for_test = false; |
| 136 base::FilePath* g_path_to_be_picked_for_test; | 115 base::FilePath* g_path_to_be_picked_for_test; |
| 137 std::vector<base::FilePath>* g_paths_to_be_picked_for_test; | 116 std::vector<base::FilePath>* g_paths_to_be_picked_for_test; |
| 138 bool g_skip_directory_confirmation_for_test = false; | 117 bool g_skip_directory_confirmation_for_test = false; |
| 139 bool g_allow_directory_access_for_test = false; | 118 bool g_allow_directory_access_for_test = false; |
| 140 | 119 |
| 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 | 120 // Expand the mime-types and extensions provided in an AcceptOption, returning |
| 146 // them within the passed extension vector. Returns false if no valid types | 121 // them within the passed extension vector. Returns false if no valid types |
| 147 // were found. | 122 // were found. |
| 148 bool GetFileTypesFromAcceptOption( | 123 bool GetFileTypesFromAcceptOption( |
| 149 const file_system::AcceptOption& accept_option, | 124 const file_system::AcceptOption& accept_option, |
| 150 std::vector<base::FilePath::StringType>* extensions, | 125 std::vector<base::FilePath::StringType>* extensions, |
| 151 base::string16* description) { | 126 base::string16* description) { |
| 152 std::set<base::FilePath::StringType> extension_set; | 127 std::set<base::FilePath::StringType> extension_set; |
| 153 int description_id = 0; | 128 int description_id = 0; |
| 154 | 129 |
| (...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 240 content::WebContents::FromRenderFrameHost(render_frame_host); | 215 content::WebContents::FromRenderFrameHost(render_frame_host); |
| 241 // Check if there is an app window associated with the web contents; if not, | 216 // Check if there is an app window associated with the web contents; if not, |
| 242 // return null. | 217 // return null. |
| 243 return AppWindowRegistry::Get(profile) | 218 return AppWindowRegistry::Get(profile) |
| 244 ->GetAppWindowForWebContents(web_contents) | 219 ->GetAppWindowForWebContents(web_contents) |
| 245 ? web_contents | 220 ? web_contents |
| 246 : nullptr; | 221 : nullptr; |
| 247 } | 222 } |
| 248 | 223 |
| 249 #if defined(OS_CHROMEOS) | 224 #if defined(OS_CHROMEOS) |
| 250 // Gets a WebContents instance handle for a current window of a platform app | |
| 251 // with |app_id|. If not found, then returns NULL. | |
| 252 content::WebContents* GetWebContentsForAppId(Profile* profile, | |
| 253 const std::string& app_id) { | |
| 254 AppWindowRegistry* const registry = AppWindowRegistry::Get(profile); | |
| 255 DCHECK(registry); | |
| 256 AppWindow* const app_window = registry->GetCurrentAppWindowForApp(app_id); | |
| 257 return app_window ? app_window->web_contents() : nullptr; | |
| 258 } | |
| 259 | |
| 260 // Fills a list of volumes mounted in the system. | 225 // Fills a list of volumes mounted in the system. |
| 261 void FillVolumeList(Profile* profile, | 226 void FillVolumeList(Profile* profile, |
| 262 std::vector<api::file_system::Volume>* result) { | 227 std::vector<api::file_system::Volume>* result) { |
| 263 file_manager::VolumeManager* const volume_manager = | 228 file_manager::VolumeManager* const volume_manager = |
| 264 file_manager::VolumeManager::Get(profile); | 229 file_manager::VolumeManager::Get(profile); |
| 265 DCHECK(volume_manager); | 230 DCHECK(volume_manager); |
| 266 | 231 |
| 267 const auto& volume_list = volume_manager->GetVolumeList(); | 232 const auto& volume_list = volume_manager->GetVolumeList(); |
| 268 // Convert volume_list to result_volume_list. | 233 // Convert volume_list to result_volume_list. |
| 269 for (const auto& volume : volume_list) { | 234 for (const auto& volume : volume_list) { |
| 270 api::file_system::Volume result_volume; | 235 api::file_system::Volume result_volume; |
| 271 result_volume.volume_id = volume->volume_id(); | 236 result_volume.volume_id = volume->volume_id(); |
| 272 result_volume.writable = !volume->is_read_only(); | 237 result_volume.writable = !volume->is_read_only(); |
| 273 result->push_back(std::move(result_volume)); | 238 result->push_back(std::move(result_volume)); |
| 274 } | 239 } |
| 275 } | 240 } |
| 276 | |
| 277 // Converts the clicked button to a consent result and passes it via the | |
| 278 // |callback|. | |
| 279 void DialogResultToConsent( | |
| 280 const file_system_api::ConsentProvider::ConsentCallback& callback, | |
| 281 ui::DialogButton button) { | |
| 282 switch (button) { | |
| 283 case ui::DIALOG_BUTTON_NONE: | |
| 284 callback.Run(file_system_api::ConsentProvider::CONSENT_IMPOSSIBLE); | |
| 285 break; | |
| 286 case ui::DIALOG_BUTTON_OK: | |
| 287 callback.Run(file_system_api::ConsentProvider::CONSENT_GRANTED); | |
| 288 break; | |
| 289 case ui::DIALOG_BUTTON_CANCEL: | |
| 290 callback.Run(file_system_api::ConsentProvider::CONSENT_REJECTED); | |
| 291 break; | |
| 292 } | |
| 293 } | |
| 294 #endif | 241 #endif |
| 295 | 242 |
| 296 } // namespace | 243 } // namespace |
| 297 | 244 |
| 298 namespace file_system_api { | 245 namespace file_system_api { |
| 299 | 246 |
| 300 base::FilePath GetLastChooseEntryDirectory(const ExtensionPrefs* prefs, | 247 base::FilePath GetLastChooseEntryDirectory(const ExtensionPrefs* prefs, |
| 301 const std::string& extension_id) { | 248 const std::string& extension_id) { |
| 302 base::FilePath path; | 249 base::FilePath path; |
| 303 std::string string_path; | 250 std::string string_path; |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 335 if (!consent_provider.IsGrantable(*extension.get())) | 282 if (!consent_provider.IsGrantable(*extension.get())) |
| 336 continue; | 283 continue; |
| 337 event_router->DispatchEventToExtension( | 284 event_router->DispatchEventToExtension( |
| 338 extension->id(), | 285 extension->id(), |
| 339 base::MakeUnique<Event>( | 286 base::MakeUnique<Event>( |
| 340 events::FILE_SYSTEM_ON_VOLUME_LIST_CHANGED, | 287 events::FILE_SYSTEM_ON_VOLUME_LIST_CHANGED, |
| 341 api::file_system::OnVolumeListChanged::kEventName, | 288 api::file_system::OnVolumeListChanged::kEventName, |
| 342 api::file_system::OnVolumeListChanged::Create(event_args))); | 289 api::file_system::OnVolumeListChanged::Create(event_args))); |
| 343 } | 290 } |
| 344 } | 291 } |
| 345 | |
| 346 ConsentProvider::ConsentProvider(DelegateInterface* delegate) | |
| 347 : delegate_(delegate) { | |
| 348 DCHECK(delegate_); | |
| 349 } | |
| 350 | |
| 351 ConsentProvider::~ConsentProvider() { | |
| 352 } | |
| 353 | |
| 354 void ConsentProvider::RequestConsent( | |
| 355 const Extension& extension, | |
| 356 const base::WeakPtr<file_manager::Volume>& volume, | |
| 357 bool writable, | |
| 358 const ConsentCallback& callback) { | |
| 359 DCHECK(IsGrantable(extension)); | |
| 360 | |
| 361 // If a whitelisted component, then no need to ask or inform the user. | |
| 362 if (extension.location() == Manifest::COMPONENT && | |
| 363 delegate_->IsWhitelistedComponent(extension)) { | |
| 364 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 365 FROM_HERE, base::Bind(callback, CONSENT_GRANTED)); | |
| 366 return; | |
| 367 } | |
| 368 | |
| 369 // If auto-launched kiosk app, then no need to ask user either, but show the | |
| 370 // notification. | |
| 371 if (delegate_->IsAutoLaunched(extension)) { | |
| 372 delegate_->ShowNotification(extension, volume, writable); | |
| 373 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 374 FROM_HERE, base::Bind(callback, CONSENT_GRANTED)); | |
| 375 return; | |
| 376 } | |
| 377 | |
| 378 // If it's a kiosk app running in manual-launch kiosk session, then show | |
| 379 // the confirmation dialog. | |
| 380 if (KioskModeInfo::IsKioskOnly(&extension) && | |
| 381 user_manager::UserManager::Get()->IsLoggedInAsKioskApp()) { | |
| 382 delegate_->ShowDialog(extension, volume, writable, | |
| 383 base::Bind(&DialogResultToConsent, callback)); | |
| 384 return; | |
| 385 } | |
| 386 | |
| 387 NOTREACHED() << "Cannot request consent for non-grantable extensions."; | |
| 388 } | |
| 389 | |
| 390 bool ConsentProvider::IsGrantable(const Extension& extension) { | |
| 391 const bool is_whitelisted_component = | |
| 392 delegate_->IsWhitelistedComponent(extension); | |
| 393 | |
| 394 const bool is_running_in_kiosk_session = | |
| 395 KioskModeInfo::IsKioskOnly(&extension) && | |
| 396 user_manager::UserManager::Get()->IsLoggedInAsKioskApp(); | |
| 397 | |
| 398 return is_whitelisted_component || is_running_in_kiosk_session; | |
| 399 } | |
| 400 | |
| 401 ConsentProviderDelegate::ConsentProviderDelegate(Profile* profile, | |
| 402 content::RenderFrameHost* host) | |
| 403 : profile_(profile), host_(host) { | |
| 404 DCHECK(profile_); | |
| 405 } | |
| 406 | |
| 407 ConsentProviderDelegate::~ConsentProviderDelegate() { | |
| 408 } | |
| 409 | |
| 410 // static | |
| 411 void ConsentProviderDelegate::SetAutoDialogButtonForTest( | |
| 412 ui::DialogButton button) { | |
| 413 g_auto_dialog_button_for_test = button; | |
| 414 } | |
| 415 | |
| 416 void ConsentProviderDelegate::ShowDialog( | |
| 417 const Extension& extension, | |
| 418 const base::WeakPtr<file_manager::Volume>& volume, | |
| 419 bool writable, | |
| 420 const file_system_api::ConsentProvider::ShowDialogCallback& callback) { | |
| 421 DCHECK(host_); | |
| 422 content::WebContents* const foreground_contents = | |
| 423 GetWebContentsForRenderFrameHost(profile_, host_); | |
| 424 // 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. | |
| 426 content::WebContents* const web_contents = | |
| 427 foreground_contents ? foreground_contents | |
| 428 : GetWebContentsForAppId(profile_, extension.id()); | |
| 429 if (!web_contents) { | |
| 430 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 431 FROM_HERE, base::Bind(callback, ui::DIALOG_BUTTON_NONE)); | |
| 432 return; | |
| 433 } | |
| 434 | |
| 435 // Short circuit the user consent dialog for tests. This is far from a pretty | |
| 436 // code design. | |
| 437 if (g_auto_dialog_button_for_test != ui::DIALOG_BUTTON_NONE) { | |
| 438 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 439 FROM_HERE, | |
| 440 base::Bind(callback, g_auto_dialog_button_for_test /* result */)); | |
| 441 return; | |
| 442 } | |
| 443 | |
| 444 // If the volume is gone, then cancel the dialog. | |
| 445 if (!volume.get()) { | |
| 446 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 447 FROM_HERE, base::Bind(callback, ui::DIALOG_BUTTON_CANCEL)); | |
| 448 return; | |
| 449 } | |
| 450 | |
| 451 RequestFileSystemDialogView::ShowDialog( | |
| 452 web_contents, extension.name(), | |
| 453 (volume->volume_label().empty() ? volume->volume_id() | |
| 454 : volume->volume_label()), | |
| 455 writable, callback); | |
| 456 } | |
| 457 | |
| 458 void ConsentProviderDelegate::ShowNotification( | |
| 459 const Extension& extension, | |
| 460 const base::WeakPtr<file_manager::Volume>& volume, | |
| 461 bool writable) { | |
| 462 RequestFileSystemNotification::ShowAutoGrantedNotification( | |
| 463 profile_, extension, volume, writable); | |
| 464 } | |
| 465 | |
| 466 bool ConsentProviderDelegate::IsAutoLaunched(const Extension& extension) { | |
| 467 chromeos::KioskAppManager::App app_info; | |
| 468 return chromeos::KioskAppManager::Get()->GetApp(extension.id(), &app_info) && | |
| 469 app_info.was_auto_launched_with_zero_delay; | |
| 470 } | |
| 471 | |
| 472 bool ConsentProviderDelegate::IsWhitelistedComponent( | |
| 473 const Extension& extension) { | |
| 474 for (auto* whitelisted_id : kRequestFileSystemComponentWhitelist) { | |
| 475 if (extension.id().compare(whitelisted_id) == 0) | |
| 476 return true; | |
| 477 } | |
| 478 return false; | |
| 479 } | |
| 480 | |
| 481 #endif | 292 #endif |
| 482 | 293 |
| 483 } // namespace file_system_api | 294 } // namespace file_system_api |
| 484 | 295 |
| 485 #if defined(OS_CHROMEOS) | 296 #if defined(OS_CHROMEOS) |
| 486 using file_system_api::ConsentProvider; | 297 using file_system_api::ConsentProvider; |
| 487 #endif | 298 #endif |
| 488 | 299 |
| 489 ExtensionFunction::ResponseAction FileSystemGetDisplayPathFunction::Run() { | 300 ExtensionFunction::ResponseAction FileSystemGetDisplayPathFunction::Run() { |
| 490 std::string filesystem_name; | 301 std::string filesystem_name; |
| (...skipping 948 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1439 return RespondNow(Error(kNotSupportedOnNonKioskSessionError)); | 1250 return RespondNow(Error(kNotSupportedOnNonKioskSessionError)); |
| 1440 std::vector<api::file_system::Volume> result_volume_list; | 1251 std::vector<api::file_system::Volume> result_volume_list; |
| 1441 FillVolumeList(chrome_details_.GetProfile(), &result_volume_list); | 1252 FillVolumeList(chrome_details_.GetProfile(), &result_volume_list); |
| 1442 | 1253 |
| 1443 return RespondNow(ArgumentList( | 1254 return RespondNow(ArgumentList( |
| 1444 api::file_system::GetVolumeList::Results::Create(result_volume_list))); | 1255 api::file_system::GetVolumeList::Results::Create(result_volume_list))); |
| 1445 } | 1256 } |
| 1446 #endif | 1257 #endif |
| 1447 | 1258 |
| 1448 } // namespace extensions | 1259 } // namespace extensions |
| OLD | NEW |