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

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

Powered by Google App Engine
This is Rietveld 408576698