OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2010 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 <string> |
| 6 |
| 7 #include "base/file_path.h" |
| 8 #include "base/logging.h" |
| 9 #include "base/message_loop.h" |
| 10 #include "base/string_util.h" |
| 11 #include "base/task.h" |
| 12 #include "base/utf_string_conversions.h" |
| 13 #include "chrome/browser/browser_process.h" |
| 14 #include "chrome/browser/chrome_thread.h" |
| 15 #include "chrome/browser/icon_manager.h" |
| 16 #include "chrome/browser/icon_url_data_manager.h" |
| 17 #include "chrome/common/url_constants.h" |
| 18 #include "gfx/codec/png_codec.h" |
| 19 #include "net/base/escape.h" |
| 20 #include "net/base/net_errors.h" |
| 21 #include "net/base/io_buffer.h" |
| 22 #include "net/url_request/url_request.h" |
| 23 #include "net/url_request/url_request_job.h" |
| 24 |
| 25 // Definition of IconURI. |
| 26 |
| 27 const std::string IconURI::DIRECTORY = "directory"; |
| 28 const std::string IconURI::PARENT_DIRECTORY = "parentdir"; |
| 29 const std::string IconURI::UNKNOWN = "unknown"; |
| 30 |
| 31 const std::string IconURI::SMALL_ID = "small"; |
| 32 const std::string IconURI::MEDIUM_ID = "medium"; |
| 33 const std::string IconURI::LARGE_ID = "large"; |
| 34 |
| 35 IconURI::IconURI() : size_(SMALL) {} |
| 36 |
| 37 IconURI::~IconURI() {} |
| 38 |
| 39 bool IconURI::ParseIconURI(const GURL& url) { |
| 40 url_ = url; |
| 41 |
| 42 if (!IsValid()) |
| 43 return false; |
| 44 |
| 45 std::string path; |
| 46 TrimString(url.path(), "/", &path); |
| 47 StringToLowerASCII(&path); |
| 48 |
| 49 size_t semi_colon_index = path.find(';'); |
| 50 std::string size_str; |
| 51 |
| 52 if (semi_colon_index != std::string::npos) { |
| 53 size_str = path.substr(semi_colon_index + 1); |
| 54 SetSize(size_str); |
| 55 } |
| 56 |
| 57 std::string identifier = path.substr(0, semi_colon_index); |
| 58 SetIdentifier(identifier); |
| 59 |
| 60 return true; |
| 61 } |
| 62 |
| 63 bool IconURI::IsValid() { |
| 64 return url_.SchemeIs("icon"); |
| 65 } |
| 66 |
| 67 std::string IconURI::extension() const { return extension_; } |
| 68 |
| 69 std::string IconURI::media_type() const { return media_type_; } |
| 70 |
| 71 std::string IconURI::keyword() const { |
| 72 if (keyword_.empty() && extension_.empty() && media_type_.empty()) |
| 73 return UNKNOWN; |
| 74 |
| 75 return keyword_; |
| 76 } |
| 77 |
| 78 IconLoader::IconSize IconURI::GetIconLoaderSize() const { |
| 79 // TODO(plafayette): Support more sizes. Scale when necessary. |
| 80 if (size_ >= LARGE) |
| 81 return IconLoader::LARGE; |
| 82 else if (size_ >= MEDIUM) |
| 83 return IconLoader::NORMAL; |
| 84 |
| 85 return IconLoader::SMALL; |
| 86 } |
| 87 |
| 88 int IconURI::size() const { return size_; } |
| 89 |
| 90 void IconURI::SetIdentifier(const std::string& identifier) { |
| 91 if (!identifier.empty() && identifier[0] == '.') { |
| 92 SetExtension(identifier); |
| 93 return; |
| 94 } |
| 95 |
| 96 if (identifier.find(':') != std::string::npos) { |
| 97 SetMediaType(identifier); |
| 98 return; |
| 99 } |
| 100 |
| 101 if (identifier.compare(DIRECTORY) == 0) |
| 102 keyword_ = DIRECTORY; |
| 103 else if (identifier.compare(PARENT_DIRECTORY) == 0) |
| 104 keyword_ = PARENT_DIRECTORY; |
| 105 else if (identifier.compare(UNKNOWN) == 0) |
| 106 keyword_ = UNKNOWN; // Use default "unknown" icon image. |
| 107 } |
| 108 |
| 109 void IconURI::SetExtension(const std::string& ext) { |
| 110 if (!ext.empty() && ext[0] != '.') |
| 111 return; |
| 112 |
| 113 extension_ = ext; |
| 114 } |
| 115 |
| 116 void IconURI::SetMediaType(const std::string& media_type) { |
| 117 size_t colon_index = media_type.find(':'); |
| 118 if (colon_index == std::string::npos || colon_index == 0) |
| 119 return; |
| 120 |
| 121 media_type_ = media_type; |
| 122 media_type_[colon_index] = '/'; // Use the normal media type separator. |
| 123 } |
| 124 |
| 125 void IconURI::SetSize(const std::string& size_str) { |
| 126 int size = atoi(size_str.c_str()); |
| 127 if (size > 0) { |
| 128 size_ = size; |
| 129 return; |
| 130 } |
| 131 |
| 132 if (size_str.compare(LARGE_ID) == 0) |
| 133 size_ = LARGE; |
| 134 else if (size_str.compare(MEDIUM_ID) == 0) |
| 135 size_ = MEDIUM; |
| 136 else |
| 137 size_ = SMALL; // Default to a 16x16 icon. |
| 138 } |
| 139 |
| 140 |
| 141 class IconJobSource : public base::RefCountedThreadSafe<IconJobSource> { |
| 142 public: |
| 143 typedef int RequestID; |
| 144 |
| 145 IconJobSource() {} |
| 146 |
| 147 ~IconJobSource() { |
| 148 cancelable_consumer_.CancelAllRequests(); |
| 149 } |
| 150 |
| 151 void StartDataRequest(const IconURI& icon_uri, RequestID request_id) { |
| 152 IconManager* im = g_browser_process->icon_manager(); |
| 153 |
| 154 // TODO(plafayette): Resolve icons by content type and keyword. |
| 155 std::string extension = icon_uri.extension(); |
| 156 |
| 157 // On Windows, the IconLoader resolves icons by file extension. To get the |
| 158 // generic file icon, we provide an unknown file extension. |
| 159 extension = extension.empty() ? ".unknown" : extension; |
| 160 |
| 161 FilePath file_path(UTF8ToWide(extension)); |
| 162 IconLoader::IconSize size = icon_uri.GetIconLoaderSize(); |
| 163 |
| 164 SkBitmap* icon = im->LookupIcon(file_path, size); |
| 165 |
| 166 if (icon) { |
| 167 scoped_refptr<RefCountedBytes> icon_data = new RefCountedBytes; |
| 168 gfx::PNGCodec::EncodeBGRASkBitmap(*icon, false, &icon_data->data); |
| 169 |
| 170 SendResponse(icon_data, request_id); |
| 171 } else { |
| 172 // Icon was not in cache, go fetch it slowly. |
| 173 IconManager::Handle h = im->LoadIcon(file_path, |
| 174 size, |
| 175 &cancelable_consumer_, |
| 176 NewCallback(this, &IconJobSource::OnFileIconDataAvailable)); |
| 177 |
| 178 cancelable_consumer_.SetClientData(im, h, request_id); |
| 179 } |
| 180 } |
| 181 |
| 182 void OnFileIconDataAvailable(IconManager::Handle handle, |
| 183 SkBitmap* icon) { |
| 184 IconManager* im = g_browser_process->icon_manager(); |
| 185 int request_id = cancelable_consumer_.GetClientData(im, handle); |
| 186 |
| 187 if (icon) { |
| 188 scoped_refptr<RefCountedBytes> icon_data = new RefCountedBytes; |
| 189 gfx::PNGCodec::EncodeBGRASkBitmap(*icon, false, &icon_data->data); |
| 190 |
| 191 SendResponse(icon_data, request_id); |
| 192 } else { |
| 193 SendResponse(NULL, request_id); |
| 194 } |
| 195 } |
| 196 |
| 197 void SendResponse(RefCountedMemory* bytes, RequestID request_id) { |
| 198 ChromeThread::PostTask( |
| 199 ChromeThread::IO, FROM_HERE, |
| 200 NewRunnableMethod(Singleton<IconURLDataManager>::get(), |
| 201 &IconURLDataManager::DataAvailable, |
| 202 request_id, |
| 203 scoped_refptr<RefCountedMemory>(bytes))); |
| 204 } |
| 205 |
| 206 private: |
| 207 CancelableRequestConsumerT<int, 0> cancelable_consumer_; |
| 208 }; |
| 209 |
| 210 |
| 211 // Definition of IconURLDataManager |
| 212 |
| 213 IconURLDataManager::IconURLDataManager() |
| 214 : data_source_(new IconJobSource()), |
| 215 next_request_id_(0) { |
| 216 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); |
| 217 } |
| 218 |
| 219 IconURLDataManager::~IconURLDataManager() {} |
| 220 |
| 221 URLRequestJob* IconURLDataManager::Factory(URLRequest* request, |
| 222 const std::string& scheme) { |
| 223 return new URLRequestIconJob(request); |
| 224 } |
| 225 |
| 226 void IconURLDataManager::StartDataRequest(const IconURI& icon_uri, |
| 227 URLRequestIconJob* job) { |
| 228 RequestID request_id = next_request_id_++; |
| 229 // Save this request so we know where to send the data. |
| 230 pending_requests_.insert(std::make_pair(request_id, job)); |
| 231 |
| 232 data_source_->StartDataRequest(icon_uri, request_id); |
| 233 } |
| 234 |
| 235 void IconURLDataManager::DataAvailable(RequestID request_id, |
| 236 scoped_refptr<RefCountedMemory> bytes) { |
| 237 PendingRequestMap::iterator i = pending_requests_.find(request_id); |
| 238 if (i != pending_requests_.end()) { |
| 239 // We acquire a reference to the job so that it doesn't disappear under the |
| 240 // feet of any method invoked here (we could trigger a callback). |
| 241 scoped_refptr<URLRequestIconJob> job = i->second; |
| 242 pending_requests_.erase(i); |
| 243 job->DataAvailable(bytes); |
| 244 } |
| 245 } |
| 246 |
| 247 void IconURLDataManager::RemoveRequest(URLRequestIconJob* job) { |
| 248 // Remove the request from our list of pending requests. |
| 249 // If/when the source sends the data that was requested, the data will just |
| 250 // be thrown away. |
| 251 for (PendingRequestMap::iterator i = pending_requests_.begin(); |
| 252 i != pending_requests_.end(); ++i) { |
| 253 if (i->second == job) { |
| 254 pending_requests_.erase(i); |
| 255 return; |
| 256 } |
| 257 } |
| 258 } |
| 259 |
| 260 bool IconURLDataManager::HasPendingJob(URLRequestIconJob* job) const { |
| 261 for (PendingRequestMap::const_iterator i = pending_requests_.begin(); |
| 262 i != pending_requests_.end(); ++i) { |
| 263 if (i->second == job) |
| 264 return true; |
| 265 } |
| 266 |
| 267 return false; |
| 268 } |
| 269 |
| 270 // URLRequestJob definition. |
| 271 |
| 272 URLRequestIconJob::URLRequestIconJob(URLRequest* request) |
| 273 : URLRequestJob(request), |
| 274 data_offset_(0), |
| 275 pending_buf_size_(0) {} |
| 276 |
| 277 void URLRequestIconJob::Start() { |
| 278 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( |
| 279 this, &URLRequestIconJob::StartAsync)); |
| 280 } |
| 281 |
| 282 void URLRequestIconJob::Kill() { |
| 283 Singleton<IconURLDataManager>()->RemoveRequest(this); |
| 284 } |
| 285 |
| 286 bool URLRequestIconJob::ReadRawData(net::IOBuffer* buf, |
| 287 int buf_size, |
| 288 int *bytes_read) { |
| 289 if (!data_.get()) { |
| 290 SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0)); |
| 291 DCHECK(!pending_buf_.get()); |
| 292 CHECK(buf->data()); |
| 293 pending_buf_ = buf; |
| 294 pending_buf_size_ = buf_size; |
| 295 return false; // Tell the caller we're still waiting for data. |
| 296 } |
| 297 |
| 298 // Otherwise, the data is available. |
| 299 CompleteRead(buf, buf_size, bytes_read); |
| 300 return true; |
| 301 } |
| 302 |
| 303 bool URLRequestIconJob::GetMimeType(std::string* mime_type) const { |
| 304 // Rely on image decoder inferring the correct type. |
| 305 return true; |
| 306 } |
| 307 |
| 308 void URLRequestIconJob::SetMimeType(const std::string& mime_type) { |
| 309 mime_type_ = mime_type; |
| 310 } |
| 311 |
| 312 URLRequestIconJob::~URLRequestIconJob() { |
| 313 CHECK(!Singleton<IconURLDataManager>()->HasPendingJob(this)); |
| 314 } |
| 315 |
| 316 void URLRequestIconJob::StartAsync() { |
| 317 if (!request_) |
| 318 return; |
| 319 |
| 320 if (StartRequest()) { |
| 321 NotifyHeadersComplete(); |
| 322 } else { |
| 323 NotifyStartError(URLRequestStatus(URLRequestStatus::FAILED, |
| 324 net::ERR_INVALID_URL)); |
| 325 } |
| 326 } |
| 327 |
| 328 bool URLRequestIconJob::StartRequest() { |
| 329 IconURI icon_uri; |
| 330 icon_uri.ParseIconURI(request_->url()); |
| 331 |
| 332 ChromeThread::PostTask(ChromeThread::UI, FROM_HERE, NewRunnableMethod( |
| 333 Singleton<IconURLDataManager>::get(), |
| 334 &IconURLDataManager::StartDataRequest, |
| 335 icon_uri, |
| 336 this)); |
| 337 |
| 338 return true; |
| 339 } |
| 340 |
| 341 void URLRequestIconJob::DataAvailable(RefCountedMemory* bytes) { |
| 342 if (bytes) { |
| 343 // The request completed, and we have all the data. |
| 344 // Clear any IO pending status. |
| 345 SetStatus(URLRequestStatus()); |
| 346 |
| 347 data_ = bytes; |
| 348 int bytes_read; |
| 349 if (pending_buf_.get()) { |
| 350 CHECK(pending_buf_->data()); |
| 351 CompleteRead(pending_buf_, pending_buf_size_, &bytes_read); |
| 352 pending_buf_ = NULL; |
| 353 NotifyReadComplete(bytes_read); |
| 354 } |
| 355 } else { |
| 356 // The request failed. |
| 357 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, 0)); |
| 358 } |
| 359 } |
| 360 |
| 361 void URLRequestIconJob::CompleteRead(net::IOBuffer* buf, |
| 362 int buf_size, |
| 363 int* bytes_read) { |
| 364 int remaining = static_cast<int>(data_->size()) - data_offset_; |
| 365 if (buf_size > remaining) |
| 366 buf_size = remaining; |
| 367 if (buf_size > 0) { |
| 368 memcpy(buf->data(), data_->front() + data_offset_, buf_size); |
| 369 data_offset_ += buf_size; |
| 370 } |
| 371 *bytes_read = buf_size; |
| 372 } |
| 373 |
| 374 void RegisterURLRequestIconJob() { |
| 375 // Initialize. |
| 376 Singleton<IconURLDataManager>::get(); |
| 377 |
| 378 URLRequest::RegisterProtocolFactory(chrome::kIconURLScheme, |
| 379 &IconURLDataManager::Factory); |
| 380 } |
OLD | NEW |