Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / notifications / desktop_notifications_unittest.cc
blob2632a87eea4c0eed808fcbf5e0647baf1a37bb63
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/strings/string_util.h"
9 #include "base/strings/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"
18 #include "ui/base/ime/input_method_initializer.h"
19 #include "ui/message_center/message_center.h"
21 #if defined(USE_ASH)
22 #include "ash/shell.h"
23 #include "ash/test/test_shell_delegate.h"
24 #include "ui/aura/env.h"
25 #include "ui/aura/root_window.h"
26 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
27 #include "ui/compositor/test/context_factories_for_test.h"
28 #endif
30 #if defined(USE_AURA)
31 #include "ui/views/corewm/wm_state.h"
32 #endif
35 using content::BrowserThread;
37 // static
38 const int MockBalloonCollection::kMockBalloonSpace = 5;
40 // static
41 std::string DesktopNotificationsTest::log_output_;
43 MockBalloonCollection::MockBalloonCollection() {}
45 MockBalloonCollection::~MockBalloonCollection() {}
47 void MockBalloonCollection::Add(const Notification& notification,
48 Profile* profile) {
49 // Swap in a logging proxy for the purpose of logging calls that
50 // would be made into javascript, then pass this down to the
51 // balloon collection.
52 Notification test_notification(
53 notification.origin_url(),
54 notification.content_url(),
55 notification.display_source(),
56 notification.replace_id(),
57 new LoggingNotificationProxy(notification.notification_id()));
58 BalloonCollectionImpl::Add(test_notification, profile);
61 bool MockBalloonCollection::HasSpace() const {
62 return count() < kMockBalloonSpace;
65 Balloon* MockBalloonCollection::MakeBalloon(const Notification& notification,
66 Profile* profile) {
67 // Start with a normal balloon but mock out the view.
68 Balloon* balloon = BalloonCollectionImpl::MakeBalloon(notification, profile);
69 balloon->set_view(new FakeBalloonView(balloon));
70 balloons_.push_back(balloon);
71 return balloon;
74 void MockBalloonCollection::OnBalloonClosed(Balloon* source) {
75 std::deque<Balloon*>::iterator it;
76 for (it = balloons_.begin(); it != balloons_.end(); ++it) {
77 if (*it == source) {
78 balloons_.erase(it);
79 BalloonCollectionImpl::OnBalloonClosed(source);
80 break;
85 const BalloonCollection::Balloons& MockBalloonCollection::GetActiveBalloons() {
86 return balloons_;
89 int MockBalloonCollection::UppermostVerticalPosition() {
90 int min = 0;
91 std::deque<Balloon*>::iterator iter;
92 for (iter = balloons_.begin(); iter != balloons_.end(); ++iter) {
93 int pos = (*iter)->GetPosition().y();
94 if (iter == balloons_.begin() || pos < min)
95 min = pos;
97 return min;
100 DesktopNotificationsTest::DesktopNotificationsTest()
101 : ui_thread_(BrowserThread::UI, &message_loop_),
102 balloon_collection_(NULL) {
105 DesktopNotificationsTest::~DesktopNotificationsTest() {
108 void DesktopNotificationsTest::SetUp() {
109 ui::InitializeInputMethodForTesting();
110 #if defined(USE_AURA)
111 wm_state_.reset(new views::corewm::WMState);
112 #endif
113 #if defined(USE_ASH)
114 ui::ScopedAnimationDurationScaleMode normal_duration_mode(
115 ui::ScopedAnimationDurationScaleMode::ZERO_DURATION);
116 // The message center is notmally initialized on |g_browser_process| which
117 // is not created for these tests.
118 message_center::MessageCenter::Initialize();
119 // The ContextFactory must exist before any Compositors are created.
120 bool allow_test_contexts = true;
121 ui::InitializeContextFactoryForTests(allow_test_contexts);
122 // MockBalloonCollection retrieves information about the screen on creation.
123 // So it is necessary to make sure the desktop gets created first.
124 ash::Shell::CreateInstance(new ash::test::TestShellDelegate);
125 #endif
126 chrome::RegisterLocalState(local_state_.registry());
127 profile_.reset(new TestingProfile());
128 ui_manager_.reset(new BalloonNotificationUIManager(&local_state_));
129 balloon_collection_ = new MockBalloonCollection();
130 ui_manager_->SetBalloonCollection(balloon_collection_);
131 service_.reset(new DesktopNotificationService(profile(), ui_manager_.get()));
132 log_output_.clear();
135 void DesktopNotificationsTest::TearDown() {
136 service_.reset(NULL);
137 ui_manager_.reset(NULL);
138 profile_.reset(NULL);
139 #if defined(USE_ASH)
140 ash::Shell::DeleteInstance();
141 // The message center is notmally shutdown on |g_browser_process| which
142 // is not created for these tests.
143 message_center::MessageCenter::Shutdown();
144 aura::Env::DeleteInstance();
145 ui::TerminateContextFactoryForTests();
146 #endif
147 #if defined(USE_AURA)
148 wm_state_.reset();
149 #endif
150 ui::ShutdownInputMethodForTesting();
153 content::ShowDesktopNotificationHostMsgParams
154 DesktopNotificationsTest::StandardTestNotification() {
155 content::ShowDesktopNotificationHostMsgParams params;
156 params.notification_id = 0;
157 params.origin = GURL("http://www.google.com");
158 params.icon_url = GURL("/icon.png");
159 params.title = base::ASCIIToUTF16("Title");
160 params.body = base::ASCIIToUTF16("Text");
161 params.direction = blink::WebTextDirectionDefault;
162 return params;
165 TEST_F(DesktopNotificationsTest, TestShow) {
166 content::ShowDesktopNotificationHostMsgParams params =
167 StandardTestNotification();
168 params.notification_id = 1;
170 EXPECT_TRUE(service_->ShowDesktopNotification(
171 params, 0, 0, DesktopNotificationService::PageNotification));
172 base::MessageLoopForUI::current()->RunUntilIdle();
173 EXPECT_EQ(1, balloon_collection_->count());
175 content::ShowDesktopNotificationHostMsgParams params2 =
176 StandardTestNotification();
177 params2.notification_id = 2;
178 params2.origin = GURL("http://www.google.com");
179 params2.body = base::ASCIIToUTF16("Text");
181 EXPECT_TRUE(service_->ShowDesktopNotification(
182 params2, 0, 0, DesktopNotificationService::PageNotification));
183 base::MessageLoopForUI::current()->RunUntilIdle();
184 EXPECT_EQ(2, balloon_collection_->count());
186 EXPECT_EQ("notification displayed\n"
187 "notification displayed\n",
188 log_output_);
191 TEST_F(DesktopNotificationsTest, TestClose) {
192 content::ShowDesktopNotificationHostMsgParams params =
193 StandardTestNotification();
194 params.notification_id = 1;
196 // Request a notification; should open a balloon.
197 EXPECT_TRUE(service_->ShowDesktopNotification(
198 params, 0, 0, DesktopNotificationService::PageNotification));
199 base::MessageLoopForUI::current()->RunUntilIdle();
200 EXPECT_EQ(1, balloon_collection_->count());
202 // Close all the open balloons.
203 while (balloon_collection_->count() > 0) {
204 (*(balloon_collection_->GetActiveBalloons().begin()))->OnClose(true);
207 EXPECT_EQ("notification displayed\n"
208 "notification closed by user\n",
209 log_output_);
212 TEST_F(DesktopNotificationsTest, TestCancel) {
213 int process_id = 0;
214 int route_id = 0;
215 int notification_id = 1;
217 content::ShowDesktopNotificationHostMsgParams params =
218 StandardTestNotification();
219 params.notification_id = notification_id;
221 // Request a notification; should open a balloon.
222 EXPECT_TRUE(service_->ShowDesktopNotification(
223 params, process_id, route_id,
224 DesktopNotificationService::PageNotification));
225 base::MessageLoopForUI::current()->RunUntilIdle();
226 EXPECT_EQ(1, balloon_collection_->count());
228 // Cancel the same notification
229 service_->CancelDesktopNotification(process_id,
230 route_id,
231 notification_id);
232 base::MessageLoopForUI::current()->RunUntilIdle();
233 // Verify that the balloon collection is now empty.
234 EXPECT_EQ(0, balloon_collection_->count());
236 EXPECT_EQ("notification displayed\n"
237 "notification closed by script\n",
238 log_output_);
241 #if defined(OS_WIN) || defined(TOOLKIT_VIEWS)
242 TEST_F(DesktopNotificationsTest, TestPositioning) {
243 content::ShowDesktopNotificationHostMsgParams params =
244 StandardTestNotification();
245 std::string expected_log;
246 // Create some toasts. After each but the first, make sure there
247 // is a minimum separation between the toasts.
248 int last_top = 0;
249 for (int id = 0; id <= 3; ++id) {
250 params.notification_id = id;
251 EXPECT_TRUE(service_->ShowDesktopNotification(
252 params, 0, 0, DesktopNotificationService::PageNotification));
253 expected_log.append("notification displayed\n");
254 int top = balloon_collection_->UppermostVerticalPosition();
255 if (id > 0)
256 EXPECT_LE(top, last_top - balloon_collection_->MinHeight());
257 last_top = top;
260 EXPECT_EQ(expected_log, log_output_);
263 TEST_F(DesktopNotificationsTest, TestVariableSize) {
264 content::ShowDesktopNotificationHostMsgParams params;
265 params.origin = GURL("http://long.google.com");
266 params.icon_url = GURL("/icon.png");
267 params.title = base::ASCIIToUTF16("Really Really Really Really Really Really "
268 "Really Really Really Really Really Really "
269 "Really Really Really Really Really Really "
270 "Really Long Title"),
271 params.body = base::ASCIIToUTF16("Text");
272 params.notification_id = 0;
274 std::string expected_log;
275 // Create some toasts. After each but the first, make sure there
276 // is a minimum separation between the toasts.
277 EXPECT_TRUE(service_->ShowDesktopNotification(
278 params, 0, 0, DesktopNotificationService::PageNotification));
279 expected_log.append("notification displayed\n");
281 params.origin = GURL("http://short.google.com");
282 params.title = base::ASCIIToUTF16("Short title");
283 params.notification_id = 1;
284 EXPECT_TRUE(service_->ShowDesktopNotification(
285 params, 0, 0, DesktopNotificationService::PageNotification));
286 expected_log.append("notification displayed\n");
288 std::deque<Balloon*>& balloons = balloon_collection_->balloons();
289 std::deque<Balloon*>::iterator iter;
290 for (iter = balloons.begin(); iter != balloons.end(); ++iter) {
291 if ((*iter)->notification().origin_url().host() == "long.google.com") {
292 EXPECT_GE((*iter)->GetViewSize().height(),
293 balloon_collection_->MinHeight());
294 EXPECT_LE((*iter)->GetViewSize().height(),
295 balloon_collection_->MaxHeight());
296 } else {
297 EXPECT_EQ((*iter)->GetViewSize().height(),
298 balloon_collection_->MinHeight());
301 EXPECT_EQ(expected_log, log_output_);
303 #endif
305 TEST_F(DesktopNotificationsTest, TestCancelByProfile) {
306 int process_id = 0;
307 int route_id = 0;
309 TestingBrowserProcess* browser_process =
310 TestingBrowserProcess::GetGlobal();
311 TestingProfileManager profile_manager(browser_process);
312 ASSERT_TRUE(profile_manager.SetUp());
314 TestingProfile* second_profile =
315 profile_manager.CreateTestingProfile("SecondTestingProfile");
317 scoped_ptr<DesktopNotificationService> second_service(
318 new DesktopNotificationService(second_profile, ui_manager_.get()));
320 // Request lots of identical notifications.
321 content::ShowDesktopNotificationHostMsgParams params =
322 StandardTestNotification();
323 params.notification_id = 1;
324 // Notice that the first one is the only one that doesn't use
325 // the second profile.
326 EXPECT_TRUE(service_->ShowDesktopNotification(
327 params, process_id, route_id,
328 DesktopNotificationService::PageNotification));
330 // |kLotsOfToasts| must be large enough to trigger a resize of the underlying
331 // std::deque while we're clearing it.
332 const int kLotsOfToasts = 20;
333 for (int id = 2; id <= kLotsOfToasts; ++id) {
334 params.notification_id = id;
335 EXPECT_TRUE(second_service->ShowDesktopNotification(
336 params, process_id, route_id,
337 DesktopNotificationService::PageNotification));
339 base::MessageLoopForUI::current()->RunUntilIdle();
341 ui_manager_->CancelAllByProfile(second_profile);
343 // Verify that the balloon collection only contains the single
344 // notification from the first profile.
345 EXPECT_EQ(1, balloon_collection_->count());
348 TEST_F(DesktopNotificationsTest, TestCancelBySourceOrigin) {
349 int process_id = 0;
350 int route_id = 0;
352 // Request lots of identical notifications.
353 content::ShowDesktopNotificationHostMsgParams params =
354 StandardTestNotification();
356 // After the first, all the notifications are from attacker.com.
357 content::ShowDesktopNotificationHostMsgParams odd_params =
358 StandardTestNotification();
359 odd_params.origin = GURL("attacker.com");
361 // Show the only non-attacker.com notification.
362 params.notification_id = 1;
363 EXPECT_TRUE(service_->ShowDesktopNotification(
364 params, process_id, route_id,
365 DesktopNotificationService::PageNotification));
367 // |kLotsOfToasts| must be large enough to trigger a resize of the underlying
368 // std::deque while we're clearing it.
369 const int kLotsOfToasts = 20;
370 for (int id = 2; id <= kLotsOfToasts; ++id) {
371 odd_params.notification_id = id;
372 EXPECT_TRUE(service_->ShowDesktopNotification(
373 odd_params, process_id, route_id,
374 DesktopNotificationService::PageNotification));
376 base::MessageLoopForUI::current()->RunUntilIdle();
378 ui_manager_->CancelAllBySourceOrigin(odd_params.origin);
380 // Verify that the balloon collection only contains the single
381 // notification which is not from the canceled origin.
382 EXPECT_EQ(1, balloon_collection_->count());
385 TEST_F(DesktopNotificationsTest, TestQueueing) {
386 int process_id = 0;
387 int route_id = 0;
389 // Request lots of identical notifications.
390 content::ShowDesktopNotificationHostMsgParams params =
391 StandardTestNotification();
392 const int kLotsOfToasts = 20;
393 for (int id = 1; id <= kLotsOfToasts; ++id) {
394 params.notification_id = id;
395 EXPECT_TRUE(service_->ShowDesktopNotification(
396 params, process_id, route_id,
397 DesktopNotificationService::PageNotification));
399 base::MessageLoopForUI::current()->RunUntilIdle();
401 // Build up an expected log of what should be happening.
402 std::string expected_log;
403 for (int i = 0; i < balloon_collection_->max_balloon_count(); ++i) {
404 expected_log.append("notification displayed\n");
407 // The max number that our balloon collection can hold should be
408 // shown.
409 EXPECT_EQ(balloon_collection_->max_balloon_count(),
410 balloon_collection_->count());
411 EXPECT_EQ(expected_log, log_output_);
413 // Cancel the notifications from the start; the balloon space should
414 // remain full.
416 int id;
417 for (id = 1;
418 id <= kLotsOfToasts - balloon_collection_->max_balloon_count();
419 ++id) {
420 service_->CancelDesktopNotification(process_id, route_id, id);
421 base::MessageLoopForUI::current()->RunUntilIdle();
422 expected_log.append("notification closed by script\n");
423 expected_log.append("notification displayed\n");
424 EXPECT_EQ(balloon_collection_->max_balloon_count(),
425 balloon_collection_->count());
426 EXPECT_EQ(expected_log, log_output_);
429 // Now cancel the rest. It should empty the balloon space.
430 for (; id <= kLotsOfToasts; ++id) {
431 service_->CancelDesktopNotification(process_id, route_id, id);
432 expected_log.append("notification closed by script\n");
433 base::MessageLoopForUI::current()->RunUntilIdle();
434 EXPECT_EQ(expected_log, log_output_);
438 // Verify that the balloon collection is now empty.
439 EXPECT_EQ(0, balloon_collection_->count());
442 TEST_F(DesktopNotificationsTest, TestEarlyDestruction) {
443 // Create some toasts and then prematurely delete the notification service,
444 // just to make sure nothing crashes/leaks.
445 content::ShowDesktopNotificationHostMsgParams params =
446 StandardTestNotification();
447 for (int id = 0; id <= 3; ++id) {
448 params.notification_id = id;
449 EXPECT_TRUE(service_->ShowDesktopNotification(
450 params, 0, 0, DesktopNotificationService::PageNotification));
452 service_.reset(NULL);
455 TEST_F(DesktopNotificationsTest, TestUserInputEscaping) {
456 // Create a test script with some HTML; assert that it doesn't get into the
457 // data:// URL that's produced for the balloon.
458 content::ShowDesktopNotificationHostMsgParams params =
459 StandardTestNotification();
460 params.title = base::ASCIIToUTF16("<script>window.alert('uh oh');</script>");
461 params.body = base::ASCIIToUTF16("<i>this text is in italics</i>");
462 params.notification_id = 1;
463 EXPECT_TRUE(service_->ShowDesktopNotification(
464 params, 0, 0, DesktopNotificationService::PageNotification));
466 base::MessageLoopForUI::current()->RunUntilIdle();
467 EXPECT_EQ(1, balloon_collection_->count());
468 Balloon* balloon = (*balloon_collection_->balloons().begin());
469 GURL data_url = balloon->notification().content_url();
470 EXPECT_EQ(std::string::npos, data_url.spec().find("<script>"));
471 EXPECT_EQ(std::string::npos, data_url.spec().find("<i>"));
472 // URL-encoded versions of tags should also not be found.
473 EXPECT_EQ(std::string::npos, data_url.spec().find("%3cscript%3e"));
474 EXPECT_EQ(std::string::npos, data_url.spec().find("%3ci%3e"));
477 TEST_F(DesktopNotificationsTest, TestBoundingBox) {
478 // Create some notifications.
479 content::ShowDesktopNotificationHostMsgParams params =
480 StandardTestNotification();
481 for (int id = 0; id <= 3; ++id) {
482 params.notification_id = id;
483 EXPECT_TRUE(service_->ShowDesktopNotification(
484 params, 0, 0, DesktopNotificationService::PageNotification));
487 gfx::Rect box = balloon_collection_->GetBalloonsBoundingBox();
489 // Try this for all positions.
490 BalloonCollection::PositionPreference pref;
491 for (pref = BalloonCollection::UPPER_RIGHT;
492 pref <= BalloonCollection::LOWER_LEFT;
493 pref = static_cast<BalloonCollection::PositionPreference>(pref + 1)) {
494 // Make sure each balloon's 4 corners are inside the box.
495 std::deque<Balloon*>& balloons = balloon_collection_->balloons();
496 std::deque<Balloon*>::iterator iter;
497 for (iter = balloons.begin(); iter != balloons.end(); ++iter) {
498 int min_x = (*iter)->GetPosition().x();
499 int max_x = min_x + (*iter)->GetViewSize().width() - 1;
500 int min_y = (*iter)->GetPosition().y();
501 int max_y = min_y + (*iter)->GetViewSize().height() - 1;
503 EXPECT_TRUE(box.Contains(gfx::Point(min_x, min_y)));
504 EXPECT_TRUE(box.Contains(gfx::Point(min_x, max_y)));
505 EXPECT_TRUE(box.Contains(gfx::Point(max_x, min_y)));
506 EXPECT_TRUE(box.Contains(gfx::Point(max_x, max_y)));
511 TEST_F(DesktopNotificationsTest, TestPositionPreference) {
512 // Set position preference to lower right.
513 local_state_.SetInteger(prefs::kDesktopNotificationPosition,
514 BalloonCollection::LOWER_RIGHT);
516 // Create some notifications.
517 content::ShowDesktopNotificationHostMsgParams params =
518 StandardTestNotification();
519 for (int id = 0; id <= 3; ++id) {
520 params.notification_id = id;
521 EXPECT_TRUE(service_->ShowDesktopNotification(
522 params, 0, 0, DesktopNotificationService::PageNotification));
525 std::deque<Balloon*>& balloons = balloon_collection_->balloons();
526 std::deque<Balloon*>::iterator iter;
528 // Check that they decrease in y-position (for MAC, with reversed
529 // coordinates, they should increase).
530 int last_y = -1;
531 int last_x = -1;
533 for (iter = balloons.begin(); iter != balloons.end(); ++iter) {
534 int current_x = (*iter)->GetPosition().x();
535 int current_y = (*iter)->GetPosition().y();
536 if (last_x > 0)
537 EXPECT_EQ(last_x, current_x);
539 if (last_y > 0) {
540 #if defined(OS_MACOSX)
541 EXPECT_GT(current_y, last_y);
542 #else
543 EXPECT_LT(current_y, last_y);
544 #endif
547 last_x = current_x;
548 last_y = current_y;
551 // Now change the position to upper right. This should cause an immediate
552 // repositioning, and we check for the reverse ordering.
553 local_state_.SetInteger(prefs::kDesktopNotificationPosition,
554 BalloonCollection::UPPER_RIGHT);
555 last_x = -1;
556 last_y = -1;
558 for (iter = balloons.begin(); iter != balloons.end(); ++iter) {
559 int current_x = (*iter)->GetPosition().x();
560 int current_y = (*iter)->GetPosition().y();
562 if (last_x > 0)
563 EXPECT_EQ(last_x, current_x);
565 if (last_y > 0) {
566 #if defined(OS_MACOSX)
567 EXPECT_LT(current_y, last_y);
568 #else
569 EXPECT_GT(current_y, last_y);
570 #endif
573 last_x = current_x;
574 last_y = current_y;
577 // Now change the position to upper left. Confirm that the X value for the
578 // balloons gets smaller.
579 local_state_.SetInteger(prefs::kDesktopNotificationPosition,
580 BalloonCollection::UPPER_LEFT);
582 int current_x = (*balloons.begin())->GetPosition().x();
583 EXPECT_LT(current_x, last_x);