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/chromeos/notifications/desktop_notifications_unittest.h"
6
7 #include "base/stringprintf.h"
8 #include "base/utf_string_conversions.h"
9 #include "chrome/browser/prefs/browser_prefs.h"
10 #include "chrome/browser/prefs/pref_service.h"
11 #include "content/common/desktop_notification_messages.h"
12
13 namespace chromeos {
14
15 // static
16 std::string DesktopNotificationsTest::log_output_;
17
18 class MockNotificationUI : public BalloonCollectionImpl::NotificationUI {
19 public:
Add(Balloon * balloon)20 virtual void Add(Balloon* balloon) {}
Update(Balloon * balloon)21 virtual bool Update(Balloon* balloon) { return false; }
Remove(Balloon * balloon)22 virtual void Remove(Balloon* balloon) {}
Show(Balloon * balloon)23 virtual void Show(Balloon* balloon) {}
ResizeNotification(Balloon * balloon,const gfx::Size & size)24 virtual void ResizeNotification(Balloon* balloon,
25 const gfx::Size& size) {}
SetActiveView(BalloonViewImpl * view)26 virtual void SetActiveView(BalloonViewImpl* view) {}
27 };
28
MockBalloonCollection()29 MockBalloonCollection::MockBalloonCollection() {
30 set_notification_ui(new MockNotificationUI());
31 }
32
Add(const Notification & notification,Profile * profile)33 void MockBalloonCollection::Add(const Notification& notification,
34 Profile* profile) {
35 // Swap in a logging proxy for the purpose of logging calls that
36 // would be made into javascript, then pass this down to the
37 // balloon collection.
38 Notification test_notification(
39 notification.origin_url(),
40 notification.content_url(),
41 notification.display_source(),
42 notification.replace_id(),
43 new LoggingNotificationProxy(notification.notification_id()));
44 BalloonCollectionImpl::Add(test_notification, profile);
45 }
46
MakeBalloon(const Notification & notification,Profile * profile)47 Balloon* MockBalloonCollection::MakeBalloon(const Notification& notification,
48 Profile* profile) {
49 // Start with a normal balloon but mock out the view.
50 Balloon* balloon = BalloonCollectionImpl::MakeBalloon(notification, profile);
51 balloon->set_view(new MockBalloonView(balloon));
52 balloons_.insert(balloon);
53 return balloon;
54 }
55
OnBalloonClosed(Balloon * source)56 void MockBalloonCollection::OnBalloonClosed(Balloon* source) {
57 balloons_.erase(source);
58 BalloonCollectionImpl::OnBalloonClosed(source);
59 }
60
UppermostVerticalPosition()61 int MockBalloonCollection::UppermostVerticalPosition() {
62 int min = 0;
63 std::set<Balloon*>::iterator iter;
64 for (iter = balloons_.begin(); iter != balloons_.end(); ++iter) {
65 int pos = (*iter)->GetPosition().y();
66 if (iter == balloons_.begin() || pos < min)
67 min = pos;
68 }
69 return min;
70 }
71
DesktopNotificationsTest()72 DesktopNotificationsTest::DesktopNotificationsTest()
73 : ui_thread_(BrowserThread::UI, &message_loop_) {
74 }
75
~DesktopNotificationsTest()76 DesktopNotificationsTest::~DesktopNotificationsTest() {
77 }
78
SetUp()79 void DesktopNotificationsTest::SetUp() {
80 browser::RegisterLocalState(&local_state_);
81 profile_.reset(new TestingProfile());
82 balloon_collection_ = new MockBalloonCollection();
83 ui_manager_.reset(new NotificationUIManager(&local_state_));
84 ui_manager_->Initialize(balloon_collection_);
85 balloon_collection_->set_space_change_listener(ui_manager_.get());
86 service_.reset(new DesktopNotificationService(profile(), ui_manager_.get()));
87 log_output_.clear();
88 }
89
TearDown()90 void DesktopNotificationsTest::TearDown() {
91 service_.reset(NULL);
92 ui_manager_.reset(NULL);
93 profile_.reset(NULL);
94 }
95
96 DesktopNotificationHostMsg_Show_Params
StandardTestNotification()97 DesktopNotificationsTest::StandardTestNotification() {
98 DesktopNotificationHostMsg_Show_Params params;
99 params.notification_id = 0;
100 params.origin = GURL("http://www.google.com");
101 params.is_html = false;
102 params.icon_url = GURL("/icon.png");
103 params.title = ASCIIToUTF16("Title");
104 params.body = ASCIIToUTF16("Text");
105 params.direction = WebKit::WebTextDirectionDefault;
106 return params;
107 }
108
TEST_F(DesktopNotificationsTest,TestShow)109 TEST_F(DesktopNotificationsTest, TestShow) {
110 DesktopNotificationHostMsg_Show_Params params = StandardTestNotification();
111 params.notification_id = 1;
112 EXPECT_TRUE(service_->ShowDesktopNotification(
113 params, 0, 0, DesktopNotificationService::PageNotification));
114
115 MessageLoopForUI::current()->RunAllPending();
116 EXPECT_EQ(1, balloon_collection_->count());
117
118 DesktopNotificationHostMsg_Show_Params params2;
119 params2.origin = GURL("http://www.google.com");
120 params2.is_html = true;
121 params2.contents_url = GURL("http://www.google.com/notification.html");
122 params2.notification_id = 2;
123
124 EXPECT_TRUE(service_->ShowDesktopNotification(
125 params2, 0, 0, DesktopNotificationService::PageNotification));
126 MessageLoopForUI::current()->RunAllPending();
127 EXPECT_EQ(2, balloon_collection_->count());
128
129 EXPECT_EQ("notification displayed\n"
130 "notification displayed\n",
131 log_output_);
132 }
133
TEST_F(DesktopNotificationsTest,TestClose)134 TEST_F(DesktopNotificationsTest, TestClose) {
135 DesktopNotificationHostMsg_Show_Params params = StandardTestNotification();
136 params.notification_id = 1;
137
138 // Request a notification; should open a balloon.
139 EXPECT_TRUE(service_->ShowDesktopNotification(
140 params, 0, 0, DesktopNotificationService::PageNotification));
141 MessageLoopForUI::current()->RunAllPending();
142 EXPECT_EQ(1, balloon_collection_->count());
143
144 // Close all the open balloons.
145 std::set<Balloon*> balloons = balloon_collection_->balloons();
146 std::set<Balloon*>::iterator iter;
147 for (iter = balloons.begin(); iter != balloons.end(); ++iter) {
148 (*iter)->OnClose(true);
149 }
150
151 // Verify that the balloon collection is now empty.
152 EXPECT_EQ(0, balloon_collection_->count());
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
TEST_F(DesktopNotificationsTest,TestManyNotifications)187 TEST_F(DesktopNotificationsTest, TestManyNotifications) {
188 int process_id = 0;
189 int route_id = 0;
190
191 // Request lots of identical notifications.
192 const int kLotsOfToasts = 20;
193 for (int id = 1; id <= kLotsOfToasts; ++id) {
194 SCOPED_TRACE(base::StringPrintf("Creation loop: id=%d", id));
195 DesktopNotificationHostMsg_Show_Params params = StandardTestNotification();
196 params.notification_id = id;
197 EXPECT_TRUE(service_->ShowDesktopNotification(
198 params, process_id, route_id,
199 DesktopNotificationService::PageNotification));
200 }
201 MessageLoopForUI::current()->RunAllPending();
202
203 // Build up an expected log of what should be happening.
204 std::string expected_log;
205 for (int i = 0; i < kLotsOfToasts; ++i) {
206 expected_log.append("notification displayed\n");
207 }
208
209 EXPECT_EQ(kLotsOfToasts, balloon_collection_->count());
210 EXPECT_EQ(expected_log, log_output_);
211
212 // Cancel half of the notifications from the start
213 int id;
214 int cancelled = kLotsOfToasts / 2;
215 for (id = 1;
216 id <= cancelled;
217 ++id) {
218 SCOPED_TRACE(base::StringPrintf("Cancel half of notifications: id=%d", id));
219 service_->CancelDesktopNotification(process_id, route_id, id);
220 MessageLoopForUI::current()->RunAllPending();
221 expected_log.append("notification closed by script\n");
222 EXPECT_EQ(kLotsOfToasts - id,
223 balloon_collection_->count());
224 EXPECT_EQ(expected_log, log_output_);
225 }
226
227 // Now cancel the rest. It should empty the balloon space.
228 for (; id <= kLotsOfToasts; ++id) {
229 SCOPED_TRACE(base::StringPrintf("Cancel loop: id=%d", id));
230 service_->CancelDesktopNotification(process_id, route_id, id);
231 expected_log.append("notification closed by script\n");
232 MessageLoopForUI::current()->RunAllPending();
233 EXPECT_EQ(expected_log, log_output_);
234 }
235
236 // Verify that the balloon collection is now empty.
237 EXPECT_EQ(0, balloon_collection_->count());
238 }
239
TEST_F(DesktopNotificationsTest,TestEarlyDestruction)240 TEST_F(DesktopNotificationsTest, TestEarlyDestruction) {
241 // Create some toasts and then prematurely delete the notification service,
242 // just to make sure nothing crashes/leaks.
243 for (int id = 0; id <= 3; ++id) {
244 SCOPED_TRACE(base::StringPrintf("Show Text loop: id=%d", id));
245
246 EXPECT_TRUE(service_->ShowDesktopNotification(
247 StandardTestNotification(), 0, 0,
248 DesktopNotificationService::PageNotification));
249 }
250 service_.reset(NULL);
251 }
252
TEST_F(DesktopNotificationsTest,TestUserInputEscaping)253 TEST_F(DesktopNotificationsTest, TestUserInputEscaping) {
254 // Create a test script with some HTML; assert that it doesn't get into the
255 // data:// URL that's produced for the balloon.
256 DesktopNotificationHostMsg_Show_Params params = StandardTestNotification();
257 params.title = ASCIIToUTF16("<script>window.alert('uh oh');</script>");
258 params.body = ASCIIToUTF16("<i>this text is in italics</i>");
259 params.notification_id = 1;
260 EXPECT_TRUE(service_->ShowDesktopNotification(
261 params, 0, 0, DesktopNotificationService::PageNotification));
262
263 MessageLoopForUI::current()->RunAllPending();
264 EXPECT_EQ(1, balloon_collection_->count());
265 Balloon* balloon = (*balloon_collection_->balloons().begin());
266 GURL data_url = balloon->notification().content_url();
267 EXPECT_EQ(std::string::npos, data_url.spec().find("<script>"));
268 EXPECT_EQ(std::string::npos, data_url.spec().find("<i>"));
269 }
270
271 } // namespace chromeos
272