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

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

Powered by Google App Engine
This is Rietveld 408576698