Add a function to create a bookmark app from a WebApplicationInfo.
[chromium-blink-merge.git] / chrome / browser / extensions / bookmark_app_helper.cc
blob3c5b43d02bd2802001bc09724d1ab1d33a351f23
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 "base/strings/utf_string_conversions.h"
8 #include "chrome/browser/chrome_notification_types.h"
9 #include "chrome/browser/extensions/crx_installer.h"
10 #include "chrome/browser/extensions/extension_service.h"
11 #include "chrome/browser/extensions/favicon_downloader.h"
12 #include "chrome/browser/extensions/tab_helper.h"
13 #include "chrome/common/extensions/extension_constants.h"
14 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
15 #include "chrome/common/extensions/manifest_handlers/icons_handler.h"
16 #include "content/public/browser/notification_service.h"
17 #include "content/public/browser/notification_source.h"
18 #include "content/public/browser/web_contents.h"
19 #include "extensions/common/extension.h"
20 #include "skia/ext/image_operations.h"
21 #include "skia/ext/platform_canvas.h"
22 #include "third_party/skia/include/core/SkBitmap.h"
23 #include "ui/gfx/color_analysis.h"
24 #include "ui/gfx/image/image.h"
26 namespace extensions {
28 // static
29 std::map<int, SkBitmap> BookmarkAppHelper::ConstrainBitmapsToSizes(
30 const std::vector<SkBitmap>& bitmaps,
31 const std::set<int>& sizes) {
32 std::map<int, SkBitmap> output_bitmaps;
33 std::map<int, SkBitmap> ordered_bitmaps;
34 for (std::vector<SkBitmap>::const_iterator it = bitmaps.begin();
35 it != bitmaps.end();
36 ++it) {
37 DCHECK(it->width() == it->height());
38 ordered_bitmaps[it->width()] = *it;
41 std::set<int>::const_iterator sizes_it = sizes.begin();
42 std::map<int, SkBitmap>::const_iterator bitmaps_it = ordered_bitmaps.begin();
43 while (sizes_it != sizes.end() && bitmaps_it != ordered_bitmaps.end()) {
44 int size = *sizes_it;
45 // Find the closest not-smaller bitmap.
46 bitmaps_it = ordered_bitmaps.lower_bound(size);
47 ++sizes_it;
48 // Ensure the bitmap is valid and smaller than the next allowed size.
49 if (bitmaps_it != ordered_bitmaps.end() &&
50 (sizes_it == sizes.end() || bitmaps_it->second.width() < *sizes_it)) {
51 // Resize the bitmap if it does not exactly match the desired size.
52 output_bitmaps[size] = bitmaps_it->second.width() == size
53 ? bitmaps_it->second
54 : skia::ImageOperations::Resize(
55 bitmaps_it->second,
56 skia::ImageOperations::RESIZE_LANCZOS3,
57 size,
58 size);
61 return output_bitmaps;
64 // static
65 void BookmarkAppHelper::GenerateContainerIcon(std::map<int, SkBitmap>* bitmaps,
66 int output_size) {
67 std::map<int, SkBitmap>::const_iterator it =
68 bitmaps->lower_bound(output_size);
69 // Do nothing if there is no icon smaller than the desired size or there is
70 // already an icon of |output_size|.
71 if (it == bitmaps->begin() || bitmaps->count(output_size))
72 return;
74 --it;
75 // This is the biggest icon smaller than |output_size|.
76 const SkBitmap& base_icon = it->second;
78 const size_t kBorderRadius = 5;
79 const size_t kColorStripHeight = 3;
80 const SkColor kBorderColor = 0xFFD5D5D5;
81 const SkColor kBackgroundColor = 0xFFFFFFFF;
83 // Create a separate canvas for the color strip.
84 scoped_ptr<SkCanvas> color_strip_canvas(
85 skia::CreateBitmapCanvas(output_size, output_size, false));
86 DCHECK(color_strip_canvas);
88 // Draw a rounded rect of the |base_icon|'s dominant color.
89 SkPaint color_strip_paint;
90 color_utils::GridSampler sampler;
91 color_strip_paint.setFlags(SkPaint::kAntiAlias_Flag);
92 color_strip_paint.setColor(color_utils::CalculateKMeanColorOfPNG(
93 gfx::Image::CreateFrom1xBitmap(base_icon).As1xPNGBytes(),
94 100,
95 665,
96 &sampler));
97 color_strip_canvas->drawRoundRect(SkRect::MakeWH(output_size, output_size),
98 kBorderRadius,
99 kBorderRadius,
100 color_strip_paint);
102 // Erase the top of the rounded rect to leave a color strip.
103 SkPaint clear_paint;
104 clear_paint.setColor(SK_ColorTRANSPARENT);
105 clear_paint.setXfermodeMode(SkXfermode::kSrc_Mode);
106 color_strip_canvas->drawRect(
107 SkRect::MakeWH(output_size, output_size - kColorStripHeight),
108 clear_paint);
110 // Draw each element to an output canvas.
111 scoped_ptr<SkCanvas> canvas(
112 skia::CreateBitmapCanvas(output_size, output_size, false));
113 DCHECK(canvas);
115 // Draw the background.
116 SkPaint background_paint;
117 background_paint.setColor(kBackgroundColor);
118 background_paint.setFlags(SkPaint::kAntiAlias_Flag);
119 canvas->drawRoundRect(SkRect::MakeWH(output_size, output_size),
120 kBorderRadius,
121 kBorderRadius,
122 background_paint);
124 // Draw the color strip.
125 canvas->drawBitmap(
126 color_strip_canvas->getDevice()->accessBitmap(false), 0, 0);
128 // Draw the border.
129 SkPaint border_paint;
130 border_paint.setColor(kBorderColor);
131 border_paint.setStyle(SkPaint::kStroke_Style);
132 border_paint.setFlags(SkPaint::kAntiAlias_Flag);
133 canvas->drawRoundRect(SkRect::MakeWH(output_size, output_size),
134 kBorderRadius,
135 kBorderRadius,
136 border_paint);
138 // Draw the centered base icon to the output canvas.
139 canvas->drawBitmap(base_icon,
140 (output_size - base_icon.width()) / 2,
141 (output_size - base_icon.height()) / 2);
143 const SkBitmap& generated_icon = canvas->getDevice()->accessBitmap(false);
144 generated_icon.deepCopyTo(&(*bitmaps)[output_size]);
147 BookmarkAppHelper::BookmarkAppHelper(ExtensionService* service,
148 WebApplicationInfo web_app_info,
149 content::WebContents* contents)
150 : web_app_info_(web_app_info),
151 crx_installer_(extensions::CrxInstaller::CreateSilent(service)) {
152 registrar_.Add(this,
153 chrome::NOTIFICATION_CRX_INSTALLER_DONE,
154 content::Source<CrxInstaller>(crx_installer_.get()));
156 registrar_.Add(this,
157 chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR,
158 content::Source<CrxInstaller>(crx_installer_.get()));
160 crx_installer_->set_error_on_unsupported_requirements(true);
162 // Add urls from the WebApplicationInfo.
163 std::vector<GURL> web_app_info_icon_urls;
164 for (std::vector<WebApplicationInfo::IconInfo>::const_iterator it =
165 web_app_info_.icons.begin();
166 it != web_app_info_.icons.end();
167 ++it) {
168 if (it->url.is_valid())
169 web_app_info_icon_urls.push_back(it->url);
172 favicon_downloader_.reset(
173 new FaviconDownloader(contents,
174 web_app_info_icon_urls,
175 base::Bind(&BookmarkAppHelper::OnIconsDownloaded,
176 base::Unretained(this))));
179 BookmarkAppHelper::~BookmarkAppHelper() {}
181 void BookmarkAppHelper::Create(const CreateBookmarkAppCallback& callback) {
182 callback_ = callback;
183 favicon_downloader_->Start();
186 void BookmarkAppHelper::OnIconsDownloaded(
187 bool success,
188 const std::map<GURL, std::vector<SkBitmap> >& bitmaps) {
189 // The tab has navigated away during the icon download. Cancel the bookmark
190 // app creation.
191 if (!success) {
192 favicon_downloader_.reset();
193 callback_.Run(NULL, web_app_info_);
194 return;
197 // Add the downloaded icons. Extensions only allow certain icon sizes. First
198 // populate icons that match the allowed sizes exactly and then downscale
199 // remaining icons to the closest allowed size that doesn't yet have an icon.
200 std::set<int> allowed_sizes(extension_misc::kExtensionIconSizes,
201 extension_misc::kExtensionIconSizes +
202 extension_misc::kNumExtensionIconSizes);
203 std::vector<SkBitmap> downloaded_icons;
204 for (FaviconDownloader::FaviconMap::const_iterator map_it = bitmaps.begin();
205 map_it != bitmaps.end();
206 ++map_it) {
207 for (std::vector<SkBitmap>::const_iterator bitmap_it =
208 map_it->second.begin();
209 bitmap_it != map_it->second.end();
210 ++bitmap_it) {
211 if (bitmap_it->empty() || bitmap_it->width() != bitmap_it->height())
212 continue;
214 downloaded_icons.push_back(*bitmap_it);
218 // If there are icons that don't match the accepted icon sizes, find the
219 // closest bigger icon to the accepted sizes and resize the icon to it. An
220 // icon will be resized and used for at most one size.
221 std::map<int, SkBitmap> resized_bitmaps(
222 ConstrainBitmapsToSizes(downloaded_icons, allowed_sizes));
224 // Generate container icons from smaller icons.
225 const int kIconSizesToGenerate[] = {extension_misc::EXTENSION_ICON_SMALL,
226 extension_misc::EXTENSION_ICON_MEDIUM, };
227 const std::set<int> generate_sizes(
228 kIconSizesToGenerate,
229 kIconSizesToGenerate + arraysize(kIconSizesToGenerate));
231 // Only generate icons if larger icons don't exist. This means the app
232 // launcher and the taskbar will do their best downsizing large icons and
233 // these container icons are only generated as a last resort against upscaling
234 // a smaller icon.
235 if (resized_bitmaps.lower_bound(*generate_sizes.rbegin()) ==
236 resized_bitmaps.end()) {
237 // Generate these from biggest to smallest so we don't end up with
238 // concentric container icons.
239 for (std::set<int>::const_reverse_iterator it = generate_sizes.rbegin();
240 it != generate_sizes.rend();
241 ++it) {
242 GenerateContainerIcon(&resized_bitmaps, *it);
246 // Populate the icon data into the WebApplicationInfo we are using to
247 // install the bookmark app.
248 for (std::map<int, SkBitmap>::const_iterator resized_bitmaps_it =
249 resized_bitmaps.begin();
250 resized_bitmaps_it != resized_bitmaps.end();
251 ++resized_bitmaps_it) {
252 WebApplicationInfo::IconInfo icon_info;
253 icon_info.data = resized_bitmaps_it->second;
254 icon_info.width = icon_info.data.width();
255 icon_info.height = icon_info.data.height();
256 web_app_info_.icons.push_back(icon_info);
259 // Install the app.
260 crx_installer_->InstallWebApp(web_app_info_);
261 favicon_downloader_.reset();
264 void BookmarkAppHelper::Observe(int type,
265 const content::NotificationSource& source,
266 const content::NotificationDetails& details) {
267 switch (type) {
268 case chrome::NOTIFICATION_CRX_INSTALLER_DONE: {
269 const Extension* extension =
270 content::Details<const Extension>(details).ptr();
271 DCHECK(extension);
272 DCHECK_EQ(AppLaunchInfo::GetLaunchWebURL(extension),
273 web_app_info_.app_url);
274 callback_.Run(extension, web_app_info_);
275 break;
277 case chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR:
278 callback_.Run(NULL, web_app_info_);
279 break;
280 default:
281 NOTREACHED();
282 break;
286 void CreateOrUpdateBookmarkApp(ExtensionService* service,
287 WebApplicationInfo& web_app_info) {
288 scoped_refptr<extensions::CrxInstaller> installer(
289 extensions::CrxInstaller::CreateSilent(service));
290 installer->set_error_on_unsupported_requirements(true);
291 installer->InstallWebApp(web_app_info);
294 } // namespace extensions