Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 |
| OLD | NEW |