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

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
Matt Giuca 2013/07/25 08:31:09 If it "is" not a symlink.
Sam McNally 2013/07/26 03:58:04 Done.
260 const base::Closure& on_success, 278 // 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>;
Matt Giuca 2013/07/25 08:31:09 Can't you just make ~WritableFileChecker protected
Sam McNally 2013/07/26 03:58:04 This is what the instructions say to do: https://c
315 virtual ~WritableFileChecker() {}
316
317 void TaskDone() {
Matt Giuca 2013/07/25 08:31:09 Comment.
Sam McNally 2013/07/26 03:58:04 Done.
318 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
319 if (!--outstanding_tasks_) {
Matt Giuca 2013/07/25 08:31:09 --outstanding_tasks_ == 0
Sam McNally 2013/07/26 03:58:04 Done.
320 if (error_.empty())
321 on_success_.Run();
322 else
323 on_failure_.Run(error_);
324 }
325 }
326
327 void Error(const std::string& message) {
Matt Giuca 2013/07/25 08:31:09 Comment. Include the fact that it must only be cal
Sam McNally 2013/07/26 03:58:04 Done.
328 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
329 DCHECK(!message.empty());
330 error_ = message;
331 TaskDone();
332 }
333
334 void CheckLocalWritableFiles(const std::vector<base::FilePath>& paths) {
335 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
336 std::string error;
337 for (std::vector<base::FilePath>::const_iterator it = paths.begin();
338 it != paths.end(); ++it) {
339 if (!DoCheckWritableFile(*it, extension_path_, &error)) {
340 content::BrowserThread::PostTask(
341 content::BrowserThread::UI,
342 FROM_HERE,
343 base::Bind(&WritableFileChecker::Error, this, error));
344 return;
345 }
346 }
347 content::BrowserThread::PostTask(
348 content::BrowserThread::UI,
349 FROM_HERE,
350 base::Bind(&WritableFileChecker::TaskDone, this));
351 }
266 352
267 #if defined(OS_CHROMEOS) 353 #if defined(OS_CHROMEOS)
268 void CheckRemoteWritableFile(const base::Closure& on_success, 354 void CheckRemoteWritableFile(drive::FileError error,
269 const base::Closure& on_failure, 355 const base::FilePath& path) {
270 drive::FileError error, 356 if (error == drive::FILE_ERROR_OK) {
271 const base::FilePath& path) { 357 content::BrowserThread::PostTask(
272 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, 358 content::BrowserThread::UI,
273 error == drive::FILE_ERROR_OK ? on_success : on_failure); 359 FROM_HERE,
274 } 360 base::Bind(&WritableFileChecker::TaskDone, this));
361 } else {
362 content::BrowserThread::PostTask(
363 content::BrowserThread::UI,
364 FROM_HERE,
365 base::Bind(
366 &WritableFileChecker::Error,
367 this,
368 base::StringPrintf(kWritableFileErrorFormat,
369 path.BaseName().AsUTF8Unsafe().c_str())));
370 }
371 }
275 #endif 372 #endif
276 373
374 int outstanding_tasks_;
375 const base::FilePath extension_path_;
376 std::string error_;
377 base::Closure on_success_;
378 base::Callback<void(const std::string&)> on_failure_;
379 };
380
277 // Expand the mime-types and extensions provided in an AcceptOption, returning 381 // Expand the mime-types and extensions provided in an AcceptOption, returning
278 // them within the passed extension vector. Returns false if no valid types 382 // them within the passed extension vector. Returns false if no valid types
279 // were found. 383 // were found.
280 bool GetFileTypesFromAcceptOption( 384 bool GetFileTypesFromAcceptOption(
281 const file_system::AcceptOption& accept_option, 385 const file_system::AcceptOption& accept_option,
282 std::vector<base::FilePath::StringType>* extensions, 386 std::vector<base::FilePath::StringType>* extensions,
283 string16* description) { 387 string16* description) {
284 std::set<base::FilePath::StringType> extension_set; 388 std::set<base::FilePath::StringType> extension_set;
285 int description_id = 0; 389 int description_id = 0;
286 390
(...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after
389 493
390 bool FileSystemEntryFunction::HasFileSystemWritePermission() { 494 bool FileSystemEntryFunction::HasFileSystemWritePermission() {
391 const extensions::Extension* extension = GetExtension(); 495 const extensions::Extension* extension = GetExtension();
392 if (!extension) 496 if (!extension)
393 return false; 497 return false;
394 498
395 return extension->HasAPIPermission(APIPermission::kFileSystemWrite); 499 return extension->HasAPIPermission(APIPermission::kFileSystemWrite);
396 } 500 }
397 501
398 void FileSystemEntryFunction::CheckWritableFile(const base::FilePath& path) { 502 void FileSystemEntryFunction::CheckWritableFile(const base::FilePath& path) {
399 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 503 std::vector<base::FilePath> paths;
400 base::Closure on_success = 504 paths.push_back(path);
401 base::Bind(&FileSystemEntryFunction::RegisterFileSystemAndSendResponse, 505 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 } 506 }
417 507
418 void FileSystemEntryFunction::RegisterFileSystemAndSendResponse( 508 void FileSystemEntryFunction::CheckWritableFiles(
419 const base::FilePath& path, EntryType entry_type) { 509 const std::vector<base::FilePath>& paths, bool multiple) {
420 RegisterFileSystemAndSendResponseWithIdOverride(path, entry_type, ""); 510 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
511 scoped_refptr<WritableFileChecker> helper = new WritableFileChecker(
512 paths, profile_, extension_->path(),
513 base::Bind(
514 &FileSystemEntryFunction::RegisterFileSystemsAndSendResponse,
515 this, paths, WRITABLE, multiple),
516 base::Bind(&FileSystemEntryFunction::HandleWritableFileError, this));
517 }
518
519 void FileSystemEntryFunction::RegisterFileSystemsAndSendResponse(
Matt Giuca 2013/07/25 08:31:09 This used to use RegisterFileSystemAndSendResponse
Sam McNally 2013/07/26 03:58:04 Done.
520 const std::vector<base::FilePath>& paths,
521 EntryType entry_type,
522 bool multiple) {
523 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
524
525 base::DictionaryValue* response = new base::DictionaryValue();
526 base::ListValue* list = new base::ListValue();
527 response->Set("entries", list);
528 response->SetBoolean("multiple", multiple);
529 SetResult(response);
530 for (std::vector<base::FilePath>::const_iterator it = paths.begin();
531 it != paths.end(); ++it) {
532 list->Append(BuildEntryDict(*it, entry_type));
533 }
534
535 SendResponse(true);
421 } 536 }
422 537
423 void FileSystemEntryFunction::RegisterFileSystemAndSendResponseWithIdOverride( 538 void FileSystemEntryFunction::RegisterFileSystemAndSendResponseWithIdOverride(
424 const base::FilePath& path, EntryType entry_type, const std::string& id) { 539 const base::FilePath& path, EntryType entry_type, const std::string& id) {
425 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 540 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
426 541
427 fileapi::IsolatedContext* isolated_context = 542 base::DictionaryValue* response = new base::DictionaryValue();
428 fileapi::IsolatedContext::GetInstance(); 543 base::ListValue* list = new base::ListValue();
429 DCHECK(isolated_context); 544 response->Set("entries", list);
545 response->SetBoolean("multiple", false);
546 SetResult(response);
547 list->Append(BuildEntryDictWithIdOverride(path, entry_type, id));
430 548
549 SendResponse(true);
550 }
551
552 base::DictionaryValue* FileSystemEntryFunction::BuildEntryDictWithIdOverride(
553 const base::FilePath& path, EntryType entry_type, const std::string& id) {
431 bool writable = entry_type == WRITABLE; 554 bool writable = entry_type == WRITABLE;
432 extensions::app_file_handler_util::GrantedFileEntry file_entry = 555 extensions::app_file_handler_util::GrantedFileEntry file_entry =
433 extensions::app_file_handler_util::CreateFileEntry(profile(), 556 extensions::app_file_handler_util::CreateFileEntry(profile(),
434 GetExtension()->id(), render_view_host_->GetProcess()->GetID(), path, 557 GetExtension()->id(), render_view_host_->GetProcess()->GetID(), path,
435 writable); 558 writable);
436 559
437 base::DictionaryValue* dict = new base::DictionaryValue(); 560 base::DictionaryValue* dict = new base::DictionaryValue();
438 SetResult(dict);
439 dict->SetString("fileSystemId", file_entry.filesystem_id); 561 dict->SetString("fileSystemId", file_entry.filesystem_id);
440 dict->SetString("baseName", file_entry.registered_name); 562 dict->SetString("baseName", file_entry.registered_name);
441 if (id.empty()) 563 if (id.empty())
442 dict->SetString("id", file_entry.id); 564 dict->SetString("id", file_entry.id);
443 else 565 else
444 dict->SetString("id", id); 566 dict->SetString("id", id);
445 567 return dict;
446 SendResponse(true);
447 } 568 }
448 569
449 void FileSystemEntryFunction::HandleWritableFileError() { 570 base::DictionaryValue* FileSystemEntryFunction::BuildEntryDict(
571 const base::FilePath& path, EntryType entry_type) {
572 return BuildEntryDictWithIdOverride(path, entry_type, "");
573 }
574
575 void FileSystemEntryFunction::HandleWritableFileError(
576 const std::string& error) {
450 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 577 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
451 error_ = kWritableFileError; 578 error_ = error;
452 SendResponse(false); 579 SendResponse(false);
453 } 580 }
454 581
455 bool FileSystemGetWritableEntryFunction::RunImpl() { 582 bool FileSystemGetWritableEntryFunction::RunImpl() {
456 std::string filesystem_name; 583 std::string filesystem_name;
457 std::string filesystem_path; 584 std::string filesystem_path;
458 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &filesystem_name)); 585 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &filesystem_name));
459 EXTENSION_FUNCTION_VALIDATE(args_->GetString(1, &filesystem_path)); 586 EXTENSION_FUNCTION_VALIDATE(args_->GetString(1, &filesystem_path));
460 587
461 if (!HasFileSystemWritePermission()) { 588 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 624 // Handles showing a dialog to the user to ask for the filename for a file to
498 // save or open. 625 // save or open.
499 class FileSystemChooseEntryFunction::FilePicker 626 class FileSystemChooseEntryFunction::FilePicker
500 : public ui::SelectFileDialog::Listener { 627 : public ui::SelectFileDialog::Listener {
501 public: 628 public:
502 FilePicker(FileSystemChooseEntryFunction* function, 629 FilePicker(FileSystemChooseEntryFunction* function,
503 content::WebContents* web_contents, 630 content::WebContents* web_contents,
504 const base::FilePath& suggested_name, 631 const base::FilePath& suggested_name,
505 const ui::SelectFileDialog::FileTypeInfo& file_type_info, 632 const ui::SelectFileDialog::FileTypeInfo& file_type_info,
506 ui::SelectFileDialog::Type picker_type, 633 ui::SelectFileDialog::Type picker_type,
507 EntryType entry_type) 634 EntryType entry_type,
635 bool multiple)
508 : entry_type_(entry_type), 636 : entry_type_(entry_type),
637 multiple_(multiple),
509 function_(function) { 638 function_(function) {
510 select_file_dialog_ = ui::SelectFileDialog::Create( 639 select_file_dialog_ = ui::SelectFileDialog::Create(
511 this, new ChromeSelectFilePolicy(web_contents)); 640 this, new ChromeSelectFilePolicy(web_contents));
512 gfx::NativeWindow owning_window = web_contents ? 641 gfx::NativeWindow owning_window = web_contents ?
513 platform_util::GetTopLevel(web_contents->GetView()->GetNativeView()) : 642 platform_util::GetTopLevel(web_contents->GetView()->GetNativeView()) :
514 NULL; 643 NULL;
515 644
516 if (g_skip_picker_for_test) { 645 if (g_skip_picker_for_test) {
517 if (g_use_suggested_path_for_test) { 646 if (g_use_suggested_path_for_test) {
518 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, 647 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
519 base::Bind( 648 base::Bind(
520 &FileSystemChooseEntryFunction::FilePicker::FileSelected, 649 &FileSystemChooseEntryFunction::FilePicker::FileSelected,
521 base::Unretained(this), suggested_name, 1, 650 base::Unretained(this), suggested_name, 1,
522 static_cast<void*>(NULL))); 651 static_cast<void*>(NULL)));
523 } else if (g_path_to_be_picked_for_test) { 652 } else if (g_path_to_be_picked_for_test) {
524 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, 653 content::BrowserThread::PostTask(
654 content::BrowserThread::UI, FROM_HERE,
525 base::Bind( 655 base::Bind(
526 &FileSystemChooseEntryFunction::FilePicker::FileSelected, 656 &FileSystemChooseEntryFunction::FilePicker::FileSelected,
527 base::Unretained(this), *g_path_to_be_picked_for_test, 1, 657 base::Unretained(this), *g_path_to_be_picked_for_test, 1,
528 static_cast<void*>(NULL))); 658 static_cast<void*>(NULL)));
659 } else if (g_paths_to_be_picked_for_test) {
660 content::BrowserThread::PostTask(
661 content::BrowserThread::UI,
662 FROM_HERE,
663 base::Bind(
664 &FileSystemChooseEntryFunction::FilePicker::MultiFilesSelected,
665 base::Unretained(this),
666 *g_paths_to_be_picked_for_test,
667 static_cast<void*>(NULL)));
529 } else { 668 } else {
530 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, 669 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
531 base::Bind( 670 base::Bind(
532 &FileSystemChooseEntryFunction::FilePicker:: 671 &FileSystemChooseEntryFunction::FilePicker::
533 FileSelectionCanceled, 672 FileSelectionCanceled,
534 base::Unretained(this), static_cast<void*>(NULL))); 673 base::Unretained(this), static_cast<void*>(NULL)));
535 } 674 }
536 return; 675 return;
537 } 676 }
538 677
539 select_file_dialog_->SelectFile(picker_type, 678 select_file_dialog_->SelectFile(picker_type,
540 string16(), 679 string16(),
541 suggested_name, 680 suggested_name,
542 &file_type_info, 681 &file_type_info,
543 0, 682 0,
544 base::FilePath::StringType(), 683 base::FilePath::StringType(),
545 owning_window, 684 owning_window,
546 NULL); 685 NULL);
547 } 686 }
548 687
549 virtual ~FilePicker() {} 688 virtual ~FilePicker() {}
550 689
551 private: 690 private:
552 // ui::SelectFileDialog::Listener implementation. 691 // ui::SelectFileDialog::Listener implementation.
553 virtual void FileSelected(const base::FilePath& path, 692 virtual void FileSelected(const base::FilePath& path,
554 int index, 693 int index,
555 void* params) OVERRIDE { 694 void* params) OVERRIDE {
556 function_->FileSelected(path, entry_type_); 695 std::vector<base::FilePath> paths;
557 delete this; 696 paths.push_back(path);
697 MultiFilesSelected(paths, params);
558 } 698 }
559 699
560 virtual void FileSelectedWithExtraInfo(const ui::SelectedFileInfo& file, 700 virtual void FileSelectedWithExtraInfo(const ui::SelectedFileInfo& file,
561 int index, 701 int index,
562 void* params) OVERRIDE { 702 void* params) OVERRIDE {
563 // Normally, file.local_path is used because it is a native path to the 703 // 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 704 // 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 705 // 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, 706 // 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. 707 // not its cache. On other platforms than Chrome OS, they are the same.
568 // 708 //
569 // TODO(kinaba): remove this, once after the file picker implements proper 709 // TODO(kinaba): remove this, once after the file picker implements proper
570 // switch of the path treatment depending on the |support_drive| flag. 710 // switch of the path treatment depending on the |support_drive| flag.
571 function_->FileSelected(file.file_path, entry_type_); 711 FileSelected(file.file_path, index, params);
712 }
713
714 virtual void MultiFilesSelected(const std::vector<base::FilePath>& files,
715 void* params) OVERRIDE {
716 function_->FilesSelected(files, entry_type_, multiple_);
572 delete this; 717 delete this;
573 } 718 }
574 719
720 virtual void MultiFilesSelectedWithExtraInfo(
721 const std::vector<ui::SelectedFileInfo>& files,
722 void* params) OVERRIDE {
723 std::vector<base::FilePath> paths;
724 for (std::vector<ui::SelectedFileInfo>::const_iterator it = files.begin();
725 it != files.end(); ++it) {
726 paths.push_back(it->file_path);
727 }
728 MultiFilesSelected(paths, params);
729 }
730
575 virtual void FileSelectionCanceled(void* params) OVERRIDE { 731 virtual void FileSelectionCanceled(void* params) OVERRIDE {
576 function_->FileSelectionCanceled(); 732 function_->FileSelectionCanceled();
577 delete this; 733 delete this;
578 } 734 }
579 735
580 EntryType entry_type_; 736 EntryType entry_type_;
737 bool multiple_;
581 738
582 scoped_refptr<ui::SelectFileDialog> select_file_dialog_; 739 scoped_refptr<ui::SelectFileDialog> select_file_dialog_;
583 scoped_refptr<FileSystemChooseEntryFunction> function_; 740 scoped_refptr<FileSystemChooseEntryFunction> function_;
584 741
585 DISALLOW_COPY_AND_ASSIGN(FilePicker); 742 DISALLOW_COPY_AND_ASSIGN(FilePicker);
586 }; 743 };
587 744
588 void FileSystemChooseEntryFunction::ShowPicker( 745 void FileSystemChooseEntryFunction::ShowPicker(
589 const ui::SelectFileDialog::FileTypeInfo& file_type_info, 746 const ui::SelectFileDialog::FileTypeInfo& file_type_info,
590 ui::SelectFileDialog::Type picker_type, 747 ui::SelectFileDialog::Type picker_type,
591 EntryType entry_type) { 748 EntryType entry_type,
749 bool multiple) {
592 // TODO(asargent/benwells) - As a short term remediation for crbug.com/179010 750 // 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 751 // we're adding the ability for a whitelisted extension to use this API since
594 // chrome.fileBrowserHandler.selectFile is ChromeOS-only. Eventually we'd 752 // chrome.fileBrowserHandler.selectFile is ChromeOS-only. Eventually we'd
595 // like a better solution and likely this code will go back to being 753 // like a better solution and likely this code will go back to being
596 // platform-app only. 754 // platform-app only.
597 content::WebContents* web_contents = NULL; 755 content::WebContents* web_contents = NULL;
598 if (extension_->is_platform_app()) { 756 if (extension_->is_platform_app()) {
599 ShellWindowRegistry* registry = ShellWindowRegistry::Get(profile()); 757 ShellWindowRegistry* registry = ShellWindowRegistry::Get(profile());
600 DCHECK(registry); 758 DCHECK(registry);
601 ShellWindow* shell_window = registry->GetShellWindowForRenderViewHost( 759 ShellWindow* shell_window = registry->GetShellWindowForRenderViewHost(
602 render_view_host()); 760 render_view_host());
603 if (!shell_window) { 761 if (!shell_window) {
604 error_ = kInvalidCallingPage; 762 error_ = kInvalidCallingPage;
605 SendResponse(false); 763 SendResponse(false);
606 return; 764 return;
607 } 765 }
608 web_contents = shell_window->web_contents(); 766 web_contents = shell_window->web_contents();
609 } else { 767 } else {
610 web_contents = GetAssociatedWebContents(); 768 web_contents = GetAssociatedWebContents();
611 } 769 }
612 // The file picker will hold a reference to this function instance, preventing 770 // The file picker will hold a reference to this function instance, preventing
613 // its destruction (and subsequent sending of the function response) until the 771 // 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 772 // 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. 773 // will delete itself, which will also free the function instance.
616 new FilePicker(this, web_contents, initial_path_, file_type_info, 774 new FilePicker(this, web_contents, initial_path_, file_type_info,
617 picker_type, entry_type); 775 picker_type, entry_type, multiple);
618 } 776 }
619 777
620 // static 778 // static
621 void FileSystemChooseEntryFunction::SkipPickerAndAlwaysSelectPathForTest( 779 void FileSystemChooseEntryFunction::SkipPickerAndAlwaysSelectPathForTest(
622 base::FilePath* path) { 780 base::FilePath* path) {
623 g_skip_picker_for_test = true; 781 g_skip_picker_for_test = true;
624 g_use_suggested_path_for_test = false; 782 g_use_suggested_path_for_test = false;
625 g_path_to_be_picked_for_test = path; 783 g_path_to_be_picked_for_test = path;
784 g_paths_to_be_picked_for_test = NULL;
785 }
786
787 void FileSystemChooseEntryFunction::SkipPickerAndAlwaysSelectPathsForTest(
788 std::vector<base::FilePath>* paths) {
789 g_skip_picker_for_test = true;
790 g_use_suggested_path_for_test = false;
791 g_paths_to_be_picked_for_test = paths;
626 } 792 }
627 793
628 // static 794 // static
629 void FileSystemChooseEntryFunction::SkipPickerAndSelectSuggestedPathForTest() { 795 void FileSystemChooseEntryFunction::SkipPickerAndSelectSuggestedPathForTest() {
630 g_skip_picker_for_test = true; 796 g_skip_picker_for_test = true;
631 g_use_suggested_path_for_test = true; 797 g_use_suggested_path_for_test = true;
632 g_path_to_be_picked_for_test = NULL; 798 g_path_to_be_picked_for_test = NULL;
799 g_paths_to_be_picked_for_test = NULL;
633 } 800 }
634 801
635 // static 802 // static
636 void FileSystemChooseEntryFunction::SkipPickerAndAlwaysCancelForTest() { 803 void FileSystemChooseEntryFunction::SkipPickerAndAlwaysCancelForTest() {
637 g_skip_picker_for_test = true; 804 g_skip_picker_for_test = true;
638 g_use_suggested_path_for_test = false; 805 g_use_suggested_path_for_test = false;
639 g_path_to_be_picked_for_test = NULL; 806 g_path_to_be_picked_for_test = NULL;
807 g_paths_to_be_picked_for_test = NULL;
640 } 808 }
641 809
642 // static 810 // static
643 void FileSystemChooseEntryFunction::StopSkippingPickerForTest() { 811 void FileSystemChooseEntryFunction::StopSkippingPickerForTest() {
644 g_skip_picker_for_test = false; 812 g_skip_picker_for_test = false;
645 } 813 }
646 814
647 // static 815 // static
648 void FileSystemChooseEntryFunction::RegisterTempExternalFileSystemForTest( 816 void FileSystemChooseEntryFunction::RegisterTempExternalFileSystemForTest(
649 const std::string& name, const base::FilePath& path) { 817 const std::string& name, const base::FilePath& path) {
(...skipping 13 matching lines...) Expand all
663 } else { 831 } else {
664 base::FilePath documents_dir; 832 base::FilePath documents_dir;
665 if (PathService::Get(chrome::DIR_USER_DOCUMENTS, &documents_dir)) { 833 if (PathService::Get(chrome::DIR_USER_DOCUMENTS, &documents_dir)) {
666 initial_path_ = documents_dir.Append(suggested_name); 834 initial_path_ = documents_dir.Append(suggested_name);
667 } else { 835 } else {
668 initial_path_ = suggested_name; 836 initial_path_ = suggested_name;
669 } 837 }
670 } 838 }
671 } 839 }
672 840
673 void FileSystemChooseEntryFunction::FileSelected(const base::FilePath& path, 841 void FileSystemChooseEntryFunction::FilesSelected(
674 EntryType entry_type) { 842 const std::vector<base::FilePath>& paths,
843 EntryType entry_type,
844 bool multiple) {
675 file_system_api::SetLastChooseEntryDirectory( 845 file_system_api::SetLastChooseEntryDirectory(
676 ExtensionPrefs::Get(profile()), 846 ExtensionPrefs::Get(profile()), GetExtension()->id(), paths[0].DirName());
677 GetExtension()->id(),
678 path.DirName());
679 if (entry_type == WRITABLE) { 847 if (entry_type == WRITABLE) {
680 CheckWritableFile(path); 848 CheckWritableFiles(paths, multiple);
681 return; 849 return;
682 } 850 }
683 851
684 // Don't need to check the file, it's for reading. 852 // Don't need to check the file, it's for reading.
685 RegisterFileSystemAndSendResponse(path, READ_ONLY); 853 RegisterFileSystemsAndSendResponse(paths, READ_ONLY, multiple);
686 } 854 }
687 855
688 void FileSystemChooseEntryFunction::FileSelectionCanceled() { 856 void FileSystemChooseEntryFunction::FileSelectionCanceled() {
689 error_ = kUserCancelled; 857 error_ = kUserCancelled;
690 SendResponse(false); 858 SendResponse(false);
691 } 859 }
692 860
693 void FileSystemChooseEntryFunction::BuildFileTypeInfo( 861 void FileSystemChooseEntryFunction::BuildFileTypeInfo(
694 ui::SelectFileDialog::FileTypeInfo* file_type_info, 862 ui::SelectFileDialog::FileTypeInfo* file_type_info,
695 const base::FilePath::StringType& suggested_extension, 863 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_)); 922 scoped_ptr<ChooseEntry::Params> params(ChooseEntry::Params::Create(*args_));
755 EXTENSION_FUNCTION_VALIDATE(params.get()); 923 EXTENSION_FUNCTION_VALIDATE(params.get());
756 924
757 base::FilePath suggested_name; 925 base::FilePath suggested_name;
758 ui::SelectFileDialog::FileTypeInfo file_type_info; 926 ui::SelectFileDialog::FileTypeInfo file_type_info;
759 EntryType entry_type = READ_ONLY; 927 EntryType entry_type = READ_ONLY;
760 ui::SelectFileDialog::Type picker_type = 928 ui::SelectFileDialog::Type picker_type =
761 ui::SelectFileDialog::SELECT_OPEN_FILE; 929 ui::SelectFileDialog::SELECT_OPEN_FILE;
762 930
763 file_system::ChooseEntryOptions* options = params->options.get(); 931 file_system::ChooseEntryOptions* options = params->options.get();
932 bool multiple = false;
764 if (options) { 933 if (options) {
934 multiple = options->accepts_multiple;
935 if (multiple)
936 picker_type = ui::SelectFileDialog::SELECT_OPEN_MULTI_FILE;
765 if (options->type == file_system::CHOOSE_ENTRY_TYPE_OPENWRITABLEFILE) { 937 if (options->type == file_system::CHOOSE_ENTRY_TYPE_OPENWRITABLEFILE) {
766 entry_type = WRITABLE; 938 entry_type = WRITABLE;
767 } else if (options->type == file_system::CHOOSE_ENTRY_TYPE_SAVEFILE) { 939 } else if (options->type == file_system::CHOOSE_ENTRY_TYPE_SAVEFILE) {
940 if (multiple) {
941 error_ = kMultipleUnsupportedError;
942 return false;
943 }
768 entry_type = WRITABLE; 944 entry_type = WRITABLE;
769 picker_type = ui::SelectFileDialog::SELECT_SAVEAS_FILE; 945 picker_type = ui::SelectFileDialog::SELECT_SAVEAS_FILE;
770 } 946 }
771 947
772 base::FilePath::StringType suggested_extension; 948 base::FilePath::StringType suggested_extension;
773 BuildSuggestion(options->suggested_name.get(), &suggested_name, 949 BuildSuggestion(options->suggested_name.get(), &suggested_name,
774 &suggested_extension); 950 &suggested_extension);
775 951
776 BuildFileTypeInfo(&file_type_info, suggested_extension, 952 BuildFileTypeInfo(&file_type_info, suggested_extension,
777 options->accepts.get(), options->accepts_all_types.get()); 953 options->accepts.get(), options->accepts_all_types.get());
(...skipping 13 matching lines...) Expand all
791 &previous_path); 967 &previous_path);
792 968
793 content::BrowserThread::PostTaskAndReply( 969 content::BrowserThread::PostTaskAndReply(
794 content::BrowserThread::FILE, 970 content::BrowserThread::FILE,
795 FROM_HERE, 971 FROM_HERE,
796 base::Bind( 972 base::Bind(
797 &FileSystemChooseEntryFunction::SetInitialPathOnFileThread, this, 973 &FileSystemChooseEntryFunction::SetInitialPathOnFileThread, this,
798 suggested_name, previous_path), 974 suggested_name, previous_path),
799 base::Bind( 975 base::Bind(
800 &FileSystemChooseEntryFunction::ShowPicker, this, file_type_info, 976 &FileSystemChooseEntryFunction::ShowPicker, this, file_type_info,
801 picker_type, entry_type)); 977 picker_type, entry_type, multiple));
802 return true; 978 return true;
803 } 979 }
804 980
805 bool FileSystemRetainEntryFunction::RunImpl() { 981 bool FileSystemRetainEntryFunction::RunImpl() {
806 std::string entry_id; 982 std::string entry_id;
807 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &entry_id)); 983 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &entry_id));
808 SavedFilesService* saved_files_service = SavedFilesService::Get(profile()); 984 SavedFilesService* saved_files_service = SavedFilesService::Get(profile());
809 // Add the file to the retain list if it is not already on there. 985 // Add the file to the retain list if it is not already on there.
810 if (!saved_files_service->IsRegistered(extension_->id(), entry_id) && 986 if (!saved_files_service->IsRegistered(extension_->id(), entry_id) &&
811 !RetainFileEntry(entry_id)) { 987 !RetainFileEntry(entry_id)) {
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
872 // ID that was passed to restoreEntry. 1048 // ID that was passed to restoreEntry.
873 RegisterFileSystemAndSendResponseWithIdOverride( 1049 RegisterFileSystemAndSendResponseWithIdOverride(
874 file_entry->path, 1050 file_entry->path,
875 file_entry->writable ? WRITABLE : READ_ONLY, 1051 file_entry->writable ? WRITABLE : READ_ONLY,
876 file_entry->id); 1052 file_entry->id);
877 } 1053 }
878 return true; 1054 return true;
879 } 1055 }
880 1056
881 } // namespace extensions 1057 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698