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

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

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

Powered by Google App Engine
This is Rietveld 408576698