Index: chrome/browser/icon_url_data_manager.cc |
diff --git a/chrome/browser/icon_url_data_manager.cc b/chrome/browser/icon_url_data_manager.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..575918eca1bb57591532788854a26d55d8f4cad0 |
--- /dev/null |
+++ b/chrome/browser/icon_url_data_manager.cc |
@@ -0,0 +1,380 @@ |
+// Copyright (c) 2010 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include <string> |
+ |
+#include "base/file_path.h" |
+#include "base/logging.h" |
+#include "base/message_loop.h" |
+#include "base/string_util.h" |
+#include "base/task.h" |
+#include "base/utf_string_conversions.h" |
+#include "chrome/browser/browser_process.h" |
+#include "chrome/browser/chrome_thread.h" |
+#include "chrome/browser/icon_manager.h" |
+#include "chrome/browser/icon_url_data_manager.h" |
+#include "chrome/common/url_constants.h" |
+#include "gfx/codec/png_codec.h" |
+#include "net/base/escape.h" |
+#include "net/base/net_errors.h" |
+#include "net/base/io_buffer.h" |
+#include "net/url_request/url_request.h" |
+#include "net/url_request/url_request_job.h" |
+ |
+// Definition of IconURI. |
+ |
+const std::string IconURI::DIRECTORY = "directory"; |
+const std::string IconURI::PARENT_DIRECTORY = "parentdir"; |
+const std::string IconURI::UNKNOWN = "unknown"; |
+ |
+const std::string IconURI::SMALL_ID = "small"; |
+const std::string IconURI::MEDIUM_ID = "medium"; |
+const std::string IconURI::LARGE_ID = "large"; |
+ |
+IconURI::IconURI() : size_(SMALL) {} |
+ |
+IconURI::~IconURI() {} |
+ |
+bool IconURI::ParseIconURI(const GURL& url) { |
+ url_ = url; |
+ |
+ if (!IsValid()) |
+ return false; |
+ |
+ std::string path; |
+ TrimString(url.path(), "/", &path); |
+ StringToLowerASCII(&path); |
+ |
+ size_t semi_colon_index = path.find(';'); |
+ std::string size_str; |
+ |
+ if (semi_colon_index != std::string::npos) { |
+ size_str = path.substr(semi_colon_index + 1); |
+ SetSize(size_str); |
+ } |
+ |
+ std::string identifier = path.substr(0, semi_colon_index); |
+ SetIdentifier(identifier); |
+ |
+ return true; |
+} |
+ |
+bool IconURI::IsValid() { |
+ return url_.SchemeIs("icon"); |
+} |
+ |
+std::string IconURI::extension() const { return extension_; } |
+ |
+std::string IconURI::media_type() const { return media_type_; } |
+ |
+std::string IconURI::keyword() const { |
+ if (keyword_.empty() && extension_.empty() && media_type_.empty()) |
+ return UNKNOWN; |
+ |
+ return keyword_; |
+} |
+ |
+IconLoader::IconSize IconURI::GetIconLoaderSize() const { |
+ // TODO(plafayette): Support more sizes. Scale when necessary. |
+ if (size_ >= LARGE) |
+ return IconLoader::LARGE; |
+ else if (size_ >= MEDIUM) |
+ return IconLoader::NORMAL; |
+ |
+ return IconLoader::SMALL; |
+} |
+ |
+int IconURI::size() const { return size_; } |
+ |
+void IconURI::SetIdentifier(const std::string& identifier) { |
+ if (!identifier.empty() && identifier[0] == '.') { |
+ SetExtension(identifier); |
+ return; |
+ } |
+ |
+ if (identifier.find(':') != std::string::npos) { |
+ SetMediaType(identifier); |
+ return; |
+ } |
+ |
+ if (identifier.compare(DIRECTORY) == 0) |
+ keyword_ = DIRECTORY; |
+ else if (identifier.compare(PARENT_DIRECTORY) == 0) |
+ keyword_ = PARENT_DIRECTORY; |
+ else if (identifier.compare(UNKNOWN) == 0) |
+ keyword_ = UNKNOWN; // Use default "unknown" icon image. |
+} |
+ |
+void IconURI::SetExtension(const std::string& ext) { |
+ if (!ext.empty() && ext[0] != '.') |
+ return; |
+ |
+ extension_ = ext; |
+} |
+ |
+void IconURI::SetMediaType(const std::string& media_type) { |
+ size_t colon_index = media_type.find(':'); |
+ if (colon_index == std::string::npos || colon_index == 0) |
+ return; |
+ |
+ media_type_ = media_type; |
+ media_type_[colon_index] = '/'; // Use the normal media type separator. |
+} |
+ |
+void IconURI::SetSize(const std::string& size_str) { |
+ int size = atoi(size_str.c_str()); |
+ if (size > 0) { |
+ size_ = size; |
+ return; |
+ } |
+ |
+ if (size_str.compare(LARGE_ID) == 0) |
+ size_ = LARGE; |
+ else if (size_str.compare(MEDIUM_ID) == 0) |
+ size_ = MEDIUM; |
+ else |
+ size_ = SMALL; // Default to a 16x16 icon. |
+} |
+ |
+ |
+class IconJobSource : public base::RefCountedThreadSafe<IconJobSource> { |
+ public: |
+ typedef int RequestID; |
+ |
+ IconJobSource() {} |
+ |
+ ~IconJobSource() { |
+ cancelable_consumer_.CancelAllRequests(); |
+ } |
+ |
+ void StartDataRequest(const IconURI& icon_uri, RequestID request_id) { |
+ IconManager* im = g_browser_process->icon_manager(); |
+ |
+ // TODO(plafayette): Resolve icons by content type and keyword. |
+ std::string extension = icon_uri.extension(); |
+ |
+ // On Windows, the IconLoader resolves icons by file extension. To get the |
+ // generic file icon, we provide an unknown file extension. |
+ extension = extension.empty() ? ".unknown" : extension; |
+ |
+ FilePath file_path(UTF8ToWide(extension)); |
+ IconLoader::IconSize size = icon_uri.GetIconLoaderSize(); |
+ |
+ SkBitmap* icon = im->LookupIcon(file_path, size); |
+ |
+ if (icon) { |
+ scoped_refptr<RefCountedBytes> icon_data = new RefCountedBytes; |
+ gfx::PNGCodec::EncodeBGRASkBitmap(*icon, false, &icon_data->data); |
+ |
+ SendResponse(icon_data, request_id); |
+ } else { |
+ // Icon was not in cache, go fetch it slowly. |
+ IconManager::Handle h = im->LoadIcon(file_path, |
+ size, |
+ &cancelable_consumer_, |
+ NewCallback(this, &IconJobSource::OnFileIconDataAvailable)); |
+ |
+ cancelable_consumer_.SetClientData(im, h, request_id); |
+ } |
+ } |
+ |
+ void OnFileIconDataAvailable(IconManager::Handle handle, |
+ SkBitmap* icon) { |
+ IconManager* im = g_browser_process->icon_manager(); |
+ int request_id = cancelable_consumer_.GetClientData(im, handle); |
+ |
+ if (icon) { |
+ scoped_refptr<RefCountedBytes> icon_data = new RefCountedBytes; |
+ gfx::PNGCodec::EncodeBGRASkBitmap(*icon, false, &icon_data->data); |
+ |
+ SendResponse(icon_data, request_id); |
+ } else { |
+ SendResponse(NULL, request_id); |
+ } |
+ } |
+ |
+ void SendResponse(RefCountedMemory* bytes, RequestID request_id) { |
+ ChromeThread::PostTask( |
+ ChromeThread::IO, FROM_HERE, |
+ NewRunnableMethod(Singleton<IconURLDataManager>::get(), |
+ &IconURLDataManager::DataAvailable, |
+ request_id, |
+ scoped_refptr<RefCountedMemory>(bytes))); |
+ } |
+ |
+ private: |
+ CancelableRequestConsumerT<int, 0> cancelable_consumer_; |
+}; |
+ |
+ |
+// Definition of IconURLDataManager |
+ |
+IconURLDataManager::IconURLDataManager() |
+ : data_source_(new IconJobSource()), |
+ next_request_id_(0) { |
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); |
+} |
+ |
+IconURLDataManager::~IconURLDataManager() {} |
+ |
+URLRequestJob* IconURLDataManager::Factory(URLRequest* request, |
+ const std::string& scheme) { |
+ return new URLRequestIconJob(request); |
+} |
+ |
+void IconURLDataManager::StartDataRequest(const IconURI& icon_uri, |
+ URLRequestIconJob* job) { |
+ RequestID request_id = next_request_id_++; |
+ // Save this request so we know where to send the data. |
+ pending_requests_.insert(std::make_pair(request_id, job)); |
+ |
+ data_source_->StartDataRequest(icon_uri, request_id); |
+} |
+ |
+void IconURLDataManager::DataAvailable(RequestID request_id, |
+ scoped_refptr<RefCountedMemory> bytes) { |
+ PendingRequestMap::iterator i = pending_requests_.find(request_id); |
+ if (i != pending_requests_.end()) { |
+ // We acquire a reference to the job so that it doesn't disappear under the |
+ // feet of any method invoked here (we could trigger a callback). |
+ scoped_refptr<URLRequestIconJob> job = i->second; |
+ pending_requests_.erase(i); |
+ job->DataAvailable(bytes); |
+ } |
+} |
+ |
+void IconURLDataManager::RemoveRequest(URLRequestIconJob* job) { |
+ // Remove the request from our list of pending requests. |
+ // If/when the source sends the data that was requested, the data will just |
+ // be thrown away. |
+ for (PendingRequestMap::iterator i = pending_requests_.begin(); |
+ i != pending_requests_.end(); ++i) { |
+ if (i->second == job) { |
+ pending_requests_.erase(i); |
+ return; |
+ } |
+ } |
+} |
+ |
+bool IconURLDataManager::HasPendingJob(URLRequestIconJob* job) const { |
+ for (PendingRequestMap::const_iterator i = pending_requests_.begin(); |
+ i != pending_requests_.end(); ++i) { |
+ if (i->second == job) |
+ return true; |
+ } |
+ |
+ return false; |
+} |
+ |
+// URLRequestJob definition. |
+ |
+URLRequestIconJob::URLRequestIconJob(URLRequest* request) |
+ : URLRequestJob(request), |
+ data_offset_(0), |
+ pending_buf_size_(0) {} |
+ |
+void URLRequestIconJob::Start() { |
+ MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( |
+ this, &URLRequestIconJob::StartAsync)); |
+} |
+ |
+void URLRequestIconJob::Kill() { |
+ Singleton<IconURLDataManager>()->RemoveRequest(this); |
+} |
+ |
+bool URLRequestIconJob::ReadRawData(net::IOBuffer* buf, |
+ int buf_size, |
+ int *bytes_read) { |
+ if (!data_.get()) { |
+ SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0)); |
+ DCHECK(!pending_buf_.get()); |
+ CHECK(buf->data()); |
+ pending_buf_ = buf; |
+ pending_buf_size_ = buf_size; |
+ return false; // Tell the caller we're still waiting for data. |
+ } |
+ |
+ // Otherwise, the data is available. |
+ CompleteRead(buf, buf_size, bytes_read); |
+ return true; |
+} |
+ |
+bool URLRequestIconJob::GetMimeType(std::string* mime_type) const { |
+ // Rely on image decoder inferring the correct type. |
+ return true; |
+} |
+ |
+void URLRequestIconJob::SetMimeType(const std::string& mime_type) { |
+ mime_type_ = mime_type; |
+} |
+ |
+URLRequestIconJob::~URLRequestIconJob() { |
+ CHECK(!Singleton<IconURLDataManager>()->HasPendingJob(this)); |
+} |
+ |
+void URLRequestIconJob::StartAsync() { |
+ if (!request_) |
+ return; |
+ |
+ if (StartRequest()) { |
+ NotifyHeadersComplete(); |
+ } else { |
+ NotifyStartError(URLRequestStatus(URLRequestStatus::FAILED, |
+ net::ERR_INVALID_URL)); |
+ } |
+} |
+ |
+bool URLRequestIconJob::StartRequest() { |
+ IconURI icon_uri; |
+ icon_uri.ParseIconURI(request_->url()); |
+ |
+ ChromeThread::PostTask(ChromeThread::UI, FROM_HERE, NewRunnableMethod( |
+ Singleton<IconURLDataManager>::get(), |
+ &IconURLDataManager::StartDataRequest, |
+ icon_uri, |
+ this)); |
+ |
+ return true; |
+} |
+ |
+void URLRequestIconJob::DataAvailable(RefCountedMemory* bytes) { |
+ if (bytes) { |
+ // The request completed, and we have all the data. |
+ // Clear any IO pending status. |
+ SetStatus(URLRequestStatus()); |
+ |
+ data_ = bytes; |
+ int bytes_read; |
+ if (pending_buf_.get()) { |
+ CHECK(pending_buf_->data()); |
+ CompleteRead(pending_buf_, pending_buf_size_, &bytes_read); |
+ pending_buf_ = NULL; |
+ NotifyReadComplete(bytes_read); |
+ } |
+ } else { |
+ // The request failed. |
+ NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, 0)); |
+ } |
+} |
+ |
+void URLRequestIconJob::CompleteRead(net::IOBuffer* buf, |
+ int buf_size, |
+ int* bytes_read) { |
+ int remaining = static_cast<int>(data_->size()) - data_offset_; |
+ if (buf_size > remaining) |
+ buf_size = remaining; |
+ if (buf_size > 0) { |
+ memcpy(buf->data(), data_->front() + data_offset_, buf_size); |
+ data_offset_ += buf_size; |
+ } |
+ *bytes_read = buf_size; |
+} |
+ |
+void RegisterURLRequestIconJob() { |
+ // Initialize. |
+ Singleton<IconURLDataManager>::get(); |
+ |
+ URLRequest::RegisterProtocolFactory(chrome::kIconURLScheme, |
+ &IconURLDataManager::Factory); |
+} |