Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(177)

Side by Side Diff: chrome/browser/extensions/api/file_system/consent_provider.cc

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

Powered by Google App Engine
This is Rietveld 408576698