Home | History | Annotate | Download | only in file_handlers
      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/extensions/api/file_handlers/mime_util.h"
      6 
      7 #include "base/files/file_path.h"
      8 #include "base/files/file_util.h"
      9 #include "chrome/browser/profiles/profile.h"
     10 #include "content/public/browser/browser_thread.h"
     11 #include "net/base/filename_util.h"
     12 #include "net/base/mime_sniffer.h"
     13 #include "net/base/mime_util.h"
     14 #include "storage/browser/fileapi/file_system_url.h"
     15 
     16 #if defined(OS_CHROMEOS)
     17 #include "chrome/browser/chromeos/file_manager/filesystem_api_util.h"
     18 #endif
     19 
     20 using content::BrowserThread;
     21 
     22 namespace extensions {
     23 namespace app_file_handler_util {
     24 namespace {
     25 
     26 // Detects MIME type by reading initial bytes from the file. If found, then
     27 // writes the MIME type to |result|.
     28 void SniffMimeType(const base::FilePath& local_path, std::string* result) {
     29   std::vector<char> content(net::kMaxBytesToSniff);
     30 
     31   const int bytes_read =
     32       base::ReadFile(local_path, &content[0], content.size());
     33 
     34   if (bytes_read >= 0) {
     35     net::SniffMimeType(&content[0],
     36                        bytes_read,
     37                        net::FilePathToFileURL(local_path),
     38                        std::string(),  // type_hint (passes no hint)
     39                        result);
     40   }
     41 }
     42 
     43 #if defined(OS_CHROMEOS)
     44 // Converts a result passed as a scoped pointer to a dereferenced value passed
     45 // to |callback|.
     46 void OnGetMimeTypeFromFileForNonNativeLocalPathCompleted(
     47     scoped_ptr<std::string> mime_type,
     48     const base::Callback<void(const std::string&)>& callback) {
     49   callback.Run(*mime_type);
     50 }
     51 
     52 // Called when fetching MIME type for a non-native local path is completed.
     53 // If |success| is false, then tries to guess the MIME type by looking at the
     54 // file name.
     55 void OnGetMimeTypeFromMetadataForNonNativeLocalPathCompleted(
     56     const base::FilePath& local_path,
     57     const base::Callback<void(const std::string&)>& callback,
     58     bool success,
     59     const std::string& mime_type) {
     60   if (success) {
     61     callback.Run(mime_type);
     62     return;
     63   }
     64 
     65   // MIME type not available with metadata, hence try to guess it from the
     66   // file's extension.
     67   scoped_ptr<std::string> mime_type_from_extension(new std::string);
     68   std::string* const mime_type_from_extension_ptr =
     69       mime_type_from_extension.get();
     70   BrowserThread::PostBlockingPoolTaskAndReply(
     71       FROM_HERE,
     72       base::Bind(base::IgnoreResult(&net::GetMimeTypeFromFile),
     73                  local_path,
     74                  mime_type_from_extension_ptr),
     75       base::Bind(&OnGetMimeTypeFromFileForNonNativeLocalPathCompleted,
     76                  base::Passed(&mime_type_from_extension),
     77                  callback));
     78 }
     79 #endif
     80 
     81 // Called when sniffing for MIME type in the native local file is completed.
     82 void OnSniffMimeTypeForNativeLocalPathCompleted(
     83     scoped_ptr<std::string> mime_type,
     84     const base::Callback<void(const std::string&)>& callback) {
     85   callback.Run(*mime_type);
     86 }
     87 
     88 }  // namespace
     89 
     90 // Handles response of net::GetMimeTypeFromFile for native file systems. If
     91 // MIME type is available, then forwards it to |callback|. Otherwise, fallbacks
     92 // to sniffing.
     93 void OnGetMimeTypeFromFileForNativeLocalPathCompleted(
     94     const base::FilePath& local_path,
     95     scoped_ptr<std::string> mime_type,
     96     const base::Callback<void(const std::string&)>& callback) {
     97   if (!mime_type->empty()) {
     98     callback.Run(*mime_type);
     99     return;
    100   }
    101 
    102   scoped_ptr<std::string> sniffed_mime_type(new std::string);
    103   std::string* const sniffed_mime_type_ptr = sniffed_mime_type.get();
    104   BrowserThread::PostBlockingPoolTaskAndReply(
    105       FROM_HERE,
    106       base::Bind(&SniffMimeType, local_path, sniffed_mime_type_ptr),
    107       base::Bind(&OnSniffMimeTypeForNativeLocalPathCompleted,
    108                  base::Passed(&sniffed_mime_type),
    109                  callback));
    110 }
    111 
    112 // Fetches MIME type for a local path and returns it with a |callback|.
    113 void GetMimeTypeForLocalPath(
    114     Profile* profile,
    115     const base::FilePath& local_path,
    116     const base::Callback<void(const std::string&)>& callback) {
    117 #if defined(OS_CHROMEOS)
    118   if (file_manager::util::IsUnderNonNativeLocalPath(profile, local_path)) {
    119     // For non-native files, try to get the MIME type from metadata. If not
    120     // available, then try to guess from the extension. Never sniff (because
    121     // it can be very slow).
    122     file_manager::util::GetNonNativeLocalPathMimeType(
    123         profile,
    124         local_path,
    125         base::Bind(&OnGetMimeTypeFromMetadataForNonNativeLocalPathCompleted,
    126                    local_path,
    127                    callback));
    128     return;
    129   }
    130 #endif
    131 
    132   // For native local files, try to guess the mime from the extension. If
    133   // not available, then try to sniff if.
    134   scoped_ptr<std::string> mime_type_from_extension(new std::string);
    135   std::string* const mime_type_from_extension_ptr =
    136       mime_type_from_extension.get();
    137   BrowserThread::PostBlockingPoolTaskAndReply(
    138       FROM_HERE,
    139       base::Bind(base::IgnoreResult(&net::GetMimeTypeFromFile),
    140                  local_path,
    141                  mime_type_from_extension_ptr),
    142       base::Bind(&OnGetMimeTypeFromFileForNativeLocalPathCompleted,
    143                  local_path,
    144                  base::Passed(&mime_type_from_extension),
    145                  callback));
    146 }
    147 
    148 MimeTypeCollector::MimeTypeCollector(Profile* profile)
    149     : profile_(profile), left_(0), weak_ptr_factory_(this) {
    150 }
    151 
    152 MimeTypeCollector::~MimeTypeCollector() {
    153 }
    154 
    155 void MimeTypeCollector::CollectForURLs(
    156     const std::vector<storage::FileSystemURL>& urls,
    157     const CompletionCallback& callback) {
    158   std::vector<base::FilePath> local_paths;
    159   for (size_t i = 0; i < urls.size(); ++i) {
    160     local_paths.push_back(urls[i].path());
    161   }
    162 
    163   CollectForLocalPaths(local_paths, callback);
    164 }
    165 
    166 void MimeTypeCollector::CollectForLocalPaths(
    167     const std::vector<base::FilePath>& local_paths,
    168     const CompletionCallback& callback) {
    169   DCHECK(!callback.is_null());
    170   callback_ = callback;
    171 
    172   DCHECK(!result_.get());
    173   result_.reset(new std::vector<std::string>(local_paths.size()));
    174   left_ = local_paths.size();
    175 
    176   if (!left_) {
    177     // Nothing to process.
    178     base::MessageLoopProxy::current()->PostTask(
    179         FROM_HERE, base::Bind(callback_, base::Passed(&result_)));
    180     callback_ = CompletionCallback();
    181     return;
    182   }
    183 
    184   for (size_t i = 0; i < local_paths.size(); ++i) {
    185     GetMimeTypeForLocalPath(profile_,
    186                             local_paths[i],
    187                             base::Bind(&MimeTypeCollector::OnMimeTypeCollected,
    188                                        weak_ptr_factory_.GetWeakPtr(),
    189                                        i));
    190   }
    191 }
    192 
    193 void MimeTypeCollector::OnMimeTypeCollected(size_t index,
    194                                             const std::string& mime_type) {
    195   (*result_)[index] = mime_type;
    196   if (!--left_) {
    197     base::MessageLoopProxy::current()->PostTask(
    198         FROM_HERE, base::Bind(callback_, base::Passed(&result_)));
    199     // Release the callback to avoid a circullar reference in case an instance
    200     // of this class is a member of a ref counted class, which instance is bound
    201     // to this callback.
    202     callback_ = CompletionCallback();
    203   }
    204 }
    205 
    206 }  // namespace app_file_handler_util
    207 }  // namespace extensions
    208