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