Don't show supervised user as "already on this device" while they're being imported.
[chromium-blink-merge.git] / chrome / browser / ui / webui / extensions / extension_icon_source.cc
blobaa4fc2edf5282ba02e410c591d7a50a379aef6c8
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.
5 #include "chrome/browser/ui/webui/extensions/extension_icon_source.h"
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/memory/ref_counted_memory.h"
10 #include "base/stl_util.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/string_split.h"
13 #include "base/strings/string_util.h"
14 #include "base/strings/stringprintf.h"
15 #include "base/threading/thread.h"
16 #include "chrome/browser/extensions/extension_service.h"
17 #include "chrome/browser/favicon/favicon_service_factory.h"
18 #include "chrome/browser/profiles/profile.h"
19 #include "chrome/common/extensions/extension_constants.h"
20 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
21 #include "chrome/common/url_constants.h"
22 #include "extensions/browser/extension_prefs.h"
23 #include "extensions/browser/extension_system.h"
24 #include "extensions/browser/image_loader.h"
25 #include "extensions/common/extension.h"
26 #include "extensions/common/extension_resource.h"
27 #include "extensions/common/manifest_handlers/icons_handler.h"
28 #include "extensions/grit/extensions_browser_resources.h"
29 #include "grit/component_extension_resources_map.h"
30 #include "skia/ext/image_operations.h"
31 #include "ui/base/layout.h"
32 #include "ui/base/resource/resource_bundle.h"
33 #include "ui/gfx/codec/png_codec.h"
34 #include "ui/gfx/color_utils.h"
35 #include "ui/gfx/favicon_size.h"
36 #include "ui/gfx/geometry/size.h"
37 #include "ui/gfx/skbitmap_operations.h"
38 #include "url/gurl.h"
40 namespace extensions {
42 namespace {
44 scoped_refptr<base::RefCountedMemory> BitmapToMemory(const SkBitmap* image) {
45 base::RefCountedBytes* image_bytes = new base::RefCountedBytes;
46 gfx::PNGCodec::EncodeBGRASkBitmap(*image, false, &image_bytes->data());
47 return image_bytes;
50 SkBitmap DesaturateImage(const SkBitmap* image) {
51 color_utils::HSL shift = {-1, 0, 0.6};
52 return SkBitmapOperations::CreateHSLShiftedBitmap(*image, shift);
55 SkBitmap* ToBitmap(const unsigned char* data, size_t size) {
56 SkBitmap* decoded = new SkBitmap();
57 bool success = gfx::PNGCodec::Decode(data, size, decoded);
58 DCHECK(success);
59 return decoded;
62 } // namespace
64 ExtensionIconSource::ExtensionIconSource(Profile* profile) : profile_(profile) {
67 struct ExtensionIconSource::ExtensionIconRequest {
68 content::URLDataSource::GotDataCallback callback;
69 scoped_refptr<const Extension> extension;
70 bool grayscale;
71 int size;
72 ExtensionIconSet::MatchType match;
75 // static
76 GURL ExtensionIconSource::GetIconURL(const Extension* extension,
77 int icon_size,
78 ExtensionIconSet::MatchType match,
79 bool grayscale,
80 bool* exists) {
81 if (exists) {
82 *exists =
83 IconsInfo::GetIconURL(extension, icon_size, match) != GURL::EmptyGURL();
86 GURL icon_url(base::StringPrintf("%s%s/%d/%d%s",
87 chrome::kChromeUIExtensionIconURL,
88 extension->id().c_str(),
89 icon_size,
90 match,
91 grayscale ? "?grayscale=true" : ""));
92 CHECK(icon_url.is_valid());
93 return icon_url;
96 // static
97 SkBitmap* ExtensionIconSource::LoadImageByResourceId(int resource_id) {
98 std::string contents = ResourceBundle::GetSharedInstance()
99 .GetRawDataResourceForScale(resource_id,
100 ui::SCALE_FACTOR_100P).as_string();
102 // Convert and return it.
103 const unsigned char* data =
104 reinterpret_cast<const unsigned char*>(contents.data());
105 return ToBitmap(data, contents.length());
108 std::string ExtensionIconSource::GetSource() const {
109 return chrome::kChromeUIExtensionIconHost;
112 std::string ExtensionIconSource::GetMimeType(const std::string&) const {
113 // We need to explicitly return a mime type, otherwise if the user tries to
114 // drag the image they get no extension.
115 return "image/png";
118 void ExtensionIconSource::StartDataRequest(
119 const std::string& path,
120 int render_process_id,
121 int render_frame_id,
122 const content::URLDataSource::GotDataCallback& callback) {
123 // This is where everything gets started. First, parse the request and make
124 // the request data available for later.
125 static int next_id = 0;
126 if (!ParseData(path, ++next_id, callback)) {
127 // If the request data cannot be parsed, request parameters will not be
128 // added to |request_map_|.
129 // Send back the default application icon (not resized or desaturated) as
130 // the default response.
131 callback.Run(BitmapToMemory(GetDefaultAppImage()).get());
132 return;
135 ExtensionIconRequest* request = GetData(next_id);
136 ExtensionResource icon = IconsInfo::GetIconResource(
137 request->extension.get(), request->size, request->match);
139 if (icon.relative_path().empty()) {
140 LoadIconFailed(next_id);
141 } else {
142 LoadExtensionImage(icon, next_id);
146 ExtensionIconSource::~ExtensionIconSource() {
147 // Clean up all the temporary data we're holding for requests.
148 STLDeleteValues(&request_map_);
151 const SkBitmap* ExtensionIconSource::GetDefaultAppImage() {
152 if (!default_app_data_.get())
153 default_app_data_.reset(LoadImageByResourceId(IDR_APP_DEFAULT_ICON));
155 return default_app_data_.get();
158 const SkBitmap* ExtensionIconSource::GetDefaultExtensionImage() {
159 if (!default_extension_data_.get()) {
160 default_extension_data_.reset(
161 LoadImageByResourceId(IDR_EXTENSION_DEFAULT_ICON));
164 return default_extension_data_.get();
167 void ExtensionIconSource::FinalizeImage(const SkBitmap* image,
168 int request_id) {
169 SkBitmap bitmap;
170 ExtensionIconRequest* request = GetData(request_id);
171 if (request->grayscale)
172 bitmap = DesaturateImage(image);
173 else
174 bitmap = *image;
176 request->callback.Run(BitmapToMemory(&bitmap).get());
177 ClearData(request_id);
180 void ExtensionIconSource::LoadDefaultImage(int request_id) {
181 ExtensionIconRequest* request = GetData(request_id);
182 const SkBitmap* default_image = NULL;
184 if (request->extension->is_app())
185 default_image = GetDefaultAppImage();
186 else
187 default_image = GetDefaultExtensionImage();
189 SkBitmap resized_image(skia::ImageOperations::Resize(
190 *default_image, skia::ImageOperations::RESIZE_LANCZOS3,
191 request->size, request->size));
193 // There are cases where Resize returns an empty bitmap, for example if you
194 // ask for an image too large. In this case it is better to return the default
195 // image than returning nothing at all.
196 if (resized_image.empty())
197 resized_image = *default_image;
199 FinalizeImage(&resized_image, request_id);
202 void ExtensionIconSource::LoadExtensionImage(const ExtensionResource& icon,
203 int request_id) {
204 ExtensionIconRequest* request = GetData(request_id);
205 ImageLoader::Get(profile_)->LoadImageAsync(
206 request->extension.get(),
207 icon,
208 gfx::Size(request->size, request->size),
209 base::Bind(&ExtensionIconSource::OnImageLoaded, AsWeakPtr(), request_id));
212 void ExtensionIconSource::LoadFaviconImage(int request_id) {
213 favicon::FaviconService* favicon_service =
214 FaviconServiceFactory::GetForProfile(profile_,
215 ServiceAccessType::EXPLICIT_ACCESS);
216 // Fall back to the default icons if the service isn't available.
217 if (favicon_service == NULL) {
218 LoadDefaultImage(request_id);
219 return;
222 GURL favicon_url =
223 AppLaunchInfo::GetFullLaunchURL(GetData(request_id)->extension.get());
224 favicon_service->GetRawFaviconForPageURL(
225 favicon_url,
226 favicon_base::FAVICON,
227 gfx::kFaviconSize,
228 base::Bind(&ExtensionIconSource::OnFaviconDataAvailable,
229 base::Unretained(this),
230 request_id),
231 &cancelable_task_tracker_);
234 void ExtensionIconSource::OnFaviconDataAvailable(
235 int request_id,
236 const favicon_base::FaviconRawBitmapResult& bitmap_result) {
237 ExtensionIconRequest* request = GetData(request_id);
239 // Fallback to the default icon if there wasn't a favicon.
240 if (!bitmap_result.is_valid()) {
241 LoadDefaultImage(request_id);
242 return;
245 if (!request->grayscale) {
246 // If we don't need a grayscale image, then we can bypass FinalizeImage
247 // to avoid unnecessary conversions.
248 request->callback.Run(bitmap_result.bitmap_data.get());
249 ClearData(request_id);
250 } else {
251 FinalizeImage(ToBitmap(bitmap_result.bitmap_data->front(),
252 bitmap_result.bitmap_data->size()), request_id);
256 void ExtensionIconSource::OnImageLoaded(int request_id,
257 const gfx::Image& image) {
258 if (image.IsEmpty())
259 LoadIconFailed(request_id);
260 else
261 FinalizeImage(image.ToSkBitmap(), request_id);
264 void ExtensionIconSource::LoadIconFailed(int request_id) {
265 ExtensionIconRequest* request = GetData(request_id);
266 ExtensionResource icon = IconsInfo::GetIconResource(
267 request->extension.get(), request->size, request->match);
269 if (request->size == extension_misc::EXTENSION_ICON_BITTY)
270 LoadFaviconImage(request_id);
271 else
272 LoadDefaultImage(request_id);
275 bool ExtensionIconSource::ParseData(
276 const std::string& path,
277 int request_id,
278 const content::URLDataSource::GotDataCallback& callback) {
279 // Extract the parameters from the path by lower casing and splitting.
280 std::string path_lower = base::StringToLowerASCII(path);
281 std::vector<std::string> path_parts;
283 base::SplitString(path_lower, '/', &path_parts);
284 if (path_lower.empty() || path_parts.size() < 3)
285 return false;
287 std::string size_param = path_parts.at(1);
288 std::string match_param = path_parts.at(2);
289 match_param = match_param.substr(0, match_param.find('?'));
291 int size;
292 if (!base::StringToInt(size_param, &size))
293 return false;
294 if (size <= 0 || size > extension_misc::EXTENSION_ICON_GIGANTOR)
295 return false;
297 ExtensionIconSet::MatchType match_type;
298 int match_num;
299 if (!base::StringToInt(match_param, &match_num))
300 return false;
301 match_type = static_cast<ExtensionIconSet::MatchType>(match_num);
302 if (!(match_type == ExtensionIconSet::MATCH_EXACTLY ||
303 match_type == ExtensionIconSet::MATCH_SMALLER ||
304 match_type == ExtensionIconSet::MATCH_BIGGER))
305 match_type = ExtensionIconSet::MATCH_EXACTLY;
307 std::string extension_id = path_parts.at(0);
308 const Extension* extension = ExtensionSystem::Get(profile_)->
309 extension_service()->GetInstalledExtension(extension_id);
310 if (!extension)
311 return false;
313 bool grayscale = path_lower.find("grayscale=true") != std::string::npos;
315 SetData(request_id, callback, extension, grayscale, size, match_type);
317 return true;
320 void ExtensionIconSource::SetData(
321 int request_id,
322 const content::URLDataSource::GotDataCallback& callback,
323 const Extension* extension,
324 bool grayscale,
325 int size,
326 ExtensionIconSet::MatchType match) {
327 ExtensionIconRequest* request = new ExtensionIconRequest();
328 request->callback = callback;
329 request->extension = extension;
330 request->grayscale = grayscale;
331 request->size = size;
332 request->match = match;
333 request_map_[request_id] = request;
336 ExtensionIconSource::ExtensionIconRequest* ExtensionIconSource::GetData(
337 int request_id) {
338 return request_map_[request_id];
341 void ExtensionIconSource::ClearData(int request_id) {
342 std::map<int, ExtensionIconRequest*>::iterator i =
343 request_map_.find(request_id);
344 if (i == request_map_.end())
345 return;
347 delete i->second;
348 request_map_.erase(i);
351 } // namespace extensions