Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / extensions / browser / extension_icon_image_unittest.cc
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 "extensions/browser/extension_icon_image.h"
6
7 #include <vector>
8
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/common/extension.h"
18 #include "extensions/common/extension_paths.h"
19 #include "extensions/common/manifest.h"
20 #include "extensions/common/manifest_handlers/icons_handler.h"
21 #include "skia/ext/image_operations.h"
22 #include "testing/gtest/include/gtest/gtest.h"
23 #include "ui/base/resource/resource_bundle.h"
24 #include "ui/gfx/image/image_skia_source.h"
25 #include "ui/gfx/skia_util.h"
26
27 using content::BrowserThread;
28
29 namespace extensions {
30 namespace {
31
32 SkBitmap CreateBlankBitmapForScale(int size_dip, ui::ScaleFactor scale_factor) {
33   SkBitmap bitmap;
34   const float scale = ui::GetScaleForScaleFactor(scale_factor);
35   bitmap.allocN32Pixels(static_cast<int>(size_dip * scale),
36                         static_cast<int>(size_dip * scale));
37   bitmap.eraseColor(SkColorSetARGB(0, 0, 0, 0));
38   return bitmap;
39 }
40
41 SkBitmap EnsureBitmapSize(const SkBitmap& original, int size) {
42   if (original.width() == size && original.height() == size)
43     return original;
44
45   SkBitmap resized = skia::ImageOperations::Resize(
46       original, skia::ImageOperations::RESIZE_LANCZOS3, size, size);
47   return resized;
48 }
49
50 // Used to test behavior including images defined by an image skia source.
51 // |GetImageForScale| simply returns image representation from the image given
52 // in the ctor.
53 class MockImageSkiaSource : public gfx::ImageSkiaSource {
54  public:
55   explicit MockImageSkiaSource(const gfx::ImageSkia& image)
56       : image_(image) {
57   }
58   ~MockImageSkiaSource() override {}
59
60   gfx::ImageSkiaRep GetImageForScale(float scale) override {
61     return image_.GetRepresentation(scale);
62   }
63
64  private:
65   gfx::ImageSkia image_;
66 };
67
68 // Helper class for synchronously loading extension image resource.
69 class TestImageLoader {
70  public:
71   explicit TestImageLoader(const Extension* extension)
72       : extension_(extension),
73         waiting_(false),
74         image_loaded_(false) {
75   }
76   virtual ~TestImageLoader() {}
77
78   void OnImageLoaded(const gfx::Image& image) {
79     image_ = image;
80     image_loaded_ = true;
81     if (waiting_)
82       base::MessageLoop::current()->Quit();
83   }
84
85   SkBitmap LoadBitmap(const std::string& path,
86                       int size) {
87     image_loaded_ = false;
88
89     image_loader_.LoadImageAsync(
90         extension_, extension_->GetResource(path), gfx::Size(size, size),
91         base::Bind(&TestImageLoader::OnImageLoaded,
92                    base::Unretained(this)));
93
94     // If |image_| still hasn't been loaded (i.e. it is being loaded
95     // asynchronously), wait for it.
96     if (!image_loaded_) {
97       waiting_ = true;
98       base::MessageLoop::current()->Run();
99       waiting_ = false;
100     }
101
102     EXPECT_TRUE(image_loaded_);
103
104     return image_.IsEmpty() ? SkBitmap() : *image_.ToSkBitmap();
105   }
106
107  private:
108   const Extension* extension_;
109   bool waiting_;
110   bool image_loaded_;
111   gfx::Image image_;
112   ImageLoader image_loader_;
113
114   DISALLOW_COPY_AND_ASSIGN(TestImageLoader);
115 };
116
117 class ExtensionIconImageTest : public ExtensionsTest,
118                                public IconImage::Observer {
119  public:
120   ExtensionIconImageTest()
121       : image_loaded_count_(0),
122         quit_in_image_loaded_(false),
123         ui_thread_(BrowserThread::UI, &ui_loop_),
124         file_thread_(BrowserThread::FILE),
125         io_thread_(BrowserThread::IO),
126         notification_service_(content::NotificationService::Create()) {}
127
128   ~ExtensionIconImageTest() override {}
129
130   void WaitForImageLoad() {
131     quit_in_image_loaded_ = true;
132     base::MessageLoop::current()->Run();
133     quit_in_image_loaded_ = false;
134   }
135
136   int ImageLoadedCount() {
137     int result = image_loaded_count_;
138     image_loaded_count_ = 0;
139     return result;
140   }
141
142   scoped_refptr<Extension> CreateExtension(const char* name,
143                                            Manifest::Location location) {
144     // Create and load an extension.
145     base::FilePath test_file;
146     if (!PathService::Get(DIR_TEST_DATA, &test_file)) {
147       EXPECT_FALSE(true);
148       return NULL;
149     }
150     test_file = test_file.AppendASCII(name);
151     int error_code = 0;
152     std::string error;
153     JSONFileValueSerializer serializer(test_file.AppendASCII("manifest.json"));
154     scoped_ptr<base::DictionaryValue> valid_value(
155         static_cast<base::DictionaryValue*>(serializer.Deserialize(&error_code,
156                                                                    &error)));
157     EXPECT_EQ(0, error_code) << error;
158     if (error_code != 0)
159       return NULL;
160
161     EXPECT_TRUE(valid_value.get());
162     if (!valid_value)
163       return NULL;
164
165     return Extension::Create(test_file, location, *valid_value,
166                              Extension::NO_FLAGS, &error);
167   }
168
169   // testing::Test overrides:
170   void SetUp() override {
171     file_thread_.Start();
172     io_thread_.Start();
173   }
174
175   // IconImage::Delegate overrides:
176   void OnExtensionIconImageChanged(IconImage* image) override {
177     image_loaded_count_++;
178     if (quit_in_image_loaded_)
179       base::MessageLoop::current()->Quit();
180   }
181
182   gfx::ImageSkia GetDefaultIcon() {
183     return gfx::ImageSkia(gfx::ImageSkiaRep(gfx::Size(16, 16), 1.0f));
184   }
185
186   // Loads an image to be used in test from the extension.
187   // The image will be loaded from the relative path |path|.
188   SkBitmap GetTestBitmap(const Extension* extension,
189                          const std::string& path,
190                          int size) {
191     TestImageLoader image_loader(extension);
192     return image_loader.LoadBitmap(path, size);
193   }
194
195  private:
196   int image_loaded_count_;
197   bool quit_in_image_loaded_;
198   base::MessageLoop ui_loop_;
199   content::TestBrowserThread ui_thread_;
200   content::TestBrowserThread file_thread_;
201   content::TestBrowserThread io_thread_;
202   scoped_ptr<content::NotificationService> notification_service_;
203
204   DISALLOW_COPY_AND_ASSIGN(ExtensionIconImageTest);
205 };
206
207 }  // namespace
208
209 TEST_F(ExtensionIconImageTest, Basic) {
210   std::vector<ui::ScaleFactor> supported_factors;
211   supported_factors.push_back(ui::SCALE_FACTOR_100P);
212   supported_factors.push_back(ui::SCALE_FACTOR_200P);
213   ui::test::ScopedSetSupportedScaleFactors scoped_supported(supported_factors);
214   scoped_refptr<Extension> extension(CreateExtension(
215       "extension_icon_image", Manifest::INVALID_LOCATION));
216   ASSERT_TRUE(extension.get() != NULL);
217
218   gfx::ImageSkia default_icon = GetDefaultIcon();
219
220   // Load images we expect to find as representations in icon_image, so we
221   // can later use them to validate icon_image.
222   SkBitmap bitmap_16 = GetTestBitmap(extension.get(), "16.png", 16);
223   ASSERT_FALSE(bitmap_16.empty());
224
225   // There is no image of size 32 defined in the extension manifest, so we
226   // should expect manifest image of size 48 resized to size 32.
227   SkBitmap bitmap_48_resized_to_32 =
228       GetTestBitmap(extension.get(), "48.png", 32);
229   ASSERT_FALSE(bitmap_48_resized_to_32.empty());
230
231   IconImage image(browser_context(),
232                   extension.get(),
233                   IconsInfo::GetIcons(extension.get()),
234                   16,
235                   default_icon,
236                   this);
237
238   // No representations in |image_| yet.
239   gfx::ImageSkia::ImageSkiaReps image_reps = image.image_skia().image_reps();
240   ASSERT_EQ(0u, image_reps.size());
241
242   // Gets representation for a scale factor.
243   gfx::ImageSkiaRep representation = image.image_skia().GetRepresentation(1.0f);
244
245   // Before the image representation is loaded, image should contain blank
246   // image representation.
247   EXPECT_TRUE(gfx::BitmapsAreEqual(
248       representation.sk_bitmap(),
249       CreateBlankBitmapForScale(16, ui::SCALE_FACTOR_100P)));
250
251   WaitForImageLoad();
252   EXPECT_EQ(1, ImageLoadedCount());
253   ASSERT_EQ(1u, image.image_skia().image_reps().size());
254
255   representation = image.image_skia().GetRepresentation(1.0f);
256
257   // We should get the right representation now.
258   EXPECT_TRUE(gfx::BitmapsAreEqual(representation.sk_bitmap(), bitmap_16));
259   EXPECT_EQ(16, representation.pixel_width());
260
261   // Gets representation for an additional scale factor.
262   representation = image.image_skia().GetRepresentation(2.0f);
263
264   EXPECT_TRUE(gfx::BitmapsAreEqual(
265       representation.sk_bitmap(),
266       CreateBlankBitmapForScale(16, ui::SCALE_FACTOR_200P)));
267
268   WaitForImageLoad();
269   EXPECT_EQ(1, ImageLoadedCount());
270   ASSERT_EQ(2u, image.image_skia().image_reps().size());
271
272   representation = image.image_skia().GetRepresentation(2.0f);
273
274   // Image should have been resized.
275   EXPECT_EQ(32, representation.pixel_width());
276   EXPECT_TRUE(gfx::BitmapsAreEqual(representation.sk_bitmap(),
277                                    bitmap_48_resized_to_32));
278 }
279
280 // There is no resource with either exact or bigger size, but there is a smaller
281 // resource.
282 TEST_F(ExtensionIconImageTest, FallbackToSmallerWhenNoBigger) {
283   std::vector<ui::ScaleFactor> supported_factors;
284   supported_factors.push_back(ui::SCALE_FACTOR_100P);
285   supported_factors.push_back(ui::SCALE_FACTOR_200P);
286   ui::test::ScopedSetSupportedScaleFactors scoped_supported(supported_factors);
287   scoped_refptr<Extension> extension(CreateExtension(
288       "extension_icon_image", Manifest::INVALID_LOCATION));
289   ASSERT_TRUE(extension.get() != NULL);
290
291   gfx::ImageSkia default_icon = GetDefaultIcon();
292
293   // Load images we expect to find as representations in icon_image, so we
294   // can later use them to validate icon_image.
295   SkBitmap bitmap_48 = GetTestBitmap(extension.get(), "48.png", 48);
296   ASSERT_FALSE(bitmap_48.empty());
297
298   IconImage image(browser_context(),
299                   extension.get(),
300                   IconsInfo::GetIcons(extension.get()),
301                   32,
302                   default_icon,
303                   this);
304
305   gfx::ImageSkiaRep representation = image.image_skia().GetRepresentation(2.0f);
306
307   WaitForImageLoad();
308   EXPECT_EQ(1, ImageLoadedCount());
309   ASSERT_EQ(1u, image.image_skia().image_reps().size());
310
311   representation = image.image_skia().GetRepresentation(2.0f);
312
313   // We should have loaded the biggest smaller resource resized to the actual
314   // size.
315   EXPECT_EQ(2.0f, representation.scale());
316   EXPECT_EQ(64, representation.pixel_width());
317   EXPECT_TRUE(gfx::BitmapsAreEqual(representation.sk_bitmap(),
318                                    EnsureBitmapSize(bitmap_48, 64)));
319 }
320
321 // There is no resource with exact size, but there is a smaller and a bigger
322 // one. The bigger resource should be loaded.
323 TEST_F(ExtensionIconImageTest, FallbackToBigger) {
324   scoped_refptr<Extension> extension(CreateExtension(
325       "extension_icon_image", Manifest::INVALID_LOCATION));
326   ASSERT_TRUE(extension.get() != NULL);
327
328   gfx::ImageSkia default_icon = GetDefaultIcon();
329
330   // Load images we expect to find as representations in icon_image, so we
331   // can later use them to validate icon_image.
332   SkBitmap bitmap_24 = GetTestBitmap(extension.get(), "24.png", 24);
333   ASSERT_FALSE(bitmap_24.empty());
334
335   IconImage image(browser_context(),
336                   extension.get(),
337                   IconsInfo::GetIcons(extension.get()),
338                   17,
339                   default_icon,
340                   this);
341
342   gfx::ImageSkiaRep representation = image.image_skia().GetRepresentation(1.0f);
343
344   WaitForImageLoad();
345   EXPECT_EQ(1, ImageLoadedCount());
346   ASSERT_EQ(1u, image.image_skia().image_reps().size());
347
348   representation = image.image_skia().GetRepresentation(1.0f);
349
350   // We should have loaded the smallest bigger (resized) resource.
351   EXPECT_EQ(1.0f, representation.scale());
352   EXPECT_EQ(17, representation.pixel_width());
353   EXPECT_TRUE(gfx::BitmapsAreEqual(representation.sk_bitmap(),
354                                    EnsureBitmapSize(bitmap_24, 17)));
355 }
356
357 // If resource set is empty, |GetRepresentation| should synchronously return
358 // default icon, without notifying observer of image change.
359 TEST_F(ExtensionIconImageTest, NoResources) {
360   scoped_refptr<Extension> extension(CreateExtension(
361       "extension_icon_image", Manifest::INVALID_LOCATION));
362   ASSERT_TRUE(extension.get() != NULL);
363
364   ExtensionIconSet empty_icon_set;
365   gfx::ImageSkia default_icon = GetDefaultIcon();
366
367   const int kRequestedSize = 24;
368   IconImage image(browser_context(),
369                   extension.get(),
370                   empty_icon_set,
371                   kRequestedSize,
372                   default_icon,
373                   this);
374
375   gfx::ImageSkiaRep representation = image.image_skia().GetRepresentation(1.0f);
376   EXPECT_TRUE(gfx::BitmapsAreEqual(
377       representation.sk_bitmap(),
378       EnsureBitmapSize(
379           default_icon.GetRepresentation(1.0f).sk_bitmap(),
380           kRequestedSize)));
381
382   EXPECT_EQ(0, ImageLoadedCount());
383   // We should have a default icon representation.
384   ASSERT_EQ(1u, image.image_skia().image_reps().size());
385
386   representation = image.image_skia().GetRepresentation(1.0f);
387   EXPECT_TRUE(gfx::BitmapsAreEqual(
388       representation.sk_bitmap(),
389       EnsureBitmapSize(
390           default_icon.GetRepresentation(1.0f).sk_bitmap(),
391           kRequestedSize)));
392 }
393
394 // If resource set is invalid, image load should be done asynchronously and
395 // the observer should be notified when it's done. |GetRepresentation| should
396 // return the default icon representation once image load is done.
397 TEST_F(ExtensionIconImageTest, InvalidResource) {
398   scoped_refptr<Extension> extension(CreateExtension(
399       "extension_icon_image", Manifest::INVALID_LOCATION));
400   ASSERT_TRUE(extension.get() != NULL);
401
402   const int kInvalidIconSize = 24;
403   ExtensionIconSet invalid_icon_set;
404   invalid_icon_set.Add(kInvalidIconSize, "invalid.png");
405
406   gfx::ImageSkia default_icon = GetDefaultIcon();
407
408   IconImage image(browser_context(),
409                   extension.get(),
410                   invalid_icon_set,
411                   kInvalidIconSize,
412                   default_icon,
413                   this);
414
415   gfx::ImageSkiaRep representation = image.image_skia().GetRepresentation(1.0f);
416   EXPECT_TRUE(gfx::BitmapsAreEqual(
417       representation.sk_bitmap(),
418       CreateBlankBitmapForScale(kInvalidIconSize, ui::SCALE_FACTOR_100P)));
419
420   WaitForImageLoad();
421   EXPECT_EQ(1, ImageLoadedCount());
422   // We should have default icon representation now.
423   ASSERT_EQ(1u, image.image_skia().image_reps().size());
424
425   representation = image.image_skia().GetRepresentation(1.0f);
426   EXPECT_TRUE(gfx::BitmapsAreEqual(
427       representation.sk_bitmap(),
428       EnsureBitmapSize(
429           default_icon.GetRepresentation(1.0f).sk_bitmap(),
430           kInvalidIconSize)));
431 }
432
433 // Test that IconImage works with lazily (but synchronously) created default
434 // icon when IconImage returns synchronously.
435 TEST_F(ExtensionIconImageTest, LazyDefaultIcon) {
436   scoped_refptr<Extension> extension(CreateExtension(
437       "extension_icon_image", Manifest::INVALID_LOCATION));
438   ASSERT_TRUE(extension.get() != NULL);
439
440   gfx::ImageSkia default_icon = GetDefaultIcon();
441   gfx::ImageSkia lazy_default_icon(new MockImageSkiaSource(default_icon),
442                                     default_icon.size());
443
444   ExtensionIconSet empty_icon_set;
445
446   const int kRequestedSize = 128;
447   IconImage image(browser_context(),
448                   extension.get(),
449                   empty_icon_set,
450                   kRequestedSize,
451                   lazy_default_icon,
452                   this);
453
454   ASSERT_FALSE(lazy_default_icon.HasRepresentation(1.0f));
455
456   gfx::ImageSkiaRep representation = image.image_skia().GetRepresentation(1.0f);
457
458   // The resouce set is empty, so we should get the result right away.
459   EXPECT_TRUE(lazy_default_icon.HasRepresentation(1.0f));
460   EXPECT_TRUE(gfx::BitmapsAreEqual(
461       representation.sk_bitmap(),
462       EnsureBitmapSize(
463           default_icon.GetRepresentation(1.0f).sk_bitmap(),
464           kRequestedSize)));
465
466   // We should have a default icon representation.
467   ASSERT_EQ(1u, image.image_skia().image_reps().size());
468 }
469
470 // Test that IconImage works with lazily (but synchronously) created default
471 // icon when IconImage returns asynchronously.
472 TEST_F(ExtensionIconImageTest, LazyDefaultIcon_AsyncIconImage) {
473   scoped_refptr<Extension> extension(CreateExtension(
474       "extension_icon_image", Manifest::INVALID_LOCATION));
475   ASSERT_TRUE(extension.get() != NULL);
476
477   gfx::ImageSkia default_icon = GetDefaultIcon();
478   gfx::ImageSkia lazy_default_icon(new MockImageSkiaSource(default_icon),
479                                     default_icon.size());
480
481   const int kInvalidIconSize = 24;
482   ExtensionIconSet invalid_icon_set;
483   invalid_icon_set.Add(kInvalidIconSize, "invalid.png");
484
485   IconImage image(browser_context(),
486                   extension.get(),
487                   invalid_icon_set,
488                   kInvalidIconSize,
489                   lazy_default_icon,
490                   this);
491
492   ASSERT_FALSE(lazy_default_icon.HasRepresentation(1.0f));
493
494   gfx::ImageSkiaRep representation = image.image_skia().GetRepresentation(1.0f);
495
496   WaitForImageLoad();
497   EXPECT_EQ(1, ImageLoadedCount());
498   // We should have default icon representation now.
499   ASSERT_EQ(1u, image.image_skia().image_reps().size());
500
501   EXPECT_TRUE(lazy_default_icon.HasRepresentation(1.0f));
502
503   representation = image.image_skia().GetRepresentation(1.0f);
504   EXPECT_TRUE(gfx::BitmapsAreEqual(
505       representation.sk_bitmap(),
506       EnsureBitmapSize(
507           default_icon.GetRepresentation(1.0f).sk_bitmap(),
508           kInvalidIconSize)));
509 }
510
511 // Tests behavior of image created by IconImage after IconImage host goes
512 // away. The image should still return loaded representations. If requested
513 // representation was not loaded while IconImage host was around, transparent
514 // representations should be returned.
515 TEST_F(ExtensionIconImageTest, IconImageDestruction) {
516   scoped_refptr<Extension> extension(CreateExtension(
517       "extension_icon_image", Manifest::INVALID_LOCATION));
518   ASSERT_TRUE(extension.get() != NULL);
519
520   gfx::ImageSkia default_icon = GetDefaultIcon();
521
522   // Load images we expect to find as representations in icon_image, so we
523   // can later use them to validate icon_image.
524   SkBitmap bitmap_16 = GetTestBitmap(extension.get(), "16.png", 16);
525   ASSERT_FALSE(bitmap_16.empty());
526
527   scoped_ptr<IconImage> image(
528       new IconImage(browser_context(),
529                     extension.get(),
530                     IconsInfo::GetIcons(extension.get()),
531                     16,
532                     default_icon,
533                     this));
534
535   // Load an image representation.
536   gfx::ImageSkiaRep representation =
537       image->image_skia().GetRepresentation(1.0f);
538
539   WaitForImageLoad();
540   EXPECT_EQ(1, ImageLoadedCount());
541   ASSERT_EQ(1u, image->image_skia().image_reps().size());
542
543   // Stash loaded image skia, and destroy |image|.
544   gfx::ImageSkia image_skia = image->image_skia();
545   image.reset();
546   extension = NULL;
547
548   // Image skia should still be able to get previously loaded representation.
549   representation = image_skia.GetRepresentation(1.0f);
550
551   EXPECT_EQ(1.0f, representation.scale());
552   EXPECT_EQ(16, representation.pixel_width());
553   EXPECT_TRUE(gfx::BitmapsAreEqual(representation.sk_bitmap(), bitmap_16));
554
555   // When requesting another representation, we should not crash and return some
556   // image of the size. It could be blank or a rescale from the existing 1.0f
557   // icon.
558   representation = image_skia.GetRepresentation(2.0f);
559
560   EXPECT_EQ(16, representation.GetWidth());
561   EXPECT_EQ(16, representation.GetHeight());
562   EXPECT_EQ(2.0f, representation.scale());
563 }
564
565 }  // namespace extensions