Revert of Add button to add new FSP services to Files app. (patchset #8 id:140001...
[chromium-blink-merge.git] / chrome / browser / chromeos / file_manager / fileapi_util.cc
blob17d34b37ad78c096833409e233a8e8c288f062f6
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 "content/public/browser/browser_thread.h"
15 #include "content/public/browser/render_view_host.h"
16 #include "content/public/browser/site_instance.h"
17 #include "content/public/browser/storage_partition.h"
18 #include "content/public/common/file_chooser_file_info.h"
19 #include "extensions/common/extension.h"
20 #include "google_apis/drive/task_util.h"
21 #include "net/base/escape.h"
22 #include "storage/browser/fileapi/file_system_context.h"
23 #include "storage/browser/fileapi/isolated_context.h"
24 #include "storage/browser/fileapi/open_file_system_mode.h"
25 #include "storage/common/fileapi/file_system_util.h"
26 #include "ui/shell_dialogs/selected_file_info.h"
27 #include "url/gurl.h"
29 using content::BrowserThread;
31 namespace file_manager {
32 namespace util {
34 namespace {
36 GURL ConvertRelativeFilePathToFileSystemUrl(const base::FilePath& relative_path,
37 const std::string& extension_id) {
38 GURL base_url = storage::GetFileSystemRootURI(
39 extensions::Extension::GetBaseURLFromExtensionId(extension_id),
40 storage::kFileSystemTypeExternal);
41 return GURL(base_url.spec() +
42 net::EscapeUrlEncodedData(relative_path.AsUTF8Unsafe(),
43 false)); // Space to %20 instead of +.
46 // Creates an ErrorDefinition with an error set to |error|.
47 EntryDefinition CreateEntryDefinitionWithError(base::File::Error error) {
48 EntryDefinition result;
49 result.error = error;
50 return result;
53 // Helper class for performing conversions from file definitions to entry
54 // definitions. It is possible to do it without a class, but the code would be
55 // crazy and super tricky.
57 // This class copies the input |file_definition_list|,
58 // so there is no need to worry about validity of passed |file_definition_list|
59 // reference. Also, it automatically deletes itself after converting finished,
60 // or if shutdown is invoked during ResolveURL(). Must be called on UI thread.
61 class FileDefinitionListConverter {
62 public:
63 FileDefinitionListConverter(Profile* profile,
64 const std::string& extension_id,
65 const FileDefinitionList& file_definition_list,
66 const EntryDefinitionListCallback& callback);
67 ~FileDefinitionListConverter() {}
69 private:
70 // Converts the element under the iterator to an entry. First, converts
71 // the virtual path to an URL, and calls OnResolvedURL(). In case of error
72 // calls OnIteratorConverted with an error entry definition.
73 void ConvertNextIterator(scoped_ptr<FileDefinitionListConverter> self_deleter,
74 FileDefinitionList::const_iterator iterator);
76 // Creates an entry definition from the URL as well as the file definition.
77 // Then, calls OnIteratorConverted with the created entry definition.
78 void OnResolvedURL(scoped_ptr<FileDefinitionListConverter> self_deleter,
79 FileDefinitionList::const_iterator iterator,
80 base::File::Error error,
81 const storage::FileSystemInfo& info,
82 const base::FilePath& file_path,
83 storage::FileSystemContext::ResolvedEntryType type);
85 // Called when the iterator is converted. Adds the |entry_definition| to
86 // |results_| and calls ConvertNextIterator() for the next element.
87 void OnIteratorConverted(scoped_ptr<FileDefinitionListConverter> self_deleter,
88 FileDefinitionList::const_iterator iterator,
89 const EntryDefinition& entry_definition);
91 scoped_refptr<storage::FileSystemContext> file_system_context_;
92 const std::string extension_id_;
93 const FileDefinitionList file_definition_list_;
94 const EntryDefinitionListCallback callback_;
95 scoped_ptr<EntryDefinitionList> result_;
98 FileDefinitionListConverter::FileDefinitionListConverter(
99 Profile* profile,
100 const std::string& extension_id,
101 const FileDefinitionList& file_definition_list,
102 const EntryDefinitionListCallback& callback)
103 : extension_id_(extension_id),
104 file_definition_list_(file_definition_list),
105 callback_(callback),
106 result_(new EntryDefinitionList) {
107 DCHECK_CURRENTLY_ON(BrowserThread::UI);
109 // File browser APIs are meant to be used only from extension context, so
110 // the extension's site is the one in whose file system context the virtual
111 // path should be found.
112 GURL site = extensions::util::GetSiteForExtensionId(extension_id_, profile);
113 file_system_context_ =
114 content::BrowserContext::GetStoragePartitionForSite(
115 profile, site)->GetFileSystemContext();
117 // Deletes the converter, once the scoped pointer gets out of scope. It is
118 // either, if the conversion is finished, or ResolveURL() is terminated, and
119 // the callback not called because of shutdown.
120 scoped_ptr<FileDefinitionListConverter> self_deleter(this);
121 ConvertNextIterator(self_deleter.Pass(), file_definition_list_.begin());
124 void FileDefinitionListConverter::ConvertNextIterator(
125 scoped_ptr<FileDefinitionListConverter> self_deleter,
126 FileDefinitionList::const_iterator iterator) {
127 if (iterator == file_definition_list_.end()) {
128 // The converter object will be destroyed since |self_deleter| gets out of
129 // scope.
130 callback_.Run(result_.Pass());
131 return;
134 if (!file_system_context_.get()) {
135 OnIteratorConverted(self_deleter.Pass(),
136 iterator,
137 CreateEntryDefinitionWithError(
138 base::File::FILE_ERROR_INVALID_OPERATION));
139 return;
142 storage::FileSystemURL url = file_system_context_->CreateCrackedFileSystemURL(
143 extensions::Extension::GetBaseURLFromExtensionId(extension_id_),
144 storage::kFileSystemTypeExternal,
145 iterator->virtual_path);
146 DCHECK(url.is_valid());
148 // The converter object will be deleted if the callback is not called because
149 // of shutdown during ResolveURL().
150 file_system_context_->ResolveURL(
151 url,
152 base::Bind(&FileDefinitionListConverter::OnResolvedURL,
153 base::Unretained(this),
154 base::Passed(&self_deleter),
155 iterator));
158 void FileDefinitionListConverter::OnResolvedURL(
159 scoped_ptr<FileDefinitionListConverter> self_deleter,
160 FileDefinitionList::const_iterator iterator,
161 base::File::Error error,
162 const storage::FileSystemInfo& info,
163 const base::FilePath& file_path,
164 storage::FileSystemContext::ResolvedEntryType type) {
165 DCHECK_CURRENTLY_ON(BrowserThread::UI);
167 if (error != base::File::FILE_OK) {
168 OnIteratorConverted(self_deleter.Pass(),
169 iterator,
170 CreateEntryDefinitionWithError(error));
171 return;
174 EntryDefinition entry_definition;
175 entry_definition.file_system_root_url = info.root_url.spec();
176 entry_definition.file_system_name = info.name;
177 switch (type) {
178 case storage::FileSystemContext::RESOLVED_ENTRY_FILE:
179 entry_definition.is_directory = false;
180 break;
181 case storage::FileSystemContext::RESOLVED_ENTRY_DIRECTORY:
182 entry_definition.is_directory = true;
183 break;
184 case storage::FileSystemContext::RESOLVED_ENTRY_NOT_FOUND:
185 entry_definition.is_directory = iterator->is_directory;
186 break;
188 entry_definition.error = base::File::FILE_OK;
190 // Construct a target Entry.fullPath value from the virtual path and the
191 // root URL. Eg. Downloads/A/b.txt -> A/b.txt.
192 const base::FilePath root_virtual_path =
193 file_system_context_->CrackURL(info.root_url).virtual_path();
194 DCHECK(root_virtual_path == iterator->virtual_path ||
195 root_virtual_path.IsParent(iterator->virtual_path));
196 base::FilePath full_path;
197 root_virtual_path.AppendRelativePath(iterator->virtual_path, &full_path);
198 entry_definition.full_path = full_path;
200 OnIteratorConverted(self_deleter.Pass(), iterator, entry_definition);
203 void FileDefinitionListConverter::OnIteratorConverted(
204 scoped_ptr<FileDefinitionListConverter> self_deleter,
205 FileDefinitionList::const_iterator iterator,
206 const EntryDefinition& entry_definition) {
207 result_->push_back(entry_definition);
208 ConvertNextIterator(self_deleter.Pass(), ++iterator);
211 // Helper function to return the converted definition entry directly, without
212 // the redundant container.
213 void OnConvertFileDefinitionDone(
214 const EntryDefinitionCallback& callback,
215 scoped_ptr<EntryDefinitionList> entry_definition_list) {
216 DCHECK_EQ(1u, entry_definition_list->size());
217 callback.Run(entry_definition_list->at(0));
220 // Checks if the |file_path| points non-native location or not.
221 bool IsUnderNonNativeLocalPath(const storage::FileSystemContext& context,
222 const base::FilePath& file_path) {
223 base::FilePath virtual_path;
224 if (!context.external_backend()->GetVirtualPath(file_path, &virtual_path))
225 return false;
227 const storage::FileSystemURL url = context.CreateCrackedFileSystemURL(
228 GURL(), storage::kFileSystemTypeExternal, virtual_path);
229 if (!url.is_valid())
230 return false;
232 return IsNonNativeFileSystemType(url.type());
235 // Helper class to convert SelectedFileInfoList into ChooserFileInfoList.
236 class ConvertSelectedFileInfoListToFileChooserFileInfoListImpl {
237 public:
238 // The scoped pointer to control lifetime of the instance itself. The pointer
239 // is passed to callback functions and binds the lifetime of the instance to
240 // the callback's lifetime.
241 typedef scoped_ptr<ConvertSelectedFileInfoListToFileChooserFileInfoListImpl>
242 Lifetime;
244 ConvertSelectedFileInfoListToFileChooserFileInfoListImpl(
245 storage::FileSystemContext* context,
246 const GURL& origin,
247 const SelectedFileInfoList& selected_info_list,
248 const FileChooserFileInfoListCallback& callback)
249 : context_(context),
250 chooser_info_list_(new FileChooserFileInfoList),
251 callback_(callback) {
252 DCHECK_CURRENTLY_ON(BrowserThread::UI);
254 Lifetime lifetime(this);
255 bool need_fill_metadata = false;
257 for (size_t i = 0; i < selected_info_list.size(); ++i) {
258 content::FileChooserFileInfo chooser_info;
260 // Native file.
261 if (!IsUnderNonNativeLocalPath(*context,
262 selected_info_list[i].file_path)) {
263 chooser_info.file_path = selected_info_list[i].file_path;
264 chooser_info.display_name = selected_info_list[i].display_name;
265 chooser_info_list_->push_back(chooser_info);
266 continue;
269 // Non-native file, but it has a native snapshot file.
270 if (!selected_info_list[i].local_path.empty()) {
271 chooser_info.file_path = selected_info_list[i].local_path;
272 chooser_info.display_name = selected_info_list[i].display_name;
273 chooser_info_list_->push_back(chooser_info);
274 continue;
277 // Non-native file without a snapshot file.
278 base::FilePath virtual_path;
279 if (!context->external_backend()->GetVirtualPath(
280 selected_info_list[i].file_path, &virtual_path)) {
281 NotifyError(lifetime.Pass());
282 return;
285 const GURL url = CreateIsolatedURLFromVirtualPath(
286 *context_, origin, virtual_path).ToGURL();
287 if (!url.is_valid()) {
288 NotifyError(lifetime.Pass());
289 return;
292 chooser_info.file_path = selected_info_list[i].file_path;
293 chooser_info.file_system_url = url;
294 chooser_info_list_->push_back(chooser_info);
295 need_fill_metadata = true;
298 // If the list includes at least one non-native file (wihtout a snapshot
299 // file), move to IO thread to obtian metadata for the non-native file.
300 if (need_fill_metadata) {
301 BrowserThread::PostTask(
302 BrowserThread::IO,
303 FROM_HERE,
304 base::Bind(&ConvertSelectedFileInfoListToFileChooserFileInfoListImpl::
305 FillMetadataOnIOThread,
306 base::Unretained(this),
307 base::Passed(&lifetime),
308 chooser_info_list_->begin()));
309 return;
312 NotifyComplete(lifetime.Pass());
315 ~ConvertSelectedFileInfoListToFileChooserFileInfoListImpl() {
316 if (chooser_info_list_) {
317 for (size_t i = 0; i < chooser_info_list_->size(); ++i) {
318 if (chooser_info_list_->at(i).file_system_url.is_valid()) {
319 storage::IsolatedContext::GetInstance()->RevokeFileSystem(
320 context_->CrackURL(chooser_info_list_->at(i).file_system_url)
321 .mount_filesystem_id());
327 private:
328 // Obtains metadata for the non-native file |it|.
329 void FillMetadataOnIOThread(Lifetime lifetime,
330 const FileChooserFileInfoList::iterator& it) {
331 DCHECK_CURRENTLY_ON(BrowserThread::IO);
333 if (it == chooser_info_list_->end()) {
334 BrowserThread::PostTask(
335 BrowserThread::UI,
336 FROM_HERE,
337 base::Bind(&ConvertSelectedFileInfoListToFileChooserFileInfoListImpl::
338 NotifyComplete,
339 base::Unretained(this),
340 base::Passed(&lifetime)));
341 return;
344 if (!it->file_system_url.is_valid()) {
345 FillMetadataOnIOThread(lifetime.Pass(), it + 1);
346 return;
349 context_->operation_runner()->GetMetadata(
350 context_->CrackURL(it->file_system_url),
351 base::Bind(&ConvertSelectedFileInfoListToFileChooserFileInfoListImpl::
352 OnGotMetadataOnIOThread,
353 base::Unretained(this),
354 base::Passed(&lifetime),
355 it));
358 // Callback invoked after GetMetadata.
359 void OnGotMetadataOnIOThread(Lifetime lifetime,
360 const FileChooserFileInfoList::iterator& it,
361 base::File::Error result,
362 const base::File::Info& file_info) {
363 DCHECK_CURRENTLY_ON(BrowserThread::IO);
365 if (result != base::File::FILE_OK) {
366 BrowserThread::PostTask(
367 BrowserThread::UI,
368 FROM_HERE,
369 base::Bind(&ConvertSelectedFileInfoListToFileChooserFileInfoListImpl::
370 NotifyError,
371 base::Unretained(this),
372 base::Passed(&lifetime)));
373 return;
376 it->length = file_info.size;
377 it->modification_time = file_info.last_modified;
378 it->is_directory = file_info.is_directory;
379 FillMetadataOnIOThread(lifetime.Pass(), it + 1);
382 // Returns a result to the |callback_|.
383 void NotifyComplete(Lifetime /* lifetime */) {
384 DCHECK_CURRENTLY_ON(BrowserThread::UI);
385 callback_.Run(*chooser_info_list_);
386 // Reset the list so that the file systems are not revoked at the
387 // destructor.
388 chooser_info_list_.reset();
391 // Returns an empty list to the |callback_|.
392 void NotifyError(Lifetime /* lifetime */) {
393 DCHECK_CURRENTLY_ON(BrowserThread::UI);
394 callback_.Run(FileChooserFileInfoList());
397 scoped_refptr<storage::FileSystemContext> context_;
398 scoped_ptr<FileChooserFileInfoList> chooser_info_list_;
399 const FileChooserFileInfoListCallback callback_;
401 DISALLOW_COPY_AND_ASSIGN(
402 ConvertSelectedFileInfoListToFileChooserFileInfoListImpl);
405 } // namespace
407 EntryDefinition::EntryDefinition() {
410 EntryDefinition::~EntryDefinition() {
413 storage::FileSystemContext* GetFileSystemContextForExtensionId(
414 Profile* profile,
415 const std::string& extension_id) {
416 GURL site = extensions::util::GetSiteForExtensionId(extension_id, profile);
417 return content::BrowserContext::GetStoragePartitionForSite(profile, site)->
418 GetFileSystemContext();
421 storage::FileSystemContext* GetFileSystemContextForRenderViewHost(
422 Profile* profile,
423 content::RenderViewHost* render_view_host) {
424 content::SiteInstance* site_instance = render_view_host->GetSiteInstance();
425 return content::BrowserContext::GetStoragePartition(profile, site_instance)->
426 GetFileSystemContext();
429 base::FilePath ConvertDrivePathToRelativeFileSystemPath(
430 Profile* profile,
431 const std::string& extension_id,
432 const base::FilePath& drive_path) {
433 // "/special/drive-xxx"
434 base::FilePath path = drive::util::GetDriveMountPointPath(profile);
435 // appended with (|drive_path| - "drive").
436 drive::util::GetDriveGrandRootPath().AppendRelativePath(drive_path, &path);
438 base::FilePath relative_path;
439 ConvertAbsoluteFilePathToRelativeFileSystemPath(profile,
440 extension_id,
441 path,
442 &relative_path);
443 return relative_path;
446 GURL ConvertDrivePathToFileSystemUrl(Profile* profile,
447 const base::FilePath& drive_path,
448 const std::string& extension_id) {
449 const base::FilePath relative_path =
450 ConvertDrivePathToRelativeFileSystemPath(profile, extension_id,
451 drive_path);
452 if (relative_path.empty())
453 return GURL();
454 return ConvertRelativeFilePathToFileSystemUrl(relative_path, extension_id);
457 bool ConvertAbsoluteFilePathToFileSystemUrl(Profile* profile,
458 const base::FilePath& absolute_path,
459 const std::string& extension_id,
460 GURL* url) {
461 base::FilePath relative_path;
462 if (!ConvertAbsoluteFilePathToRelativeFileSystemPath(profile,
463 extension_id,
464 absolute_path,
465 &relative_path)) {
466 return false;
468 *url = ConvertRelativeFilePathToFileSystemUrl(relative_path, extension_id);
469 return true;
472 bool ConvertAbsoluteFilePathToRelativeFileSystemPath(
473 Profile* profile,
474 const std::string& extension_id,
475 const base::FilePath& absolute_path,
476 base::FilePath* virtual_path) {
477 // File browser APIs are meant to be used only from extension context, so the
478 // extension's site is the one in whose file system context the virtual path
479 // should be found.
480 GURL site = extensions::util::GetSiteForExtensionId(extension_id, profile);
481 storage::ExternalFileSystemBackend* backend =
482 content::BrowserContext::GetStoragePartitionForSite(profile, site)
483 ->GetFileSystemContext()
484 ->external_backend();
485 if (!backend)
486 return false;
488 // Find if this file path is managed by the external backend.
489 if (!backend->GetVirtualPath(absolute_path, virtual_path))
490 return false;
492 return true;
495 void ConvertFileDefinitionListToEntryDefinitionList(
496 Profile* profile,
497 const std::string& extension_id,
498 const FileDefinitionList& file_definition_list,
499 const EntryDefinitionListCallback& callback) {
500 DCHECK_CURRENTLY_ON(BrowserThread::UI);
502 // The converter object destroys itself.
503 new FileDefinitionListConverter(
504 profile, extension_id, file_definition_list, callback);
507 void ConvertFileDefinitionToEntryDefinition(
508 Profile* profile,
509 const std::string& extension_id,
510 const FileDefinition& file_definition,
511 const EntryDefinitionCallback& callback) {
512 DCHECK_CURRENTLY_ON(BrowserThread::UI);
514 FileDefinitionList file_definition_list;
515 file_definition_list.push_back(file_definition);
516 ConvertFileDefinitionListToEntryDefinitionList(
517 profile,
518 extension_id,
519 file_definition_list,
520 base::Bind(&OnConvertFileDefinitionDone, callback));
523 void ConvertSelectedFileInfoListToFileChooserFileInfoList(
524 storage::FileSystemContext* context,
525 const GURL& origin,
526 const SelectedFileInfoList& selected_info_list,
527 const FileChooserFileInfoListCallback& callback) {
528 // The object deletes itself.
529 new ConvertSelectedFileInfoListToFileChooserFileInfoListImpl(
530 context, origin, selected_info_list, callback);
533 void CheckIfDirectoryExists(
534 scoped_refptr<storage::FileSystemContext> file_system_context,
535 const base::FilePath& directory_path,
536 const storage::FileSystemOperationRunner::StatusCallback& callback) {
537 DCHECK_CURRENTLY_ON(BrowserThread::UI);
539 storage::ExternalFileSystemBackend* const backend =
540 file_system_context->external_backend();
541 DCHECK(backend);
542 const storage::FileSystemURL internal_url =
543 backend->CreateInternalURL(file_system_context.get(), directory_path);
545 BrowserThread::PostTask(
546 BrowserThread::IO, FROM_HERE,
547 base::Bind(base::IgnoreResult(
548 &storage::FileSystemOperationRunner::DirectoryExists),
549 file_system_context->operation_runner()->AsWeakPtr(),
550 internal_url, google_apis::CreateRelayCallback(callback)));
553 void GetMetadataForPath(
554 scoped_refptr<storage::FileSystemContext> file_system_context,
555 const base::FilePath& entry_path,
556 const storage::FileSystemOperationRunner::GetMetadataCallback& callback) {
557 DCHECK_CURRENTLY_ON(BrowserThread::UI);
559 storage::ExternalFileSystemBackend* const backend =
560 file_system_context->external_backend();
561 DCHECK(backend);
562 const storage::FileSystemURL internal_url =
563 backend->CreateInternalURL(file_system_context.get(), entry_path);
565 BrowserThread::PostTask(
566 BrowserThread::IO, FROM_HERE,
567 base::Bind(
568 base::IgnoreResult(&storage::FileSystemOperationRunner::GetMetadata),
569 file_system_context->operation_runner()->AsWeakPtr(), internal_url,
570 google_apis::CreateRelayCallback(callback)));
573 storage::FileSystemURL CreateIsolatedURLFromVirtualPath(
574 const storage::FileSystemContext& context,
575 const GURL& origin,
576 const base::FilePath& virtual_path) {
577 const storage::FileSystemURL original_url =
578 context.CreateCrackedFileSystemURL(
579 origin, storage::kFileSystemTypeExternal, virtual_path);
581 std::string register_name;
582 const std::string isolated_file_system_id =
583 storage::IsolatedContext::GetInstance()->RegisterFileSystemForPath(
584 original_url.type(),
585 original_url.filesystem_id(),
586 original_url.path(),
587 &register_name);
588 const storage::FileSystemURL isolated_url =
589 context.CreateCrackedFileSystemURL(
590 origin,
591 storage::kFileSystemTypeIsolated,
592 base::FilePath(isolated_file_system_id).Append(register_name));
593 return isolated_url;
596 } // namespace util
597 } // namespace file_manager