| 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);
|
| +}
|
|
|