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

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

Powered by Google App Engine
This is Rietveld 408576698