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

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, 4 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 // posts a task to run AllTasksDone on the UI thread.
319 void TaskDone() {
benwells 2013/08/02 07:41:31 Nit: Add a DCHECK on file thread here. Also consi
Sam McNally 2013/08/02 08:17:52 Done.
320 if (--outstanding_tasks_ == 0) {
321 content::BrowserThread::PostTask(
322 content::BrowserThread::UI,
323 FROM_HERE,
324 base::Bind(&WritableFileChecker::AllTasksDone, this));
325 }
326 }
327
328 // Called on the UI thread when all tasks are done.
329 void AllTasksDone() {
330 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
331 if (error_.empty())
332 on_success_.Run();
333 else
334 on_failure_.Run(error_);
335 }
336
337 // Reports an error in completing a work item. This may be called more than
338 // once, but only the last message will be retained.
339 void Error(const std::string& message) {
340 DCHECK(!message.empty());
341 error_ = message;
342 TaskDone();
343 }
344
345 void CheckLocalWritableFiles(const std::vector<base::FilePath>& paths) {
346 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
347 std::string error;
348 for (std::vector<base::FilePath>::const_iterator it = paths.begin();
349 it != paths.end(); ++it) {
350 if (!DoCheckWritableFile(*it, extension_path_, &error)) {
351 Error(error);
352 return;
353 }
354 }
355 TaskDone();
356 }
266 357
267 #if defined(OS_CHROMEOS) 358 #if defined(OS_CHROMEOS)
268 void CheckRemoteWritableFile(const base::Closure& on_success, 359 void CheckRemoteWritableFile(drive::FileError error,
269 const base::Closure& on_failure, 360 const base::FilePath& path) {
270 drive::FileError error, 361 if (error == drive::FILE_ERROR_OK) {
271 const base::FilePath& path) { 362 TaskDone();
272 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, 363 } else {
273 error == drive::FILE_ERROR_OK ? on_success : on_failure); 364 Error(base::StringPrintf(kWritableFileErrorFormat,
274 } 365 path.BaseName().AsUTF8Unsafe().c_str()));
366 }
367 }
275 #endif 368 #endif
276 369
370 int outstanding_tasks_;
371 const base::FilePath extension_path_;
372 std::string error_;
373 base::Closure on_success_;
374 base::Callback<void(const std::string&)> on_failure_;
375 };
376
277 // Expand the mime-types and extensions provided in an AcceptOption, returning 377 // Expand the mime-types and extensions provided in an AcceptOption, returning
278 // them within the passed extension vector. Returns false if no valid types 378 // them within the passed extension vector. Returns false if no valid types
279 // were found. 379 // were found.
280 bool GetFileTypesFromAcceptOption( 380 bool GetFileTypesFromAcceptOption(
281 const file_system::AcceptOption& accept_option, 381 const file_system::AcceptOption& accept_option,
282 std::vector<base::FilePath::StringType>* extensions, 382 std::vector<base::FilePath::StringType>* extensions,
283 string16* description) { 383 string16* description) {
284 std::set<base::FilePath::StringType> extension_set; 384 std::set<base::FilePath::StringType> extension_set;
285 int description_id = 0; 385 int description_id = 0;
286 386
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after
380 base::FilePath file_path; 480 base::FilePath file_path;
381 if (!GetFilePathOfFileEntry(filesystem_name, filesystem_path, 481 if (!GetFilePathOfFileEntry(filesystem_name, filesystem_path,
382 render_view_host_, &file_path, &error_)) 482 render_view_host_, &file_path, &error_))
383 return false; 483 return false;
384 484
385 file_path = PrettifyPath(file_path); 485 file_path = PrettifyPath(file_path);
386 SetResult(base::Value::CreateStringValue(file_path.value())); 486 SetResult(base::Value::CreateStringValue(file_path.value()));
387 return true; 487 return true;
388 } 488 }
389 489
490 FileSystemEntryFunction::FileSystemEntryFunction()
491 : multiple_(false),
492 entry_type_(READ_ONLY),
493 response_(NULL) {}
494
390 bool FileSystemEntryFunction::HasFileSystemWritePermission() { 495 bool FileSystemEntryFunction::HasFileSystemWritePermission() {
391 const extensions::Extension* extension = GetExtension(); 496 const extensions::Extension* extension = GetExtension();
392 if (!extension) 497 if (!extension)
393 return false; 498 return false;
394 499
395 return extension->HasAPIPermission(APIPermission::kFileSystemWrite); 500 return extension->HasAPIPermission(APIPermission::kFileSystemWrite);
396 } 501 }
397 502
398 void FileSystemEntryFunction::CheckWritableFile(const base::FilePath& path) { 503 void FileSystemEntryFunction::CheckWritableFiles(
504 const std::vector<base::FilePath>& paths) {
399 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 505 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
400 base::Closure on_success = 506 scoped_refptr<WritableFileChecker> helper = new WritableFileChecker(
401 base::Bind(&FileSystemEntryFunction::RegisterFileSystemAndSendResponse, 507 paths, profile_, extension_->path(),
402 this, path, WRITABLE); 508 base::Bind(
403 base::Closure on_failure = 509 &FileSystemEntryFunction::RegisterFileSystemsAndSendResponse,
404 base::Bind(&FileSystemEntryFunction::HandleWritableFileError, this); 510 this, paths),
405 511 base::Bind(&FileSystemEntryFunction::HandleWritableFileError, this));
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 } 512 }
417 513
418 void FileSystemEntryFunction::RegisterFileSystemAndSendResponse( 514 void FileSystemEntryFunction::RegisterFileSystemsAndSendResponse(
419 const base::FilePath& path, EntryType entry_type) { 515 const std::vector<base::FilePath>& paths) {
420 RegisterFileSystemAndSendResponseWithIdOverride(path, entry_type, "");
421 }
422
423 void FileSystemEntryFunction::RegisterFileSystemAndSendResponseWithIdOverride(
424 const base::FilePath& path, EntryType entry_type, const std::string& id) {
425 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 516 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
426 517
427 fileapi::IsolatedContext* isolated_context = 518 CreateResponse();
428 fileapi::IsolatedContext::GetInstance(); 519 for (std::vector<base::FilePath>::const_iterator it = paths.begin();
429 DCHECK(isolated_context); 520 it != paths.end(); ++it) {
430 521 AddEntryToResponse(*it, "");
431 bool writable = entry_type == WRITABLE; 522 }
432 extensions::app_file_handler_util::GrantedFileEntry file_entry =
433 extensions::app_file_handler_util::CreateFileEntry(profile(),
434 GetExtension()->id(), render_view_host_->GetProcess()->GetID(), path,
435 writable);
436
437 base::DictionaryValue* dict = new base::DictionaryValue();
438 SetResult(dict);
439 dict->SetString("fileSystemId", file_entry.filesystem_id);
440 dict->SetString("baseName", file_entry.registered_name);
441 if (id.empty())
442 dict->SetString("id", file_entry.id);
443 else
444 dict->SetString("id", id);
445
446 SendResponse(true); 523 SendResponse(true);
447 } 524 }
448 525
449 void FileSystemEntryFunction::HandleWritableFileError() { 526 void FileSystemEntryFunction::CreateResponse() {
527 DCHECK(!response_);
528 response_ = new base::DictionaryValue();
529 base::ListValue* list = new base::ListValue();
530 response_->Set("entries", list);
531 response_->SetBoolean("multiple", multiple_);
532 SetResult(response_);
533 }
534
535 void FileSystemEntryFunction::AddEntryToResponse(const base::FilePath& path,
536 const std::string& id) {
benwells 2013/08/02 07:41:31 nit: id -> id_override
Sam McNally 2013/08/02 08:17:52 Done.
537 DCHECK(response_);
538 bool writable = entry_type_ == WRITABLE;
539 extensions::app_file_handler_util::GrantedFileEntry file_entry =
540 extensions::app_file_handler_util::CreateFileEntry(
541 profile(),
542 GetExtension()->id(),
543 render_view_host_->GetProcess()->GetID(),
544 path,
545 writable);
546 base::ListValue* entries;
547 bool success = response_->GetList("entries", &entries);
548 DCHECK(success);
549
550 base::DictionaryValue* entry = new base::DictionaryValue();
551 entry->SetString("fileSystemId", file_entry.filesystem_id);
552 entry->SetString("baseName", file_entry.registered_name);
553 if (id.empty())
554 entry->SetString("id", file_entry.id);
555 else
556 entry->SetString("id", id);
557 entries->Append(entry);
558 }
559
560 void FileSystemEntryFunction::HandleWritableFileError(
561 const std::string& error) {
450 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 562 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
451 error_ = kWritableFileError; 563 error_ = error;
452 SendResponse(false); 564 SendResponse(false);
453 } 565 }
454 566
455 bool FileSystemGetWritableEntryFunction::RunImpl() { 567 bool FileSystemGetWritableEntryFunction::RunImpl() {
456 std::string filesystem_name; 568 std::string filesystem_name;
457 std::string filesystem_path; 569 std::string filesystem_path;
458 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &filesystem_name)); 570 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &filesystem_name));
459 EXTENSION_FUNCTION_VALIDATE(args_->GetString(1, &filesystem_path)); 571 EXTENSION_FUNCTION_VALIDATE(args_->GetString(1, &filesystem_path));
460 572
461 if (!HasFileSystemWritePermission()) { 573 if (!HasFileSystemWritePermission()) {
462 error_ = kRequiresFileSystemWriteError; 574 error_ = kRequiresFileSystemWriteError;
463 return false; 575 return false;
464 } 576 }
577 entry_type_ = WRITABLE;
465 578
466 base::FilePath path; 579 base::FilePath path;
467 if (!GetFilePathOfFileEntry(filesystem_name, filesystem_path, 580 if (!GetFilePathOfFileEntry(filesystem_name, filesystem_path,
468 render_view_host_, &path, &error_)) 581 render_view_host_, &path, &error_))
469 return false; 582 return false;
470 583
471 CheckWritableFile(path); 584 std::vector<base::FilePath> paths;
585 paths.push_back(path);
586 CheckWritableFiles(paths);
472 return true; 587 return true;
473 } 588 }
474 589
475 bool FileSystemIsWritableEntryFunction::RunImpl() { 590 bool FileSystemIsWritableEntryFunction::RunImpl() {
476 std::string filesystem_name; 591 std::string filesystem_name;
477 std::string filesystem_path; 592 std::string filesystem_path;
478 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &filesystem_name)); 593 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &filesystem_name));
479 EXTENSION_FUNCTION_VALIDATE(args_->GetString(1, &filesystem_path)); 594 EXTENSION_FUNCTION_VALIDATE(args_->GetString(1, &filesystem_path));
480 595
481 std::string filesystem_id; 596 std::string filesystem_id;
(...skipping 14 matching lines...) Expand all
496 611
497 // Handles showing a dialog to the user to ask for the filename for a file to 612 // Handles showing a dialog to the user to ask for the filename for a file to
498 // save or open. 613 // save or open.
499 class FileSystemChooseEntryFunction::FilePicker 614 class FileSystemChooseEntryFunction::FilePicker
500 : public ui::SelectFileDialog::Listener { 615 : public ui::SelectFileDialog::Listener {
501 public: 616 public:
502 FilePicker(FileSystemChooseEntryFunction* function, 617 FilePicker(FileSystemChooseEntryFunction* function,
503 content::WebContents* web_contents, 618 content::WebContents* web_contents,
504 const base::FilePath& suggested_name, 619 const base::FilePath& suggested_name,
505 const ui::SelectFileDialog::FileTypeInfo& file_type_info, 620 const ui::SelectFileDialog::FileTypeInfo& file_type_info,
506 ui::SelectFileDialog::Type picker_type, 621 ui::SelectFileDialog::Type picker_type)
507 EntryType entry_type) 622 : function_(function) {
508 : entry_type_(entry_type),
509 function_(function) {
510 select_file_dialog_ = ui::SelectFileDialog::Create( 623 select_file_dialog_ = ui::SelectFileDialog::Create(
511 this, new ChromeSelectFilePolicy(web_contents)); 624 this, new ChromeSelectFilePolicy(web_contents));
512 gfx::NativeWindow owning_window = web_contents ? 625 gfx::NativeWindow owning_window = web_contents ?
513 platform_util::GetTopLevel(web_contents->GetView()->GetNativeView()) : 626 platform_util::GetTopLevel(web_contents->GetView()->GetNativeView()) :
514 NULL; 627 NULL;
515 628
516 if (g_skip_picker_for_test) { 629 if (g_skip_picker_for_test) {
517 if (g_use_suggested_path_for_test) { 630 if (g_use_suggested_path_for_test) {
518 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, 631 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
519 base::Bind( 632 base::Bind(
520 &FileSystemChooseEntryFunction::FilePicker::FileSelected, 633 &FileSystemChooseEntryFunction::FilePicker::FileSelected,
521 base::Unretained(this), suggested_name, 1, 634 base::Unretained(this), suggested_name, 1,
522 static_cast<void*>(NULL))); 635 static_cast<void*>(NULL)));
523 } else if (g_path_to_be_picked_for_test) { 636 } else if (g_path_to_be_picked_for_test) {
524 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, 637 content::BrowserThread::PostTask(
638 content::BrowserThread::UI, FROM_HERE,
525 base::Bind( 639 base::Bind(
526 &FileSystemChooseEntryFunction::FilePicker::FileSelected, 640 &FileSystemChooseEntryFunction::FilePicker::FileSelected,
527 base::Unretained(this), *g_path_to_be_picked_for_test, 1, 641 base::Unretained(this), *g_path_to_be_picked_for_test, 1,
528 static_cast<void*>(NULL))); 642 static_cast<void*>(NULL)));
643 } else if (g_paths_to_be_picked_for_test) {
644 content::BrowserThread::PostTask(
645 content::BrowserThread::UI,
646 FROM_HERE,
647 base::Bind(
648 &FileSystemChooseEntryFunction::FilePicker::MultiFilesSelected,
649 base::Unretained(this),
650 *g_paths_to_be_picked_for_test,
651 static_cast<void*>(NULL)));
529 } else { 652 } else {
530 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, 653 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
531 base::Bind( 654 base::Bind(
532 &FileSystemChooseEntryFunction::FilePicker:: 655 &FileSystemChooseEntryFunction::FilePicker::
533 FileSelectionCanceled, 656 FileSelectionCanceled,
534 base::Unretained(this), static_cast<void*>(NULL))); 657 base::Unretained(this), static_cast<void*>(NULL)));
535 } 658 }
536 return; 659 return;
537 } 660 }
538 661
539 select_file_dialog_->SelectFile(picker_type, 662 select_file_dialog_->SelectFile(picker_type,
540 string16(), 663 string16(),
541 suggested_name, 664 suggested_name,
542 &file_type_info, 665 &file_type_info,
543 0, 666 0,
544 base::FilePath::StringType(), 667 base::FilePath::StringType(),
545 owning_window, 668 owning_window,
546 NULL); 669 NULL);
547 } 670 }
548 671
549 virtual ~FilePicker() {} 672 virtual ~FilePicker() {}
550 673
551 private: 674 private:
552 // ui::SelectFileDialog::Listener implementation. 675 // ui::SelectFileDialog::Listener implementation.
553 virtual void FileSelected(const base::FilePath& path, 676 virtual void FileSelected(const base::FilePath& path,
554 int index, 677 int index,
555 void* params) OVERRIDE { 678 void* params) OVERRIDE {
556 function_->FileSelected(path, entry_type_); 679 std::vector<base::FilePath> paths;
557 delete this; 680 paths.push_back(path);
681 MultiFilesSelected(paths, params);
558 } 682 }
559 683
560 virtual void FileSelectedWithExtraInfo(const ui::SelectedFileInfo& file, 684 virtual void FileSelectedWithExtraInfo(const ui::SelectedFileInfo& file,
561 int index, 685 int index,
562 void* params) OVERRIDE { 686 void* params) OVERRIDE {
563 // Normally, file.local_path is used because it is a native path to the 687 // 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 688 // 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 689 // 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, 690 // 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. 691 // not its cache. On other platforms than Chrome OS, they are the same.
568 // 692 //
569 // TODO(kinaba): remove this, once after the file picker implements proper 693 // TODO(kinaba): remove this, once after the file picker implements proper
570 // switch of the path treatment depending on the |support_drive| flag. 694 // switch of the path treatment depending on the |support_drive| flag.
571 function_->FileSelected(file.file_path, entry_type_); 695 FileSelected(file.file_path, index, params);
696 }
697
698 virtual void MultiFilesSelected(const std::vector<base::FilePath>& files,
699 void* params) OVERRIDE {
700 function_->FilesSelected(files);
572 delete this; 701 delete this;
573 } 702 }
574 703
704 virtual void MultiFilesSelectedWithExtraInfo(
705 const std::vector<ui::SelectedFileInfo>& files,
706 void* params) OVERRIDE {
707 std::vector<base::FilePath> paths;
708 for (std::vector<ui::SelectedFileInfo>::const_iterator it = files.begin();
709 it != files.end(); ++it) {
710 paths.push_back(it->file_path);
711 }
712 MultiFilesSelected(paths, params);
713 }
714
575 virtual void FileSelectionCanceled(void* params) OVERRIDE { 715 virtual void FileSelectionCanceled(void* params) OVERRIDE {
576 function_->FileSelectionCanceled(); 716 function_->FileSelectionCanceled();
577 delete this; 717 delete this;
578 } 718 }
579 719
580 EntryType entry_type_;
581
582 scoped_refptr<ui::SelectFileDialog> select_file_dialog_; 720 scoped_refptr<ui::SelectFileDialog> select_file_dialog_;
583 scoped_refptr<FileSystemChooseEntryFunction> function_; 721 scoped_refptr<FileSystemChooseEntryFunction> function_;
584 722
585 DISALLOW_COPY_AND_ASSIGN(FilePicker); 723 DISALLOW_COPY_AND_ASSIGN(FilePicker);
586 }; 724 };
587 725
588 void FileSystemChooseEntryFunction::ShowPicker( 726 void FileSystemChooseEntryFunction::ShowPicker(
589 const ui::SelectFileDialog::FileTypeInfo& file_type_info, 727 const ui::SelectFileDialog::FileTypeInfo& file_type_info,
590 ui::SelectFileDialog::Type picker_type, 728 ui::SelectFileDialog::Type picker_type) {
591 EntryType entry_type) {
592 // TODO(asargent/benwells) - As a short term remediation for crbug.com/179010 729 // 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 730 // we're adding the ability for a whitelisted extension to use this API since
594 // chrome.fileBrowserHandler.selectFile is ChromeOS-only. Eventually we'd 731 // chrome.fileBrowserHandler.selectFile is ChromeOS-only. Eventually we'd
595 // like a better solution and likely this code will go back to being 732 // like a better solution and likely this code will go back to being
596 // platform-app only. 733 // platform-app only.
597 content::WebContents* web_contents = NULL; 734 content::WebContents* web_contents = NULL;
598 if (extension_->is_platform_app()) { 735 if (extension_->is_platform_app()) {
599 ShellWindowRegistry* registry = ShellWindowRegistry::Get(profile()); 736 ShellWindowRegistry* registry = ShellWindowRegistry::Get(profile());
600 DCHECK(registry); 737 DCHECK(registry);
601 ShellWindow* shell_window = registry->GetShellWindowForRenderViewHost( 738 ShellWindow* shell_window = registry->GetShellWindowForRenderViewHost(
602 render_view_host()); 739 render_view_host());
603 if (!shell_window) { 740 if (!shell_window) {
604 error_ = kInvalidCallingPage; 741 error_ = kInvalidCallingPage;
605 SendResponse(false); 742 SendResponse(false);
606 return; 743 return;
607 } 744 }
608 web_contents = shell_window->web_contents(); 745 web_contents = shell_window->web_contents();
609 } else { 746 } else {
610 web_contents = GetAssociatedWebContents(); 747 web_contents = GetAssociatedWebContents();
611 } 748 }
612 // The file picker will hold a reference to this function instance, preventing 749 // The file picker will hold a reference to this function instance, preventing
613 // its destruction (and subsequent sending of the function response) until the 750 // 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 751 // 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. 752 // will delete itself, which will also free the function instance.
616 new FilePicker(this, web_contents, initial_path_, file_type_info, 753 new FilePicker(
617 picker_type, entry_type); 754 this, web_contents, initial_path_, file_type_info, picker_type);
618 } 755 }
619 756
620 // static 757 // static
621 void FileSystemChooseEntryFunction::SkipPickerAndAlwaysSelectPathForTest( 758 void FileSystemChooseEntryFunction::SkipPickerAndAlwaysSelectPathForTest(
622 base::FilePath* path) { 759 base::FilePath* path) {
623 g_skip_picker_for_test = true; 760 g_skip_picker_for_test = true;
624 g_use_suggested_path_for_test = false; 761 g_use_suggested_path_for_test = false;
625 g_path_to_be_picked_for_test = path; 762 g_path_to_be_picked_for_test = path;
763 g_paths_to_be_picked_for_test = NULL;
764 }
765
766 void FileSystemChooseEntryFunction::SkipPickerAndAlwaysSelectPathsForTest(
767 std::vector<base::FilePath>* paths) {
768 g_skip_picker_for_test = true;
769 g_use_suggested_path_for_test = false;
770 g_paths_to_be_picked_for_test = paths;
626 } 771 }
627 772
628 // static 773 // static
629 void FileSystemChooseEntryFunction::SkipPickerAndSelectSuggestedPathForTest() { 774 void FileSystemChooseEntryFunction::SkipPickerAndSelectSuggestedPathForTest() {
630 g_skip_picker_for_test = true; 775 g_skip_picker_for_test = true;
631 g_use_suggested_path_for_test = true; 776 g_use_suggested_path_for_test = true;
632 g_path_to_be_picked_for_test = NULL; 777 g_path_to_be_picked_for_test = NULL;
778 g_paths_to_be_picked_for_test = NULL;
633 } 779 }
634 780
635 // static 781 // static
636 void FileSystemChooseEntryFunction::SkipPickerAndAlwaysCancelForTest() { 782 void FileSystemChooseEntryFunction::SkipPickerAndAlwaysCancelForTest() {
637 g_skip_picker_for_test = true; 783 g_skip_picker_for_test = true;
638 g_use_suggested_path_for_test = false; 784 g_use_suggested_path_for_test = false;
639 g_path_to_be_picked_for_test = NULL; 785 g_path_to_be_picked_for_test = NULL;
786 g_paths_to_be_picked_for_test = NULL;
640 } 787 }
641 788
642 // static 789 // static
643 void FileSystemChooseEntryFunction::StopSkippingPickerForTest() { 790 void FileSystemChooseEntryFunction::StopSkippingPickerForTest() {
644 g_skip_picker_for_test = false; 791 g_skip_picker_for_test = false;
645 } 792 }
646 793
647 // static 794 // static
648 void FileSystemChooseEntryFunction::RegisterTempExternalFileSystemForTest( 795 void FileSystemChooseEntryFunction::RegisterTempExternalFileSystemForTest(
649 const std::string& name, const base::FilePath& path) { 796 const std::string& name, const base::FilePath& path) {
(...skipping 13 matching lines...) Expand all
663 } else { 810 } else {
664 base::FilePath documents_dir; 811 base::FilePath documents_dir;
665 if (PathService::Get(chrome::DIR_USER_DOCUMENTS, &documents_dir)) { 812 if (PathService::Get(chrome::DIR_USER_DOCUMENTS, &documents_dir)) {
666 initial_path_ = documents_dir.Append(suggested_name); 813 initial_path_ = documents_dir.Append(suggested_name);
667 } else { 814 } else {
668 initial_path_ = suggested_name; 815 initial_path_ = suggested_name;
669 } 816 }
670 } 817 }
671 } 818 }
672 819
673 void FileSystemChooseEntryFunction::FileSelected(const base::FilePath& path, 820 void FileSystemChooseEntryFunction::FilesSelected(
674 EntryType entry_type) { 821 const std::vector<base::FilePath>& paths) {
822 DCHECK(!paths.empty());
675 file_system_api::SetLastChooseEntryDirectory( 823 file_system_api::SetLastChooseEntryDirectory(
676 ExtensionPrefs::Get(profile()), 824 ExtensionPrefs::Get(profile()), GetExtension()->id(), paths[0].DirName());
677 GetExtension()->id(), 825 if (entry_type_ == WRITABLE) {
678 path.DirName()); 826 CheckWritableFiles(paths);
679 if (entry_type == WRITABLE) {
680 CheckWritableFile(path);
681 return; 827 return;
682 } 828 }
683 829
684 // Don't need to check the file, it's for reading. 830 // Don't need to check the file, it's for reading.
685 RegisterFileSystemAndSendResponse(path, READ_ONLY); 831 RegisterFileSystemsAndSendResponse(paths);
686 } 832 }
687 833
688 void FileSystemChooseEntryFunction::FileSelectionCanceled() { 834 void FileSystemChooseEntryFunction::FileSelectionCanceled() {
689 error_ = kUserCancelled; 835 error_ = kUserCancelled;
690 SendResponse(false); 836 SendResponse(false);
691 } 837 }
692 838
693 void FileSystemChooseEntryFunction::BuildFileTypeInfo( 839 void FileSystemChooseEntryFunction::BuildFileTypeInfo(
694 ui::SelectFileDialog::FileTypeInfo* file_type_info, 840 ui::SelectFileDialog::FileTypeInfo* file_type_info,
695 const base::FilePath::StringType& suggested_extension, 841 const base::FilePath::StringType& suggested_extension,
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
749 suggested_extension->erase(suggested_extension->begin()); // drop the . 895 suggested_extension->erase(suggested_extension->begin()); // drop the .
750 } 896 }
751 } 897 }
752 898
753 bool FileSystemChooseEntryFunction::RunImpl() { 899 bool FileSystemChooseEntryFunction::RunImpl() {
754 scoped_ptr<ChooseEntry::Params> params(ChooseEntry::Params::Create(*args_)); 900 scoped_ptr<ChooseEntry::Params> params(ChooseEntry::Params::Create(*args_));
755 EXTENSION_FUNCTION_VALIDATE(params.get()); 901 EXTENSION_FUNCTION_VALIDATE(params.get());
756 902
757 base::FilePath suggested_name; 903 base::FilePath suggested_name;
758 ui::SelectFileDialog::FileTypeInfo file_type_info; 904 ui::SelectFileDialog::FileTypeInfo file_type_info;
759 EntryType entry_type = READ_ONLY;
760 ui::SelectFileDialog::Type picker_type = 905 ui::SelectFileDialog::Type picker_type =
761 ui::SelectFileDialog::SELECT_OPEN_FILE; 906 ui::SelectFileDialog::SELECT_OPEN_FILE;
762 907
763 file_system::ChooseEntryOptions* options = params->options.get(); 908 file_system::ChooseEntryOptions* options = params->options.get();
764 if (options) { 909 if (options) {
910 multiple_ = options->accepts_multiple;
911 if (multiple_)
912 picker_type = ui::SelectFileDialog::SELECT_OPEN_MULTI_FILE;
765 if (options->type == file_system::CHOOSE_ENTRY_TYPE_OPENWRITABLEFILE) { 913 if (options->type == file_system::CHOOSE_ENTRY_TYPE_OPENWRITABLEFILE) {
766 entry_type = WRITABLE; 914 entry_type_ = WRITABLE;
767 } else if (options->type == file_system::CHOOSE_ENTRY_TYPE_SAVEFILE) { 915 } else if (options->type == file_system::CHOOSE_ENTRY_TYPE_SAVEFILE) {
768 entry_type = WRITABLE; 916 if (multiple_) {
917 error_ = kMultipleUnsupportedError;
918 return false;
919 }
920 entry_type_ = WRITABLE;
769 picker_type = ui::SelectFileDialog::SELECT_SAVEAS_FILE; 921 picker_type = ui::SelectFileDialog::SELECT_SAVEAS_FILE;
770 } 922 }
771 923
772 base::FilePath::StringType suggested_extension; 924 base::FilePath::StringType suggested_extension;
773 BuildSuggestion(options->suggested_name.get(), &suggested_name, 925 BuildSuggestion(options->suggested_name.get(), &suggested_name,
774 &suggested_extension); 926 &suggested_extension);
775 927
776 BuildFileTypeInfo(&file_type_info, suggested_extension, 928 BuildFileTypeInfo(&file_type_info, suggested_extension,
777 options->accepts.get(), options->accepts_all_types.get()); 929 options->accepts.get(), options->accepts_all_types.get());
778 } 930 }
779 931
780 if (entry_type == WRITABLE && !HasFileSystemWritePermission()) { 932 if (entry_type_ == WRITABLE && !HasFileSystemWritePermission()) {
781 error_ = kRequiresFileSystemWriteError; 933 error_ = kRequiresFileSystemWriteError;
782 return false; 934 return false;
783 } 935 }
784 936
785 file_type_info.support_drive = true; 937 file_type_info.support_drive = true;
786 938
787 base::FilePath previous_path; 939 base::FilePath previous_path;
788 file_system_api::GetLastChooseEntryDirectory( 940 file_system_api::GetLastChooseEntryDirectory(
789 ExtensionPrefs::Get(profile()), 941 ExtensionPrefs::Get(profile()),
790 GetExtension()->id(), 942 GetExtension()->id(),
791 &previous_path); 943 &previous_path);
792 944
793 content::BrowserThread::PostTaskAndReply( 945 content::BrowserThread::PostTaskAndReply(
794 content::BrowserThread::FILE, 946 content::BrowserThread::FILE,
795 FROM_HERE, 947 FROM_HERE,
796 base::Bind( 948 base::Bind(
797 &FileSystemChooseEntryFunction::SetInitialPathOnFileThread, this, 949 &FileSystemChooseEntryFunction::SetInitialPathOnFileThread, this,
798 suggested_name, previous_path), 950 suggested_name, previous_path),
799 base::Bind( 951 base::Bind(
800 &FileSystemChooseEntryFunction::ShowPicker, this, file_type_info, 952 &FileSystemChooseEntryFunction::ShowPicker, this, file_type_info,
801 picker_type, entry_type)); 953 picker_type));
802 return true; 954 return true;
803 } 955 }
804 956
805 bool FileSystemRetainEntryFunction::RunImpl() { 957 bool FileSystemRetainEntryFunction::RunImpl() {
806 std::string entry_id; 958 std::string entry_id;
807 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &entry_id)); 959 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &entry_id));
808 SavedFilesService* saved_files_service = SavedFilesService::Get(profile()); 960 SavedFilesService* saved_files_service = SavedFilesService::Get(profile());
809 // Add the file to the retain list if it is not already on there. 961 // Add the file to the retain list if it is not already on there.
810 if (!saved_files_service->IsRegistered(extension_->id(), entry_id) && 962 if (!saved_files_service->IsRegistered(extension_->id(), entry_id) &&
811 !RetainFileEntry(entry_id)) { 963 !RetainFileEntry(entry_id)) {
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
861 return false; 1013 return false;
862 } 1014 }
863 1015
864 SavedFilesService::Get(profile())->EnqueueFileEntry( 1016 SavedFilesService::Get(profile())->EnqueueFileEntry(
865 extension_->id(), entry_id); 1017 extension_->id(), entry_id);
866 1018
867 // Only create a new file entry if the renderer requests one. 1019 // Only create a new file entry if the renderer requests one.
868 // |needs_new_entry| will be false if the renderer already has an Entry for 1020 // |needs_new_entry| will be false if the renderer already has an Entry for
869 // |entry_id|. 1021 // |entry_id|.
870 if (needs_new_entry) { 1022 if (needs_new_entry) {
871 // Reuse the ID of the retained file entry so retainEntry returns the same 1023 entry_type_ = file_entry->writable ? WRITABLE : READ_ONLY;
872 // ID that was passed to restoreEntry. 1024 CreateResponse();
873 RegisterFileSystemAndSendResponseWithIdOverride( 1025 AddEntryToResponse(file_entry->path, file_entry->id);
874 file_entry->path,
875 file_entry->writable ? WRITABLE : READ_ONLY,
876 file_entry->id);
877 } 1026 }
1027 SendResponse(true);
878 return true; 1028 return true;
879 } 1029 }
880 1030
881 } // namespace extensions 1031 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698