Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3267)

Unified Diff: chrome/browser/icon_url_data_manager.cc

Issue 3324009: Work in progress implementation of Icon URI scheme. (Closed) Base URL: git://git.chromium.org/chromium.git
Patch Set: Created 10 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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);
+}

Powered by Google App Engine
This is Rietveld 408576698