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