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/extensions/api/file_handlers/mime_util.h"
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"
16 #if defined(OS_CHROMEOS)
17 #include "chrome/browser/chromeos/file_manager/filesystem_api_util.h"
20 using content::BrowserThread
;
22 namespace extensions
{
23 namespace app_file_handler_util
{
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
);
31 const int bytes_read
=
32 base::ReadFile(local_path
, &content
[0], content
.size());
34 if (bytes_read
>= 0) {
35 net::SniffMimeType(&content
[0],
37 net::FilePathToFileURL(local_path
),
38 std::string(), // type_hint (passes no hint)
43 #if defined(OS_CHROMEOS)
44 // Converts a result passed as a scoped pointer to a dereferenced value passed
46 void OnGetMimeTypeFromFileForNonNativeLocalPathCompleted(
47 scoped_ptr
<std::string
> mime_type
,
48 const base::Callback
<void(const std::string
&)>& callback
) {
49 callback
.Run(*mime_type
);
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
55 void OnGetMimeTypeFromMetadataForNonNativeLocalPathCompleted(
56 const base::FilePath
& local_path
,
57 const base::Callback
<void(const std::string
&)>& callback
,
59 const std::string
& mime_type
) {
61 callback
.Run(mime_type
);
65 // MIME type not available with metadata, hence try to guess it from the
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(
72 base::Bind(base::IgnoreResult(&net::GetMimeTypeFromFile
),
74 mime_type_from_extension_ptr
),
75 base::Bind(&OnGetMimeTypeFromFileForNonNativeLocalPathCompleted
,
76 base::Passed(&mime_type_from_extension
),
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
);
90 // Handles response of net::GetMimeTypeFromFile for native file systems. If
91 // MIME type is available, then forwards it to |callback|. Otherwise, fallbacks
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
);
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(
106 base::Bind(&SniffMimeType
, local_path
, sniffed_mime_type_ptr
),
107 base::Bind(&OnSniffMimeTypeForNativeLocalPathCompleted
,
108 base::Passed(&sniffed_mime_type
),
112 // Fetches MIME type for a local path and returns it with a |callback|.
113 void GetMimeTypeForLocalPath(
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(
125 base::Bind(&OnGetMimeTypeFromMetadataForNonNativeLocalPathCompleted
,
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(
139 base::Bind(base::IgnoreResult(&net::GetMimeTypeFromFile
),
141 mime_type_from_extension_ptr
),
142 base::Bind(&OnGetMimeTypeFromFileForNativeLocalPathCompleted
,
144 base::Passed(&mime_type_from_extension
),
148 MimeTypeCollector::MimeTypeCollector(Profile
* profile
)
149 : profile_(profile
), left_(0), weak_ptr_factory_(this) {
152 MimeTypeCollector::~MimeTypeCollector() {
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());
163 CollectForLocalPaths(local_paths
, 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
;
172 DCHECK(!result_
.get());
173 result_
.reset(new std::vector
<std::string
>(local_paths
.size()));
174 left_
= local_paths
.size();
177 // Nothing to process.
178 base::MessageLoopProxy::current()->PostTask(
179 FROM_HERE
, base::Bind(callback_
, base::Passed(&result_
)));
180 callback_
= CompletionCallback();
184 for (size_t i
= 0; i
< local_paths
.size(); ++i
) {
185 GetMimeTypeForLocalPath(profile_
,
187 base::Bind(&MimeTypeCollector::OnMimeTypeCollected
,
188 weak_ptr_factory_
.GetWeakPtr(),
193 void MimeTypeCollector::OnMimeTypeCollected(size_t index
,
194 const std::string
& mime_type
) {
195 (*result_
)[index
] = mime_type
;
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
202 callback_
= CompletionCallback();
206 } // namespace app_file_handler_util
207 } // namespace extensions