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

Side by Side Diff: chrome/browser/extensions/bookmark_app_helper.cc

Issue 782693002: Ensure there are always nice icons for bookmark apps. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Mac compile Created 6 years 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 unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/extensions/bookmark_app_helper.h" 5 #include "chrome/browser/extensions/bookmark_app_helper.h"
6 6
7 #include <cctype> 7 #include <cctype>
8 8
9 #include "base/strings/utf_string_conversions.h" 9 #include "base/strings/utf_string_conversions.h"
10 #include "chrome/browser/extensions/crx_installer.h" 10 #include "chrome/browser/extensions/crx_installer.h"
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after
105 ++it) { 105 ++it) {
106 WebApplicationInfo::IconInfo icon_info; 106 WebApplicationInfo::IconInfo icon_info;
107 icon_info.data = *it->ToSkBitmap(); 107 icon_info.data = *it->ToSkBitmap();
108 icon_info.width = icon_info.data.width(); 108 icon_info.width = icon_info.data.width();
109 icon_info.height = icon_info.data.height(); 109 icon_info.height = icon_info.data.height();
110 web_app_info.icons.push_back(icon_info); 110 web_app_info.icons.push_back(icon_info);
111 } 111 }
112 callback.Run(web_app_info); 112 callback.Run(web_app_info);
113 } 113 }
114 114
115 std::set<int> SizesToGenerate() {
116 // Generate container icons from smaller icons.
117 const int kIconSizesToGenerate[] = {
118 extension_misc::EXTENSION_ICON_SMALL,
119 extension_misc::EXTENSION_ICON_MEDIUM,
120 };
121 return std::set<int>(kIconSizesToGenerate,
122 kIconSizesToGenerate + arraysize(kIconSizesToGenerate));
123 }
124
125 void GenerateIcons(std::set<int> generate_sizes,
126 const GURL& app_url,
127 SkColor generated_icon_color,
128 std::map<int, SkBitmap>* bitmap_map) {
129 // The letter that will be painted on the generated icon.
130 char icon_letter = ' ';
131 std::string domain_and_registry(
132 net::registry_controlled_domains::GetDomainAndRegistry(
133 app_url,
134 net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES));
135 if (!domain_and_registry.empty()) {
136 icon_letter = domain_and_registry[0];
137 } else if (!app_url.host().empty()) {
138 icon_letter = app_url.host()[0];
139 }
140
141 // If no color has been specified, use a dark gray so it will stand out on the
142 // black shelf.
143 if (generated_icon_color == SK_ColorTRANSPARENT)
144 generated_icon_color = SK_ColorDKGRAY;
145
146 for (std::set<int>::const_iterator it = generate_sizes.begin();
147 it != generate_sizes.end(); ++it) {
148 extensions::BookmarkAppHelper::GenerateIcon(
149 bitmap_map, *it, generated_icon_color, icon_letter);
150 // Also generate the 2x resource for this size.
151 extensions::BookmarkAppHelper::GenerateIcon(
152 bitmap_map, *it * 2, generated_icon_color, icon_letter);
153 }
154 }
155
156 void ReplaceWebAppIcons(std::map<int, SkBitmap> bitmap_map,
157 WebApplicationInfo* web_app_info) {
158 web_app_info->icons.clear();
159
160 // Populate the icon data into the WebApplicationInfo we are using to
161 // install the bookmark app.
162 for (std::map<int, SkBitmap>::const_iterator bitmap_map_it =
163 bitmap_map.begin();
164 bitmap_map_it != bitmap_map.end(); ++bitmap_map_it) {
165 WebApplicationInfo::IconInfo icon_info;
166 icon_info.data = bitmap_map_it->second;
167 icon_info.width = icon_info.data.width();
168 icon_info.height = icon_info.data.height();
169 web_app_info->icons.push_back(icon_info);
170 }
171 }
172
115 } // namespace 173 } // namespace
116 174
117 namespace extensions { 175 namespace extensions {
118 176
119 // static 177 // static
120 void BookmarkAppHelper::UpdateWebAppInfoFromManifest( 178 void BookmarkAppHelper::UpdateWebAppInfoFromManifest(
121 const content::Manifest& manifest, 179 const content::Manifest& manifest,
122 WebApplicationInfo* web_app_info) { 180 WebApplicationInfo* web_app_info) {
123 if (!manifest.short_name.is_null()) 181 if (!manifest.short_name.is_null())
124 web_app_info->title = manifest.short_name.string(); 182 web_app_info->title = manifest.short_name.string();
(...skipping 13 matching lines...) Expand all
138 for (const auto& icon : manifest.icons) { 196 for (const auto& icon : manifest.icons) {
139 // TODO(benwells): Take the declared icon density and sizes into account. 197 // TODO(benwells): Take the declared icon density and sizes into account.
140 WebApplicationInfo::IconInfo info; 198 WebApplicationInfo::IconInfo info;
141 info.url = icon.src; 199 info.url = icon.src;
142 web_app_info->icons.push_back(info); 200 web_app_info->icons.push_back(info);
143 } 201 }
144 } 202 }
145 } 203 }
146 204
147 // static 205 // static
148 std::map<int, SkBitmap> BookmarkAppHelper::ConstrainBitmapsToSizes(
149 const std::vector<SkBitmap>& bitmaps,
150 const std::set<int>& sizes) {
151 std::map<int, SkBitmap> output_bitmaps;
152 std::map<int, SkBitmap> ordered_bitmaps;
153 for (std::vector<SkBitmap>::const_iterator it = bitmaps.begin();
154 it != bitmaps.end();
155 ++it) {
156 DCHECK(it->width() == it->height());
157 ordered_bitmaps[it->width()] = *it;
158 }
159
160 std::set<int>::const_iterator sizes_it = sizes.begin();
161 std::map<int, SkBitmap>::const_iterator bitmaps_it = ordered_bitmaps.begin();
162 while (sizes_it != sizes.end() && bitmaps_it != ordered_bitmaps.end()) {
163 int size = *sizes_it;
164 // Find the closest not-smaller bitmap.
165 bitmaps_it = ordered_bitmaps.lower_bound(size);
166 ++sizes_it;
167 // Ensure the bitmap is valid and smaller than the next allowed size.
168 if (bitmaps_it != ordered_bitmaps.end() &&
169 (sizes_it == sizes.end() || bitmaps_it->second.width() < *sizes_it)) {
170 // Resize the bitmap if it does not exactly match the desired size.
171 output_bitmaps[size] = bitmaps_it->second.width() == size
172 ? bitmaps_it->second
173 : skia::ImageOperations::Resize(
174 bitmaps_it->second,
175 skia::ImageOperations::RESIZE_LANCZOS3,
176 size,
177 size);
178 }
179 }
180 return output_bitmaps;
181 }
182
183 // static
184 void BookmarkAppHelper::GenerateIcon(std::map<int, SkBitmap>* bitmaps, 206 void BookmarkAppHelper::GenerateIcon(std::map<int, SkBitmap>* bitmaps,
185 int output_size, 207 int output_size,
186 SkColor color, 208 SkColor color,
187 char letter) { 209 char letter) {
188 // Do nothing if there is already an icon of |output_size|. 210 // Do nothing if there is already an icon of |output_size|.
189 if (bitmaps->count(output_size)) 211 if (bitmaps->count(output_size))
190 return; 212 return;
191 213
192 gfx::ImageSkia icon_image( 214 gfx::ImageSkia icon_image(
193 new GeneratedIconImageSource(letter, color, output_size), 215 new GeneratedIconImageSource(letter, color, output_size),
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
253 bool success, 275 bool success,
254 const std::map<GURL, std::vector<SkBitmap> >& bitmaps) { 276 const std::map<GURL, std::vector<SkBitmap> >& bitmaps) {
255 // The tab has navigated away during the icon download. Cancel the bookmark 277 // The tab has navigated away during the icon download. Cancel the bookmark
256 // app creation. 278 // app creation.
257 if (!success) { 279 if (!success) {
258 favicon_downloader_.reset(); 280 favicon_downloader_.reset();
259 callback_.Run(NULL, web_app_info_); 281 callback_.Run(NULL, web_app_info_);
260 return; 282 return;
261 } 283 }
262 284
263 // Add the downloaded icons. Extensions only allow certain icon sizes. First
264 // populate icons that match the allowed sizes exactly and then downscale
265 // remaining icons to the closest allowed size that doesn't yet have an icon.
266 std::set<int> allowed_sizes(extension_misc::kExtensionIconSizes,
267 extension_misc::kExtensionIconSizes +
268 extension_misc::kNumExtensionIconSizes);
269 std::vector<SkBitmap> downloaded_icons; 285 std::vector<SkBitmap> downloaded_icons;
270 for (FaviconDownloader::FaviconMap::const_iterator map_it = bitmaps.begin(); 286 for (FaviconDownloader::FaviconMap::const_iterator map_it = bitmaps.begin();
271 map_it != bitmaps.end(); 287 map_it != bitmaps.end();
272 ++map_it) { 288 ++map_it) {
273 for (std::vector<SkBitmap>::const_iterator bitmap_it = 289 for (std::vector<SkBitmap>::const_iterator bitmap_it =
274 map_it->second.begin(); 290 map_it->second.begin();
275 bitmap_it != map_it->second.end(); 291 bitmap_it != map_it->second.end();
276 ++bitmap_it) { 292 ++bitmap_it) {
277 if (bitmap_it->empty() || bitmap_it->width() != bitmap_it->height()) 293 if (bitmap_it->empty() || bitmap_it->width() != bitmap_it->height())
278 continue; 294 continue;
279 295
280 downloaded_icons.push_back(*bitmap_it); 296 downloaded_icons.push_back(*bitmap_it);
281 } 297 }
282 } 298 }
283 299
284 // Add all existing icons from WebApplicationInfo. 300 // Add all existing icons from WebApplicationInfo.
285 for (std::vector<WebApplicationInfo::IconInfo>::const_iterator it = 301 for (std::vector<WebApplicationInfo::IconInfo>::const_iterator it =
286 web_app_info_.icons.begin(); 302 web_app_info_.icons.begin();
287 it != web_app_info_.icons.end(); 303 it != web_app_info_.icons.end();
288 ++it) { 304 ++it) {
289 const SkBitmap& icon = it->data; 305 const SkBitmap& icon = it->data;
290 if (!icon.drawsNothing() && icon.width() == icon.height()) 306 if (!icon.drawsNothing() && icon.width() == icon.height())
291 downloaded_icons.push_back(icon); 307 downloaded_icons.push_back(icon);
292 } 308 }
293 309
294 web_app_info_.icons.clear(); 310 // Add the downloaded icons. Extensions only allow certain icon sizes. First
311 // populate icons that match the allowed sizes exactly and then downscale
312 // remaining icons to the closest allowed size that doesn't yet have an icon.
313 std::set<int> allowed_sizes(extension_misc::kExtensionIconSizes,
314 extension_misc::kExtensionIconSizes +
315 extension_misc::kNumExtensionIconSizes);
295 316
296 // If there are icons that don't match the accepted icon sizes, find the 317 web_app_info_.generated_icon_color = SK_ColorTRANSPARENT;
297 // closest bigger icon to the accepted sizes and resize the icon to it. An 318 // Determine the color that will be used for the icon's background. For this
298 // icon will be resized and used for at most one size. 319 // the dominant color of the first icon found is used.
299 std::map<int, SkBitmap> resized_bitmaps( 320 if (downloaded_icons.size()) {
300 ConstrainBitmapsToSizes(downloaded_icons, allowed_sizes)); 321 color_utils::GridSampler sampler;
301 322 web_app_info_.generated_icon_color =
302 // Generate container icons from smaller icons. 323 color_utils::CalculateKMeanColorOfBitmap(downloaded_icons[0]);
303 const int kIconSizesToGenerate[] = {extension_misc::EXTENSION_ICON_SMALL,
304 extension_misc::EXTENSION_ICON_MEDIUM, };
305 const std::set<int> generate_sizes(
306 kIconSizesToGenerate,
307 kIconSizesToGenerate + arraysize(kIconSizesToGenerate));
308
309 // Only generate icons if larger icons don't exist. This means the app
310 // launcher and the taskbar will do their best downsizing large icons and
311 // these icons are only generated as a last resort against upscaling a smaller
312 // icon.
313 if (resized_bitmaps.lower_bound(*generate_sizes.rbegin()) ==
314 resized_bitmaps.end()) {
315 GURL app_url = web_app_info_.app_url;
316
317 // The letter that will be painted on the generated icon.
318 char icon_letter = ' ';
319 std::string domain_and_registry(
320 net::registry_controlled_domains::GetDomainAndRegistry(
321 app_url,
322 net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES));
323 if (!domain_and_registry.empty()) {
324 icon_letter = domain_and_registry[0];
325 } else if (!app_url.host().empty()) {
326 icon_letter = app_url.host()[0];
327 }
328
329 // The color that will be used for the icon's background.
330 SkColor background_color = SK_ColorBLACK;
331 if (resized_bitmaps.size()) {
332 color_utils::GridSampler sampler;
333 background_color = color_utils::CalculateKMeanColorOfBitmap(
334 resized_bitmaps.begin()->second);
335 }
336
337 for (std::set<int>::const_iterator it = generate_sizes.begin();
338 it != generate_sizes.end();
339 ++it) {
340 GenerateIcon(&resized_bitmaps, *it, background_color, icon_letter);
341 // Also generate the 2x resource for this size.
342 GenerateIcon(&resized_bitmaps, *it * 2, background_color, icon_letter);
343 }
344 } 324 }
345 325
346 // Populate the icon data into the WebApplicationInfo we are using to 326 std::set<int> generate_sizes = SizesToGenerate();
347 // install the bookmark app. 327
348 for (std::map<int, SkBitmap>::const_iterator resized_bitmaps_it = 328 std::map<int, SkBitmap> generated_icons;
349 resized_bitmaps.begin(); 329 // Icons are always generated, replacing the icons that were downloaded. This
350 resized_bitmaps_it != resized_bitmaps.end(); 330 // is done so that the icons are consistent across machines.
351 ++resized_bitmaps_it) { 331 // TODO(benwells): Use blob sync once it is available to sync the downloaded
352 WebApplicationInfo::IconInfo icon_info; 332 // icons, and then only generate when there are required sizes missing.
353 icon_info.data = resized_bitmaps_it->second; 333 GenerateIcons(generate_sizes, web_app_info_.app_url,
354 icon_info.width = icon_info.data.width(); 334 web_app_info_.generated_icon_color, &generated_icons);
355 icon_info.height = icon_info.data.height(); 335
356 web_app_info_.icons.push_back(icon_info); 336 ReplaceWebAppIcons(generated_icons, &web_app_info_);
357 }
358 337
359 // Install the app. 338 // Install the app.
360 crx_installer_->InstallWebApp(web_app_info_); 339 crx_installer_->InstallWebApp(web_app_info_);
361 favicon_downloader_.reset(); 340 favicon_downloader_.reset();
362 } 341 }
363 342
364 void BookmarkAppHelper::Observe(int type, 343 void BookmarkAppHelper::Observe(int type,
365 const content::NotificationSource& source, 344 const content::NotificationSource& source,
366 const content::NotificationDetails& details) { 345 const content::NotificationDetails& details) {
367 switch (type) { 346 switch (type) {
368 case extensions::NOTIFICATION_CRX_INSTALLER_DONE: { 347 case extensions::NOTIFICATION_CRX_INSTALLER_DONE: {
369 const Extension* extension = 348 const Extension* extension =
370 content::Details<const Extension>(details).ptr(); 349 content::Details<const Extension>(details).ptr();
371 DCHECK(extension); 350 DCHECK(extension);
372 DCHECK_EQ(AppLaunchInfo::GetLaunchWebURL(extension), 351 DCHECK_EQ(AppLaunchInfo::GetLaunchWebURL(extension),
373 web_app_info_.app_url); 352 web_app_info_.app_url);
374 callback_.Run(extension, web_app_info_); 353 callback_.Run(extension, web_app_info_);
375 break; 354 break;
376 } 355 }
377 case extensions::NOTIFICATION_EXTENSION_INSTALL_ERROR: 356 case extensions::NOTIFICATION_EXTENSION_INSTALL_ERROR:
378 callback_.Run(NULL, web_app_info_); 357 callback_.Run(NULL, web_app_info_);
379 break; 358 break;
380 default: 359 default:
381 NOTREACHED(); 360 NOTREACHED();
382 break; 361 break;
383 } 362 }
384 } 363 }
385 364
386 void CreateOrUpdateBookmarkApp(ExtensionService* service, 365 void CreateOrUpdateBookmarkApp(ExtensionService* service,
387 WebApplicationInfo& web_app_info) { 366 WebApplicationInfo* web_app_info) {
388 scoped_refptr<extensions::CrxInstaller> installer( 367 scoped_refptr<extensions::CrxInstaller> installer(
389 extensions::CrxInstaller::CreateSilent(service)); 368 extensions::CrxInstaller::CreateSilent(service));
390 installer->set_error_on_unsupported_requirements(true); 369 installer->set_error_on_unsupported_requirements(true);
391 installer->InstallWebApp(web_app_info); 370 if (web_app_info->icons.empty()) {
371 std::map<int, SkBitmap> bitmap_map;
372 GenerateIcons(SizesToGenerate(), web_app_info->app_url,
373 web_app_info->generated_icon_color, &bitmap_map);
374 ReplaceWebAppIcons(bitmap_map, web_app_info);
375 }
376
377 installer->InstallWebApp(*web_app_info);
392 } 378 }
393 379
394 void GetWebApplicationInfoFromApp( 380 void GetWebApplicationInfoFromApp(
395 content::BrowserContext* browser_context, 381 content::BrowserContext* browser_context,
396 const extensions::Extension* extension, 382 const extensions::Extension* extension,
397 const base::Callback<void(const WebApplicationInfo&)> callback) { 383 const base::Callback<void(const WebApplicationInfo&)> callback) {
398 if (!extension->from_bookmark()) { 384 if (!extension->from_bookmark()) {
399 callback.Run(WebApplicationInfo()); 385 callback.Run(WebApplicationInfo());
400 return; 386 return;
401 } 387 }
(...skipping 22 matching lines...) Expand all
424 extension, info_list, base::Bind(&OnIconsLoaded, web_app_info, callback)); 410 extension, info_list, base::Bind(&OnIconsLoaded, web_app_info, callback));
425 } 411 }
426 412
427 bool IsValidBookmarkAppUrl(const GURL& url) { 413 bool IsValidBookmarkAppUrl(const GURL& url) {
428 URLPattern origin_only_pattern(Extension::kValidWebExtentSchemes); 414 URLPattern origin_only_pattern(Extension::kValidWebExtentSchemes);
429 origin_only_pattern.SetMatchAllURLs(true); 415 origin_only_pattern.SetMatchAllURLs(true);
430 return url.is_valid() && origin_only_pattern.MatchesURL(url); 416 return url.is_valid() && origin_only_pattern.MatchesURL(url);
431 } 417 }
432 418
433 } // namespace extensions 419 } // namespace extensions
OLDNEW
« no previous file with comments | « chrome/browser/extensions/bookmark_app_helper.h ('k') | chrome/browser/extensions/bookmark_app_helper_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698