|  | // Copyright 2014 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "extensions/browser/extension_icon_image.h" | 
|  |  | 
|  | #include <vector> | 
|  |  | 
|  | #include "base/json/json_file_value_serializer.h" | 
|  | #include "base/macros.h" | 
|  | #include "base/memory/ptr_util.h" | 
|  | #include "base/path_service.h" | 
|  | #include "base/run_loop.h" | 
|  | #include "content/public/test/test_browser_context.h" | 
|  | #include "content/public/test/test_browser_thread_bundle.h" | 
|  | #include "extensions/browser/extensions_test.h" | 
|  | #include "extensions/browser/image_loader.h" | 
|  | #include "extensions/browser/test_image_loader.h" | 
|  | #include "extensions/common/extension.h" | 
|  | #include "extensions/common/extension_paths.h" | 
|  | #include "extensions/common/manifest.h" | 
|  | #include "extensions/common/manifest_handlers/icons_handler.h" | 
|  | #include "skia/ext/image_operations.h" | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  | #include "ui/base/resource/resource_bundle.h" | 
|  | #include "ui/gfx/image/image_skia_source.h" | 
|  | #include "ui/gfx/skia_util.h" | 
|  |  | 
|  | namespace extensions { | 
|  | namespace { | 
|  |  | 
|  | SkBitmap CreateBlankBitmapForScale(int size_dip, ui::ScaleFactor scale_factor) { | 
|  | SkBitmap bitmap; | 
|  | const float scale = ui::GetScaleForScaleFactor(scale_factor); | 
|  | bitmap.allocN32Pixels(static_cast<int>(size_dip * scale), | 
|  | static_cast<int>(size_dip * scale)); | 
|  | bitmap.eraseColor(SkColorSetARGB(0, 0, 0, 0)); | 
|  | return bitmap; | 
|  | } | 
|  |  | 
|  | SkBitmap EnsureBitmapSize(const SkBitmap& original, int size) { | 
|  | if (original.width() == size && original.height() == size) | 
|  | return original; | 
|  |  | 
|  | SkBitmap resized = skia::ImageOperations::Resize( | 
|  | original, skia::ImageOperations::RESIZE_LANCZOS3, size, size); | 
|  | return resized; | 
|  | } | 
|  |  | 
|  | // Used to test behavior including images defined by an image skia source. | 
|  | // |GetImageForScale| simply returns image representation from the image given | 
|  | // in the ctor. | 
|  | class MockImageSkiaSource : public gfx::ImageSkiaSource { | 
|  | public: | 
|  | explicit MockImageSkiaSource(const gfx::ImageSkia& image) | 
|  | : image_(image) { | 
|  | } | 
|  | ~MockImageSkiaSource() override {} | 
|  |  | 
|  | gfx::ImageSkiaRep GetImageForScale(float scale) override { | 
|  | return image_.GetRepresentation(scale); | 
|  | } | 
|  |  | 
|  | private: | 
|  | gfx::ImageSkia image_; | 
|  | }; | 
|  |  | 
|  | class ExtensionIconImageTest : public ExtensionsTest, | 
|  | public IconImage::Observer { | 
|  | public: | 
|  | ExtensionIconImageTest() | 
|  | : ExtensionsTest(std::make_unique<content::TestBrowserThreadBundle>()), | 
|  | image_loaded_count_(0), | 
|  | quit_in_image_loaded_(false) {} | 
|  |  | 
|  | ~ExtensionIconImageTest() override {} | 
|  |  | 
|  | void WaitForImageLoad() { | 
|  | quit_in_image_loaded_ = true; | 
|  | base::RunLoop().Run(); | 
|  | quit_in_image_loaded_ = false; | 
|  | } | 
|  |  | 
|  | int ImageLoadedCount() { | 
|  | int result = image_loaded_count_; | 
|  | image_loaded_count_ = 0; | 
|  | return result; | 
|  | } | 
|  |  | 
|  | scoped_refptr<Extension> CreateExtension(const char* name, | 
|  | Manifest::Location location) { | 
|  | // Create and load an extension. | 
|  | base::FilePath test_file; | 
|  | if (!PathService::Get(DIR_TEST_DATA, &test_file)) { | 
|  | EXPECT_FALSE(true); | 
|  | return NULL; | 
|  | } | 
|  | test_file = test_file.AppendASCII(name); | 
|  | int error_code = 0; | 
|  | std::string error; | 
|  | JSONFileValueDeserializer deserializer( | 
|  | test_file.AppendASCII("manifest.json")); | 
|  | std::unique_ptr<base::DictionaryValue> valid_value = | 
|  | base::DictionaryValue::From( | 
|  | deserializer.Deserialize(&error_code, &error)); | 
|  | EXPECT_EQ(0, error_code) << error; | 
|  | if (error_code != 0) | 
|  | return NULL; | 
|  |  | 
|  | EXPECT_TRUE(valid_value.get()); | 
|  | if (!valid_value) | 
|  | return NULL; | 
|  |  | 
|  | return Extension::Create(test_file, location, *valid_value, | 
|  | Extension::NO_FLAGS, &error); | 
|  | } | 
|  |  | 
|  | // IconImage::Delegate overrides: | 
|  | void OnExtensionIconImageChanged(IconImage* image) override { | 
|  | image_loaded_count_++; | 
|  | if (quit_in_image_loaded_) | 
|  | base::RunLoop::QuitCurrentWhenIdleDeprecated(); | 
|  | } | 
|  |  | 
|  | gfx::ImageSkia GetDefaultIcon() { | 
|  | return gfx::ImageSkia(gfx::ImageSkiaRep(gfx::Size(16, 16), 1.0f)); | 
|  | } | 
|  |  | 
|  | private: | 
|  | int image_loaded_count_; | 
|  | bool quit_in_image_loaded_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(ExtensionIconImageTest); | 
|  | }; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | TEST_F(ExtensionIconImageTest, Basic) { | 
|  | std::vector<ui::ScaleFactor> supported_factors; | 
|  | supported_factors.push_back(ui::SCALE_FACTOR_100P); | 
|  | supported_factors.push_back(ui::SCALE_FACTOR_200P); | 
|  | ui::test::ScopedSetSupportedScaleFactors scoped_supported(supported_factors); | 
|  | scoped_refptr<Extension> extension(CreateExtension( | 
|  | "extension_icon_image", Manifest::INVALID_LOCATION)); | 
|  | ASSERT_TRUE(extension.get() != NULL); | 
|  |  | 
|  | gfx::ImageSkia default_icon = GetDefaultIcon(); | 
|  |  | 
|  | // Load images we expect to find as representations in icon_image, so we | 
|  | // can later use them to validate icon_image. | 
|  | SkBitmap bitmap_16 = | 
|  | TestImageLoader::LoadAndGetExtensionBitmap(extension.get(), "16.png", 16); | 
|  | ASSERT_FALSE(bitmap_16.empty()); | 
|  |  | 
|  | // There is no image of size 32 defined in the extension manifest, so we | 
|  | // should expect manifest image of size 48 resized to size 32. | 
|  | SkBitmap bitmap_48_resized_to_32 = | 
|  | TestImageLoader::LoadAndGetExtensionBitmap(extension.get(), "48.png", 32); | 
|  | ASSERT_FALSE(bitmap_48_resized_to_32.empty()); | 
|  |  | 
|  | IconImage image(browser_context(), | 
|  | extension.get(), | 
|  | IconsInfo::GetIcons(extension.get()), | 
|  | 16, | 
|  | default_icon, | 
|  | this); | 
|  |  | 
|  | // No representations in |image_| yet. | 
|  | gfx::ImageSkia::ImageSkiaReps image_reps = image.image_skia().image_reps(); | 
|  | ASSERT_EQ(0u, image_reps.size()); | 
|  |  | 
|  | // Gets representation for a scale factor. | 
|  | gfx::ImageSkiaRep representation = image.image_skia().GetRepresentation(1.0f); | 
|  |  | 
|  | // Before the image representation is loaded, image should contain blank | 
|  | // image representation. | 
|  | EXPECT_TRUE(gfx::BitmapsAreEqual( | 
|  | representation.sk_bitmap(), | 
|  | CreateBlankBitmapForScale(16, ui::SCALE_FACTOR_100P))); | 
|  |  | 
|  | WaitForImageLoad(); | 
|  | EXPECT_EQ(1, ImageLoadedCount()); | 
|  | ASSERT_EQ(1u, image.image_skia().image_reps().size()); | 
|  |  | 
|  | representation = image.image_skia().GetRepresentation(1.0f); | 
|  |  | 
|  | // We should get the right representation now. | 
|  | EXPECT_TRUE(gfx::BitmapsAreEqual(representation.sk_bitmap(), bitmap_16)); | 
|  | EXPECT_EQ(16, representation.pixel_width()); | 
|  |  | 
|  | // Gets representation for an additional scale factor. | 
|  | representation = image.image_skia().GetRepresentation(2.0f); | 
|  |  | 
|  | EXPECT_TRUE(gfx::BitmapsAreEqual( | 
|  | representation.sk_bitmap(), | 
|  | CreateBlankBitmapForScale(16, ui::SCALE_FACTOR_200P))); | 
|  |  | 
|  | WaitForImageLoad(); | 
|  | EXPECT_EQ(1, ImageLoadedCount()); | 
|  | ASSERT_EQ(2u, image.image_skia().image_reps().size()); | 
|  |  | 
|  | representation = image.image_skia().GetRepresentation(2.0f); | 
|  |  | 
|  | // Image should have been resized. | 
|  | EXPECT_EQ(32, representation.pixel_width()); | 
|  | EXPECT_TRUE(gfx::BitmapsAreEqual(representation.sk_bitmap(), | 
|  | bitmap_48_resized_to_32)); | 
|  | } | 
|  |  | 
|  | // There is no resource with either exact or bigger size, but there is a smaller | 
|  | // resource. | 
|  | TEST_F(ExtensionIconImageTest, FallbackToSmallerWhenNoBigger) { | 
|  | std::vector<ui::ScaleFactor> supported_factors; | 
|  | supported_factors.push_back(ui::SCALE_FACTOR_100P); | 
|  | supported_factors.push_back(ui::SCALE_FACTOR_200P); | 
|  | ui::test::ScopedSetSupportedScaleFactors scoped_supported(supported_factors); | 
|  | scoped_refptr<Extension> extension(CreateExtension( | 
|  | "extension_icon_image", Manifest::INVALID_LOCATION)); | 
|  | ASSERT_TRUE(extension.get() != NULL); | 
|  |  | 
|  | gfx::ImageSkia default_icon = GetDefaultIcon(); | 
|  |  | 
|  | // Load images we expect to find as representations in icon_image, so we | 
|  | // can later use them to validate icon_image. | 
|  | SkBitmap bitmap_48 = | 
|  | TestImageLoader::LoadAndGetExtensionBitmap(extension.get(), "48.png", 48); | 
|  | ASSERT_FALSE(bitmap_48.empty()); | 
|  |  | 
|  | IconImage image(browser_context(), | 
|  | extension.get(), | 
|  | IconsInfo::GetIcons(extension.get()), | 
|  | 32, | 
|  | default_icon, | 
|  | this); | 
|  |  | 
|  | gfx::ImageSkiaRep representation = image.image_skia().GetRepresentation(2.0f); | 
|  |  | 
|  | WaitForImageLoad(); | 
|  | EXPECT_EQ(1, ImageLoadedCount()); | 
|  | ASSERT_EQ(1u, image.image_skia().image_reps().size()); | 
|  |  | 
|  | representation = image.image_skia().GetRepresentation(2.0f); | 
|  |  | 
|  | // We should have loaded the biggest smaller resource resized to the actual | 
|  | // size. | 
|  | EXPECT_EQ(2.0f, representation.scale()); | 
|  | EXPECT_EQ(64, representation.pixel_width()); | 
|  | EXPECT_TRUE(gfx::BitmapsAreEqual(representation.sk_bitmap(), | 
|  | EnsureBitmapSize(bitmap_48, 64))); | 
|  | } | 
|  |  | 
|  | // There is no resource with exact size, but there is a smaller and a bigger | 
|  | // one. The bigger resource should be loaded. | 
|  | TEST_F(ExtensionIconImageTest, FallbackToBigger) { | 
|  | scoped_refptr<Extension> extension(CreateExtension( | 
|  | "extension_icon_image", Manifest::INVALID_LOCATION)); | 
|  | ASSERT_TRUE(extension.get() != NULL); | 
|  |  | 
|  | gfx::ImageSkia default_icon = GetDefaultIcon(); | 
|  |  | 
|  | // Load images we expect to find as representations in icon_image, so we | 
|  | // can later use them to validate icon_image. | 
|  | SkBitmap bitmap_24 = | 
|  | TestImageLoader::LoadAndGetExtensionBitmap(extension.get(), "24.png", 24); | 
|  | ASSERT_FALSE(bitmap_24.empty()); | 
|  |  | 
|  | IconImage image(browser_context(), | 
|  | extension.get(), | 
|  | IconsInfo::GetIcons(extension.get()), | 
|  | 17, | 
|  | default_icon, | 
|  | this); | 
|  |  | 
|  | gfx::ImageSkiaRep representation = image.image_skia().GetRepresentation(1.0f); | 
|  |  | 
|  | WaitForImageLoad(); | 
|  | EXPECT_EQ(1, ImageLoadedCount()); | 
|  | ASSERT_EQ(1u, image.image_skia().image_reps().size()); | 
|  |  | 
|  | representation = image.image_skia().GetRepresentation(1.0f); | 
|  |  | 
|  | // We should have loaded the smallest bigger (resized) resource. | 
|  | EXPECT_EQ(1.0f, representation.scale()); | 
|  | EXPECT_EQ(17, representation.pixel_width()); | 
|  | EXPECT_TRUE(gfx::BitmapsAreEqual(representation.sk_bitmap(), | 
|  | EnsureBitmapSize(bitmap_24, 17))); | 
|  | } | 
|  |  | 
|  | // If resource set is empty, |GetRepresentation| should synchronously return | 
|  | // default icon, without notifying observer of image change. | 
|  | TEST_F(ExtensionIconImageTest, NoResources) { | 
|  | scoped_refptr<Extension> extension(CreateExtension( | 
|  | "extension_icon_image", Manifest::INVALID_LOCATION)); | 
|  | ASSERT_TRUE(extension.get() != NULL); | 
|  |  | 
|  | ExtensionIconSet empty_icon_set; | 
|  | gfx::ImageSkia default_icon = GetDefaultIcon(); | 
|  |  | 
|  | const int kRequestedSize = 24; | 
|  | IconImage image(browser_context(), | 
|  | extension.get(), | 
|  | empty_icon_set, | 
|  | kRequestedSize, | 
|  | default_icon, | 
|  | this); | 
|  |  | 
|  | // Default icon is loaded asynchronously. | 
|  | image.image_skia().GetRepresentation(1.0f); | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | gfx::ImageSkiaRep representation = image.image_skia().GetRepresentation(1.0f); | 
|  |  | 
|  | EXPECT_TRUE(gfx::BitmapsAreEqual( | 
|  | representation.sk_bitmap(), | 
|  | EnsureBitmapSize( | 
|  | default_icon.GetRepresentation(1.0f).sk_bitmap(), | 
|  | kRequestedSize))); | 
|  |  | 
|  | EXPECT_EQ(1, ImageLoadedCount()); | 
|  | // We should have a default icon representation. | 
|  | ASSERT_EQ(1u, image.image_skia().image_reps().size()); | 
|  |  | 
|  | representation = image.image_skia().GetRepresentation(1.0f); | 
|  | EXPECT_TRUE(gfx::BitmapsAreEqual( | 
|  | representation.sk_bitmap(), | 
|  | EnsureBitmapSize( | 
|  | default_icon.GetRepresentation(1.0f).sk_bitmap(), | 
|  | kRequestedSize))); | 
|  | } | 
|  |  | 
|  | // If resource set is invalid, image load should be done asynchronously and | 
|  | // the observer should be notified when it's done. |GetRepresentation| should | 
|  | // return the default icon representation once image load is done. | 
|  | TEST_F(ExtensionIconImageTest, InvalidResource) { | 
|  | scoped_refptr<Extension> extension(CreateExtension( | 
|  | "extension_icon_image", Manifest::INVALID_LOCATION)); | 
|  | ASSERT_TRUE(extension.get() != NULL); | 
|  |  | 
|  | const int kInvalidIconSize = 24; | 
|  | ExtensionIconSet invalid_icon_set; | 
|  | invalid_icon_set.Add(kInvalidIconSize, "invalid.png"); | 
|  |  | 
|  | gfx::ImageSkia default_icon = GetDefaultIcon(); | 
|  |  | 
|  | IconImage image(browser_context(), | 
|  | extension.get(), | 
|  | invalid_icon_set, | 
|  | kInvalidIconSize, | 
|  | default_icon, | 
|  | this); | 
|  |  | 
|  | gfx::ImageSkiaRep representation = image.image_skia().GetRepresentation(1.0f); | 
|  | EXPECT_TRUE(gfx::BitmapsAreEqual( | 
|  | representation.sk_bitmap(), | 
|  | CreateBlankBitmapForScale(kInvalidIconSize, ui::SCALE_FACTOR_100P))); | 
|  |  | 
|  | WaitForImageLoad(); | 
|  | EXPECT_EQ(1, ImageLoadedCount()); | 
|  | // We should have default icon representation now. | 
|  | ASSERT_EQ(1u, image.image_skia().image_reps().size()); | 
|  |  | 
|  | representation = image.image_skia().GetRepresentation(1.0f); | 
|  | EXPECT_TRUE(gfx::BitmapsAreEqual( | 
|  | representation.sk_bitmap(), | 
|  | EnsureBitmapSize( | 
|  | default_icon.GetRepresentation(1.0f).sk_bitmap(), | 
|  | kInvalidIconSize))); | 
|  | } | 
|  |  | 
|  | // Test that IconImage works with lazily (but synchronously) created default | 
|  | // icon when IconImage returns synchronously. | 
|  | TEST_F(ExtensionIconImageTest, LazyDefaultIcon) { | 
|  | scoped_refptr<Extension> extension(CreateExtension( | 
|  | "extension_icon_image", Manifest::INVALID_LOCATION)); | 
|  | ASSERT_TRUE(extension.get() != NULL); | 
|  |  | 
|  | gfx::ImageSkia default_icon = GetDefaultIcon(); | 
|  | gfx::ImageSkia lazy_default_icon( | 
|  | std::make_unique<MockImageSkiaSource>(default_icon), default_icon.size()); | 
|  |  | 
|  | ExtensionIconSet empty_icon_set; | 
|  |  | 
|  | const int kRequestedSize = 128; | 
|  | IconImage image(browser_context(), | 
|  | extension.get(), | 
|  | empty_icon_set, | 
|  | kRequestedSize, | 
|  | lazy_default_icon, | 
|  | this); | 
|  |  | 
|  | ASSERT_FALSE(lazy_default_icon.HasRepresentation(1.0f)); | 
|  |  | 
|  | // Default icon is loaded asynchronously. | 
|  | image.image_skia().GetRepresentation(1.0f); | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | gfx::ImageSkiaRep representation = image.image_skia().GetRepresentation(1.0f); | 
|  |  | 
|  | // The resouce set is empty, so we should get the result right away. | 
|  | EXPECT_TRUE(lazy_default_icon.HasRepresentation(1.0f)); | 
|  | EXPECT_TRUE(gfx::BitmapsAreEqual( | 
|  | representation.sk_bitmap(), | 
|  | EnsureBitmapSize( | 
|  | default_icon.GetRepresentation(1.0f).sk_bitmap(), | 
|  | kRequestedSize))); | 
|  |  | 
|  | // We should have a default icon representation. | 
|  | ASSERT_EQ(1u, image.image_skia().image_reps().size()); | 
|  | } | 
|  |  | 
|  | // Test that IconImage works with lazily (but synchronously) created default | 
|  | // icon when IconImage returns asynchronously. | 
|  | TEST_F(ExtensionIconImageTest, LazyDefaultIcon_AsyncIconImage) { | 
|  | scoped_refptr<Extension> extension(CreateExtension( | 
|  | "extension_icon_image", Manifest::INVALID_LOCATION)); | 
|  | ASSERT_TRUE(extension.get() != NULL); | 
|  |  | 
|  | gfx::ImageSkia default_icon = GetDefaultIcon(); | 
|  | gfx::ImageSkia lazy_default_icon( | 
|  | std::make_unique<MockImageSkiaSource>(default_icon), default_icon.size()); | 
|  |  | 
|  | const int kInvalidIconSize = 24; | 
|  | ExtensionIconSet invalid_icon_set; | 
|  | invalid_icon_set.Add(kInvalidIconSize, "invalid.png"); | 
|  |  | 
|  | IconImage image(browser_context(), | 
|  | extension.get(), | 
|  | invalid_icon_set, | 
|  | kInvalidIconSize, | 
|  | lazy_default_icon, | 
|  | this); | 
|  |  | 
|  | ASSERT_FALSE(lazy_default_icon.HasRepresentation(1.0f)); | 
|  |  | 
|  | gfx::ImageSkiaRep representation = image.image_skia().GetRepresentation(1.0f); | 
|  |  | 
|  | WaitForImageLoad(); | 
|  | EXPECT_EQ(1, ImageLoadedCount()); | 
|  | // We should have default icon representation now. | 
|  | ASSERT_EQ(1u, image.image_skia().image_reps().size()); | 
|  |  | 
|  | EXPECT_TRUE(lazy_default_icon.HasRepresentation(1.0f)); | 
|  |  | 
|  | representation = image.image_skia().GetRepresentation(1.0f); | 
|  | EXPECT_TRUE(gfx::BitmapsAreEqual( | 
|  | representation.sk_bitmap(), | 
|  | EnsureBitmapSize( | 
|  | default_icon.GetRepresentation(1.0f).sk_bitmap(), | 
|  | kInvalidIconSize))); | 
|  | } | 
|  |  | 
|  | // Tests behavior of image created by IconImage after IconImage host goes | 
|  | // away. The image should still return loaded representations. If requested | 
|  | // representation was not loaded while IconImage host was around, transparent | 
|  | // representations should be returned. | 
|  | TEST_F(ExtensionIconImageTest, IconImageDestruction) { | 
|  | scoped_refptr<Extension> extension(CreateExtension( | 
|  | "extension_icon_image", Manifest::INVALID_LOCATION)); | 
|  | ASSERT_TRUE(extension.get() != NULL); | 
|  |  | 
|  | gfx::ImageSkia default_icon = GetDefaultIcon(); | 
|  |  | 
|  | // Load images we expect to find as representations in icon_image, so we | 
|  | // can later use them to validate icon_image. | 
|  | SkBitmap bitmap_16 = | 
|  | TestImageLoader::LoadAndGetExtensionBitmap(extension.get(), "16.png", 16); | 
|  | ASSERT_FALSE(bitmap_16.empty()); | 
|  |  | 
|  | std::unique_ptr<IconImage> image(new IconImage( | 
|  | browser_context(), extension.get(), IconsInfo::GetIcons(extension.get()), | 
|  | 16, default_icon, this)); | 
|  |  | 
|  | // Load an image representation. | 
|  | gfx::ImageSkiaRep representation = | 
|  | image->image_skia().GetRepresentation(1.0f); | 
|  |  | 
|  | WaitForImageLoad(); | 
|  | EXPECT_EQ(1, ImageLoadedCount()); | 
|  | ASSERT_EQ(1u, image->image_skia().image_reps().size()); | 
|  |  | 
|  | // Stash loaded image skia, and destroy |image|. | 
|  | gfx::ImageSkia image_skia = image->image_skia(); | 
|  | image.reset(); | 
|  | extension = NULL; | 
|  |  | 
|  | // Image skia should still be able to get previously loaded representation. | 
|  | representation = image_skia.GetRepresentation(1.0f); | 
|  |  | 
|  | EXPECT_EQ(1.0f, representation.scale()); | 
|  | EXPECT_EQ(16, representation.pixel_width()); | 
|  | EXPECT_TRUE(gfx::BitmapsAreEqual(representation.sk_bitmap(), bitmap_16)); | 
|  |  | 
|  | // When requesting another representation, we should not crash and return some | 
|  | // image of the size. It could be blank or a rescale from the existing 1.0f | 
|  | // icon. | 
|  | representation = image_skia.GetRepresentation(2.0f); | 
|  |  | 
|  | EXPECT_EQ(16, representation.GetWidth()); | 
|  | EXPECT_EQ(16, representation.GetHeight()); | 
|  | EXPECT_EQ(2.0f, representation.scale()); | 
|  | } | 
|  |  | 
|  | // Test that new representations added to the image of an IconImageSkia are | 
|  | // cached for future use. | 
|  | TEST_F(ExtensionIconImageTest, ImageCachesNewRepresentations) { | 
|  | // Load up an extension and create an icon image. | 
|  | scoped_refptr<Extension> extension( | 
|  | CreateExtension("extension_icon_image", Manifest::INVALID_LOCATION)); | 
|  | ASSERT_TRUE(extension.get() != NULL); | 
|  | gfx::ImageSkia default_icon = GetDefaultIcon(); | 
|  | std::unique_ptr<IconImage> icon_image(new IconImage( | 
|  | browser_context(), extension.get(), IconsInfo::GetIcons(extension.get()), | 
|  | 16, default_icon, this)); | 
|  |  | 
|  | // Load an blank image representation. | 
|  | EXPECT_EQ(0, ImageLoadedCount()); | 
|  | icon_image->image_skia().GetRepresentation(1.0f); | 
|  | EXPECT_EQ(0, ImageLoadedCount()); | 
|  | WaitForImageLoad(); | 
|  | EXPECT_EQ(1, ImageLoadedCount()); | 
|  | icon_image->image_skia().GetRepresentation(1.0f); | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | EXPECT_EQ(0, ImageLoadedCount()); | 
|  | icon_image->image_skia().GetRepresentation(1.0f); | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | EXPECT_EQ(0, ImageLoadedCount()); | 
|  | } | 
|  |  | 
|  | }  // namespace extensions |