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