Home | History | Annotate | Download | only in browser
      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/image_loader.h"
      6 
      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_thread.h"
     14 #include "extensions/browser/extensions_browser_client.h"
     15 #include "extensions/browser/notification_types.h"
     16 #include "extensions/common/constants.h"
     17 #include "extensions/common/extension.h"
     18 #include "extensions/common/extension_icon_set.h"
     19 #include "extensions/common/extension_paths.h"
     20 #include "extensions/common/extension_resource.h"
     21 #include "extensions/common/manifest.h"
     22 #include "extensions/common/manifest_handlers/icons_handler.h"
     23 #include "testing/gtest/include/gtest/gtest.h"
     24 #include "third_party/skia/include/core/SkBitmap.h"
     25 #include "ui/gfx/image/image.h"
     26 #include "ui/gfx/image/image_family.h"
     27 #include "ui/gfx/image/image_skia.h"
     28 #include "ui/gfx/size.h"
     29 
     30 using content::BrowserThread;
     31 using content::NotificationService;
     32 
     33 namespace extensions {
     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         notification_service_(NotificationService::Create()) {}
     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* dir_name,
     72                                            Manifest::Location location) {
     73     // Create and load an extension.
     74     base::FilePath extension_dir;
     75     if (!PathService::Get(DIR_TEST_DATA, &extension_dir)) {
     76       EXPECT_FALSE(true);
     77       return NULL;
     78     }
     79     extension_dir = extension_dir.AppendASCII(dir_name);
     80     int error_code = 0;
     81     std::string error;
     82     JSONFileValueSerializer serializer(
     83         extension_dir.AppendASCII("manifest.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     return Extension::Create(
     96         extension_dir, location, *valid_value, Extension::NO_FLAGS, &error);
     97   }
     98 
     99   gfx::Image image_;
    100   gfx::ImageFamily image_family_;
    101 
    102  private:
    103   virtual void SetUp() OVERRIDE {
    104     testing::Test::SetUp();
    105     file_thread_.Start();
    106     io_thread_.Start();
    107   }
    108 
    109   int image_loaded_count_;
    110   bool quit_in_image_loaded_;
    111   base::MessageLoop ui_loop_;
    112   content::TestBrowserThread ui_thread_;
    113   content::TestBrowserThread file_thread_;
    114   content::TestBrowserThread io_thread_;
    115   scoped_ptr<NotificationService> notification_service_;
    116 };
    117 
    118 // Tests loading an image works correctly.
    119 TEST_F(ImageLoaderTest, LoadImage) {
    120   scoped_refptr<Extension> extension(
    121       CreateExtension("image_loader", Manifest::INVALID_LOCATION));
    122   ASSERT_TRUE(extension.get() != NULL);
    123 
    124   ExtensionResource image_resource =
    125       IconsInfo::GetIconResource(extension.get(),
    126                                  extension_misc::EXTENSION_ICON_SMALLISH,
    127                                  ExtensionIconSet::MATCH_EXACTLY);
    128   gfx::Size max_size(extension_misc::EXTENSION_ICON_SMALLISH,
    129                      extension_misc::EXTENSION_ICON_SMALLISH);
    130   ImageLoader loader;
    131   loader.LoadImageAsync(extension.get(),
    132                         image_resource,
    133                         max_size,
    134                         base::Bind(&ImageLoaderTest::OnImageLoaded,
    135                                    base::Unretained(this)));
    136 
    137   // The image isn't cached, so we should not have received notification.
    138   EXPECT_EQ(0, image_loaded_count());
    139 
    140   WaitForImageLoad();
    141 
    142   // We should have gotten the image.
    143   EXPECT_FALSE(image_.IsEmpty());
    144   EXPECT_EQ(1, image_loaded_count());
    145 
    146   // Check that the image was loaded.
    147   EXPECT_EQ(extension_misc::EXTENSION_ICON_SMALLISH,
    148             image_.ToSkBitmap()->width());
    149 }
    150 
    151 // Tests deleting an extension while waiting for the image to load doesn't cause
    152 // problems.
    153 TEST_F(ImageLoaderTest, DeleteExtensionWhileWaitingForCache) {
    154   scoped_refptr<Extension> extension(
    155       CreateExtension("image_loader", Manifest::INVALID_LOCATION));
    156   ASSERT_TRUE(extension.get() != NULL);
    157 
    158   ExtensionResource image_resource =
    159       IconsInfo::GetIconResource(extension.get(),
    160                                  extension_misc::EXTENSION_ICON_SMALLISH,
    161                                  ExtensionIconSet::MATCH_EXACTLY);
    162   gfx::Size max_size(extension_misc::EXTENSION_ICON_SMALLISH,
    163                      extension_misc::EXTENSION_ICON_SMALLISH);
    164   ImageLoader loader;
    165   std::set<int> sizes;
    166   sizes.insert(extension_misc::EXTENSION_ICON_SMALLISH);
    167   loader.LoadImageAsync(extension.get(),
    168                         image_resource,
    169                         max_size,
    170                         base::Bind(&ImageLoaderTest::OnImageLoaded,
    171                                    base::Unretained(this)));
    172 
    173   // The image isn't cached, so we should not have received notification.
    174   EXPECT_EQ(0, image_loaded_count());
    175 
    176   // Send out notification the extension was uninstalled.
    177   UnloadedExtensionInfo details(extension.get(),
    178                                 UnloadedExtensionInfo::REASON_UNINSTALL);
    179   content::NotificationService::current()->Notify(
    180       NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED,
    181       content::NotificationService::AllSources(),
    182       content::Details<UnloadedExtensionInfo>(&details));
    183 
    184   // Chuck the extension, that way if anyone tries to access it we should crash
    185   // or get valgrind errors.
    186   extension = NULL;
    187 
    188   WaitForImageLoad();
    189 
    190   // Even though we deleted the extension, we should still get the image.
    191   // We should still have gotten the image.
    192   EXPECT_EQ(1, image_loaded_count());
    193 
    194   // Check that the image was loaded.
    195   EXPECT_EQ(extension_misc::EXTENSION_ICON_SMALLISH,
    196             image_.ToSkBitmap()->width());
    197 }
    198 
    199 // Tests loading multiple dimensions of the same image.
    200 TEST_F(ImageLoaderTest, MultipleImages) {
    201   scoped_refptr<Extension> extension(
    202       CreateExtension("image_loader", Manifest::INVALID_LOCATION));
    203   ASSERT_TRUE(extension.get() != NULL);
    204 
    205   std::vector<ImageLoader::ImageRepresentation> info_list;
    206   int sizes[] = {extension_misc::EXTENSION_ICON_BITTY,
    207                  extension_misc::EXTENSION_ICON_SMALLISH, };
    208   for (size_t i = 0; i < arraysize(sizes); ++i) {
    209     ExtensionResource resource = IconsInfo::GetIconResource(
    210         extension.get(), sizes[i], ExtensionIconSet::MATCH_EXACTLY);
    211     info_list.push_back(ImageLoader::ImageRepresentation(
    212         resource,
    213         ImageLoader::ImageRepresentation::RESIZE_WHEN_LARGER,
    214         gfx::Size(sizes[i], sizes[i]),
    215         ui::SCALE_FACTOR_NONE));
    216   }
    217 
    218   ImageLoader loader;
    219   loader.LoadImagesAsync(extension.get(), info_list,
    220                          base::Bind(&ImageLoaderTest::OnImageLoaded,
    221                                     base::Unretained(this)));
    222 
    223   // The image isn't cached, so we should not have received notification.
    224   EXPECT_EQ(0, image_loaded_count());
    225 
    226   WaitForImageLoad();
    227 
    228   // We should have gotten the image.
    229   EXPECT_EQ(1, image_loaded_count());
    230 
    231   // Check that all images were loaded.
    232   std::vector<gfx::ImageSkiaRep> image_reps =
    233       image_.ToImageSkia()->image_reps();
    234   ASSERT_EQ(2u, image_reps.size());
    235 
    236   const gfx::ImageSkiaRep* img_rep1 = &image_reps[0];
    237   const gfx::ImageSkiaRep* img_rep2 = &image_reps[1];
    238   EXPECT_EQ(extension_misc::EXTENSION_ICON_BITTY,
    239             img_rep1->pixel_width());
    240   EXPECT_EQ(extension_misc::EXTENSION_ICON_SMALLISH,
    241             img_rep2->pixel_width());
    242 }
    243 
    244 // Tests loading multiple dimensions of the same image into an image family.
    245 TEST_F(ImageLoaderTest, LoadImageFamily) {
    246   scoped_refptr<Extension> extension(
    247       CreateExtension("image_loader", Manifest::INVALID_LOCATION));
    248   ASSERT_TRUE(extension.get() != NULL);
    249 
    250   std::vector<ImageLoader::ImageRepresentation> info_list;
    251   int sizes[] = {extension_misc::EXTENSION_ICON_BITTY,
    252                  extension_misc::EXTENSION_ICON_SMALLISH, };
    253   for (size_t i = 0; i < arraysize(sizes); ++i) {
    254     ExtensionResource resource = IconsInfo::GetIconResource(
    255         extension.get(), sizes[i], ExtensionIconSet::MATCH_EXACTLY);
    256     info_list.push_back(ImageLoader::ImageRepresentation(
    257         resource,
    258         ImageLoader::ImageRepresentation::NEVER_RESIZE,
    259         gfx::Size(sizes[i], sizes[i]),
    260         ui::SCALE_FACTOR_100P));
    261   }
    262 
    263   // Add a second icon of 200P which should get grouped with the smaller icon's
    264   // ImageSkia.
    265   ExtensionResource resource =
    266       IconsInfo::GetIconResource(extension.get(),
    267                                  extension_misc::EXTENSION_ICON_SMALLISH,
    268                                  ExtensionIconSet::MATCH_EXACTLY);
    269   info_list.push_back(ImageLoader::ImageRepresentation(
    270       resource,
    271       ImageLoader::ImageRepresentation::NEVER_RESIZE,
    272       gfx::Size(extension_misc::EXTENSION_ICON_BITTY,
    273                 extension_misc::EXTENSION_ICON_BITTY),
    274       ui::SCALE_FACTOR_200P));
    275 
    276   ImageLoader loader;
    277   loader.LoadImageFamilyAsync(extension.get(),
    278                               info_list,
    279                               base::Bind(&ImageLoaderTest::OnImageFamilyLoaded,
    280                                          base::Unretained(this)));
    281 
    282   // The image isn't cached, so we should not have received notification.
    283   EXPECT_EQ(0, image_loaded_count());
    284 
    285   WaitForImageLoad();
    286 
    287   // We should have gotten the image.
    288   EXPECT_EQ(1, image_loaded_count());
    289 
    290   // Check that all images were loaded.
    291   for (size_t i = 0; i < arraysize(sizes); ++i) {
    292     const gfx::Image* image = image_family_.GetBest(sizes[i], sizes[i]);
    293     EXPECT_EQ(sizes[i], image->Width());
    294   }
    295 
    296   // Check the smaller image has 2 representations of different scale factors.
    297   std::vector<gfx::ImageSkiaRep> image_reps =
    298       image_family_.GetBest(extension_misc::EXTENSION_ICON_BITTY,
    299                             extension_misc::EXTENSION_ICON_BITTY)
    300           ->ToImageSkia()
    301           ->image_reps();
    302 
    303   ASSERT_EQ(2u, image_reps.size());
    304 
    305   const gfx::ImageSkiaRep* img_rep1 = &image_reps[0];
    306   const gfx::ImageSkiaRep* img_rep2 = &image_reps[1];
    307   EXPECT_EQ(extension_misc::EXTENSION_ICON_BITTY, img_rep1->pixel_width());
    308   EXPECT_EQ(1.0f, img_rep1->scale());
    309   EXPECT_EQ(extension_misc::EXTENSION_ICON_SMALLISH, img_rep2->pixel_width());
    310   EXPECT_EQ(2.0f, img_rep2->scale());
    311 }
    312 
    313 }  // namespace extensions
    314