Home | History | Annotate | Download | only in extensions
      1 // Copyright (c) 2012 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 "chrome/browser/extensions/image_loader.h"
      6 
      7 #include "base/json/json_file_value_serializer.h"
      8 #include "base/message_loop/message_loop.h"
      9 #include "base/path_service.h"
     10 #include "chrome/browser/chrome_notification_types.h"
     11 #include "chrome/common/chrome_paths.h"
     12 #include "chrome/common/extensions/extension_constants.h"
     13 #include "chrome/common/extensions/extension_icon_set.h"
     14 #include "chrome/common/extensions/manifest_handlers/icons_handler.h"
     15 #include "content/public/browser/notification_service.h"
     16 #include "content/public/test/test_browser_thread.h"
     17 #include "extensions/common/extension.h"
     18 #include "extensions/common/extension_resource.h"
     19 #include "extensions/common/manifest.h"
     20 #include "grit/component_extension_resources.h"
     21 #include "testing/gtest/include/gtest/gtest.h"
     22 #include "third_party/skia/include/core/SkBitmap.h"
     23 #include "ui/gfx/image/image.h"
     24 #include "ui/gfx/image/image_skia.h"
     25 #include "ui/gfx/size.h"
     26 
     27 using content::BrowserThread;
     28 using extensions::Extension;
     29 using extensions::ExtensionResource;
     30 using extensions::ImageLoader;
     31 using extensions::Manifest;
     32 using extensions::UnloadedExtensionInfo;
     33 
     34 class ImageLoaderTest : public testing::Test {
     35  public:
     36   ImageLoaderTest()
     37       : image_loaded_count_(0),
     38         quit_in_image_loaded_(false),
     39         ui_thread_(BrowserThread::UI, &ui_loop_),
     40         file_thread_(BrowserThread::FILE),
     41         io_thread_(BrowserThread::IO) {
     42   }
     43 
     44   void OnImageLoaded(const gfx::Image& image) {
     45     image_loaded_count_++;
     46     if (quit_in_image_loaded_)
     47       base::MessageLoop::current()->Quit();
     48     image_ = image;
     49   }
     50 
     51   void WaitForImageLoad() {
     52     quit_in_image_loaded_ = true;
     53     base::MessageLoop::current()->Run();
     54     quit_in_image_loaded_ = false;
     55   }
     56 
     57   int image_loaded_count() {
     58     int result = image_loaded_count_;
     59     image_loaded_count_ = 0;
     60     return result;
     61   }
     62 
     63   scoped_refptr<Extension> CreateExtension(const char* name,
     64                                            Manifest::Location location) {
     65     // Create and load an extension.
     66     base::FilePath test_file;
     67     if (!PathService::Get(chrome::DIR_TEST_DATA, &test_file)) {
     68       EXPECT_FALSE(true);
     69       return NULL;
     70     }
     71     test_file = test_file.AppendASCII("extensions")
     72                          .AppendASCII(name);
     73     int error_code = 0;
     74     std::string error;
     75     JSONFileValueSerializer serializer(test_file.AppendASCII("app.json"));
     76     scoped_ptr<DictionaryValue> valid_value(
     77         static_cast<DictionaryValue*>(serializer.Deserialize(&error_code,
     78                                                              &error)));
     79     EXPECT_EQ(0, error_code) << error;
     80     if (error_code != 0)
     81       return NULL;
     82 
     83     EXPECT_TRUE(valid_value.get());
     84     if (!valid_value)
     85       return NULL;
     86 
     87     if (location == Manifest::COMPONENT) {
     88       if (!PathService::Get(chrome::DIR_RESOURCES, &test_file)) {
     89         EXPECT_FALSE(true);
     90         return NULL;
     91       }
     92       test_file = test_file.AppendASCII(name);
     93     }
     94     return Extension::Create(test_file, location, *valid_value,
     95                              Extension::NO_FLAGS, &error);
     96   }
     97 
     98   gfx::Image image_;
     99 
    100  private:
    101   virtual void SetUp() OVERRIDE {
    102     testing::Test::SetUp();
    103     file_thread_.Start();
    104     io_thread_.Start();
    105   }
    106 
    107   int image_loaded_count_;
    108   bool quit_in_image_loaded_;
    109   base::MessageLoop ui_loop_;
    110   content::TestBrowserThread ui_thread_;
    111   content::TestBrowserThread file_thread_;
    112   content::TestBrowserThread io_thread_;
    113 };
    114 
    115 // Tests loading an image works correctly.
    116 TEST_F(ImageLoaderTest, LoadImage) {
    117   scoped_refptr<Extension> extension(CreateExtension(
    118       "image_loading_tracker", Manifest::INVALID_LOCATION));
    119   ASSERT_TRUE(extension.get() != NULL);
    120 
    121   ExtensionResource image_resource = extensions::IconsInfo::GetIconResource(
    122       extension.get(),
    123       extension_misc::EXTENSION_ICON_SMALLISH,
    124       ExtensionIconSet::MATCH_EXACTLY);
    125   gfx::Size max_size(extension_misc::EXTENSION_ICON_SMALLISH,
    126                      extension_misc::EXTENSION_ICON_SMALLISH);
    127   ImageLoader loader;
    128   loader.LoadImageAsync(extension.get(),
    129                         image_resource,
    130                         max_size,
    131                         base::Bind(&ImageLoaderTest::OnImageLoaded,
    132                                    base::Unretained(this)));
    133 
    134   // The image isn't cached, so we should not have received notification.
    135   EXPECT_EQ(0, image_loaded_count());
    136 
    137   WaitForImageLoad();
    138 
    139   // We should have gotten the image.
    140   EXPECT_EQ(1, image_loaded_count());
    141 
    142   // Check that the image was loaded.
    143   EXPECT_EQ(extension_misc::EXTENSION_ICON_SMALLISH,
    144             image_.ToSkBitmap()->width());
    145 }
    146 
    147 // Tests deleting an extension while waiting for the image to load doesn't cause
    148 // problems.
    149 TEST_F(ImageLoaderTest, DeleteExtensionWhileWaitingForCache) {
    150   scoped_refptr<Extension> extension(CreateExtension(
    151       "image_loading_tracker", Manifest::INVALID_LOCATION));
    152   ASSERT_TRUE(extension.get() != NULL);
    153 
    154   ExtensionResource image_resource = extensions::IconsInfo::GetIconResource(
    155       extension.get(),
    156       extension_misc::EXTENSION_ICON_SMALLISH,
    157       ExtensionIconSet::MATCH_EXACTLY);
    158   gfx::Size max_size(extension_misc::EXTENSION_ICON_SMALLISH,
    159                      extension_misc::EXTENSION_ICON_SMALLISH);
    160   ImageLoader loader;
    161   std::set<int> sizes;
    162   sizes.insert(extension_misc::EXTENSION_ICON_SMALLISH);
    163   loader.LoadImageAsync(extension.get(),
    164                         image_resource,
    165                         max_size,
    166                         base::Bind(&ImageLoaderTest::OnImageLoaded,
    167                                    base::Unretained(this)));
    168 
    169   // The image isn't cached, so we should not have received notification.
    170   EXPECT_EQ(0, image_loaded_count());
    171 
    172   // Send out notification the extension was uninstalled.
    173   UnloadedExtensionInfo details(extension.get(),
    174                                 UnloadedExtensionInfo::REASON_UNINSTALL);
    175   content::NotificationService::current()->Notify(
    176       chrome::NOTIFICATION_EXTENSION_UNLOADED,
    177       content::NotificationService::AllSources(),
    178       content::Details<UnloadedExtensionInfo>(&details));
    179 
    180   // Chuck the extension, that way if anyone tries to access it we should crash
    181   // or get valgrind errors.
    182   extension = NULL;
    183 
    184   WaitForImageLoad();
    185 
    186   // Even though we deleted the extension, we should still get the image.
    187   // We should still have gotten the image.
    188   EXPECT_EQ(1, image_loaded_count());
    189 
    190   // Check that the image was loaded.
    191   EXPECT_EQ(extension_misc::EXTENSION_ICON_SMALLISH,
    192             image_.ToSkBitmap()->width());
    193 }
    194 
    195 // Tests loading multiple dimensions of the same image.
    196 TEST_F(ImageLoaderTest, MultipleImages) {
    197   scoped_refptr<Extension> extension(CreateExtension(
    198       "image_loading_tracker", Manifest::INVALID_LOCATION));
    199   ASSERT_TRUE(extension.get() != NULL);
    200 
    201   std::vector<ImageLoader::ImageRepresentation> info_list;
    202   int sizes[] = {extension_misc::EXTENSION_ICON_SMALLISH,
    203                  extension_misc::EXTENSION_ICON_BITTY};
    204   for (size_t i = 0; i < arraysize(sizes); ++i) {
    205     ExtensionResource resource = extensions::IconsInfo::GetIconResource(
    206         extension.get(), sizes[i], ExtensionIconSet::MATCH_EXACTLY);
    207     info_list.push_back(ImageLoader::ImageRepresentation(
    208         resource,
    209         ImageLoader::ImageRepresentation::RESIZE_WHEN_LARGER,
    210         gfx::Size(sizes[i], sizes[i]),
    211         ui::SCALE_FACTOR_NONE));
    212   }
    213 
    214   ImageLoader loader;
    215   loader.LoadImagesAsync(extension.get(), info_list,
    216                          base::Bind(&ImageLoaderTest::OnImageLoaded,
    217                                     base::Unretained(this)));
    218 
    219   // The image isn't cached, so we should not have received notification.
    220   EXPECT_EQ(0, image_loaded_count());
    221 
    222   WaitForImageLoad();
    223 
    224   // We should have gotten the image.
    225   EXPECT_EQ(1, image_loaded_count());
    226 
    227   // Check that all images were loaded.
    228   std::vector<gfx::ImageSkiaRep> image_reps =
    229       image_.ToImageSkia()->image_reps();
    230   ASSERT_EQ(2u, image_reps.size());
    231   const gfx::ImageSkiaRep* img_rep1 = &image_reps[0];
    232   const gfx::ImageSkiaRep* img_rep2 = &image_reps[1];
    233   if (img_rep1->pixel_width() > img_rep2->pixel_width()) {
    234     std::swap(img_rep1, img_rep2);
    235   }
    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());
    240 }
    241 
    242 // Tests IsComponentExtensionResource function.
    243 TEST_F(ImageLoaderTest, IsComponentExtensionResource) {
    244   scoped_refptr<Extension> extension(CreateExtension(
    245       "file_manager", Manifest::COMPONENT));
    246   ASSERT_TRUE(extension.get() != NULL);
    247 
    248   ExtensionResource resource = extensions::IconsInfo::GetIconResource(
    249       extension.get(),
    250       extension_misc::EXTENSION_ICON_BITTY,
    251       ExtensionIconSet::MATCH_EXACTLY);
    252 
    253 #if defined(FILE_MANAGER_EXTENSION)
    254   int resource_id;
    255   ASSERT_EQ(true,
    256             ImageLoader::IsComponentExtensionResource(extension->path(),
    257                                                       resource.relative_path(),
    258                                                       &resource_id));
    259   ASSERT_EQ(IDR_FILE_MANAGER_ICON_16, resource_id);
    260 #endif
    261 }
    262