| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 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/image_loader.h" | |
| 6 | |
| 7 #include "base/json/json_file_value_serializer.h" | |
| 8 #include "base/message_loop/message_loop.h" | |
| 9 #include "base/path_service.h" | |
| 10 #include "chrome/browser/chrome_notification_types.h" | |
| 11 #include "chrome/common/chrome_paths.h" | |
| 12 #include "content/public/browser/notification_service.h" | |
| 13 #include "content/public/test/test_browser_thread.h" | |
| 14 #include "extensions/common/constants.h" | |
| 15 #include "extensions/common/extension.h" | |
| 16 #include "extensions/common/extension_icon_set.h" | |
| 17 #include "extensions/common/extension_resource.h" | |
| 18 #include "extensions/common/manifest.h" | |
| 19 #include "extensions/common/manifest_handlers/icons_handler.h" | |
| 20 #include "testing/gtest/include/gtest/gtest.h" | |
| 21 #include "third_party/skia/include/core/SkBitmap.h" | |
| 22 #include "ui/file_manager/grit/file_manager_resources.h" | |
| 23 #include "ui/gfx/image/image.h" | |
| 24 #include "ui/gfx/image/image_family.h" | |
| 25 #include "ui/gfx/image/image_skia.h" | |
| 26 #include "ui/gfx/size.h" | |
| 27 | |
| 28 using content::BrowserThread; | |
| 29 using extensions::Extension; | |
| 30 using extensions::ExtensionResource; | |
| 31 using extensions::ImageLoader; | |
| 32 using extensions::Manifest; | |
| 33 using extensions::UnloadedExtensionInfo; | |
| 34 | |
| 35 class ImageLoaderTest : public testing::Test { | |
| 36 public: | |
| 37 ImageLoaderTest() | |
| 38 : image_loaded_count_(0), | |
| 39 quit_in_image_loaded_(false), | |
| 40 ui_thread_(BrowserThread::UI, &ui_loop_), | |
| 41 file_thread_(BrowserThread::FILE), | |
| 42 io_thread_(BrowserThread::IO) { | |
| 43 } | |
| 44 | |
| 45 void OnImageLoaded(const gfx::Image& image) { | |
| 46 image_loaded_count_++; | |
| 47 if (quit_in_image_loaded_) | |
| 48 base::MessageLoop::current()->Quit(); | |
| 49 image_ = image; | |
| 50 } | |
| 51 | |
| 52 void OnImageFamilyLoaded(const gfx::ImageFamily& image_family) { | |
| 53 image_loaded_count_++; | |
| 54 if (quit_in_image_loaded_) | |
| 55 base::MessageLoop::current()->Quit(); | |
| 56 image_family_ = image_family; | |
| 57 } | |
| 58 | |
| 59 void WaitForImageLoad() { | |
| 60 quit_in_image_loaded_ = true; | |
| 61 base::MessageLoop::current()->Run(); | |
| 62 quit_in_image_loaded_ = false; | |
| 63 } | |
| 64 | |
| 65 int image_loaded_count() { | |
| 66 int result = image_loaded_count_; | |
| 67 image_loaded_count_ = 0; | |
| 68 return result; | |
| 69 } | |
| 70 | |
| 71 scoped_refptr<Extension> CreateExtension(const char* name, | |
| 72 Manifest::Location location) { | |
| 73 // Create and load an extension. | |
| 74 base::FilePath test_file; | |
| 75 if (!PathService::Get(chrome::DIR_TEST_DATA, &test_file)) { | |
| 76 EXPECT_FALSE(true); | |
| 77 return NULL; | |
| 78 } | |
| 79 test_file = test_file.AppendASCII("extensions") | |
| 80 .AppendASCII(name); | |
| 81 int error_code = 0; | |
| 82 std::string error; | |
| 83 JSONFileValueSerializer serializer(test_file.AppendASCII("app.json")); | |
| 84 scoped_ptr<base::DictionaryValue> valid_value( | |
| 85 static_cast<base::DictionaryValue*>(serializer.Deserialize(&error_code, | |
| 86 &error))); | |
| 87 EXPECT_EQ(0, error_code) << error; | |
| 88 if (error_code != 0) | |
| 89 return NULL; | |
| 90 | |
| 91 EXPECT_TRUE(valid_value.get()); | |
| 92 if (!valid_value) | |
| 93 return NULL; | |
| 94 | |
| 95 if (location == Manifest::COMPONENT) { | |
| 96 if (!PathService::Get(chrome::DIR_RESOURCES, &test_file)) { | |
| 97 EXPECT_FALSE(true); | |
| 98 return NULL; | |
| 99 } | |
| 100 test_file = test_file.AppendASCII(name); | |
| 101 } | |
| 102 return Extension::Create(test_file, location, *valid_value, | |
| 103 Extension::NO_FLAGS, &error); | |
| 104 } | |
| 105 | |
| 106 gfx::Image image_; | |
| 107 gfx::ImageFamily image_family_; | |
| 108 | |
| 109 private: | |
| 110 virtual void SetUp() OVERRIDE { | |
| 111 testing::Test::SetUp(); | |
| 112 file_thread_.Start(); | |
| 113 io_thread_.Start(); | |
| 114 } | |
| 115 | |
| 116 int image_loaded_count_; | |
| 117 bool quit_in_image_loaded_; | |
| 118 base::MessageLoop ui_loop_; | |
| 119 content::TestBrowserThread ui_thread_; | |
| 120 content::TestBrowserThread file_thread_; | |
| 121 content::TestBrowserThread io_thread_; | |
| 122 }; | |
| 123 | |
| 124 // Tests loading an image works correctly. | |
| 125 TEST_F(ImageLoaderTest, LoadImage) { | |
| 126 scoped_refptr<Extension> extension(CreateExtension( | |
| 127 "image_loading_tracker", Manifest::INVALID_LOCATION)); | |
| 128 ASSERT_TRUE(extension.get() != NULL); | |
| 129 | |
| 130 ExtensionResource image_resource = extensions::IconsInfo::GetIconResource( | |
| 131 extension.get(), | |
| 132 extension_misc::EXTENSION_ICON_SMALLISH, | |
| 133 ExtensionIconSet::MATCH_EXACTLY); | |
| 134 gfx::Size max_size(extension_misc::EXTENSION_ICON_SMALLISH, | |
| 135 extension_misc::EXTENSION_ICON_SMALLISH); | |
| 136 ImageLoader loader; | |
| 137 loader.LoadImageAsync(extension.get(), | |
| 138 image_resource, | |
| 139 max_size, | |
| 140 base::Bind(&ImageLoaderTest::OnImageLoaded, | |
| 141 base::Unretained(this))); | |
| 142 | |
| 143 // The image isn't cached, so we should not have received notification. | |
| 144 EXPECT_EQ(0, image_loaded_count()); | |
| 145 | |
| 146 WaitForImageLoad(); | |
| 147 | |
| 148 // We should have gotten the image. | |
| 149 EXPECT_EQ(1, image_loaded_count()); | |
| 150 | |
| 151 // Check that the image was loaded. | |
| 152 EXPECT_EQ(extension_misc::EXTENSION_ICON_SMALLISH, | |
| 153 image_.ToSkBitmap()->width()); | |
| 154 } | |
| 155 | |
| 156 // Tests deleting an extension while waiting for the image to load doesn't cause | |
| 157 // problems. | |
| 158 TEST_F(ImageLoaderTest, DeleteExtensionWhileWaitingForCache) { | |
| 159 scoped_refptr<Extension> extension(CreateExtension( | |
| 160 "image_loading_tracker", Manifest::INVALID_LOCATION)); | |
| 161 ASSERT_TRUE(extension.get() != NULL); | |
| 162 | |
| 163 ExtensionResource image_resource = extensions::IconsInfo::GetIconResource( | |
| 164 extension.get(), | |
| 165 extension_misc::EXTENSION_ICON_SMALLISH, | |
| 166 ExtensionIconSet::MATCH_EXACTLY); | |
| 167 gfx::Size max_size(extension_misc::EXTENSION_ICON_SMALLISH, | |
| 168 extension_misc::EXTENSION_ICON_SMALLISH); | |
| 169 ImageLoader loader; | |
| 170 std::set<int> sizes; | |
| 171 sizes.insert(extension_misc::EXTENSION_ICON_SMALLISH); | |
| 172 loader.LoadImageAsync(extension.get(), | |
| 173 image_resource, | |
| 174 max_size, | |
| 175 base::Bind(&ImageLoaderTest::OnImageLoaded, | |
| 176 base::Unretained(this))); | |
| 177 | |
| 178 // The image isn't cached, so we should not have received notification. | |
| 179 EXPECT_EQ(0, image_loaded_count()); | |
| 180 | |
| 181 // Send out notification the extension was uninstalled. | |
| 182 UnloadedExtensionInfo details(extension.get(), | |
| 183 UnloadedExtensionInfo::REASON_UNINSTALL); | |
| 184 content::NotificationService::current()->Notify( | |
| 185 chrome::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED, | |
| 186 content::NotificationService::AllSources(), | |
| 187 content::Details<UnloadedExtensionInfo>(&details)); | |
| 188 | |
| 189 // Chuck the extension, that way if anyone tries to access it we should crash | |
| 190 // or get valgrind errors. | |
| 191 extension = NULL; | |
| 192 | |
| 193 WaitForImageLoad(); | |
| 194 | |
| 195 // Even though we deleted the extension, we should still get the image. | |
| 196 // We should still have gotten the image. | |
| 197 EXPECT_EQ(1, image_loaded_count()); | |
| 198 | |
| 199 // Check that the image was loaded. | |
| 200 EXPECT_EQ(extension_misc::EXTENSION_ICON_SMALLISH, | |
| 201 image_.ToSkBitmap()->width()); | |
| 202 } | |
| 203 | |
| 204 // Tests loading multiple dimensions of the same image. | |
| 205 TEST_F(ImageLoaderTest, MultipleImages) { | |
| 206 scoped_refptr<Extension> extension(CreateExtension( | |
| 207 "image_loading_tracker", Manifest::INVALID_LOCATION)); | |
| 208 ASSERT_TRUE(extension.get() != NULL); | |
| 209 | |
| 210 std::vector<ImageLoader::ImageRepresentation> info_list; | |
| 211 int sizes[] = {extension_misc::EXTENSION_ICON_BITTY, | |
| 212 extension_misc::EXTENSION_ICON_SMALLISH, }; | |
| 213 for (size_t i = 0; i < arraysize(sizes); ++i) { | |
| 214 ExtensionResource resource = extensions::IconsInfo::GetIconResource( | |
| 215 extension.get(), sizes[i], ExtensionIconSet::MATCH_EXACTLY); | |
| 216 info_list.push_back(ImageLoader::ImageRepresentation( | |
| 217 resource, | |
| 218 ImageLoader::ImageRepresentation::RESIZE_WHEN_LARGER, | |
| 219 gfx::Size(sizes[i], sizes[i]), | |
| 220 ui::SCALE_FACTOR_NONE)); | |
| 221 } | |
| 222 | |
| 223 ImageLoader loader; | |
| 224 loader.LoadImagesAsync(extension.get(), info_list, | |
| 225 base::Bind(&ImageLoaderTest::OnImageLoaded, | |
| 226 base::Unretained(this))); | |
| 227 | |
| 228 // The image isn't cached, so we should not have received notification. | |
| 229 EXPECT_EQ(0, image_loaded_count()); | |
| 230 | |
| 231 WaitForImageLoad(); | |
| 232 | |
| 233 // We should have gotten the image. | |
| 234 EXPECT_EQ(1, image_loaded_count()); | |
| 235 | |
| 236 // Check that all images were loaded. | |
| 237 std::vector<gfx::ImageSkiaRep> image_reps = | |
| 238 image_.ToImageSkia()->image_reps(); | |
| 239 ASSERT_EQ(2u, image_reps.size()); | |
| 240 | |
| 241 const gfx::ImageSkiaRep* img_rep1 = &image_reps[0]; | |
| 242 const gfx::ImageSkiaRep* img_rep2 = &image_reps[1]; | |
| 243 EXPECT_EQ(extension_misc::EXTENSION_ICON_BITTY, | |
| 244 img_rep1->pixel_width()); | |
| 245 EXPECT_EQ(extension_misc::EXTENSION_ICON_SMALLISH, | |
| 246 img_rep2->pixel_width()); | |
| 247 } | |
| 248 | |
| 249 // Tests loading multiple dimensions of the same image into an image family. | |
| 250 TEST_F(ImageLoaderTest, LoadImageFamily) { | |
| 251 scoped_refptr<Extension> extension( | |
| 252 CreateExtension("image_loading_tracker", Manifest::INVALID_LOCATION)); | |
| 253 ASSERT_TRUE(extension.get() != NULL); | |
| 254 | |
| 255 std::vector<ImageLoader::ImageRepresentation> info_list; | |
| 256 int sizes[] = {extension_misc::EXTENSION_ICON_BITTY, | |
| 257 extension_misc::EXTENSION_ICON_SMALLISH, }; | |
| 258 for (size_t i = 0; i < arraysize(sizes); ++i) { | |
| 259 ExtensionResource resource = extensions::IconsInfo::GetIconResource( | |
| 260 extension.get(), sizes[i], ExtensionIconSet::MATCH_EXACTLY); | |
| 261 info_list.push_back(ImageLoader::ImageRepresentation( | |
| 262 resource, | |
| 263 ImageLoader::ImageRepresentation::NEVER_RESIZE, | |
| 264 gfx::Size(sizes[i], sizes[i]), | |
| 265 ui::SCALE_FACTOR_100P)); | |
| 266 } | |
| 267 | |
| 268 // Add a second icon of 200P which should get grouped with the smaller icon's | |
| 269 // ImageSkia. | |
| 270 ExtensionResource resource = extensions::IconsInfo::GetIconResource( | |
| 271 extension.get(), | |
| 272 extension_misc::EXTENSION_ICON_SMALLISH, | |
| 273 ExtensionIconSet::MATCH_EXACTLY); | |
| 274 info_list.push_back(ImageLoader::ImageRepresentation( | |
| 275 resource, | |
| 276 ImageLoader::ImageRepresentation::NEVER_RESIZE, | |
| 277 gfx::Size(extension_misc::EXTENSION_ICON_BITTY, | |
| 278 extension_misc::EXTENSION_ICON_BITTY), | |
| 279 ui::SCALE_FACTOR_200P)); | |
| 280 | |
| 281 ImageLoader loader; | |
| 282 loader.LoadImageFamilyAsync(extension.get(), | |
| 283 info_list, | |
| 284 base::Bind(&ImageLoaderTest::OnImageFamilyLoaded, | |
| 285 base::Unretained(this))); | |
| 286 | |
| 287 // The image isn't cached, so we should not have received notification. | |
| 288 EXPECT_EQ(0, image_loaded_count()); | |
| 289 | |
| 290 WaitForImageLoad(); | |
| 291 | |
| 292 // We should have gotten the image. | |
| 293 EXPECT_EQ(1, image_loaded_count()); | |
| 294 | |
| 295 // Check that all images were loaded. | |
| 296 for (size_t i = 0; i < arraysize(sizes); ++i) { | |
| 297 const gfx::Image* image = image_family_.GetBest(sizes[i], sizes[i]); | |
| 298 EXPECT_EQ(sizes[i], image->Width()); | |
| 299 } | |
| 300 | |
| 301 // Check the smaller image has 2 representations of different scale factors. | |
| 302 std::vector<gfx::ImageSkiaRep> image_reps = | |
| 303 image_family_.GetBest(extension_misc::EXTENSION_ICON_BITTY, | |
| 304 extension_misc::EXTENSION_ICON_BITTY) | |
| 305 ->ToImageSkia() | |
| 306 ->image_reps(); | |
| 307 | |
| 308 ASSERT_EQ(2u, image_reps.size()); | |
| 309 | |
| 310 const gfx::ImageSkiaRep* img_rep1 = &image_reps[0]; | |
| 311 const gfx::ImageSkiaRep* img_rep2 = &image_reps[1]; | |
| 312 EXPECT_EQ(extension_misc::EXTENSION_ICON_BITTY, img_rep1->pixel_width()); | |
| 313 EXPECT_EQ(1.0f, img_rep1->scale()); | |
| 314 EXPECT_EQ(extension_misc::EXTENSION_ICON_SMALLISH, img_rep2->pixel_width()); | |
| 315 EXPECT_EQ(2.0f, img_rep2->scale()); | |
| 316 } | |
| 317 | |
| 318 // Tests IsComponentExtensionResource function. | |
| 319 TEST_F(ImageLoaderTest, IsComponentExtensionResource) { | |
| 320 scoped_refptr<Extension> extension(CreateExtension( | |
| 321 "file_manager", Manifest::COMPONENT)); | |
| 322 ASSERT_TRUE(extension.get() != NULL); | |
| 323 | |
| 324 ExtensionResource resource = extensions::IconsInfo::GetIconResource( | |
| 325 extension.get(), | |
| 326 extension_misc::EXTENSION_ICON_BITTY, | |
| 327 ExtensionIconSet::MATCH_EXACTLY); | |
| 328 | |
| 329 #if defined(OS_CHROMEOS) | |
| 330 int resource_id; | |
| 331 ASSERT_EQ(true, | |
| 332 ImageLoader::IsComponentExtensionResource(extension->path(), | |
| 333 resource.relative_path(), | |
| 334 &resource_id)); | |
| 335 ASSERT_EQ(IDR_FILE_MANAGER_ICON_16, resource_id); | |
| 336 #endif | |
| 337 } | |
| OLD | NEW |