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 "chrome/browser/chrome_notification_types.h"
     13 #include "chrome/common/chrome_paths.h"
     14 #include "content/public/browser/notification_service.h"
     15 #include "content/public/test/test_browser_thread.h"
     16 #include "extensions/browser/component_extension_resource_manager.h"
     17 #include "extensions/browser/extensions_browser_client.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_resource.h"
     22 #include "extensions/common/manifest.h"
     23 #include "extensions/common/manifest_handlers/icons_handler.h"
     24 #include "testing/gtest/include/gtest/gtest.h"
     25 #include "third_party/skia/include/core/SkBitmap.h"
     26 #include "ui/gfx/image/image.h"
     27 #include "ui/gfx/image/image_family.h"
     28 #include "ui/gfx/image/image_skia.h"
     29 #include "ui/gfx/size.h"
     30 
     31 #if defined(OS_CHROMEOS)
     32 #include "ui/file_manager/grit/file_manager_resources.h"
     33 #endif
     34 
     35 using content::BrowserThread;
     36 using extensions::Extension;
     37 using extensions::ExtensionResource;
     38 using extensions::ImageLoader;
     39 using extensions::Manifest;
     40 using extensions::UnloadedExtensionInfo;
     41 
     42 class ImageLoaderTest : public testing::Test {
     43  public:
     44   ImageLoaderTest()
     45       : image_loaded_count_(0),
     46         quit_in_image_loaded_(false),
     47         ui_thread_(BrowserThread::UI, &ui_loop_),
     48         file_thread_(BrowserThread::FILE),
     49         io_thread_(BrowserThread::IO) {
     50   }
     51 
     52   void OnImageLoaded(const gfx::Image& image) {
     53     image_loaded_count_++;
     54     if (quit_in_image_loaded_)
     55       base::MessageLoop::current()->Quit();
     56     image_ = image;
     57   }
     58 
     59   void OnImageFamilyLoaded(const gfx::ImageFamily& image_family) {
     60     image_loaded_count_++;
     61     if (quit_in_image_loaded_)
     62       base::MessageLoop::current()->Quit();
     63     image_family_ = image_family;
     64   }
     65 
     66   void WaitForImageLoad() {
     67     quit_in_image_loaded_ = true;
     68     base::MessageLoop::current()->Run();
     69     quit_in_image_loaded_ = false;
     70   }
     71 
     72   int image_loaded_count() {
     73     int result = image_loaded_count_;
     74     image_loaded_count_ = 0;
     75     return result;
     76   }
     77 
     78   scoped_refptr<Extension> CreateExtension(const char* name,
     79                                            Manifest::Location location) {
     80     // Create and load an extension.
     81     base::FilePath test_file;
     82     if (!PathService::Get(chrome::DIR_TEST_DATA, &test_file)) {
     83       EXPECT_FALSE(true);
     84       return NULL;
     85     }
     86     test_file = test_file.AppendASCII("extensions")
     87                          .AppendASCII(name);
     88     int error_code = 0;
     89     std::string error;
     90     JSONFileValueSerializer serializer(test_file.AppendASCII("app.json"));
     91     scoped_ptr<base::DictionaryValue> valid_value(
     92         static_cast<base::DictionaryValue*>(serializer.Deserialize(&error_code,
     93                                                                    &error)));
     94     EXPECT_EQ(0, error_code) << error;
     95     if (error_code != 0)
     96       return NULL;
     97 
     98     EXPECT_TRUE(valid_value.get());
     99     if (!valid_value)
    100       return NULL;
    101 
    102     if (location == Manifest::COMPONENT) {
    103       if (!PathService::Get(chrome::DIR_RESOURCES, &test_file)) {
    104         EXPECT_FALSE(true);
    105         return NULL;
    106       }
    107       test_file = test_file.AppendASCII(name);
    108     }
    109     return Extension::Create(test_file, location, *valid_value,
    110                              Extension::NO_FLAGS, &error);
    111   }
    112 
    113   gfx::Image image_;
    114   gfx::ImageFamily image_family_;
    115 
    116  private:
    117   virtual void SetUp() OVERRIDE {
    118     testing::Test::SetUp();
    119     file_thread_.Start();
    120     io_thread_.Start();
    121   }
    122 
    123   int image_loaded_count_;
    124   bool quit_in_image_loaded_;
    125   base::MessageLoop ui_loop_;
    126   content::TestBrowserThread ui_thread_;
    127   content::TestBrowserThread file_thread_;
    128   content::TestBrowserThread io_thread_;
    129 };
    130 
    131 // Tests loading an image works correctly.
    132 TEST_F(ImageLoaderTest, LoadImage) {
    133   scoped_refptr<Extension> extension(CreateExtension(
    134       "image_loading_tracker", Manifest::INVALID_LOCATION));
    135   ASSERT_TRUE(extension.get() != NULL);
    136 
    137   ExtensionResource image_resource = extensions::IconsInfo::GetIconResource(
    138       extension.get(),
    139       extension_misc::EXTENSION_ICON_SMALLISH,
    140       ExtensionIconSet::MATCH_EXACTLY);
    141   gfx::Size max_size(extension_misc::EXTENSION_ICON_SMALLISH,
    142                      extension_misc::EXTENSION_ICON_SMALLISH);
    143   ImageLoader loader;
    144   loader.LoadImageAsync(extension.get(),
    145                         image_resource,
    146                         max_size,
    147                         base::Bind(&ImageLoaderTest::OnImageLoaded,
    148                                    base::Unretained(this)));
    149 
    150   // The image isn't cached, so we should not have received notification.
    151   EXPECT_EQ(0, image_loaded_count());
    152 
    153   WaitForImageLoad();
    154 
    155   // We should have gotten the image.
    156   EXPECT_EQ(1, image_loaded_count());
    157 
    158   // Check that the image was loaded.
    159   EXPECT_EQ(extension_misc::EXTENSION_ICON_SMALLISH,
    160             image_.ToSkBitmap()->width());
    161 }
    162 
    163 // Tests deleting an extension while waiting for the image to load doesn't cause
    164 // problems.
    165 TEST_F(ImageLoaderTest, DeleteExtensionWhileWaitingForCache) {
    166   scoped_refptr<Extension> extension(CreateExtension(
    167       "image_loading_tracker", Manifest::INVALID_LOCATION));
    168   ASSERT_TRUE(extension.get() != NULL);
    169 
    170   ExtensionResource image_resource = extensions::IconsInfo::GetIconResource(
    171       extension.get(),
    172       extension_misc::EXTENSION_ICON_SMALLISH,
    173       ExtensionIconSet::MATCH_EXACTLY);
    174   gfx::Size max_size(extension_misc::EXTENSION_ICON_SMALLISH,
    175                      extension_misc::EXTENSION_ICON_SMALLISH);
    176   ImageLoader loader;
    177   std::set<int> sizes;
    178   sizes.insert(extension_misc::EXTENSION_ICON_SMALLISH);
    179   loader.LoadImageAsync(extension.get(),
    180                         image_resource,
    181                         max_size,
    182                         base::Bind(&ImageLoaderTest::OnImageLoaded,
    183                                    base::Unretained(this)));
    184 
    185   // The image isn't cached, so we should not have received notification.
    186   EXPECT_EQ(0, image_loaded_count());
    187 
    188   // Send out notification the extension was uninstalled.
    189   UnloadedExtensionInfo details(extension.get(),
    190                                 UnloadedExtensionInfo::REASON_UNINSTALL);
    191   content::NotificationService::current()->Notify(
    192       chrome::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED,
    193       content::NotificationService::AllSources(),
    194       content::Details<UnloadedExtensionInfo>(&details));
    195 
    196   // Chuck the extension, that way if anyone tries to access it we should crash
    197   // or get valgrind errors.
    198   extension = NULL;
    199 
    200   WaitForImageLoad();
    201 
    202   // Even though we deleted the extension, we should still get the image.
    203   // We should still have gotten the image.
    204   EXPECT_EQ(1, image_loaded_count());
    205 
    206   // Check that the image was loaded.
    207   EXPECT_EQ(extension_misc::EXTENSION_ICON_SMALLISH,
    208             image_.ToSkBitmap()->width());
    209 }
    210 
    211 // Tests loading multiple dimensions of the same image.
    212 TEST_F(ImageLoaderTest, MultipleImages) {
    213   scoped_refptr<Extension> extension(CreateExtension(
    214       "image_loading_tracker", Manifest::INVALID_LOCATION));
    215   ASSERT_TRUE(extension.get() != NULL);
    216 
    217   std::vector<ImageLoader::ImageRepresentation> info_list;
    218   int sizes[] = {extension_misc::EXTENSION_ICON_BITTY,
    219                  extension_misc::EXTENSION_ICON_SMALLISH, };
    220   for (size_t i = 0; i < arraysize(sizes); ++i) {
    221     ExtensionResource resource = extensions::IconsInfo::GetIconResource(
    222         extension.get(), sizes[i], ExtensionIconSet::MATCH_EXACTLY);
    223     info_list.push_back(ImageLoader::ImageRepresentation(
    224         resource,
    225         ImageLoader::ImageRepresentation::RESIZE_WHEN_LARGER,
    226         gfx::Size(sizes[i], sizes[i]),
    227         ui::SCALE_FACTOR_NONE));
    228   }
    229 
    230   ImageLoader loader;
    231   loader.LoadImagesAsync(extension.get(), info_list,
    232                          base::Bind(&ImageLoaderTest::OnImageLoaded,
    233                                     base::Unretained(this)));
    234 
    235   // The image isn't cached, so we should not have received notification.
    236   EXPECT_EQ(0, image_loaded_count());
    237 
    238   WaitForImageLoad();
    239 
    240   // We should have gotten the image.
    241   EXPECT_EQ(1, image_loaded_count());
    242 
    243   // Check that all images were loaded.
    244   std::vector<gfx::ImageSkiaRep> image_reps =
    245       image_.ToImageSkia()->image_reps();
    246   ASSERT_EQ(2u, image_reps.size());
    247 
    248   const gfx::ImageSkiaRep* img_rep1 = &image_reps[0];
    249   const gfx::ImageSkiaRep* img_rep2 = &image_reps[1];
    250   EXPECT_EQ(extension_misc::EXTENSION_ICON_BITTY,
    251             img_rep1->pixel_width());
    252   EXPECT_EQ(extension_misc::EXTENSION_ICON_SMALLISH,
    253             img_rep2->pixel_width());
    254 }
    255 
    256 // Tests loading multiple dimensions of the same image into an image family.
    257 TEST_F(ImageLoaderTest, LoadImageFamily) {
    258   scoped_refptr<Extension> extension(
    259       CreateExtension("image_loading_tracker", Manifest::INVALID_LOCATION));
    260   ASSERT_TRUE(extension.get() != NULL);
    261 
    262   std::vector<ImageLoader::ImageRepresentation> info_list;
    263   int sizes[] = {extension_misc::EXTENSION_ICON_BITTY,
    264                  extension_misc::EXTENSION_ICON_SMALLISH, };
    265   for (size_t i = 0; i < arraysize(sizes); ++i) {
    266     ExtensionResource resource = extensions::IconsInfo::GetIconResource(
    267         extension.get(), sizes[i], ExtensionIconSet::MATCH_EXACTLY);
    268     info_list.push_back(ImageLoader::ImageRepresentation(
    269         resource,
    270         ImageLoader::ImageRepresentation::NEVER_RESIZE,
    271         gfx::Size(sizes[i], sizes[i]),
    272         ui::SCALE_FACTOR_100P));
    273   }
    274 
    275   // Add a second icon of 200P which should get grouped with the smaller icon's
    276   // ImageSkia.
    277   ExtensionResource resource = extensions::IconsInfo::GetIconResource(
    278       extension.get(),
    279       extension_misc::EXTENSION_ICON_SMALLISH,
    280       ExtensionIconSet::MATCH_EXACTLY);
    281   info_list.push_back(ImageLoader::ImageRepresentation(
    282       resource,
    283       ImageLoader::ImageRepresentation::NEVER_RESIZE,
    284       gfx::Size(extension_misc::EXTENSION_ICON_BITTY,
    285                 extension_misc::EXTENSION_ICON_BITTY),
    286       ui::SCALE_FACTOR_200P));
    287 
    288   ImageLoader loader;
    289   loader.LoadImageFamilyAsync(extension.get(),
    290                               info_list,
    291                               base::Bind(&ImageLoaderTest::OnImageFamilyLoaded,
    292                                          base::Unretained(this)));
    293 
    294   // The image isn't cached, so we should not have received notification.
    295   EXPECT_EQ(0, image_loaded_count());
    296 
    297   WaitForImageLoad();
    298 
    299   // We should have gotten the image.
    300   EXPECT_EQ(1, image_loaded_count());
    301 
    302   // Check that all images were loaded.
    303   for (size_t i = 0; i < arraysize(sizes); ++i) {
    304     const gfx::Image* image = image_family_.GetBest(sizes[i], sizes[i]);
    305     EXPECT_EQ(sizes[i], image->Width());
    306   }
    307 
    308   // Check the smaller image has 2 representations of different scale factors.
    309   std::vector<gfx::ImageSkiaRep> image_reps =
    310       image_family_.GetBest(extension_misc::EXTENSION_ICON_BITTY,
    311                             extension_misc::EXTENSION_ICON_BITTY)
    312           ->ToImageSkia()
    313           ->image_reps();
    314 
    315   ASSERT_EQ(2u, image_reps.size());
    316 
    317   const gfx::ImageSkiaRep* img_rep1 = &image_reps[0];
    318   const gfx::ImageSkiaRep* img_rep2 = &image_reps[1];
    319   EXPECT_EQ(extension_misc::EXTENSION_ICON_BITTY, img_rep1->pixel_width());
    320   EXPECT_EQ(1.0f, img_rep1->scale());
    321   EXPECT_EQ(extension_misc::EXTENSION_ICON_SMALLISH, img_rep2->pixel_width());
    322   EXPECT_EQ(2.0f, img_rep2->scale());
    323 }
    324 
    325 // Tests IsComponentExtensionResource function.
    326 // TODO(mukai): move this to ChromeComponentExtensionResourceManager's test.
    327 TEST_F(ImageLoaderTest, IsComponentExtensionResource) {
    328   extensions::ComponentExtensionResourceManager* resource_manager =
    329       extensions::ExtensionsBrowserClient::Get()->
    330       GetComponentExtensionResourceManager();
    331   if (!resource_manager)
    332     return;
    333 
    334   scoped_refptr<Extension> extension(CreateExtension(
    335       "file_manager", Manifest::COMPONENT));
    336   ASSERT_TRUE(extension.get() != NULL);
    337 
    338   ExtensionResource resource = extensions::IconsInfo::GetIconResource(
    339       extension.get(),
    340       extension_misc::EXTENSION_ICON_BITTY,
    341       ExtensionIconSet::MATCH_EXACTLY);
    342 
    343 #if defined(OS_CHROMEOS)
    344   int resource_id;
    345   ASSERT_TRUE(resource_manager->IsComponentExtensionResource(
    346       extension->path(),
    347       resource.relative_path(),
    348       &resource_id));
    349   ASSERT_EQ(IDR_FILE_MANAGER_ICON_16, resource_id);
    350 #endif
    351 }
    352