desktop_notifications_unittest.cc revision 3f50c38dc070f4bb515c1b64450dae14f316474e
1// Copyright (c) 2010 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. 4 5#include "chrome/browser/notifications/desktop_notifications_unittest.h" 6 7#include "base/string_util.h" 8#include "base/utf_string_conversions.h" 9#include "chrome/common/pref_names.h" 10#include "chrome/common/render_messages_params.h" 11#include "chrome/test/testing_pref_service.h" 12 13// static 14const int MockBalloonCollection::kMockBalloonSpace = 5; 15 16// static 17std::string DesktopNotificationsTest::log_output_; 18 19void MockBalloonCollection::Add(const Notification& notification, 20 Profile* profile) { 21 // Swap in a logging proxy for the purpose of logging calls that 22 // would be made into javascript, then pass this down to the 23 // balloon collection. 24 Notification test_notification( 25 notification.origin_url(), 26 notification.content_url(), 27 notification.display_source(), 28 notification.replace_id(), 29 new LoggingNotificationProxy(notification.notification_id())); 30 BalloonCollectionImpl::Add(test_notification, profile); 31} 32 33Balloon* MockBalloonCollection::MakeBalloon(const Notification& notification, 34 Profile* profile) { 35 // Start with a normal balloon but mock out the view. 36 Balloon* balloon = BalloonCollectionImpl::MakeBalloon(notification, profile); 37 balloon->set_view(new MockBalloonView(balloon)); 38 balloons_.push_back(balloon); 39 return balloon; 40} 41 42void MockBalloonCollection::OnBalloonClosed(Balloon* source) { 43 std::deque<Balloon*>::iterator it; 44 for (it = balloons_.begin(); it != balloons_.end(); ++it) { 45 if (*it == source) { 46 balloons_.erase(it); 47 BalloonCollectionImpl::OnBalloonClosed(source); 48 break; 49 } 50 } 51} 52 53int MockBalloonCollection::UppermostVerticalPosition() { 54 int min = 0; 55 std::deque<Balloon*>::iterator iter; 56 for (iter = balloons_.begin(); iter != balloons_.end(); ++iter) { 57 int pos = (*iter)->GetPosition().y(); 58 if (iter == balloons_.begin() || pos < min) 59 min = pos; 60 } 61 return min; 62} 63 64DesktopNotificationsTest::DesktopNotificationsTest() 65 : ui_thread_(BrowserThread::UI, &message_loop_) { 66} 67 68DesktopNotificationsTest::~DesktopNotificationsTest() { 69} 70 71void DesktopNotificationsTest::SetUp() { 72 profile_.reset(new TestingProfile()); 73 balloon_collection_ = new MockBalloonCollection(); 74 ui_manager_.reset( 75 new NotificationUIManager(profile_->GetTestingPrefService())); 76 ui_manager_->Initialize(balloon_collection_); 77 balloon_collection_->set_space_change_listener(ui_manager_.get()); 78 service_.reset(new DesktopNotificationService(profile(), ui_manager_.get())); 79 log_output_.clear(); 80} 81 82void DesktopNotificationsTest::TearDown() { 83 service_.reset(NULL); 84 ui_manager_.reset(NULL); 85 profile_.reset(NULL); 86} 87 88ViewHostMsg_ShowNotification_Params 89DesktopNotificationsTest::StandardTestNotification() { 90 ViewHostMsg_ShowNotification_Params params; 91 params.notification_id = 0; 92 params.origin = GURL("http://www.google.com"); 93 params.is_html = false; 94 params.icon_url = GURL("/icon.png"); 95 params.title = ASCIIToUTF16("Title"); 96 params.body = ASCIIToUTF16("Text"); 97 params.direction = WebKit::WebTextDirectionDefault; 98 return params; 99} 100 101TEST_F(DesktopNotificationsTest, TestShow) { 102 ViewHostMsg_ShowNotification_Params params = StandardTestNotification(); 103 params.notification_id = 1; 104 105 EXPECT_TRUE(service_->ShowDesktopNotification( 106 params, 0, 0, DesktopNotificationService::PageNotification)); 107 MessageLoopForUI::current()->RunAllPending(); 108 EXPECT_EQ(1, balloon_collection_->count()); 109 110 ViewHostMsg_ShowNotification_Params params2; 111 params2.origin = GURL("http://www.google.com"); 112 params2.is_html = true; 113 params2.contents_url = GURL("http://www.google.com/notification.html"); 114 params2.notification_id = 2; 115 116 EXPECT_TRUE(service_->ShowDesktopNotification( 117 params2, 0, 0, DesktopNotificationService::PageNotification)); 118 MessageLoopForUI::current()->RunAllPending(); 119 EXPECT_EQ(2, balloon_collection_->count()); 120 121 EXPECT_EQ("notification displayed\n" 122 "notification displayed\n", 123 log_output_); 124} 125 126TEST_F(DesktopNotificationsTest, TestClose) { 127 ViewHostMsg_ShowNotification_Params params = StandardTestNotification(); 128 params.notification_id = 1; 129 130 // Request a notification; should open a balloon. 131 EXPECT_TRUE(service_->ShowDesktopNotification( 132 params, 0, 0, DesktopNotificationService::PageNotification)); 133 MessageLoopForUI::current()->RunAllPending(); 134 EXPECT_EQ(1, balloon_collection_->count()); 135 136 // Close all the open balloons. 137 while (balloon_collection_->count() > 0) { 138 (*(balloon_collection_->GetActiveBalloons().begin()))->OnClose(true); 139 } 140 141 EXPECT_EQ("notification displayed\n" 142 "notification closed by user\n", 143 log_output_); 144} 145 146TEST_F(DesktopNotificationsTest, TestCancel) { 147 int process_id = 0; 148 int route_id = 0; 149 int notification_id = 1; 150 151 ViewHostMsg_ShowNotification_Params params = StandardTestNotification(); 152 params.notification_id = notification_id; 153 154 // Request a notification; should open a balloon. 155 EXPECT_TRUE(service_->ShowDesktopNotification( 156 params, process_id, route_id, 157 DesktopNotificationService::PageNotification)); 158 MessageLoopForUI::current()->RunAllPending(); 159 EXPECT_EQ(1, balloon_collection_->count()); 160 161 // Cancel the same notification 162 service_->CancelDesktopNotification(process_id, 163 route_id, 164 notification_id); 165 MessageLoopForUI::current()->RunAllPending(); 166 // Verify that the balloon collection is now empty. 167 EXPECT_EQ(0, balloon_collection_->count()); 168 169 EXPECT_EQ("notification displayed\n" 170 "notification closed by script\n", 171 log_output_); 172} 173 174#if defined(OS_WIN) || defined(TOOLKIT_VIEWS) 175TEST_F(DesktopNotificationsTest, TestPositioning) { 176 ViewHostMsg_ShowNotification_Params params = StandardTestNotification(); 177 std::string expected_log; 178 // Create some toasts. After each but the first, make sure there 179 // is a minimum separation between the toasts. 180 int last_top = 0; 181 for (int id = 0; id <= 3; ++id) { 182 params.notification_id = id; 183 EXPECT_TRUE(service_->ShowDesktopNotification( 184 params, 0, 0, DesktopNotificationService::PageNotification)); 185 expected_log.append("notification displayed\n"); 186 int top = balloon_collection_->UppermostVerticalPosition(); 187 if (id > 0) 188 EXPECT_LE(top, last_top - balloon_collection_->MinHeight()); 189 last_top = top; 190 } 191 192 EXPECT_EQ(expected_log, log_output_); 193} 194 195TEST_F(DesktopNotificationsTest, TestVariableSize) { 196 ViewHostMsg_ShowNotification_Params params; 197 params.origin = GURL("http://long.google.com"); 198 params.is_html = false; 199 params.icon_url = GURL("/icon.png"); 200 params.title = ASCIIToUTF16("Really Really Really Really Really Really " 201 "Really Really Really Really Really Really " 202 "Really Really Really Really Really Really " 203 "Really Long Title"), 204 params.body = ASCIIToUTF16("Text"); 205 params.notification_id = 0; 206 207 std::string expected_log; 208 // Create some toasts. After each but the first, make sure there 209 // is a minimum separation between the toasts. 210 EXPECT_TRUE(service_->ShowDesktopNotification( 211 params, 0, 0, DesktopNotificationService::PageNotification)); 212 expected_log.append("notification displayed\n"); 213 214 params.origin = GURL("http://short.google.com"); 215 params.title = ASCIIToUTF16("Short title"); 216 params.notification_id = 1; 217 EXPECT_TRUE(service_->ShowDesktopNotification( 218 params, 0, 0, DesktopNotificationService::PageNotification)); 219 expected_log.append("notification displayed\n"); 220 221 std::deque<Balloon*>& balloons = balloon_collection_->balloons(); 222 std::deque<Balloon*>::iterator iter; 223 for (iter = balloons.begin(); iter != balloons.end(); ++iter) { 224 if ((*iter)->notification().origin_url().host() == "long.google.com") { 225 EXPECT_GE((*iter)->GetViewSize().height(), 226 balloon_collection_->MinHeight()); 227 EXPECT_LE((*iter)->GetViewSize().height(), 228 balloon_collection_->MaxHeight()); 229 } else { 230 EXPECT_EQ((*iter)->GetViewSize().height(), 231 balloon_collection_->MinHeight()); 232 } 233 } 234 EXPECT_EQ(expected_log, log_output_); 235} 236#endif 237 238TEST_F(DesktopNotificationsTest, TestQueueing) { 239 int process_id = 0; 240 int route_id = 0; 241 242 // Request lots of identical notifications. 243 ViewHostMsg_ShowNotification_Params params = StandardTestNotification(); 244 const int kLotsOfToasts = 20; 245 for (int id = 1; id <= kLotsOfToasts; ++id) { 246 params.notification_id = id; 247 EXPECT_TRUE(service_->ShowDesktopNotification( 248 params, process_id, route_id, 249 DesktopNotificationService::PageNotification)); 250 } 251 MessageLoopForUI::current()->RunAllPending(); 252 253 // Build up an expected log of what should be happening. 254 std::string expected_log; 255 for (int i = 0; i < balloon_collection_->max_balloon_count(); ++i) { 256 expected_log.append("notification displayed\n"); 257 } 258 259 // The max number that our balloon collection can hold should be 260 // shown. 261 EXPECT_EQ(balloon_collection_->max_balloon_count(), 262 balloon_collection_->count()); 263 EXPECT_EQ(expected_log, log_output_); 264 265 // Cancel the notifications from the start; the balloon space should 266 // remain full. 267 { 268 int id; 269 for (id = 1; 270 id <= kLotsOfToasts - balloon_collection_->max_balloon_count(); 271 ++id) { 272 service_->CancelDesktopNotification(process_id, route_id, id); 273 MessageLoopForUI::current()->RunAllPending(); 274 expected_log.append("notification closed by script\n"); 275 expected_log.append("notification displayed\n"); 276 EXPECT_EQ(balloon_collection_->max_balloon_count(), 277 balloon_collection_->count()); 278 EXPECT_EQ(expected_log, log_output_); 279 } 280 281 // Now cancel the rest. It should empty the balloon space. 282 for (; id <= kLotsOfToasts; ++id) { 283 service_->CancelDesktopNotification(process_id, route_id, id); 284 expected_log.append("notification closed by script\n"); 285 MessageLoopForUI::current()->RunAllPending(); 286 EXPECT_EQ(expected_log, log_output_); 287 } 288 } 289 290 // Verify that the balloon collection is now empty. 291 EXPECT_EQ(0, balloon_collection_->count()); 292} 293 294TEST_F(DesktopNotificationsTest, TestEarlyDestruction) { 295 // Create some toasts and then prematurely delete the notification service, 296 // just to make sure nothing crashes/leaks. 297 ViewHostMsg_ShowNotification_Params params = StandardTestNotification(); 298 for (int id = 0; id <= 3; ++id) { 299 params.notification_id = id; 300 EXPECT_TRUE(service_->ShowDesktopNotification( 301 params, 0, 0, DesktopNotificationService::PageNotification)); 302 } 303 service_.reset(NULL); 304} 305 306TEST_F(DesktopNotificationsTest, TestUserInputEscaping) { 307 // Create a test script with some HTML; assert that it doesn't get into the 308 // data:// URL that's produced for the balloon. 309 ViewHostMsg_ShowNotification_Params params = StandardTestNotification(); 310 params.title = ASCIIToUTF16("<script>window.alert('uh oh');</script>"); 311 params.body = ASCIIToUTF16("<i>this text is in italics</i>"); 312 params.notification_id = 1; 313 EXPECT_TRUE(service_->ShowDesktopNotification( 314 params, 0, 0, DesktopNotificationService::PageNotification)); 315 316 MessageLoopForUI::current()->RunAllPending(); 317 EXPECT_EQ(1, balloon_collection_->count()); 318 Balloon* balloon = (*balloon_collection_->balloons().begin()); 319 GURL data_url = balloon->notification().content_url(); 320 EXPECT_EQ(std::string::npos, data_url.spec().find("<script>")); 321 EXPECT_EQ(std::string::npos, data_url.spec().find("<i>")); 322 // URL-encoded versions of tags should also not be found. 323 EXPECT_EQ(std::string::npos, data_url.spec().find("%3cscript%3e")); 324 EXPECT_EQ(std::string::npos, data_url.spec().find("%3ci%3e")); 325} 326 327TEST_F(DesktopNotificationsTest, TestPositionPreference) { 328 // Set position preference to lower right. 329 profile_->GetPrefs()->SetInteger(prefs::kDesktopNotificationPosition, 330 BalloonCollection::LOWER_RIGHT); 331 332 // Create some notifications. 333 ViewHostMsg_ShowNotification_Params params = StandardTestNotification(); 334 for (int id = 0; id <= 3; ++id) { 335 params.notification_id = id; 336 EXPECT_TRUE(service_->ShowDesktopNotification( 337 params, 0, 0, DesktopNotificationService::PageNotification)); 338 } 339 340 std::deque<Balloon*>& balloons = balloon_collection_->balloons(); 341 std::deque<Balloon*>::iterator iter; 342 343 // Check that they decrease in y-position (for MAC, with reversed 344 // coordinates, they should increase). 345 int last_y = -1; 346 int last_x = -1; 347 348 for (iter = balloons.begin(); iter != balloons.end(); ++iter) { 349 int current_x = (*iter)->GetPosition().x(); 350 int current_y = (*iter)->GetPosition().y(); 351 if (last_x > 0) 352 EXPECT_EQ(last_x, current_x); 353 354 if (last_y > 0) { 355#if defined(OS_MACOSX) 356 EXPECT_GT(current_y, last_y); 357#else 358 EXPECT_LT(current_y, last_y); 359#endif 360 } 361 362 last_x = current_x; 363 last_y = current_y; 364 } 365 366 // Now change the position to upper right. This should cause an immediate 367 // repositioning, and we check for the reverse ordering. 368 profile_->GetPrefs()->SetInteger(prefs::kDesktopNotificationPosition, 369 BalloonCollection::UPPER_RIGHT); 370 last_x = -1; 371 last_y = -1; 372 373 for (iter = balloons.begin(); iter != balloons.end(); ++iter) { 374 int current_x = (*iter)->GetPosition().x(); 375 int current_y = (*iter)->GetPosition().y(); 376 377 if (last_x > 0) 378 EXPECT_EQ(last_x, current_x); 379 380 if (last_y > 0) { 381#if defined(OS_MACOSX) 382 EXPECT_LT(current_y, last_y); 383#else 384 EXPECT_GT(current_y, last_y); 385#endif 386 } 387 388 last_x = current_x; 389 last_y = current_y; 390 } 391 392 // Now change the position to upper left. Confirm that the X value for the 393 // balloons gets smaller. 394 profile_->GetPrefs()->SetInteger(prefs::kDesktopNotificationPosition, 395 BalloonCollection::UPPER_LEFT); 396 397 int current_x = (*balloons.begin())->GetPosition().x(); 398 EXPECT_LT(current_x, last_x); 399} 400