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/notifications/notification_delegate.h"
9 #include "chrome/browser/notifications/notification_test_util.h"
10 #include "chrome/browser/notifications/platform_notification_service_impl.h"
11 #include "chrome/test/base/testing_profile.h"
12 #include "components/content_settings/core/browser/host_content_settings_map.h"
13 #include "content/public/browser/desktop_notification_delegate.h"
14 #include "content/public/common/platform_notification_data.h"
15 #include "content/public/test/test_browser_thread_bundle.h"
16 #include "testing/gmock/include/gmock/gmock.h"
17 #include "testing/gtest/include/gtest/gtest.h"
18 #include "third_party/skia/include/core/SkBitmap.h"
20 #if defined(ENABLE_EXTENSIONS)
21 #include "base/command_line.h"
22 #include "chrome/browser/extensions/extension_service.h"
23 #include "chrome/browser/extensions/test_extension_system.h"
24 #include "extensions/browser/extension_registry.h"
25 #include "extensions/browser/process_map.h"
26 #include "extensions/common/extension.h"
27 #include "extensions/common/extension_builder.h"
28 #include "extensions/common/permissions/api_permission.h"
29 #include "extensions/common/value_builder.h"
32 #if defined(ENABLE_EXTENSIONS) && defined(OS_CHROMEOS)
33 #include "chrome/browser/chromeos/login/users/scoped_test_user_manager.h"
34 #include "chrome/browser/chromeos/settings/cros_settings.h"
35 #include "chrome/browser/chromeos/settings/device_settings_service.h"
40 const int kNotificationVibrationPattern
[] = { 100, 200, 300 };
42 #if !defined(OS_ANDROID)
43 const int64_t kPersistentNotificationId
= 42;
46 class MockDesktopNotificationDelegate
47 : public content::DesktopNotificationDelegate
{
49 MockDesktopNotificationDelegate()
53 ~MockDesktopNotificationDelegate() override
{}
55 // content::DesktopNotificationDelegate implementation.
56 void NotificationDisplayed() override
{ displayed_
= true; }
57 void NotificationClosed() override
{}
58 void NotificationClick() override
{ clicked_
= true; }
60 bool displayed() const { return displayed_
; }
61 bool clicked() const { return clicked_
; }
70 class PlatformNotificationServiceTest
: public testing::Test
{
72 void SetUp() override
{
73 ui_manager_
.reset(new StubNotificationUIManager
);
74 profile_
.reset(new TestingProfile());
76 service()->SetNotificationUIManagerForTesting(ui_manager_
.get());
79 void TearDown() override
{
80 service()->SetNotificationUIManagerForTesting(nullptr);
87 // Displays a simple, fake notifications and returns a weak pointer to the
88 // delegate receiving events for it (ownership is transferred to the service).
89 MockDesktopNotificationDelegate
* CreateSimplePageNotification() const {
90 return CreateSimplePageNotificationWithCloseClosure(nullptr);
93 // Displays a simple, fake notification and returns a weak pointer to the
94 // delegate receiving events for it (ownership is transferred to the service).
95 // The close closure may be specified if so desired.
96 MockDesktopNotificationDelegate
* CreateSimplePageNotificationWithCloseClosure(
97 base::Closure
* close_closure
) const {
98 content::PlatformNotificationData notification_data
;
99 notification_data
.title
= base::ASCIIToUTF16("My Notification");
100 notification_data
.body
= base::ASCIIToUTF16("Hello, world!");
102 MockDesktopNotificationDelegate
* delegate
=
103 new MockDesktopNotificationDelegate();
105 service()->DisplayNotification(profile(),
106 GURL("https://chrome.com/"),
109 make_scoped_ptr(delegate
),
115 // Returns the Platform Notification Service these unit tests are for.
116 PlatformNotificationServiceImpl
* service() const {
117 return PlatformNotificationServiceImpl::GetInstance();
120 // Returns the Profile to be used for these tests.
121 Profile
* profile() const { return profile_
.get(); }
123 // Returns the UI Manager on which notifications will be displayed.
124 StubNotificationUIManager
* ui_manager() const { return ui_manager_
.get(); }
127 scoped_ptr
<StubNotificationUIManager
> ui_manager_
;
128 scoped_ptr
<TestingProfile
> profile_
;
130 content::TestBrowserThreadBundle thread_bundle_
;
133 TEST_F(PlatformNotificationServiceTest
, DisplayPageDisplayedEvent
) {
134 auto* delegate
= CreateSimplePageNotification();
136 EXPECT_EQ(1u, ui_manager()->GetNotificationCount());
137 EXPECT_TRUE(delegate
->displayed());
140 TEST_F(PlatformNotificationServiceTest
, DisplayPageCloseClosure
) {
141 base::Closure close_closure
;
142 CreateSimplePageNotificationWithCloseClosure(&close_closure
);
144 EXPECT_EQ(1u, ui_manager()->GetNotificationCount());
146 ASSERT_FALSE(close_closure
.is_null());
149 EXPECT_EQ(0u, ui_manager()->GetNotificationCount());
151 // Note that we cannot verify whether the closed event was called on the
152 // delegate given that it'd result in a use-after-free.
155 // TODO(peter): Re-enable this test when //content is responsible for creating
156 // the notification delegate ids.
157 #if !defined(OS_ANDROID)
158 TEST_F(PlatformNotificationServiceTest
, PersistentNotificationDisplay
) {
159 content::PlatformNotificationData notification_data
;
160 notification_data
.title
= base::ASCIIToUTF16("My notification's title");
161 notification_data
.body
= base::ASCIIToUTF16("Hello, world!");
163 service()->DisplayPersistentNotification(
164 profile(), kPersistentNotificationId
, GURL("https://chrome.com/"),
165 SkBitmap(), notification_data
);
167 ASSERT_EQ(1u, ui_manager()->GetNotificationCount());
169 const Notification
& notification
= ui_manager()->GetNotificationAt(0);
170 EXPECT_EQ("https://chrome.com/", notification
.origin_url().spec());
171 EXPECT_EQ("My notification's title",
172 base::UTF16ToUTF8(notification
.title()));
173 EXPECT_EQ("Hello, world!",
174 base::UTF16ToUTF8(notification
.message()));
176 service()->ClosePersistentNotification(profile(), kPersistentNotificationId
);
177 EXPECT_EQ(0u, ui_manager()->GetNotificationCount());
179 #endif // !defined(OS_ANDROID)
181 TEST_F(PlatformNotificationServiceTest
, DisplayPageNotificationMatches
) {
182 std::vector
<int> vibration_pattern(
183 kNotificationVibrationPattern
,
184 kNotificationVibrationPattern
+ arraysize(kNotificationVibrationPattern
));
186 content::PlatformNotificationData notification_data
;
187 notification_data
.title
= base::ASCIIToUTF16("My notification's title");
188 notification_data
.body
= base::ASCIIToUTF16("Hello, world!");
189 notification_data
.vibration_pattern
= vibration_pattern
;
190 notification_data
.silent
= true;
191 notification_data
.actions
.resize(2);
192 notification_data
.actions
[0].title
= base::ASCIIToUTF16("Button 1");
193 notification_data
.actions
[1].title
= base::ASCIIToUTF16("Button 2");
195 MockDesktopNotificationDelegate
* delegate
196 = new MockDesktopNotificationDelegate();
197 service()->DisplayNotification(profile(),
198 GURL("https://chrome.com/"),
201 make_scoped_ptr(delegate
),
204 ASSERT_EQ(1u, ui_manager()->GetNotificationCount());
206 const Notification
& notification
= ui_manager()->GetNotificationAt(0);
207 EXPECT_EQ("https://chrome.com/", notification
.origin_url().spec());
208 EXPECT_EQ("My notification's title",
209 base::UTF16ToUTF8(notification
.title()));
210 EXPECT_EQ("Hello, world!",
211 base::UTF16ToUTF8(notification
.message()));
213 EXPECT_THAT(notification
.vibration_pattern(),
214 testing::ElementsAreArray(kNotificationVibrationPattern
));
216 EXPECT_TRUE(notification
.silent());
218 const auto& buttons
= notification
.buttons();
219 ASSERT_EQ(2u, buttons
.size());
220 EXPECT_EQ("Button 1", base::UTF16ToUTF8(buttons
[0].title
));
221 EXPECT_EQ("Button 2", base::UTF16ToUTF8(buttons
[1].title
));
224 TEST_F(PlatformNotificationServiceTest
, NotificationPermissionLastUsage
) {
225 // Both page and persistent notifications should update the last usage
226 // time of the notification permission for the origin.
227 GURL
origin("https://chrome.com/");
228 base::Time begin_time
;
230 CreateSimplePageNotification();
232 base::Time after_page_notification
=
233 profile()->GetHostContentSettingsMap()->GetLastUsage(
234 origin
, origin
, CONTENT_SETTINGS_TYPE_NOTIFICATIONS
);
235 EXPECT_GT(after_page_notification
, begin_time
);
237 // Ensure that there is at least some time between the two calls.
238 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(1));
240 service()->DisplayPersistentNotification(
241 profile(), 42 /* sw_registration_id */, origin
, SkBitmap(),
242 content::PlatformNotificationData());
244 base::Time after_persistent_notification
=
245 profile()->GetHostContentSettingsMap()->GetLastUsage(
246 origin
, origin
, CONTENT_SETTINGS_TYPE_NOTIFICATIONS
);
247 EXPECT_GT(after_persistent_notification
, after_page_notification
);
250 #if defined(ENABLE_EXTENSIONS)
252 TEST_F(PlatformNotificationServiceTest
, DisplayNameForContextMessage
) {
253 base::string16 display_name
= service()->DisplayNameForContextMessage(
254 profile(), GURL("https://chrome.com/"));
256 EXPECT_TRUE(display_name
.empty());
258 // Create a mocked extension.
259 scoped_refptr
<extensions::Extension
> extension
=
260 extensions::ExtensionBuilder()
261 .SetID("honijodknafkokifofgiaalefdiedpko")
262 .SetManifest(extensions::DictionaryBuilder()
263 .Set("name", "NotificationTest")
264 .Set("version", "1.0")
265 .Set("manifest_version", 2)
266 .Set("description", "Test Extension"))
269 extensions::ExtensionRegistry
* registry
=
270 extensions::ExtensionRegistry::Get(profile());
271 EXPECT_TRUE(registry
->AddEnabled(extension
));
273 display_name
= service()->DisplayNameForContextMessage(
275 GURL("chrome-extension://honijodknafkokifofgiaalefdiedpko/main.html"));
276 EXPECT_EQ("NotificationTest", base::UTF16ToUTF8(display_name
));
279 TEST_F(PlatformNotificationServiceTest
, ExtensionPermissionChecks
) {
280 #if defined(OS_CHROMEOS)
281 // The ExtensionService on Chrome OS requires these objects to be initialized.
282 chromeos::ScopedTestDeviceSettingsService test_device_settings_service
;
283 chromeos::ScopedTestCrosSettings test_cros_settings
;
284 chromeos::ScopedTestUserManager test_user_manager
;
287 base::CommandLine
command_line(base::CommandLine::NO_PROGRAM
);
288 extensions::TestExtensionSystem
* test_extension_system
=
289 static_cast<extensions::TestExtensionSystem
*>(
290 extensions::ExtensionSystem::Get(profile()));
292 ExtensionService
* extension_service
=
293 test_extension_system
->CreateExtensionService(
294 &command_line
, base::FilePath() /* install_directory */,
295 false /* autoupdate_enabled */);
297 // Create a mocked extension that has the notifications API permission.
298 scoped_refptr
<extensions::Extension
> extension
=
299 extensions::ExtensionBuilder()
300 .SetManifest(extensions::DictionaryBuilder()
301 .Set("name", "NotificationTest")
302 .Set("version", "1.0")
303 .Set("manifest_version", 2)
304 .Set("description", "Test Extension")
305 .Set("permissions", extensions::ListBuilder().Append(
309 // Install the extension on the faked extension service, and verify that it
310 // has been added to the extension registry successfully.
311 extension_service
->AddExtension(extension
.get());
312 extensions::ExtensionRegistry
* registry
=
313 extensions::ExtensionRegistry::Get(profile());
315 ASSERT_TRUE(registry
->GetExtensionById(
316 extension
->id(), extensions::ExtensionRegistry::ENABLED
));
318 const int kFakeRenderProcessId
= 42;
320 // Mock that the extension is running in a fake render process id.
321 extensions::ProcessMap::Get(profile())->Insert(extension
->id(),
322 kFakeRenderProcessId
,
323 -1 /* site_instance_id */);
325 // Verify that the service indicates that permission has been granted. We only
326 // check the UI thread-method for now, as that's the one guarding the behavior
327 // in the browser process.
328 EXPECT_EQ(blink::WebNotificationPermissionAllowed
,
329 service()->CheckPermissionOnUIThread(profile(),
331 kFakeRenderProcessId
));
334 TEST_F(PlatformNotificationServiceTest
, CreateNotificationFromData
) {
335 content::PlatformNotificationData notification_data
;
336 notification_data
.title
= base::ASCIIToUTF16("My Notification");
337 notification_data
.body
= base::ASCIIToUTF16("Hello, world!");
339 Notification notification
= service()->CreateNotificationFromData(
340 profile(), GURL("https://chrome.com/"), SkBitmap(), notification_data
,
341 new MockNotificationDelegate("hello"));
342 EXPECT_TRUE(notification
.context_message().empty());
344 // Create a mocked extension.
345 scoped_refptr
<extensions::Extension
> extension
=
346 extensions::ExtensionBuilder()
347 .SetID("honijodknafkokifofgiaalefdiedpko")
348 .SetManifest(extensions::DictionaryBuilder()
349 .Set("name", "NotificationTest")
350 .Set("version", "1.0")
351 .Set("manifest_version", 2)
352 .Set("description", "Test Extension"))
355 extensions::ExtensionRegistry
* registry
=
356 extensions::ExtensionRegistry::Get(profile());
357 EXPECT_TRUE(registry
->AddEnabled(extension
));
359 notification
= service()->CreateNotificationFromData(
361 GURL("chrome-extension://honijodknafkokifofgiaalefdiedpko/main.html"),
362 SkBitmap(), notification_data
, new MockNotificationDelegate("hello"));
363 EXPECT_EQ("NotificationTest",
364 base::UTF16ToUTF8(notification
.context_message()));
367 #endif // defined(ENABLE_EXTENSIONS)