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
3 // found in the LICENSE file.
5 #include "chrome/browser/extensions/api/file_system/file_system_api.h"
10 #include "apps/saved_files_service.h"
11 #include "base/bind.h"
12 #include "base/files/file_path.h"
13 #include "base/files/file_util.h"
14 #include "base/logging.h"
15 #include "base/memory/linked_ptr.h"
16 #include "base/path_service.h"
17 #include "base/strings/string_util.h"
18 #include "base/strings/stringprintf.h"
19 #include "base/strings/sys_string_conversions.h"
20 #include "base/strings/utf_string_conversions.h"
21 #include "base/value_conversions.h"
22 #include "base/values.h"
23 #include "chrome/browser/extensions/api/file_handlers/app_file_handler_util.h"
24 #include "chrome/browser/extensions/extension_service.h"
25 #include "chrome/browser/extensions/extension_util.h"
26 #include "chrome/browser/extensions/path_util.h"
27 #include "chrome/browser/platform_util.h"
28 #include "chrome/browser/profiles/profile.h"
29 #include "chrome/browser/ui/apps/directory_access_confirmation_dialog.h"
30 #include "chrome/browser/ui/chrome_select_file_policy.h"
31 #include "chrome/common/chrome_paths.h"
32 #include "chrome/common/extensions/api/file_system.h"
33 #include "chrome/grit/generated_resources.h"
34 #include "content/public/browser/browser_thread.h"
35 #include "content/public/browser/child_process_security_policy.h"
36 #include "content/public/browser/render_frame_host.h"
37 #include "content/public/browser/render_process_host.h"
38 #include "content/public/browser/storage_partition.h"
39 #include "content/public/browser/web_contents.h"
40 #include "extensions/browser/app_window/app_window.h"
41 #include "extensions/browser/app_window/app_window_registry.h"
42 #include "extensions/browser/extension_prefs.h"
43 #include "extensions/browser/extension_system.h"
44 #include "extensions/browser/granted_file_entry.h"
45 #include "extensions/common/permissions/api_permission.h"
46 #include "extensions/common/permissions/permissions_data.h"
47 #include "net/base/mime_util.h"
48 #include "storage/browser/fileapi/external_mount_points.h"
49 #include "storage/browser/fileapi/file_system_operation_runner.h"
50 #include "storage/browser/fileapi/isolated_context.h"
51 #include "storage/common/fileapi/file_system_types.h"
52 #include "storage/common/fileapi/file_system_util.h"
53 #include "ui/base/l10n/l10n_util.h"
54 #include "ui/shell_dialogs/select_file_dialog.h"
55 #include "ui/shell_dialogs/selected_file_info.h"
57 #if defined(OS_MACOSX)
58 #include <CoreFoundation/CoreFoundation.h>
59 #include "base/mac/foundation_util.h"
62 #if defined(OS_CHROMEOS)
63 #include "base/prefs/testing_pref_service.h"
64 #include "base/strings/string16.h"
65 #include "base/thread_task_runner_handle.h"
66 #include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h"
67 #include "chrome/browser/chromeos/file_manager/app_id.h"
68 #include "chrome/browser/chromeos/file_manager/filesystem_api_util.h"
69 #include "chrome/browser/extensions/api/file_system/request_file_system_dialog_view.h"
70 #include "chrome/browser/extensions/api/file_system/request_file_system_notification.h"
71 #include "chrome/browser/ui/simple_message_box.h"
72 #include "components/user_manager/user_manager.h"
73 #include "extensions/browser/event_router.h"
74 #include "extensions/browser/extension_registry.h"
75 #include "extensions/common/constants.h"
76 #include "extensions/common/manifest_handlers/kiosk_mode_info.h"
77 #include "url/url_constants.h"
80 using apps::SavedFileEntry
;
81 using apps::SavedFilesService
;
82 using storage::IsolatedContext
;
84 const char kInvalidCallingPage
[] = "Invalid calling page. This function can't "
85 "be called from a background page.";
86 const char kUserCancelled
[] = "User cancelled";
87 const char kWritableFileErrorFormat
[] = "Error opening %s";
88 const char kRequiresFileSystemWriteError
[] =
89 "Operation requires fileSystem.write permission";
90 const char kRequiresFileSystemDirectoryError
[] =
91 "Operation requires fileSystem.directory permission";
92 const char kMultipleUnsupportedError
[] =
93 "acceptsMultiple: true is not supported for 'saveFile'";
94 const char kUnknownIdError
[] = "Unknown id";
96 #if !defined(OS_CHROMEOS)
97 const char kNotSupportedOnCurrentPlatformError
[] =
98 "Operation not supported on the current platform.";
100 const char kNotSupportedOnNonKioskSessionError
[] =
101 "Operation only supported for kiosk apps running in a kiosk session.";
102 const char kVolumeNotFoundError
[] = "Volume not found.";
103 const char kSecurityError
[] = "Security error.";
104 const char kConsentImpossible
[] =
105 "Impossible to ask for user consent as there is no app window visible.";
107 // List of whitelisted component apps and extensions by their ids for
108 // chrome.fileSystem.requestFileSystem.
109 const char* const kRequestFileSystemComponentWhitelist
[] = {
110 file_manager::kFileManagerAppId
,
111 file_manager::kVideoPlayerAppId
,
112 file_manager::kGalleryAppId
,
113 file_manager::kAudioPlayerAppId
,
114 file_manager::kImageLoaderExtensionId
,
115 // TODO(mtomasz): Remove this extension id, and add it only for tests.
116 "pkplfbidichfdicaijlchgnapepdginl" // Testing extensions.
120 namespace extensions
{
122 namespace file_system
= api::file_system
;
123 namespace ChooseEntry
= file_system::ChooseEntry
;
127 bool g_skip_picker_for_test
= false;
128 bool g_use_suggested_path_for_test
= false;
129 base::FilePath
* g_path_to_be_picked_for_test
;
130 std::vector
<base::FilePath
>* g_paths_to_be_picked_for_test
;
131 bool g_skip_directory_confirmation_for_test
= false;
132 bool g_allow_directory_access_for_test
= false;
134 #if defined(OS_CHROMEOS)
135 ui::DialogButton g_auto_dialog_button_for_test
= ui::DIALOG_BUTTON_NONE
;
138 // Expand the mime-types and extensions provided in an AcceptOption, returning
139 // them within the passed extension vector. Returns false if no valid types
141 bool GetFileTypesFromAcceptOption(
142 const file_system::AcceptOption
& accept_option
,
143 std::vector
<base::FilePath::StringType
>* extensions
,
144 base::string16
* description
) {
145 std::set
<base::FilePath::StringType
> extension_set
;
146 int description_id
= 0;
148 if (accept_option
.mime_types
.get()) {
149 std::vector
<std::string
>* list
= accept_option
.mime_types
.get();
150 bool valid_type
= false;
151 for (std::vector
<std::string
>::const_iterator iter
= list
->begin();
152 iter
!= list
->end(); ++iter
) {
153 std::vector
<base::FilePath::StringType
> inner
;
154 std::string accept_type
= base::ToLowerASCII(*iter
);
155 net::GetExtensionsForMimeType(accept_type
, &inner
);
160 description_id
= 0; // We already have an accept type with label; if
161 // we find another, give up and use the default.
162 else if (accept_type
== "image/*")
163 description_id
= IDS_IMAGE_FILES
;
164 else if (accept_type
== "audio/*")
165 description_id
= IDS_AUDIO_FILES
;
166 else if (accept_type
== "video/*")
167 description_id
= IDS_VIDEO_FILES
;
169 extension_set
.insert(inner
.begin(), inner
.end());
174 if (accept_option
.extensions
.get()) {
175 std::vector
<std::string
>* list
= accept_option
.extensions
.get();
176 for (std::vector
<std::string
>::const_iterator iter
= list
->begin();
177 iter
!= list
->end(); ++iter
) {
178 std::string extension
= base::ToLowerASCII(*iter
);
180 extension_set
.insert(base::UTF8ToWide(*iter
));
182 extension_set
.insert(*iter
);
187 extensions
->assign(extension_set
.begin(), extension_set
.end());
188 if (extensions
->empty())
191 if (accept_option
.description
.get())
192 *description
= base::UTF8ToUTF16(*accept_option
.description
.get());
193 else if (description_id
)
194 *description
= l10n_util::GetStringUTF16(description_id
);
199 // Key for the path of the directory of the file last chosen by the user in
200 // response to a chrome.fileSystem.chooseEntry() call.
201 const char kLastChooseEntryDirectory
[] = "last_choose_file_directory";
203 const int kGraylistedPaths
[] = {
206 base::DIR_PROGRAM_FILES
,
207 base::DIR_PROGRAM_FILESX86
,
212 typedef base::Callback
<void(scoped_ptr
<base::File::Info
>)> FileInfoOptCallback
;
214 // Passes optional file info to the UI thread depending on |result| and |info|.
215 void PassFileInfoToUIThread(const FileInfoOptCallback
& callback
,
216 base::File::Error result
,
217 const base::File::Info
& info
) {
218 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
219 scoped_ptr
<base::File::Info
> file_info(
220 result
== base::File::FILE_OK
? new base::File::Info(info
) : NULL
);
221 content::BrowserThread::PostTask(
222 content::BrowserThread::UI
,
224 base::Bind(callback
, base::Passed(&file_info
)));
227 // Gets a WebContents instance handle for a platform app hosted in
228 // |render_frame_host|. If not found, then returns NULL.
229 content::WebContents
* GetWebContentsForRenderFrameHost(
231 content::RenderFrameHost
* render_frame_host
) {
232 content::WebContents
* web_contents
=
233 content::WebContents::FromRenderFrameHost(render_frame_host
);
234 // Check if there is an app window associated with the web contents; if not,
236 return AppWindowRegistry::Get(profile
)
237 ->GetAppWindowForWebContents(web_contents
)
242 #if defined(OS_CHROMEOS)
243 // Gets a WebContents instance handle for a current window of a platform app
244 // with |app_id|. If not found, then returns NULL.
245 content::WebContents
* GetWebContentsForAppId(Profile
* profile
,
246 const std::string
& app_id
) {
247 AppWindowRegistry
* const registry
= AppWindowRegistry::Get(profile
);
249 AppWindow
* const app_window
= registry
->GetCurrentAppWindowForApp(app_id
);
250 return app_window
? app_window
->web_contents() : nullptr;
253 // Fills a list of volumes mounted in the system.
254 void FillVolumeList(Profile
* profile
,
255 std::vector
<linked_ptr
<api::file_system::Volume
>>* result
) {
256 using file_manager::VolumeManager
;
257 VolumeManager
* const volume_manager
= VolumeManager::Get(profile
);
258 DCHECK(volume_manager
);
260 using api::file_system::Volume
;
261 const auto& volume_list
= volume_manager
->GetVolumeList();
262 // Convert volume_list to result_volume_list.
263 for (const auto& volume
: volume_list
) {
264 const linked_ptr
<Volume
> result_volume(new Volume
);
265 result_volume
->volume_id
= volume
->volume_id();
266 result_volume
->writable
= !volume
->is_read_only();
267 result
->push_back(result_volume
);
274 namespace file_system_api
{
276 base::FilePath
GetLastChooseEntryDirectory(const ExtensionPrefs
* prefs
,
277 const std::string
& extension_id
) {
279 std::string string_path
;
280 if (prefs
->ReadPrefAsString(extension_id
,
281 kLastChooseEntryDirectory
,
283 path
= base::FilePath::FromUTF8Unsafe(string_path
);
288 void SetLastChooseEntryDirectory(ExtensionPrefs
* prefs
,
289 const std::string
& extension_id
,
290 const base::FilePath
& path
) {
291 prefs
->UpdateExtensionPref(extension_id
,
292 kLastChooseEntryDirectory
,
293 base::CreateFilePathValue(path
));
296 std::vector
<base::FilePath
> GetGrayListedDirectories() {
297 std::vector
<base::FilePath
> graylisted_directories
;
298 for (size_t i
= 0; i
< arraysize(kGraylistedPaths
); ++i
) {
299 base::FilePath graylisted_path
;
300 if (PathService::Get(kGraylistedPaths
[i
], &graylisted_path
))
301 graylisted_directories
.push_back(graylisted_path
);
303 return graylisted_directories
;
306 #if defined(OS_CHROMEOS)
307 void DispatchVolumeListChangeEvent(Profile
* profile
) {
309 EventRouter
* const event_router
= EventRouter::Get(profile
);
310 if (!event_router
) // Possible on shutdown.
313 ExtensionRegistry
* const registry
= ExtensionRegistry::Get(profile
);
314 if (!registry
) // Possible on shutdown.
317 ConsentProviderDelegate
consent_provider_delegate(profile
, nullptr);
318 ConsentProvider
consent_provider(&consent_provider_delegate
);
319 api::file_system::VolumeListChangedEvent event_args
;
320 FillVolumeList(profile
, &event_args
.volumes
);
321 for (const auto& extension
: registry
->enabled_extensions()) {
322 if (!consent_provider
.IsGrantable(*extension
.get()))
324 event_router
->DispatchEventToExtension(
326 make_scoped_ptr(new Event(
327 events::FILE_SYSTEM_ON_VOLUME_LIST_CHANGED
,
328 api::file_system::OnVolumeListChanged::kEventName
,
329 api::file_system::OnVolumeListChanged::Create(event_args
))));
333 ConsentProvider::ConsentProvider(DelegateInterface
* delegate
)
334 : delegate_(delegate
) {
338 ConsentProvider::~ConsentProvider() {
341 void ConsentProvider::RequestConsent(
342 const Extension
& extension
,
343 const base::WeakPtr
<file_manager::Volume
>& volume
,
345 const ConsentCallback
& callback
) {
346 DCHECK(IsGrantable(extension
));
348 // If a whitelisted component, then no need to ask or inform the user.
349 if (extension
.location() == Manifest::COMPONENT
&&
350 delegate_
->IsWhitelistedComponent(extension
)) {
351 base::ThreadTaskRunnerHandle::Get()->PostTask(
352 FROM_HERE
, base::Bind(callback
, CONSENT_GRANTED
));
356 // If auto-launched kiosk app, then no need to ask user either, but show the
358 if (delegate_
->IsAutoLaunched(extension
)) {
359 delegate_
->ShowNotification(extension
, volume
, writable
);
360 base::ThreadTaskRunnerHandle::Get()->PostTask(
361 FROM_HERE
, base::Bind(callback
, CONSENT_GRANTED
));
365 // If it's a kiosk app running in manual-launch kiosk session, then show
366 // the confirmation dialog.
367 if (KioskModeInfo::IsKioskOnly(&extension
) &&
368 user_manager::UserManager::Get()->IsLoggedInAsKioskApp()) {
369 delegate_
->ShowDialog(extension
, volume
, writable
,
370 base::Bind(&ConsentProvider::DialogResultToConsent
,
371 base::Unretained(this), callback
));
375 NOTREACHED() << "Cannot request consent for non-grantable extensions.";
378 bool ConsentProvider::IsGrantable(const Extension
& extension
) {
379 const bool is_whitelisted_component
=
380 delegate_
->IsWhitelistedComponent(extension
);
382 const bool is_running_in_kiosk_session
=
383 KioskModeInfo::IsKioskOnly(&extension
) &&
384 user_manager::UserManager::Get()->IsLoggedInAsKioskApp();
386 return is_whitelisted_component
|| is_running_in_kiosk_session
;
389 void ConsentProvider::DialogResultToConsent(const ConsentCallback
& callback
,
390 ui::DialogButton button
) {
392 case ui::DIALOG_BUTTON_NONE
:
393 callback
.Run(CONSENT_IMPOSSIBLE
);
395 case ui::DIALOG_BUTTON_OK
:
396 callback
.Run(CONSENT_GRANTED
);
398 case ui::DIALOG_BUTTON_CANCEL
:
399 callback
.Run(CONSENT_REJECTED
);
404 ConsentProviderDelegate::ConsentProviderDelegate(Profile
* profile
,
405 content::RenderFrameHost
* host
)
406 : profile_(profile
), host_(host
) {
410 ConsentProviderDelegate::~ConsentProviderDelegate() {
414 void ConsentProviderDelegate::SetAutoDialogButtonForTest(
415 ui::DialogButton button
) {
416 g_auto_dialog_button_for_test
= button
;
419 void ConsentProviderDelegate::ShowDialog(
420 const Extension
& extension
,
421 const base::WeakPtr
<file_manager::Volume
>& volume
,
423 const file_system_api::ConsentProvider::ShowDialogCallback
& callback
) {
425 content::WebContents
* const foreground_contents
=
426 GetWebContentsForRenderFrameHost(profile_
, host_
);
427 // If there is no web contents handle, then the method is most probably
428 // executed from a background page. Find an app window to host the dialog.
429 content::WebContents
* const web_contents
=
430 foreground_contents
? foreground_contents
431 : GetWebContentsForAppId(profile_
, extension
.id());
433 base::ThreadTaskRunnerHandle::Get()->PostTask(
434 FROM_HERE
, base::Bind(callback
, ui::DIALOG_BUTTON_NONE
));
438 // Short circuit the user consent dialog for tests. This is far from a pretty
440 if (g_auto_dialog_button_for_test
!= ui::DIALOG_BUTTON_NONE
) {
441 base::ThreadTaskRunnerHandle::Get()->PostTask(
443 base::Bind(callback
, g_auto_dialog_button_for_test
/* result */));
447 RequestFileSystemDialogView::ShowDialog(web_contents
, extension
, volume
,
448 writable
, base::Bind(callback
));
451 void ConsentProviderDelegate::ShowNotification(
452 const Extension
& extension
,
453 const base::WeakPtr
<file_manager::Volume
>& volume
,
455 RequestFileSystemNotification::ShowAutoGrantedNotification(
456 profile_
, extension
, volume
, writable
);
459 bool ConsentProviderDelegate::IsAutoLaunched(const Extension
& extension
) {
460 chromeos::KioskAppManager::App app_info
;
461 return chromeos::KioskAppManager::Get()->GetApp(extension
.id(), &app_info
) &&
462 app_info
.was_auto_launched_with_zero_delay
;
465 bool ConsentProviderDelegate::IsWhitelistedComponent(
466 const Extension
& extension
) {
467 for (const auto& whitelisted_id
: kRequestFileSystemComponentWhitelist
) {
468 if (extension
.id().compare(whitelisted_id
) == 0)
476 } // namespace file_system_api
478 #if defined(OS_CHROMEOS)
479 using file_system_api::ConsentProvider
;
482 bool FileSystemGetDisplayPathFunction::RunSync() {
483 std::string filesystem_name
;
484 std::string filesystem_path
;
485 EXTENSION_FUNCTION_VALIDATE(args_
->GetString(0, &filesystem_name
));
486 EXTENSION_FUNCTION_VALIDATE(args_
->GetString(1, &filesystem_path
));
488 base::FilePath file_path
;
489 if (!app_file_handler_util::ValidateFileEntryAndGetPath(
490 filesystem_name
, filesystem_path
,
491 render_frame_host()->GetProcess()->GetID(), &file_path
, &error_
))
494 file_path
= path_util::PrettifyPath(file_path
);
495 SetResult(new base::StringValue(file_path
.value()));
499 FileSystemEntryFunction::FileSystemEntryFunction()
501 is_directory_(false),
504 void FileSystemEntryFunction::PrepareFilesForWritableApp(
505 const std::vector
<base::FilePath
>& paths
) {
506 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
507 app_file_handler_util::PrepareFilesForWritableApp(
511 base::Bind(&FileSystemEntryFunction::RegisterFileSystemsAndSendResponse
,
514 base::Bind(&FileSystemEntryFunction::HandleWritableFileError
, this));
517 void FileSystemEntryFunction::RegisterFileSystemsAndSendResponse(
518 const std::vector
<base::FilePath
>& paths
) {
519 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
520 if (!render_frame_host())
524 for (std::vector
<base::FilePath
>::const_iterator it
= paths
.begin();
525 it
!= paths
.end(); ++it
) {
526 AddEntryToResponse(*it
, "");
531 void FileSystemEntryFunction::CreateResponse() {
533 response_
= new base::DictionaryValue();
534 base::ListValue
* list
= new base::ListValue();
535 response_
->Set("entries", list
);
536 response_
->SetBoolean("multiple", multiple_
);
537 SetResult(response_
);
540 void FileSystemEntryFunction::AddEntryToResponse(
541 const base::FilePath
& path
,
542 const std::string
& id_override
) {
544 GrantedFileEntry file_entry
= app_file_handler_util::CreateFileEntry(
547 render_frame_host()->GetProcess()->GetID(),
550 base::ListValue
* entries
;
551 bool success
= response_
->GetList("entries", &entries
);
554 base::DictionaryValue
* entry
= new base::DictionaryValue();
555 entry
->SetString("fileSystemId", file_entry
.filesystem_id
);
556 entry
->SetString("baseName", file_entry
.registered_name
);
557 if (id_override
.empty())
558 entry
->SetString("id", file_entry
.id
);
560 entry
->SetString("id", id_override
);
561 entry
->SetBoolean("isDirectory", is_directory_
);
562 entries
->Append(entry
);
565 void FileSystemEntryFunction::HandleWritableFileError(
566 const base::FilePath
& error_path
) {
567 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
568 error_
= base::StringPrintf(kWritableFileErrorFormat
,
569 error_path
.BaseName().AsUTF8Unsafe().c_str());
573 bool FileSystemGetWritableEntryFunction::RunAsync() {
574 std::string filesystem_name
;
575 std::string filesystem_path
;
576 EXTENSION_FUNCTION_VALIDATE(args_
->GetString(0, &filesystem_name
));
577 EXTENSION_FUNCTION_VALIDATE(args_
->GetString(1, &filesystem_path
));
579 if (!app_file_handler_util::HasFileSystemWritePermission(extension_
.get())) {
580 error_
= kRequiresFileSystemWriteError
;
584 if (!app_file_handler_util::ValidateFileEntryAndGetPath(
585 filesystem_name
, filesystem_path
,
586 render_frame_host()->GetProcess()->GetID(), &path_
, &error_
))
589 content::BrowserThread::PostTaskAndReply(
590 content::BrowserThread::FILE,
593 &FileSystemGetWritableEntryFunction::SetIsDirectoryOnFileThread
,
596 &FileSystemGetWritableEntryFunction::CheckPermissionAndSendResponse
,
601 void FileSystemGetWritableEntryFunction::CheckPermissionAndSendResponse() {
602 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
604 !extension_
->permissions_data()->HasAPIPermission(
605 APIPermission::kFileSystemDirectory
)) {
606 error_
= kRequiresFileSystemDirectoryError
;
609 std::vector
<base::FilePath
> paths
;
610 paths
.push_back(path_
);
611 PrepareFilesForWritableApp(paths
);
614 void FileSystemGetWritableEntryFunction::SetIsDirectoryOnFileThread() {
615 DCHECK_CURRENTLY_ON(content::BrowserThread::FILE);
616 if (base::DirectoryExists(path_
)) {
617 is_directory_
= true;
621 bool FileSystemIsWritableEntryFunction::RunSync() {
622 std::string filesystem_name
;
623 std::string filesystem_path
;
624 EXTENSION_FUNCTION_VALIDATE(args_
->GetString(0, &filesystem_name
));
625 EXTENSION_FUNCTION_VALIDATE(args_
->GetString(1, &filesystem_path
));
627 std::string filesystem_id
;
628 if (!storage::CrackIsolatedFileSystemName(filesystem_name
, &filesystem_id
)) {
629 error_
= app_file_handler_util::kInvalidParameters
;
633 content::ChildProcessSecurityPolicy
* policy
=
634 content::ChildProcessSecurityPolicy::GetInstance();
635 int renderer_id
= render_frame_host()->GetProcess()->GetID();
636 bool is_writable
= policy
->CanReadWriteFileSystem(renderer_id
,
639 SetResult(new base::FundamentalValue(is_writable
));
643 // Handles showing a dialog to the user to ask for the filename for a file to
645 class FileSystemChooseEntryFunction::FilePicker
646 : public ui::SelectFileDialog::Listener
{
648 FilePicker(FileSystemChooseEntryFunction
* function
,
649 content::WebContents
* web_contents
,
650 const base::FilePath
& suggested_name
,
651 const ui::SelectFileDialog::FileTypeInfo
& file_type_info
,
652 ui::SelectFileDialog::Type picker_type
)
653 : function_(function
) {
654 select_file_dialog_
= ui::SelectFileDialog::Create(
655 this, new ChromeSelectFilePolicy(web_contents
));
656 gfx::NativeWindow owning_window
= web_contents
?
657 platform_util::GetTopLevel(web_contents
->GetNativeView()) :
660 if (g_skip_picker_for_test
) {
661 if (g_use_suggested_path_for_test
) {
662 content::BrowserThread::PostTask(content::BrowserThread::UI
, FROM_HERE
,
664 &FileSystemChooseEntryFunction::FilePicker::FileSelected
,
665 base::Unretained(this), suggested_name
, 1,
666 static_cast<void*>(NULL
)));
667 } else if (g_path_to_be_picked_for_test
) {
668 content::BrowserThread::PostTask(
669 content::BrowserThread::UI
, FROM_HERE
,
671 &FileSystemChooseEntryFunction::FilePicker::FileSelected
,
672 base::Unretained(this), *g_path_to_be_picked_for_test
, 1,
673 static_cast<void*>(NULL
)));
674 } else if (g_paths_to_be_picked_for_test
) {
675 content::BrowserThread::PostTask(
676 content::BrowserThread::UI
,
679 &FileSystemChooseEntryFunction::FilePicker::MultiFilesSelected
,
680 base::Unretained(this),
681 *g_paths_to_be_picked_for_test
,
682 static_cast<void*>(NULL
)));
684 content::BrowserThread::PostTask(content::BrowserThread::UI
, FROM_HERE
,
686 &FileSystemChooseEntryFunction::FilePicker::
687 FileSelectionCanceled
,
688 base::Unretained(this), static_cast<void*>(NULL
)));
693 select_file_dialog_
->SelectFile(picker_type
,
698 base::FilePath::StringType(),
703 ~FilePicker() override
{}
706 // ui::SelectFileDialog::Listener implementation.
707 void FileSelected(const base::FilePath
& path
,
709 void* params
) override
{
710 std::vector
<base::FilePath
> paths
;
711 paths
.push_back(path
);
712 MultiFilesSelected(paths
, params
);
715 void FileSelectedWithExtraInfo(const ui::SelectedFileInfo
& file
,
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.
724 // TODO(kinaba): remove this, once after the file picker implements proper
725 // switch of the path treatment depending on the |support_drive| flag.
726 FileSelected(file
.file_path
, index
, params
);
729 void MultiFilesSelected(const std::vector
<base::FilePath
>& files
,
730 void* params
) override
{
731 function_
->FilesSelected(files
);
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
);
743 MultiFilesSelected(paths
, params
);
746 void FileSelectionCanceled(void* params
) override
{
747 function_
->FileSelectionCanceled();
751 scoped_refptr
<ui::SelectFileDialog
> select_file_dialog_
;
752 scoped_refptr
<FileSystemChooseEntryFunction
> function_
;
754 DISALLOW_COPY_AND_ASSIGN(FilePicker
);
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();
770 error_
= kInvalidCallingPage
;
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.
780 this, web_contents
, initial_path_
, file_type_info
, picker_type
);
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
;
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
;
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
;
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
;
816 void FileSystemChooseEntryFunction::StopSkippingPickerForTest() {
817 g_skip_picker_for_test
= false;
821 void FileSystemChooseEntryFunction::SkipDirectoryConfirmationForTest() {
822 g_skip_directory_confirmation_for_test
= true;
823 g_allow_directory_access_for_test
= true;
827 void FileSystemChooseEntryFunction::AutoCancelDirectoryConfirmationForTest() {
828 g_skip_directory_confirmation_for_test
= true;
829 g_allow_directory_access_for_test
= false;
833 void FileSystemChooseEntryFunction::StopSkippingDirectoryConfirmationForTest() {
834 g_skip_directory_confirmation_for_test
= false;
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(
845 storage::kFileSystemTypeNativeLocal
,
846 storage::FileSystemMountOption(),
850 void FileSystemChooseEntryFunction::SetInitialPathOnFileThread(
851 const base::FilePath
& suggested_name
,
852 const base::FilePath
& previous_path
) {
853 DCHECK_CURRENTLY_ON(content::BrowserThread::FILE);
854 if (!previous_path
.empty() && base::DirectoryExists(previous_path
)) {
855 initial_path_
= previous_path
.Append(suggested_name
);
857 base::FilePath documents_dir
;
858 if (PathService::Get(chrome::DIR_USER_DOCUMENTS
, &documents_dir
)) {
859 initial_path_
= documents_dir
.Append(suggested_name
);
861 initial_path_
= suggested_name
;
866 void FileSystemChooseEntryFunction::FilesSelected(
867 const std::vector
<base::FilePath
>& paths
) {
868 DCHECK(!paths
.empty());
869 base::FilePath last_choose_directory
;
871 last_choose_directory
= paths
[0];
873 last_choose_directory
= paths
[0].DirName();
875 file_system_api::SetLastChooseEntryDirectory(
876 ExtensionPrefs::Get(GetProfile()),
878 last_choose_directory
);
880 // Get the WebContents for the app window to be the parent window of the
881 // confirmation dialog if necessary.
882 content::WebContents
* const web_contents
=
883 GetWebContentsForRenderFrameHost(GetProfile(), render_frame_host());
885 error_
= kInvalidCallingPage
;
890 DCHECK_EQ(paths
.size(), 1u);
891 bool non_native_path
= false;
892 #if defined(OS_CHROMEOS)
894 file_manager::util::IsUnderNonNativeLocalPath(GetProfile(), paths
[0]);
897 content::BrowserThread::PostTask(
898 content::BrowserThread::FILE,
901 &FileSystemChooseEntryFunction::ConfirmDirectoryAccessOnFileThread
,
909 OnDirectoryAccessConfirmed(paths
);
912 void FileSystemChooseEntryFunction::FileSelectionCanceled() {
913 error_
= kUserCancelled
;
917 void FileSystemChooseEntryFunction::ConfirmDirectoryAccessOnFileThread(
918 bool non_native_path
,
919 const std::vector
<base::FilePath
>& paths
,
920 content::WebContents
* web_contents
) {
921 const base::FilePath check_path
=
922 non_native_path
? paths
[0] : base::MakeAbsoluteFilePath(paths
[0]);
923 if (check_path
.empty()) {
924 content::BrowserThread::PostTask(
925 content::BrowserThread::UI
,
927 base::Bind(&FileSystemChooseEntryFunction::FileSelectionCanceled
,
932 for (size_t i
= 0; i
< arraysize(kGraylistedPaths
); i
++) {
933 base::FilePath graylisted_path
;
934 if (PathService::Get(kGraylistedPaths
[i
], &graylisted_path
) &&
935 (check_path
== graylisted_path
||
936 check_path
.IsParent(graylisted_path
))) {
937 if (g_skip_directory_confirmation_for_test
) {
938 if (g_allow_directory_access_for_test
) {
941 content::BrowserThread::PostTask(
942 content::BrowserThread::UI
,
944 base::Bind(&FileSystemChooseEntryFunction::FileSelectionCanceled
,
950 content::BrowserThread::PostTask(
951 content::BrowserThread::UI
,
954 CreateDirectoryAccessConfirmationDialog
,
955 app_file_handler_util::HasFileSystemWritePermission(
957 base::UTF8ToUTF16(extension_
->name()),
960 &FileSystemChooseEntryFunction::OnDirectoryAccessConfirmed
,
963 base::Bind(&FileSystemChooseEntryFunction::FileSelectionCanceled
,
969 content::BrowserThread::PostTask(
970 content::BrowserThread::UI
,
972 base::Bind(&FileSystemChooseEntryFunction::OnDirectoryAccessConfirmed
,
976 void FileSystemChooseEntryFunction::OnDirectoryAccessConfirmed(
977 const std::vector
<base::FilePath
>& paths
) {
978 if (app_file_handler_util::HasFileSystemWritePermission(extension_
.get())) {
979 PrepareFilesForWritableApp(paths
);
983 // Don't need to check the file, it's for reading.
984 RegisterFileSystemsAndSendResponse(paths
);
987 void FileSystemChooseEntryFunction::BuildFileTypeInfo(
988 ui::SelectFileDialog::FileTypeInfo
* file_type_info
,
989 const base::FilePath::StringType
& suggested_extension
,
990 const AcceptOptions
* accepts
,
991 const bool* acceptsAllTypes
) {
992 file_type_info
->include_all_files
= true;
994 file_type_info
->include_all_files
= *acceptsAllTypes
;
996 bool need_suggestion
= !file_type_info
->include_all_files
&&
997 !suggested_extension
.empty();
1000 typedef file_system::AcceptOption AcceptOption
;
1001 for (std::vector
<linked_ptr
<AcceptOption
> >::const_iterator iter
=
1002 accepts
->begin(); iter
!= accepts
->end(); ++iter
) {
1003 base::string16 description
;
1004 std::vector
<base::FilePath::StringType
> extensions
;
1006 if (!GetFileTypesFromAcceptOption(**iter
, &extensions
, &description
))
1007 continue; // No extensions were found.
1009 file_type_info
->extensions
.push_back(extensions
);
1010 file_type_info
->extension_description_overrides
.push_back(description
);
1012 // If we still need to find suggested_extension, hunt for it inside the
1013 // extensions returned from GetFileTypesFromAcceptOption.
1014 if (need_suggestion
&& std::find(extensions
.begin(),
1015 extensions
.end(), suggested_extension
) != extensions
.end()) {
1016 need_suggestion
= false;
1021 // If there's nothing in our accepted extension list or we couldn't find the
1022 // suggested extension required, then default to accepting all types.
1023 if (file_type_info
->extensions
.empty() || need_suggestion
)
1024 file_type_info
->include_all_files
= true;
1027 void FileSystemChooseEntryFunction::BuildSuggestion(
1028 const std::string
*opt_name
,
1029 base::FilePath
* suggested_name
,
1030 base::FilePath::StringType
* suggested_extension
) {
1032 *suggested_name
= base::FilePath::FromUTF8Unsafe(*opt_name
);
1034 // Don't allow any path components; shorten to the base name. This should
1035 // result in a relative path, but in some cases may not. Clear the
1036 // suggestion for safety if this is the case.
1037 *suggested_name
= suggested_name
->BaseName();
1038 if (suggested_name
->IsAbsolute())
1039 *suggested_name
= base::FilePath();
1041 *suggested_extension
= suggested_name
->Extension();
1042 if (!suggested_extension
->empty())
1043 suggested_extension
->erase(suggested_extension
->begin()); // drop the .
1047 bool FileSystemChooseEntryFunction::RunAsync() {
1048 scoped_ptr
<ChooseEntry::Params
> params(ChooseEntry::Params::Create(*args_
));
1049 EXTENSION_FUNCTION_VALIDATE(params
.get());
1051 base::FilePath suggested_name
;
1052 ui::SelectFileDialog::FileTypeInfo file_type_info
;
1053 ui::SelectFileDialog::Type picker_type
=
1054 ui::SelectFileDialog::SELECT_OPEN_FILE
;
1056 file_system::ChooseEntryOptions
* options
= params
->options
.get();
1058 multiple_
= options
->accepts_multiple
;
1060 picker_type
= ui::SelectFileDialog::SELECT_OPEN_MULTI_FILE
;
1062 if (options
->type
== file_system::CHOOSE_ENTRY_TYPE_OPENWRITABLEFILE
&&
1063 !app_file_handler_util::HasFileSystemWritePermission(
1064 extension_
.get())) {
1065 error_
= kRequiresFileSystemWriteError
;
1067 } else if (options
->type
== file_system::CHOOSE_ENTRY_TYPE_SAVEFILE
) {
1068 if (!app_file_handler_util::HasFileSystemWritePermission(
1069 extension_
.get())) {
1070 error_
= kRequiresFileSystemWriteError
;
1074 error_
= kMultipleUnsupportedError
;
1077 picker_type
= ui::SelectFileDialog::SELECT_SAVEAS_FILE
;
1078 } else if (options
->type
== file_system::CHOOSE_ENTRY_TYPE_OPENDIRECTORY
) {
1079 is_directory_
= true;
1080 if (!extension_
->permissions_data()->HasAPIPermission(
1081 APIPermission::kFileSystemDirectory
)) {
1082 error_
= kRequiresFileSystemDirectoryError
;
1086 error_
= kMultipleUnsupportedError
;
1089 picker_type
= ui::SelectFileDialog::SELECT_FOLDER
;
1092 base::FilePath::StringType suggested_extension
;
1093 BuildSuggestion(options
->suggested_name
.get(), &suggested_name
,
1094 &suggested_extension
);
1096 BuildFileTypeInfo(&file_type_info
, suggested_extension
,
1097 options
->accepts
.get(), options
->accepts_all_types
.get());
1100 file_type_info
.support_drive
= true;
1102 base::FilePath previous_path
= file_system_api::GetLastChooseEntryDirectory(
1103 ExtensionPrefs::Get(GetProfile()), extension()->id());
1105 content::BrowserThread::PostTaskAndReply(
1106 content::BrowserThread::FILE,
1108 base::Bind(&FileSystemChooseEntryFunction::SetInitialPathOnFileThread
,
1112 base::Bind(&FileSystemChooseEntryFunction::ShowPicker
,
1119 bool FileSystemRetainEntryFunction::RunAsync() {
1120 std::string entry_id
;
1121 EXTENSION_FUNCTION_VALIDATE(args_
->GetString(0, &entry_id
));
1122 SavedFilesService
* saved_files_service
= SavedFilesService::Get(GetProfile());
1123 // Add the file to the retain list if it is not already on there.
1124 if (!saved_files_service
->IsRegistered(extension_
->id(), entry_id
)) {
1125 std::string filesystem_name
;
1126 std::string filesystem_path
;
1127 base::FilePath path
;
1128 EXTENSION_FUNCTION_VALIDATE(args_
->GetString(1, &filesystem_name
));
1129 EXTENSION_FUNCTION_VALIDATE(args_
->GetString(2, &filesystem_path
));
1130 if (!app_file_handler_util::ValidateFileEntryAndGetPath(
1131 filesystem_name
, filesystem_path
,
1132 render_frame_host()->GetProcess()->GetID(), &path
, &error_
)) {
1136 std::string filesystem_id
;
1137 if (!storage::CrackIsolatedFileSystemName(filesystem_name
, &filesystem_id
))
1140 const GURL site
= util::GetSiteForExtensionId(extension_id(), GetProfile());
1141 storage::FileSystemContext
* const context
=
1142 content::BrowserContext::GetStoragePartitionForSite(GetProfile(), site
)
1143 ->GetFileSystemContext();
1144 const storage::FileSystemURL url
= context
->CreateCrackedFileSystemURL(
1146 storage::kFileSystemTypeIsolated
,
1147 IsolatedContext::GetInstance()
1148 ->CreateVirtualRootPath(filesystem_id
)
1149 .Append(base::FilePath::FromUTF8Unsafe(filesystem_path
)));
1151 content::BrowserThread::PostTask(
1152 content::BrowserThread::IO
,
1156 &storage::FileSystemOperationRunner::GetMetadata
),
1157 context
->operation_runner()->AsWeakPtr(),
1160 &PassFileInfoToUIThread
,
1161 base::Bind(&FileSystemRetainEntryFunction::RetainFileEntry
,
1168 saved_files_service
->EnqueueFileEntry(extension_
->id(), entry_id
);
1173 void FileSystemRetainEntryFunction::RetainFileEntry(
1174 const std::string
& entry_id
,
1175 const base::FilePath
& path
,
1176 scoped_ptr
<base::File::Info
> file_info
) {
1178 SendResponse(false);
1182 SavedFilesService
* saved_files_service
= SavedFilesService::Get(GetProfile());
1183 saved_files_service
->RegisterFileEntry(
1184 extension_
->id(), entry_id
, path
, file_info
->is_directory
);
1185 saved_files_service
->EnqueueFileEntry(extension_
->id(), entry_id
);
1189 bool FileSystemIsRestorableFunction::RunSync() {
1190 std::string entry_id
;
1191 EXTENSION_FUNCTION_VALIDATE(args_
->GetString(0, &entry_id
));
1192 SetResult(new base::FundamentalValue(SavedFilesService::Get(
1193 GetProfile())->IsRegistered(extension_
->id(), entry_id
)));
1197 bool FileSystemRestoreEntryFunction::RunAsync() {
1198 std::string entry_id
;
1199 bool needs_new_entry
;
1200 EXTENSION_FUNCTION_VALIDATE(args_
->GetString(0, &entry_id
));
1201 EXTENSION_FUNCTION_VALIDATE(args_
->GetBoolean(1, &needs_new_entry
));
1202 const SavedFileEntry
* file_entry
= SavedFilesService::Get(
1203 GetProfile())->GetFileEntry(extension_
->id(), entry_id
);
1205 error_
= kUnknownIdError
;
1209 SavedFilesService::Get(GetProfile())
1210 ->EnqueueFileEntry(extension_
->id(), entry_id
);
1212 // Only create a new file entry if the renderer requests one.
1213 // |needs_new_entry| will be false if the renderer already has an Entry for
1215 if (needs_new_entry
) {
1216 is_directory_
= file_entry
->is_directory
;
1218 AddEntryToResponse(file_entry
->path
, file_entry
->id
);
1224 bool FileSystemObserveDirectoryFunction::RunSync() {
1226 error_
= kUnknownIdError
;
1230 bool FileSystemUnobserveEntryFunction::RunSync() {
1232 error_
= kUnknownIdError
;
1236 bool FileSystemGetObservedEntriesFunction::RunSync() {
1238 error_
= kUnknownIdError
;
1242 #if !defined(OS_CHROMEOS)
1243 ExtensionFunction::ResponseAction
FileSystemRequestFileSystemFunction::Run() {
1244 using api::file_system::RequestFileSystem::Params
;
1245 const scoped_ptr
<Params
> params(Params::Create(*args_
));
1246 EXTENSION_FUNCTION_VALIDATE(params
);
1249 return RespondNow(Error(kNotSupportedOnCurrentPlatformError
));
1252 ExtensionFunction::ResponseAction
FileSystemGetVolumeListFunction::Run() {
1254 return RespondNow(Error(kNotSupportedOnCurrentPlatformError
));
1258 FileSystemRequestFileSystemFunction::FileSystemRequestFileSystemFunction()
1259 : chrome_details_(this) {
1262 FileSystemRequestFileSystemFunction::~FileSystemRequestFileSystemFunction() {
1265 ExtensionFunction::ResponseAction
FileSystemRequestFileSystemFunction::Run() {
1266 using api::file_system::RequestFileSystem::Params
;
1267 const scoped_ptr
<Params
> params(Params::Create(*args_
));
1268 EXTENSION_FUNCTION_VALIDATE(params
);
1270 // Only kiosk apps in kiosk sessions can use this API.
1271 // Additionally it is enabled for whitelisted component extensions and apps.
1272 file_system_api::ConsentProviderDelegate
consent_provider_delegate(
1273 chrome_details_
.GetProfile(), render_frame_host());
1274 file_system_api::ConsentProvider
consent_provider(&consent_provider_delegate
);
1276 if (!consent_provider
.IsGrantable(*extension()))
1277 return RespondNow(Error(kNotSupportedOnNonKioskSessionError
));
1279 using file_manager::VolumeManager
;
1280 using file_manager::Volume
;
1281 VolumeManager
* const volume_manager
=
1282 VolumeManager::Get(chrome_details_
.GetProfile());
1283 DCHECK(volume_manager
);
1285 const bool writable
=
1286 params
->options
.writable
.get() && *params
->options
.writable
.get();
1288 !app_file_handler_util::HasFileSystemWritePermission(extension_
.get())) {
1289 return RespondNow(Error(kRequiresFileSystemWriteError
));
1292 base::WeakPtr
<file_manager::Volume
> volume
=
1293 volume_manager
->FindVolumeById(params
->options
.volume_id
);
1295 return RespondNow(Error(kVolumeNotFoundError
));
1298 util::GetSiteForExtensionId(extension_id(), chrome_details_
.GetProfile());
1299 scoped_refptr
<storage::FileSystemContext
> file_system_context
=
1300 content::BrowserContext::GetStoragePartitionForSite(
1301 chrome_details_
.GetProfile(), site
)->GetFileSystemContext();
1302 storage::ExternalFileSystemBackend
* const backend
=
1303 file_system_context
->external_backend();
1306 base::FilePath virtual_path
;
1307 if (!backend
->GetVirtualPath(volume
->mount_path(), &virtual_path
))
1308 return RespondNow(Error(kSecurityError
));
1310 if (writable
&& (volume
->is_read_only()))
1311 return RespondNow(Error(kSecurityError
));
1313 consent_provider
.RequestConsent(
1314 *extension(), volume
, writable
,
1315 base::Bind(&FileSystemRequestFileSystemFunction::OnConsentReceived
, this,
1317 return RespondLater();
1320 void FileSystemRequestFileSystemFunction::OnConsentReceived(
1321 const base::WeakPtr
<file_manager::Volume
>& volume
,
1323 ConsentProvider::Consent result
) {
1324 using file_manager::VolumeManager
;
1325 using file_manager::Volume
;
1328 case ConsentProvider::CONSENT_REJECTED
:
1329 SetError(kSecurityError
);
1330 SendResponse(false);
1333 case ConsentProvider::CONSENT_IMPOSSIBLE
:
1334 SetError(kConsentImpossible
);
1335 SendResponse(false);
1338 case ConsentProvider::CONSENT_GRANTED
:
1342 if (!volume
.get()) {
1343 SetError(kVolumeNotFoundError
);
1344 SendResponse(false);
1349 util::GetSiteForExtensionId(extension_id(), chrome_details_
.GetProfile());
1350 scoped_refptr
<storage::FileSystemContext
> file_system_context
=
1351 content::BrowserContext::GetStoragePartitionForSite(
1352 chrome_details_
.GetProfile(), site
)->GetFileSystemContext();
1353 storage::ExternalFileSystemBackend
* const backend
=
1354 file_system_context
->external_backend();
1357 base::FilePath virtual_path
;
1358 if (!backend
->GetVirtualPath(volume
->mount_path(), &virtual_path
)) {
1359 SetError(kSecurityError
);
1360 SendResponse(false);
1364 storage::IsolatedContext
* const isolated_context
=
1365 storage::IsolatedContext::GetInstance();
1366 DCHECK(isolated_context
);
1368 const storage::FileSystemURL original_url
=
1369 file_system_context
->CreateCrackedFileSystemURL(
1370 GURL(std::string(kExtensionScheme
) + url::kStandardSchemeSeparator
+
1372 storage::kFileSystemTypeExternal
, virtual_path
);
1374 // Set a fixed register name, as the automatic one would leak the mount point
1376 std::string register_name
= "fs";
1377 const std::string file_system_id
=
1378 isolated_context
->RegisterFileSystemForPath(
1379 storage::kFileSystemTypeNativeForPlatformApp
,
1380 std::string() /* file_system_id */, original_url
.path(),
1382 if (file_system_id
.empty()) {
1383 SetError(kSecurityError
);
1384 SendResponse(false);
1388 backend
->GrantFileAccessToExtension(extension_
->id(), virtual_path
);
1390 // Grant file permissions to the renderer hosting component.
1391 content::ChildProcessSecurityPolicy
* policy
=
1392 content::ChildProcessSecurityPolicy::GetInstance();
1395 // Read-only permisisons.
1396 policy
->GrantReadFile(render_frame_host()->GetProcess()->GetID(),
1397 volume
->mount_path());
1398 policy
->GrantReadFileSystem(render_frame_host()->GetProcess()->GetID(),
1401 // Additional write permissions.
1403 policy
->GrantCreateReadWriteFile(render_frame_host()->GetProcess()->GetID(),
1404 volume
->mount_path());
1405 policy
->GrantCopyInto(render_frame_host()->GetProcess()->GetID(),
1406 volume
->mount_path());
1407 policy
->GrantWriteFileSystem(render_frame_host()->GetProcess()->GetID(),
1409 policy
->GrantDeleteFromFileSystem(
1410 render_frame_host()->GetProcess()->GetID(), file_system_id
);
1411 policy
->GrantCreateFileForFileSystem(
1412 render_frame_host()->GetProcess()->GetID(), file_system_id
);
1415 base::DictionaryValue
* const dict
= new base::DictionaryValue();
1416 dict
->SetString("file_system_id", file_system_id
);
1417 dict
->SetString("file_system_path", register_name
);
1423 FileSystemGetVolumeListFunction::FileSystemGetVolumeListFunction()
1424 : chrome_details_(this) {
1427 FileSystemGetVolumeListFunction::~FileSystemGetVolumeListFunction() {
1430 ExtensionFunction::ResponseAction
FileSystemGetVolumeListFunction::Run() {
1431 // Only kiosk apps in kiosk sessions can use this API.
1432 // Additionally it is enabled for whitelisted component extensions and apps.
1433 file_system_api::ConsentProviderDelegate
consent_provider_delegate(
1434 chrome_details_
.GetProfile(), render_frame_host());
1435 file_system_api::ConsentProvider
consent_provider(&consent_provider_delegate
);
1437 if (!consent_provider
.IsGrantable(*extension()))
1438 return RespondNow(Error(kNotSupportedOnNonKioskSessionError
));
1439 using api::file_system::Volume
;
1440 std::vector
<linked_ptr
<Volume
>> result_volume_list
;
1441 FillVolumeList(chrome_details_
.GetProfile(), &result_volume_list
);
1444 ArgumentList(api::file_system::GetVolumeList::Results::Create(
1445 result_volume_list
).Pass()));
1449 } // namespace extensions