Update V8 to version 4.7.16.
[chromium-blink-merge.git] / extensions / browser / extension_icon_image_unittest.cc
blob29bb1ff207c59f60cda97e2e5d4105e9eafd7143
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/extension_icon_image.h"
7 #include <vector>
9 #include "base/json/json_file_value_serializer.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/path_service.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/extensions_test.h"
16 #include "extensions/browser/image_loader.h"
17 #include "extensions/browser/test_image_loader.h"
18 #include "extensions/common/extension.h"
19 #include "extensions/common/extension_paths.h"
20 #include "extensions/common/manifest.h"
21 #include "extensions/common/manifest_handlers/icons_handler.h"
22 #include "skia/ext/image_operations.h"
23 #include "testing/gtest/include/gtest/gtest.h"
24 #include "ui/base/resource/resource_bundle.h"
25 #include "ui/gfx/image/image_skia_source.h"
26 #include "ui/gfx/skia_util.h"
28 using content::BrowserThread;
30 namespace extensions {
31 namespace {
33 SkBitmap CreateBlankBitmapForScale(int size_dip, ui::ScaleFactor scale_factor) {
34 SkBitmap bitmap;
35 const float scale = ui::GetScaleForScaleFactor(scale_factor);
36 bitmap.allocN32Pixels(static_cast<int>(size_dip * scale),
37 static_cast<int>(size_dip * scale));
38 bitmap.eraseColor(SkColorSetARGB(0, 0, 0, 0));
39 return bitmap;
42 SkBitmap EnsureBitmapSize(const SkBitmap& original, int size) {
43 if (original.width() == size && original.height() == size)
44 return original;
46 SkBitmap resized = skia::ImageOperations::Resize(
47 original, skia::ImageOperations::RESIZE_LANCZOS3, size, size);
48 return resized;
51 // Used to test behavior including images defined by an image skia source.
52 // |GetImageForScale| simply returns image representation from the image given
53 // in the ctor.
54 class MockImageSkiaSource : public gfx::ImageSkiaSource {
55 public:
56 explicit MockImageSkiaSource(const gfx::ImageSkia& image)
57 : image_(image) {
59 ~MockImageSkiaSource() override {}
61 gfx::ImageSkiaRep GetImageForScale(float scale) override {
62 return image_.GetRepresentation(scale);
65 private:
66 gfx::ImageSkia image_;
69 class ExtensionIconImageTest : public ExtensionsTest,
70 public IconImage::Observer {
71 public:
72 ExtensionIconImageTest()
73 : image_loaded_count_(0),
74 quit_in_image_loaded_(false),
75 ui_thread_(BrowserThread::UI, &ui_loop_),
76 file_thread_(BrowserThread::FILE),
77 io_thread_(BrowserThread::IO),
78 notification_service_(content::NotificationService::Create()) {}
80 ~ExtensionIconImageTest() override {}
82 void WaitForImageLoad() {
83 quit_in_image_loaded_ = true;
84 base::MessageLoop::current()->Run();
85 quit_in_image_loaded_ = false;
88 int ImageLoadedCount() {
89 int result = image_loaded_count_;
90 image_loaded_count_ = 0;
91 return result;
94 scoped_refptr<Extension> CreateExtension(const char* name,
95 Manifest::Location location) {
96 // Create and load an extension.
97 base::FilePath test_file;
98 if (!PathService::Get(DIR_TEST_DATA, &test_file)) {
99 EXPECT_FALSE(true);
100 return NULL;
102 test_file = test_file.AppendASCII(name);
103 int error_code = 0;
104 std::string error;
105 JSONFileValueDeserializer deserializer(
106 test_file.AppendASCII("manifest.json"));
107 scoped_ptr<base::DictionaryValue> valid_value(
108 static_cast<base::DictionaryValue*>(
109 deserializer.Deserialize(&error_code, &error)));
110 EXPECT_EQ(0, error_code) << error;
111 if (error_code != 0)
112 return NULL;
114 EXPECT_TRUE(valid_value.get());
115 if (!valid_value)
116 return NULL;
118 return Extension::Create(test_file, location, *valid_value,
119 Extension::NO_FLAGS, &error);
122 // testing::Test overrides:
123 void SetUp() override {
124 file_thread_.Start();
125 io_thread_.Start();
128 // IconImage::Delegate overrides:
129 void OnExtensionIconImageChanged(IconImage* image) override {
130 image_loaded_count_++;
131 if (quit_in_image_loaded_)
132 base::MessageLoop::current()->Quit();
135 gfx::ImageSkia GetDefaultIcon() {
136 return gfx::ImageSkia(gfx::ImageSkiaRep(gfx::Size(16, 16), 1.0f));
139 private:
140 int image_loaded_count_;
141 bool quit_in_image_loaded_;
142 base::MessageLoop ui_loop_;
143 content::TestBrowserThread ui_thread_;
144 content::TestBrowserThread file_thread_;
145 content::TestBrowserThread io_thread_;
146 scoped_ptr<content::NotificationService> notification_service_;
148 DISALLOW_COPY_AND_ASSIGN(ExtensionIconImageTest);
151 } // namespace
153 TEST_F(ExtensionIconImageTest, Basic) {
154 std::vector<ui::ScaleFactor> supported_factors;
155 supported_factors.push_back(ui::SCALE_FACTOR_100P);
156 supported_factors.push_back(ui::SCALE_FACTOR_200P);
157 ui::test::ScopedSetSupportedScaleFactors scoped_supported(supported_factors);
158 scoped_refptr<Extension> extension(CreateExtension(
159 "extension_icon_image", Manifest::INVALID_LOCATION));
160 ASSERT_TRUE(extension.get() != NULL);
162 gfx::ImageSkia default_icon = GetDefaultIcon();
164 // Load images we expect to find as representations in icon_image, so we
165 // can later use them to validate icon_image.
166 SkBitmap bitmap_16 =
167 TestImageLoader::LoadAndGetExtensionBitmap(extension.get(), "16.png", 16);
168 ASSERT_FALSE(bitmap_16.empty());
170 // There is no image of size 32 defined in the extension manifest, so we
171 // should expect manifest image of size 48 resized to size 32.
172 SkBitmap bitmap_48_resized_to_32 =
173 TestImageLoader::LoadAndGetExtensionBitmap(extension.get(), "48.png", 32);
174 ASSERT_FALSE(bitmap_48_resized_to_32.empty());
176 IconImage image(browser_context(),
177 extension.get(),
178 IconsInfo::GetIcons(extension.get()),
180 default_icon,
181 this);
183 // No representations in |image_| yet.
184 gfx::ImageSkia::ImageSkiaReps image_reps = image.image_skia().image_reps();
185 ASSERT_EQ(0u, image_reps.size());
187 // Gets representation for a scale factor.
188 gfx::ImageSkiaRep representation = image.image_skia().GetRepresentation(1.0f);
190 // Before the image representation is loaded, image should contain blank
191 // image representation.
192 EXPECT_TRUE(gfx::BitmapsAreEqual(
193 representation.sk_bitmap(),
194 CreateBlankBitmapForScale(16, ui::SCALE_FACTOR_100P)));
196 WaitForImageLoad();
197 EXPECT_EQ(1, ImageLoadedCount());
198 ASSERT_EQ(1u, image.image_skia().image_reps().size());
200 representation = image.image_skia().GetRepresentation(1.0f);
202 // We should get the right representation now.
203 EXPECT_TRUE(gfx::BitmapsAreEqual(representation.sk_bitmap(), bitmap_16));
204 EXPECT_EQ(16, representation.pixel_width());
206 // Gets representation for an additional scale factor.
207 representation = image.image_skia().GetRepresentation(2.0f);
209 EXPECT_TRUE(gfx::BitmapsAreEqual(
210 representation.sk_bitmap(),
211 CreateBlankBitmapForScale(16, ui::SCALE_FACTOR_200P)));
213 WaitForImageLoad();
214 EXPECT_EQ(1, ImageLoadedCount());
215 ASSERT_EQ(2u, image.image_skia().image_reps().size());
217 representation = image.image_skia().GetRepresentation(2.0f);
219 // Image should have been resized.
220 EXPECT_EQ(32, representation.pixel_width());
221 EXPECT_TRUE(gfx::BitmapsAreEqual(representation.sk_bitmap(),
222 bitmap_48_resized_to_32));
225 // There is no resource with either exact or bigger size, but there is a smaller
226 // resource.
227 TEST_F(ExtensionIconImageTest, FallbackToSmallerWhenNoBigger) {
228 std::vector<ui::ScaleFactor> supported_factors;
229 supported_factors.push_back(ui::SCALE_FACTOR_100P);
230 supported_factors.push_back(ui::SCALE_FACTOR_200P);
231 ui::test::ScopedSetSupportedScaleFactors scoped_supported(supported_factors);
232 scoped_refptr<Extension> extension(CreateExtension(
233 "extension_icon_image", Manifest::INVALID_LOCATION));
234 ASSERT_TRUE(extension.get() != NULL);
236 gfx::ImageSkia default_icon = GetDefaultIcon();
238 // Load images we expect to find as representations in icon_image, so we
239 // can later use them to validate icon_image.
240 SkBitmap bitmap_48 =
241 TestImageLoader::LoadAndGetExtensionBitmap(extension.get(), "48.png", 48);
242 ASSERT_FALSE(bitmap_48.empty());
244 IconImage image(browser_context(),
245 extension.get(),
246 IconsInfo::GetIcons(extension.get()),
248 default_icon,
249 this);
251 gfx::ImageSkiaRep representation = image.image_skia().GetRepresentation(2.0f);
253 WaitForImageLoad();
254 EXPECT_EQ(1, ImageLoadedCount());
255 ASSERT_EQ(1u, image.image_skia().image_reps().size());
257 representation = image.image_skia().GetRepresentation(2.0f);
259 // We should have loaded the biggest smaller resource resized to the actual
260 // size.
261 EXPECT_EQ(2.0f, representation.scale());
262 EXPECT_EQ(64, representation.pixel_width());
263 EXPECT_TRUE(gfx::BitmapsAreEqual(representation.sk_bitmap(),
264 EnsureBitmapSize(bitmap_48, 64)));
267 // There is no resource with exact size, but there is a smaller and a bigger
268 // one. The bigger resource should be loaded.
269 TEST_F(ExtensionIconImageTest, FallbackToBigger) {
270 scoped_refptr<Extension> extension(CreateExtension(
271 "extension_icon_image", Manifest::INVALID_LOCATION));
272 ASSERT_TRUE(extension.get() != NULL);
274 gfx::ImageSkia default_icon = GetDefaultIcon();
276 // Load images we expect to find as representations in icon_image, so we
277 // can later use them to validate icon_image.
278 SkBitmap bitmap_24 =
279 TestImageLoader::LoadAndGetExtensionBitmap(extension.get(), "24.png", 24);
280 ASSERT_FALSE(bitmap_24.empty());
282 IconImage image(browser_context(),
283 extension.get(),
284 IconsInfo::GetIcons(extension.get()),
286 default_icon,
287 this);
289 gfx::ImageSkiaRep representation = image.image_skia().GetRepresentation(1.0f);
291 WaitForImageLoad();
292 EXPECT_EQ(1, ImageLoadedCount());
293 ASSERT_EQ(1u, image.image_skia().image_reps().size());
295 representation = image.image_skia().GetRepresentation(1.0f);
297 // We should have loaded the smallest bigger (resized) resource.
298 EXPECT_EQ(1.0f, representation.scale());
299 EXPECT_EQ(17, representation.pixel_width());
300 EXPECT_TRUE(gfx::BitmapsAreEqual(representation.sk_bitmap(),
301 EnsureBitmapSize(bitmap_24, 17)));
304 // If resource set is empty, |GetRepresentation| should synchronously return
305 // default icon, without notifying observer of image change.
306 TEST_F(ExtensionIconImageTest, NoResources) {
307 scoped_refptr<Extension> extension(CreateExtension(
308 "extension_icon_image", Manifest::INVALID_LOCATION));
309 ASSERT_TRUE(extension.get() != NULL);
311 ExtensionIconSet empty_icon_set;
312 gfx::ImageSkia default_icon = GetDefaultIcon();
314 const int kRequestedSize = 24;
315 IconImage image(browser_context(),
316 extension.get(),
317 empty_icon_set,
318 kRequestedSize,
319 default_icon,
320 this);
322 gfx::ImageSkiaRep representation = image.image_skia().GetRepresentation(1.0f);
323 EXPECT_TRUE(gfx::BitmapsAreEqual(
324 representation.sk_bitmap(),
325 EnsureBitmapSize(
326 default_icon.GetRepresentation(1.0f).sk_bitmap(),
327 kRequestedSize)));
329 EXPECT_EQ(0, ImageLoadedCount());
330 // We should have a default icon representation.
331 ASSERT_EQ(1u, image.image_skia().image_reps().size());
333 representation = image.image_skia().GetRepresentation(1.0f);
334 EXPECT_TRUE(gfx::BitmapsAreEqual(
335 representation.sk_bitmap(),
336 EnsureBitmapSize(
337 default_icon.GetRepresentation(1.0f).sk_bitmap(),
338 kRequestedSize)));
341 // If resource set is invalid, image load should be done asynchronously and
342 // the observer should be notified when it's done. |GetRepresentation| should
343 // return the default icon representation once image load is done.
344 TEST_F(ExtensionIconImageTest, InvalidResource) {
345 scoped_refptr<Extension> extension(CreateExtension(
346 "extension_icon_image", Manifest::INVALID_LOCATION));
347 ASSERT_TRUE(extension.get() != NULL);
349 const int kInvalidIconSize = 24;
350 ExtensionIconSet invalid_icon_set;
351 invalid_icon_set.Add(kInvalidIconSize, "invalid.png");
353 gfx::ImageSkia default_icon = GetDefaultIcon();
355 IconImage image(browser_context(),
356 extension.get(),
357 invalid_icon_set,
358 kInvalidIconSize,
359 default_icon,
360 this);
362 gfx::ImageSkiaRep representation = image.image_skia().GetRepresentation(1.0f);
363 EXPECT_TRUE(gfx::BitmapsAreEqual(
364 representation.sk_bitmap(),
365 CreateBlankBitmapForScale(kInvalidIconSize, ui::SCALE_FACTOR_100P)));
367 WaitForImageLoad();
368 EXPECT_EQ(1, ImageLoadedCount());
369 // We should have default icon representation now.
370 ASSERT_EQ(1u, image.image_skia().image_reps().size());
372 representation = image.image_skia().GetRepresentation(1.0f);
373 EXPECT_TRUE(gfx::BitmapsAreEqual(
374 representation.sk_bitmap(),
375 EnsureBitmapSize(
376 default_icon.GetRepresentation(1.0f).sk_bitmap(),
377 kInvalidIconSize)));
380 // Test that IconImage works with lazily (but synchronously) created default
381 // icon when IconImage returns synchronously.
382 TEST_F(ExtensionIconImageTest, LazyDefaultIcon) {
383 scoped_refptr<Extension> extension(CreateExtension(
384 "extension_icon_image", Manifest::INVALID_LOCATION));
385 ASSERT_TRUE(extension.get() != NULL);
387 gfx::ImageSkia default_icon = GetDefaultIcon();
388 gfx::ImageSkia lazy_default_icon(new MockImageSkiaSource(default_icon),
389 default_icon.size());
391 ExtensionIconSet empty_icon_set;
393 const int kRequestedSize = 128;
394 IconImage image(browser_context(),
395 extension.get(),
396 empty_icon_set,
397 kRequestedSize,
398 lazy_default_icon,
399 this);
401 ASSERT_FALSE(lazy_default_icon.HasRepresentation(1.0f));
403 gfx::ImageSkiaRep representation = image.image_skia().GetRepresentation(1.0f);
405 // The resouce set is empty, so we should get the result right away.
406 EXPECT_TRUE(lazy_default_icon.HasRepresentation(1.0f));
407 EXPECT_TRUE(gfx::BitmapsAreEqual(
408 representation.sk_bitmap(),
409 EnsureBitmapSize(
410 default_icon.GetRepresentation(1.0f).sk_bitmap(),
411 kRequestedSize)));
413 // We should have a default icon representation.
414 ASSERT_EQ(1u, image.image_skia().image_reps().size());
417 // Test that IconImage works with lazily (but synchronously) created default
418 // icon when IconImage returns asynchronously.
419 TEST_F(ExtensionIconImageTest, LazyDefaultIcon_AsyncIconImage) {
420 scoped_refptr<Extension> extension(CreateExtension(
421 "extension_icon_image", Manifest::INVALID_LOCATION));
422 ASSERT_TRUE(extension.get() != NULL);
424 gfx::ImageSkia default_icon = GetDefaultIcon();
425 gfx::ImageSkia lazy_default_icon(new MockImageSkiaSource(default_icon),
426 default_icon.size());
428 const int kInvalidIconSize = 24;
429 ExtensionIconSet invalid_icon_set;
430 invalid_icon_set.Add(kInvalidIconSize, "invalid.png");
432 IconImage image(browser_context(),
433 extension.get(),
434 invalid_icon_set,
435 kInvalidIconSize,
436 lazy_default_icon,
437 this);
439 ASSERT_FALSE(lazy_default_icon.HasRepresentation(1.0f));
441 gfx::ImageSkiaRep representation = image.image_skia().GetRepresentation(1.0f);
443 WaitForImageLoad();
444 EXPECT_EQ(1, ImageLoadedCount());
445 // We should have default icon representation now.
446 ASSERT_EQ(1u, image.image_skia().image_reps().size());
448 EXPECT_TRUE(lazy_default_icon.HasRepresentation(1.0f));
450 representation = image.image_skia().GetRepresentation(1.0f);
451 EXPECT_TRUE(gfx::BitmapsAreEqual(
452 representation.sk_bitmap(),
453 EnsureBitmapSize(
454 default_icon.GetRepresentation(1.0f).sk_bitmap(),
455 kInvalidIconSize)));
458 // Tests behavior of image created by IconImage after IconImage host goes
459 // away. The image should still return loaded representations. If requested
460 // representation was not loaded while IconImage host was around, transparent
461 // representations should be returned.
462 TEST_F(ExtensionIconImageTest, IconImageDestruction) {
463 scoped_refptr<Extension> extension(CreateExtension(
464 "extension_icon_image", Manifest::INVALID_LOCATION));
465 ASSERT_TRUE(extension.get() != NULL);
467 gfx::ImageSkia default_icon = GetDefaultIcon();
469 // Load images we expect to find as representations in icon_image, so we
470 // can later use them to validate icon_image.
471 SkBitmap bitmap_16 =
472 TestImageLoader::LoadAndGetExtensionBitmap(extension.get(), "16.png", 16);
473 ASSERT_FALSE(bitmap_16.empty());
475 scoped_ptr<IconImage> image(
476 new IconImage(browser_context(),
477 extension.get(),
478 IconsInfo::GetIcons(extension.get()),
480 default_icon,
481 this));
483 // Load an image representation.
484 gfx::ImageSkiaRep representation =
485 image->image_skia().GetRepresentation(1.0f);
487 WaitForImageLoad();
488 EXPECT_EQ(1, ImageLoadedCount());
489 ASSERT_EQ(1u, image->image_skia().image_reps().size());
491 // Stash loaded image skia, and destroy |image|.
492 gfx::ImageSkia image_skia = image->image_skia();
493 image.reset();
494 extension = NULL;
496 // Image skia should still be able to get previously loaded representation.
497 representation = image_skia.GetRepresentation(1.0f);
499 EXPECT_EQ(1.0f, representation.scale());
500 EXPECT_EQ(16, representation.pixel_width());
501 EXPECT_TRUE(gfx::BitmapsAreEqual(representation.sk_bitmap(), bitmap_16));
503 // When requesting another representation, we should not crash and return some
504 // image of the size. It could be blank or a rescale from the existing 1.0f
505 // icon.
506 representation = image_skia.GetRepresentation(2.0f);
508 EXPECT_EQ(16, representation.GetWidth());
509 EXPECT_EQ(16, representation.GetHeight());
510 EXPECT_EQ(2.0f, representation.scale());
513 // Test that new representations added to the image of an IconImage are cached
514 // for future use.
515 TEST_F(ExtensionIconImageTest, ImageCachesNewRepresentations) {
516 // Load up an extension and create an icon image.
517 scoped_refptr<Extension> extension(
518 CreateExtension("extension_icon_image", Manifest::INVALID_LOCATION));
519 ASSERT_TRUE(extension.get() != NULL);
520 gfx::ImageSkia default_icon = GetDefaultIcon();
521 scoped_ptr<IconImage> icon_image(
522 new IconImage(browser_context(),
523 extension.get(),
524 IconsInfo::GetIcons(extension.get()),
526 default_icon,
527 this));
529 // Load an image representation.
530 gfx::ImageSkiaRep representation =
531 icon_image->image_skia().GetRepresentation(1.0f);
532 WaitForImageLoad();
534 // Cache for later use.
535 gfx::Image prior_image = icon_image->image();
537 // Now the fun part: access the image from the IconImage, and create a png
538 // representation of it.
539 gfx::Image image = icon_image->image();
540 scoped_refptr<base::RefCountedMemory> image_as_png = image.As1xPNGBytes();
542 // Access the image from the IconImage again, and get a png representation.
543 // The two png representations should be exactly equal (the same object),
544 // because image storage is shared, so when we created one from the first
545 // image, all other images should also have that representation...
546 gfx::Image image2 = icon_image->image();
547 EXPECT_EQ(image_as_png.get(), image2.As1xPNGBytes().get());
549 // ...even images that were copied before the representation was constructed.
550 EXPECT_EQ(image_as_png.get(), prior_image.As1xPNGBytes().get());
553 } // namespace extensions