• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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|.
SniffMimeType(const base::FilePath & local_path,std::string * 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|.
OnGetMimeTypeFromFileForNonNativeLocalPathCompleted(scoped_ptr<std::string> mime_type,const base::Callback<void (const std::string &)> & 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.
OnGetMimeTypeFromMetadataForNonNativeLocalPathCompleted(const base::FilePath & local_path,const base::Callback<void (const std::string &)> & callback,bool success,const std::string & mime_type)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.
OnSniffMimeTypeForNativeLocalPathCompleted(scoped_ptr<std::string> mime_type,const base::Callback<void (const std::string &)> & callback)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.
OnGetMimeTypeFromFileForNativeLocalPathCompleted(const base::FilePath & local_path,scoped_ptr<std::string> mime_type,const base::Callback<void (const std::string &)> & callback)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|.
GetMimeTypeForLocalPath(Profile * profile,const base::FilePath & local_path,const base::Callback<void (const std::string &)> & 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  
MimeTypeCollector(Profile * profile)148  MimeTypeCollector::MimeTypeCollector(Profile* profile)
149      : profile_(profile), left_(0), weak_ptr_factory_(this) {
150  }
151  
~MimeTypeCollector()152  MimeTypeCollector::~MimeTypeCollector() {
153  }
154  
CollectForURLs(const std::vector<storage::FileSystemURL> & urls,const CompletionCallback & callback)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  
CollectForLocalPaths(const std::vector<base::FilePath> & local_paths,const CompletionCallback & callback)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  
OnMimeTypeCollected(size_t index,const std::string & mime_type)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