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
= *iter
;
155 base::StringToLowerASCII(&accept_type
);
156 net::GetExtensionsForMimeType(accept_type
, &inner
);
161 description_id
= 0; // We already have an accept type with label; if
162 // we find another, give up and use the default.
163 else if (accept_type
== "image/*")
164 description_id
= IDS_IMAGE_FILES
;
165 else if (accept_type
== "audio/*")
166 description_id
= IDS_AUDIO_FILES
;
167 else if (accept_type
== "video/*")
168 description_id
= IDS_VIDEO_FILES
;
170 extension_set
.insert(inner
.begin(), inner
.end());
175 if (accept_option
.extensions
.get()) {
176 std::vector
<std::string
>* list
= accept_option
.extensions
.get();
177 for (std::vector
<std::string
>::const_iterator iter
= list
->begin();
178 iter
!= list
->end(); ++iter
) {
179 std::string extension
= *iter
;
180 base::StringToLowerASCII(&extension
);
182 extension_set
.insert(base::UTF8ToWide(*iter
));
184 extension_set
.insert(*iter
);
189 extensions
->assign(extension_set
.begin(), extension_set
.end());
190 if (extensions
->empty())
193 if (accept_option
.description
.get())
194 *description
= base::UTF8ToUTF16(*accept_option
.description
.get());
195 else if (description_id
)
196 *description
= l10n_util::GetStringUTF16(description_id
);
201 // Key for the path of the directory of the file last chosen by the user in
202 // response to a chrome.fileSystem.chooseEntry() call.
203 const char kLastChooseEntryDirectory
[] = "last_choose_file_directory";
205 const int kGraylistedPaths
[] = {
208 base::DIR_PROGRAM_FILES
,
209 base::DIR_PROGRAM_FILESX86
,
214 typedef base::Callback
<void(scoped_ptr
<base::File::Info
>)> FileInfoOptCallback
;
216 // Passes optional file info to the UI thread depending on |result| and |info|.
217 void PassFileInfoToUIThread(const FileInfoOptCallback
& callback
,
218 base::File::Error result
,
219 const base::File::Info
& info
) {
220 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
221 scoped_ptr
<base::File::Info
> file_info(
222 result
== base::File::FILE_OK
? new base::File::Info(info
) : NULL
);
223 content::BrowserThread::PostTask(
224 content::BrowserThread::UI
,
226 base::Bind(callback
, base::Passed(&file_info
)));
229 // Gets a WebContents instance handle for a platform app hosted in
230 // |render_frame_host|. If not found, then returns NULL.
231 content::WebContents
* GetWebContentsForRenderFrameHost(
233 content::RenderFrameHost
* render_frame_host
) {
234 content::WebContents
* web_contents
=
235 content::WebContents::FromRenderFrameHost(render_frame_host
);
236 // Check if there is an app window associated with the web contents; if not,
238 return AppWindowRegistry::Get(profile
)
239 ->GetAppWindowForWebContents(web_contents
)
244 #if defined(OS_CHROMEOS)
245 // Gets a WebContents instance handle for a current window of a platform app
246 // with |app_id|. If not found, then returns NULL.
247 content::WebContents
* GetWebContentsForAppId(Profile
* profile
,
248 const std::string
& app_id
) {
249 AppWindowRegistry
* const registry
= AppWindowRegistry::Get(profile
);
251 AppWindow
* const app_window
= registry
->GetCurrentAppWindowForApp(app_id
);
252 return app_window
? app_window
->web_contents() : nullptr;
255 // Fills a list of volumes mounted in the system.
256 void FillVolumeList(Profile
* profile
,
257 std::vector
<linked_ptr
<api::file_system::Volume
>>* result
) {
258 using file_manager::VolumeManager
;
259 VolumeManager
* const volume_manager
= VolumeManager::Get(profile
);
260 DCHECK(volume_manager
);
262 using api::file_system::Volume
;
263 const auto& volume_list
= volume_manager
->GetVolumeList();
264 // Convert volume_list to result_volume_list.
265 for (const auto& volume
: volume_list
) {
266 const linked_ptr
<Volume
> result_volume(new Volume
);
267 result_volume
->volume_id
= volume
->volume_id();
268 result_volume
->writable
= !volume
->is_read_only();
269 result
->push_back(result_volume
);
276 namespace file_system_api
{
278 base::FilePath
GetLastChooseEntryDirectory(const ExtensionPrefs
* prefs
,
279 const std::string
& extension_id
) {
281 std::string string_path
;
282 if (prefs
->ReadPrefAsString(extension_id
,
283 kLastChooseEntryDirectory
,
285 path
= base::FilePath::FromUTF8Unsafe(string_path
);
290 void SetLastChooseEntryDirectory(ExtensionPrefs
* prefs
,
291 const std::string
& extension_id
,
292 const base::FilePath
& path
) {
293 prefs
->UpdateExtensionPref(extension_id
,
294 kLastChooseEntryDirectory
,
295 base::CreateFilePathValue(path
));
298 std::vector
<base::FilePath
> GetGrayListedDirectories() {
299 std::vector
<base::FilePath
> graylisted_directories
;
300 for (size_t i
= 0; i
< arraysize(kGraylistedPaths
); ++i
) {
301 base::FilePath graylisted_path
;
302 if (PathService::Get(kGraylistedPaths
[i
], &graylisted_path
))
303 graylisted_directories
.push_back(graylisted_path
);
305 return graylisted_directories
;
308 #if defined(OS_CHROMEOS)
309 void DispatchVolumeListChangeEvent(Profile
* profile
) {
311 EventRouter
* const event_router
= EventRouter::Get(profile
);
312 if (!event_router
) // Possible on shutdown.
315 ExtensionRegistry
* const registry
= ExtensionRegistry::Get(profile
);
316 if (!registry
) // Possible on shutdown.
319 ConsentProviderDelegate
consent_provider_delegate(profile
, nullptr);
320 ConsentProvider
consent_provider(&consent_provider_delegate
);
321 api::file_system::VolumeListChangedEvent event_args
;
322 FillVolumeList(profile
, &event_args
.volumes
);
323 for (const auto& extension
: registry
->enabled_extensions()) {
324 if (!consent_provider
.IsGrantable(*extension
.get()))
326 event_router
->DispatchEventToExtension(
328 make_scoped_ptr(new Event(
329 events::FILE_SYSTEM_ON_VOLUME_LIST_CHANGED
,
330 api::file_system::OnVolumeListChanged::kEventName
,
331 api::file_system::OnVolumeListChanged::Create(event_args
))));
335 ConsentProvider::ConsentProvider(DelegateInterface
* delegate
)
336 : delegate_(delegate
) {
340 ConsentProvider::~ConsentProvider() {
343 void ConsentProvider::RequestConsent(
344 const Extension
& extension
,
345 const base::WeakPtr
<file_manager::Volume
>& volume
,
347 const ConsentCallback
& callback
) {
348 DCHECK(IsGrantable(extension
));
350 // If a whitelisted component, then no need to ask or inform the user.
351 if (extension
.location() == Manifest::COMPONENT
&&
352 delegate_
->IsWhitelistedComponent(extension
)) {
353 base::ThreadTaskRunnerHandle::Get()->PostTask(
354 FROM_HERE
, base::Bind(callback
, CONSENT_GRANTED
));
358 // If auto-launched kiosk app, then no need to ask user either, but show the
360 if (delegate_
->IsAutoLaunched(extension
)) {
361 delegate_
->ShowNotification(extension
, volume
, writable
);
362 base::ThreadTaskRunnerHandle::Get()->PostTask(
363 FROM_HERE
, base::Bind(callback
, CONSENT_GRANTED
));
367 // If it's a kiosk app running in manual-launch kiosk session, then show
368 // the confirmation dialog.
369 if (KioskModeInfo::IsKioskOnly(&extension
) &&
370 user_manager::UserManager::Get()->IsLoggedInAsKioskApp()) {
371 delegate_
->ShowDialog(extension
, volume
, writable
,
372 base::Bind(&ConsentProvider::DialogResultToConsent
,
373 base::Unretained(this), callback
));
377 NOTREACHED() << "Cannot request consent for non-grantable extensions.";
380 bool ConsentProvider::IsGrantable(const Extension
& extension
) {
381 const bool is_whitelisted_component
=
382 delegate_
->IsWhitelistedComponent(extension
);
384 const bool is_running_in_kiosk_session
=
385 KioskModeInfo::IsKioskOnly(&extension
) &&
386 user_manager::UserManager::Get()->IsLoggedInAsKioskApp();
388 return is_whitelisted_component
|| is_running_in_kiosk_session
;
391 void ConsentProvider::DialogResultToConsent(const ConsentCallback
& callback
,
392 ui::DialogButton button
) {
394 case ui::DIALOG_BUTTON_NONE
:
395 callback
.Run(CONSENT_IMPOSSIBLE
);
397 case ui::DIALOG_BUTTON_OK
:
398 callback
.Run(CONSENT_GRANTED
);
400 case ui::DIALOG_BUTTON_CANCEL
:
401 callback
.Run(CONSENT_REJECTED
);
406 ConsentProviderDelegate::ConsentProviderDelegate(Profile
* profile
,
407 content::RenderFrameHost
* host
)
408 : profile_(profile
), host_(host
) {
412 ConsentProviderDelegate::~ConsentProviderDelegate() {
416 void ConsentProviderDelegate::SetAutoDialogButtonForTest(
417 ui::DialogButton button
) {
418 g_auto_dialog_button_for_test
= button
;
421 void ConsentProviderDelegate::ShowDialog(
422 const Extension
& extension
,
423 const base::WeakPtr
<file_manager::Volume
>& volume
,
425 const file_system_api::ConsentProvider::ShowDialogCallback
& callback
) {
427 content::WebContents
* const foreground_contents
=
428 GetWebContentsForRenderFrameHost(profile_
, host_
);
429 // If there is no web contents handle, then the method is most probably
430 // executed from a background page. Find an app window to host the dialog.
431 content::WebContents
* const web_contents
=
432 foreground_contents
? foreground_contents
433 : GetWebContentsForAppId(profile_
, extension
.id());
435 base::ThreadTaskRunnerHandle::Get()->PostTask(
436 FROM_HERE
, base::Bind(callback
, ui::DIALOG_BUTTON_NONE
));
440 // Short circuit the user consent dialog for tests. This is far from a pretty
442 if (g_auto_dialog_button_for_test
!= ui::DIALOG_BUTTON_NONE
) {
443 base::ThreadTaskRunnerHandle::Get()->PostTask(
445 base::Bind(callback
, g_auto_dialog_button_for_test
/* result */));
449 RequestFileSystemDialogView::ShowDialog(web_contents
, extension
, volume
,
450 writable
, base::Bind(callback
));
453 void ConsentProviderDelegate::ShowNotification(
454 const Extension
& extension
,
455 const base::WeakPtr
<file_manager::Volume
>& volume
,
457 RequestFileSystemNotification::ShowAutoGrantedNotification(
458 profile_
, extension
, volume
, writable
);
461 bool ConsentProviderDelegate::IsAutoLaunched(const Extension
& extension
) {
462 chromeos::KioskAppManager::App app_info
;
463 return chromeos::KioskAppManager::Get()->GetApp(extension
.id(), &app_info
) &&
464 app_info
.was_auto_launched_with_zero_delay
;
467 bool ConsentProviderDelegate::IsWhitelistedComponent(
468 const Extension
& extension
) {
469 for (const auto& whitelisted_id
: kRequestFileSystemComponentWhitelist
) {
470 if (extension
.id().compare(whitelisted_id
) == 0)
478 } // namespace file_system_api
480 #if defined(OS_CHROMEOS)
481 using file_system_api::ConsentProvider
;
484 bool FileSystemGetDisplayPathFunction::RunSync() {
485 std::string filesystem_name
;
486 std::string filesystem_path
;
487 EXTENSION_FUNCTION_VALIDATE(args_
->GetString(0, &filesystem_name
));
488 EXTENSION_FUNCTION_VALIDATE(args_
->GetString(1, &filesystem_path
));
490 base::FilePath file_path
;
491 if (!app_file_handler_util::ValidateFileEntryAndGetPath(
492 filesystem_name
, filesystem_path
,
493 render_frame_host()->GetProcess()->GetID(), &file_path
, &error_
))
496 file_path
= path_util::PrettifyPath(file_path
);
497 SetResult(new base::StringValue(file_path
.value()));
501 FileSystemEntryFunction::FileSystemEntryFunction()
503 is_directory_(false),
506 void FileSystemEntryFunction::PrepareFilesForWritableApp(
507 const std::vector
<base::FilePath
>& paths
) {
508 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
509 app_file_handler_util::PrepareFilesForWritableApp(
513 base::Bind(&FileSystemEntryFunction::RegisterFileSystemsAndSendResponse
,
516 base::Bind(&FileSystemEntryFunction::HandleWritableFileError
, this));
519 void FileSystemEntryFunction::RegisterFileSystemsAndSendResponse(
520 const std::vector
<base::FilePath
>& paths
) {
521 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
522 if (!render_frame_host())
526 for (std::vector
<base::FilePath
>::const_iterator it
= paths
.begin();
527 it
!= paths
.end(); ++it
) {
528 AddEntryToResponse(*it
, "");
533 void FileSystemEntryFunction::CreateResponse() {
535 response_
= new base::DictionaryValue();
536 base::ListValue
* list
= new base::ListValue();
537 response_
->Set("entries", list
);
538 response_
->SetBoolean("multiple", multiple_
);
539 SetResult(response_
);
542 void FileSystemEntryFunction::AddEntryToResponse(
543 const base::FilePath
& path
,
544 const std::string
& id_override
) {
546 GrantedFileEntry file_entry
= app_file_handler_util::CreateFileEntry(
549 render_frame_host()->GetProcess()->GetID(),
552 base::ListValue
* entries
;
553 bool success
= response_
->GetList("entries", &entries
);
556 base::DictionaryValue
* entry
= new base::DictionaryValue();
557 entry
->SetString("fileSystemId", file_entry
.filesystem_id
);
558 entry
->SetString("baseName", file_entry
.registered_name
);
559 if (id_override
.empty())
560 entry
->SetString("id", file_entry
.id
);
562 entry
->SetString("id", id_override
);
563 entry
->SetBoolean("isDirectory", is_directory_
);
564 entries
->Append(entry
);
567 void FileSystemEntryFunction::HandleWritableFileError(
568 const base::FilePath
& error_path
) {
569 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
570 error_
= base::StringPrintf(kWritableFileErrorFormat
,
571 error_path
.BaseName().AsUTF8Unsafe().c_str());
575 bool FileSystemGetWritableEntryFunction::RunAsync() {
576 std::string filesystem_name
;
577 std::string filesystem_path
;
578 EXTENSION_FUNCTION_VALIDATE(args_
->GetString(0, &filesystem_name
));
579 EXTENSION_FUNCTION_VALIDATE(args_
->GetString(1, &filesystem_path
));
581 if (!app_file_handler_util::HasFileSystemWritePermission(extension_
.get())) {
582 error_
= kRequiresFileSystemWriteError
;
586 if (!app_file_handler_util::ValidateFileEntryAndGetPath(
587 filesystem_name
, filesystem_path
,
588 render_frame_host()->GetProcess()->GetID(), &path_
, &error_
))
591 content::BrowserThread::PostTaskAndReply(
592 content::BrowserThread::FILE,
595 &FileSystemGetWritableEntryFunction::SetIsDirectoryOnFileThread
,
598 &FileSystemGetWritableEntryFunction::CheckPermissionAndSendResponse
,
603 void FileSystemGetWritableEntryFunction::CheckPermissionAndSendResponse() {
604 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
606 !extension_
->permissions_data()->HasAPIPermission(
607 APIPermission::kFileSystemDirectory
)) {
608 error_
= kRequiresFileSystemDirectoryError
;
611 std::vector
<base::FilePath
> paths
;
612 paths
.push_back(path_
);
613 PrepareFilesForWritableApp(paths
);
616 void FileSystemGetWritableEntryFunction::SetIsDirectoryOnFileThread() {
617 DCHECK_CURRENTLY_ON(content::BrowserThread::FILE);
618 if (base::DirectoryExists(path_
)) {
619 is_directory_
= true;
623 bool FileSystemIsWritableEntryFunction::RunSync() {
624 std::string filesystem_name
;
625 std::string filesystem_path
;
626 EXTENSION_FUNCTION_VALIDATE(args_
->GetString(0, &filesystem_name
));
627 EXTENSION_FUNCTION_VALIDATE(args_
->GetString(1, &filesystem_path
));
629 std::string filesystem_id
;
630 if (!storage::CrackIsolatedFileSystemName(filesystem_name
, &filesystem_id
)) {
631 error_
= app_file_handler_util::kInvalidParameters
;
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
,
641 SetResult(new base::FundamentalValue(is_writable
));
645 // Handles showing a dialog to the user to ask for the filename for a file to
647 class FileSystemChooseEntryFunction::FilePicker
648 : public ui::SelectFileDialog::Listener
{
650 FilePicker(FileSystemChooseEntryFunction
* function
,
651 content::WebContents
* web_contents
,
652 const base::FilePath
& suggested_name
,
653 const ui::SelectFileDialog::FileTypeInfo
& file_type_info
,
654 ui::SelectFileDialog::Type picker_type
)
655 : function_(function
) {
656 select_file_dialog_
= ui::SelectFileDialog::Create(
657 this, new ChromeSelectFilePolicy(web_contents
));
658 gfx::NativeWindow owning_window
= web_contents
?
659 platform_util::GetTopLevel(web_contents
->GetNativeView()) :
662 if (g_skip_picker_for_test
) {
663 if (g_use_suggested_path_for_test
) {
664 content::BrowserThread::PostTask(content::BrowserThread::UI
, FROM_HERE
,
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
,
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
,
681 &FileSystemChooseEntryFunction::FilePicker::MultiFilesSelected
,
682 base::Unretained(this),
683 *g_paths_to_be_picked_for_test
,
684 static_cast<void*>(NULL
)));
686 content::BrowserThread::PostTask(content::BrowserThread::UI
, FROM_HERE
,
688 &FileSystemChooseEntryFunction::FilePicker::
689 FileSelectionCanceled
,
690 base::Unretained(this), static_cast<void*>(NULL
)));
695 select_file_dialog_
->SelectFile(picker_type
,
700 base::FilePath::StringType(),
705 ~FilePicker() override
{}
708 // ui::SelectFileDialog::Listener implementation.
709 void FileSelected(const base::FilePath
& path
,
711 void* params
) override
{
712 std::vector
<base::FilePath
> paths
;
713 paths
.push_back(path
);
714 MultiFilesSelected(paths
, params
);
717 void FileSelectedWithExtraInfo(const ui::SelectedFileInfo
& file
,
719 void* params
) override
{
720 // Normally, file.local_path is used because it is a native path to the
721 // local read-only cached file in the case of remote file system like
722 // Chrome OS's Google Drive integration. Here, however, |file.file_path| is
723 // necessary because we need to create a FileEntry denoting the remote file,
724 // not its cache. On other platforms than Chrome OS, they are the same.
726 // TODO(kinaba): remove this, once after the file picker implements proper
727 // switch of the path treatment depending on the |support_drive| flag.
728 FileSelected(file
.file_path
, index
, params
);
731 void MultiFilesSelected(const std::vector
<base::FilePath
>& files
,
732 void* params
) override
{
733 function_
->FilesSelected(files
);
737 void MultiFilesSelectedWithExtraInfo(
738 const std::vector
<ui::SelectedFileInfo
>& files
,
739 void* params
) override
{
740 std::vector
<base::FilePath
> paths
;
741 for (std::vector
<ui::SelectedFileInfo
>::const_iterator it
= files
.begin();
742 it
!= files
.end(); ++it
) {
743 paths
.push_back(it
->file_path
);
745 MultiFilesSelected(paths
, params
);
748 void FileSelectionCanceled(void* params
) override
{
749 function_
->FileSelectionCanceled();
753 scoped_refptr
<ui::SelectFileDialog
> select_file_dialog_
;
754 scoped_refptr
<FileSystemChooseEntryFunction
> function_
;
756 DISALLOW_COPY_AND_ASSIGN(FilePicker
);
759 void FileSystemChooseEntryFunction::ShowPicker(
760 const ui::SelectFileDialog::FileTypeInfo
& file_type_info
,
761 ui::SelectFileDialog::Type picker_type
) {
762 // TODO(asargent/benwells) - As a short term remediation for crbug.com/179010
763 // we're adding the ability for a whitelisted extension to use this API since
764 // chrome.fileBrowserHandler.selectFile is ChromeOS-only. Eventually we'd
765 // like a better solution and likely this code will go back to being
766 // platform-app only.
767 content::WebContents
* const web_contents
=
768 extension_
->is_platform_app()
769 ? GetWebContentsForRenderFrameHost(GetProfile(), render_frame_host())
770 : GetAssociatedWebContents();
772 error_
= kInvalidCallingPage
;
777 // The file picker will hold a reference to this function instance, preventing
778 // its destruction (and subsequent sending of the function response) until the
779 // user has selected a file or cancelled the picker. At that point, the picker
780 // will delete itself, which will also free the function instance.
782 this, web_contents
, initial_path_
, file_type_info
, picker_type
);
786 void FileSystemChooseEntryFunction::SkipPickerAndAlwaysSelectPathForTest(
787 base::FilePath
* path
) {
788 g_skip_picker_for_test
= true;
789 g_use_suggested_path_for_test
= false;
790 g_path_to_be_picked_for_test
= path
;
791 g_paths_to_be_picked_for_test
= NULL
;
794 void FileSystemChooseEntryFunction::SkipPickerAndAlwaysSelectPathsForTest(
795 std::vector
<base::FilePath
>* paths
) {
796 g_skip_picker_for_test
= true;
797 g_use_suggested_path_for_test
= false;
798 g_paths_to_be_picked_for_test
= paths
;
802 void FileSystemChooseEntryFunction::SkipPickerAndSelectSuggestedPathForTest() {
803 g_skip_picker_for_test
= true;
804 g_use_suggested_path_for_test
= true;
805 g_path_to_be_picked_for_test
= NULL
;
806 g_paths_to_be_picked_for_test
= NULL
;
810 void FileSystemChooseEntryFunction::SkipPickerAndAlwaysCancelForTest() {
811 g_skip_picker_for_test
= true;
812 g_use_suggested_path_for_test
= false;
813 g_path_to_be_picked_for_test
= NULL
;
814 g_paths_to_be_picked_for_test
= NULL
;
818 void FileSystemChooseEntryFunction::StopSkippingPickerForTest() {
819 g_skip_picker_for_test
= false;
823 void FileSystemChooseEntryFunction::SkipDirectoryConfirmationForTest() {
824 g_skip_directory_confirmation_for_test
= true;
825 g_allow_directory_access_for_test
= true;
829 void FileSystemChooseEntryFunction::AutoCancelDirectoryConfirmationForTest() {
830 g_skip_directory_confirmation_for_test
= true;
831 g_allow_directory_access_for_test
= false;
835 void FileSystemChooseEntryFunction::StopSkippingDirectoryConfirmationForTest() {
836 g_skip_directory_confirmation_for_test
= false;
840 void FileSystemChooseEntryFunction::RegisterTempExternalFileSystemForTest(
841 const std::string
& name
, const base::FilePath
& path
) {
842 // For testing on Chrome OS, where to deal with remote and local paths
843 // smoothly, all accessed paths need to be registered in the list of
844 // external mount points.
845 storage::ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
847 storage::kFileSystemTypeNativeLocal
,
848 storage::FileSystemMountOption(),
852 void FileSystemChooseEntryFunction::SetInitialPathOnFileThread(
853 const base::FilePath
& suggested_name
,
854 const base::FilePath
& previous_path
) {
855 DCHECK_CURRENTLY_ON(content::BrowserThread::FILE);
856 if (!previous_path
.empty() && base::DirectoryExists(previous_path
)) {
857 initial_path_
= previous_path
.Append(suggested_name
);
859 base::FilePath documents_dir
;
860 if (PathService::Get(chrome::DIR_USER_DOCUMENTS
, &documents_dir
)) {
861 initial_path_
= documents_dir
.Append(suggested_name
);
863 initial_path_
= suggested_name
;
868 void FileSystemChooseEntryFunction::FilesSelected(
869 const std::vector
<base::FilePath
>& paths
) {
870 DCHECK(!paths
.empty());
871 base::FilePath last_choose_directory
;
873 last_choose_directory
= paths
[0];
875 last_choose_directory
= paths
[0].DirName();
877 file_system_api::SetLastChooseEntryDirectory(
878 ExtensionPrefs::Get(GetProfile()),
880 last_choose_directory
);
882 // Get the WebContents for the app window to be the parent window of the
883 // confirmation dialog if necessary.
884 content::WebContents
* const web_contents
=
885 GetWebContentsForRenderFrameHost(GetProfile(), render_frame_host());
887 error_
= kInvalidCallingPage
;
892 DCHECK_EQ(paths
.size(), 1u);
893 bool non_native_path
= false;
894 #if defined(OS_CHROMEOS)
896 file_manager::util::IsUnderNonNativeLocalPath(GetProfile(), paths
[0]);
899 content::BrowserThread::PostTask(
900 content::BrowserThread::FILE,
903 &FileSystemChooseEntryFunction::ConfirmDirectoryAccessOnFileThread
,
911 OnDirectoryAccessConfirmed(paths
);
914 void FileSystemChooseEntryFunction::FileSelectionCanceled() {
915 error_
= kUserCancelled
;
919 void FileSystemChooseEntryFunction::ConfirmDirectoryAccessOnFileThread(
920 bool non_native_path
,
921 const std::vector
<base::FilePath
>& paths
,
922 content::WebContents
* web_contents
) {
923 const base::FilePath check_path
=
924 non_native_path
? paths
[0] : base::MakeAbsoluteFilePath(paths
[0]);
925 if (check_path
.empty()) {
926 content::BrowserThread::PostTask(
927 content::BrowserThread::UI
,
929 base::Bind(&FileSystemChooseEntryFunction::FileSelectionCanceled
,
934 for (size_t i
= 0; i
< arraysize(kGraylistedPaths
); i
++) {
935 base::FilePath graylisted_path
;
936 if (PathService::Get(kGraylistedPaths
[i
], &graylisted_path
) &&
937 (check_path
== graylisted_path
||
938 check_path
.IsParent(graylisted_path
))) {
939 if (g_skip_directory_confirmation_for_test
) {
940 if (g_allow_directory_access_for_test
) {
943 content::BrowserThread::PostTask(
944 content::BrowserThread::UI
,
946 base::Bind(&FileSystemChooseEntryFunction::FileSelectionCanceled
,
952 content::BrowserThread::PostTask(
953 content::BrowserThread::UI
,
956 CreateDirectoryAccessConfirmationDialog
,
957 app_file_handler_util::HasFileSystemWritePermission(
959 base::UTF8ToUTF16(extension_
->name()),
962 &FileSystemChooseEntryFunction::OnDirectoryAccessConfirmed
,
965 base::Bind(&FileSystemChooseEntryFunction::FileSelectionCanceled
,
971 content::BrowserThread::PostTask(
972 content::BrowserThread::UI
,
974 base::Bind(&FileSystemChooseEntryFunction::OnDirectoryAccessConfirmed
,
978 void FileSystemChooseEntryFunction::OnDirectoryAccessConfirmed(
979 const std::vector
<base::FilePath
>& paths
) {
980 if (app_file_handler_util::HasFileSystemWritePermission(extension_
.get())) {
981 PrepareFilesForWritableApp(paths
);
985 // Don't need to check the file, it's for reading.
986 RegisterFileSystemsAndSendResponse(paths
);
989 void FileSystemChooseEntryFunction::BuildFileTypeInfo(
990 ui::SelectFileDialog::FileTypeInfo
* file_type_info
,
991 const base::FilePath::StringType
& suggested_extension
,
992 const AcceptOptions
* accepts
,
993 const bool* acceptsAllTypes
) {
994 file_type_info
->include_all_files
= true;
996 file_type_info
->include_all_files
= *acceptsAllTypes
;
998 bool need_suggestion
= !file_type_info
->include_all_files
&&
999 !suggested_extension
.empty();
1002 typedef file_system::AcceptOption AcceptOption
;
1003 for (std::vector
<linked_ptr
<AcceptOption
> >::const_iterator iter
=
1004 accepts
->begin(); iter
!= accepts
->end(); ++iter
) {
1005 base::string16 description
;
1006 std::vector
<base::FilePath::StringType
> extensions
;
1008 if (!GetFileTypesFromAcceptOption(**iter
, &extensions
, &description
))
1009 continue; // No extensions were found.
1011 file_type_info
->extensions
.push_back(extensions
);
1012 file_type_info
->extension_description_overrides
.push_back(description
);
1014 // If we still need to find suggested_extension, hunt for it inside the
1015 // extensions returned from GetFileTypesFromAcceptOption.
1016 if (need_suggestion
&& std::find(extensions
.begin(),
1017 extensions
.end(), suggested_extension
) != extensions
.end()) {
1018 need_suggestion
= false;
1023 // If there's nothing in our accepted extension list or we couldn't find the
1024 // suggested extension required, then default to accepting all types.
1025 if (file_type_info
->extensions
.empty() || need_suggestion
)
1026 file_type_info
->include_all_files
= true;
1029 void FileSystemChooseEntryFunction::BuildSuggestion(
1030 const std::string
*opt_name
,
1031 base::FilePath
* suggested_name
,
1032 base::FilePath::StringType
* suggested_extension
) {
1034 *suggested_name
= base::FilePath::FromUTF8Unsafe(*opt_name
);
1036 // Don't allow any path components; shorten to the base name. This should
1037 // result in a relative path, but in some cases may not. Clear the
1038 // suggestion for safety if this is the case.
1039 *suggested_name
= suggested_name
->BaseName();
1040 if (suggested_name
->IsAbsolute())
1041 *suggested_name
= base::FilePath();
1043 *suggested_extension
= suggested_name
->Extension();
1044 if (!suggested_extension
->empty())
1045 suggested_extension
->erase(suggested_extension
->begin()); // drop the .
1049 bool FileSystemChooseEntryFunction::RunAsync() {
1050 scoped_ptr
<ChooseEntry::Params
> params(ChooseEntry::Params::Create(*args_
));
1051 EXTENSION_FUNCTION_VALIDATE(params
.get());
1053 base::FilePath suggested_name
;
1054 ui::SelectFileDialog::FileTypeInfo file_type_info
;
1055 ui::SelectFileDialog::Type picker_type
=
1056 ui::SelectFileDialog::SELECT_OPEN_FILE
;
1058 file_system::ChooseEntryOptions
* options
= params
->options
.get();
1060 multiple_
= options
->accepts_multiple
;
1062 picker_type
= ui::SelectFileDialog::SELECT_OPEN_MULTI_FILE
;
1064 if (options
->type
== file_system::CHOOSE_ENTRY_TYPE_OPENWRITABLEFILE
&&
1065 !app_file_handler_util::HasFileSystemWritePermission(
1066 extension_
.get())) {
1067 error_
= kRequiresFileSystemWriteError
;
1069 } else if (options
->type
== file_system::CHOOSE_ENTRY_TYPE_SAVEFILE
) {
1070 if (!app_file_handler_util::HasFileSystemWritePermission(
1071 extension_
.get())) {
1072 error_
= kRequiresFileSystemWriteError
;
1076 error_
= kMultipleUnsupportedError
;
1079 picker_type
= ui::SelectFileDialog::SELECT_SAVEAS_FILE
;
1080 } else if (options
->type
== file_system::CHOOSE_ENTRY_TYPE_OPENDIRECTORY
) {
1081 is_directory_
= true;
1082 if (!extension_
->permissions_data()->HasAPIPermission(
1083 APIPermission::kFileSystemDirectory
)) {
1084 error_
= kRequiresFileSystemDirectoryError
;
1088 error_
= kMultipleUnsupportedError
;
1091 picker_type
= ui::SelectFileDialog::SELECT_FOLDER
;
1094 base::FilePath::StringType suggested_extension
;
1095 BuildSuggestion(options
->suggested_name
.get(), &suggested_name
,
1096 &suggested_extension
);
1098 BuildFileTypeInfo(&file_type_info
, suggested_extension
,
1099 options
->accepts
.get(), options
->accepts_all_types
.get());
1102 file_type_info
.support_drive
= true;
1104 base::FilePath previous_path
= file_system_api::GetLastChooseEntryDirectory(
1105 ExtensionPrefs::Get(GetProfile()), extension()->id());
1107 content::BrowserThread::PostTaskAndReply(
1108 content::BrowserThread::FILE,
1110 base::Bind(&FileSystemChooseEntryFunction::SetInitialPathOnFileThread
,
1114 base::Bind(&FileSystemChooseEntryFunction::ShowPicker
,
1121 bool FileSystemRetainEntryFunction::RunAsync() {
1122 std::string entry_id
;
1123 EXTENSION_FUNCTION_VALIDATE(args_
->GetString(0, &entry_id
));
1124 SavedFilesService
* saved_files_service
= SavedFilesService::Get(GetProfile());
1125 // Add the file to the retain list if it is not already on there.
1126 if (!saved_files_service
->IsRegistered(extension_
->id(), entry_id
)) {
1127 std::string filesystem_name
;
1128 std::string filesystem_path
;
1129 base::FilePath path
;
1130 EXTENSION_FUNCTION_VALIDATE(args_
->GetString(1, &filesystem_name
));
1131 EXTENSION_FUNCTION_VALIDATE(args_
->GetString(2, &filesystem_path
));
1132 if (!app_file_handler_util::ValidateFileEntryAndGetPath(
1133 filesystem_name
, filesystem_path
,
1134 render_frame_host()->GetProcess()->GetID(), &path
, &error_
)) {
1138 std::string filesystem_id
;
1139 if (!storage::CrackIsolatedFileSystemName(filesystem_name
, &filesystem_id
))
1142 const GURL site
= util::GetSiteForExtensionId(extension_id(), GetProfile());
1143 storage::FileSystemContext
* const context
=
1144 content::BrowserContext::GetStoragePartitionForSite(GetProfile(), site
)
1145 ->GetFileSystemContext();
1146 const storage::FileSystemURL url
= context
->CreateCrackedFileSystemURL(
1148 storage::kFileSystemTypeIsolated
,
1149 IsolatedContext::GetInstance()
1150 ->CreateVirtualRootPath(filesystem_id
)
1151 .Append(base::FilePath::FromUTF8Unsafe(filesystem_path
)));
1153 content::BrowserThread::PostTask(
1154 content::BrowserThread::IO
,
1158 &storage::FileSystemOperationRunner::GetMetadata
),
1159 context
->operation_runner()->AsWeakPtr(),
1162 &PassFileInfoToUIThread
,
1163 base::Bind(&FileSystemRetainEntryFunction::RetainFileEntry
,
1170 saved_files_service
->EnqueueFileEntry(extension_
->id(), entry_id
);
1175 void FileSystemRetainEntryFunction::RetainFileEntry(
1176 const std::string
& entry_id
,
1177 const base::FilePath
& path
,
1178 scoped_ptr
<base::File::Info
> file_info
) {
1180 SendResponse(false);
1184 SavedFilesService
* saved_files_service
= SavedFilesService::Get(GetProfile());
1185 saved_files_service
->RegisterFileEntry(
1186 extension_
->id(), entry_id
, path
, file_info
->is_directory
);
1187 saved_files_service
->EnqueueFileEntry(extension_
->id(), entry_id
);
1191 bool FileSystemIsRestorableFunction::RunSync() {
1192 std::string entry_id
;
1193 EXTENSION_FUNCTION_VALIDATE(args_
->GetString(0, &entry_id
));
1194 SetResult(new base::FundamentalValue(SavedFilesService::Get(
1195 GetProfile())->IsRegistered(extension_
->id(), entry_id
)));
1199 bool FileSystemRestoreEntryFunction::RunAsync() {
1200 std::string entry_id
;
1201 bool needs_new_entry
;
1202 EXTENSION_FUNCTION_VALIDATE(args_
->GetString(0, &entry_id
));
1203 EXTENSION_FUNCTION_VALIDATE(args_
->GetBoolean(1, &needs_new_entry
));
1204 const SavedFileEntry
* file_entry
= SavedFilesService::Get(
1205 GetProfile())->GetFileEntry(extension_
->id(), entry_id
);
1207 error_
= kUnknownIdError
;
1211 SavedFilesService::Get(GetProfile())
1212 ->EnqueueFileEntry(extension_
->id(), entry_id
);
1214 // Only create a new file entry if the renderer requests one.
1215 // |needs_new_entry| will be false if the renderer already has an Entry for
1217 if (needs_new_entry
) {
1218 is_directory_
= file_entry
->is_directory
;
1220 AddEntryToResponse(file_entry
->path
, file_entry
->id
);
1226 bool FileSystemObserveDirectoryFunction::RunSync() {
1228 error_
= kUnknownIdError
;
1232 bool FileSystemUnobserveEntryFunction::RunSync() {
1234 error_
= kUnknownIdError
;
1238 bool FileSystemGetObservedEntriesFunction::RunSync() {
1240 error_
= kUnknownIdError
;
1244 #if !defined(OS_CHROMEOS)
1245 ExtensionFunction::ResponseAction
FileSystemRequestFileSystemFunction::Run() {
1246 using api::file_system::RequestFileSystem::Params
;
1247 const scoped_ptr
<Params
> params(Params::Create(*args_
));
1248 EXTENSION_FUNCTION_VALIDATE(params
);
1251 return RespondNow(Error(kNotSupportedOnCurrentPlatformError
));
1254 ExtensionFunction::ResponseAction
FileSystemGetVolumeListFunction::Run() {
1256 return RespondNow(Error(kNotSupportedOnCurrentPlatformError
));
1260 FileSystemRequestFileSystemFunction::FileSystemRequestFileSystemFunction()
1261 : chrome_details_(this) {
1264 FileSystemRequestFileSystemFunction::~FileSystemRequestFileSystemFunction() {
1267 ExtensionFunction::ResponseAction
FileSystemRequestFileSystemFunction::Run() {
1268 using api::file_system::RequestFileSystem::Params
;
1269 const scoped_ptr
<Params
> params(Params::Create(*args_
));
1270 EXTENSION_FUNCTION_VALIDATE(params
);
1272 // Only kiosk apps in kiosk sessions can use this API.
1273 // Additionally it is enabled for whitelisted component extensions and apps.
1274 file_system_api::ConsentProviderDelegate
consent_provider_delegate(
1275 chrome_details_
.GetProfile(), render_frame_host());
1276 file_system_api::ConsentProvider
consent_provider(&consent_provider_delegate
);
1278 if (!consent_provider
.IsGrantable(*extension()))
1279 return RespondNow(Error(kNotSupportedOnNonKioskSessionError
));
1281 using file_manager::VolumeManager
;
1282 using file_manager::Volume
;
1283 VolumeManager
* const volume_manager
=
1284 VolumeManager::Get(chrome_details_
.GetProfile());
1285 DCHECK(volume_manager
);
1287 const bool writable
=
1288 params
->options
.writable
.get() && *params
->options
.writable
.get();
1290 !app_file_handler_util::HasFileSystemWritePermission(extension_
.get())) {
1291 return RespondNow(Error(kRequiresFileSystemWriteError
));
1294 base::WeakPtr
<file_manager::Volume
> volume
=
1295 volume_manager
->FindVolumeById(params
->options
.volume_id
);
1297 return RespondNow(Error(kVolumeNotFoundError
));
1300 util::GetSiteForExtensionId(extension_id(), chrome_details_
.GetProfile());
1301 scoped_refptr
<storage::FileSystemContext
> file_system_context
=
1302 content::BrowserContext::GetStoragePartitionForSite(
1303 chrome_details_
.GetProfile(), site
)->GetFileSystemContext();
1304 storage::ExternalFileSystemBackend
* const backend
=
1305 file_system_context
->external_backend();
1308 base::FilePath virtual_path
;
1309 if (!backend
->GetVirtualPath(volume
->mount_path(), &virtual_path
))
1310 return RespondNow(Error(kSecurityError
));
1312 if (writable
&& (volume
->is_read_only()))
1313 return RespondNow(Error(kSecurityError
));
1315 consent_provider
.RequestConsent(
1316 *extension(), volume
, writable
,
1317 base::Bind(&FileSystemRequestFileSystemFunction::OnConsentReceived
, this,
1319 return RespondLater();
1322 void FileSystemRequestFileSystemFunction::OnConsentReceived(
1323 const base::WeakPtr
<file_manager::Volume
>& volume
,
1325 ConsentProvider::Consent result
) {
1326 using file_manager::VolumeManager
;
1327 using file_manager::Volume
;
1330 case ConsentProvider::CONSENT_REJECTED
:
1331 SetError(kSecurityError
);
1332 SendResponse(false);
1335 case ConsentProvider::CONSENT_IMPOSSIBLE
:
1336 SetError(kConsentImpossible
);
1337 SendResponse(false);
1340 case ConsentProvider::CONSENT_GRANTED
:
1344 if (!volume
.get()) {
1345 SetError(kVolumeNotFoundError
);
1346 SendResponse(false);
1351 util::GetSiteForExtensionId(extension_id(), chrome_details_
.GetProfile());
1352 scoped_refptr
<storage::FileSystemContext
> file_system_context
=
1353 content::BrowserContext::GetStoragePartitionForSite(
1354 chrome_details_
.GetProfile(), site
)->GetFileSystemContext();
1355 storage::ExternalFileSystemBackend
* const backend
=
1356 file_system_context
->external_backend();
1359 base::FilePath virtual_path
;
1360 if (!backend
->GetVirtualPath(volume
->mount_path(), &virtual_path
)) {
1361 SetError(kSecurityError
);
1362 SendResponse(false);
1366 storage::IsolatedContext
* const isolated_context
=
1367 storage::IsolatedContext::GetInstance();
1368 DCHECK(isolated_context
);
1370 const storage::FileSystemURL original_url
=
1371 file_system_context
->CreateCrackedFileSystemURL(
1372 GURL(std::string(kExtensionScheme
) + url::kStandardSchemeSeparator
+
1374 storage::kFileSystemTypeExternal
, virtual_path
);
1376 // Set a fixed register name, as the automatic one would leak the mount point
1378 std::string register_name
= "fs";
1379 const std::string file_system_id
=
1380 isolated_context
->RegisterFileSystemForPath(
1381 storage::kFileSystemTypeNativeForPlatformApp
,
1382 std::string() /* file_system_id */, original_url
.path(),
1384 if (file_system_id
.empty()) {
1385 SetError(kSecurityError
);
1386 SendResponse(false);
1390 backend
->GrantFileAccessToExtension(extension_
->id(), virtual_path
);
1392 // Grant file permissions to the renderer hosting component.
1393 content::ChildProcessSecurityPolicy
* policy
=
1394 content::ChildProcessSecurityPolicy::GetInstance();
1397 // Read-only permisisons.
1398 policy
->GrantReadFile(render_frame_host()->GetProcess()->GetID(),
1399 volume
->mount_path());
1400 policy
->GrantReadFileSystem(render_frame_host()->GetProcess()->GetID(),
1403 // Additional write permissions.
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(),
1411 policy
->GrantDeleteFromFileSystem(
1412 render_frame_host()->GetProcess()->GetID(), file_system_id
);
1413 policy
->GrantCreateFileForFileSystem(
1414 render_frame_host()->GetProcess()->GetID(), file_system_id
);
1417 base::DictionaryValue
* const dict
= new base::DictionaryValue();
1418 dict
->SetString("file_system_id", file_system_id
);
1419 dict
->SetString("file_system_path", register_name
);
1425 FileSystemGetVolumeListFunction::FileSystemGetVolumeListFunction()
1426 : chrome_details_(this) {
1429 FileSystemGetVolumeListFunction::~FileSystemGetVolumeListFunction() {
1432 ExtensionFunction::ResponseAction
FileSystemGetVolumeListFunction::Run() {
1433 // Only kiosk apps in kiosk sessions can use this API.
1434 // Additionally it is enabled for whitelisted component extensions and apps.
1435 file_system_api::ConsentProviderDelegate
consent_provider_delegate(
1436 chrome_details_
.GetProfile(), render_frame_host());
1437 file_system_api::ConsentProvider
consent_provider(&consent_provider_delegate
);
1439 if (!consent_provider
.IsGrantable(*extension()))
1440 return RespondNow(Error(kNotSupportedOnNonKioskSessionError
));
1441 using api::file_system::Volume
;
1442 std::vector
<linked_ptr
<Volume
>> result_volume_list
;
1443 FillVolumeList(chrome_details_
.GetProfile(), &result_volume_list
);
1446 ArgumentList(api::file_system::GetVolumeList::Results::Create(
1447 result_volume_list
).Pass()));
1451 } // namespace extensions