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/files/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/users/scoped_test_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
.allocN32Pixels(static_cast<int>(size_dip
* scale
),
59 static_cast<int>(size_dip
* scale
));
60 bitmap
.eraseColor(SkColorSetARGB(0, 0, 0, 0));
61 return gfx::ImageSkiaRep(bitmap
, scale
);
64 gfx::Image
LoadIcon(const std::string
& filename
) {
66 PathService::Get(chrome::DIR_TEST_DATA
, &path
);
67 path
= path
.AppendASCII("extensions/api_test").AppendASCII(filename
);
69 std::string file_contents
;
70 base::ReadFileToString(path
, &file_contents
);
71 const unsigned char* data
=
72 reinterpret_cast<const unsigned char*>(file_contents
.data());
75 gfx::PNGCodec::Decode(data
, file_contents
.length(), &bitmap
);
77 return gfx::Image::CreateFrom1xBitmap(bitmap
);
80 class ExtensionActionIconFactoryTest
81 : public testing::Test
,
82 public ExtensionActionIconFactory::Observer
{
84 ExtensionActionIconFactoryTest()
85 : quit_in_icon_updated_(false),
86 ui_thread_(BrowserThread::UI
, &ui_loop_
),
87 file_thread_(BrowserThread::FILE),
88 io_thread_(BrowserThread::IO
) {
91 ~ExtensionActionIconFactoryTest() override
{}
93 void WaitForIconUpdate() {
94 quit_in_icon_updated_
= true;
95 base::MessageLoop::current()->Run();
96 quit_in_icon_updated_
= false;
99 scoped_refptr
<Extension
> CreateExtension(const char* name
,
100 Manifest::Location location
) {
101 // Create and load an extension.
102 base::FilePath test_file
;
103 if (!PathService::Get(chrome::DIR_TEST_DATA
, &test_file
)) {
107 test_file
= test_file
.AppendASCII("extensions/api_test").AppendASCII(name
);
110 JSONFileValueDeserializer
deserializer(
111 test_file
.AppendASCII("manifest.json"));
112 scoped_ptr
<base::DictionaryValue
> valid_value(
113 static_cast<base::DictionaryValue
*>(
114 deserializer
.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 void SetUp() override
{
135 file_thread_
.Start();
137 profile_
.reset(new TestingProfile
);
138 base::CommandLine
command_line(base::CommandLine::NO_PROGRAM
);
139 extension_service_
= static_cast<extensions::TestExtensionSystem
*>(
140 extensions::ExtensionSystem::Get(profile_
.get()))->
141 CreateExtensionService(&command_line
, base::FilePath(), false);
144 void TearDown() override
{
145 profile_
.reset(); // Get all DeleteSoon calls sent to ui_loop_.
146 ui_loop_
.RunUntilIdle();
149 // ExtensionActionIconFactory::Observer overrides:
150 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*/).IsEmpty());
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*/).IsEmpty());
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*/).IsEmpty());
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*/).IsEmpty());
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
->SetDefaultIconForTest(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