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.
5 #include "extensions/browser/image_loader.h"
7 #include "base/files/file_path.h"
8 #include "base/json/json_file_value_serializer.h"
9 #include "base/message_loop/message_loop.h"
10 #include "base/path_service.h"
11 #include "base/strings/string_util.h"
12 #include "content/public/browser/notification_service.h"
13 #include "content/public/test/test_browser_context.h"
14 #include "content/public/test/test_browser_thread.h"
15 #include "extensions/browser/extension_registry.h"
16 #include "extensions/browser/extensions_browser_client.h"
17 #include "extensions/browser/extensions_test.h"
18 #include "extensions/common/constants.h"
19 #include "extensions/common/extension.h"
20 #include "extensions/common/extension_icon_set.h"
21 #include "extensions/common/extension_paths.h"
22 #include "extensions/common/extension_resource.h"
23 #include "extensions/common/manifest.h"
24 #include "extensions/common/manifest_handlers/icons_handler.h"
25 #include "testing/gtest/include/gtest/gtest.h"
26 #include "third_party/skia/include/core/SkBitmap.h"
27 #include "ui/gfx/geometry/size.h"
28 #include "ui/gfx/image/image.h"
29 #include "ui/gfx/image/image_family.h"
30 #include "ui/gfx/image/image_skia.h"
32 using content::BrowserThread
;
33 using content::NotificationService
;
35 namespace extensions
{
37 class ImageLoaderTest
: public ExtensionsTest
{
40 : image_loaded_count_(0),
41 quit_in_image_loaded_(false),
42 ui_thread_(BrowserThread::UI
, &ui_loop_
),
43 file_thread_(BrowserThread::FILE),
44 io_thread_(BrowserThread::IO
),
45 notification_service_(NotificationService::Create()) {}
47 void OnImageLoaded(const gfx::Image
& image
) {
48 image_loaded_count_
++;
49 if (quit_in_image_loaded_
)
50 base::MessageLoop::current()->Quit();
54 void OnImageFamilyLoaded(const gfx::ImageFamily
& image_family
) {
55 image_loaded_count_
++;
56 if (quit_in_image_loaded_
)
57 base::MessageLoop::current()->Quit();
58 image_family_
= image_family
;
61 void WaitForImageLoad() {
62 quit_in_image_loaded_
= true;
63 base::MessageLoop::current()->Run();
64 quit_in_image_loaded_
= false;
67 int image_loaded_count() {
68 int result
= image_loaded_count_
;
69 image_loaded_count_
= 0;
73 scoped_refptr
<Extension
> CreateExtension(const char* dir_name
,
74 Manifest::Location location
) {
75 // Create and load an extension.
76 base::FilePath extension_dir
;
77 if (!PathService::Get(DIR_TEST_DATA
, &extension_dir
)) {
81 extension_dir
= extension_dir
.AppendASCII(dir_name
);
84 JSONFileValueDeserializer
deserializer(
85 extension_dir
.AppendASCII("manifest.json"));
86 scoped_ptr
<base::DictionaryValue
> valid_value(
87 static_cast<base::DictionaryValue
*>(
88 deserializer
.Deserialize(&error_code
, &error
)));
89 EXPECT_EQ(0, error_code
) << error
;
93 EXPECT_TRUE(valid_value
.get());
97 return Extension::Create(
98 extension_dir
, location
, *valid_value
, Extension::NO_FLAGS
, &error
);
102 gfx::ImageFamily image_family_
;
105 void SetUp() override
{
106 testing::Test::SetUp();
107 file_thread_
.Start();
111 int image_loaded_count_
;
112 bool quit_in_image_loaded_
;
113 base::MessageLoop ui_loop_
;
114 content::TestBrowserThread ui_thread_
;
115 content::TestBrowserThread file_thread_
;
116 content::TestBrowserThread io_thread_
;
117 scoped_ptr
<NotificationService
> notification_service_
;
120 // Tests loading an image works correctly.
121 TEST_F(ImageLoaderTest
, LoadImage
) {
122 scoped_refptr
<Extension
> extension(
123 CreateExtension("image_loader", Manifest::INVALID_LOCATION
));
124 ASSERT_TRUE(extension
.get() != NULL
);
126 ExtensionResource image_resource
=
127 IconsInfo::GetIconResource(extension
.get(),
128 extension_misc::EXTENSION_ICON_SMALLISH
,
129 ExtensionIconSet::MATCH_EXACTLY
);
130 gfx::Size
max_size(extension_misc::EXTENSION_ICON_SMALLISH
,
131 extension_misc::EXTENSION_ICON_SMALLISH
);
133 loader
.LoadImageAsync(extension
.get(),
136 base::Bind(&ImageLoaderTest::OnImageLoaded
,
137 base::Unretained(this)));
139 // The image isn't cached, so we should not have received notification.
140 EXPECT_EQ(0, image_loaded_count());
144 // We should have gotten the image.
145 EXPECT_FALSE(image_
.IsEmpty());
146 EXPECT_EQ(1, image_loaded_count());
148 // Check that the image was loaded.
149 EXPECT_EQ(extension_misc::EXTENSION_ICON_SMALLISH
,
150 image_
.ToSkBitmap()->width());
153 // Tests deleting an extension while waiting for the image to load doesn't cause
155 TEST_F(ImageLoaderTest
, DeleteExtensionWhileWaitingForCache
) {
156 scoped_refptr
<Extension
> extension(
157 CreateExtension("image_loader", Manifest::INVALID_LOCATION
));
158 ASSERT_TRUE(extension
.get() != NULL
);
160 ExtensionResource image_resource
=
161 IconsInfo::GetIconResource(extension
.get(),
162 extension_misc::EXTENSION_ICON_SMALLISH
,
163 ExtensionIconSet::MATCH_EXACTLY
);
164 gfx::Size
max_size(extension_misc::EXTENSION_ICON_SMALLISH
,
165 extension_misc::EXTENSION_ICON_SMALLISH
);
168 sizes
.insert(extension_misc::EXTENSION_ICON_SMALLISH
);
169 loader
.LoadImageAsync(extension
.get(),
172 base::Bind(&ImageLoaderTest::OnImageLoaded
,
173 base::Unretained(this)));
175 // The image isn't cached, so we should not have received notification.
176 EXPECT_EQ(0, image_loaded_count());
178 // Send out notification the extension was uninstalled.
179 ExtensionRegistry::Get(browser_context())->TriggerOnUnloaded(
180 extension
.get(), UnloadedExtensionInfo::REASON_UNINSTALL
);
182 // Chuck the extension, that way if anyone tries to access it we should crash
183 // or get valgrind errors.
188 // Even though we deleted the extension, we should still get the image.
189 // We should still have gotten the image.
190 EXPECT_EQ(1, image_loaded_count());
192 // Check that the image was loaded.
193 EXPECT_EQ(extension_misc::EXTENSION_ICON_SMALLISH
,
194 image_
.ToSkBitmap()->width());
197 // Tests loading multiple dimensions of the same image.
198 TEST_F(ImageLoaderTest
, MultipleImages
) {
199 scoped_refptr
<Extension
> extension(
200 CreateExtension("image_loader", Manifest::INVALID_LOCATION
));
201 ASSERT_TRUE(extension
.get() != NULL
);
203 std::vector
<ImageLoader::ImageRepresentation
> info_list
;
204 int sizes
[] = {extension_misc::EXTENSION_ICON_BITTY
,
205 extension_misc::EXTENSION_ICON_SMALLISH
, };
206 for (size_t i
= 0; i
< arraysize(sizes
); ++i
) {
207 ExtensionResource resource
= IconsInfo::GetIconResource(
208 extension
.get(), sizes
[i
], ExtensionIconSet::MATCH_EXACTLY
);
209 info_list
.push_back(ImageLoader::ImageRepresentation(
211 ImageLoader::ImageRepresentation::RESIZE_WHEN_LARGER
,
212 gfx::Size(sizes
[i
], sizes
[i
]),
213 ui::SCALE_FACTOR_NONE
));
217 loader
.LoadImagesAsync(extension
.get(), info_list
,
218 base::Bind(&ImageLoaderTest::OnImageLoaded
,
219 base::Unretained(this)));
221 // The image isn't cached, so we should not have received notification.
222 EXPECT_EQ(0, image_loaded_count());
226 // We should have gotten the image.
227 EXPECT_EQ(1, image_loaded_count());
229 // Check that all images were loaded.
230 std::vector
<gfx::ImageSkiaRep
> image_reps
=
231 image_
.ToImageSkia()->image_reps();
232 ASSERT_EQ(2u, image_reps
.size());
234 const gfx::ImageSkiaRep
* img_rep1
= &image_reps
[0];
235 const gfx::ImageSkiaRep
* img_rep2
= &image_reps
[1];
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());
242 // Tests loading multiple dimensions of the same image into an image family.
243 TEST_F(ImageLoaderTest
, LoadImageFamily
) {
244 scoped_refptr
<Extension
> extension(
245 CreateExtension("image_loader", Manifest::INVALID_LOCATION
));
246 ASSERT_TRUE(extension
.get() != NULL
);
248 std::vector
<ImageLoader::ImageRepresentation
> info_list
;
249 int sizes
[] = {extension_misc::EXTENSION_ICON_BITTY
,
250 extension_misc::EXTENSION_ICON_SMALLISH
, };
251 for (size_t i
= 0; i
< arraysize(sizes
); ++i
) {
252 ExtensionResource resource
= IconsInfo::GetIconResource(
253 extension
.get(), sizes
[i
], ExtensionIconSet::MATCH_EXACTLY
);
254 info_list
.push_back(ImageLoader::ImageRepresentation(
256 ImageLoader::ImageRepresentation::NEVER_RESIZE
,
257 gfx::Size(sizes
[i
], sizes
[i
]),
258 ui::SCALE_FACTOR_100P
));
261 // Add a second icon of 200P which should get grouped with the smaller icon's
263 ExtensionResource resource
=
264 IconsInfo::GetIconResource(extension
.get(),
265 extension_misc::EXTENSION_ICON_SMALLISH
,
266 ExtensionIconSet::MATCH_EXACTLY
);
267 info_list
.push_back(ImageLoader::ImageRepresentation(
269 ImageLoader::ImageRepresentation::NEVER_RESIZE
,
270 gfx::Size(extension_misc::EXTENSION_ICON_BITTY
,
271 extension_misc::EXTENSION_ICON_BITTY
),
272 ui::SCALE_FACTOR_200P
));
275 loader
.LoadImageFamilyAsync(extension
.get(),
277 base::Bind(&ImageLoaderTest::OnImageFamilyLoaded
,
278 base::Unretained(this)));
280 // The image isn't cached, so we should not have received notification.
281 EXPECT_EQ(0, image_loaded_count());
285 // We should have gotten the image.
286 EXPECT_EQ(1, image_loaded_count());
288 // Check that all images were loaded.
289 for (size_t i
= 0; i
< arraysize(sizes
); ++i
) {
290 const gfx::Image
* image
= image_family_
.GetBest(sizes
[i
], sizes
[i
]);
291 EXPECT_EQ(sizes
[i
], image
->Width());
294 // Check the smaller image has 2 representations of different scale factors.
295 std::vector
<gfx::ImageSkiaRep
> image_reps
=
296 image_family_
.GetBest(extension_misc::EXTENSION_ICON_BITTY
,
297 extension_misc::EXTENSION_ICON_BITTY
)
301 ASSERT_EQ(2u, image_reps
.size());
303 const gfx::ImageSkiaRep
* img_rep1
= &image_reps
[0];
304 const gfx::ImageSkiaRep
* img_rep2
= &image_reps
[1];
305 EXPECT_EQ(extension_misc::EXTENSION_ICON_BITTY
, img_rep1
->pixel_width());
306 EXPECT_EQ(1.0f
, img_rep1
->scale());
307 EXPECT_EQ(extension_misc::EXTENSION_ICON_SMALLISH
, img_rep2
->pixel_width());
308 EXPECT_EQ(2.0f
, img_rep2
->scale());
311 } // namespace extensions