Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / chromeos / file_manager / fileapi_util.cc
blob3fd1505520d9b67c78cc11e22e273e01416dc0c1
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/chromeos/file_manager/fileapi_util.h"
7 #include "base/files/file.h"
8 #include "base/files/file_path.h"
9 #include "chrome/browser/chromeos/drive/file_system_util.h"
10 #include "chrome/browser/chromeos/file_manager/app_id.h"
11 #include "chrome/browser/chromeos/file_manager/filesystem_api_util.h"
12 #include "chrome/browser/extensions/extension_util.h"
13 #include "chrome/browser/profiles/profile.h"
14 #include "components/drive/file_system_core_util.h"
15 #include "content/public/browser/browser_thread.h"
16 #include "content/public/browser/render_frame_host.h"
17 #include "content/public/browser/site_instance.h"
18 #include "content/public/browser/storage_partition.h"
19 #include "content/public/common/file_chooser_file_info.h"
20 #include "extensions/common/extension.h"
21 #include "google_apis/drive/task_util.h"
22 #include "net/base/escape.h"
23 #include "storage/browser/fileapi/file_system_context.h"
24 #include "storage/browser/fileapi/isolated_context.h"
25 #include "storage/browser/fileapi/open_file_system_mode.h"
26 #include "storage/common/fileapi/file_system_util.h"
27 #include "ui/shell_dialogs/selected_file_info.h"
28 #include "url/gurl.h"
30 using content::BrowserThread;
32 namespace file_manager {
33 namespace util {
35 namespace {
37 GURL ConvertRelativeFilePathToFileSystemUrl(const base::FilePath& relative_path,
38 const std::string& extension_id) {
39 GURL base_url = storage::GetFileSystemRootURI(
40 extensions::Extension::GetBaseURLFromExtensionId(extension_id),
41 storage::kFileSystemTypeExternal);
42 return GURL(base_url.spec() +
43 net::EscapeUrlEncodedData(relative_path.AsUTF8Unsafe(),
44 false)); // Space to %20 instead of +.
47 // Creates an ErrorDefinition with an error set to |error|.
48 EntryDefinition CreateEntryDefinitionWithError(base::File::Error error) {
49 EntryDefinition result;
50 result.error = error;
51 return result;
54 // Helper class for performing conversions from file definitions to entry
55 // definitions. It is possible to do it without a class, but the code would be
56 // crazy and super tricky.
58 // This class copies the input |file_definition_list|,
59 // so there is no need to worry about validity of passed |file_definition_list|
60 // reference. Also, it automatically deletes itself after converting finished,
61 // or if shutdown is invoked during ResolveURL(). Must be called on UI thread.
62 class FileDefinitionListConverter {
63 public:
64 FileDefinitionListConverter(Profile* profile,
65 const std::string& extension_id,
66 const FileDefinitionList& file_definition_list,
67 const EntryDefinitionListCallback& callback);
68 ~FileDefinitionListConverter() {}
70 private:
71 // Converts the element under the iterator to an entry. First, converts
72 // the virtual path to an URL, and calls OnResolvedURL(). In case of error
73 // calls OnIteratorConverted with an error entry definition.
74 void ConvertNextIterator(scoped_ptr<FileDefinitionListConverter> self_deleter,
75 FileDefinitionList::const_iterator iterator);
77 // Creates an entry definition from the URL as well as the file definition.
78 // Then, calls OnIteratorConverted with the created entry definition.
79 void OnResolvedURL(scoped_ptr<FileDefinitionListConverter> self_deleter,
80 FileDefinitionList::const_iterator iterator,
81 base::File::Error error,
82 const storage::FileSystemInfo& info,
83 const base::FilePath& file_path,
84 storage::FileSystemContext::ResolvedEntryType type);
86 // Called when the iterator is converted. Adds the |entry_definition| to
87 // |results_| and calls ConvertNextIterator() for the next element.
88 void OnIteratorConverted(scoped_ptr<FileDefinitionListConverter> self_deleter,
89 FileDefinitionList::const_iterator iterator,
90 const EntryDefinition& entry_definition);
92 scoped_refptr<storage::FileSystemContext> file_system_context_;
93 const std::string extension_id_;
94 const FileDefinitionList file_definition_list_;
95 const EntryDefinitionListCallback callback_;
96 scoped_ptr<EntryDefinitionList> result_;
99 FileDefinitionListConverter::FileDefinitionListConverter(
100 Profile* profile,
101 const std::string& extension_id,
102 const FileDefinitionList& file_definition_list,
103 const EntryDefinitionListCallback& callback)
104 : extension_id_(extension_id),
105 file_definition_list_(file_definition_list),
106 callback_(callback),
107 result_(new EntryDefinitionList) {
108 DCHECK_CURRENTLY_ON(BrowserThread::UI);
110 // File browser APIs are meant to be used only from extension context, so
111 // the extension's site is the one in whose file system context the virtual
112 // path should be found.
113 GURL site = extensions::util::GetSiteForExtensionId(extension_id_, profile);
114 file_system_context_ =
115 content::BrowserContext::GetStoragePartitionForSite(
116 profile, site)->GetFileSystemContext();
118 // Deletes the converter, once the scoped pointer gets out of scope. It is
119 // either, if the conversion is finished, or ResolveURL() is terminated, and
120 // the callback not called because of shutdown.
121 scoped_ptr<FileDefinitionListConverter> self_deleter(this);
122 ConvertNextIterator(self_deleter.Pass(), file_definition_list_.begin());
125 void FileDefinitionListConverter::ConvertNextIterator(
126 scoped_ptr<FileDefinitionListConverter> self_deleter,
127 FileDefinitionList::const_iterator iterator) {
128 if (iterator == file_definition_list_.end()) {
129 // The converter object will be destroyed since |self_deleter| gets out of
130 // scope.
131 callback_.Run(result_.Pass());
132 return;
135 if (!file_system_context_.get()) {
136 OnIteratorConverted(self_deleter.Pass(),
137 iterator,
138 CreateEntryDefinitionWithError(
139 base::File::FILE_ERROR_INVALID_OPERATION));
140 return;
143 storage::FileSystemURL url = file_system_context_->CreateCrackedFileSystemURL(
144 extensions::Extension::GetBaseURLFromExtensionId(extension_id_),
145 storage::kFileSystemTypeExternal,
146 iterator->virtual_path);
147 DCHECK(url.is_valid());
149 // The converter object will be deleted if the callback is not called because
150 // of shutdown during ResolveURL().
151 file_system_context_->ResolveURL(
152 url,
153 base::Bind(&FileDefinitionListConverter::OnResolvedURL,
154 base::Unretained(this),
155 base::Passed(&self_deleter),
156 iterator));
159 void FileDefinitionListConverter::OnResolvedURL(
160 scoped_ptr<FileDefinitionListConverter> self_deleter,
161 FileDefinitionList::const_iterator iterator,
162 base::File::Error error,
163 const storage::FileSystemInfo& info,
164 const base::FilePath& file_path,
165 storage::FileSystemContext::ResolvedEntryType type) {
166 DCHECK_CURRENTLY_ON(BrowserThread::UI);
168 if (error != base::File::FILE_OK) {
169 OnIteratorConverted(self_deleter.Pass(),
170 iterator,
171 CreateEntryDefinitionWithError(error));
172 return;
175 EntryDefinition entry_definition;
176 entry_definition.file_system_root_url = info.root_url.spec();
177 entry_definition.file_system_name = info.name;
178 switch (type) {
179 case storage::FileSystemContext::RESOLVED_ENTRY_FILE:
180 entry_definition.is_directory = false;
181 break;
182 case storage::FileSystemContext::RESOLVED_ENTRY_DIRECTORY:
183 entry_definition.is_directory = true;
184 break;
185 case storage::FileSystemContext::RESOLVED_ENTRY_NOT_FOUND:
186 entry_definition.is_directory = iterator->is_directory;
187 break;
189 entry_definition.error = base::File::FILE_OK;
191 // Construct a target Entry.fullPath value from the virtual path and the
192 // root URL. Eg. Downloads/A/b.txt -> A/b.txt.
193 const base::FilePath root_virtual_path =
194 file_system_context_->CrackURL(info.root_url).virtual_path();
195 DCHECK(root_virtual_path == iterator->virtual_path ||
196 root_virtual_path.IsParent(iterator->virtual_path));
197 base::FilePath full_path;
198 root_virtual_path.AppendRelativePath(iterator->virtual_path, &full_path);
199 entry_definition.full_path = full_path;
201 OnIteratorConverted(self_deleter.Pass(), iterator, entry_definition);
204 void FileDefinitionListConverter::OnIteratorConverted(
205 scoped_ptr<FileDefinitionListConverter> self_deleter,
206 FileDefinitionList::const_iterator iterator,
207 const EntryDefinition& entry_definition) {
208 result_->push_back(entry_definition);
209 ConvertNextIterator(self_deleter.Pass(), ++iterator);
212 // Helper function to return the converted definition entry directly, without
213 // the redundant container.
214 void OnConvertFileDefinitionDone(
215 const EntryDefinitionCallback& callback,
216 scoped_ptr<EntryDefinitionList> entry_definition_list) {
217 DCHECK_EQ(1u, entry_definition_list->size());
218 callback.Run(entry_definition_list->at(0));
221 // Checks if the |file_path| points non-native location or not.
222 bool IsUnderNonNativeLocalPath(const storage::FileSystemContext& context,
223 const base::FilePath& file_path) {
224 base::FilePath virtual_path;
225 if (!context.external_backend()->GetVirtualPath(file_path, &virtual_path))
226 return false;
228 const storage::FileSystemURL url = context.CreateCrackedFileSystemURL(
229 GURL(), storage::kFileSystemTypeExternal, virtual_path);
230 if (!url.is_valid())
231 return false;
233 return IsNonNativeFileSystemType(url.type());
236 // Helper class to convert SelectedFileInfoList into ChooserFileInfoList.
237 class ConvertSelectedFileInfoListToFileChooserFileInfoListImpl {
238 public:
239 // The scoped pointer to control lifetime of the instance itself. The pointer
240 // is passed to callback functions and binds the lifetime of the instance to
241 // the callback's lifetime.
242 typedef scoped_ptr<ConvertSelectedFileInfoListToFileChooserFileInfoListImpl>
243 Lifetime;
245 ConvertSelectedFileInfoListToFileChooserFileInfoListImpl(
246 storage::FileSystemContext* context,
247 const GURL& origin,
248 const SelectedFileInfoList& selected_info_list,
249 const FileChooserFileInfoListCallback& callback)
250 : context_(context),
251 chooser_info_list_(new FileChooserFileInfoList),
252 callback_(callback) {
253 DCHECK_CURRENTLY_ON(BrowserThread::UI);
255 Lifetime lifetime(this);
256 bool need_fill_metadata = false;
258 for (size_t i = 0; i < selected_info_list.size(); ++i) {
259 content::FileChooserFileInfo chooser_info;
261 // Native file.
262 if (!IsUnderNonNativeLocalPath(*context,
263 selected_info_list[i].file_path)) {
264 chooser_info.file_path = selected_info_list[i].file_path;
265 chooser_info.display_name = selected_info_list[i].display_name;
266 chooser_info_list_->push_back(chooser_info);
267 continue;
270 // Non-native file, but it has a native snapshot file.
271 if (!selected_info_list[i].local_path.empty()) {
272 chooser_info.file_path = selected_info_list[i].local_path;
273 chooser_info.display_name = selected_info_list[i].display_name;
274 chooser_info_list_->push_back(chooser_info);
275 continue;
278 // Non-native file without a snapshot file.
279 base::FilePath virtual_path;
280 if (!context->external_backend()->GetVirtualPath(
281 selected_info_list[i].file_path, &virtual_path)) {
282 NotifyError(lifetime.Pass());
283 return;
286 const GURL url = CreateIsolatedURLFromVirtualPath(
287 *context_, origin, virtual_path).ToGURL();
288 if (!url.is_valid()) {
289 NotifyError(lifetime.Pass());
290 return;
293 chooser_info.file_path = selected_info_list[i].file_path;
294 chooser_info.file_system_url = url;
295 chooser_info_list_->push_back(chooser_info);
296 need_fill_metadata = true;
299 // If the list includes at least one non-native file (wihtout a snapshot
300 // file), move to IO thread to obtian metadata for the non-native file.
301 if (need_fill_metadata) {
302 BrowserThread::PostTask(
303 BrowserThread::IO,
304 FROM_HERE,
305 base::Bind(&ConvertSelectedFileInfoListToFileChooserFileInfoListImpl::
306 FillMetadataOnIOThread,
307 base::Unretained(this),
308 base::Passed(&lifetime),
309 chooser_info_list_->begin()));
310 return;
313 NotifyComplete(lifetime.Pass());
316 ~ConvertSelectedFileInfoListToFileChooserFileInfoListImpl() {
317 if (chooser_info_list_) {
318 for (size_t i = 0; i < chooser_info_list_->size(); ++i) {
319 if (chooser_info_list_->at(i).file_system_url.is_valid()) {
320 storage::IsolatedContext::GetInstance()->RevokeFileSystem(
321 context_->CrackURL(chooser_info_list_->at(i).file_system_url)
322 .mount_filesystem_id());
328 private:
329 // Obtains metadata for the non-native file |it|.
330 void FillMetadataOnIOThread(Lifetime lifetime,
331 const FileChooserFileInfoList::iterator& it) {
332 DCHECK_CURRENTLY_ON(BrowserThread::IO);
334 if (it == chooser_info_list_->end()) {
335 BrowserThread::PostTask(
336 BrowserThread::UI,
337 FROM_HERE,
338 base::Bind(&ConvertSelectedFileInfoListToFileChooserFileInfoListImpl::
339 NotifyComplete,
340 base::Unretained(this),
341 base::Passed(&lifetime)));
342 return;
345 if (!it->file_system_url.is_valid()) {
346 FillMetadataOnIOThread(lifetime.Pass(), it + 1);
347 return;
350 context_->operation_runner()->GetMetadata(
351 context_->CrackURL(it->file_system_url),
352 base::Bind(&ConvertSelectedFileInfoListToFileChooserFileInfoListImpl::
353 OnGotMetadataOnIOThread,
354 base::Unretained(this),
355 base::Passed(&lifetime),
356 it));
359 // Callback invoked after GetMetadata.
360 void OnGotMetadataOnIOThread(Lifetime lifetime,
361 const FileChooserFileInfoList::iterator& it,
362 base::File::Error result,
363 const base::File::Info& file_info) {
364 DCHECK_CURRENTLY_ON(BrowserThread::IO);
366 if (result != base::File::FILE_OK) {
367 BrowserThread::PostTask(
368 BrowserThread::UI,
369 FROM_HERE,
370 base::Bind(&ConvertSelectedFileInfoListToFileChooserFileInfoListImpl::
371 NotifyError,
372 base::Unretained(this),
373 base::Passed(&lifetime)));
374 return;
377 it->length = file_info.size;
378 it->modification_time = file_info.last_modified;
379 it->is_directory = file_info.is_directory;
380 FillMetadataOnIOThread(lifetime.Pass(), it + 1);
383 // Returns a result to the |callback_|.
384 void NotifyComplete(Lifetime /* lifetime */) {
385 DCHECK_CURRENTLY_ON(BrowserThread::UI);
386 callback_.Run(*chooser_info_list_);
387 // Reset the list so that the file systems are not revoked at the
388 // destructor.
389 chooser_info_list_.reset();
392 // Returns an empty list to the |callback_|.
393 void NotifyError(Lifetime /* lifetime */) {
394 DCHECK_CURRENTLY_ON(BrowserThread::UI);
395 callback_.Run(FileChooserFileInfoList());
398 scoped_refptr<storage::FileSystemContext> context_;
399 scoped_ptr<FileChooserFileInfoList> chooser_info_list_;
400 const FileChooserFileInfoListCallback callback_;
402 DISALLOW_COPY_AND_ASSIGN(
403 ConvertSelectedFileInfoListToFileChooserFileInfoListImpl);
406 } // namespace
408 EntryDefinition::EntryDefinition() {
411 EntryDefinition::~EntryDefinition() {
414 storage::FileSystemContext* GetFileSystemContextForExtensionId(
415 Profile* profile,
416 const std::string& extension_id) {
417 GURL site = extensions::util::GetSiteForExtensionId(extension_id, profile);
418 return content::BrowserContext::GetStoragePartitionForSite(profile, site)->
419 GetFileSystemContext();
422 storage::FileSystemContext* GetFileSystemContextForRenderFrameHost(
423 Profile* profile,
424 content::RenderFrameHost* render_frame_host) {
425 content::SiteInstance* site_instance = render_frame_host->GetSiteInstance();
426 return content::BrowserContext::GetStoragePartition(profile, site_instance)->
427 GetFileSystemContext();
430 base::FilePath ConvertDrivePathToRelativeFileSystemPath(
431 Profile* profile,
432 const std::string& extension_id,
433 const base::FilePath& drive_path) {
434 // "/special/drive-xxx"
435 base::FilePath path = drive::util::GetDriveMountPointPath(profile);
436 // appended with (|drive_path| - "drive").
437 drive::util::GetDriveGrandRootPath().AppendRelativePath(drive_path, &path);
439 base::FilePath relative_path;
440 ConvertAbsoluteFilePathToRelativeFileSystemPath(profile,
441 extension_id,
442 path,
443 &relative_path);
444 return relative_path;
447 GURL ConvertDrivePathToFileSystemUrl(Profile* profile,
448 const base::FilePath& drive_path,
449 const std::string& extension_id) {
450 const base::FilePath relative_path =
451 ConvertDrivePathToRelativeFileSystemPath(profile, extension_id,
452 drive_path);
453 if (relative_path.empty())
454 return GURL();
455 return ConvertRelativeFilePathToFileSystemUrl(relative_path, extension_id);
458 bool ConvertAbsoluteFilePathToFileSystemUrl(Profile* profile,
459 const base::FilePath& absolute_path,
460 const std::string& extension_id,
461 GURL* url) {
462 base::FilePath relative_path;
463 if (!ConvertAbsoluteFilePathToRelativeFileSystemPath(profile,
464 extension_id,
465 absolute_path,
466 &relative_path)) {
467 return false;
469 *url = ConvertRelativeFilePathToFileSystemUrl(relative_path, extension_id);
470 return true;
473 bool ConvertAbsoluteFilePathToRelativeFileSystemPath(
474 Profile* profile,
475 const std::string& extension_id,
476 const base::FilePath& absolute_path,
477 base::FilePath* virtual_path) {
478 // File browser APIs are meant to be used only from extension context, so the
479 // extension's site is the one in whose file system context the virtual path
480 // should be found.
481 GURL site = extensions::util::GetSiteForExtensionId(extension_id, profile);
482 storage::ExternalFileSystemBackend* backend =
483 content::BrowserContext::GetStoragePartitionForSite(profile, site)
484 ->GetFileSystemContext()
485 ->external_backend();
486 if (!backend)
487 return false;
489 // Find if this file path is managed by the external backend.
490 if (!backend->GetVirtualPath(absolute_path, virtual_path))
491 return false;
493 return true;
496 void ConvertFileDefinitionListToEntryDefinitionList(
497 Profile* profile,
498 const std::string& extension_id,
499 const FileDefinitionList& file_definition_list,
500 const EntryDefinitionListCallback& callback) {
501 DCHECK_CURRENTLY_ON(BrowserThread::UI);
503 // The converter object destroys itself.
504 new FileDefinitionListConverter(
505 profile, extension_id, file_definition_list, callback);
508 void ConvertFileDefinitionToEntryDefinition(
509 Profile* profile,
510 const std::string& extension_id,
511 const FileDefinition& file_definition,
512 const EntryDefinitionCallback& callback) {
513 DCHECK_CURRENTLY_ON(BrowserThread::UI);
515 FileDefinitionList file_definition_list;
516 file_definition_list.push_back(file_definition);
517 ConvertFileDefinitionListToEntryDefinitionList(
518 profile,
519 extension_id,
520 file_definition_list,
521 base::Bind(&OnConvertFileDefinitionDone, callback));
524 void ConvertSelectedFileInfoListToFileChooserFileInfoList(
525 storage::FileSystemContext* context,
526 const GURL& origin,
527 const SelectedFileInfoList& selected_info_list,
528 const FileChooserFileInfoListCallback& callback) {
529 // The object deletes itself.
530 new ConvertSelectedFileInfoListToFileChooserFileInfoListImpl(
531 context, origin, selected_info_list, callback);
534 void CheckIfDirectoryExists(
535 scoped_refptr<storage::FileSystemContext> file_system_context,
536 const base::FilePath& directory_path,
537 const storage::FileSystemOperationRunner::StatusCallback& callback) {
538 DCHECK_CURRENTLY_ON(BrowserThread::UI);
540 storage::ExternalFileSystemBackend* const backend =
541 file_system_context->external_backend();
542 DCHECK(backend);
543 const storage::FileSystemURL internal_url =
544 backend->CreateInternalURL(file_system_context.get(), directory_path);
546 BrowserThread::PostTask(
547 BrowserThread::IO, FROM_HERE,
548 base::Bind(base::IgnoreResult(
549 &storage::FileSystemOperationRunner::DirectoryExists),
550 file_system_context->operation_runner()->AsWeakPtr(),
551 internal_url, google_apis::CreateRelayCallback(callback)));
554 void GetMetadataForPath(
555 scoped_refptr<storage::FileSystemContext> file_system_context,
556 const base::FilePath& entry_path,
557 const storage::FileSystemOperationRunner::GetMetadataCallback& callback) {
558 DCHECK_CURRENTLY_ON(BrowserThread::UI);
560 storage::ExternalFileSystemBackend* const backend =
561 file_system_context->external_backend();
562 DCHECK(backend);
563 const storage::FileSystemURL internal_url =
564 backend->CreateInternalURL(file_system_context.get(), entry_path);
566 BrowserThread::PostTask(
567 BrowserThread::IO, FROM_HERE,
568 base::Bind(
569 base::IgnoreResult(&storage::FileSystemOperationRunner::GetMetadata),
570 file_system_context->operation_runner()->AsWeakPtr(), internal_url,
571 google_apis::CreateRelayCallback(callback)));
574 storage::FileSystemURL CreateIsolatedURLFromVirtualPath(
575 const storage::FileSystemContext& context,
576 const GURL& origin,
577 const base::FilePath& virtual_path) {
578 const storage::FileSystemURL original_url =
579 context.CreateCrackedFileSystemURL(
580 origin, storage::kFileSystemTypeExternal, virtual_path);
582 std::string register_name;
583 const std::string isolated_file_system_id =
584 storage::IsolatedContext::GetInstance()->RegisterFileSystemForPath(
585 original_url.type(),
586 original_url.filesystem_id(),
587 original_url.path(),
588 &register_name);
589 const storage::FileSystemURL isolated_url =
590 context.CreateCrackedFileSystemURL(
591 origin,
592 storage::kFileSystemTypeIsolated,
593 base::FilePath(isolated_file_system_id).Append(register_name));
594 return isolated_url;
597 } // namespace util
598 } // namespace file_manager