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

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: Self review; test 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 128 matching lines...) Expand 10 before | Expand all | Expand 10 after
253 bool success, 311 bool success,
254 const std::map<GURL, std::vector<SkBitmap> >& bitmaps) { 312 const std::map<GURL, std::vector<SkBitmap> >& bitmaps) {
255 // The tab has navigated away during the icon download. Cancel the bookmark 313 // The tab has navigated away during the icon download. Cancel the bookmark
256 // app creation. 314 // app creation.
257 if (!success) { 315 if (!success) {
258 favicon_downloader_.reset(); 316 favicon_downloader_.reset();
259 callback_.Run(NULL, web_app_info_); 317 callback_.Run(NULL, web_app_info_);
260 return; 318 return;
261 } 319 }
262 320
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; 321 std::vector<SkBitmap> downloaded_icons;
270 for (FaviconDownloader::FaviconMap::const_iterator map_it = bitmaps.begin(); 322 for (FaviconDownloader::FaviconMap::const_iterator map_it = bitmaps.begin();
271 map_it != bitmaps.end(); 323 map_it != bitmaps.end();
272 ++map_it) { 324 ++map_it) {
273 for (std::vector<SkBitmap>::const_iterator bitmap_it = 325 for (std::vector<SkBitmap>::const_iterator bitmap_it =
274 map_it->second.begin(); 326 map_it->second.begin();
275 bitmap_it != map_it->second.end(); 327 bitmap_it != map_it->second.end();
276 ++bitmap_it) { 328 ++bitmap_it) {
277 if (bitmap_it->empty() || bitmap_it->width() != bitmap_it->height()) 329 if (bitmap_it->empty() || bitmap_it->width() != bitmap_it->height())
278 continue; 330 continue;
279 331
280 downloaded_icons.push_back(*bitmap_it); 332 downloaded_icons.push_back(*bitmap_it);
281 } 333 }
282 } 334 }
283 335
284 // Add all existing icons from WebApplicationInfo. 336 // Add all existing icons from WebApplicationInfo.
285 for (std::vector<WebApplicationInfo::IconInfo>::const_iterator it = 337 for (std::vector<WebApplicationInfo::IconInfo>::const_iterator it =
286 web_app_info_.icons.begin(); 338 web_app_info_.icons.begin();
287 it != web_app_info_.icons.end(); 339 it != web_app_info_.icons.end();
288 ++it) { 340 ++it) {
289 const SkBitmap& icon = it->data; 341 const SkBitmap& icon = it->data;
290 if (!icon.drawsNothing() && icon.width() == icon.height()) 342 if (!icon.drawsNothing() && icon.width() == icon.height())
291 downloaded_icons.push_back(icon); 343 downloaded_icons.push_back(icon);
292 } 344 }
293 345
294 web_app_info_.icons.clear(); 346 // Add the downloaded icons. Extensions only allow certain icon sizes. First
347 // populate icons that match the allowed sizes exactly and then downscale
348 // remaining icons to the closest allowed size that doesn't yet have an icon.
349 std::set<int> allowed_sizes(extension_misc::kExtensionIconSizes,
350 extension_misc::kExtensionIconSizes +
351 extension_misc::kNumExtensionIconSizes);
295 352
296 // If there are icons that don't match the accepted icon sizes, find the 353 // If there are icons that don't match the accepted icon sizes, find the
297 // closest bigger icon to the accepted sizes and resize the icon to it. An 354 // closest bigger icon to the accepted sizes and resize the icon to it. An
298 // icon will be resized and used for at most one size. 355 // icon will be resized and used for at most one size.
299 std::map<int, SkBitmap> resized_bitmaps( 356 std::map<int, SkBitmap> resized_bitmaps(
300 ConstrainBitmapsToSizes(downloaded_icons, allowed_sizes)); 357 extensions::BookmarkAppHelper::ConstrainBitmapsToSizes(downloaded_icons,
358 allowed_sizes));
calamity 2014/12/09 03:46:11 Hmm. Technically, this is now unnecessary work. Yo
benwells 2014/12/09 08:28:35 OK. I'm just going to use the first one we find, I
301 359
302 // Generate container icons from smaller icons. 360 web_app_info_.generated_icon_color = SK_ColorTRANSPARENT;
303 const int kIconSizesToGenerate[] = {extension_misc::EXTENSION_ICON_SMALL, 361 // Determine the color that will be used for the icon's background/
calamity 2014/12/09 03:46:11 s/d\//./
benwells 2014/12/09 08:28:35 Done.
304 extension_misc::EXTENSION_ICON_MEDIUM, }; 362 if (resized_bitmaps.size()) {
305 const std::set<int> generate_sizes( 363 color_utils::GridSampler sampler;
306 kIconSizesToGenerate, 364 web_app_info_.generated_icon_color =
307 kIconSizesToGenerate + arraysize(kIconSizesToGenerate)); 365 color_utils::CalculateKMeanColorOfBitmap(
308 366 resized_bitmaps.begin()->second);
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 } 367 }
345 368
346 // Populate the icon data into the WebApplicationInfo we are using to 369 std::set<int> generate_sizes = SizesToGenerate();
347 // install the bookmark app. 370
348 for (std::map<int, SkBitmap>::const_iterator resized_bitmaps_it = 371 // Icons are always generated, replacing the icons that were downloaded. This
349 resized_bitmaps.begin(); 372 // is done so that the icons are consistent across machines.
350 resized_bitmaps_it != resized_bitmaps.end(); 373 // TODO(benwells): Use blob sync once it is available to sync the downloaded
351 ++resized_bitmaps_it) { 374 // icons, and then only generate when there are required sizes missing.
352 WebApplicationInfo::IconInfo icon_info; 375 resized_bitmaps.clear();
353 icon_info.data = resized_bitmaps_it->second; 376 GenerateIcons(generate_sizes, web_app_info_.app_url,
354 icon_info.width = icon_info.data.width(); 377 web_app_info_.generated_icon_color, &resized_bitmaps);
355 icon_info.height = icon_info.data.height(); 378
356 web_app_info_.icons.push_back(icon_info); 379 ReplaceWebAppIcons(resized_bitmaps, &web_app_info_);
357 }
358 380
359 // Install the app. 381 // Install the app.
360 crx_installer_->InstallWebApp(web_app_info_); 382 crx_installer_->InstallWebApp(web_app_info_);
361 favicon_downloader_.reset(); 383 favicon_downloader_.reset();
362 } 384 }
363 385
364 void BookmarkAppHelper::Observe(int type, 386 void BookmarkAppHelper::Observe(int type,
365 const content::NotificationSource& source, 387 const content::NotificationSource& source,
366 const content::NotificationDetails& details) { 388 const content::NotificationDetails& details) {
367 switch (type) { 389 switch (type) {
368 case extensions::NOTIFICATION_CRX_INSTALLER_DONE: { 390 case extensions::NOTIFICATION_CRX_INSTALLER_DONE: {
369 const Extension* extension = 391 const Extension* extension =
370 content::Details<const Extension>(details).ptr(); 392 content::Details<const Extension>(details).ptr();
371 DCHECK(extension); 393 DCHECK(extension);
372 DCHECK_EQ(AppLaunchInfo::GetLaunchWebURL(extension), 394 DCHECK_EQ(AppLaunchInfo::GetLaunchWebURL(extension),
373 web_app_info_.app_url); 395 web_app_info_.app_url);
374 callback_.Run(extension, web_app_info_); 396 callback_.Run(extension, web_app_info_);
375 break; 397 break;
376 } 398 }
377 case extensions::NOTIFICATION_EXTENSION_INSTALL_ERROR: 399 case extensions::NOTIFICATION_EXTENSION_INSTALL_ERROR:
378 callback_.Run(NULL, web_app_info_); 400 callback_.Run(NULL, web_app_info_);
379 break; 401 break;
380 default: 402 default:
381 NOTREACHED(); 403 NOTREACHED();
382 break; 404 break;
383 } 405 }
384 } 406 }
385 407
386 void CreateOrUpdateBookmarkApp(ExtensionService* service, 408 void CreateOrUpdateBookmarkApp(ExtensionService* service,
387 WebApplicationInfo& web_app_info) { 409 WebApplicationInfo& web_app_info) {
calamity 2014/12/09 03:46:11 non-const ref? Can this be changed to a pointer?
benwells 2014/12/09 08:28:35 Nice spot, done.
388 scoped_refptr<extensions::CrxInstaller> installer( 410 scoped_refptr<extensions::CrxInstaller> installer(
389 extensions::CrxInstaller::CreateSilent(service)); 411 extensions::CrxInstaller::CreateSilent(service));
390 installer->set_error_on_unsupported_requirements(true); 412 installer->set_error_on_unsupported_requirements(true);
413 if (web_app_info.icons.empty()) {
414 std::map<int, SkBitmap> bitmap_map;
415 GenerateIcons(SizesToGenerate(), web_app_info.app_url,
416 web_app_info.generated_icon_color, &bitmap_map);
417 ReplaceWebAppIcons(bitmap_map, &web_app_info);
418 }
419
391 installer->InstallWebApp(web_app_info); 420 installer->InstallWebApp(web_app_info);
392 } 421 }
393 422
394 void GetWebApplicationInfoFromApp( 423 void GetWebApplicationInfoFromApp(
395 content::BrowserContext* browser_context, 424 content::BrowserContext* browser_context,
396 const extensions::Extension* extension, 425 const extensions::Extension* extension,
397 const base::Callback<void(const WebApplicationInfo&)> callback) { 426 const base::Callback<void(const WebApplicationInfo&)> callback) {
398 if (!extension->from_bookmark()) { 427 if (!extension->from_bookmark()) {
399 callback.Run(WebApplicationInfo()); 428 callback.Run(WebApplicationInfo());
400 return; 429 return;
(...skipping 23 matching lines...) Expand all
424 extension, info_list, base::Bind(&OnIconsLoaded, web_app_info, callback)); 453 extension, info_list, base::Bind(&OnIconsLoaded, web_app_info, callback));
425 } 454 }
426 455
427 bool IsValidBookmarkAppUrl(const GURL& url) { 456 bool IsValidBookmarkAppUrl(const GURL& url) {
428 URLPattern origin_only_pattern(Extension::kValidWebExtentSchemes); 457 URLPattern origin_only_pattern(Extension::kValidWebExtentSchemes);
429 origin_only_pattern.SetMatchAllURLs(true); 458 origin_only_pattern.SetMatchAllURLs(true);
430 return url.is_valid() && origin_only_pattern.MatchesURL(url); 459 return url.is_valid() && origin_only_pattern.MatchesURL(url);
431 } 460 }
432 461
433 } // namespace extensions 462 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698