summaryrefslogtreecommitdiffstats
path: root/chrome/browser/ui/webui/extension_icon_source.cc
diff options
context:
space:
mode:
authorjstritar@chromium.org <jstritar@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-03-02 19:15:21 +0000
committerjstritar@chromium.org <jstritar@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-03-02 19:15:21 +0000
commitf83773f72845ed180a23dcbbfa9afbc3c295f758 (patch)
treeb5b6e561a0367be32f1785609ea97ab9e648e857 /chrome/browser/ui/webui/extension_icon_source.cc
parentd1ca70cfc72762d6ff489f2558e6eb1333cbabf2 (diff)
downloadchromium_src-f83773f72845ed180a23dcbbfa9afbc3c295f758.zip
chromium_src-f83773f72845ed180a23dcbbfa9afbc3c295f758.tar.gz
chromium_src-f83773f72845ed180a23dcbbfa9afbc3c295f758.tar.bz2
Display disabled apps with grayed out images on the NTP. Call the re-enable logic when the user tries to launch the app. This also adds a chrome://extension-icon endpoint that provides easy access to extension and app icons.
BUG=61270 TEST=ExtensionIconSourceTest.IconsLoaded Review URL: http://codereview.chromium.org/6495002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@76579 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/ui/webui/extension_icon_source.cc')
-rw-r--r--chrome/browser/ui/webui/extension_icon_source.cc318
1 files changed, 318 insertions, 0 deletions
diff --git a/chrome/browser/ui/webui/extension_icon_source.cc b/chrome/browser/ui/webui/extension_icon_source.cc
new file mode 100644
index 0000000..321cd55
--- /dev/null
+++ b/chrome/browser/ui/webui/extension_icon_source.cc
@@ -0,0 +1,318 @@
+// Copyright (c) 2011 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 "chrome/browser/ui/webui/extension_icon_source.h"
+
+#include "base/callback.h"
+#include "base/ref_counted_memory.h"
+#include "base/string_number_conversions.h"
+#include "base/string_split.h"
+#include "base/string_util.h"
+#include "base/stringprintf.h"
+#include "base/task.h"
+#include "base/threading/thread.h"
+#include "chrome/browser/extensions/extension_prefs.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/extensions/extension.h"
+#include "chrome/common/extensions/extension_resource.h"
+#include "chrome/common/url_constants.h"
+#include "grit/theme_resources.h"
+#include "googleurl/src/gurl.h"
+#include "skia/ext/image_operations.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/gfx/codec/png_codec.h"
+#include "ui/gfx/color_utils.h"
+#include "ui/gfx/skbitmap_operations.h"
+#include "webkit/glue/image_decoder.h"
+
+namespace {
+
+scoped_refptr<RefCountedMemory> BitmapToMemory(SkBitmap* image) {
+ std::vector<unsigned char> output;
+ gfx::PNGCodec::EncodeBGRASkBitmap(*image, false, &output);
+
+ scoped_refptr<RefCountedBytes> image_bytes(new RefCountedBytes);
+ image_bytes->data.resize(output.size());
+ std::copy(output.begin(), output.end(), image_bytes->data.begin());
+ return image_bytes;
+}
+
+void DesaturateImage(SkBitmap* image) {
+ color_utils::HSL shift = {-1, 0, 0.6};
+ *image = SkBitmapOperations::CreateHSLShiftedBitmap(*image, shift);
+}
+
+SkBitmap* ToBitmap(const unsigned char* data, size_t size) {
+ webkit_glue::ImageDecoder decoder;
+ SkBitmap* decoded = new SkBitmap();
+ *decoded = decoder.Decode(data, size);
+ return decoded;
+}
+
+SkBitmap* LoadImageByResourceId(int resource_id) {
+ std::string contents = ResourceBundle::GetSharedInstance()
+ .GetRawDataResource(resource_id).as_string();
+
+ // Convert and return it.
+ const unsigned char* data =
+ reinterpret_cast<const unsigned char*>(contents.data());
+ return ToBitmap(data, contents.length());
+}
+
+} // namespace
+
+
+ExtensionIconSource::ExtensionIconSource(Profile* profile)
+ : DataSource(chrome::kChromeUIExtensionIconHost, MessageLoop::current()),
+ profile_(profile),
+ next_tracker_id_(0) {
+ tracker_.reset(new ImageLoadingTracker(this));
+}
+
+struct ExtensionIconSource::ExtensionIconRequest {
+ int request_id;
+ const Extension* extension;
+ bool grayscale;
+ Extension::Icons size;
+ ExtensionIconSet::MatchType match;
+};
+
+ExtensionIconSource::~ExtensionIconSource() {
+ // Clean up all the temporary data we're holding for requests.
+ std::map<int, ExtensionIconRequest*>::iterator i;
+ for (i = request_map_.begin(); i != request_map_.end(); i++) {
+ delete i->second;
+ request_map_.erase(i);
+ }
+}
+
+// static
+GURL ExtensionIconSource::GetIconURL(const Extension* extension,
+ Extension::Icons icon_size,
+ ExtensionIconSet::MatchType match,
+ bool grayscale) {
+ GURL icon_url(base::StringPrintf("%s%s/%d/%d%s",
+ chrome::kChromeUIExtensionIconURL,
+ extension->id().c_str(),
+ icon_size,
+ match,
+ grayscale ? "?grayscale=true" : ""));
+ CHECK(icon_url.is_valid());
+ return icon_url;
+}
+
+std::string ExtensionIconSource::GetMimeType(const std::string&) const {
+ // We need to explicitly return a mime type, otherwise if the user tries to
+ // drag the image they get no extension.
+ return "image/png";
+}
+
+void ExtensionIconSource::StartDataRequest(const std::string& path,
+ bool is_off_the_record,
+ int request_id) {
+ // This is where everything gets started. First, parse the request and make
+ // the request data available for later.
+ if (!ParseData(path, request_id)) {
+ SendDefaultResponse(request_id);
+ return;
+ }
+
+ ExtensionIconRequest* request = GetData(request_id);
+ ExtensionResource icon =
+ request->extension->GetIconResource(request->size, request->match);
+
+ // We fall back to multiple sources, using the following order:
+ // 1) The icons as listed in the extension / app manifests.
+ // 2) If a 16px icon and the extension has a launch URL, see if Chrome
+ // has a corresponding favicon.
+ // 3) If still no matches, load the default extension / application icon.
+ if (!icon.relative_path().empty()) {
+ LoadExtensionImage(icon, request_id);
+ return;
+ }
+
+ if (request->size == Extension::EXTENSION_ICON_BITTY)
+ LoadFaviconImage(request_id);
+ else
+ LoadDefaultImage(request_id);
+}
+
+SkBitmap* ExtensionIconSource::GetDefaultAppImage() {
+ if (!default_app_data_.get())
+ default_app_data_.reset(LoadImageByResourceId(IDR_APP_DEFAULT_ICON));
+
+ return default_app_data_.get();
+}
+
+SkBitmap* ExtensionIconSource::GetDefaultExtensionImage() {
+ if (!default_extension_data_.get())
+ default_extension_data_.reset(
+ LoadImageByResourceId(IDR_EXTENSION_DEFAULT_ICON));
+
+ return default_extension_data_.get();
+}
+
+void ExtensionIconSource::FinalizeImage(SkBitmap* image,
+ int request_id) {
+ if (GetData(request_id)->grayscale)
+ DesaturateImage(image);
+
+ ClearData(request_id);
+ SendResponse(request_id, BitmapToMemory(image));
+}
+
+void ExtensionIconSource::LoadDefaultImage(int request_id) {
+ ExtensionIconRequest* request = GetData(request_id);
+ SkBitmap* decoded = NULL;
+
+ if (request->extension->is_app())
+ decoded = GetDefaultAppImage();
+ else
+ decoded = GetDefaultExtensionImage();
+
+ *decoded = skia::ImageOperations::Resize(
+ *decoded, skia::ImageOperations::RESIZE_LANCZOS3,
+ request->size, request->size);
+
+ FinalizeImage(decoded, request_id);
+}
+
+void ExtensionIconSource::LoadExtensionImage(ExtensionResource icon,
+ int request_id) {
+ ExtensionIconRequest* request = GetData(request_id);
+ tracker_map_[next_tracker_id_++] = request_id;
+ tracker_->LoadImage(request->extension,
+ icon,
+ gfx::Size(request->size, request->size),
+ ImageLoadingTracker::DONT_CACHE);
+}
+
+void ExtensionIconSource::LoadFaviconImage(int request_id) {
+ FaviconService* favicon_service =
+ profile_->GetFaviconService(Profile::EXPLICIT_ACCESS);
+ // Fall back to the default icons if the service isn't available.
+ if (favicon_service == NULL) {
+ LoadDefaultImage(request_id);
+ return;
+ }
+
+ GURL favicon_url = GetData(request_id)->extension->GetFullLaunchURL();
+ FaviconService::Handle handle = favicon_service->GetFaviconForURL(
+ favicon_url, &cancelable_consumer_,
+ NewCallback(this, &ExtensionIconSource::OnFaviconDataAvailable));
+ cancelable_consumer_.SetClientData(favicon_service, handle, request_id);
+}
+
+void ExtensionIconSource::OnFaviconDataAvailable(
+ FaviconService::Handle request_handle,
+ bool know_favicon,
+ scoped_refptr<RefCountedMemory> data,
+ bool expired,
+ GURL icon_url) {
+ int request_id = cancelable_consumer_.GetClientData(
+ profile_->GetFaviconService(Profile::EXPLICIT_ACCESS), request_handle);
+ ExtensionIconRequest* request = GetData(request_id);
+
+ // Fallback to the default icon if there wasn't a favicon.
+ if (!know_favicon || !data.get() || !data->size()) {
+ LoadDefaultImage(request_id);
+ return;
+ }
+
+ if (!request->grayscale) {
+ // If we don't need a grayscale image, then we can bypass FinalizeImage
+ // to avoid unnecessary conversions.
+ ClearData(request_id);
+ SendResponse(request_id, data);
+ } else {
+ FinalizeImage(ToBitmap(data->front(), data->size()), request_id);
+ }
+}
+
+void ExtensionIconSource::OnImageLoaded(SkBitmap* image,
+ ExtensionResource resource,
+ int index) {
+ int request_id = tracker_map_[index];
+ tracker_map_.erase(tracker_map_.find(index));
+ FinalizeImage(image, request_id);
+}
+
+bool ExtensionIconSource::ParseData(const std::string& path,
+ int request_id) {
+ // Extract the parameters from the path by lower casing and splitting.
+ std::string path_lower = StringToLowerASCII(path);
+ std::vector<std::string> path_parts;
+
+ base::SplitString(path_lower, '/', &path_parts);
+ if (path_lower.empty() || path_parts.size() < 3)
+ return false;
+
+ std::string size_param = path_parts.at(1);
+ std::string match_param = path_parts.at(2);
+ match_param = match_param.substr(0, match_param.find('?'));
+
+ // The icon size and match types are encoded as string representations of
+ // their enum values, so to get the enum back, we read the string as an int
+ // and then cast to the enum.
+ Extension::Icons size;
+ int size_num;
+ if (!base::StringToInt(size_param, &size_num))
+ return false;
+ size = static_cast<Extension::Icons>(size_num);
+
+ ExtensionIconSet::MatchType match_type;
+ int match_num;
+ if (!base::StringToInt(match_param, &match_num))
+ return false;
+ match_type = static_cast<ExtensionIconSet::MatchType>(match_num);
+
+ std::string extension_id = path_parts.at(0);
+ const Extension* extension =
+ profile_->GetExtensionService()->GetExtensionById(extension_id, true);
+ if (!extension)
+ return false;
+
+ bool grayscale = path_lower.find("grayscale=true") != std::string::npos;
+
+ SetData(request_id, extension, grayscale, size, match_type);
+
+ return true;
+}
+
+void ExtensionIconSource::SendDefaultResponse(int request_id) {
+ // We send back the default application icon (not resized or desaturated)
+ // as the default response, like when there is no data.
+ ClearData(request_id);
+ SendResponse(request_id, BitmapToMemory(GetDefaultAppImage()));
+}
+
+void ExtensionIconSource::SetData(int request_id,
+ const Extension* extension,
+ bool grayscale,
+ Extension::Icons size,
+ ExtensionIconSet::MatchType match) {
+ ExtensionIconRequest* request = new ExtensionIconRequest();
+ request->request_id = request_id;
+ request->extension = extension;
+ request->grayscale = grayscale;
+ request->size = size;
+ request->match = match;
+ request_map_[request_id] = request;
+}
+
+ExtensionIconSource::ExtensionIconRequest* ExtensionIconSource::GetData(
+ int request_id) {
+ return request_map_[request_id];
+}
+
+void ExtensionIconSource::ClearData(int request_id) {
+ std::map<int, ExtensionIconRequest*>::iterator i =
+ request_map_.find(request_id);
+ if (i == request_map_.end())
+ return;
+
+ delete i->second;
+ request_map_.erase(i);
+}