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 25#if !defined(OS_ANDROID) 26const char kAppUrl[] = "http://www.chromium.org"; 27const char kAppTitle[] = "Test title"; 28const char kAlternativeAppTitle[] = "Different test title"; 29const char kAppDescription[] = "Test description"; 30 31const int kIconSizeTiny = extension_misc::EXTENSION_ICON_BITTY; 32const int kIconSizeSmall = extension_misc::EXTENSION_ICON_SMALL; 33const int kIconSizeMedium = extension_misc::EXTENSION_ICON_MEDIUM; 34const int kIconSizeLarge = extension_misc::EXTENSION_ICON_LARGE; 35#endif 36 37class BookmarkAppHelperTest : public testing::Test { 38 public: 39 BookmarkAppHelperTest() {} 40 virtual ~BookmarkAppHelperTest() {} 41 42 private: 43 DISALLOW_COPY_AND_ASSIGN(BookmarkAppHelperTest); 44}; 45 46class BookmarkAppHelperExtensionServiceTest 47 : public extensions::ExtensionServiceTestBase { 48 public: 49 BookmarkAppHelperExtensionServiceTest() {} 50 virtual ~BookmarkAppHelperExtensionServiceTest() {} 51 52 virtual void SetUp() OVERRIDE { 53 extensions::ExtensionServiceTestBase::SetUp(); 54 InitializeEmptyExtensionService(); 55 service_->Init(); 56 EXPECT_EQ(0u, service_->extensions()->size()); 57 } 58 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 76SkBitmap 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 84void 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) 93WebApplicationInfo::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 101void 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 121namespace extensions { 122 123class TestBookmarkAppHelper : public BookmarkAppHelper { 124 public: 125 TestBookmarkAppHelper(ExtensionService* service, 126 WebApplicationInfo web_app_info, 127 content::WebContents* contents) 128 : BookmarkAppHelper(service, web_app_info, contents) {} 129 130 virtual ~TestBookmarkAppHelper() {} 131 132 void CreationComplete(const extensions::Extension* extension, 133 const WebApplicationInfo& web_app_info) { 134 extension_ = extension; 135 } 136 137 void CompleteIconDownload( 138 bool success, 139 const std::map<GURL, std::vector<SkBitmap> >& bitmaps) { 140 BookmarkAppHelper::OnIconsDownloaded(success, bitmaps); 141 } 142 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) 153TEST_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 185TEST_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 226TEST_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 272TEST_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 297TEST_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 336TEST_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