1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/browser/ui/webui/ntp/favicon_webui_handler.h" 6 7 #include "base/bind.h" 8 #include "base/bind_helpers.h" 9 #include "base/strings/string_split.h" 10 #include "base/strings/string_util.h" 11 #include "base/strings/stringprintf.h" 12 #include "base/values.h" 13 #include "chrome/browser/extensions/extension_icon_manager.h" 14 #include "chrome/browser/extensions/extension_service.h" 15 #include "chrome/browser/favicon/favicon_service.h" 16 #include "chrome/browser/favicon/favicon_service_factory.h" 17 #include "chrome/browser/history/top_sites.h" 18 #include "chrome/browser/profiles/profile.h" 19 #include "chrome/common/url_constants.h" 20 #include "content/public/browser/web_ui.h" 21 #include "grit/ui_resources.h" 22 #include "third_party/skia/include/core/SkBitmap.h" 23 #include "ui/base/l10n/l10n_util.h" 24 #include "ui/gfx/codec/png_codec.h" 25 #include "ui/gfx/color_analysis.h" 26 #include "ui/gfx/favicon_size.h" 27 28 namespace { 29 30 base::StringValue* SkColorToCss(SkColor color) { 31 return new base::StringValue(base::StringPrintf("rgb(%d, %d, %d)", 32 SkColorGetR(color), 33 SkColorGetG(color), 34 SkColorGetB(color))); 35 } 36 37 base::StringValue* GetDominantColorCssString( 38 scoped_refptr<base::RefCountedMemory> png) { 39 color_utils::GridSampler sampler; 40 SkColor color = color_utils::CalculateKMeanColorOfPNG(png); 41 return SkColorToCss(color); 42 } 43 44 } // namespace 45 46 // Thin inheritance-dependent trampoline to forward notification of app 47 // icon loads to the FaviconWebUIHandler. Base class does caching of icons. 48 class ExtensionIconColorManager : public ExtensionIconManager { 49 public: 50 explicit ExtensionIconColorManager(FaviconWebUIHandler* handler) 51 : ExtensionIconManager(), 52 handler_(handler) {} 53 virtual ~ExtensionIconColorManager() {} 54 55 virtual void OnImageLoaded(const std::string& extension_id, 56 const gfx::Image& image) OVERRIDE { 57 ExtensionIconManager::OnImageLoaded(extension_id, image); 58 handler_->NotifyAppIconReady(extension_id); 59 } 60 61 private: 62 FaviconWebUIHandler* handler_; 63 }; 64 65 FaviconWebUIHandler::FaviconWebUIHandler() 66 : id_(0), 67 app_icon_color_manager_(new ExtensionIconColorManager(this)) { 68 } 69 70 FaviconWebUIHandler::~FaviconWebUIHandler() { 71 } 72 73 void FaviconWebUIHandler::RegisterMessages() { 74 web_ui()->RegisterMessageCallback("getFaviconDominantColor", 75 base::Bind(&FaviconWebUIHandler::HandleGetFaviconDominantColor, 76 base::Unretained(this))); 77 web_ui()->RegisterMessageCallback("getAppIconDominantColor", 78 base::Bind(&FaviconWebUIHandler::HandleGetAppIconDominantColor, 79 base::Unretained(this))); 80 } 81 82 void FaviconWebUIHandler::HandleGetFaviconDominantColor( 83 const base::ListValue* args) { 84 std::string path; 85 CHECK(args->GetString(0, &path)); 86 std::string prefix = "chrome://favicon/size/"; 87 DCHECK(StartsWithASCII(path, prefix, false)) << "path is " << path; 88 size_t slash = path.find("/", prefix.length()); 89 path = path.substr(slash + 1); 90 91 std::string dom_id; 92 CHECK(args->GetString(1, &dom_id)); 93 94 FaviconService* favicon_service = FaviconServiceFactory::GetForProfile( 95 Profile::FromWebUI(web_ui()), Profile::EXPLICIT_ACCESS); 96 if (!favicon_service || path.empty()) 97 return; 98 99 GURL url(path); 100 // Intercept requests for prepopulated pages. 101 for (size_t i = 0; i < arraysize(history::kPrepopulatedPages); i++) { 102 if (url.spec() == 103 l10n_util::GetStringUTF8(history::kPrepopulatedPages[i].url_id)) { 104 base::StringValue dom_id_value(dom_id); 105 scoped_ptr<base::StringValue> color( 106 SkColorToCss(history::kPrepopulatedPages[i].color)); 107 web_ui()->CallJavascriptFunction("ntp.setFaviconDominantColor", 108 dom_id_value, *color); 109 return; 110 } 111 } 112 113 dom_id_map_[id_] = dom_id; 114 favicon_service->GetRawFaviconForPageURL( 115 FaviconService::FaviconForPageURLParams( 116 url, favicon_base::FAVICON, gfx::kFaviconSize), 117 1.0f, 118 base::Bind(&FaviconWebUIHandler::OnFaviconDataAvailable, 119 base::Unretained(this), 120 id_++), 121 &cancelable_task_tracker_); 122 } 123 124 void FaviconWebUIHandler::OnFaviconDataAvailable( 125 int id, 126 const favicon_base::FaviconRawBitmapResult& bitmap_result) { 127 scoped_ptr<base::StringValue> color_value; 128 129 if (bitmap_result.is_valid()) 130 color_value.reset(GetDominantColorCssString(bitmap_result.bitmap_data)); 131 else 132 color_value.reset(new base::StringValue("#919191")); 133 134 base::StringValue dom_id(dom_id_map_[id]); 135 web_ui()->CallJavascriptFunction("ntp.setFaviconDominantColor", 136 dom_id, *color_value); 137 dom_id_map_.erase(id); 138 } 139 140 void FaviconWebUIHandler::HandleGetAppIconDominantColor( 141 const base::ListValue* args) { 142 std::string extension_id; 143 CHECK(args->GetString(0, &extension_id)); 144 145 ExtensionService* extension_service = 146 Profile::FromWebUI(web_ui())->GetExtensionService(); 147 const extensions::Extension* extension = extension_service->GetExtensionById( 148 extension_id, false); 149 if (!extension) 150 return; 151 app_icon_color_manager_->LoadIcon(extension_service->profile(), extension); 152 } 153 154 void FaviconWebUIHandler::NotifyAppIconReady(const std::string& extension_id) { 155 const SkBitmap& bitmap = app_icon_color_manager_->GetIcon(extension_id); 156 // TODO(estade): would be nice to avoid a round trip through png encoding. 157 std::vector<unsigned char> bits; 158 if (!gfx::PNGCodec::EncodeBGRASkBitmap(bitmap, true, &bits)) 159 return; 160 scoped_refptr<base::RefCountedStaticMemory> bits_mem( 161 new base::RefCountedStaticMemory(&bits.front(), bits.size())); 162 scoped_ptr<base::StringValue> color_value( 163 GetDominantColorCssString(bits_mem)); 164 base::StringValue id(extension_id); 165 web_ui()->CallJavascriptFunction( 166 "ntp.setFaviconDominantColor", id, *color_value); 167 } 168