OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/extensions/api/file_system/file_system_api.h" | 5 #include "chrome/browser/extensions/api/file_system/file_system_api.h" |
6 | 6 |
7 #include "apps/saved_files_service.h" | 7 #include "apps/saved_files_service.h" |
8 #include "apps/shell_window.h" | 8 #include "apps/shell_window.h" |
9 #include "base/bind.h" | 9 #include "base/bind.h" |
10 #include "base/file_util.h" | 10 #include "base/file_util.h" |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
53 using apps::SavedFileEntry; | 53 using apps::SavedFileEntry; |
54 using apps::SavedFilesService; | 54 using apps::SavedFilesService; |
55 using apps::ShellWindow; | 55 using apps::ShellWindow; |
56 using fileapi::IsolatedContext; | 56 using fileapi::IsolatedContext; |
57 | 57 |
58 const char kInvalidParameters[] = "Invalid parameters"; | 58 const char kInvalidParameters[] = "Invalid parameters"; |
59 const char kSecurityError[] = "Security error"; | 59 const char kSecurityError[] = "Security error"; |
60 const char kInvalidCallingPage[] = "Invalid calling page. This function can't " | 60 const char kInvalidCallingPage[] = "Invalid calling page. This function can't " |
61 "be called from a background page."; | 61 "be called from a background page."; |
62 const char kUserCancelled[] = "User cancelled"; | 62 const char kUserCancelled[] = "User cancelled"; |
63 const char kWritableFileError[] = | 63 const char kWritableFileRestrictedLocationError[] = |
64 "Cannot write to file in a restricted location"; | 64 "Cannot write to file in a restricted location"; |
65 const char kWritableFileErrorPrefix[] = "Error opening "; | |
Matt Giuca
2013/07/19 02:28:11
I think this should be a full format string like "
Sam McNally
2013/07/19 04:22:14
Done.
| |
65 const char kRequiresFileSystemWriteError[] = | 66 const char kRequiresFileSystemWriteError[] = |
66 "Operation requires fileSystem.write permission"; | 67 "Operation requires fileSystem.write permission"; |
68 const char kMultipleUnsupportedError[] = | |
69 "acceptsMultiple: true is not supported for 'saveFile'"; | |
67 const char kUnknownIdError[] = "Unknown id"; | 70 const char kUnknownIdError[] = "Unknown id"; |
68 | 71 |
69 namespace file_system = extensions::api::file_system; | 72 namespace file_system = extensions::api::file_system; |
70 namespace ChooseEntry = file_system::ChooseEntry; | 73 namespace ChooseEntry = file_system::ChooseEntry; |
71 | 74 |
72 namespace { | 75 namespace { |
73 | 76 |
74 const int kBlacklistedPaths[] = { | 77 const int kBlacklistedPaths[] = { |
75 chrome::DIR_APP, | 78 chrome::DIR_APP, |
76 chrome::DIR_USER_DATA, | 79 chrome::DIR_USER_DATA, |
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
149 && home_path.AppendRelativePath(source_path, &display_path)) | 152 && home_path.AppendRelativePath(source_path, &display_path)) |
150 return display_path; | 153 return display_path; |
151 #endif | 154 #endif |
152 return source_path; | 155 return source_path; |
153 } | 156 } |
154 #endif // defined(OS_MACOSX) | 157 #endif // defined(OS_MACOSX) |
155 | 158 |
156 bool g_skip_picker_for_test = false; | 159 bool g_skip_picker_for_test = false; |
157 bool g_use_suggested_path_for_test = false; | 160 bool g_use_suggested_path_for_test = false; |
158 base::FilePath* g_path_to_be_picked_for_test; | 161 base::FilePath* g_path_to_be_picked_for_test; |
162 std::vector<base::FilePath>* g_paths_to_be_picked_for_test; | |
159 | 163 |
160 bool GetFileSystemAndPathOfFileEntry( | 164 bool GetFileSystemAndPathOfFileEntry( |
161 const std::string& filesystem_name, | 165 const std::string& filesystem_name, |
162 const std::string& filesystem_path, | 166 const std::string& filesystem_path, |
163 const content::RenderViewHost* render_view_host, | 167 const content::RenderViewHost* render_view_host, |
164 std::string* filesystem_id, | 168 std::string* filesystem_id, |
165 base::FilePath* file_path, | 169 base::FilePath* file_path, |
166 std::string* error) { | 170 std::string* error) { |
167 if (!fileapi::CrackIsolatedFileSystemName(filesystem_name, filesystem_id)) { | 171 if (!fileapi::CrackIsolatedFileSystemName(filesystem_name, filesystem_id)) { |
168 *error = kInvalidParameters; | 172 *error = kInvalidParameters; |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
203 std::string filesystem_id; | 207 std::string filesystem_id; |
204 return GetFileSystemAndPathOfFileEntry(filesystem_name, | 208 return GetFileSystemAndPathOfFileEntry(filesystem_name, |
205 filesystem_path, | 209 filesystem_path, |
206 render_view_host, | 210 render_view_host, |
207 &filesystem_id, | 211 &filesystem_id, |
208 file_path, | 212 file_path, |
209 error); | 213 error); |
210 } | 214 } |
211 | 215 |
212 bool DoCheckWritableFile(const base::FilePath& path, | 216 bool DoCheckWritableFile(const base::FilePath& path, |
213 const base::FilePath& extension_directory) { | 217 const base::FilePath& extension_directory, |
218 std::string* error_message) { | |
214 // Don't allow links. | 219 // Don't allow links. |
215 if (file_util::PathExists(path) && file_util::IsLink(path)) | 220 if (file_util::PathExists(path) && file_util::IsLink(path)) { |
221 *error_message = kWritableFileErrorPrefix + path.BaseName().AsUTF8Unsafe(); | |
216 return false; | 222 return false; |
223 } | |
217 | 224 |
218 if (extension_directory == path || extension_directory.IsParent(path)) | 225 if (extension_directory == path || extension_directory.IsParent(path)) { |
226 *error_message = kWritableFileRestrictedLocationError; | |
219 return false; | 227 return false; |
228 } | |
220 | 229 |
221 bool is_whitelisted_path = false; | 230 bool is_whitelisted_path = false; |
222 | 231 |
223 #if defined(OS_CHROMEOS) | 232 #if defined(OS_CHROMEOS) |
224 for (size_t i = 0; i < arraysize(kWhitelistedPaths); i++) { | 233 for (size_t i = 0; i < arraysize(kWhitelistedPaths); i++) { |
225 base::FilePath whitelisted_path; | 234 base::FilePath whitelisted_path; |
226 if (PathService::Get(kWhitelistedPaths[i], &whitelisted_path) && | 235 if (PathService::Get(kWhitelistedPaths[i], &whitelisted_path) && |
227 (whitelisted_path == path || whitelisted_path.IsParent(path))) { | 236 (whitelisted_path == path || whitelisted_path.IsParent(path))) { |
228 is_whitelisted_path = true; | 237 is_whitelisted_path = true; |
229 break; | 238 break; |
230 } | 239 } |
231 } | 240 } |
232 #endif | 241 #endif |
233 | 242 |
234 if (!is_whitelisted_path) { | 243 if (!is_whitelisted_path) { |
235 for (size_t i = 0; i < arraysize(kBlacklistedPaths); i++) { | 244 for (size_t i = 0; i < arraysize(kBlacklistedPaths); i++) { |
236 base::FilePath blacklisted_path; | 245 base::FilePath blacklisted_path; |
237 if (PathService::Get(kBlacklistedPaths[i], &blacklisted_path) && | 246 if (PathService::Get(kBlacklistedPaths[i], &blacklisted_path) && |
238 (blacklisted_path == path || blacklisted_path.IsParent(path))) { | 247 (blacklisted_path == path || blacklisted_path.IsParent(path))) { |
248 *error_message = kWritableFileRestrictedLocationError; | |
239 return false; | 249 return false; |
240 } | 250 } |
241 } | 251 } |
242 } | 252 } |
243 | 253 |
244 // Create the file if it doesn't already exist. | 254 // Create the file if it doesn't already exist. |
245 base::PlatformFileError error = base::PLATFORM_FILE_OK; | 255 base::PlatformFileError error = base::PLATFORM_FILE_OK; |
246 int creation_flags = base::PLATFORM_FILE_CREATE | | 256 int creation_flags = base::PLATFORM_FILE_CREATE | |
247 base::PLATFORM_FILE_READ | | 257 base::PLATFORM_FILE_READ | |
248 base::PLATFORM_FILE_WRITE; | 258 base::PLATFORM_FILE_WRITE; |
249 base::PlatformFile file = base::CreatePlatformFile(path, creation_flags, | 259 base::PlatformFile file = base::CreatePlatformFile(path, creation_flags, |
250 NULL, &error); | 260 NULL, &error); |
251 // Close the file so we don't keep a lock open. | 261 // Close the file so we don't keep a lock open. |
252 if (file != base::kInvalidPlatformFileValue) | 262 if (file != base::kInvalidPlatformFileValue) |
253 base::ClosePlatformFile(file); | 263 base::ClosePlatformFile(file); |
254 return error == base::PLATFORM_FILE_OK || | 264 if (error != base::PLATFORM_FILE_OK && |
255 error == base::PLATFORM_FILE_ERROR_EXISTS; | 265 error != base::PLATFORM_FILE_ERROR_EXISTS) { |
266 *error_message = kWritableFileErrorPrefix + path.BaseName().AsUTF8Unsafe(); | |
267 return false; | |
268 } | |
269 | |
270 return true; | |
256 } | 271 } |
257 | 272 |
258 void CheckLocalWritableFile(const base::FilePath& path, | 273 // Handles checking whether a list of paths are all OK for writing and calls a |
Matt Giuca
2013/07/19 02:28:11
"Checks whether".
What does "OK" mean? (Not read-
Sam McNally
2013/07/19 04:22:14
Done.
| |
259 const base::FilePath& extension_directory, | 274 // provided on_success or on_failure callback when done. |
260 const base::Closure& on_success, | 275 class WritableFileChecker |
261 const base::Closure& on_failure) { | 276 : public base::RefCountedThreadSafe<WritableFileChecker> { |
262 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); | 277 public: |
263 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, | 278 WritableFileChecker( |
264 DoCheckWritableFile(path, extension_directory) ? on_success : on_failure); | 279 const std::vector<base::FilePath>& paths, |
265 } | 280 Profile* profile, |
281 const base::FilePath& extension_path, | |
282 const base::Closure& on_success, | |
283 const base::Callback<void(const std::string&)>& on_failure) | |
284 : outstanding_tasks_(1), | |
285 extension_path_(extension_path), | |
286 on_success_(on_success), | |
287 on_failure_(on_failure) { | |
288 #if defined(OS_CHROMEOS) | |
289 if (drive::util::IsUnderDriveMountPoint(paths[0])) { | |
290 outstanding_tasks_ = paths.size(); | |
291 for (std::vector<base::FilePath>::const_iterator it = paths.begin(); | |
292 it != paths.end(); ++it) { | |
293 DCHECK(drive::util::IsUnderDriveMountPoint(*it)); | |
294 drive::util::PrepareWritableFileAndRun( | |
295 profile, | |
296 *it, | |
297 base::Bind(&WritableFileChecker::CheckRemoteWritableFile, this)); | |
298 } | |
299 return; | |
300 } | |
301 #endif | |
302 content::BrowserThread::PostTask( | |
303 content::BrowserThread::FILE, | |
304 FROM_HERE, | |
305 base::Bind(&WritableFileChecker::CheckLocalWritableFiles, this, paths)); | |
306 } | |
307 | |
308 private: | |
309 friend class base::RefCountedThreadSafe<WritableFileChecker>; | |
310 virtual ~WritableFileChecker() {} | |
311 | |
312 void TaskDone() { | |
313 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
314 if (!--outstanding_tasks_) { | |
315 if (error_.empty()) | |
316 on_success_.Run(); | |
317 else | |
318 on_failure_.Run(error_); | |
319 } | |
320 } | |
321 | |
322 void Error(const std::string& message) { | |
323 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
324 DCHECK(!message.empty()); | |
325 error_ = message; | |
326 TaskDone(); | |
327 } | |
328 | |
329 void CheckLocalWritableFiles(const std::vector<base::FilePath>& paths) { | |
330 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); | |
331 std::string error; | |
332 for (std::vector<base::FilePath>::const_iterator it = paths.begin(); | |
333 it != paths.end(); ++it) { | |
334 if (!DoCheckWritableFile(*it, extension_path_, &error)) { | |
335 content::BrowserThread::PostTask( | |
336 content::BrowserThread::UI, | |
337 FROM_HERE, | |
338 base::Bind(&WritableFileChecker::Error, this, error)); | |
339 return; | |
340 } | |
341 } | |
342 content::BrowserThread::PostTask( | |
343 content::BrowserThread::UI, | |
344 FROM_HERE, | |
345 base::Bind(&WritableFileChecker::TaskDone, this)); | |
346 } | |
266 | 347 |
267 #if defined(OS_CHROMEOS) | 348 #if defined(OS_CHROMEOS) |
268 void CheckRemoteWritableFile(const base::Closure& on_success, | 349 void CheckRemoteWritableFile(drive::FileError error, |
269 const base::Closure& on_failure, | 350 const base::FilePath& path) { |
270 drive::FileError error, | 351 if (error == drive::FILE_ERROR_OK) { |
271 const base::FilePath& path) { | 352 content::BrowserThread::PostTask( |
272 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, | 353 content::BrowserThread::UI, |
273 error == drive::FILE_ERROR_OK ? on_success : on_failure); | 354 FROM_HERE, |
274 } | 355 base::Bind(&WritableFileChecker::TaskDone, this)); |
356 } else { | |
357 content::BrowserThread::PostTask( | |
358 content::BrowserThread::UI, | |
359 FROM_HERE, | |
360 base::Bind( | |
361 &WritableFileChecker::Error, | |
362 this, | |
363 kWritableFileErrorPrefix + path.BaseName().AsUTF8Unsafe())); | |
364 } | |
365 } | |
275 #endif | 366 #endif |
276 | 367 |
368 int outstanding_tasks_; | |
369 const base::FilePath extension_path_; | |
370 std::string error_; | |
371 base::Closure on_success_; | |
372 base::Callback<void(const std::string&)> on_failure_; | |
373 }; | |
374 | |
277 // Expand the mime-types and extensions provided in an AcceptOption, returning | 375 // Expand the mime-types and extensions provided in an AcceptOption, returning |
278 // them within the passed extension vector. Returns false if no valid types | 376 // them within the passed extension vector. Returns false if no valid types |
279 // were found. | 377 // were found. |
280 bool GetFileTypesFromAcceptOption( | 378 bool GetFileTypesFromAcceptOption( |
281 const file_system::AcceptOption& accept_option, | 379 const file_system::AcceptOption& accept_option, |
282 std::vector<base::FilePath::StringType>* extensions, | 380 std::vector<base::FilePath::StringType>* extensions, |
283 string16* description) { | 381 string16* description) { |
284 std::set<base::FilePath::StringType> extension_set; | 382 std::set<base::FilePath::StringType> extension_set; |
285 int description_id = 0; | 383 int description_id = 0; |
286 | 384 |
(...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
389 | 487 |
390 bool FileSystemEntryFunction::HasFileSystemWritePermission() { | 488 bool FileSystemEntryFunction::HasFileSystemWritePermission() { |
391 const extensions::Extension* extension = GetExtension(); | 489 const extensions::Extension* extension = GetExtension(); |
392 if (!extension) | 490 if (!extension) |
393 return false; | 491 return false; |
394 | 492 |
395 return extension->HasAPIPermission(APIPermission::kFileSystemWrite); | 493 return extension->HasAPIPermission(APIPermission::kFileSystemWrite); |
396 } | 494 } |
397 | 495 |
398 void FileSystemEntryFunction::CheckWritableFile(const base::FilePath& path) { | 496 void FileSystemEntryFunction::CheckWritableFile(const base::FilePath& path) { |
399 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | 497 std::vector<base::FilePath> paths; |
400 base::Closure on_success = | 498 paths.push_back(path); |
401 base::Bind(&FileSystemEntryFunction::RegisterFileSystemAndSendResponse, | 499 CheckWritableFiles(paths, false); |
402 this, path, WRITABLE); | |
403 base::Closure on_failure = | |
404 base::Bind(&FileSystemEntryFunction::HandleWritableFileError, this); | |
405 | |
406 #if defined(OS_CHROMEOS) | |
407 if (drive::util::IsUnderDriveMountPoint(path)) { | |
408 drive::util::PrepareWritableFileAndRun(profile_, path, | |
409 base::Bind(&CheckRemoteWritableFile, on_success, on_failure)); | |
410 return; | |
411 } | |
412 #endif | |
413 content::BrowserThread::PostTask(content::BrowserThread::FILE, FROM_HERE, | |
414 base::Bind(&CheckLocalWritableFile, path, extension_->path(), on_success, | |
415 on_failure)); | |
416 } | 500 } |
417 | 501 |
418 void FileSystemEntryFunction::RegisterFileSystemAndSendResponse( | 502 void FileSystemEntryFunction::CheckWritableFiles( |
419 const base::FilePath& path, EntryType entry_type) { | 503 const std::vector<base::FilePath>& paths, bool multiple) { |
420 RegisterFileSystemAndSendResponseWithIdOverride(path, entry_type, ""); | 504 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
505 scoped_refptr<WritableFileChecker> helper = new WritableFileChecker( | |
506 paths, profile_, extension_->path(), | |
507 base::Bind( | |
508 &FileSystemEntryFunction::RegisterFileSystemsAndSendResponse, | |
509 this, paths, WRITABLE, multiple), | |
510 base::Bind(&FileSystemEntryFunction::HandleWritableFileError, this)); | |
511 } | |
512 | |
513 void FileSystemEntryFunction::RegisterFileSystemsAndSendResponse( | |
514 const std::vector<base::FilePath>& paths, | |
515 EntryType entry_type, | |
516 bool multiple) { | |
517 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
518 | |
519 base::DictionaryValue* response = new base::DictionaryValue(); | |
520 base::ListValue* list = new base::ListValue(); | |
521 response->Set("entries", list); | |
522 response->SetBoolean("multiple", multiple); | |
523 SetResult(response); | |
524 for (std::vector<base::FilePath>::const_iterator it = paths.begin(); | |
525 it != paths.end(); ++it) { | |
526 list->Append(BuildEntryDict(*it, entry_type)); | |
527 } | |
528 | |
529 SendResponse(true); | |
421 } | 530 } |
422 | 531 |
423 void FileSystemEntryFunction::RegisterFileSystemAndSendResponseWithIdOverride( | 532 void FileSystemEntryFunction::RegisterFileSystemAndSendResponseWithIdOverride( |
424 const base::FilePath& path, EntryType entry_type, const std::string& id) { | 533 const base::FilePath& path, EntryType entry_type, const std::string& id) { |
425 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | 534 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
426 | 535 |
427 fileapi::IsolatedContext* isolated_context = | 536 base::DictionaryValue* response = new base::DictionaryValue(); |
428 fileapi::IsolatedContext::GetInstance(); | 537 base::ListValue* list = new base::ListValue(); |
429 DCHECK(isolated_context); | 538 response->Set("entries", list); |
539 response->SetBoolean("multiple", false); | |
540 SetResult(response); | |
541 list->Append(BuildEntryDictWithIdOverride(path, entry_type, id)); | |
430 | 542 |
543 SendResponse(true); | |
544 } | |
545 | |
546 base::DictionaryValue* FileSystemEntryFunction::BuildEntryDictWithIdOverride( | |
547 const base::FilePath& path, EntryType entry_type, const std::string& id) { | |
431 bool writable = entry_type == WRITABLE; | 548 bool writable = entry_type == WRITABLE; |
432 extensions::app_file_handler_util::GrantedFileEntry file_entry = | 549 extensions::app_file_handler_util::GrantedFileEntry file_entry = |
433 extensions::app_file_handler_util::CreateFileEntry(profile(), | 550 extensions::app_file_handler_util::CreateFileEntry(profile(), |
434 GetExtension()->id(), render_view_host_->GetProcess()->GetID(), path, | 551 GetExtension()->id(), render_view_host_->GetProcess()->GetID(), path, |
435 writable); | 552 writable); |
436 | 553 |
437 base::DictionaryValue* dict = new base::DictionaryValue(); | 554 base::DictionaryValue* dict = new base::DictionaryValue(); |
438 SetResult(dict); | |
439 dict->SetString("fileSystemId", file_entry.filesystem_id); | 555 dict->SetString("fileSystemId", file_entry.filesystem_id); |
440 dict->SetString("baseName", file_entry.registered_name); | 556 dict->SetString("baseName", file_entry.registered_name); |
441 if (id.empty()) | 557 if (id.empty()) |
442 dict->SetString("id", file_entry.id); | 558 dict->SetString("id", file_entry.id); |
443 else | 559 else |
444 dict->SetString("id", id); | 560 dict->SetString("id", id); |
445 | 561 return dict; |
446 SendResponse(true); | |
447 } | 562 } |
448 | 563 |
449 void FileSystemEntryFunction::HandleWritableFileError() { | 564 base::DictionaryValue* FileSystemEntryFunction::BuildEntryDict( |
565 const base::FilePath& path, EntryType entry_type) { | |
566 return BuildEntryDictWithIdOverride(path, entry_type, ""); | |
567 } | |
568 | |
569 void FileSystemEntryFunction::HandleWritableFileError( | |
570 const std::string& error) { | |
450 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | 571 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
451 error_ = kWritableFileError; | 572 error_ = error; |
452 SendResponse(false); | 573 SendResponse(false); |
453 } | 574 } |
454 | 575 |
455 bool FileSystemGetWritableEntryFunction::RunImpl() { | 576 bool FileSystemGetWritableEntryFunction::RunImpl() { |
456 std::string filesystem_name; | 577 std::string filesystem_name; |
457 std::string filesystem_path; | 578 std::string filesystem_path; |
458 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &filesystem_name)); | 579 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &filesystem_name)); |
459 EXTENSION_FUNCTION_VALIDATE(args_->GetString(1, &filesystem_path)); | 580 EXTENSION_FUNCTION_VALIDATE(args_->GetString(1, &filesystem_path)); |
460 | 581 |
461 if (!HasFileSystemWritePermission()) { | 582 if (!HasFileSystemWritePermission()) { |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
497 // Handles showing a dialog to the user to ask for the filename for a file to | 618 // Handles showing a dialog to the user to ask for the filename for a file to |
498 // save or open. | 619 // save or open. |
499 class FileSystemChooseEntryFunction::FilePicker | 620 class FileSystemChooseEntryFunction::FilePicker |
500 : public ui::SelectFileDialog::Listener { | 621 : public ui::SelectFileDialog::Listener { |
501 public: | 622 public: |
502 FilePicker(FileSystemChooseEntryFunction* function, | 623 FilePicker(FileSystemChooseEntryFunction* function, |
503 content::WebContents* web_contents, | 624 content::WebContents* web_contents, |
504 const base::FilePath& suggested_name, | 625 const base::FilePath& suggested_name, |
505 const ui::SelectFileDialog::FileTypeInfo& file_type_info, | 626 const ui::SelectFileDialog::FileTypeInfo& file_type_info, |
506 ui::SelectFileDialog::Type picker_type, | 627 ui::SelectFileDialog::Type picker_type, |
507 EntryType entry_type) | 628 EntryType entry_type, |
629 bool multiple) | |
508 : entry_type_(entry_type), | 630 : entry_type_(entry_type), |
631 multiple_(multiple), | |
509 function_(function) { | 632 function_(function) { |
510 select_file_dialog_ = ui::SelectFileDialog::Create( | 633 select_file_dialog_ = ui::SelectFileDialog::Create( |
511 this, new ChromeSelectFilePolicy(web_contents)); | 634 this, new ChromeSelectFilePolicy(web_contents)); |
512 gfx::NativeWindow owning_window = web_contents ? | 635 gfx::NativeWindow owning_window = web_contents ? |
513 platform_util::GetTopLevel(web_contents->GetView()->GetNativeView()) : | 636 platform_util::GetTopLevel(web_contents->GetView()->GetNativeView()) : |
514 NULL; | 637 NULL; |
515 | 638 |
516 if (g_skip_picker_for_test) { | 639 if (g_skip_picker_for_test) { |
517 if (g_use_suggested_path_for_test) { | 640 if (g_use_suggested_path_for_test) { |
518 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, | 641 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, |
519 base::Bind( | 642 base::Bind( |
520 &FileSystemChooseEntryFunction::FilePicker::FileSelected, | 643 &FileSystemChooseEntryFunction::FilePicker::FileSelected, |
521 base::Unretained(this), suggested_name, 1, | 644 base::Unretained(this), suggested_name, 1, |
522 static_cast<void*>(NULL))); | 645 static_cast<void*>(NULL))); |
523 } else if (g_path_to_be_picked_for_test) { | 646 } else if (g_path_to_be_picked_for_test) { |
524 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, | 647 content::BrowserThread::PostTask( |
648 content::BrowserThread::UI, FROM_HERE, | |
525 base::Bind( | 649 base::Bind( |
526 &FileSystemChooseEntryFunction::FilePicker::FileSelected, | 650 &FileSystemChooseEntryFunction::FilePicker::FileSelected, |
527 base::Unretained(this), *g_path_to_be_picked_for_test, 1, | 651 base::Unretained(this), *g_path_to_be_picked_for_test, 1, |
528 static_cast<void*>(NULL))); | 652 static_cast<void*>(NULL))); |
653 } else if (g_paths_to_be_picked_for_test) { | |
654 content::BrowserThread::PostTask( | |
655 content::BrowserThread::UI, | |
656 FROM_HERE, | |
657 base::Bind( | |
658 &FileSystemChooseEntryFunction::FilePicker::MultiFilesSelected, | |
659 base::Unretained(this), | |
660 *g_paths_to_be_picked_for_test, | |
661 static_cast<void*>(NULL))); | |
529 } else { | 662 } else { |
530 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, | 663 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, |
531 base::Bind( | 664 base::Bind( |
532 &FileSystemChooseEntryFunction::FilePicker:: | 665 &FileSystemChooseEntryFunction::FilePicker:: |
533 FileSelectionCanceled, | 666 FileSelectionCanceled, |
534 base::Unretained(this), static_cast<void*>(NULL))); | 667 base::Unretained(this), static_cast<void*>(NULL))); |
535 } | 668 } |
536 return; | 669 return; |
537 } | 670 } |
538 | 671 |
539 select_file_dialog_->SelectFile(picker_type, | 672 select_file_dialog_->SelectFile(picker_type, |
540 string16(), | 673 string16(), |
541 suggested_name, | 674 suggested_name, |
542 &file_type_info, | 675 &file_type_info, |
543 0, | 676 0, |
544 base::FilePath::StringType(), | 677 base::FilePath::StringType(), |
545 owning_window, | 678 owning_window, |
546 NULL); | 679 NULL); |
547 } | 680 } |
548 | 681 |
549 virtual ~FilePicker() {} | 682 virtual ~FilePicker() {} |
550 | 683 |
551 private: | 684 private: |
552 // ui::SelectFileDialog::Listener implementation. | 685 // ui::SelectFileDialog::Listener implementation. |
553 virtual void FileSelected(const base::FilePath& path, | 686 virtual void FileSelected(const base::FilePath& path, |
554 int index, | 687 int index, |
555 void* params) OVERRIDE { | 688 void* params) OVERRIDE { |
556 function_->FileSelected(path, entry_type_); | 689 std::vector<base::FilePath> paths; |
557 delete this; | 690 paths.push_back(path); |
691 MultiFilesSelected(paths, params); | |
558 } | 692 } |
559 | 693 |
560 virtual void FileSelectedWithExtraInfo(const ui::SelectedFileInfo& file, | 694 virtual void FileSelectedWithExtraInfo(const ui::SelectedFileInfo& file, |
561 int index, | 695 int index, |
562 void* params) OVERRIDE { | 696 void* params) OVERRIDE { |
563 // Normally, file.local_path is used because it is a native path to the | 697 // Normally, file.local_path is used because it is a native path to the |
564 // local read-only cached file in the case of remote file system like | 698 // local read-only cached file in the case of remote file system like |
565 // Chrome OS's Google Drive integration. Here, however, |file.file_path| is | 699 // Chrome OS's Google Drive integration. Here, however, |file.file_path| is |
566 // necessary because we need to create a FileEntry denoting the remote file, | 700 // necessary because we need to create a FileEntry denoting the remote file, |
567 // not its cache. On other platforms than Chrome OS, they are the same. | 701 // not its cache. On other platforms than Chrome OS, they are the same. |
568 // | 702 // |
569 // TODO(kinaba): remove this, once after the file picker implements proper | 703 // TODO(kinaba): remove this, once after the file picker implements proper |
570 // switch of the path treatment depending on the |support_drive| flag. | 704 // switch of the path treatment depending on the |support_drive| flag. |
571 function_->FileSelected(file.file_path, entry_type_); | 705 FileSelected(file.file_path, index, params); |
706 } | |
707 | |
708 virtual void MultiFilesSelected(const std::vector<base::FilePath>& files, | |
709 void* params) OVERRIDE { | |
710 function_->FilesSelected(files, entry_type_, multiple_); | |
572 delete this; | 711 delete this; |
573 } | 712 } |
574 | 713 |
714 virtual void MultiFilesSelectedWithExtraInfo( | |
715 const std::vector<ui::SelectedFileInfo>& files, | |
716 void* params) OVERRIDE { | |
717 std::vector<base::FilePath> paths; | |
718 for (std::vector<ui::SelectedFileInfo>::const_iterator it = files.begin(); | |
719 it != files.end(); ++it) { | |
720 paths.push_back(it->file_path); | |
721 } | |
722 MultiFilesSelected(paths, params); | |
723 } | |
724 | |
575 virtual void FileSelectionCanceled(void* params) OVERRIDE { | 725 virtual void FileSelectionCanceled(void* params) OVERRIDE { |
576 function_->FileSelectionCanceled(); | 726 function_->FileSelectionCanceled(); |
577 delete this; | 727 delete this; |
578 } | 728 } |
579 | 729 |
580 EntryType entry_type_; | 730 EntryType entry_type_; |
731 bool multiple_; | |
581 | 732 |
582 scoped_refptr<ui::SelectFileDialog> select_file_dialog_; | 733 scoped_refptr<ui::SelectFileDialog> select_file_dialog_; |
583 scoped_refptr<FileSystemChooseEntryFunction> function_; | 734 scoped_refptr<FileSystemChooseEntryFunction> function_; |
584 | 735 |
585 DISALLOW_COPY_AND_ASSIGN(FilePicker); | 736 DISALLOW_COPY_AND_ASSIGN(FilePicker); |
586 }; | 737 }; |
587 | 738 |
588 void FileSystemChooseEntryFunction::ShowPicker( | 739 void FileSystemChooseEntryFunction::ShowPicker( |
589 const ui::SelectFileDialog::FileTypeInfo& file_type_info, | 740 const ui::SelectFileDialog::FileTypeInfo& file_type_info, |
590 ui::SelectFileDialog::Type picker_type, | 741 ui::SelectFileDialog::Type picker_type, |
591 EntryType entry_type) { | 742 EntryType entry_type, |
743 bool multiple) { | |
592 // TODO(asargent/benwells) - As a short term remediation for crbug.com/179010 | 744 // TODO(asargent/benwells) - As a short term remediation for crbug.com/179010 |
593 // we're adding the ability for a whitelisted extension to use this API since | 745 // we're adding the ability for a whitelisted extension to use this API since |
594 // chrome.fileBrowserHandler.selectFile is ChromeOS-only. Eventually we'd | 746 // chrome.fileBrowserHandler.selectFile is ChromeOS-only. Eventually we'd |
595 // like a better solution and likely this code will go back to being | 747 // like a better solution and likely this code will go back to being |
596 // platform-app only. | 748 // platform-app only. |
597 content::WebContents* web_contents = NULL; | 749 content::WebContents* web_contents = NULL; |
598 if (extension_->is_platform_app()) { | 750 if (extension_->is_platform_app()) { |
599 ShellWindowRegistry* registry = ShellWindowRegistry::Get(profile()); | 751 ShellWindowRegistry* registry = ShellWindowRegistry::Get(profile()); |
600 DCHECK(registry); | 752 DCHECK(registry); |
601 ShellWindow* shell_window = registry->GetShellWindowForRenderViewHost( | 753 ShellWindow* shell_window = registry->GetShellWindowForRenderViewHost( |
602 render_view_host()); | 754 render_view_host()); |
603 if (!shell_window) { | 755 if (!shell_window) { |
604 error_ = kInvalidCallingPage; | 756 error_ = kInvalidCallingPage; |
605 SendResponse(false); | 757 SendResponse(false); |
606 return; | 758 return; |
607 } | 759 } |
608 web_contents = shell_window->web_contents(); | 760 web_contents = shell_window->web_contents(); |
609 } else { | 761 } else { |
610 web_contents = GetAssociatedWebContents(); | 762 web_contents = GetAssociatedWebContents(); |
611 } | 763 } |
612 // The file picker will hold a reference to this function instance, preventing | 764 // The file picker will hold a reference to this function instance, preventing |
613 // its destruction (and subsequent sending of the function response) until the | 765 // its destruction (and subsequent sending of the function response) until the |
614 // user has selected a file or cancelled the picker. At that point, the picker | 766 // user has selected a file or cancelled the picker. At that point, the picker |
615 // will delete itself, which will also free the function instance. | 767 // will delete itself, which will also free the function instance. |
616 new FilePicker(this, web_contents, initial_path_, file_type_info, | 768 new FilePicker(this, web_contents, initial_path_, file_type_info, |
617 picker_type, entry_type); | 769 picker_type, entry_type, multiple); |
618 } | 770 } |
619 | 771 |
620 // static | 772 // static |
621 void FileSystemChooseEntryFunction::SkipPickerAndAlwaysSelectPathForTest( | 773 void FileSystemChooseEntryFunction::SkipPickerAndAlwaysSelectPathForTest( |
622 base::FilePath* path) { | 774 base::FilePath* path) { |
623 g_skip_picker_for_test = true; | 775 g_skip_picker_for_test = true; |
624 g_use_suggested_path_for_test = false; | 776 g_use_suggested_path_for_test = false; |
625 g_path_to_be_picked_for_test = path; | 777 g_path_to_be_picked_for_test = path; |
778 g_paths_to_be_picked_for_test = NULL; | |
779 } | |
780 | |
781 void FileSystemChooseEntryFunction::SkipPickerAndAlwaysSelectPathsForTest( | |
782 std::vector<base::FilePath>* paths) { | |
783 g_skip_picker_for_test = true; | |
784 g_use_suggested_path_for_test = false; | |
785 g_paths_to_be_picked_for_test = paths; | |
626 } | 786 } |
627 | 787 |
628 // static | 788 // static |
629 void FileSystemChooseEntryFunction::SkipPickerAndSelectSuggestedPathForTest() { | 789 void FileSystemChooseEntryFunction::SkipPickerAndSelectSuggestedPathForTest() { |
630 g_skip_picker_for_test = true; | 790 g_skip_picker_for_test = true; |
631 g_use_suggested_path_for_test = true; | 791 g_use_suggested_path_for_test = true; |
632 g_path_to_be_picked_for_test = NULL; | 792 g_path_to_be_picked_for_test = NULL; |
793 g_paths_to_be_picked_for_test = NULL; | |
633 } | 794 } |
634 | 795 |
635 // static | 796 // static |
636 void FileSystemChooseEntryFunction::SkipPickerAndAlwaysCancelForTest() { | 797 void FileSystemChooseEntryFunction::SkipPickerAndAlwaysCancelForTest() { |
637 g_skip_picker_for_test = true; | 798 g_skip_picker_for_test = true; |
638 g_use_suggested_path_for_test = false; | 799 g_use_suggested_path_for_test = false; |
639 g_path_to_be_picked_for_test = NULL; | 800 g_path_to_be_picked_for_test = NULL; |
801 g_paths_to_be_picked_for_test = NULL; | |
640 } | 802 } |
641 | 803 |
642 // static | 804 // static |
643 void FileSystemChooseEntryFunction::StopSkippingPickerForTest() { | 805 void FileSystemChooseEntryFunction::StopSkippingPickerForTest() { |
644 g_skip_picker_for_test = false; | 806 g_skip_picker_for_test = false; |
645 } | 807 } |
646 | 808 |
647 // static | 809 // static |
648 void FileSystemChooseEntryFunction::RegisterTempExternalFileSystemForTest( | 810 void FileSystemChooseEntryFunction::RegisterTempExternalFileSystemForTest( |
649 const std::string& name, const base::FilePath& path) { | 811 const std::string& name, const base::FilePath& path) { |
(...skipping 13 matching lines...) Expand all Loading... | |
663 } else { | 825 } else { |
664 base::FilePath documents_dir; | 826 base::FilePath documents_dir; |
665 if (PathService::Get(chrome::DIR_USER_DOCUMENTS, &documents_dir)) { | 827 if (PathService::Get(chrome::DIR_USER_DOCUMENTS, &documents_dir)) { |
666 initial_path_ = documents_dir.Append(suggested_name); | 828 initial_path_ = documents_dir.Append(suggested_name); |
667 } else { | 829 } else { |
668 initial_path_ = suggested_name; | 830 initial_path_ = suggested_name; |
669 } | 831 } |
670 } | 832 } |
671 } | 833 } |
672 | 834 |
673 void FileSystemChooseEntryFunction::FileSelected(const base::FilePath& path, | 835 void FileSystemChooseEntryFunction::FilesSelected( |
674 EntryType entry_type) { | 836 const std::vector<base::FilePath>& paths, |
837 EntryType entry_type, | |
838 bool multiple) { | |
675 file_system_api::SetLastChooseEntryDirectory( | 839 file_system_api::SetLastChooseEntryDirectory( |
676 ExtensionPrefs::Get(profile()), | 840 ExtensionPrefs::Get(profile()), GetExtension()->id(), paths[0].DirName()); |
677 GetExtension()->id(), | |
678 path.DirName()); | |
679 if (entry_type == WRITABLE) { | 841 if (entry_type == WRITABLE) { |
680 CheckWritableFile(path); | 842 CheckWritableFiles(paths, multiple); |
681 return; | 843 return; |
682 } | 844 } |
683 | 845 |
684 // Don't need to check the file, it's for reading. | 846 // Don't need to check the file, it's for reading. |
685 RegisterFileSystemAndSendResponse(path, READ_ONLY); | 847 RegisterFileSystemsAndSendResponse(paths, READ_ONLY, multiple); |
686 } | 848 } |
687 | 849 |
688 void FileSystemChooseEntryFunction::FileSelectionCanceled() { | 850 void FileSystemChooseEntryFunction::FileSelectionCanceled() { |
689 error_ = kUserCancelled; | 851 error_ = kUserCancelled; |
690 SendResponse(false); | 852 SendResponse(false); |
691 } | 853 } |
692 | 854 |
693 void FileSystemChooseEntryFunction::BuildFileTypeInfo( | 855 void FileSystemChooseEntryFunction::BuildFileTypeInfo( |
694 ui::SelectFileDialog::FileTypeInfo* file_type_info, | 856 ui::SelectFileDialog::FileTypeInfo* file_type_info, |
695 const base::FilePath::StringType& suggested_extension, | 857 const base::FilePath::StringType& suggested_extension, |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
754 scoped_ptr<ChooseEntry::Params> params(ChooseEntry::Params::Create(*args_)); | 916 scoped_ptr<ChooseEntry::Params> params(ChooseEntry::Params::Create(*args_)); |
755 EXTENSION_FUNCTION_VALIDATE(params.get()); | 917 EXTENSION_FUNCTION_VALIDATE(params.get()); |
756 | 918 |
757 base::FilePath suggested_name; | 919 base::FilePath suggested_name; |
758 ui::SelectFileDialog::FileTypeInfo file_type_info; | 920 ui::SelectFileDialog::FileTypeInfo file_type_info; |
759 EntryType entry_type = READ_ONLY; | 921 EntryType entry_type = READ_ONLY; |
760 ui::SelectFileDialog::Type picker_type = | 922 ui::SelectFileDialog::Type picker_type = |
761 ui::SelectFileDialog::SELECT_OPEN_FILE; | 923 ui::SelectFileDialog::SELECT_OPEN_FILE; |
762 | 924 |
763 file_system::ChooseEntryOptions* options = params->options.get(); | 925 file_system::ChooseEntryOptions* options = params->options.get(); |
926 bool multiple = false; | |
764 if (options) { | 927 if (options) { |
928 multiple = options->accepts_multiple; | |
929 if (multiple) | |
930 picker_type = ui::SelectFileDialog::SELECT_OPEN_MULTI_FILE; | |
765 if (options->type == file_system::CHOOSE_ENTRY_TYPE_OPENWRITABLEFILE) { | 931 if (options->type == file_system::CHOOSE_ENTRY_TYPE_OPENWRITABLEFILE) { |
766 entry_type = WRITABLE; | 932 entry_type = WRITABLE; |
767 } else if (options->type == file_system::CHOOSE_ENTRY_TYPE_SAVEFILE) { | 933 } else if (options->type == file_system::CHOOSE_ENTRY_TYPE_SAVEFILE) { |
934 if (multiple) { | |
935 error_ = kMultipleUnsupportedError; | |
936 return false; | |
937 } | |
768 entry_type = WRITABLE; | 938 entry_type = WRITABLE; |
769 picker_type = ui::SelectFileDialog::SELECT_SAVEAS_FILE; | 939 picker_type = ui::SelectFileDialog::SELECT_SAVEAS_FILE; |
770 } | 940 } |
771 | 941 |
772 base::FilePath::StringType suggested_extension; | 942 base::FilePath::StringType suggested_extension; |
773 BuildSuggestion(options->suggested_name.get(), &suggested_name, | 943 BuildSuggestion(options->suggested_name.get(), &suggested_name, |
774 &suggested_extension); | 944 &suggested_extension); |
775 | 945 |
776 BuildFileTypeInfo(&file_type_info, suggested_extension, | 946 BuildFileTypeInfo(&file_type_info, suggested_extension, |
777 options->accepts.get(), options->accepts_all_types.get()); | 947 options->accepts.get(), options->accepts_all_types.get()); |
(...skipping 13 matching lines...) Expand all Loading... | |
791 &previous_path); | 961 &previous_path); |
792 | 962 |
793 content::BrowserThread::PostTaskAndReply( | 963 content::BrowserThread::PostTaskAndReply( |
794 content::BrowserThread::FILE, | 964 content::BrowserThread::FILE, |
795 FROM_HERE, | 965 FROM_HERE, |
796 base::Bind( | 966 base::Bind( |
797 &FileSystemChooseEntryFunction::SetInitialPathOnFileThread, this, | 967 &FileSystemChooseEntryFunction::SetInitialPathOnFileThread, this, |
798 suggested_name, previous_path), | 968 suggested_name, previous_path), |
799 base::Bind( | 969 base::Bind( |
800 &FileSystemChooseEntryFunction::ShowPicker, this, file_type_info, | 970 &FileSystemChooseEntryFunction::ShowPicker, this, file_type_info, |
801 picker_type, entry_type)); | 971 picker_type, entry_type, multiple)); |
802 return true; | 972 return true; |
803 } | 973 } |
804 | 974 |
805 bool FileSystemRetainEntryFunction::RunImpl() { | 975 bool FileSystemRetainEntryFunction::RunImpl() { |
806 std::string entry_id; | 976 std::string entry_id; |
807 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &entry_id)); | 977 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &entry_id)); |
808 SavedFilesService* saved_files_service = SavedFilesService::Get(profile()); | 978 SavedFilesService* saved_files_service = SavedFilesService::Get(profile()); |
809 // Add the file to the retain list if it is not already on there. | 979 // Add the file to the retain list if it is not already on there. |
810 if (!saved_files_service->IsRegistered(extension_->id(), entry_id) && | 980 if (!saved_files_service->IsRegistered(extension_->id(), entry_id) && |
811 !RetainFileEntry(entry_id)) { | 981 !RetainFileEntry(entry_id)) { |
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
872 // ID that was passed to restoreEntry. | 1042 // ID that was passed to restoreEntry. |
873 RegisterFileSystemAndSendResponseWithIdOverride( | 1043 RegisterFileSystemAndSendResponseWithIdOverride( |
874 file_entry->path, | 1044 file_entry->path, |
875 file_entry->writable ? WRITABLE : READ_ONLY, | 1045 file_entry->writable ? WRITABLE : READ_ONLY, |
876 file_entry->id); | 1046 file_entry->id); |
877 } | 1047 } |
878 return true; | 1048 return true; |
879 } | 1049 } |
880 | 1050 |
881 } // namespace extensions | 1051 } // namespace extensions |
OLD | NEW |