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.
7 #include "base/basictypes.h"
8 #include "base/command_line.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "base/prefs/pref_service.h"
11 #include "base/prefs/scoped_user_pref_update.h"
12 #include "base/run_loop.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "chrome/browser/background/background_contents.h"
15 #include "chrome/browser/background/background_contents_service.h"
16 #include "chrome/browser/background/background_contents_service_factory.h"
17 #include "chrome/browser/chrome_notification_types.h"
18 #include "chrome/browser/ui/browser_list.h"
19 #include "chrome/common/extensions/extension_test_util.h"
20 #include "chrome/common/pref_names.h"
21 #include "chrome/test/base/browser_with_test_window_test.h"
22 #include "chrome/test/base/testing_browser_process.h"
23 #include "chrome/test/base/testing_profile.h"
24 #include "chrome/test/base/testing_profile_manager.h"
25 #include "content/public/browser/notification_service.h"
26 #include "content/public/test/test_browser_thread.h"
27 #include "content/public/test/test_browser_thread_bundle.h"
28 #include "extensions/common/extension.h"
29 #include "testing/gtest/include/gtest/gtest.h"
30 #include "testing/platform_test.h"
33 #if defined(ENABLE_NOTIFICATIONS)
34 #include "chrome/browser/notifications/message_center_notification_manager.h"
35 #include "chrome/browser/notifications/notification.h"
36 #include "ui/message_center/fake_message_center_tray_delegate.h"
37 #include "ui/message_center/message_center.h"
38 #include "ui/message_center/message_center_observer.h"
41 class BackgroundContentsServiceTest
: public testing::Test
{
43 BackgroundContentsServiceTest() {}
44 ~BackgroundContentsServiceTest() override
{}
45 void SetUp() override
{
46 command_line_
.reset(new base::CommandLine(base::CommandLine::NO_PROGRAM
));
47 BackgroundContentsService::DisableCloseBalloonForTesting(true);
50 void TearDown() override
{
51 BackgroundContentsService::DisableCloseBalloonForTesting(false);
54 const base::DictionaryValue
* GetPrefs(Profile
* profile
) {
55 return profile
->GetPrefs()->GetDictionary(
56 prefs::kRegisteredBackgroundContents
);
59 // Returns the stored pref URL for the passed app id.
60 std::string
GetPrefURLForApp(Profile
* profile
, const base::string16
& appid
) {
61 const base::DictionaryValue
* pref
= GetPrefs(profile
);
62 EXPECT_TRUE(pref
->HasKey(base::UTF16ToUTF8(appid
)));
63 const base::DictionaryValue
* value
;
64 pref
->GetDictionaryWithoutPathExpansion(base::UTF16ToUTF8(appid
), &value
);
66 value
->GetString("url", &url
);
70 content::TestBrowserThreadBundle thread_bundle_
;
71 scoped_ptr
<base::CommandLine
> command_line_
;
74 class MockBackgroundContents
: public BackgroundContents
{
76 explicit MockBackgroundContents(Profile
* profile
)
77 : appid_(base::ASCIIToUTF16("app_id")),
80 MockBackgroundContents(Profile
* profile
, const std::string
& id
)
81 : appid_(base::ASCIIToUTF16(id
)),
85 void SendOpenedNotification(BackgroundContentsService
* service
) {
86 base::string16 frame_name
= base::ASCIIToUTF16("background");
87 BackgroundContentsOpenedDetails details
= {
88 this, frame_name
, appid_
};
89 service
->BackgroundContentsOpened(&details
, profile_
);
92 virtual void Navigate(GURL url
) {
94 content::NotificationService::current()->Notify(
95 chrome::NOTIFICATION_BACKGROUND_CONTENTS_NAVIGATED
,
96 content::Source
<Profile
>(profile_
),
97 content::Details
<BackgroundContents
>(this));
99 const GURL
& GetURL() const override
{ return url_
; }
101 void MockClose(Profile
* profile
) {
102 content::NotificationService::current()->Notify(
103 chrome::NOTIFICATION_BACKGROUND_CONTENTS_CLOSED
,
104 content::Source
<Profile
>(profile
),
105 content::Details
<BackgroundContents
>(this));
109 ~MockBackgroundContents() override
{
110 content::NotificationService::current()->Notify(
111 chrome::NOTIFICATION_BACKGROUND_CONTENTS_DELETED
,
112 content::Source
<Profile
>(profile_
),
113 content::Details
<BackgroundContents
>(this));
116 const base::string16
& appid() { return appid_
; }
121 // The ID of our parent application
122 base::string16 appid_
;
128 #if defined(ENABLE_NOTIFICATIONS)
129 // Wait for the notification created.
130 class NotificationWaiter
: public message_center::MessageCenterObserver
{
132 explicit NotificationWaiter(const std::string
& target_id
, Profile
* profile
)
133 : target_id_(target_id
), profile_(profile
) {}
134 ~NotificationWaiter() override
{}
136 void WaitForNotificationAdded() {
137 DCHECK(!run_loop_
.running());
138 message_center::MessageCenter
* message_center
=
139 message_center::MessageCenter::Get();
141 message_center
->AddObserver(this);
143 message_center
->RemoveObserver(this);
147 // message_center::MessageCenterObserver overrides:
148 void OnNotificationAdded(const std::string
& notification_id
) override
{
149 if (notification_id
== FindNotificationIdFromDelegateId(target_id_
))
153 void OnNotificationUpdated(const std::string
& notification_id
) override
{
154 if (notification_id
== FindNotificationIdFromDelegateId(target_id_
))
158 std::string
FindNotificationIdFromDelegateId(const std::string
& delegate_id
) {
159 MessageCenterNotificationManager
* manager
=
160 static_cast<MessageCenterNotificationManager
*>(
161 g_browser_process
->notification_ui_manager());
163 return manager
->FindById(delegate_id
, profile_
)->id();
166 std::string target_id_
;
168 base::RunLoop run_loop_
;
170 DISALLOW_COPY_AND_ASSIGN(NotificationWaiter
);
173 class BackgroundContentsServiceNotificationTest
174 : public BrowserWithTestWindowTest
{
176 BackgroundContentsServiceNotificationTest() {}
177 ~BackgroundContentsServiceNotificationTest() override
{}
179 // Overridden from testing::Test
180 void SetUp() override
{
181 BrowserWithTestWindowTest::SetUp();
182 // In ChromeOS environment, BrowserWithTestWindowTest initializes
184 #if !defined(OS_CHROMEOS)
185 message_center::MessageCenter::Initialize();
187 profile_manager_
.reset(new TestingProfileManager(
188 TestingBrowserProcess::GetGlobal()));
189 ASSERT_TRUE(profile_manager_
->SetUp());
190 MessageCenterNotificationManager
* manager
=
191 static_cast<MessageCenterNotificationManager
*>(
192 g_browser_process
->notification_ui_manager());
193 manager
->SetMessageCenterTrayDelegateForTest(
194 new message_center::FakeMessageCenterTrayDelegate(
195 message_center::MessageCenter::Get(), base::Closure()));
198 void TearDown() override
{
199 g_browser_process
->notification_ui_manager()->CancelAll();
200 profile_manager_
.reset();
201 #if !defined(OS_CHROMEOS)
202 message_center::MessageCenter::Shutdown();
204 BrowserWithTestWindowTest::TearDown();
208 // Creates crash notification for the specified extension and returns
210 const Notification
* CreateCrashNotification(
211 scoped_refptr
<extensions::Extension
> extension
) {
212 std::string notification_id
= BackgroundContentsService::
213 GetNotificationDelegateIdForExtensionForTesting(extension
->id());
214 NotificationWaiter
waiter(notification_id
, profile());
215 BackgroundContentsService::ShowBalloonForTesting(
216 extension
.get(), profile());
217 waiter
.WaitForNotificationAdded();
219 return g_browser_process
->notification_ui_manager()->FindById(
220 notification_id
, profile());
224 scoped_ptr
<TestingProfileManager
> profile_manager_
;
226 DISALLOW_COPY_AND_ASSIGN(BackgroundContentsServiceNotificationTest
);
228 #endif // ENABLE_NOTIFICATIONS
230 TEST_F(BackgroundContentsServiceTest
, Create
) {
231 // Check for creation and leaks.
232 TestingProfile profile
;
233 BackgroundContentsService
service(&profile
, command_line_
.get());
236 TEST_F(BackgroundContentsServiceTest
, BackgroundContentsCreateDestroy
) {
237 TestingProfile profile
;
238 BackgroundContentsService
service(&profile
, command_line_
.get());
239 MockBackgroundContents
* contents
= new MockBackgroundContents(&profile
);
240 EXPECT_FALSE(service
.IsTracked(contents
));
241 contents
->SendOpenedNotification(&service
);
242 EXPECT_TRUE(service
.IsTracked(contents
));
244 EXPECT_FALSE(service
.IsTracked(contents
));
247 TEST_F(BackgroundContentsServiceTest
, BackgroundContentsUrlAdded
) {
248 TestingProfile profile
;
249 BackgroundContentsService
service(&profile
, command_line_
.get());
250 BackgroundContentsServiceFactory::GetInstance()->
251 RegisterUserPrefsOnBrowserContextForTest(&profile
);
253 GURL
url("http://a/");
254 GURL
url2("http://a/");
256 scoped_ptr
<MockBackgroundContents
> contents(
257 new MockBackgroundContents(&profile
));
258 EXPECT_EQ(0U, GetPrefs(&profile
)->size());
259 contents
->SendOpenedNotification(&service
);
261 contents
->Navigate(url
);
262 EXPECT_EQ(1U, GetPrefs(&profile
)->size());
263 EXPECT_EQ(url
.spec(), GetPrefURLForApp(&profile
, contents
->appid()));
265 // Navigate the contents to a new url, should not change url.
266 contents
->Navigate(url2
);
267 EXPECT_EQ(1U, GetPrefs(&profile
)->size());
268 EXPECT_EQ(url
.spec(), GetPrefURLForApp(&profile
, contents
->appid()));
270 // Contents are deleted, url should persist.
271 EXPECT_EQ(1U, GetPrefs(&profile
)->size());
274 TEST_F(BackgroundContentsServiceTest
, BackgroundContentsUrlAddedAndClosed
) {
275 TestingProfile profile
;
276 BackgroundContentsService
service(&profile
, command_line_
.get());
277 BackgroundContentsServiceFactory::GetInstance()->
278 RegisterUserPrefsOnBrowserContextForTest(&profile
);
280 GURL
url("http://a/");
281 MockBackgroundContents
* contents
= new MockBackgroundContents(&profile
);
282 EXPECT_EQ(0U, GetPrefs(&profile
)->size());
283 contents
->SendOpenedNotification(&service
);
284 contents
->Navigate(url
);
285 EXPECT_EQ(1U, GetPrefs(&profile
)->size());
286 EXPECT_EQ(url
.spec(), GetPrefURLForApp(&profile
, contents
->appid()));
288 // Fake a window closed by script.
289 contents
->MockClose(&profile
);
290 EXPECT_EQ(0U, GetPrefs(&profile
)->size());
293 // Test what happens if a BackgroundContents shuts down (say, due to a renderer
294 // crash) then is restarted. Should not persist URL twice.
295 TEST_F(BackgroundContentsServiceTest
, RestartBackgroundContents
) {
296 TestingProfile profile
;
297 BackgroundContentsService
service(&profile
, command_line_
.get());
298 BackgroundContentsServiceFactory::GetInstance()->
299 RegisterUserPrefsOnBrowserContextForTest(&profile
);
301 GURL
url("http://a/");
303 scoped_ptr
<MockBackgroundContents
> contents(new MockBackgroundContents(
305 contents
->SendOpenedNotification(&service
);
306 contents
->Navigate(url
);
307 EXPECT_EQ(1U, GetPrefs(&profile
)->size());
308 EXPECT_EQ(url
.spec(), GetPrefURLForApp(&profile
, contents
->appid()));
310 // Contents deleted, url should be persisted.
311 EXPECT_EQ(1U, GetPrefs(&profile
)->size());
314 // Reopen the BackgroundContents to the same URL, we should not register the
316 scoped_ptr
<MockBackgroundContents
> contents(new MockBackgroundContents(
318 contents
->SendOpenedNotification(&service
);
319 contents
->Navigate(url
);
320 EXPECT_EQ(1U, GetPrefs(&profile
)->size());
324 // Ensures that BackgroundContentsService properly tracks the association
325 // between a BackgroundContents and its parent extension, including
326 // unregistering the BC when the extension is uninstalled.
327 TEST_F(BackgroundContentsServiceTest
, TestApplicationIDLinkage
) {
328 TestingProfile profile
;
329 BackgroundContentsService
service(&profile
, command_line_
.get());
330 BackgroundContentsServiceFactory::GetInstance()->
331 RegisterUserPrefsOnBrowserContextForTest(&profile
);
334 service
.GetAppBackgroundContents(base::ASCIIToUTF16("appid")));
335 MockBackgroundContents
* contents
= new MockBackgroundContents(&profile
,
337 scoped_ptr
<MockBackgroundContents
> contents2(
338 new MockBackgroundContents(&profile
, "appid2"));
339 contents
->SendOpenedNotification(&service
);
340 EXPECT_EQ(contents
, service
.GetAppBackgroundContents(contents
->appid()));
341 contents2
->SendOpenedNotification(&service
);
342 EXPECT_EQ(contents2
.get(), service
.GetAppBackgroundContents(
343 contents2
->appid()));
344 EXPECT_EQ(0U, GetPrefs(&profile
)->size());
346 // Navigate the contents, then make sure the one associated with the extension
348 GURL
url("http://a/");
349 GURL
url2("http://b/");
350 contents
->Navigate(url
);
351 EXPECT_EQ(1U, GetPrefs(&profile
)->size());
352 contents2
->Navigate(url2
);
353 EXPECT_EQ(2U, GetPrefs(&profile
)->size());
354 service
.ShutdownAssociatedBackgroundContents(base::ASCIIToUTF16("appid"));
355 EXPECT_FALSE(service
.IsTracked(contents
));
357 service
.GetAppBackgroundContents(base::ASCIIToUTF16("appid")));
358 EXPECT_EQ(1U, GetPrefs(&profile
)->size());
359 EXPECT_EQ(url2
.spec(), GetPrefURLForApp(&profile
, contents2
->appid()));
362 #if defined(ENABLE_NOTIFICATIONS)
363 TEST_F(BackgroundContentsServiceNotificationTest
, TestShowBalloon
) {
364 scoped_refptr
<extensions::Extension
> extension
=
365 extension_test_util::LoadManifest("image_loading_tracker", "app.json");
366 ASSERT_TRUE(extension
.get());
367 ASSERT_TRUE(extension
->GetManifestData("icons"));
369 const Notification
* notification
= CreateCrashNotification(extension
);
370 EXPECT_FALSE(notification
->icon().IsEmpty());
373 // Verify if a test notification can show the default extension icon for
374 // a crash notification for an extension without icon.
375 TEST_F(BackgroundContentsServiceNotificationTest
, TestShowBalloonNoIcon
) {
376 // Extension manifest file with no 'icon' field.
377 scoped_refptr
<extensions::Extension
> extension
=
378 extension_test_util::LoadManifest("app", "manifest.json");
379 ASSERT_TRUE(extension
.get());
380 ASSERT_FALSE(extension
->GetManifestData("icons"));
382 const Notification
* notification
= CreateCrashNotification(extension
);
383 EXPECT_FALSE(notification
->icon().IsEmpty());
386 TEST_F(BackgroundContentsServiceNotificationTest
, TestShowTwoBalloons
) {
387 TestingProfile profile
;
388 scoped_refptr
<extensions::Extension
> extension
=
389 extension_test_util::LoadManifest("app", "manifest.json");
390 ASSERT_TRUE(extension
.get());
391 CreateCrashNotification(extension
);
392 CreateCrashNotification(extension
);
394 message_center::MessageCenter
* message_center
=
395 message_center::MessageCenter::Get();
396 message_center::NotificationList::Notifications notifications
=
397 message_center
->GetVisibleNotifications();
398 ASSERT_EQ(1u, notifications
.size());