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 virtual ~ExtensionActionIconFactoryTest() {}
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 JSONFileValueSerializer
serializer(test_file
.AppendASCII("manifest.json"));
111 scoped_ptr
<base::DictionaryValue
> valid_value(
112 static_cast<base::DictionaryValue
*>(serializer
.Deserialize(&error_code
,
114 EXPECT_EQ(0, error_code
) << error
;
118 EXPECT_TRUE(valid_value
.get());
122 scoped_refptr
<Extension
> extension
=
123 Extension::Create(test_file
, location
, *valid_value
,
124 Extension::NO_FLAGS
, &error
);
125 EXPECT_TRUE(extension
.get()) << error
;
127 extension_service_
->AddExtension(extension
.get());
131 // testing::Test overrides:
132 virtual void SetUp() override
{
133 file_thread_
.Start();
135 profile_
.reset(new TestingProfile
);
136 CommandLine
command_line(CommandLine::NO_PROGRAM
);
137 extension_service_
= static_cast<extensions::TestExtensionSystem
*>(
138 extensions::ExtensionSystem::Get(profile_
.get()))->
139 CreateExtensionService(&command_line
, base::FilePath(), false);
142 virtual void TearDown() override
{
143 profile_
.reset(); // Get all DeleteSoon calls sent to ui_loop_.
144 ui_loop_
.RunUntilIdle();
147 // ExtensionActionIconFactory::Observer overrides:
148 void OnIconUpdated() override
{
149 if (quit_in_icon_updated_
)
150 base::MessageLoop::current()->Quit();
153 gfx::ImageSkia
GetFavicon() {
154 return *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
155 IDR_EXTENSIONS_FAVICON
);
158 ExtensionAction
* GetBrowserAction(const Extension
& extension
) {
159 return ExtensionActionManager::Get(profile())->GetBrowserAction(extension
);
162 TestingProfile
* profile() { return profile_
.get(); }
165 bool quit_in_icon_updated_
;
166 base::MessageLoop ui_loop_
;
167 content::TestBrowserThread ui_thread_
;
168 content::TestBrowserThread file_thread_
;
169 content::TestBrowserThread io_thread_
;
170 scoped_ptr
<TestingProfile
> profile_
;
171 ExtensionService
* extension_service_
;
173 #if defined OS_CHROMEOS
174 chromeos::ScopedTestDeviceSettingsService test_device_settings_service_
;
175 chromeos::ScopedTestCrosSettings test_cros_settings_
;
176 chromeos::ScopedTestUserManager test_user_manager_
;
179 DISALLOW_COPY_AND_ASSIGN(ExtensionActionIconFactoryTest
);
182 // If there is no default icon, and the icon has not been set using |SetIcon|,
183 // the factory should return favicon.
184 TEST_F(ExtensionActionIconFactoryTest
, NoIcons
) {
185 // Load an extension that has browser action without default icon set in the
186 // manifest and does not call |SetIcon| by default.
187 scoped_refptr
<Extension
> extension(CreateExtension(
188 "browser_action/no_icon", Manifest::INVALID_LOCATION
));
189 ASSERT_TRUE(extension
.get() != NULL
);
190 ExtensionAction
* browser_action
= GetBrowserAction(*extension
.get());
191 ASSERT_TRUE(browser_action
);
192 ASSERT_FALSE(browser_action
->default_icon());
193 ASSERT_TRUE(browser_action
->GetExplicitlySetIcon(0 /*tab id*/).isNull());
195 gfx::ImageSkia favicon
= GetFavicon();
197 ExtensionActionIconFactory
icon_factory(
198 profile(), extension
.get(), browser_action
, this);
200 gfx::Image icon
= icon_factory
.GetIcon(0);
202 EXPECT_TRUE(ImageRepsAreEqual(
203 favicon
.GetRepresentation(1.0f
),
204 icon
.ToImageSkia()->GetRepresentation(1.0f
)));
207 // If the icon has been set using |SetIcon|, the factory should return that
209 TEST_F(ExtensionActionIconFactoryTest
, AfterSetIcon
) {
210 // Load an extension that has browser action without default icon set in the
211 // manifest and does not call |SetIcon| by default (but has an browser action
213 scoped_refptr
<Extension
> extension(CreateExtension(
214 "browser_action/no_icon", Manifest::INVALID_LOCATION
));
215 ASSERT_TRUE(extension
.get() != NULL
);
216 ExtensionAction
* browser_action
= GetBrowserAction(*extension
.get());
217 ASSERT_TRUE(browser_action
);
218 ASSERT_FALSE(browser_action
->default_icon());
219 ASSERT_TRUE(browser_action
->GetExplicitlySetIcon(0 /*tab id*/).isNull());
221 gfx::Image set_icon
= LoadIcon("browser_action/no_icon/icon.png");
222 ASSERT_FALSE(set_icon
.IsEmpty());
224 browser_action
->SetIcon(0, set_icon
);
226 ASSERT_FALSE(browser_action
->GetExplicitlySetIcon(0 /*tab id*/).isNull());
228 ExtensionActionIconFactory
icon_factory(
229 profile(), extension
.get(), browser_action
, this);
231 gfx::Image icon
= icon_factory
.GetIcon(0);
233 EXPECT_TRUE(ImageRepsAreEqual(
234 set_icon
.ToImageSkia()->GetRepresentation(1.0f
),
235 icon
.ToImageSkia()->GetRepresentation(1.0f
)));
237 // It should still return favicon for another tabs.
238 icon
= icon_factory
.GetIcon(1);
240 EXPECT_TRUE(ImageRepsAreEqual(
241 GetFavicon().GetRepresentation(1.0f
),
242 icon
.ToImageSkia()->GetRepresentation(1.0f
)));
245 // If there is a default icon, and the icon has not been set using |SetIcon|,
246 // the factory should return the default icon.
247 TEST_F(ExtensionActionIconFactoryTest
, DefaultIcon
) {
248 // Load an extension that has browser action without default icon set in the
249 // manifest and does not call |SetIcon| by default (but has an browser action
251 scoped_refptr
<Extension
> extension(CreateExtension(
252 "browser_action/no_icon", Manifest::INVALID_LOCATION
));
253 ASSERT_TRUE(extension
.get() != NULL
);
254 ExtensionAction
* browser_action
= GetBrowserAction(*extension
.get());
255 ASSERT_TRUE(browser_action
);
256 ASSERT_FALSE(browser_action
->default_icon());
257 ASSERT_TRUE(browser_action
->GetExplicitlySetIcon(0 /*tab id*/).isNull());
259 gfx::Image default_icon
=
260 EnsureImageSize(LoadIcon("browser_action/no_icon/icon.png"), 19);
261 ASSERT_FALSE(default_icon
.IsEmpty());
263 scoped_ptr
<ExtensionIconSet
> default_icon_set(new ExtensionIconSet());
264 default_icon_set
->Add(19, "icon.png");
266 browser_action
->set_default_icon(default_icon_set
.Pass());
267 ASSERT_TRUE(browser_action
->default_icon());
269 ExtensionActionIconFactory
icon_factory(
270 profile(), extension
.get(), browser_action
, this);
272 gfx::Image icon
= icon_factory
.GetIcon(0);
274 // The icon should be loaded asynchronously. Initially a transparent icon
275 // should be returned.
276 EXPECT_TRUE(ImageRepsAreEqual(
277 CreateBlankRep(19, 1.0f
),
278 icon
.ToImageSkia()->GetRepresentation(1.0f
)));
282 icon
= icon_factory
.GetIcon(0);
284 // The default icon representation should be loaded at this point.
285 EXPECT_TRUE(ImageRepsAreEqual(
286 default_icon
.ToImageSkia()->GetRepresentation(1.0f
),
287 icon
.ToImageSkia()->GetRepresentation(1.0f
)));
289 // The same icon should be returned for the other tabs.
290 icon
= icon_factory
.GetIcon(1);
292 EXPECT_TRUE(ImageRepsAreEqual(
293 default_icon
.ToImageSkia()->GetRepresentation(1.0f
),
294 icon
.ToImageSkia()->GetRepresentation(1.0f
)));
299 } // namespace extensions