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 "chrome/common/extensions/extension_constants.h" 13 #include "chrome/common/extensions/extension_icon_set.h" 14 #include "chrome/common/extensions/manifest_handlers/icons_handler.h" 15 #include "content/public/browser/notification_service.h" 16 #include "content/public/test/test_browser_thread.h" 17 #include "extensions/common/extension.h" 18 #include "extensions/common/extension_resource.h" 19 #include "extensions/common/manifest.h" 20 #include "grit/component_extension_resources.h" 21 #include "testing/gtest/include/gtest/gtest.h" 22 #include "third_party/skia/include/core/SkBitmap.h" 23 #include "ui/gfx/image/image.h" 24 #include "ui/gfx/image/image_skia.h" 25 #include "ui/gfx/size.h" 26 27 using content::BrowserThread; 28 using extensions::Extension; 29 using extensions::ExtensionResource; 30 using extensions::ImageLoader; 31 using extensions::Manifest; 32 using extensions::UnloadedExtensionInfo; 33 34 class ImageLoaderTest : public testing::Test { 35 public: 36 ImageLoaderTest() 37 : image_loaded_count_(0), 38 quit_in_image_loaded_(false), 39 ui_thread_(BrowserThread::UI, &ui_loop_), 40 file_thread_(BrowserThread::FILE), 41 io_thread_(BrowserThread::IO) { 42 } 43 44 void OnImageLoaded(const gfx::Image& image) { 45 image_loaded_count_++; 46 if (quit_in_image_loaded_) 47 base::MessageLoop::current()->Quit(); 48 image_ = image; 49 } 50 51 void WaitForImageLoad() { 52 quit_in_image_loaded_ = true; 53 base::MessageLoop::current()->Run(); 54 quit_in_image_loaded_ = false; 55 } 56 57 int image_loaded_count() { 58 int result = image_loaded_count_; 59 image_loaded_count_ = 0; 60 return result; 61 } 62 63 scoped_refptr<Extension> CreateExtension(const char* name, 64 Manifest::Location location) { 65 // Create and load an extension. 66 base::FilePath test_file; 67 if (!PathService::Get(chrome::DIR_TEST_DATA, &test_file)) { 68 EXPECT_FALSE(true); 69 return NULL; 70 } 71 test_file = test_file.AppendASCII("extensions") 72 .AppendASCII(name); 73 int error_code = 0; 74 std::string error; 75 JSONFileValueSerializer serializer(test_file.AppendASCII("app.json")); 76 scoped_ptr<DictionaryValue> valid_value( 77 static_cast<DictionaryValue*>(serializer.Deserialize(&error_code, 78 &error))); 79 EXPECT_EQ(0, error_code) << error; 80 if (error_code != 0) 81 return NULL; 82 83 EXPECT_TRUE(valid_value.get()); 84 if (!valid_value) 85 return NULL; 86 87 if (location == Manifest::COMPONENT) { 88 if (!PathService::Get(chrome::DIR_RESOURCES, &test_file)) { 89 EXPECT_FALSE(true); 90 return NULL; 91 } 92 test_file = test_file.AppendASCII(name); 93 } 94 return Extension::Create(test_file, location, *valid_value, 95 Extension::NO_FLAGS, &error); 96 } 97 98 gfx::Image image_; 99 100 private: 101 virtual void SetUp() OVERRIDE { 102 testing::Test::SetUp(); 103 file_thread_.Start(); 104 io_thread_.Start(); 105 } 106 107 int image_loaded_count_; 108 bool quit_in_image_loaded_; 109 base::MessageLoop ui_loop_; 110 content::TestBrowserThread ui_thread_; 111 content::TestBrowserThread file_thread_; 112 content::TestBrowserThread io_thread_; 113 }; 114 115 // Tests loading an image works correctly. 116 TEST_F(ImageLoaderTest, LoadImage) { 117 scoped_refptr<Extension> extension(CreateExtension( 118 "image_loading_tracker", Manifest::INVALID_LOCATION)); 119 ASSERT_TRUE(extension.get() != NULL); 120 121 ExtensionResource image_resource = extensions::IconsInfo::GetIconResource( 122 extension.get(), 123 extension_misc::EXTENSION_ICON_SMALLISH, 124 ExtensionIconSet::MATCH_EXACTLY); 125 gfx::Size max_size(extension_misc::EXTENSION_ICON_SMALLISH, 126 extension_misc::EXTENSION_ICON_SMALLISH); 127 ImageLoader loader; 128 loader.LoadImageAsync(extension.get(), 129 image_resource, 130 max_size, 131 base::Bind(&ImageLoaderTest::OnImageLoaded, 132 base::Unretained(this))); 133 134 // The image isn't cached, so we should not have received notification. 135 EXPECT_EQ(0, image_loaded_count()); 136 137 WaitForImageLoad(); 138 139 // We should have gotten the image. 140 EXPECT_EQ(1, image_loaded_count()); 141 142 // Check that the image was loaded. 143 EXPECT_EQ(extension_misc::EXTENSION_ICON_SMALLISH, 144 image_.ToSkBitmap()->width()); 145 } 146 147 // Tests deleting an extension while waiting for the image to load doesn't cause 148 // problems. 149 TEST_F(ImageLoaderTest, DeleteExtensionWhileWaitingForCache) { 150 scoped_refptr<Extension> extension(CreateExtension( 151 "image_loading_tracker", Manifest::INVALID_LOCATION)); 152 ASSERT_TRUE(extension.get() != NULL); 153 154 ExtensionResource image_resource = extensions::IconsInfo::GetIconResource( 155 extension.get(), 156 extension_misc::EXTENSION_ICON_SMALLISH, 157 ExtensionIconSet::MATCH_EXACTLY); 158 gfx::Size max_size(extension_misc::EXTENSION_ICON_SMALLISH, 159 extension_misc::EXTENSION_ICON_SMALLISH); 160 ImageLoader loader; 161 std::set<int> sizes; 162 sizes.insert(extension_misc::EXTENSION_ICON_SMALLISH); 163 loader.LoadImageAsync(extension.get(), 164 image_resource, 165 max_size, 166 base::Bind(&ImageLoaderTest::OnImageLoaded, 167 base::Unretained(this))); 168 169 // The image isn't cached, so we should not have received notification. 170 EXPECT_EQ(0, image_loaded_count()); 171 172 // Send out notification the extension was uninstalled. 173 UnloadedExtensionInfo details(extension.get(), 174 UnloadedExtensionInfo::REASON_UNINSTALL); 175 content::NotificationService::current()->Notify( 176 chrome::NOTIFICATION_EXTENSION_UNLOADED, 177 content::NotificationService::AllSources(), 178 content::Details<UnloadedExtensionInfo>(&details)); 179 180 // Chuck the extension, that way if anyone tries to access it we should crash 181 // or get valgrind errors. 182 extension = NULL; 183 184 WaitForImageLoad(); 185 186 // Even though we deleted the extension, we should still get the image. 187 // We should still have gotten the image. 188 EXPECT_EQ(1, image_loaded_count()); 189 190 // Check that the image was loaded. 191 EXPECT_EQ(extension_misc::EXTENSION_ICON_SMALLISH, 192 image_.ToSkBitmap()->width()); 193 } 194 195 // Tests loading multiple dimensions of the same image. 196 TEST_F(ImageLoaderTest, MultipleImages) { 197 scoped_refptr<Extension> extension(CreateExtension( 198 "image_loading_tracker", Manifest::INVALID_LOCATION)); 199 ASSERT_TRUE(extension.get() != NULL); 200 201 std::vector<ImageLoader::ImageRepresentation> info_list; 202 int sizes[] = {extension_misc::EXTENSION_ICON_SMALLISH, 203 extension_misc::EXTENSION_ICON_BITTY}; 204 for (size_t i = 0; i < arraysize(sizes); ++i) { 205 ExtensionResource resource = extensions::IconsInfo::GetIconResource( 206 extension.get(), sizes[i], ExtensionIconSet::MATCH_EXACTLY); 207 info_list.push_back(ImageLoader::ImageRepresentation( 208 resource, 209 ImageLoader::ImageRepresentation::RESIZE_WHEN_LARGER, 210 gfx::Size(sizes[i], sizes[i]), 211 ui::SCALE_FACTOR_NONE)); 212 } 213 214 ImageLoader loader; 215 loader.LoadImagesAsync(extension.get(), info_list, 216 base::Bind(&ImageLoaderTest::OnImageLoaded, 217 base::Unretained(this))); 218 219 // The image isn't cached, so we should not have received notification. 220 EXPECT_EQ(0, image_loaded_count()); 221 222 WaitForImageLoad(); 223 224 // We should have gotten the image. 225 EXPECT_EQ(1, image_loaded_count()); 226 227 // Check that all images were loaded. 228 std::vector<gfx::ImageSkiaRep> image_reps = 229 image_.ToImageSkia()->image_reps(); 230 ASSERT_EQ(2u, image_reps.size()); 231 const gfx::ImageSkiaRep* img_rep1 = &image_reps[0]; 232 const gfx::ImageSkiaRep* img_rep2 = &image_reps[1]; 233 if (img_rep1->pixel_width() > img_rep2->pixel_width()) { 234 std::swap(img_rep1, img_rep2); 235 } 236 EXPECT_EQ(extension_misc::EXTENSION_ICON_BITTY, 237 img_rep1->pixel_width()); 238 EXPECT_EQ(extension_misc::EXTENSION_ICON_SMALLISH, 239 img_rep2->pixel_width()); 240 } 241 242 // Tests IsComponentExtensionResource function. 243 TEST_F(ImageLoaderTest, IsComponentExtensionResource) { 244 scoped_refptr<Extension> extension(CreateExtension( 245 "file_manager", Manifest::COMPONENT)); 246 ASSERT_TRUE(extension.get() != NULL); 247 248 ExtensionResource resource = extensions::IconsInfo::GetIconResource( 249 extension.get(), 250 extension_misc::EXTENSION_ICON_BITTY, 251 ExtensionIconSet::MATCH_EXACTLY); 252 253 #if defined(FILE_MANAGER_EXTENSION) 254 int resource_id; 255 ASSERT_EQ(true, 256 ImageLoader::IsComponentExtensionResource(extension->path(), 257 resource.relative_path(), 258 &resource_id)); 259 ASSERT_EQ(IDR_FILE_MANAGER_ICON_16, resource_id); 260 #endif 261 } 262