Home | History | Annotate | Download | only in file_manager
      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.
      4 
      5 #include "chrome/browser/chromeos/file_manager/fileapi_util.h"
      6 
      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/extensions/extension_util.h"
     12 #include "chrome/browser/profiles/profile.h"
     13 #include "content/public/browser/browser_thread.h"
     14 #include "content/public/browser/render_view_host.h"
     15 #include "content/public/browser/site_instance.h"
     16 #include "content/public/browser/storage_partition.h"
     17 #include "extensions/common/extension.h"
     18 #include "google_apis/drive/task_util.h"
     19 #include "net/base/escape.h"
     20 #include "storage/browser/fileapi/file_system_context.h"
     21 #include "storage/browser/fileapi/open_file_system_mode.h"
     22 #include "storage/common/fileapi/file_system_util.h"
     23 #include "url/gurl.h"
     24 
     25 using content::BrowserThread;
     26 
     27 namespace file_manager {
     28 namespace util {
     29 
     30 namespace {
     31 
     32 GURL ConvertRelativeFilePathToFileSystemUrl(const base::FilePath& relative_path,
     33                                             const std::string& extension_id) {
     34   GURL base_url = storage::GetFileSystemRootURI(
     35       extensions::Extension::GetBaseURLFromExtensionId(extension_id),
     36       storage::kFileSystemTypeExternal);
     37   return GURL(base_url.spec() +
     38               net::EscapeUrlEncodedData(relative_path.AsUTF8Unsafe(),
     39                                         false));  // Space to %20 instead of +.
     40 }
     41 
     42 // Creates an ErrorDefinition with an error set to |error|.
     43 EntryDefinition CreateEntryDefinitionWithError(base::File::Error error) {
     44   EntryDefinition result;
     45   result.error = error;
     46   return result;
     47 }
     48 
     49 // Helper class for performing conversions from file definitions to entry
     50 // definitions. It is possible to do it without a class, but the code would be
     51 // crazy and super tricky.
     52 //
     53 // This class copies the input |file_definition_list|,
     54 // so there is no need to worry about validity of passed |file_definition_list|
     55 // reference. Also, it automatically deletes itself after converting finished,
     56 // or if shutdown is invoked during ResolveURL(). Must be called on UI thread.
     57 class FileDefinitionListConverter {
     58  public:
     59   FileDefinitionListConverter(Profile* profile,
     60                               const std::string& extension_id,
     61                               const FileDefinitionList& file_definition_list,
     62                               const EntryDefinitionListCallback& callback);
     63   ~FileDefinitionListConverter() {}
     64 
     65  private:
     66   // Converts the element under the iterator to an entry. First, converts
     67   // the virtual path to an URL, and calls OnResolvedURL(). In case of error
     68   // calls OnIteratorConverted with an error entry definition.
     69   void ConvertNextIterator(scoped_ptr<FileDefinitionListConverter> self_deleter,
     70                            FileDefinitionList::const_iterator iterator);
     71 
     72   // Creates an entry definition from the URL as well as the file definition.
     73   // Then, calls OnIteratorConverted with the created entry definition.
     74   void OnResolvedURL(scoped_ptr<FileDefinitionListConverter> self_deleter,
     75                      FileDefinitionList::const_iterator iterator,
     76                      base::File::Error error,
     77                      const storage::FileSystemInfo& info,
     78                      const base::FilePath& file_path,
     79                      storage::FileSystemContext::ResolvedEntryType type);
     80 
     81   // Called when the iterator is converted. Adds the |entry_definition| to
     82   // |results_| and calls ConvertNextIterator() for the next element.
     83   void OnIteratorConverted(scoped_ptr<FileDefinitionListConverter> self_deleter,
     84                            FileDefinitionList::const_iterator iterator,
     85                            const EntryDefinition& entry_definition);
     86 
     87   scoped_refptr<storage::FileSystemContext> file_system_context_;
     88   const std::string extension_id_;
     89   const FileDefinitionList file_definition_list_;
     90   const EntryDefinitionListCallback callback_;
     91   scoped_ptr<EntryDefinitionList> result_;
     92 };
     93 
     94 FileDefinitionListConverter::FileDefinitionListConverter(
     95     Profile* profile,
     96     const std::string& extension_id,
     97     const FileDefinitionList& file_definition_list,
     98     const EntryDefinitionListCallback& callback)
     99     : extension_id_(extension_id),
    100       file_definition_list_(file_definition_list),
    101       callback_(callback),
    102       result_(new EntryDefinitionList) {
    103   DCHECK_CURRENTLY_ON(BrowserThread::UI);
    104 
    105   // File browser APIs are meant to be used only from extension context, so
    106   // the extension's site is the one in whose file system context the virtual
    107   // path should be found.
    108   GURL site = extensions::util::GetSiteForExtensionId(extension_id_, profile);
    109   file_system_context_ =
    110       content::BrowserContext::GetStoragePartitionForSite(
    111           profile, site)->GetFileSystemContext();
    112 
    113   // Deletes the converter, once the scoped pointer gets out of scope. It is
    114   // either, if the conversion is finished, or ResolveURL() is terminated, and
    115   // the callback not called because of shutdown.
    116   scoped_ptr<FileDefinitionListConverter> self_deleter(this);
    117   ConvertNextIterator(self_deleter.Pass(), file_definition_list_.begin());
    118 }
    119 
    120 void FileDefinitionListConverter::ConvertNextIterator(
    121     scoped_ptr<FileDefinitionListConverter> self_deleter,
    122     FileDefinitionList::const_iterator iterator) {
    123   if (iterator == file_definition_list_.end()) {
    124     // The converter object will be destroyed since |self_deleter| gets out of
    125     // scope.
    126     callback_.Run(result_.Pass());
    127     return;
    128   }
    129 
    130   if (!file_system_context_.get()) {
    131     OnIteratorConverted(self_deleter.Pass(),
    132                         iterator,
    133                         CreateEntryDefinitionWithError(
    134                             base::File::FILE_ERROR_INVALID_OPERATION));
    135     return;
    136   }
    137 
    138   storage::FileSystemURL url = file_system_context_->CreateCrackedFileSystemURL(
    139       extensions::Extension::GetBaseURLFromExtensionId(extension_id_),
    140       storage::kFileSystemTypeExternal,
    141       iterator->virtual_path);
    142   DCHECK(url.is_valid());
    143 
    144   // The converter object will be deleted if the callback is not called because
    145   // of shutdown during ResolveURL().
    146   file_system_context_->ResolveURL(
    147       url,
    148       base::Bind(&FileDefinitionListConverter::OnResolvedURL,
    149                  base::Unretained(this),
    150                  base::Passed(&self_deleter),
    151                  iterator));
    152 }
    153 
    154 void FileDefinitionListConverter::OnResolvedURL(
    155     scoped_ptr<FileDefinitionListConverter> self_deleter,
    156     FileDefinitionList::const_iterator iterator,
    157     base::File::Error error,
    158     const storage::FileSystemInfo& info,
    159     const base::FilePath& file_path,
    160     storage::FileSystemContext::ResolvedEntryType type) {
    161   DCHECK_CURRENTLY_ON(BrowserThread::UI);
    162 
    163   if (error != base::File::FILE_OK) {
    164     OnIteratorConverted(self_deleter.Pass(),
    165                         iterator,
    166                         CreateEntryDefinitionWithError(error));
    167     return;
    168   }
    169 
    170   EntryDefinition entry_definition;
    171   entry_definition.file_system_root_url = info.root_url.spec();
    172   entry_definition.file_system_name = info.name;
    173   switch (type) {
    174     case storage::FileSystemContext::RESOLVED_ENTRY_FILE:
    175       entry_definition.is_directory = false;
    176       break;
    177     case storage::FileSystemContext::RESOLVED_ENTRY_DIRECTORY:
    178       entry_definition.is_directory = true;
    179       break;
    180     case storage::FileSystemContext::RESOLVED_ENTRY_NOT_FOUND:
    181       entry_definition.is_directory = iterator->is_directory;
    182       break;
    183   }
    184   entry_definition.error = base::File::FILE_OK;
    185 
    186   // Construct a target Entry.fullPath value from the virtual path and the
    187   // root URL. Eg. Downloads/A/b.txt -> A/b.txt.
    188   const base::FilePath root_virtual_path =
    189       file_system_context_->CrackURL(info.root_url).virtual_path();
    190   DCHECK(root_virtual_path == iterator->virtual_path ||
    191          root_virtual_path.IsParent(iterator->virtual_path));
    192   base::FilePath full_path;
    193   root_virtual_path.AppendRelativePath(iterator->virtual_path, &full_path);
    194   entry_definition.full_path = full_path;
    195 
    196   OnIteratorConverted(self_deleter.Pass(), iterator, entry_definition);
    197 }
    198 
    199 void FileDefinitionListConverter::OnIteratorConverted(
    200     scoped_ptr<FileDefinitionListConverter> self_deleter,
    201     FileDefinitionList::const_iterator iterator,
    202     const EntryDefinition& entry_definition) {
    203   result_->push_back(entry_definition);
    204   ConvertNextIterator(self_deleter.Pass(), ++iterator);
    205 }
    206 
    207 // Helper function to return the converted definition entry directly, without
    208 // the redundant container.
    209 void OnConvertFileDefinitionDone(
    210     const EntryDefinitionCallback& callback,
    211     scoped_ptr<EntryDefinitionList> entry_definition_list) {
    212   DCHECK_EQ(1u, entry_definition_list->size());
    213   callback.Run(entry_definition_list->at(0));
    214 }
    215 
    216 // Used to implement CheckIfDirectoryExists().
    217 void CheckIfDirectoryExistsOnIOThread(
    218     scoped_refptr<storage::FileSystemContext> file_system_context,
    219     const GURL& url,
    220     const storage::FileSystemOperationRunner::StatusCallback& callback) {
    221   DCHECK_CURRENTLY_ON(BrowserThread::IO);
    222 
    223   storage::FileSystemURL file_system_url = file_system_context->CrackURL(url);
    224   file_system_context->operation_runner()->DirectoryExists(
    225       file_system_url, callback);
    226 }
    227 
    228 }  // namespace
    229 
    230 EntryDefinition::EntryDefinition() {
    231 }
    232 
    233 EntryDefinition::~EntryDefinition() {
    234 }
    235 
    236 storage::FileSystemContext* GetFileSystemContextForExtensionId(
    237     Profile* profile,
    238     const std::string& extension_id) {
    239   GURL site = extensions::util::GetSiteForExtensionId(extension_id, profile);
    240   return content::BrowserContext::GetStoragePartitionForSite(profile, site)->
    241       GetFileSystemContext();
    242 }
    243 
    244 storage::FileSystemContext* GetFileSystemContextForRenderViewHost(
    245     Profile* profile,
    246     content::RenderViewHost* render_view_host) {
    247   content::SiteInstance* site_instance = render_view_host->GetSiteInstance();
    248   return content::BrowserContext::GetStoragePartition(profile, site_instance)->
    249       GetFileSystemContext();
    250 }
    251 
    252 base::FilePath ConvertDrivePathToRelativeFileSystemPath(
    253     Profile* profile,
    254     const std::string& extension_id,
    255     const base::FilePath& drive_path) {
    256   // "/special/drive-xxx"
    257   base::FilePath path = drive::util::GetDriveMountPointPath(profile);
    258   // appended with (|drive_path| - "drive").
    259   drive::util::GetDriveGrandRootPath().AppendRelativePath(drive_path, &path);
    260 
    261   base::FilePath relative_path;
    262   ConvertAbsoluteFilePathToRelativeFileSystemPath(profile,
    263                                                   extension_id,
    264                                                   path,
    265                                                   &relative_path);
    266   return relative_path;
    267 }
    268 
    269 GURL ConvertDrivePathToFileSystemUrl(Profile* profile,
    270                                      const base::FilePath& drive_path,
    271                                      const std::string& extension_id) {
    272   const base::FilePath relative_path =
    273       ConvertDrivePathToRelativeFileSystemPath(profile, extension_id,
    274                                                drive_path);
    275   if (relative_path.empty())
    276     return GURL();
    277   return ConvertRelativeFilePathToFileSystemUrl(relative_path, extension_id);
    278 }
    279 
    280 bool ConvertAbsoluteFilePathToFileSystemUrl(Profile* profile,
    281                                             const base::FilePath& absolute_path,
    282                                             const std::string& extension_id,
    283                                             GURL* url) {
    284   base::FilePath relative_path;
    285   if (!ConvertAbsoluteFilePathToRelativeFileSystemPath(profile,
    286                                                        extension_id,
    287                                                        absolute_path,
    288                                                        &relative_path)) {
    289     return false;
    290   }
    291   *url = ConvertRelativeFilePathToFileSystemUrl(relative_path, extension_id);
    292   return true;
    293 }
    294 
    295 bool ConvertAbsoluteFilePathToRelativeFileSystemPath(
    296     Profile* profile,
    297     const std::string& extension_id,
    298     const base::FilePath& absolute_path,
    299     base::FilePath* virtual_path) {
    300   // File browser APIs are meant to be used only from extension context, so the
    301   // extension's site is the one in whose file system context the virtual path
    302   // should be found.
    303   GURL site = extensions::util::GetSiteForExtensionId(extension_id, profile);
    304   storage::ExternalFileSystemBackend* backend =
    305       content::BrowserContext::GetStoragePartitionForSite(profile, site)
    306           ->GetFileSystemContext()
    307           ->external_backend();
    308   if (!backend)
    309     return false;
    310 
    311   // Find if this file path is managed by the external backend.
    312   if (!backend->GetVirtualPath(absolute_path, virtual_path))
    313     return false;
    314 
    315   return true;
    316 }
    317 
    318 void ConvertFileDefinitionListToEntryDefinitionList(
    319     Profile* profile,
    320     const std::string& extension_id,
    321     const FileDefinitionList& file_definition_list,
    322     const EntryDefinitionListCallback& callback) {
    323   DCHECK_CURRENTLY_ON(BrowserThread::UI);
    324 
    325   // The converter object destroys itself.
    326   new FileDefinitionListConverter(
    327       profile, extension_id, file_definition_list, callback);
    328 }
    329 
    330 void ConvertFileDefinitionToEntryDefinition(
    331     Profile* profile,
    332     const std::string& extension_id,
    333     const FileDefinition& file_definition,
    334     const EntryDefinitionCallback& callback) {
    335   DCHECK_CURRENTLY_ON(BrowserThread::UI);
    336 
    337   FileDefinitionList file_definition_list;
    338   file_definition_list.push_back(file_definition);
    339   ConvertFileDefinitionListToEntryDefinitionList(
    340       profile,
    341       extension_id,
    342       file_definition_list,
    343       base::Bind(&OnConvertFileDefinitionDone, callback));
    344 }
    345 
    346 void CheckIfDirectoryExists(
    347     scoped_refptr<storage::FileSystemContext> file_system_context,
    348     const GURL& url,
    349     const storage::FileSystemOperationRunner::StatusCallback& callback) {
    350   DCHECK_CURRENTLY_ON(BrowserThread::UI);
    351 
    352   // Check the existence of directory using file system API implementation on
    353   // behalf of the file manager app. We need to grant access beforehand.
    354   storage::ExternalFileSystemBackend* backend =
    355       file_system_context->external_backend();
    356   DCHECK(backend);
    357   backend->GrantFullAccessToExtension(kFileManagerAppId);
    358 
    359   BrowserThread::PostTask(
    360       BrowserThread::IO, FROM_HERE,
    361       base::Bind(&CheckIfDirectoryExistsOnIOThread,
    362                  file_system_context,
    363                  url,
    364                  google_apis::CreateRelayCallback(callback)));
    365 }
    366 
    367 }  // namespace util
    368 }  // namespace file_manager
    369