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