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.
5 #include "chrome/browser/extensions/extension_action_icon_factory.h"
7 #include "base/command_line.h"
8 #include "base/file_util.h"
9 #include "base/json/json_file_value_serializer.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/path_service.h"
12 #include "chrome/browser/extensions/extension_action.h"
13 #include "chrome/browser/extensions/extension_action_manager.h"
14 #include "chrome/browser/extensions/extension_service.h"
15 #include "chrome/browser/extensions/test_extension_system.h"
16 #include "chrome/common/chrome_paths.h"
17 #include "chrome/test/base/testing_profile.h"
18 #include "content/public/test/test_browser_thread.h"
19 #include "extensions/common/extension.h"
20 #include "grit/theme_resources.h"
21 #include "skia/ext/image_operations.h"
22 #include "testing/gtest/include/gtest/gtest.h"
23 #include "ui/base/resource/resource_bundle.h"
24 #include "ui/gfx/codec/png_codec.h"
25 #include "ui/gfx/image/image_skia.h"
26 #include "ui/gfx/skia_util.h"
28 #if defined(OS_CHROMEOS)
29 #include "chrome/browser/chromeos/login/user_manager.h"
30 #include "chrome/browser/chromeos/settings/cros_settings.h"
31 #include "chrome/browser/chromeos/settings/device_settings_service.h"
34 using content::BrowserThread
;
36 namespace extensions
{
39 bool ImageRepsAreEqual(const gfx::ImageSkiaRep
& image_rep1
,
40 const gfx::ImageSkiaRep
& image_rep2
) {
41 return image_rep1
.scale() == image_rep2
.scale() &&
42 gfx::BitmapsAreEqual(image_rep1
.sk_bitmap(), image_rep2
.sk_bitmap());
45 gfx::Image
EnsureImageSize(const gfx::Image
& original
, int size
) {
46 const SkBitmap
* original_bitmap
= original
.ToSkBitmap();
47 if (original_bitmap
->width() == size
&& original_bitmap
->height() == size
)
50 SkBitmap resized
= skia::ImageOperations::Resize(
51 *original
.ToSkBitmap(), skia::ImageOperations::RESIZE_LANCZOS3
,
53 return gfx::Image::CreateFrom1xBitmap(resized
);
56 gfx::ImageSkiaRep
CreateBlankRep(int size_dip
, float scale
) {
58 bitmap
.setConfig(SkBitmap::kARGB_8888_Config
,
59 static_cast<int>(size_dip
* scale
),
60 static_cast<int>(size_dip
* scale
));
62 bitmap
.eraseColor(SkColorSetARGB(0, 0, 0, 0));
63 return gfx::ImageSkiaRep(bitmap
, scale
);
66 gfx::Image
LoadIcon(const std::string
& filename
) {
68 PathService::Get(chrome::DIR_TEST_DATA
, &path
);
69 path
= path
.AppendASCII("extensions/api_test").AppendASCII(filename
);
71 std::string file_contents
;
72 base::ReadFileToString(path
, &file_contents
);
73 const unsigned char* data
=
74 reinterpret_cast<const unsigned char*>(file_contents
.data());
77 gfx::PNGCodec::Decode(data
, file_contents
.length(), &bitmap
);
79 return gfx::Image::CreateFrom1xBitmap(bitmap
);
82 class ExtensionActionIconFactoryTest
83 : public testing::Test
,
84 public ExtensionActionIconFactory::Observer
{
86 ExtensionActionIconFactoryTest()
87 : quit_in_icon_updated_(false),
88 ui_thread_(BrowserThread::UI
, &ui_loop_
),
89 file_thread_(BrowserThread::FILE),
90 io_thread_(BrowserThread::IO
) {
93 virtual ~ExtensionActionIconFactoryTest() {}
95 void WaitForIconUpdate() {
96 quit_in_icon_updated_
= true;
97 base::MessageLoop::current()->Run();
98 quit_in_icon_updated_
= false;
101 scoped_refptr
<Extension
> CreateExtension(const char* name
,
102 Manifest::Location location
) {
103 // Create and load an extension.
104 base::FilePath test_file
;
105 if (!PathService::Get(chrome::DIR_TEST_DATA
, &test_file
)) {
109 test_file
= test_file
.AppendASCII("extensions/api_test").AppendASCII(name
);
112 JSONFileValueSerializer
serializer(test_file
.AppendASCII("manifest.json"));
113 scoped_ptr
<base::DictionaryValue
> valid_value(
114 static_cast<base::DictionaryValue
*>(serializer
.Deserialize(&error_code
,
116 EXPECT_EQ(0, error_code
) << error
;
120 EXPECT_TRUE(valid_value
.get());
124 scoped_refptr
<Extension
> extension
=
125 Extension::Create(test_file
, location
, *valid_value
,
126 Extension::NO_FLAGS
, &error
);
127 EXPECT_TRUE(extension
.get()) << error
;
129 extension_service_
->AddExtension(extension
.get());
133 // testing::Test overrides:
134 virtual void SetUp() OVERRIDE
{
135 file_thread_
.Start();
137 profile_
.reset(new TestingProfile
);
138 CommandLine
command_line(CommandLine::NO_PROGRAM
);
139 extension_service_
= static_cast<extensions::TestExtensionSystem
*>(
140 extensions::ExtensionSystem::Get(profile_
.get()))->
141 CreateExtensionService(&command_line
, base::FilePath(), false);
144 virtual void TearDown() OVERRIDE
{
145 profile_
.reset(); // Get all DeleteSoon calls sent to ui_loop_.
146 ui_loop_
.RunUntilIdle();
149 // ExtensionActionIconFactory::Observer overrides:
150 virtual void OnIconUpdated() OVERRIDE
{
151 if (quit_in_icon_updated_
)
152 base::MessageLoop::current()->Quit();
155 gfx::ImageSkia
GetFavicon() {
156 return *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
157 IDR_EXTENSIONS_FAVICON
);
160 ExtensionAction
* GetBrowserAction(const Extension
& extension
) {
161 return ExtensionActionManager::Get(profile())->GetBrowserAction(extension
);
164 TestingProfile
* profile() { return profile_
.get(); }
167 bool quit_in_icon_updated_
;
168 base::MessageLoop ui_loop_
;
169 content::TestBrowserThread ui_thread_
;
170 content::TestBrowserThread file_thread_
;
171 content::TestBrowserThread io_thread_
;
172 scoped_ptr
<TestingProfile
> profile_
;
173 ExtensionService
* extension_service_
;
175 #if defined OS_CHROMEOS
176 chromeos::ScopedTestDeviceSettingsService test_device_settings_service_
;
177 chromeos::ScopedTestCrosSettings test_cros_settings_
;
178 chromeos::ScopedTestUserManager test_user_manager_
;
181 DISALLOW_COPY_AND_ASSIGN(ExtensionActionIconFactoryTest
);
184 // If there is no default icon, and the icon has not been set using |SetIcon|,
185 // the factory should return favicon.
186 TEST_F(ExtensionActionIconFactoryTest
, NoIcons
) {
187 // Load an extension that has browser action without default icon set in the
188 // manifest and does not call |SetIcon| by default.
189 scoped_refptr
<Extension
> extension(CreateExtension(
190 "browser_action/no_icon", Manifest::INVALID_LOCATION
));
191 ASSERT_TRUE(extension
.get() != NULL
);
192 ExtensionAction
* browser_action
= GetBrowserAction(*extension
.get());
193 ASSERT_TRUE(browser_action
);
194 ASSERT_FALSE(browser_action
->default_icon());
195 ASSERT_TRUE(browser_action
->GetExplicitlySetIcon(0 /*tab id*/).isNull());
197 gfx::ImageSkia favicon
= GetFavicon();
199 ExtensionActionIconFactory
icon_factory(
200 profile(), extension
.get(), browser_action
, this);
202 gfx::Image icon
= icon_factory
.GetIcon(0);
204 EXPECT_TRUE(ImageRepsAreEqual(
205 favicon
.GetRepresentation(1.0f
),
206 icon
.ToImageSkia()->GetRepresentation(1.0f
)));
209 // If the icon has been set using |SetIcon|, the factory should return that
211 TEST_F(ExtensionActionIconFactoryTest
, AfterSetIcon
) {
212 // Load an extension that has browser action without default icon set in the
213 // manifest and does not call |SetIcon| by default (but has an browser action
215 scoped_refptr
<Extension
> extension(CreateExtension(
216 "browser_action/no_icon", Manifest::INVALID_LOCATION
));
217 ASSERT_TRUE(extension
.get() != NULL
);
218 ExtensionAction
* browser_action
= GetBrowserAction(*extension
.get());
219 ASSERT_TRUE(browser_action
);
220 ASSERT_FALSE(browser_action
->default_icon());
221 ASSERT_TRUE(browser_action
->GetExplicitlySetIcon(0 /*tab id*/).isNull());
223 gfx::Image set_icon
= LoadIcon("browser_action/no_icon/icon.png");
224 ASSERT_FALSE(set_icon
.IsEmpty());
226 browser_action
->SetIcon(0, set_icon
);
228 ASSERT_FALSE(browser_action
->GetExplicitlySetIcon(0 /*tab id*/).isNull());
230 ExtensionActionIconFactory
icon_factory(
231 profile(), extension
.get(), browser_action
, this);
233 gfx::Image icon
= icon_factory
.GetIcon(0);
235 EXPECT_TRUE(ImageRepsAreEqual(
236 set_icon
.ToImageSkia()->GetRepresentation(1.0f
),
237 icon
.ToImageSkia()->GetRepresentation(1.0f
)));
239 // It should still return favicon for another tabs.
240 icon
= icon_factory
.GetIcon(1);
242 EXPECT_TRUE(ImageRepsAreEqual(
243 GetFavicon().GetRepresentation(1.0f
),
244 icon
.ToImageSkia()->GetRepresentation(1.0f
)));
247 // If there is a default icon, and the icon has not been set using |SetIcon|,
248 // the factory should return the default icon.
249 TEST_F(ExtensionActionIconFactoryTest
, DefaultIcon
) {
250 // Load an extension that has browser action without default icon set in the
251 // manifest and does not call |SetIcon| by default (but has an browser action
253 scoped_refptr
<Extension
> extension(CreateExtension(
254 "browser_action/no_icon", Manifest::INVALID_LOCATION
));
255 ASSERT_TRUE(extension
.get() != NULL
);
256 ExtensionAction
* browser_action
= GetBrowserAction(*extension
.get());
257 ASSERT_TRUE(browser_action
);
258 ASSERT_FALSE(browser_action
->default_icon());
259 ASSERT_TRUE(browser_action
->GetExplicitlySetIcon(0 /*tab id*/).isNull());
261 gfx::Image default_icon
=
262 EnsureImageSize(LoadIcon("browser_action/no_icon/icon.png"), 19);
263 ASSERT_FALSE(default_icon
.IsEmpty());
265 scoped_ptr
<ExtensionIconSet
> default_icon_set(new ExtensionIconSet());
266 default_icon_set
->Add(19, "icon.png");
268 browser_action
->set_default_icon(default_icon_set
.Pass());
269 ASSERT_TRUE(browser_action
->default_icon());
271 ExtensionActionIconFactory
icon_factory(
272 profile(), extension
.get(), browser_action
, this);
274 gfx::Image icon
= icon_factory
.GetIcon(0);
276 // The icon should be loaded asynchronously. Initially a transparent icon
277 // should be returned.
278 EXPECT_TRUE(ImageRepsAreEqual(
279 CreateBlankRep(19, 1.0f
),
280 icon
.ToImageSkia()->GetRepresentation(1.0f
)));
284 icon
= icon_factory
.GetIcon(0);
286 // The default icon representation should be loaded at this point.
287 EXPECT_TRUE(ImageRepsAreEqual(
288 default_icon
.ToImageSkia()->GetRepresentation(1.0f
),
289 icon
.ToImageSkia()->GetRepresentation(1.0f
)));
291 // The same icon should be returned for the other tabs.
292 icon
= icon_factory
.GetIcon(1);
294 EXPECT_TRUE(ImageRepsAreEqual(
295 default_icon
.ToImageSkia()->GetRepresentation(1.0f
),
296 icon
.ToImageSkia()->GetRepresentation(1.0f
)));
301 } // namespace extensions