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

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

Issue 18331017: Support choosing multiple files with fileSystem.chooseEntry. (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Created 7 years, 5 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 (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
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
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
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
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
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
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698