Fix build break
[chromium-blink-merge.git] / chrome / browser / notifications / desktop_notifications_unittest.cc
blob50caecc3abd651bd842b59b174b045dad46a1091
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/notifications/desktop_notifications_unittest.h"
7 #include "base/prefs/testing_pref_service.h"
8 #include "base/string_util.h"
9 #include "base/utf_string_conversions.h"
10 #include "chrome/browser/notifications/balloon_notification_ui_manager.h"
11 #include "chrome/browser/notifications/fake_balloon_view.h"
12 #include "chrome/browser/prefs/browser_prefs.h"
13 #include "chrome/common/pref_names.h"
14 #include "chrome/test/base/testing_browser_process.h"
15 #include "chrome/test/base/testing_profile.h"
16 #include "chrome/test/base/testing_profile_manager.h"
17 #include "content/public/common/show_desktop_notification_params.h"
19 #if defined(ENABLE_MESSAGE_CENTER)
20 #include "ui/message_center/message_center.h"
21 #endif
23 #if defined(USE_ASH)
24 #include "ash/shell.h"
25 #include "ash/test/test_shell_delegate.h"
26 #include "chrome/browser/ui/aura/active_desktop_monitor.h"
27 #include "third_party/WebKit/Source/WebKit/chromium/public/WebKit.h"
28 #include "ui/aura/env.h"
29 #include "ui/aura/root_window.h"
30 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
31 #endif
33 #if defined(OS_WIN)
34 #include "base/win/metro.h"
35 #include "ui/base/ime/win/tsf_bridge.h"
36 #endif
38 using content::BrowserThread;
40 // static
41 const int MockBalloonCollection::kMockBalloonSpace = 5;
43 // static
44 std::string DesktopNotificationsTest::log_output_;
46 MockBalloonCollection::MockBalloonCollection() {}
48 MockBalloonCollection::~MockBalloonCollection() {}
50 void MockBalloonCollection::Add(const Notification& notification,
51 Profile* profile) {
52 // Swap in a logging proxy for the purpose of logging calls that
53 // would be made into javascript, then pass this down to the
54 // balloon collection.
55 Notification test_notification(
56 notification.origin_url(),
57 notification.content_url(),
58 notification.display_source(),
59 notification.replace_id(),
60 new LoggingNotificationProxy(notification.notification_id()));
61 BalloonCollectionImpl::Add(test_notification, profile);
64 bool MockBalloonCollection::HasSpace() const {
65 return count() < kMockBalloonSpace;
68 Balloon* MockBalloonCollection::MakeBalloon(const Notification& notification,
69 Profile* profile) {
70 // Start with a normal balloon but mock out the view.
71 Balloon* balloon = BalloonCollectionImpl::MakeBalloon(notification, profile);
72 balloon->set_view(new FakeBalloonView(balloon));
73 balloons_.push_back(balloon);
74 return balloon;
77 void MockBalloonCollection::OnBalloonClosed(Balloon* source) {
78 std::deque<Balloon*>::iterator it;
79 for (it = balloons_.begin(); it != balloons_.end(); ++it) {
80 if (*it == source) {
81 balloons_.erase(it);
82 BalloonCollectionImpl::OnBalloonClosed(source);
83 break;
88 const BalloonCollection::Balloons& MockBalloonCollection::GetActiveBalloons() {
89 return balloons_;
92 int MockBalloonCollection::UppermostVerticalPosition() {
93 int min = 0;
94 std::deque<Balloon*>::iterator iter;
95 for (iter = balloons_.begin(); iter != balloons_.end(); ++iter) {
96 int pos = (*iter)->GetPosition().y();
97 if (iter == balloons_.begin() || pos < min)
98 min = pos;
100 return min;
103 DesktopNotificationsTest::DesktopNotificationsTest()
104 : ui_thread_(BrowserThread::UI, &message_loop_) {
107 DesktopNotificationsTest::~DesktopNotificationsTest() {
110 void DesktopNotificationsTest::SetUp() {
111 #if defined(OS_WIN)
112 if (base::win::IsTSFAwareRequired())
113 ui::TSFBridge::Initialize();
114 #endif
115 #if defined(USE_ASH)
116 WebKit::initialize(webkit_platform_support_.Get());
117 ui::ScopedAnimationDurationScaleMode normal_duration_mode(
118 ui::ScopedAnimationDurationScaleMode::ZERO_DURATION);
119 #if defined(ENABLE_MESSAGE_CENTER)
120 // The message center is notmally initialized on |g_browser_process| which
121 // is not created for these tests.
122 message_center::MessageCenter::Initialize();
123 #endif
124 // MockBalloonCollection retrieves information about the screen on creation.
125 // So it is necessary to make sure the desktop gets created first.
126 ash::Shell::CreateInstance(new ash::test::TestShellDelegate);
127 active_desktop_monitor_.reset(new ActiveDesktopMonitor);
128 #endif
130 chrome::RegisterLocalState(local_state_.registry());
131 profile_.reset(new TestingProfile());
132 ui_manager_.reset(new BalloonNotificationUIManager(&local_state_));
133 balloon_collection_ = new MockBalloonCollection();
134 ui_manager_->SetBalloonCollection(balloon_collection_);
135 service_.reset(new DesktopNotificationService(profile(), ui_manager_.get()));
136 log_output_.clear();
139 void DesktopNotificationsTest::TearDown() {
140 service_.reset(NULL);
141 ui_manager_.reset(NULL);
142 profile_.reset(NULL);
143 #if defined(USE_ASH)
144 active_desktop_monitor_.reset();
145 ash::Shell::DeleteInstance();
146 #if defined(ENABLE_MESSAGE_CENTER)
147 // The message center is notmally shutdown on |g_browser_process| which
148 // is not created for these tests.
149 message_center::MessageCenter::Shutdown();
150 #endif
151 aura::Env::DeleteInstance();
152 WebKit::shutdown();
153 #endif
156 content::ShowDesktopNotificationHostMsgParams
157 DesktopNotificationsTest::StandardTestNotification() {
158 content::ShowDesktopNotificationHostMsgParams params;
159 params.notification_id = 0;
160 params.origin = GURL("http://www.google.com");
161 params.is_html = false;
162 params.icon_url = GURL("/icon.png");
163 params.title = ASCIIToUTF16("Title");
164 params.body = ASCIIToUTF16("Text");
165 params.direction = WebKit::WebTextDirectionDefault;
166 return params;
169 TEST_F(DesktopNotificationsTest, TestShow) {
170 content::ShowDesktopNotificationHostMsgParams params =
171 StandardTestNotification();
172 params.notification_id = 1;
174 EXPECT_TRUE(service_->ShowDesktopNotification(
175 params, 0, 0, DesktopNotificationService::PageNotification));
176 MessageLoopForUI::current()->RunUntilIdle();
177 EXPECT_EQ(1, balloon_collection_->count());
179 content::ShowDesktopNotificationHostMsgParams params2;
180 params2.origin = GURL("http://www.google.com");
181 params2.is_html = true;
182 params2.contents_url = GURL("http://www.google.com/notification.html");
183 params2.notification_id = 2;
185 EXPECT_TRUE(service_->ShowDesktopNotification(
186 params2, 0, 0, DesktopNotificationService::PageNotification));
187 MessageLoopForUI::current()->RunUntilIdle();
188 EXPECT_EQ(2, balloon_collection_->count());
190 EXPECT_EQ("notification displayed\n"
191 "notification displayed\n",
192 log_output_);
195 TEST_F(DesktopNotificationsTest, TestClose) {
196 content::ShowDesktopNotificationHostMsgParams params =
197 StandardTestNotification();
198 params.notification_id = 1;
200 // Request a notification; should open a balloon.
201 EXPECT_TRUE(service_->ShowDesktopNotification(
202 params, 0, 0, DesktopNotificationService::PageNotification));
203 MessageLoopForUI::current()->RunUntilIdle();
204 EXPECT_EQ(1, balloon_collection_->count());
206 // Close all the open balloons.
207 while (balloon_collection_->count() > 0) {
208 (*(balloon_collection_->GetActiveBalloons().begin()))->OnClose(true);
211 EXPECT_EQ("notification displayed\n"
212 "notification closed by user\n",
213 log_output_);
216 TEST_F(DesktopNotificationsTest, TestCancel) {
217 int process_id = 0;
218 int route_id = 0;
219 int notification_id = 1;
221 content::ShowDesktopNotificationHostMsgParams params =
222 StandardTestNotification();
223 params.notification_id = notification_id;
225 // Request a notification; should open a balloon.
226 EXPECT_TRUE(service_->ShowDesktopNotification(
227 params, process_id, route_id,
228 DesktopNotificationService::PageNotification));
229 MessageLoopForUI::current()->RunUntilIdle();
230 EXPECT_EQ(1, balloon_collection_->count());
232 // Cancel the same notification
233 service_->CancelDesktopNotification(process_id,
234 route_id,
235 notification_id);
236 MessageLoopForUI::current()->RunUntilIdle();
237 // Verify that the balloon collection is now empty.
238 EXPECT_EQ(0, balloon_collection_->count());
240 EXPECT_EQ("notification displayed\n"
241 "notification closed by script\n",
242 log_output_);
245 #if defined(OS_WIN) || defined(TOOLKIT_VIEWS)
246 TEST_F(DesktopNotificationsTest, TestPositioning) {
247 content::ShowDesktopNotificationHostMsgParams params =
248 StandardTestNotification();
249 std::string expected_log;
250 // Create some toasts. After each but the first, make sure there
251 // is a minimum separation between the toasts.
252 int last_top = 0;
253 for (int id = 0; id <= 3; ++id) {
254 params.notification_id = id;
255 EXPECT_TRUE(service_->ShowDesktopNotification(
256 params, 0, 0, DesktopNotificationService::PageNotification));
257 expected_log.append("notification displayed\n");
258 int top = balloon_collection_->UppermostVerticalPosition();
259 if (id > 0)
260 EXPECT_LE(top, last_top - balloon_collection_->MinHeight());
261 last_top = top;
264 EXPECT_EQ(expected_log, log_output_);
267 TEST_F(DesktopNotificationsTest, TestVariableSize) {
268 content::ShowDesktopNotificationHostMsgParams params;
269 params.origin = GURL("http://long.google.com");
270 params.is_html = false;
271 params.icon_url = GURL("/icon.png");
272 params.title = ASCIIToUTF16("Really Really Really Really Really Really "
273 "Really Really Really Really Really Really "
274 "Really Really Really Really Really Really "
275 "Really Long Title"),
276 params.body = ASCIIToUTF16("Text");
277 params.notification_id = 0;
279 std::string expected_log;
280 // Create some toasts. After each but the first, make sure there
281 // is a minimum separation between the toasts.
282 EXPECT_TRUE(service_->ShowDesktopNotification(
283 params, 0, 0, DesktopNotificationService::PageNotification));
284 expected_log.append("notification displayed\n");
286 params.origin = GURL("http://short.google.com");
287 params.title = ASCIIToUTF16("Short title");
288 params.notification_id = 1;
289 EXPECT_TRUE(service_->ShowDesktopNotification(
290 params, 0, 0, DesktopNotificationService::PageNotification));
291 expected_log.append("notification displayed\n");
293 std::deque<Balloon*>& balloons = balloon_collection_->balloons();
294 std::deque<Balloon*>::iterator iter;
295 for (iter = balloons.begin(); iter != balloons.end(); ++iter) {
296 if ((*iter)->notification().origin_url().host() == "long.google.com") {
297 EXPECT_GE((*iter)->GetViewSize().height(),
298 balloon_collection_->MinHeight());
299 EXPECT_LE((*iter)->GetViewSize().height(),
300 balloon_collection_->MaxHeight());
301 } else {
302 EXPECT_EQ((*iter)->GetViewSize().height(),
303 balloon_collection_->MinHeight());
306 EXPECT_EQ(expected_log, log_output_);
308 #endif
310 TEST_F(DesktopNotificationsTest, TestCancelByProfile) {
311 int process_id = 0;
312 int route_id = 0;
314 TestingBrowserProcess* browser_process =
315 TestingBrowserProcess::GetGlobal();
316 TestingProfileManager profile_manager(browser_process);
317 ASSERT_TRUE(profile_manager.SetUp());
319 TestingProfile* second_profile =
320 profile_manager.CreateTestingProfile("SecondTestingProfile");
322 scoped_ptr<DesktopNotificationService> second_service(
323 new DesktopNotificationService(second_profile, ui_manager_.get()));
325 // Request lots of identical notifications.
326 content::ShowDesktopNotificationHostMsgParams params =
327 StandardTestNotification();
328 params.notification_id = 1;
329 // Notice that the first one is the only one that doesn't use
330 // the second profile.
331 EXPECT_TRUE(service_->ShowDesktopNotification(
332 params, process_id, route_id,
333 DesktopNotificationService::PageNotification));
335 // |kLotsOfToasts| must be large enough to trigger a resize of the underlying
336 // std::deque while we're clearing it.
337 const int kLotsOfToasts = 20;
338 for (int id = 2; id <= kLotsOfToasts; ++id) {
339 params.notification_id = id;
340 EXPECT_TRUE(second_service->ShowDesktopNotification(
341 params, process_id, route_id,
342 DesktopNotificationService::PageNotification));
344 MessageLoopForUI::current()->RunUntilIdle();
346 ui_manager_->CancelAllByProfile(second_profile);
348 // Verify that the balloon collection only contains the single
349 // notification from the first profile.
350 EXPECT_EQ(1, balloon_collection_->count());
353 TEST_F(DesktopNotificationsTest, TestCancelBySourceOrigin) {
354 int process_id = 0;
355 int route_id = 0;
357 // Request lots of identical notifications.
358 content::ShowDesktopNotificationHostMsgParams params =
359 StandardTestNotification();
361 // After the first, all the notifications are from attacker.com.
362 content::ShowDesktopNotificationHostMsgParams odd_params =
363 StandardTestNotification();
364 odd_params.origin = GURL("attacker.com");
366 // Show the only non-attacker.com notification.
367 params.notification_id = 1;
368 EXPECT_TRUE(service_->ShowDesktopNotification(
369 params, process_id, route_id,
370 DesktopNotificationService::PageNotification));
372 // |kLotsOfToasts| must be large enough to trigger a resize of the underlying
373 // std::deque while we're clearing it.
374 const int kLotsOfToasts = 20;
375 for (int id = 2; id <= kLotsOfToasts; ++id) {
376 odd_params.notification_id = id;
377 EXPECT_TRUE(service_->ShowDesktopNotification(
378 odd_params, process_id, route_id,
379 DesktopNotificationService::PageNotification));
381 MessageLoopForUI::current()->RunUntilIdle();
383 ui_manager_->CancelAllBySourceOrigin(odd_params.origin);
385 // Verify that the balloon collection only contains the single
386 // notification which is not from the canceled origin.
387 EXPECT_EQ(1, balloon_collection_->count());
390 TEST_F(DesktopNotificationsTest, TestQueueing) {
391 int process_id = 0;
392 int route_id = 0;
394 // Request lots of identical notifications.
395 content::ShowDesktopNotificationHostMsgParams params =
396 StandardTestNotification();
397 const int kLotsOfToasts = 20;
398 for (int id = 1; id <= kLotsOfToasts; ++id) {
399 params.notification_id = id;
400 EXPECT_TRUE(service_->ShowDesktopNotification(
401 params, process_id, route_id,
402 DesktopNotificationService::PageNotification));
404 MessageLoopForUI::current()->RunUntilIdle();
406 // Build up an expected log of what should be happening.
407 std::string expected_log;
408 for (int i = 0; i < balloon_collection_->max_balloon_count(); ++i) {
409 expected_log.append("notification displayed\n");
412 // The max number that our balloon collection can hold should be
413 // shown.
414 EXPECT_EQ(balloon_collection_->max_balloon_count(),
415 balloon_collection_->count());
416 EXPECT_EQ(expected_log, log_output_);
418 // Cancel the notifications from the start; the balloon space should
419 // remain full.
421 int id;
422 for (id = 1;
423 id <= kLotsOfToasts - balloon_collection_->max_balloon_count();
424 ++id) {
425 service_->CancelDesktopNotification(process_id, route_id, id);
426 MessageLoopForUI::current()->RunUntilIdle();
427 expected_log.append("notification closed by script\n");
428 expected_log.append("notification displayed\n");
429 EXPECT_EQ(balloon_collection_->max_balloon_count(),
430 balloon_collection_->count());
431 EXPECT_EQ(expected_log, log_output_);
434 // Now cancel the rest. It should empty the balloon space.
435 for (; id <= kLotsOfToasts; ++id) {
436 service_->CancelDesktopNotification(process_id, route_id, id);
437 expected_log.append("notification closed by script\n");
438 MessageLoopForUI::current()->RunUntilIdle();
439 EXPECT_EQ(expected_log, log_output_);
443 // Verify that the balloon collection is now empty.
444 EXPECT_EQ(0, balloon_collection_->count());
447 TEST_F(DesktopNotificationsTest, TestEarlyDestruction) {
448 // Create some toasts and then prematurely delete the notification service,
449 // just to make sure nothing crashes/leaks.
450 content::ShowDesktopNotificationHostMsgParams params =
451 StandardTestNotification();
452 for (int id = 0; id <= 3; ++id) {
453 params.notification_id = id;
454 EXPECT_TRUE(service_->ShowDesktopNotification(
455 params, 0, 0, DesktopNotificationService::PageNotification));
457 service_.reset(NULL);
460 TEST_F(DesktopNotificationsTest, TestUserInputEscaping) {
461 // Create a test script with some HTML; assert that it doesn't get into the
462 // data:// URL that's produced for the balloon.
463 content::ShowDesktopNotificationHostMsgParams params =
464 StandardTestNotification();
465 params.title = ASCIIToUTF16("<script>window.alert('uh oh');</script>");
466 params.body = ASCIIToUTF16("<i>this text is in italics</i>");
467 params.notification_id = 1;
468 EXPECT_TRUE(service_->ShowDesktopNotification(
469 params, 0, 0, DesktopNotificationService::PageNotification));
471 MessageLoopForUI::current()->RunUntilIdle();
472 EXPECT_EQ(1, balloon_collection_->count());
473 Balloon* balloon = (*balloon_collection_->balloons().begin());
474 GURL data_url = balloon->notification().content_url();
475 EXPECT_EQ(std::string::npos, data_url.spec().find("<script>"));
476 EXPECT_EQ(std::string::npos, data_url.spec().find("<i>"));
477 // URL-encoded versions of tags should also not be found.
478 EXPECT_EQ(std::string::npos, data_url.spec().find("%3cscript%3e"));
479 EXPECT_EQ(std::string::npos, data_url.spec().find("%3ci%3e"));
482 TEST_F(DesktopNotificationsTest, TestBoundingBox) {
483 // Create some notifications.
484 content::ShowDesktopNotificationHostMsgParams params =
485 StandardTestNotification();
486 for (int id = 0; id <= 3; ++id) {
487 params.notification_id = id;
488 EXPECT_TRUE(service_->ShowDesktopNotification(
489 params, 0, 0, DesktopNotificationService::PageNotification));
492 gfx::Rect box = balloon_collection_->GetBalloonsBoundingBox();
494 // Try this for all positions.
495 BalloonCollection::PositionPreference pref;
496 for (pref = BalloonCollection::UPPER_RIGHT;
497 pref <= BalloonCollection::LOWER_LEFT;
498 pref = static_cast<BalloonCollection::PositionPreference>(pref + 1)) {
499 // Make sure each balloon's 4 corners are inside the box.
500 std::deque<Balloon*>& balloons = balloon_collection_->balloons();
501 std::deque<Balloon*>::iterator iter;
502 for (iter = balloons.begin(); iter != balloons.end(); ++iter) {
503 int min_x = (*iter)->GetPosition().x();
504 int max_x = min_x + (*iter)->GetViewSize().width() - 1;
505 int min_y = (*iter)->GetPosition().y();
506 int max_y = min_y + (*iter)->GetViewSize().height() - 1;
508 EXPECT_TRUE(box.Contains(gfx::Point(min_x, min_y)));
509 EXPECT_TRUE(box.Contains(gfx::Point(min_x, max_y)));
510 EXPECT_TRUE(box.Contains(gfx::Point(max_x, min_y)));
511 EXPECT_TRUE(box.Contains(gfx::Point(max_x, max_y)));
516 TEST_F(DesktopNotificationsTest, TestPositionPreference) {
517 // Set position preference to lower right.
518 local_state_.SetInteger(prefs::kDesktopNotificationPosition,
519 BalloonCollection::LOWER_RIGHT);
521 // Create some notifications.
522 content::ShowDesktopNotificationHostMsgParams params =
523 StandardTestNotification();
524 for (int id = 0; id <= 3; ++id) {
525 params.notification_id = id;
526 EXPECT_TRUE(service_->ShowDesktopNotification(
527 params, 0, 0, DesktopNotificationService::PageNotification));
530 std::deque<Balloon*>& balloons = balloon_collection_->balloons();
531 std::deque<Balloon*>::iterator iter;
533 // Check that they decrease in y-position (for MAC, with reversed
534 // coordinates, they should increase).
535 int last_y = -1;
536 int last_x = -1;
538 for (iter = balloons.begin(); iter != balloons.end(); ++iter) {
539 int current_x = (*iter)->GetPosition().x();
540 int current_y = (*iter)->GetPosition().y();
541 if (last_x > 0)
542 EXPECT_EQ(last_x, current_x);
544 if (last_y > 0) {
545 #if defined(OS_MACOSX)
546 EXPECT_GT(current_y, last_y);
547 #else
548 EXPECT_LT(current_y, last_y);
549 #endif
552 last_x = current_x;
553 last_y = current_y;
556 // Now change the position to upper right. This should cause an immediate
557 // repositioning, and we check for the reverse ordering.
558 local_state_.SetInteger(prefs::kDesktopNotificationPosition,
559 BalloonCollection::UPPER_RIGHT);
560 last_x = -1;
561 last_y = -1;
563 for (iter = balloons.begin(); iter != balloons.end(); ++iter) {
564 int current_x = (*iter)->GetPosition().x();
565 int current_y = (*iter)->GetPosition().y();
567 if (last_x > 0)
568 EXPECT_EQ(last_x, current_x);
570 if (last_y > 0) {
571 #if defined(OS_MACOSX)
572 EXPECT_LT(current_y, last_y);
573 #else
574 EXPECT_GT(current_y, last_y);
575 #endif
578 last_x = current_x;
579 last_y = current_y;
582 // Now change the position to upper left. Confirm that the X value for the
583 // balloons gets smaller.
584 local_state_.SetInteger(prefs::kDesktopNotificationPosition,
585 BalloonCollection::UPPER_LEFT);
587 int current_x = (*balloons.begin())->GetPosition().x();
588 EXPECT_LT(current_x, last_x);