Switch global error menu icon to vectorized MD asset
[chromium-blink-merge.git] / chrome / browser / extensions / bookmark_app_helper.cc
blobb327db852cc63f87bde90cfee35b8f9af981a59c
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"
7 #include <cctype>
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"
63 #endif
65 #if defined(USE_ASH)
66 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
67 #endif
69 namespace {
71 using extensions::BookmarkAppHelper;
73 // Overlays a shortcut icon over the bottom left corner of a given image.
74 class GeneratedIconImageSource : public gfx::CanvasImageSource {
75 public:
76 explicit GeneratedIconImageSource(char letter, SkColor color, int output_size)
77 : gfx::CanvasImageSource(gfx::Size(output_size, output_size), false),
78 letter_(letter),
79 color_(color),
80 output_size_(output_size) {}
81 ~GeneratedIconImageSource() override {}
83 private:
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;
97 #endif
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,
116 text_rect,
117 gfx::Canvas::TEXT_ALIGN_CENTER);
120 char letter_;
122 SkColor color_;
124 int output_size_;
126 DISALLOW_COPY_AND_ASSIGN(GeneratedIconImageSource);
129 void OnIconsLoaded(
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();
135 ++it) {
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));
156 void GenerateIcons(
157 std::set<int> generate_sizes,
158 const GURL& app_url,
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(
165 app_url,
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
174 // black shelf.
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
206 // the icons.
207 class BookmarkAppInstaller : public base::RefCounted<BookmarkAppInstaller>,
208 public chrome::BitmapFetcherDelegate {
209 public:
210 BookmarkAppInstaller(ExtensionService* service,
211 const WebApplicationInfo& web_app_info)
212 : service_(service), web_app_info_(web_app_info) {}
214 void Run() {
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()) {
221 DownloadNextImage();
223 // Matched in OnFetchComplete.
224 AddRef();
225 return;
228 FinishInstallation();
231 private:
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()) {
243 DownloadNextImage();
244 return;
247 FinishInstallation();
248 Release();
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_,
267 SizesToGenerate(),
268 &web_app_info_);
269 BookmarkAppHelper::UpdateWebAppIconsWithoutChangingLinks(size_map,
270 &web_app_info_);
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_;
285 } // namespace
287 namespace extensions {
289 // static
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;
311 info.url = icon.src;
312 web_app_info->icons.push_back(info);
317 // static
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);
337 ++sizes_it;
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,
347 size, size);
351 return output_bitmaps;
354 // static
355 void BookmarkAppHelper::GenerateIcon(
356 std::map<int, BookmarkAppHelper::BitmapAndSource>* bitmaps,
357 int output_size,
358 SkColor color,
359 char letter) {
360 // Do nothing if there is already an icon of |output_size|.
361 if (bitmaps->count(output_size))
362 return;
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);
370 // static
371 bool BookmarkAppHelper::BookmarkOrHostedAppInstalled(
372 content::BrowserContext* browser_context,
373 const GURL& url) {
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())
383 continue;
385 if (extension->web_extent().MatchesURL(url) ||
386 AppLaunchInfo::GetLaunchWebURL(extension) == url) {
387 return true;
390 return false;
393 // static
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;
436 // static
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),
468 bitmap(bitmap_p) {
471 BookmarkAppHelper::BitmapAndSource::~BitmapAndSource() {
474 BookmarkAppHelper::BookmarkAppHelper(Profile* profile,
475 WebApplicationInfo web_app_info,
476 content::WebContents* contents)
477 : profile_(profile),
478 contents_(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("...");
495 registrar_.Add(this,
496 extensions::NOTIFICATION_CRX_INSTALLER_DONE,
497 content::Source<CrxInstaller>(crx_installer_.get()));
499 registrar_.Add(this,
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.
512 if (contents_ &&
513 !contents_->GetVisibleURL().SchemeIs(extensions::kExtensionScheme)) {
514 contents_->GetManifest(base::Bind(&BookmarkAppHelper::OnDidGetManifest,
515 base::Unretained(this)));
516 } else {
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())
533 return;
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();
542 ++it) {
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(
556 bool success,
557 const std::map<GURL, std::vector<SkBitmap> >& bitmaps) {
558 // The tab has navigated away during the icon download. Cancel the bookmark
559 // app creation.
560 if (!success) {
561 favicon_downloader_.reset();
562 callback_.Run(nullptr, web_app_info_);
563 return;
566 std::vector<BitmapAndSource> downloaded_icons;
567 for (FaviconDownloader::FaviconMap::const_iterator map_it = bitmaps.begin();
568 map_it != bitmaps.end();
569 ++map_it) {
570 for (std::vector<SkBitmap>::const_iterator bitmap_it =
571 map_it->second.begin();
572 bitmap_it != map_it->second.end();
573 ++bitmap_it) {
574 if (bitmap_it->empty() || bitmap_it->width() != bitmap_it->height())
575 continue;
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();
585 ++it) {
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(),
595 &web_app_info_);
596 ReplaceWebAppIcons(size_to_icons, &web_app_info_);
597 favicon_downloader_.reset();
599 if (!contents_) {
600 // The web contents can be null in tests.
601 OnBubbleCompleted(true, web_app_info_);
602 return;
605 Browser* browser = chrome::FindBrowserWithWebContents(contents_);
606 if (!browser) {
607 // The browser can be null in tests.
608 OnBubbleCompleted(true, web_app_info_);
609 return;
611 browser->window()->ShowBookmarkAppBubble(
612 web_app_info_, base::Bind(&BookmarkAppHelper::OnBubbleCompleted,
613 base::Unretained(this)));
616 void BookmarkAppHelper::OnBubbleCompleted(
617 bool user_accepted,
618 const WebApplicationInfo& web_app_info) {
619 if (user_accepted) {
620 web_app_info_ = web_app_info;
621 crx_installer_->InstallWebApp(web_app_info_);
622 } else {
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
629 // shown.
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);
639 if (!contents_) {
640 // The web contents can be null in tests.
641 callback_.Run(extension, web_app_info_);
642 return;
645 Browser* browser = chrome::FindBrowserWithWebContents(contents_);
646 if (!browser) {
647 // The browser can be null in tests.
648 callback_.Run(extension, web_app_info_);
649 return;
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;
663 #else
664 creation_locations.on_desktop = false;
665 #endif
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);
681 #if defined(USE_ASH)
682 } else {
683 ChromeLauncherController::instance()->PinAppWithID(extension->id());
684 #endif
686 #endif
688 #if defined(OS_MACOSX)
689 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
690 switches::kDisableHostedAppShimCreation)) {
691 web_app::RevealAppShimInFinderForApp(current_profile, extension);
693 #endif
695 callback_.Run(extension, web_app_info_);
698 void BookmarkAppHelper::Observe(int type,
699 const content::NotificationSource& source,
700 const content::NotificationDetails& details) {
701 switch (type) {
702 case extensions::NOTIFICATION_CRX_INSTALLER_DONE: {
703 const Extension* extension =
704 content::Details<const Extension>(details).ptr();
705 DCHECK(extension);
706 DCHECK_EQ(AppLaunchInfo::GetLaunchWebURL(extension),
707 web_app_info_.app_url);
708 FinishInstallation(extension);
709 break;
711 case extensions::NOTIFICATION_EXTENSION_INSTALL_ERROR:
712 callback_.Run(nullptr, web_app_info_);
713 break;
714 default:
715 NOTREACHED();
716 break;
720 void CreateOrUpdateBookmarkApp(ExtensionService* service,
721 WebApplicationInfo* web_app_info) {
722 scoped_refptr<BookmarkAppInstaller> installer(
723 new BookmarkAppInstaller(service, *web_app_info));
724 installer->Run();
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());
733 return;
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(
749 resource,
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