1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
|
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/chromeos/notifications/desktop_notifications_unittest.h"
#include "base/stringprintf.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/prefs/browser_prefs.h"
#include "chrome/browser/prefs/pref_service.h"
#include "content/public/common/show_desktop_notification_params.h"
#if defined(USE_AURA)
#include "chrome/browser/chromeos/notifications/balloon_collection_impl_aura.h"
#include "ui/aura/root_window.h"
#else
#include "chrome/browser/chromeos/notifications/balloon_collection_impl.h"
#endif
#if defined(USE_AURA)
typedef class chromeos::BalloonCollectionImplAura BalloonCollectionImplType;
#else
typedef class chromeos::BalloonCollectionImpl BalloonCollectionImplType;
#endif
using content::BrowserThread;
namespace chromeos {
// static
std::string DesktopNotificationsTest::log_output_;
class BalloonViewImpl;
#if !defined(USE_AURA)
class MockNotificationUI : public BalloonCollectionImplType::NotificationUI {
public:
virtual void Add(Balloon* balloon) {}
virtual bool Update(Balloon* balloon) { return false; }
virtual void Remove(Balloon* balloon) {}
virtual void Show(Balloon* balloon) {}
virtual void ResizeNotification(Balloon* balloon,
const gfx::Size& size) {}
virtual void SetActiveView(BalloonViewImpl* view) {}
};
#endif
// Test version of the balloon collection which counts the number
// of notifications that are added to it.
class MockBalloonCollection : public BalloonCollectionImplType {
public:
MockBalloonCollection() {
#if !defined(USE_AURA)
set_notification_ui(new MockNotificationUI());
#endif
}
virtual ~MockBalloonCollection() {};
// BalloonCollectionImplType overrides
virtual void Add(const Notification& notification, Profile* profile) OVERRIDE;
virtual Balloon* MakeBalloon(const Notification& notification,
Profile* profile) OVERRIDE;
virtual void OnBalloonClosed(Balloon* source) OVERRIDE;
// Number of balloons being shown.
std::set<Balloon*>& balloons() { return balloons_; }
int count() const { return balloons_.size(); }
private:
std::set<Balloon*> balloons_;
};
void MockBalloonCollection::Add(const Notification& notification,
Profile* profile) {
// Swap in a logging proxy for the purpose of logging calls that
// would be made into javascript, then pass this down to the
// balloon collection.
typedef LoggingNotificationDelegate<DesktopNotificationsTest>
LoggingNotificationProxy;
Notification test_notification(
notification.origin_url(),
notification.content_url(),
notification.display_source(),
notification.replace_id(),
new LoggingNotificationProxy(notification.notification_id()));
BalloonCollectionImplType::Add(test_notification, profile);
}
Balloon* MockBalloonCollection::MakeBalloon(const Notification& notification,
Profile* profile) {
// Start with a normal balloon but mock out the view.
Balloon* balloon =
BalloonCollectionImplType::MakeBalloon(notification, profile);
balloon->set_view(new MockBalloonView(balloon));
balloons_.insert(balloon);
return balloon;
}
void MockBalloonCollection::OnBalloonClosed(Balloon* source) {
balloons_.erase(source);
BalloonCollectionImplType::OnBalloonClosed(source);
}
// DesktopNotificationsTest
DesktopNotificationsTest::DesktopNotificationsTest()
: ui_thread_(BrowserThread::UI, &message_loop_) {
}
DesktopNotificationsTest::~DesktopNotificationsTest() {
}
void DesktopNotificationsTest::SetUp() {
#if defined(USE_AURA)
// Make sure a root window has been instantiated.
aura::RootWindow::GetInstance();
#endif
browser::RegisterLocalState(&local_state_);
profile_.reset(new TestingProfile());
balloon_collection_ = new MockBalloonCollection();
ui_manager_.reset(NotificationUIManager::Create(&local_state_,
balloon_collection_));
service_.reset(new DesktopNotificationService(profile(), ui_manager_.get()));
log_output_.clear();
}
void DesktopNotificationsTest::TearDown() {
service_.reset(NULL);
ui_manager_.reset(NULL);
profile_.reset(NULL);
}
content::ShowDesktopNotificationHostMsgParams
DesktopNotificationsTest::StandardTestNotification() {
content::ShowDesktopNotificationHostMsgParams params;
params.notification_id = 0;
params.origin = GURL("http://www.google.com");
params.is_html = false;
params.icon_url = GURL("/icon.png");
params.title = ASCIIToUTF16("Title");
params.body = ASCIIToUTF16("Text");
params.direction = WebKit::WebTextDirectionDefault;
return params;
}
TEST_F(DesktopNotificationsTest, TestShow) {
content::ShowDesktopNotificationHostMsgParams params =
StandardTestNotification();
params.notification_id = 1;
EXPECT_TRUE(service_->ShowDesktopNotification(
params, 0, 0, DesktopNotificationService::PageNotification));
MessageLoopForUI::current()->RunAllPending();
EXPECT_EQ(1, balloon_collection_->count());
content::ShowDesktopNotificationHostMsgParams params2;
params2.origin = GURL("http://www.google.com");
params2.is_html = true;
params2.contents_url = GURL("http://www.google.com/notification.html");
params2.notification_id = 2;
EXPECT_TRUE(service_->ShowDesktopNotification(
params2, 0, 0, DesktopNotificationService::PageNotification));
MessageLoopForUI::current()->RunAllPending();
EXPECT_EQ(2, balloon_collection_->count());
EXPECT_EQ("notification displayed\n"
"notification displayed\n",
log_output_);
}
TEST_F(DesktopNotificationsTest, TestClose) {
content::ShowDesktopNotificationHostMsgParams params =
StandardTestNotification();
params.notification_id = 1;
// Request a notification; should open a balloon.
EXPECT_TRUE(service_->ShowDesktopNotification(
params, 0, 0, DesktopNotificationService::PageNotification));
MessageLoopForUI::current()->RunAllPending();
EXPECT_EQ(1, balloon_collection_->count());
// Close all the open balloons.
std::set<Balloon*> balloons = balloon_collection_->balloons();
std::set<Balloon*>::iterator iter;
for (iter = balloons.begin(); iter != balloons.end(); ++iter) {
(*iter)->OnClose(true);
}
// Verify that the balloon collection is now empty.
EXPECT_EQ(0, balloon_collection_->count());
EXPECT_EQ("notification displayed\n"
"notification closed by user\n",
log_output_);
}
TEST_F(DesktopNotificationsTest, TestCancel) {
int process_id = 0;
int route_id = 0;
int notification_id = 1;
content::ShowDesktopNotificationHostMsgParams params =
StandardTestNotification();
params.notification_id = notification_id;
// Request a notification; should open a balloon.
EXPECT_TRUE(service_->ShowDesktopNotification(
params, process_id, route_id,
DesktopNotificationService::PageNotification));
MessageLoopForUI::current()->RunAllPending();
EXPECT_EQ(1, balloon_collection_->count());
// Cancel the same notification
service_->CancelDesktopNotification(process_id,
route_id,
notification_id);
MessageLoopForUI::current()->RunAllPending();
// Verify that the balloon collection is now empty.
EXPECT_EQ(0, balloon_collection_->count());
EXPECT_EQ("notification displayed\n"
"notification closed by script\n",
log_output_);
}
TEST_F(DesktopNotificationsTest, TestManyNotifications) {
int process_id = 0;
int route_id = 0;
// Request lots of identical notifications.
#if defined(USE_AURA)
// Aura is using the non-chromeos notification system which has a limit
// of 4 visible toasts.
const int kLotsOfToasts = 4;
#else
const int kLotsOfToasts = 20;
#endif
for (int id = 1; id <= kLotsOfToasts; ++id) {
SCOPED_TRACE(base::StringPrintf("Creation loop: id=%d", id));
content::ShowDesktopNotificationHostMsgParams params =
StandardTestNotification();
params.notification_id = id;
EXPECT_TRUE(service_->ShowDesktopNotification(
params, process_id, route_id,
DesktopNotificationService::PageNotification));
}
MessageLoopForUI::current()->RunAllPending();
// Build up an expected log of what should be happening.
std::string expected_log;
for (int i = 0; i < kLotsOfToasts; ++i) {
expected_log.append("notification displayed\n");
}
EXPECT_EQ(kLotsOfToasts, balloon_collection_->count());
EXPECT_EQ(expected_log, log_output_);
// Cancel half of the notifications from the start
int id;
int cancelled = kLotsOfToasts / 2;
for (id = 1;
id <= cancelled;
++id) {
SCOPED_TRACE(base::StringPrintf("Cancel half of notifications: id=%d", id));
service_->CancelDesktopNotification(process_id, route_id, id);
MessageLoopForUI::current()->RunAllPending();
expected_log.append("notification closed by script\n");
EXPECT_EQ(kLotsOfToasts - id,
balloon_collection_->count());
EXPECT_EQ(expected_log, log_output_);
}
// Now cancel the rest. It should empty the balloon space.
for (; id <= kLotsOfToasts; ++id) {
SCOPED_TRACE(base::StringPrintf("Cancel loop: id=%d", id));
service_->CancelDesktopNotification(process_id, route_id, id);
expected_log.append("notification closed by script\n");
MessageLoopForUI::current()->RunAllPending();
EXPECT_EQ(expected_log, log_output_);
}
// Verify that the balloon collection is now empty.
EXPECT_EQ(0, balloon_collection_->count());
}
TEST_F(DesktopNotificationsTest, TestEarlyDestruction) {
// Create some toasts and then prematurely delete the notification service,
// just to make sure nothing crashes/leaks.
for (int id = 0; id <= 3; ++id) {
SCOPED_TRACE(base::StringPrintf("Show Text loop: id=%d", id));
EXPECT_TRUE(service_->ShowDesktopNotification(
StandardTestNotification(), 0, 0,
DesktopNotificationService::PageNotification));
}
service_.reset(NULL);
}
TEST_F(DesktopNotificationsTest, TestUserInputEscaping) {
// Create a test script with some HTML; assert that it doesn't get into the
// data:// URL that's produced for the balloon.
content::ShowDesktopNotificationHostMsgParams params =
StandardTestNotification();
params.title = ASCIIToUTF16("<script>window.alert('uh oh');</script>");
params.body = ASCIIToUTF16("<i>this text is in italics</i>");
params.notification_id = 1;
EXPECT_TRUE(service_->ShowDesktopNotification(
params, 0, 0, DesktopNotificationService::PageNotification));
MessageLoopForUI::current()->RunAllPending();
EXPECT_EQ(1, balloon_collection_->count());
Balloon* balloon = (*balloon_collection_->balloons().begin());
GURL data_url = balloon->notification().content_url();
EXPECT_EQ(std::string::npos, data_url.spec().find("<script>"));
EXPECT_EQ(std::string::npos, data_url.spec().find("<i>"));
}
} // namespace chromeos
|