1 // Copyright 2014 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 "base/strings/utf_string_conversions.h"
6 #include "base/threading/platform_thread.h"
7 #include "base/time/time.h"
8 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
9 #include "chrome/browser/notifications/notification_delegate.h"
10 #include "chrome/browser/notifications/notification_test_util.h"
11 #include "chrome/browser/notifications/platform_notification_service_impl.h"
12 #include "chrome/test/base/testing_profile.h"
13 #include "components/content_settings/core/browser/host_content_settings_map.h"
14 #include "content/public/browser/desktop_notification_delegate.h"
15 #include "content/public/common/platform_notification_data.h"
16 #include "content/public/test/test_browser_thread_bundle.h"
17 #include "testing/gmock/include/gmock/gmock.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19 #include "third_party/skia/include/core/SkBitmap.h"
21 #if defined(ENABLE_EXTENSIONS)
22 #include "base/command_line.h"
23 #include "chrome/browser/extensions/extension_service.h"
24 #include "chrome/browser/extensions/test_extension_system.h"
25 #include "extensions/browser/extension_registry.h"
26 #include "extensions/browser/process_map.h"
27 #include "extensions/common/extension.h"
28 #include "extensions/common/extension_builder.h"
29 #include "extensions/common/permissions/api_permission.h"
30 #include "extensions/common/value_builder.h"
33 #if defined(ENABLE_EXTENSIONS) && defined(OS_CHROMEOS)
34 #include "chrome/browser/chromeos/login/users/scoped_test_user_manager.h"
35 #include "chrome/browser/chromeos/settings/cros_settings.h"
36 #include "chrome/browser/chromeos/settings/device_settings_service.h"
41 const int kNotificationVibrationPattern
[] = { 100, 200, 300 };
43 #if !defined(OS_ANDROID)
44 const int64_t kPersistentNotificationId
= 42;
47 class MockDesktopNotificationDelegate
48 : public content::DesktopNotificationDelegate
{
50 MockDesktopNotificationDelegate()
54 ~MockDesktopNotificationDelegate() override
{}
56 // content::DesktopNotificationDelegate implementation.
57 void NotificationDisplayed() override
{ displayed_
= true; }
58 void NotificationClosed() override
{}
59 void NotificationClick() override
{ clicked_
= true; }
61 bool displayed() const { return displayed_
; }
62 bool clicked() const { return clicked_
; }
71 class PlatformNotificationServiceTest
: public testing::Test
{
73 void SetUp() override
{
74 ui_manager_
.reset(new StubNotificationUIManager
);
75 profile_
.reset(new TestingProfile());
77 service()->SetNotificationUIManagerForTesting(ui_manager_
.get());
80 void TearDown() override
{
81 service()->SetNotificationUIManagerForTesting(nullptr);
88 // Displays a simple, fake notifications and returns a weak pointer to the
89 // delegate receiving events for it (ownership is transferred to the service).
90 MockDesktopNotificationDelegate
* CreateSimplePageNotification() const {
91 return CreateSimplePageNotificationWithCloseClosure(nullptr);
94 // Displays a simple, fake notification and returns a weak pointer to the
95 // delegate receiving events for it (ownership is transferred to the service).
96 // The close closure may be specified if so desired.
97 MockDesktopNotificationDelegate
* CreateSimplePageNotificationWithCloseClosure(
98 base::Closure
* close_closure
) const {
99 content::PlatformNotificationData notification_data
;
100 notification_data
.title
= base::ASCIIToUTF16("My Notification");
101 notification_data
.body
= base::ASCIIToUTF16("Hello, world!");
103 MockDesktopNotificationDelegate
* delegate
=
104 new MockDesktopNotificationDelegate();
106 service()->DisplayNotification(profile(),
107 GURL("https://chrome.com/"),
110 make_scoped_ptr(delegate
),
116 // Returns the Platform Notification Service these unit tests are for.
117 PlatformNotificationServiceImpl
* service() const {
118 return PlatformNotificationServiceImpl::GetInstance();
121 // Returns the Profile to be used for these tests.
122 Profile
* profile() const { return profile_
.get(); }
124 // Returns the UI Manager on which notifications will be displayed.
125 StubNotificationUIManager
* ui_manager() const { return ui_manager_
.get(); }
128 scoped_ptr
<StubNotificationUIManager
> ui_manager_
;
129 scoped_ptr
<TestingProfile
> profile_
;
131 content::TestBrowserThreadBundle thread_bundle_
;
134 TEST_F(PlatformNotificationServiceTest
, DisplayPageDisplayedEvent
) {
135 auto* delegate
= CreateSimplePageNotification();
137 EXPECT_EQ(1u, ui_manager()->GetNotificationCount());
138 EXPECT_TRUE(delegate
->displayed());
141 TEST_F(PlatformNotificationServiceTest
, DisplayPageCloseClosure
) {
142 base::Closure close_closure
;
143 CreateSimplePageNotificationWithCloseClosure(&close_closure
);
145 EXPECT_EQ(1u, ui_manager()->GetNotificationCount());
147 ASSERT_FALSE(close_closure
.is_null());
150 EXPECT_EQ(0u, ui_manager()->GetNotificationCount());
152 // Note that we cannot verify whether the closed event was called on the
153 // delegate given that it'd result in a use-after-free.
156 // TODO(peter): Re-enable this test when //content is responsible for creating
157 // the notification delegate ids.
158 #if !defined(OS_ANDROID)
159 TEST_F(PlatformNotificationServiceTest
, PersistentNotificationDisplay
) {
160 content::PlatformNotificationData notification_data
;
161 notification_data
.title
= base::ASCIIToUTF16("My notification's title");
162 notification_data
.body
= base::ASCIIToUTF16("Hello, world!");
164 service()->DisplayPersistentNotification(
165 profile(), kPersistentNotificationId
, GURL("https://chrome.com/"),
166 SkBitmap(), notification_data
);
168 ASSERT_EQ(1u, ui_manager()->GetNotificationCount());
170 const Notification
& notification
= ui_manager()->GetNotificationAt(0);
171 EXPECT_EQ("https://chrome.com/", notification
.origin_url().spec());
172 EXPECT_EQ("My notification's title",
173 base::UTF16ToUTF8(notification
.title()));
174 EXPECT_EQ("Hello, world!",
175 base::UTF16ToUTF8(notification
.message()));
177 service()->ClosePersistentNotification(profile(), kPersistentNotificationId
);
178 EXPECT_EQ(0u, ui_manager()->GetNotificationCount());
180 #endif // !defined(OS_ANDROID)
182 TEST_F(PlatformNotificationServiceTest
, DisplayPageNotificationMatches
) {
183 std::vector
<int> vibration_pattern(
184 kNotificationVibrationPattern
,
185 kNotificationVibrationPattern
+ arraysize(kNotificationVibrationPattern
));
187 content::PlatformNotificationData notification_data
;
188 notification_data
.title
= base::ASCIIToUTF16("My notification's title");
189 notification_data
.body
= base::ASCIIToUTF16("Hello, world!");
190 notification_data
.vibration_pattern
= vibration_pattern
;
191 notification_data
.silent
= true;
193 MockDesktopNotificationDelegate
* delegate
194 = new MockDesktopNotificationDelegate();
195 service()->DisplayNotification(profile(),
196 GURL("https://chrome.com/"),
199 make_scoped_ptr(delegate
),
202 ASSERT_EQ(1u, ui_manager()->GetNotificationCount());
204 const Notification
& notification
= ui_manager()->GetNotificationAt(0);
205 EXPECT_EQ("https://chrome.com/", notification
.origin_url().spec());
206 EXPECT_EQ("My notification's title",
207 base::UTF16ToUTF8(notification
.title()));
208 EXPECT_EQ("Hello, world!",
209 base::UTF16ToUTF8(notification
.message()));
211 EXPECT_THAT(notification
.vibration_pattern(),
212 testing::ElementsAreArray(kNotificationVibrationPattern
));
214 EXPECT_TRUE(notification
.silent());
217 TEST_F(PlatformNotificationServiceTest
, DisplayPersistentNotificationMatches
) {
218 std::vector
<int> vibration_pattern(
219 kNotificationVibrationPattern
,
220 kNotificationVibrationPattern
+ arraysize(kNotificationVibrationPattern
));
222 content::PlatformNotificationData notification_data
;
223 notification_data
.title
= base::ASCIIToUTF16("My notification's title");
224 notification_data
.body
= base::ASCIIToUTF16("Hello, world!");
225 notification_data
.vibration_pattern
= vibration_pattern
;
226 notification_data
.silent
= true;
227 notification_data
.actions
.resize(2);
228 notification_data
.actions
[0].title
= base::ASCIIToUTF16("Button 1");
229 notification_data
.actions
[1].title
= base::ASCIIToUTF16("Button 2");
231 service()->DisplayPersistentNotification(
232 profile(), 0u /* persistent notification */, GURL("https://chrome.com/"),
233 SkBitmap(), notification_data
);
235 ASSERT_EQ(1u, ui_manager()->GetNotificationCount());
237 const Notification
& notification
= ui_manager()->GetNotificationAt(0);
238 EXPECT_EQ("https://chrome.com/", notification
.origin_url().spec());
239 EXPECT_EQ("My notification's title", base::UTF16ToUTF8(notification
.title()));
240 EXPECT_EQ("Hello, world!", base::UTF16ToUTF8(notification
.message()));
242 EXPECT_THAT(notification
.vibration_pattern(),
243 testing::ElementsAreArray(kNotificationVibrationPattern
));
245 EXPECT_TRUE(notification
.silent());
247 const auto& buttons
= notification
.buttons();
248 ASSERT_EQ(2u, buttons
.size());
249 EXPECT_EQ("Button 1", base::UTF16ToUTF8(buttons
[0].title
));
250 EXPECT_EQ("Button 2", base::UTF16ToUTF8(buttons
[1].title
));
253 TEST_F(PlatformNotificationServiceTest
, NotificationPermissionLastUsage
) {
254 // Both page and persistent notifications should update the last usage
255 // time of the notification permission for the origin.
256 GURL
origin("https://chrome.com/");
257 base::Time begin_time
;
259 CreateSimplePageNotification();
261 base::Time after_page_notification
=
262 HostContentSettingsMapFactory::GetForProfile(profile())->GetLastUsage(
263 origin
, origin
, CONTENT_SETTINGS_TYPE_NOTIFICATIONS
);
264 EXPECT_GT(after_page_notification
, begin_time
);
266 // Ensure that there is at least some time between the two calls.
267 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(1));
269 service()->DisplayPersistentNotification(
270 profile(), 42 /* sw_registration_id */, origin
, SkBitmap(),
271 content::PlatformNotificationData());
273 base::Time after_persistent_notification
=
274 HostContentSettingsMapFactory::GetForProfile(profile())->GetLastUsage(
275 origin
, origin
, CONTENT_SETTINGS_TYPE_NOTIFICATIONS
);
276 EXPECT_GT(after_persistent_notification
, after_page_notification
);
279 #if defined(ENABLE_EXTENSIONS)
281 TEST_F(PlatformNotificationServiceTest
, DisplayNameForContextMessage
) {
282 base::string16 display_name
= service()->DisplayNameForContextMessage(
283 profile(), GURL("https://chrome.com/"));
285 EXPECT_TRUE(display_name
.empty());
287 // Create a mocked extension.
288 scoped_refptr
<extensions::Extension
> extension
=
289 extensions::ExtensionBuilder()
290 .SetID("honijodknafkokifofgiaalefdiedpko")
291 .SetManifest(extensions::DictionaryBuilder()
292 .Set("name", "NotificationTest")
293 .Set("version", "1.0")
294 .Set("manifest_version", 2)
295 .Set("description", "Test Extension"))
298 extensions::ExtensionRegistry
* registry
=
299 extensions::ExtensionRegistry::Get(profile());
300 EXPECT_TRUE(registry
->AddEnabled(extension
));
302 display_name
= service()->DisplayNameForContextMessage(
304 GURL("chrome-extension://honijodknafkokifofgiaalefdiedpko/main.html"));
305 EXPECT_EQ("NotificationTest", base::UTF16ToUTF8(display_name
));
308 TEST_F(PlatformNotificationServiceTest
, ExtensionPermissionChecks
) {
309 #if defined(OS_CHROMEOS)
310 // The ExtensionService on Chrome OS requires these objects to be initialized.
311 chromeos::ScopedTestDeviceSettingsService test_device_settings_service
;
312 chromeos::ScopedTestCrosSettings test_cros_settings
;
313 chromeos::ScopedTestUserManager test_user_manager
;
316 base::CommandLine
command_line(base::CommandLine::NO_PROGRAM
);
317 extensions::TestExtensionSystem
* test_extension_system
=
318 static_cast<extensions::TestExtensionSystem
*>(
319 extensions::ExtensionSystem::Get(profile()));
321 ExtensionService
* extension_service
=
322 test_extension_system
->CreateExtensionService(
323 &command_line
, base::FilePath() /* install_directory */,
324 false /* autoupdate_enabled */);
326 // Create a mocked extension that has the notifications API permission.
327 scoped_refptr
<extensions::Extension
> extension
=
328 extensions::ExtensionBuilder()
329 .SetManifest(extensions::DictionaryBuilder()
330 .Set("name", "NotificationTest")
331 .Set("version", "1.0")
332 .Set("manifest_version", 2)
333 .Set("description", "Test Extension")
334 .Set("permissions", extensions::ListBuilder().Append(
338 // Install the extension on the faked extension service, and verify that it
339 // has been added to the extension registry successfully.
340 extension_service
->AddExtension(extension
.get());
341 extensions::ExtensionRegistry
* registry
=
342 extensions::ExtensionRegistry::Get(profile());
344 ASSERT_TRUE(registry
->GetExtensionById(
345 extension
->id(), extensions::ExtensionRegistry::ENABLED
));
347 const int kFakeRenderProcessId
= 42;
349 // Mock that the extension is running in a fake render process id.
350 extensions::ProcessMap::Get(profile())->Insert(extension
->id(),
351 kFakeRenderProcessId
,
352 -1 /* site_instance_id */);
354 // Verify that the service indicates that permission has been granted. We only
355 // check the UI thread-method for now, as that's the one guarding the behavior
356 // in the browser process.
357 EXPECT_EQ(blink::WebNotificationPermissionAllowed
,
358 service()->CheckPermissionOnUIThread(profile(),
360 kFakeRenderProcessId
));
363 TEST_F(PlatformNotificationServiceTest
, CreateNotificationFromData
) {
364 content::PlatformNotificationData notification_data
;
365 notification_data
.title
= base::ASCIIToUTF16("My Notification");
366 notification_data
.body
= base::ASCIIToUTF16("Hello, world!");
368 Notification notification
= service()->CreateNotificationFromData(
369 profile(), GURL("https://chrome.com/"), SkBitmap(), notification_data
,
370 new MockNotificationDelegate("hello"));
371 EXPECT_TRUE(notification
.context_message().empty());
373 // Create a mocked extension.
374 scoped_refptr
<extensions::Extension
> extension
=
375 extensions::ExtensionBuilder()
376 .SetID("honijodknafkokifofgiaalefdiedpko")
377 .SetManifest(extensions::DictionaryBuilder()
378 .Set("name", "NotificationTest")
379 .Set("version", "1.0")
380 .Set("manifest_version", 2)
381 .Set("description", "Test Extension"))
384 extensions::ExtensionRegistry
* registry
=
385 extensions::ExtensionRegistry::Get(profile());
386 EXPECT_TRUE(registry
->AddEnabled(extension
));
388 notification
= service()->CreateNotificationFromData(
390 GURL("chrome-extension://honijodknafkokifofgiaalefdiedpko/main.html"),
391 SkBitmap(), notification_data
, new MockNotificationDelegate("hello"));
392 EXPECT_EQ("NotificationTest",
393 base::UTF16ToUTF8(notification
.context_message()));
396 #endif // defined(ENABLE_EXTENSIONS)