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.
4
5#include "chrome/browser/extensions/bookmark_app_helper.h"
6
7#include "base/strings/utf_string_conversions.h"
8#include "chrome/browser/extensions/extension_service.h"
9#include "chrome/browser/extensions/extension_service_test_base.h"
10#include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
11#include "chrome/test/base/testing_profile.h"
12#include "content/public/browser/render_process_host.h"
13#include "content/public/browser/web_contents.h"
14#include "content/public/test/web_contents_tester.h"
15#include "extensions/browser/extension_registry.h"
16#include "extensions/common/constants.h"
17#include "extensions/common/extension_icon_set.h"
18#include "extensions/common/manifest_handlers/icons_handler.h"
19#include "testing/gtest/include/gtest/gtest.h"
20#include "third_party/skia/include/core/SkBitmap.h"
21#include "ui/gfx/skia_util.h"
22
23namespace {
24
25const char kAppUrl[] = "http://www.chromium.org";
26const char kAppTitle[] = "Test title";
27const char kAlternativeAppTitle[] = "Different test title";
28const char kAppDescription[] = "Test description";
29
30const int kIconSizeTiny = extension_misc::EXTENSION_ICON_BITTY;
31const int kIconSizeSmall = extension_misc::EXTENSION_ICON_SMALL;
32const int kIconSizeMedium = extension_misc::EXTENSION_ICON_MEDIUM;
33const int kIconSizeLarge = extension_misc::EXTENSION_ICON_LARGE;
34
35class BookmarkAppHelperTest : public testing::Test {
36 public:
37  BookmarkAppHelperTest() {}
38  virtual ~BookmarkAppHelperTest() {}
39
40 private:
41  DISALLOW_COPY_AND_ASSIGN(BookmarkAppHelperTest);
42};
43
44class BookmarkAppHelperExtensionServiceTest
45    : public extensions::ExtensionServiceTestBase {
46 public:
47  BookmarkAppHelperExtensionServiceTest() {}
48  virtual ~BookmarkAppHelperExtensionServiceTest() {}
49
50  virtual void SetUp() OVERRIDE {
51    extensions::ExtensionServiceTestBase::SetUp();
52    InitializeEmptyExtensionService();
53    service_->Init();
54    EXPECT_EQ(0u, service_->extensions()->size());
55  }
56
57  virtual void TearDown() OVERRIDE {
58    ExtensionServiceTestBase::TearDown();
59    for (content::RenderProcessHost::iterator i(
60             content::RenderProcessHost::AllHostsIterator());
61         !i.IsAtEnd();
62         i.Advance()) {
63      content::RenderProcessHost* host = i.GetCurrentValue();
64      if (Profile::FromBrowserContext(host->GetBrowserContext()) ==
65          profile_.get())
66        host->Cleanup();
67    }
68  }
69
70 private:
71  DISALLOW_COPY_AND_ASSIGN(BookmarkAppHelperExtensionServiceTest);
72};
73
74SkBitmap CreateSquareBitmapWithColor(int size, SkColor color) {
75  SkBitmap bitmap;
76  bitmap.allocN32Pixels(size, size);
77  bitmap.eraseColor(color);
78  return bitmap;
79}
80
81void ValidateBitmapSizeAndColor(SkBitmap bitmap, int size, SkColor color) {
82  // Obtain pixel lock to access pixels.
83  SkAutoLockPixels lock(bitmap);
84  EXPECT_EQ(color, bitmap.getColor(0, 0));
85  EXPECT_EQ(size, bitmap.width());
86  EXPECT_EQ(size, bitmap.height());
87}
88
89WebApplicationInfo::IconInfo CreateIconInfoWithBitmap(int size, SkColor color) {
90  WebApplicationInfo::IconInfo icon_info;
91  icon_info.width = size;
92  icon_info.height = size;
93  icon_info.data = CreateSquareBitmapWithColor(size, color);
94  return icon_info;
95}
96
97void ValidateWebApplicationInfo(base::Closure callback,
98                                const WebApplicationInfo& expected,
99                                const WebApplicationInfo& actual) {
100  EXPECT_EQ(expected.title, actual.title);
101  EXPECT_EQ(expected.description, actual.description);
102  EXPECT_EQ(expected.app_url, actual.app_url);
103  EXPECT_EQ(expected.icons.size(), actual.icons.size());
104  for (size_t i = 0; i < expected.icons.size(); ++i) {
105    EXPECT_EQ(expected.icons[i].width, actual.icons[i].width);
106    EXPECT_EQ(expected.icons[i].height, actual.icons[i].height);
107    EXPECT_EQ(expected.icons[i].url, actual.icons[i].url);
108    EXPECT_TRUE(
109        gfx::BitmapsAreEqual(expected.icons[i].data, actual.icons[i].data));
110  }
111  callback.Run();
112}
113
114}  // namespace
115
116namespace extensions {
117
118class TestBookmarkAppHelper : public BookmarkAppHelper {
119 public:
120  TestBookmarkAppHelper(ExtensionService* service,
121                        WebApplicationInfo web_app_info,
122                        content::WebContents* contents)
123      : BookmarkAppHelper(service, web_app_info, contents) {}
124
125  virtual ~TestBookmarkAppHelper() {}
126
127  void CreationComplete(const extensions::Extension* extension,
128                        const WebApplicationInfo& web_app_info) {
129    extension_ = extension;
130  }
131
132  void CompleteIconDownload(
133      bool success,
134      const std::map<GURL, std::vector<SkBitmap> >& bitmaps) {
135    BookmarkAppHelper::OnIconsDownloaded(success, bitmaps);
136  }
137
138  const Extension* extension() { return extension_; }
139
140 private:
141  const Extension* extension_;
142
143  DISALLOW_COPY_AND_ASSIGN(TestBookmarkAppHelper);
144};
145
146TEST_F(BookmarkAppHelperExtensionServiceTest, CreateBookmarkApp) {
147  WebApplicationInfo web_app_info;
148  web_app_info.app_url = GURL(kAppUrl);
149  web_app_info.title = base::UTF8ToUTF16(kAppTitle);
150  web_app_info.description = base::UTF8ToUTF16(kAppDescription);
151
152  scoped_ptr<content::WebContents> contents(
153      content::WebContentsTester::CreateTestWebContents(profile_.get(), NULL));
154  TestBookmarkAppHelper helper(service_, web_app_info, contents.get());
155  helper.Create(base::Bind(&TestBookmarkAppHelper::CreationComplete,
156                           base::Unretained(&helper)));
157
158  std::map<GURL, std::vector<SkBitmap> > icon_map;
159  icon_map[GURL(kAppUrl)].push_back(
160      CreateSquareBitmapWithColor(kIconSizeSmall, SK_ColorRED));
161  helper.CompleteIconDownload(true, icon_map);
162
163  base::RunLoop().RunUntilIdle();
164  EXPECT_TRUE(helper.extension());
165  const Extension* extension =
166      service_->GetInstalledExtension(helper.extension()->id());
167  EXPECT_TRUE(extension);
168  EXPECT_EQ(1u, service_->extensions()->size());
169  EXPECT_TRUE(extension->from_bookmark());
170  EXPECT_EQ(kAppTitle, extension->name());
171  EXPECT_EQ(kAppDescription, extension->description());
172  EXPECT_EQ(GURL(kAppUrl), AppLaunchInfo::GetLaunchWebURL(extension));
173  EXPECT_FALSE(
174      IconsInfo::GetIconResource(
175          extension, kIconSizeSmall, ExtensionIconSet::MATCH_EXACTLY).empty());
176}
177
178TEST_F(BookmarkAppHelperExtensionServiceTest, CreateBookmarkAppNoContents) {
179  WebApplicationInfo web_app_info;
180  web_app_info.app_url = GURL(kAppUrl);
181  web_app_info.title = base::UTF8ToUTF16(kAppTitle);
182  web_app_info.description = base::UTF8ToUTF16(kAppDescription);
183  web_app_info.icons.push_back(
184      CreateIconInfoWithBitmap(kIconSizeTiny, SK_ColorRED));
185
186  TestBookmarkAppHelper helper(service_, web_app_info, NULL);
187  helper.Create(base::Bind(&TestBookmarkAppHelper::CreationComplete,
188                           base::Unretained(&helper)));
189
190  base::RunLoop().RunUntilIdle();
191  EXPECT_TRUE(helper.extension());
192  const Extension* extension =
193      service_->GetInstalledExtension(helper.extension()->id());
194  EXPECT_TRUE(extension);
195  EXPECT_EQ(1u, service_->extensions()->size());
196  EXPECT_TRUE(extension->from_bookmark());
197  EXPECT_EQ(kAppTitle, extension->name());
198  EXPECT_EQ(kAppDescription, extension->description());
199  EXPECT_EQ(GURL(kAppUrl), AppLaunchInfo::GetLaunchWebURL(extension));
200  EXPECT_FALSE(
201      IconsInfo::GetIconResource(
202          extension, kIconSizeTiny, ExtensionIconSet::MATCH_EXACTLY).empty());
203  EXPECT_FALSE(
204      IconsInfo::GetIconResource(
205          extension, kIconSizeSmall, ExtensionIconSet::MATCH_EXACTLY).empty());
206  EXPECT_FALSE(
207      IconsInfo::GetIconResource(extension,
208                                 kIconSizeSmall * 2,
209                                 ExtensionIconSet::MATCH_EXACTLY).empty());
210  EXPECT_FALSE(
211      IconsInfo::GetIconResource(
212          extension, kIconSizeMedium, ExtensionIconSet::MATCH_EXACTLY).empty());
213  EXPECT_FALSE(
214      IconsInfo::GetIconResource(extension,
215                                 kIconSizeMedium * 2,
216                                 ExtensionIconSet::MATCH_EXACTLY).empty());
217}
218
219TEST_F(BookmarkAppHelperExtensionServiceTest, CreateAndUpdateBookmarkApp) {
220  EXPECT_EQ(0u, registry()->enabled_extensions().size());
221  WebApplicationInfo web_app_info;
222  web_app_info.app_url = GURL(kAppUrl);
223  web_app_info.title = base::UTF8ToUTF16(kAppTitle);
224  web_app_info.description = base::UTF8ToUTF16(kAppDescription);
225  web_app_info.icons.push_back(
226      CreateIconInfoWithBitmap(kIconSizeSmall, SK_ColorRED));
227
228  extensions::CreateOrUpdateBookmarkApp(service_, web_app_info);
229  base::RunLoop().RunUntilIdle();
230
231  {
232    EXPECT_EQ(1u, registry()->enabled_extensions().size());
233    const Extension* extension = service_->extensions()->begin()->get();
234    EXPECT_TRUE(extension->from_bookmark());
235    EXPECT_EQ(kAppTitle, extension->name());
236    EXPECT_EQ(kAppDescription, extension->description());
237    EXPECT_EQ(GURL(kAppUrl), AppLaunchInfo::GetLaunchWebURL(extension));
238    EXPECT_FALSE(extensions::IconsInfo::GetIconResource(
239                     extension, kIconSizeSmall, ExtensionIconSet::MATCH_EXACTLY)
240                     .empty());
241  }
242
243  web_app_info.title = base::UTF8ToUTF16(kAlternativeAppTitle);
244  web_app_info.icons[0] = CreateIconInfoWithBitmap(kIconSizeLarge, SK_ColorRED);
245
246  extensions::CreateOrUpdateBookmarkApp(service_, web_app_info);
247  base::RunLoop().RunUntilIdle();
248
249  {
250    EXPECT_EQ(1u, registry()->enabled_extensions().size());
251    const Extension* extension = service_->extensions()->begin()->get();
252    EXPECT_TRUE(extension->from_bookmark());
253    EXPECT_EQ(kAlternativeAppTitle, extension->name());
254    EXPECT_EQ(kAppDescription, extension->description());
255    EXPECT_EQ(GURL(kAppUrl), AppLaunchInfo::GetLaunchWebURL(extension));
256    EXPECT_TRUE(extensions::IconsInfo::GetIconResource(
257                    extension, kIconSizeSmall, ExtensionIconSet::MATCH_EXACTLY)
258                    .empty());
259    EXPECT_FALSE(extensions::IconsInfo::GetIconResource(
260                     extension, kIconSizeLarge, ExtensionIconSet::MATCH_EXACTLY)
261                     .empty());
262  }
263}
264
265TEST_F(BookmarkAppHelperExtensionServiceTest, GetWebApplicationInfo) {
266  WebApplicationInfo web_app_info;
267  web_app_info.app_url = GURL(kAppUrl);
268  web_app_info.title = base::UTF8ToUTF16(kAppTitle);
269  web_app_info.description = base::UTF8ToUTF16(kAppDescription);
270
271  web_app_info.icons.push_back(
272      CreateIconInfoWithBitmap(kIconSizeSmall, SK_ColorRED));
273  web_app_info.icons.push_back(
274      CreateIconInfoWithBitmap(kIconSizeLarge, SK_ColorRED));
275
276  extensions::CreateOrUpdateBookmarkApp(service_, web_app_info);
277  base::RunLoop().RunUntilIdle();
278
279  EXPECT_EQ(1u, registry()->enabled_extensions().size());
280  base::RunLoop run_loop;
281  extensions::GetWebApplicationInfoFromApp(
282      profile_.get(),
283      service_->extensions()->begin()->get(),
284      base::Bind(
285          &ValidateWebApplicationInfo, run_loop.QuitClosure(), web_app_info));
286  run_loop.Run();
287}
288
289TEST_F(BookmarkAppHelperTest, ConstrainBitmapsToSizes) {
290  std::set<int> desired_sizes;
291  desired_sizes.insert(16);
292  desired_sizes.insert(32);
293  desired_sizes.insert(128);
294  desired_sizes.insert(256);
295
296  {
297    std::vector<SkBitmap> bitmaps;
298    bitmaps.push_back(CreateSquareBitmapWithColor(16, SK_ColorRED));
299    bitmaps.push_back(CreateSquareBitmapWithColor(32, SK_ColorGREEN));
300    bitmaps.push_back(CreateSquareBitmapWithColor(48, SK_ColorBLUE));
301    bitmaps.push_back(CreateSquareBitmapWithColor(144, SK_ColorYELLOW));
302
303    std::map<int, SkBitmap> results(
304        BookmarkAppHelper::ConstrainBitmapsToSizes(bitmaps, desired_sizes));
305
306    EXPECT_EQ(3u, results.size());
307    ValidateBitmapSizeAndColor(results[16], 16, SK_ColorRED);
308    ValidateBitmapSizeAndColor(results[32], 32, SK_ColorGREEN);
309    ValidateBitmapSizeAndColor(results[128], 128, SK_ColorYELLOW);
310  }
311  {
312    std::vector<SkBitmap> bitmaps;
313    bitmaps.push_back(CreateSquareBitmapWithColor(512, SK_ColorRED));
314    bitmaps.push_back(CreateSquareBitmapWithColor(18, SK_ColorGREEN));
315    bitmaps.push_back(CreateSquareBitmapWithColor(33, SK_ColorBLUE));
316    bitmaps.push_back(CreateSquareBitmapWithColor(17, SK_ColorYELLOW));
317
318    std::map<int, SkBitmap> results(
319        BookmarkAppHelper::ConstrainBitmapsToSizes(bitmaps, desired_sizes));
320
321    EXPECT_EQ(3u, results.size());
322    ValidateBitmapSizeAndColor(results[16], 16, SK_ColorYELLOW);
323    ValidateBitmapSizeAndColor(results[32], 32, SK_ColorBLUE);
324    ValidateBitmapSizeAndColor(results[256], 256, SK_ColorRED);
325  }
326}
327
328TEST_F(BookmarkAppHelperTest, IsValidBookmarkAppUrl) {
329  EXPECT_TRUE(IsValidBookmarkAppUrl(GURL("https://www.chromium.org")));
330  EXPECT_TRUE(IsValidBookmarkAppUrl(GURL("http://www.chromium.org/path")));
331  EXPECT_FALSE(IsValidBookmarkAppUrl(GURL("ftp://www.chromium.org")));
332  EXPECT_FALSE(IsValidBookmarkAppUrl(GURL("chrome://flags")));
333}
334
335}  // namespace extensions
336