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
3 // found in the LICENSE file.
5 #include "chrome/browser/extensions/bookmark_app_helper.h"
9 #include "base/prefs/pref_service.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "chrome/browser/bitmap_fetcher/bitmap_fetcher.h"
12 #include "chrome/browser/bitmap_fetcher/bitmap_fetcher_delegate.h"
13 #include "chrome/browser/chrome_notification_types.h"
14 #include "chrome/browser/extensions/crx_installer.h"
15 #include "chrome/browser/extensions/extension_service.h"
16 #include "chrome/browser/extensions/favicon_downloader.h"
17 #include "chrome/browser/extensions/launch_util.h"
18 #include "chrome/browser/extensions/tab_helper.h"
19 #include "chrome/browser/profiles/profile.h"
20 #include "chrome/browser/ui/app_list/app_list_service.h"
21 #include "chrome/browser/ui/app_list/app_list_util.h"
22 #include "chrome/browser/ui/browser_finder.h"
23 #include "chrome/browser/ui/browser_window.h"
24 #include "chrome/browser/ui/host_desktop.h"
25 #include "chrome/browser/web_applications/web_app.h"
26 #include "chrome/common/extensions/extension_constants.h"
27 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
28 #include "chrome/common/url_constants.h"
29 #include "content/public/browser/notification_service.h"
30 #include "content/public/browser/notification_source.h"
31 #include "content/public/browser/web_contents.h"
32 #include "extensions/browser/extension_registry.h"
33 #include "extensions/browser/extension_system.h"
34 #include "extensions/browser/image_loader.h"
35 #include "extensions/browser/notification_types.h"
36 #include "extensions/browser/pref_names.h"
37 #include "extensions/common/constants.h"
38 #include "extensions/common/extension.h"
39 #include "extensions/common/manifest_handlers/icons_handler.h"
40 #include "extensions/common/url_pattern.h"
41 #include "grit/platform_locale_settings.h"
42 #include "net/base/load_flags.h"
43 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
44 #include "net/url_request/url_request.h"
45 #include "skia/ext/image_operations.h"
46 #include "skia/ext/platform_canvas.h"
47 #include "third_party/skia/include/core/SkBitmap.h"
48 #include "ui/base/l10n/l10n_util.h"
49 #include "ui/gfx/canvas.h"
50 #include "ui/gfx/color_analysis.h"
51 #include "ui/gfx/color_utils.h"
52 #include "ui/gfx/font.h"
53 #include "ui/gfx/font_list.h"
54 #include "ui/gfx/geometry/rect.h"
55 #include "ui/gfx/image/canvas_image_source.h"
56 #include "ui/gfx/image/image.h"
57 #include "ui/gfx/image/image_family.h"
59 #if defined(OS_MACOSX)
60 #include "base/command_line.h"
61 #include "chrome/browser/web_applications/web_app_mac.h"
62 #include "chrome/common/chrome_switches.h"
66 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
71 using extensions::BookmarkAppHelper
;
73 // Overlays a shortcut icon over the bottom left corner of a given image.
74 class GeneratedIconImageSource
: public gfx::CanvasImageSource
{
76 explicit GeneratedIconImageSource(char letter
, SkColor color
, int output_size
)
77 : gfx::CanvasImageSource(gfx::Size(output_size
, output_size
), false),
80 output_size_(output_size
) {}
81 ~GeneratedIconImageSource() override
{}
84 // gfx::CanvasImageSource overrides:
85 void Draw(gfx::Canvas
* canvas
) override
{
86 const unsigned char kLuminanceThreshold
= 190;
87 const int icon_size
= output_size_
* 3 / 4;
88 const int icon_inset
= output_size_
/ 8;
89 const size_t border_radius
= output_size_
/ 16;
90 const size_t font_size
= output_size_
* 7 / 16;
92 std::string font_name
=
93 l10n_util::GetStringUTF8(IDS_SANS_SERIF_FONT_FAMILY
);
94 #if defined(OS_CHROMEOS)
95 const std::string kChromeOSFontFamily
= "Noto Sans";
96 font_name
= kChromeOSFontFamily
;
99 // Draw a rounded rect of the given |color|.
100 SkPaint background_paint
;
101 background_paint
.setFlags(SkPaint::kAntiAlias_Flag
);
102 background_paint
.setColor(color_
);
104 gfx::Rect
icon_rect(icon_inset
, icon_inset
, icon_size
, icon_size
);
105 canvas
->DrawRoundRect(icon_rect
, border_radius
, background_paint
);
107 // The text rect's size needs to be odd to center the text correctly.
108 gfx::Rect
text_rect(icon_inset
, icon_inset
, icon_size
+ 1, icon_size
+ 1);
109 // Draw the letter onto the rounded rect. The letter's color depends on the
110 // luminance of |color|.
111 unsigned char luminance
= color_utils::GetLuminanceForColor(color_
);
112 canvas
->DrawStringRectWithFlags(
113 base::string16(1, std::toupper(letter_
)),
114 gfx::FontList(gfx::Font(font_name
, font_size
)),
115 luminance
> kLuminanceThreshold
? SK_ColorBLACK
: SK_ColorWHITE
,
117 gfx::Canvas::TEXT_ALIGN_CENTER
);
126 DISALLOW_COPY_AND_ASSIGN(GeneratedIconImageSource
);
130 WebApplicationInfo web_app_info
,
131 const base::Callback
<void(const WebApplicationInfo
&)> callback
,
132 const gfx::ImageFamily
& image_family
) {
133 for (gfx::ImageFamily::const_iterator it
= image_family
.begin();
134 it
!= image_family
.end();
136 WebApplicationInfo::IconInfo icon_info
;
137 icon_info
.data
= *it
->ToSkBitmap();
138 icon_info
.width
= icon_info
.data
.width();
139 icon_info
.height
= icon_info
.data
.height();
140 web_app_info
.icons
.push_back(icon_info
);
142 callback
.Run(web_app_info
);
145 std::set
<int> SizesToGenerate() {
146 // Generate container icons from smaller icons.
147 const int kIconSizesToGenerate
[] = {
148 extension_misc::EXTENSION_ICON_SMALL
,
149 extension_misc::EXTENSION_ICON_MEDIUM
,
150 extension_misc::EXTENSION_ICON_LARGE
,
152 return std::set
<int>(kIconSizesToGenerate
,
153 kIconSizesToGenerate
+ arraysize(kIconSizesToGenerate
));
157 std::set
<int> generate_sizes
,
159 SkColor generated_icon_color
,
160 std::map
<int, BookmarkAppHelper::BitmapAndSource
>* bitmap_map
) {
161 // The letter that will be painted on the generated icon.
162 char icon_letter
= ' ';
163 std::string
domain_and_registry(
164 net::registry_controlled_domains::GetDomainAndRegistry(
166 net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES
));
167 if (!domain_and_registry
.empty()) {
168 icon_letter
= domain_and_registry
[0];
169 } else if (!app_url
.host().empty()) {
170 icon_letter
= app_url
.host()[0];
173 // If no color has been specified, use a dark gray so it will stand out on the
175 if (generated_icon_color
== SK_ColorTRANSPARENT
)
176 generated_icon_color
= SK_ColorDKGRAY
;
178 for (std::set
<int>::const_iterator it
= generate_sizes
.begin();
179 it
!= generate_sizes
.end(); ++it
) {
180 extensions::BookmarkAppHelper::GenerateIcon(
181 bitmap_map
, *it
, generated_icon_color
, icon_letter
);
182 // Also generate the 2x resource for this size.
183 extensions::BookmarkAppHelper::GenerateIcon(
184 bitmap_map
, *it
* 2, generated_icon_color
, icon_letter
);
188 void ReplaceWebAppIcons(
189 std::map
<int, BookmarkAppHelper::BitmapAndSource
> bitmap_map
,
190 WebApplicationInfo
* web_app_info
) {
191 web_app_info
->icons
.clear();
193 // Populate the icon data into the WebApplicationInfo we are using to
194 // install the bookmark app.
195 for (const auto& pair
: bitmap_map
) {
196 WebApplicationInfo::IconInfo icon_info
;
197 icon_info
.data
= pair
.second
.bitmap
;
198 icon_info
.url
= pair
.second
.source_url
;
199 icon_info
.width
= icon_info
.data
.width();
200 icon_info
.height
= icon_info
.data
.height();
201 web_app_info
->icons
.push_back(icon_info
);
205 // Class to handle installing a bookmark app. Handles downloading and decoding
207 class BookmarkAppInstaller
: public base::RefCounted
<BookmarkAppInstaller
>,
208 public chrome::BitmapFetcherDelegate
{
210 BookmarkAppInstaller(ExtensionService
* service
,
211 const WebApplicationInfo
& web_app_info
)
212 : service_(service
), web_app_info_(web_app_info
) {}
215 for (const auto& icon
: web_app_info_
.icons
) {
216 if (icon
.url
.is_valid())
217 urls_to_download_
.push_back(icon
.url
);
220 if (urls_to_download_
.size()) {
223 // Matched in OnFetchComplete.
228 FinishInstallation();
232 friend class base::RefCounted
<BookmarkAppInstaller
>;
233 ~BookmarkAppInstaller() override
{}
235 // BitmapFetcherDelegate:
236 void OnFetchComplete(const GURL
& url
, const SkBitmap
* bitmap
) override
{
237 if (bitmap
&& !bitmap
->empty() && bitmap
->width() == bitmap
->height()) {
238 downloaded_bitmaps_
.push_back(
239 BookmarkAppHelper::BitmapAndSource(url
, *bitmap
));
242 if (urls_to_download_
.size()) {
247 FinishInstallation();
251 void DownloadNextImage() {
252 DCHECK(urls_to_download_
.size());
254 bitmap_fetcher_
.reset(
255 new chrome::BitmapFetcher(urls_to_download_
.back(), this));
256 urls_to_download_
.pop_back();
257 bitmap_fetcher_
->Init(
258 service_
->profile()->GetRequestContext(), std::string(),
259 net::URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE
,
260 net::LOAD_DO_NOT_SAVE_COOKIES
| net::LOAD_DO_NOT_SEND_COOKIES
);
261 bitmap_fetcher_
->Start();
264 void FinishInstallation() {
265 std::map
<int, BookmarkAppHelper::BitmapAndSource
> size_map
=
266 BookmarkAppHelper::ResizeIconsAndGenerateMissing(downloaded_bitmaps_
,
269 BookmarkAppHelper::UpdateWebAppIconsWithoutChangingLinks(size_map
,
271 scoped_refptr
<extensions::CrxInstaller
> installer(
272 extensions::CrxInstaller::CreateSilent(service_
));
273 installer
->set_error_on_unsupported_requirements(true);
274 installer
->InstallWebApp(web_app_info_
);
277 ExtensionService
* service_
;
278 WebApplicationInfo web_app_info_
;
280 scoped_ptr
<chrome::BitmapFetcher
> bitmap_fetcher_
;
281 std::vector
<GURL
> urls_to_download_
;
282 std::vector
<BookmarkAppHelper::BitmapAndSource
> downloaded_bitmaps_
;
287 namespace extensions
{
290 void BookmarkAppHelper::UpdateWebAppInfoFromManifest(
291 const content::Manifest
& manifest
,
292 WebApplicationInfo
* web_app_info
) {
293 if (!manifest
.short_name
.is_null())
294 web_app_info
->title
= manifest
.short_name
.string();
296 // Give the full length name priority.
297 if (!manifest
.name
.is_null())
298 web_app_info
->title
= manifest
.name
.string();
300 // Set the url based on the manifest value, if any.
301 if (manifest
.start_url
.is_valid())
302 web_app_info
->app_url
= manifest
.start_url
;
304 // If any icons are specified in the manifest, they take precedence over any
305 // we picked up from the web_app stuff.
306 if (!manifest
.icons
.empty()) {
307 web_app_info
->icons
.clear();
308 for (const auto& icon
: manifest
.icons
) {
309 // TODO(benwells): Take the declared icon density and sizes into account.
310 WebApplicationInfo::IconInfo info
;
312 web_app_info
->icons
.push_back(info
);
318 std::map
<int, BookmarkAppHelper::BitmapAndSource
>
319 BookmarkAppHelper::ConstrainBitmapsToSizes(
320 const std::vector
<BookmarkAppHelper::BitmapAndSource
>& bitmaps
,
321 const std::set
<int>& sizes
) {
322 std::map
<int, BitmapAndSource
> output_bitmaps
;
323 std::map
<int, BitmapAndSource
> ordered_bitmaps
;
324 for (std::vector
<BitmapAndSource
>::const_iterator it
= bitmaps
.begin();
325 it
!= bitmaps
.end(); ++it
) {
326 DCHECK(it
->bitmap
.width() == it
->bitmap
.height());
327 ordered_bitmaps
[it
->bitmap
.width()] = *it
;
330 std::set
<int>::const_iterator sizes_it
= sizes
.begin();
331 std::map
<int, BitmapAndSource
>::const_iterator bitmaps_it
=
332 ordered_bitmaps
.begin();
333 while (sizes_it
!= sizes
.end() && bitmaps_it
!= ordered_bitmaps
.end()) {
334 int size
= *sizes_it
;
335 // Find the closest not-smaller bitmap.
336 bitmaps_it
= ordered_bitmaps
.lower_bound(size
);
338 // Ensure the bitmap is valid and smaller than the next allowed size.
339 if (bitmaps_it
!= ordered_bitmaps
.end() &&
340 (sizes_it
== sizes
.end() ||
341 bitmaps_it
->second
.bitmap
.width() < *sizes_it
)) {
342 output_bitmaps
[size
] = bitmaps_it
->second
;
343 // Resize the bitmap if it does not exactly match the desired size.
344 if (output_bitmaps
[size
].bitmap
.width() != size
) {
345 output_bitmaps
[size
].bitmap
= skia::ImageOperations::Resize(
346 output_bitmaps
[size
].bitmap
, skia::ImageOperations::RESIZE_LANCZOS3
,
351 return output_bitmaps
;
355 void BookmarkAppHelper::GenerateIcon(
356 std::map
<int, BookmarkAppHelper::BitmapAndSource
>* bitmaps
,
360 // Do nothing if there is already an icon of |output_size|.
361 if (bitmaps
->count(output_size
))
364 gfx::ImageSkia
icon_image(
365 new GeneratedIconImageSource(letter
, color
, output_size
),
366 gfx::Size(output_size
, output_size
));
367 icon_image
.bitmap()->deepCopyTo(&(*bitmaps
)[output_size
].bitmap
);
371 bool BookmarkAppHelper::BookmarkOrHostedAppInstalled(
372 content::BrowserContext
* browser_context
,
374 ExtensionRegistry
* registry
= ExtensionRegistry::Get(browser_context
);
375 const ExtensionSet
& extensions
= registry
->enabled_extensions();
377 // Iterate through the extensions and extract the LaunchWebUrl (bookmark apps)
378 // or check the web extent (hosted apps).
379 for (extensions::ExtensionSet::const_iterator iter
= extensions
.begin();
380 iter
!= extensions
.end(); ++iter
) {
381 const Extension
* extension
= iter
->get();
382 if (!extension
->is_hosted_app())
385 if (extension
->web_extent().MatchesURL(url
) ||
386 AppLaunchInfo::GetLaunchWebURL(extension
) == url
) {
394 std::map
<int, BookmarkAppHelper::BitmapAndSource
>
395 BookmarkAppHelper::ResizeIconsAndGenerateMissing(
396 std::vector
<BookmarkAppHelper::BitmapAndSource
> icons
,
397 std::set
<int> sizes_to_generate
,
398 WebApplicationInfo
* web_app_info
) {
399 // Add the downloaded icons. Extensions only allow certain icon sizes. First
400 // populate icons that match the allowed sizes exactly and then downscale
401 // remaining icons to the closest allowed size that doesn't yet have an icon.
402 std::set
<int> allowed_sizes(extension_misc::kExtensionIconSizes
,
403 extension_misc::kExtensionIconSizes
+
404 extension_misc::kNumExtensionIconSizes
);
406 // If there are icons that don't match the accepted icon sizes, find the
407 // closest bigger icon to the accepted sizes and resize the icon to it. An
408 // icon will be resized and used for at most one size.
409 std::map
<int, BitmapAndSource
> resized_bitmaps(
410 ConstrainBitmapsToSizes(icons
, allowed_sizes
));
412 // Determine the color that will be used for the icon's background. For this
413 // the dominant color of the first icon found is used.
414 if (resized_bitmaps
.size()) {
415 color_utils::GridSampler sampler
;
416 web_app_info
->generated_icon_color
=
417 color_utils::CalculateKMeanColorOfBitmap(
418 resized_bitmaps
.begin()->second
.bitmap
);
421 // Work out what icons we need to generate here. Icons are only generated if:
422 // a. there is no icon in the required size, AND
423 // b. there is no icon LARGER than the required size.
424 // Larger icons will be scaled down and used at display time.
425 std::set
<int> generate_sizes
;
426 for (int size
: sizes_to_generate
) {
427 if (resized_bitmaps
.lower_bound(size
) == resized_bitmaps
.end())
428 generate_sizes
.insert(size
);
430 GenerateIcons(generate_sizes
, web_app_info
->app_url
,
431 web_app_info
->generated_icon_color
, &resized_bitmaps
);
433 return resized_bitmaps
;
437 void BookmarkAppHelper::UpdateWebAppIconsWithoutChangingLinks(
438 std::map
<int, BookmarkAppHelper::BitmapAndSource
> bitmap_map
,
439 WebApplicationInfo
* web_app_info
) {
440 // First add in the icon data that have urls with the url / size data from the
441 // original web app info, and the data from the new icons (if any).
442 for (auto& icon
: web_app_info
->icons
) {
443 if (!icon
.url
.is_empty() && icon
.data
.empty()) {
444 const auto& it
= bitmap_map
.find(icon
.width
);
445 if (it
!= bitmap_map
.end() && it
->second
.source_url
== icon
.url
)
446 icon
.data
= it
->second
.bitmap
;
450 // Now add in any icons from the updated list that don't have URLs.
451 for (const auto& pair
: bitmap_map
) {
452 if (pair
.second
.source_url
.is_empty()) {
453 WebApplicationInfo::IconInfo icon_info
;
454 icon_info
.data
= pair
.second
.bitmap
;
455 icon_info
.width
= pair
.first
;
456 icon_info
.height
= pair
.first
;
457 web_app_info
->icons
.push_back(icon_info
);
462 BookmarkAppHelper::BitmapAndSource::BitmapAndSource() {
465 BookmarkAppHelper::BitmapAndSource::BitmapAndSource(const GURL
& source_url_p
,
466 const SkBitmap
& bitmap_p
)
467 : source_url(source_url_p
),
471 BookmarkAppHelper::BitmapAndSource::~BitmapAndSource() {
474 BookmarkAppHelper::BookmarkAppHelper(Profile
* profile
,
475 WebApplicationInfo web_app_info
,
476 content::WebContents
* contents
)
479 web_app_info_(web_app_info
),
480 crx_installer_(extensions::CrxInstaller::CreateSilent(
481 ExtensionSystem::Get(profile
)->extension_service())) {
482 web_app_info_
.open_as_window
=
483 profile_
->GetPrefs()->GetInteger(
484 extensions::pref_names::kBookmarkAppCreationLaunchType
) ==
485 extensions::LAUNCH_TYPE_WINDOW
;
487 // The default app title is the page title, which can be quite long. Limit the
488 // default name used to something sensible.
489 const int kMaxDefaultTitle
= 40;
490 if (web_app_info_
.title
.length() > kMaxDefaultTitle
) {
491 web_app_info_
.title
= web_app_info_
.title
.substr(0, kMaxDefaultTitle
- 3) +
492 base::UTF8ToUTF16("...");
496 extensions::NOTIFICATION_CRX_INSTALLER_DONE
,
497 content::Source
<CrxInstaller
>(crx_installer_
.get()));
500 extensions::NOTIFICATION_EXTENSION_INSTALL_ERROR
,
501 content::Source
<CrxInstaller
>(crx_installer_
.get()));
503 crx_installer_
->set_error_on_unsupported_requirements(true);
506 BookmarkAppHelper::~BookmarkAppHelper() {}
508 void BookmarkAppHelper::Create(const CreateBookmarkAppCallback
& callback
) {
509 callback_
= callback
;
511 // Do not fetch the manifest for extension URLs.
513 !contents_
->GetVisibleURL().SchemeIs(extensions::kExtensionScheme
)) {
514 contents_
->GetManifest(base::Bind(&BookmarkAppHelper::OnDidGetManifest
,
515 base::Unretained(this)));
517 OnIconsDownloaded(true, std::map
<GURL
, std::vector
<SkBitmap
> >());
521 void BookmarkAppHelper::CreateFromAppBanner(
522 const CreateBookmarkAppCallback
& callback
,
523 const content::Manifest
& manifest
) {
524 DCHECK(!manifest
.short_name
.is_null() || !manifest
.name
.is_null());
525 DCHECK(manifest
.start_url
.is_valid());
527 callback_
= callback
;
528 OnDidGetManifest(manifest
);
531 void BookmarkAppHelper::OnDidGetManifest(const content::Manifest
& manifest
) {
532 if (contents_
->IsBeingDestroyed())
535 UpdateWebAppInfoFromManifest(manifest
, &web_app_info_
);
537 // Add urls from the WebApplicationInfo.
538 std::vector
<GURL
> web_app_info_icon_urls
;
539 for (std::vector
<WebApplicationInfo::IconInfo
>::const_iterator it
=
540 web_app_info_
.icons
.begin();
541 it
!= web_app_info_
.icons
.end();
543 if (it
->url
.is_valid())
544 web_app_info_icon_urls
.push_back(it
->url
);
547 favicon_downloader_
.reset(
548 new FaviconDownloader(contents_
,
549 web_app_info_icon_urls
,
550 base::Bind(&BookmarkAppHelper::OnIconsDownloaded
,
551 base::Unretained(this))));
552 favicon_downloader_
->Start();
555 void BookmarkAppHelper::OnIconsDownloaded(
557 const std::map
<GURL
, std::vector
<SkBitmap
> >& bitmaps
) {
558 // The tab has navigated away during the icon download. Cancel the bookmark
561 favicon_downloader_
.reset();
562 callback_
.Run(nullptr, web_app_info_
);
566 std::vector
<BitmapAndSource
> downloaded_icons
;
567 for (FaviconDownloader::FaviconMap::const_iterator map_it
= bitmaps
.begin();
568 map_it
!= bitmaps
.end();
570 for (std::vector
<SkBitmap
>::const_iterator bitmap_it
=
571 map_it
->second
.begin();
572 bitmap_it
!= map_it
->second
.end();
574 if (bitmap_it
->empty() || bitmap_it
->width() != bitmap_it
->height())
577 downloaded_icons
.push_back(BitmapAndSource(map_it
->first
, *bitmap_it
));
581 // Add all existing icons from WebApplicationInfo.
582 for (std::vector
<WebApplicationInfo::IconInfo
>::const_iterator it
=
583 web_app_info_
.icons
.begin();
584 it
!= web_app_info_
.icons
.end();
586 const SkBitmap
& icon
= it
->data
;
587 if (!icon
.drawsNothing() && icon
.width() == icon
.height()) {
588 downloaded_icons
.push_back(BitmapAndSource(it
->url
, icon
));
592 web_app_info_
.generated_icon_color
= SK_ColorTRANSPARENT
;
593 std::map
<int, BitmapAndSource
> size_to_icons
=
594 ResizeIconsAndGenerateMissing(downloaded_icons
, SizesToGenerate(),
596 ReplaceWebAppIcons(size_to_icons
, &web_app_info_
);
597 favicon_downloader_
.reset();
600 // The web contents can be null in tests.
601 OnBubbleCompleted(true, web_app_info_
);
605 Browser
* browser
= chrome::FindBrowserWithWebContents(contents_
);
607 // The browser can be null in tests.
608 OnBubbleCompleted(true, web_app_info_
);
611 browser
->window()->ShowBookmarkAppBubble(
612 web_app_info_
, base::Bind(&BookmarkAppHelper::OnBubbleCompleted
,
613 base::Unretained(this)));
616 void BookmarkAppHelper::OnBubbleCompleted(
618 const WebApplicationInfo
& web_app_info
) {
620 web_app_info_
= web_app_info
;
621 crx_installer_
->InstallWebApp(web_app_info_
);
623 callback_
.Run(nullptr, web_app_info_
);
627 void BookmarkAppHelper::FinishInstallation(const Extension
* extension
) {
628 // Set the default 'open as' preference for use next time the dialog is
630 extensions::LaunchType launch_type
= web_app_info_
.open_as_window
631 ? extensions::LAUNCH_TYPE_WINDOW
632 : extensions::LAUNCH_TYPE_REGULAR
;
633 profile_
->GetPrefs()->SetInteger(
634 extensions::pref_names::kBookmarkAppCreationLaunchType
, launch_type
);
636 // Set the launcher type for the app.
637 extensions::SetLaunchType(profile_
, extension
->id(), launch_type
);
640 // The web contents can be null in tests.
641 callback_
.Run(extension
, web_app_info_
);
645 Browser
* browser
= chrome::FindBrowserWithWebContents(contents_
);
647 // The browser can be null in tests.
648 callback_
.Run(extension
, web_app_info_
);
652 // Pin the app to the relevant launcher depending on the OS.
653 Profile
* current_profile
= profile_
->GetOriginalProfile();
655 // On Mac, shortcuts are automatically created for hosted apps when they are
656 // installed, so there is no need to create them again.
657 #if !defined(OS_MACOSX)
658 chrome::HostDesktopType desktop
= browser
->host_desktop_type();
659 if (desktop
!= chrome::HOST_DESKTOP_TYPE_ASH
) {
660 web_app::ShortcutLocations creation_locations
;
661 #if defined(OS_LINUX)
662 creation_locations
.on_desktop
= true;
664 creation_locations
.on_desktop
= false;
666 creation_locations
.applications_menu_location
=
667 web_app::APP_MENU_LOCATION_SUBDIR_CHROMEAPPS
;
668 web_app::CreateShortcuts(web_app::SHORTCUT_CREATION_BY_USER
,
669 creation_locations
, current_profile
, extension
);
670 // Creating shortcuts in the start menu fails when the language is set
671 // to certain languages (e.g. Hindi). To work around this, the taskbar /
672 // quick launch icon is created separately to ensure it doesn't fail
673 // due to the start menu shortcut creation failing.
674 // See http://crbug.com/477297 and http://crbug.com/484577.
675 creation_locations
.on_desktop
= false;
676 creation_locations
.applications_menu_location
=
677 web_app::APP_MENU_LOCATION_NONE
;
678 creation_locations
.in_quick_launch_bar
= true;
679 web_app::CreateShortcuts(web_app::SHORTCUT_CREATION_BY_USER
,
680 creation_locations
, current_profile
, extension
);
683 ChromeLauncherController::instance()->PinAppWithID(extension
->id());
688 #if defined(OS_MACOSX)
689 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
690 switches::kDisableHostedAppShimCreation
)) {
691 web_app::RevealAppShimInFinderForApp(current_profile
, extension
);
695 callback_
.Run(extension
, web_app_info_
);
698 void BookmarkAppHelper::Observe(int type
,
699 const content::NotificationSource
& source
,
700 const content::NotificationDetails
& details
) {
702 case extensions::NOTIFICATION_CRX_INSTALLER_DONE
: {
703 const Extension
* extension
=
704 content::Details
<const Extension
>(details
).ptr();
706 DCHECK_EQ(AppLaunchInfo::GetLaunchWebURL(extension
),
707 web_app_info_
.app_url
);
708 FinishInstallation(extension
);
711 case extensions::NOTIFICATION_EXTENSION_INSTALL_ERROR
:
712 callback_
.Run(nullptr, web_app_info_
);
720 void CreateOrUpdateBookmarkApp(ExtensionService
* service
,
721 WebApplicationInfo
* web_app_info
) {
722 scoped_refptr
<BookmarkAppInstaller
> installer(
723 new BookmarkAppInstaller(service
, *web_app_info
));
727 void GetWebApplicationInfoFromApp(
728 content::BrowserContext
* browser_context
,
729 const extensions::Extension
* extension
,
730 const base::Callback
<void(const WebApplicationInfo
&)> callback
) {
731 if (!extension
->from_bookmark()) {
732 callback
.Run(WebApplicationInfo());
736 WebApplicationInfo web_app_info
;
737 web_app_info
.app_url
= AppLaunchInfo::GetLaunchWebURL(extension
);
738 web_app_info
.title
= base::UTF8ToUTF16(extension
->non_localized_name());
739 web_app_info
.description
= base::UTF8ToUTF16(extension
->description());
741 std::vector
<extensions::ImageLoader::ImageRepresentation
> info_list
;
742 for (size_t i
= 0; i
< extension_misc::kNumExtensionIconSizes
; ++i
) {
743 int size
= extension_misc::kExtensionIconSizes
[i
];
744 extensions::ExtensionResource resource
=
745 extensions::IconsInfo::GetIconResource(
746 extension
, size
, ExtensionIconSet::MATCH_EXACTLY
);
747 if (!resource
.empty()) {
748 info_list
.push_back(extensions::ImageLoader::ImageRepresentation(
750 extensions::ImageLoader::ImageRepresentation::ALWAYS_RESIZE
,
751 gfx::Size(size
, size
),
752 ui::SCALE_FACTOR_100P
));
756 extensions::ImageLoader::Get(browser_context
)->LoadImageFamilyAsync(
757 extension
, info_list
, base::Bind(&OnIconsLoaded
, web_app_info
, callback
));
760 bool IsValidBookmarkAppUrl(const GURL
& url
) {
761 URLPattern
origin_only_pattern(Extension::kValidBookmarkAppSchemes
);
762 origin_only_pattern
.SetMatchAllURLs(true);
763 return url
.is_valid() && origin_only_pattern
.MatchesURL(url
);
766 } // namespace extensions