blob: cea9a78bec488ff4ae31a5e5e2acef4930014078 [file] [log] [blame]
// Copyright (c) 2012 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 "chrome/browser/extensions/extension_action_icon_factory.h"
#include "base/file_util.h"
#include "base/json/json_file_value_serializer.h"
#include "base/message_loop.h"
#include "base/path_service.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/extensions/extension.h"
#include "chrome/common/extensions/extension_action.h"
#include "content/public/test/test_browser_thread.h"
#include "grit/theme_resources.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.h"
#include "ui/gfx/skia_util.h"
#include "webkit/glue/image_decoder.h"
using content::BrowserThread;
using extensions::Extension;
namespace {
bool ImageRepsAreEqual(const gfx::ImageSkiaRep& image_rep1,
const gfx::ImageSkiaRep& image_rep2) {
return image_rep1.scale_factor() == image_rep2.scale_factor() &&
gfx::BitmapsAreEqual(image_rep1.sk_bitmap(), image_rep2.sk_bitmap());
}
gfx::Image EnsureImageSize(const gfx::Image& original, int size) {
const SkBitmap* original_bitmap = original.ToSkBitmap();
if (original_bitmap->width() == size && original_bitmap->height() == size)
return original;
SkBitmap resized = skia::ImageOperations::Resize(
*original.ToSkBitmap(), skia::ImageOperations::RESIZE_LANCZOS3,
size, size);
return gfx::Image(resized);
}
gfx::ImageSkiaRep CreateBlankRep(int size_dip, ui::ScaleFactor scale_factor) {
SkBitmap bitmap;
const float scale = ui::GetScaleFactorScale(scale_factor);
bitmap.setConfig(SkBitmap::kARGB_8888_Config,
static_cast<int>(size_dip * scale),
static_cast<int>(size_dip * scale));
bitmap.allocPixels();
bitmap.eraseColor(SkColorSetARGB(0, 0, 0, 0));
return gfx::ImageSkiaRep(bitmap, scale_factor);
}
gfx::Image LoadIcon(const std::string& filename) {
FilePath path;
PathService::Get(chrome::DIR_TEST_DATA, &path);
path = path.AppendASCII("extensions/api_test").AppendASCII(filename);
std::string file_contents;
file_util::ReadFileToString(path, &file_contents);
const unsigned char* data =
reinterpret_cast<const unsigned char*>(file_contents.data());
SkBitmap bitmap;
webkit_glue::ImageDecoder decoder;
bitmap = decoder.Decode(data, file_contents.length());
return gfx::Image(bitmap);
}
class ExtensionActionIconFactoryTest
: public testing::Test,
public ExtensionActionIconFactory::Observer {
public:
ExtensionActionIconFactoryTest()
: quit_in_icon_updated_(false),
ui_thread_(BrowserThread::UI, &ui_loop_),
file_thread_(BrowserThread::FILE),
io_thread_(BrowserThread::IO) {
}
virtual ~ExtensionActionIconFactoryTest() {}
void WaitForIconUpdate() {
quit_in_icon_updated_ = true;
MessageLoop::current()->Run();
quit_in_icon_updated_ = false;
}
scoped_refptr<Extension> CreateExtension(const char* name,
Extension::Location location) {
// Create and load an extension.
FilePath test_file;
if (!PathService::Get(chrome::DIR_TEST_DATA, &test_file)) {
EXPECT_FALSE(true);
return NULL;
}
test_file = test_file.AppendASCII("extensions/api_test").AppendASCII(name);
int error_code = 0;
std::string error;
JSONFileValueSerializer serializer(test_file.AppendASCII("manifest.json"));
scoped_ptr<DictionaryValue> valid_value(
static_cast<DictionaryValue*>(serializer.Deserialize(&error_code,
&error)));
EXPECT_EQ(0, error_code) << error;
if (error_code != 0)
return NULL;
EXPECT_TRUE(valid_value.get());
if (!valid_value.get())
return NULL;
return Extension::Create(test_file, location, *valid_value,
Extension::NO_FLAGS, &error);
}
// testing::Test overrides:
virtual void SetUp() OVERRIDE {
file_thread_.Start();
io_thread_.Start();
}
// ExtensionActionIconFactory::Observer overrides:
virtual void OnIconUpdated() OVERRIDE {
if (quit_in_icon_updated_)
MessageLoop::current()->Quit();
}
gfx::ImageSkia GetFavicon() {
return *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
IDR_EXTENSIONS_FAVICON);
}
private:
bool quit_in_icon_updated_;
MessageLoop ui_loop_;
content::TestBrowserThread ui_thread_;
content::TestBrowserThread file_thread_;
content::TestBrowserThread io_thread_;
DISALLOW_COPY_AND_ASSIGN(ExtensionActionIconFactoryTest);
};
// If there is no default icon, and the icon has not been set using |SetIcon|,
// the factory should return favicon.
TEST_F(ExtensionActionIconFactoryTest, NoIcons) {
// Load an extension that has browser action without default icon set in the
// manifest and does not call |SetIcon| by default.
scoped_refptr<Extension> extension(CreateExtension(
"browser_action/no_icon", Extension::INVALID));
ASSERT_TRUE(extension.get() != NULL);
ASSERT_TRUE(extension->browser_action());
ASSERT_FALSE(extension->browser_action()->default_icon());
ASSERT_TRUE(
extension->browser_action()->GetExplicitlySetIcon(0 /*tab id*/).isNull());
gfx::ImageSkia favicon = GetFavicon();
ExtensionActionIconFactory icon_factory(extension,
extension->browser_action(),
this);
gfx::Image icon = icon_factory.GetIcon(0);
EXPECT_TRUE(ImageRepsAreEqual(
favicon.GetRepresentation(ui::SCALE_FACTOR_100P),
icon.ToImageSkia()->GetRepresentation(ui::SCALE_FACTOR_100P)));
}
// If the icon has been set using |SetIcon|, the factory should return that
// icon.
TEST_F(ExtensionActionIconFactoryTest, AfterSetIcon) {
// Load an extension that has browser action without default icon set in the
// manifest and does not call |SetIcon| by default (but has an browser action
// icon resource).
scoped_refptr<Extension> extension(CreateExtension(
"browser_action/no_icon", Extension::INVALID));
ASSERT_TRUE(extension.get() != NULL);
ASSERT_TRUE(extension->browser_action());
ASSERT_FALSE(extension->browser_action()->default_icon());
ASSERT_TRUE(
extension->browser_action()->GetExplicitlySetIcon(0 /*tab id*/).isNull());
gfx::Image set_icon = LoadIcon("browser_action/no_icon/icon.png");
ASSERT_FALSE(set_icon.IsEmpty());
extension->browser_action()->SetIcon(0, set_icon);
ASSERT_FALSE(
extension->browser_action()->GetExplicitlySetIcon(0 /*tab id*/).isNull());
ExtensionActionIconFactory icon_factory(extension,
extension->browser_action(),
this);
gfx::Image icon = icon_factory.GetIcon(0);
EXPECT_TRUE(ImageRepsAreEqual(
set_icon.ToImageSkia()->GetRepresentation(ui::SCALE_FACTOR_100P),
icon.ToImageSkia()->GetRepresentation(ui::SCALE_FACTOR_100P)));
// It should still return favicon for another tabs.
icon = icon_factory.GetIcon(1);
EXPECT_TRUE(ImageRepsAreEqual(
GetFavicon().GetRepresentation(ui::SCALE_FACTOR_100P),
icon.ToImageSkia()->GetRepresentation(ui::SCALE_FACTOR_100P)));
}
// If there is a default icon, and the icon has not been set using |SetIcon|,
// the factory should return the default icon.
TEST_F(ExtensionActionIconFactoryTest, DefaultIcon) {
// Load an extension that has browser action without default icon set in the
// manifest and does not call |SetIcon| by default (but has an browser action
// icon resource).
scoped_refptr<Extension> extension(CreateExtension(
"browser_action/no_icon", Extension::INVALID));
ASSERT_TRUE(extension.get() != NULL);
ASSERT_TRUE(extension->browser_action());
ASSERT_FALSE(extension->browser_action()->default_icon());
ASSERT_TRUE(
extension->browser_action()->GetExplicitlySetIcon(0 /*tab id*/).isNull());
gfx::Image default_icon =
EnsureImageSize(LoadIcon("browser_action/no_icon/icon.png"), 19);
ASSERT_FALSE(default_icon.IsEmpty());
scoped_ptr<ExtensionIconSet> default_icon_set(new ExtensionIconSet());
default_icon_set->Add(19, "icon.png");
extension->browser_action()->set_default_icon(default_icon_set.Pass());
ASSERT_TRUE(extension->browser_action()->default_icon());
ExtensionActionIconFactory icon_factory(extension,
extension->browser_action(),
this);
gfx::Image icon = icon_factory.GetIcon(0);
// The icon should be loaded asynchronously. Initially a transparent icon
// should be returned.
EXPECT_TRUE(ImageRepsAreEqual(
CreateBlankRep(19, ui::SCALE_FACTOR_100P),
icon.ToImageSkia()->GetRepresentation(ui::SCALE_FACTOR_100P)));
WaitForIconUpdate();
icon = icon_factory.GetIcon(0);
// The default icon representation should be loaded at this point.
EXPECT_TRUE(ImageRepsAreEqual(
default_icon.ToImageSkia()->GetRepresentation(ui::SCALE_FACTOR_100P),
icon.ToImageSkia()->GetRepresentation(ui::SCALE_FACTOR_100P)));
// The same icon should be returned for the other tabs.
icon = icon_factory.GetIcon(1);
EXPECT_TRUE(ImageRepsAreEqual(
default_icon.ToImageSkia()->GetRepresentation(ui::SCALE_FACTOR_100P),
icon.ToImageSkia()->GetRepresentation(ui::SCALE_FACTOR_100P)));
}
} // namespace