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_test_util.h"
9 #include "chrome/browser/notifications/platform_notification_service_impl.h"
10 #include "chrome/test/base/testing_profile.h"
11 #include "components/content_settings/core/browser/host_content_settings_map.h"
12 #include "content/public/browser/desktop_notification_delegate.h"
13 #include "content/public/common/platform_notification_data.h"
14 #include "content/public/test/test_browser_thread_bundle.h"
15 #include "testing/gmock/include/gmock/gmock.h"
16 #include "testing/gtest/include/gtest/gtest.h"
17 #include "third_party/skia/include/core/SkBitmap.h"
19 #if defined(ENABLE_EXTENSIONS)
20 #include "base/command_line.h"
21 #include "chrome/browser/extensions/extension_service.h"
22 #include "chrome/browser/extensions/test_extension_system.h"
23 #include "extensions/browser/extension_registry.h"
24 #include "extensions/browser/process_map.h"
25 #include "extensions/common/extension.h"
26 #include "extensions/common/extension_builder.h"
27 #include "extensions/common/permissions/api_permission.h"
28 #include "extensions/common/value_builder.h"
31 #if defined(ENABLE_EXTENSIONS) && defined(OS_CHROMEOS)
32 #include "chrome/browser/chromeos/login/users/scoped_test_user_manager.h"
33 #include "chrome/browser/chromeos/settings/cros_settings.h"
34 #include "chrome/browser/chromeos/settings/device_settings_service.h"
39 const int kNotificationVibrationPattern
[] = { 100, 200, 300 };
41 #if !defined(OS_ANDROID)
42 const int64_t kPersistentNotificationId
= 42;
45 class MockDesktopNotificationDelegate
46 : public content::DesktopNotificationDelegate
{
48 MockDesktopNotificationDelegate()
52 ~MockDesktopNotificationDelegate() override
{}
54 // content::DesktopNotificationDelegate implementation.
55 void NotificationDisplayed() override
{ displayed_
= true; }
56 void NotificationClosed() override
{}
57 void NotificationClick() override
{ clicked_
= true; }
59 bool displayed() const { return displayed_
; }
60 bool clicked() const { return clicked_
; }
69 class PlatformNotificationServiceTest
: public testing::Test
{
71 void SetUp() override
{
72 ui_manager_
.reset(new StubNotificationUIManager
);
73 profile_
.reset(new TestingProfile());
75 service()->SetNotificationUIManagerForTesting(ui_manager_
.get());
78 void TearDown() override
{
79 service()->SetNotificationUIManagerForTesting(nullptr);
86 // Displays a simple, fake notifications and returns a weak pointer to the
87 // delegate receiving events for it (ownership is transferred to the service).
88 MockDesktopNotificationDelegate
* CreateSimplePageNotification() const {
89 return CreateSimplePageNotificationWithCloseClosure(nullptr);
92 // Displays a simple, fake notification and returns a weak pointer to the
93 // delegate receiving events for it (ownership is transferred to the service).
94 // The close closure may be specified if so desired.
95 MockDesktopNotificationDelegate
* CreateSimplePageNotificationWithCloseClosure(
96 base::Closure
* close_closure
) const {
97 content::PlatformNotificationData notification_data
;
98 notification_data
.title
= base::ASCIIToUTF16("My Notification");
99 notification_data
.body
= base::ASCIIToUTF16("Hello, world!");
101 MockDesktopNotificationDelegate
* delegate
=
102 new MockDesktopNotificationDelegate();
104 service()->DisplayNotification(profile(),
105 GURL("https://chrome.com/"),
108 make_scoped_ptr(delegate
),
114 // Returns the Platform Notification Service these unit tests are for.
115 PlatformNotificationServiceImpl
* service() const {
116 return PlatformNotificationServiceImpl::GetInstance();
119 // Returns the Profile to be used for these tests.
120 Profile
* profile() const { return profile_
.get(); }
122 // Returns the UI Manager on which notifications will be displayed.
123 StubNotificationUIManager
* ui_manager() const { return ui_manager_
.get(); }
126 scoped_ptr
<StubNotificationUIManager
> ui_manager_
;
127 scoped_ptr
<TestingProfile
> profile_
;
129 content::TestBrowserThreadBundle thread_bundle_
;
132 TEST_F(PlatformNotificationServiceTest
, DisplayPageDisplayedEvent
) {
133 auto* delegate
= CreateSimplePageNotification();
135 EXPECT_EQ(1u, ui_manager()->GetNotificationCount());
136 EXPECT_TRUE(delegate
->displayed());
139 TEST_F(PlatformNotificationServiceTest
, DisplayPageCloseClosure
) {
140 base::Closure close_closure
;
141 CreateSimplePageNotificationWithCloseClosure(&close_closure
);
143 EXPECT_EQ(1u, ui_manager()->GetNotificationCount());
145 ASSERT_FALSE(close_closure
.is_null());
148 EXPECT_EQ(0u, ui_manager()->GetNotificationCount());
150 // Note that we cannot verify whether the closed event was called on the
151 // delegate given that it'd result in a use-after-free.
154 // TODO(peter): Re-enable this test when //content is responsible for creating
155 // the notification delegate ids.
156 #if !defined(OS_ANDROID)
157 TEST_F(PlatformNotificationServiceTest
, PersistentNotificationDisplay
) {
158 content::PlatformNotificationData notification_data
;
159 notification_data
.title
= base::ASCIIToUTF16("My notification's title");
160 notification_data
.body
= base::ASCIIToUTF16("Hello, world!");
162 service()->DisplayPersistentNotification(
163 profile(), kPersistentNotificationId
, GURL("https://chrome.com/"),
164 SkBitmap(), notification_data
);
166 ASSERT_EQ(1u, ui_manager()->GetNotificationCount());
168 const Notification
& notification
= ui_manager()->GetNotificationAt(0);
169 EXPECT_EQ("https://chrome.com/", notification
.origin_url().spec());
170 EXPECT_EQ("My notification's title",
171 base::UTF16ToUTF8(notification
.title()));
172 EXPECT_EQ("Hello, world!",
173 base::UTF16ToUTF8(notification
.message()));
175 service()->ClosePersistentNotification(profile(), kPersistentNotificationId
);
176 EXPECT_EQ(0u, ui_manager()->GetNotificationCount());
178 #endif // !defined(OS_ANDROID)
180 TEST_F(PlatformNotificationServiceTest
, DisplayPageNotificationMatches
) {
181 std::vector
<int> vibration_pattern(
182 kNotificationVibrationPattern
,
183 kNotificationVibrationPattern
+ arraysize(kNotificationVibrationPattern
));
185 content::PlatformNotificationData notification_data
;
186 notification_data
.title
= base::ASCIIToUTF16("My notification's title");
187 notification_data
.body
= base::ASCIIToUTF16("Hello, world!");
188 notification_data
.vibration_pattern
= vibration_pattern
;
189 notification_data
.silent
= true;
190 notification_data
.actions
.resize(2);
191 notification_data
.actions
[0].title
= base::ASCIIToUTF16("Button 1");
192 notification_data
.actions
[1].title
= base::ASCIIToUTF16("Button 2");
194 MockDesktopNotificationDelegate
* delegate
195 = new MockDesktopNotificationDelegate();
196 service()->DisplayNotification(profile(),
197 GURL("https://chrome.com/"),
200 make_scoped_ptr(delegate
),
203 ASSERT_EQ(1u, ui_manager()->GetNotificationCount());
205 const Notification
& notification
= ui_manager()->GetNotificationAt(0);
206 EXPECT_EQ("https://chrome.com/", notification
.origin_url().spec());
207 EXPECT_EQ("My notification's title",
208 base::UTF16ToUTF8(notification
.title()));
209 EXPECT_EQ("Hello, world!",
210 base::UTF16ToUTF8(notification
.message()));
212 EXPECT_THAT(notification
.vibration_pattern(),
213 testing::ElementsAreArray(kNotificationVibrationPattern
));
215 EXPECT_TRUE(notification
.silent());
217 const auto& buttons
= notification
.buttons();
218 ASSERT_EQ(2u, buttons
.size());
219 EXPECT_EQ("Button 1", base::UTF16ToUTF8(buttons
[0].title
));
220 EXPECT_EQ("Button 2", base::UTF16ToUTF8(buttons
[1].title
));
223 TEST_F(PlatformNotificationServiceTest
, DisplayNameForOrigin
) {
224 base::string16 display_name
=
225 service()->DisplayNameForOrigin(profile(), GURL("https://chrome.com/"));
227 EXPECT_EQ(base::ASCIIToUTF16("chrome.com"), display_name
);
229 // TODO(peter): Include unit tests for the extension-name translation
230 // functionality of DisplayNameForOriginInProcessId.
233 TEST_F(PlatformNotificationServiceTest
, TestWebOriginDisplayName
) {
234 std::string
language("en-us");
236 GURL
https_origin("https://mail.google.com/");
237 base::string16 expected_display_name
= base::ASCIIToUTF16("mail.google.com");
238 EXPECT_EQ(expected_display_name
,
239 PlatformNotificationServiceImpl::WebOriginDisplayName(https_origin
,
242 GURL
https_origin_standard_port("https://mail.google.com:443/");
243 expected_display_name
= base::ASCIIToUTF16("mail.google.com");
244 EXPECT_EQ(expected_display_name
,
245 PlatformNotificationServiceImpl::WebOriginDisplayName(
246 https_origin_standard_port
, language
));
248 GURL
https_origin_nonstandard_port("https://mail.google.com:444/");
249 expected_display_name
= base::ASCIIToUTF16("mail.google.com:444");
250 EXPECT_EQ(expected_display_name
,
251 PlatformNotificationServiceImpl::WebOriginDisplayName(
252 https_origin_nonstandard_port
, language
));
254 GURL
http_origin("http://mail.google.com/");
255 expected_display_name
= base::ASCIIToUTF16("http://mail.google.com");
256 EXPECT_EQ(expected_display_name
,
257 PlatformNotificationServiceImpl::WebOriginDisplayName(http_origin
,
260 GURL
http_origin_standard_port("http://mail.google.com:80/");
261 expected_display_name
= base::ASCIIToUTF16("http://mail.google.com");
262 EXPECT_EQ(expected_display_name
,
263 PlatformNotificationServiceImpl::WebOriginDisplayName(
264 http_origin_standard_port
, language
));
266 GURL
http_origin_nonstandard_port("http://mail.google.com:81/");
267 expected_display_name
= base::ASCIIToUTF16("http://mail.google.com:81");
268 EXPECT_EQ(expected_display_name
,
269 PlatformNotificationServiceImpl::WebOriginDisplayName(
270 http_origin_nonstandard_port
, language
));
271 // TODO(dewittj): Add file origin once it's supported.
274 TEST_F(PlatformNotificationServiceTest
, NotificationPermissionLastUsage
) {
275 // Both page and persistent notifications should update the last usage
276 // time of the notification permission for the origin.
277 GURL
origin("https://chrome.com/");
278 base::Time begin_time
;
280 CreateSimplePageNotification();
282 base::Time after_page_notification
=
283 profile()->GetHostContentSettingsMap()->GetLastUsage(
284 origin
, origin
, CONTENT_SETTINGS_TYPE_NOTIFICATIONS
);
285 EXPECT_GT(after_page_notification
, begin_time
);
287 // Ensure that there is at least some time between the two calls.
288 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(1));
290 service()->DisplayPersistentNotification(
291 profile(), 42 /* sw_registration_id */, origin
, SkBitmap(),
292 content::PlatformNotificationData());
294 base::Time after_persistent_notification
=
295 profile()->GetHostContentSettingsMap()->GetLastUsage(
296 origin
, origin
, CONTENT_SETTINGS_TYPE_NOTIFICATIONS
);
297 EXPECT_GT(after_persistent_notification
, after_page_notification
);
300 #if defined(ENABLE_EXTENSIONS)
302 TEST_F(PlatformNotificationServiceTest
, ExtensionPermissionChecks
) {
303 #if defined(OS_CHROMEOS)
304 // The ExtensionService on Chrome OS requires these objects to be initialized.
305 chromeos::ScopedTestDeviceSettingsService test_device_settings_service
;
306 chromeos::ScopedTestCrosSettings test_cros_settings
;
307 chromeos::ScopedTestUserManager test_user_manager
;
310 base::CommandLine
command_line(base::CommandLine::NO_PROGRAM
);
311 extensions::TestExtensionSystem
* test_extension_system
=
312 static_cast<extensions::TestExtensionSystem
*>(
313 extensions::ExtensionSystem::Get(profile()));
315 ExtensionService
* extension_service
=
316 test_extension_system
->CreateExtensionService(
317 &command_line
, base::FilePath() /* install_directory */,
318 false /* autoupdate_enabled*/);
320 // Create a mocked extension that has the notifications API permission.
321 scoped_refptr
<extensions::Extension
> extension
=
322 extensions::ExtensionBuilder().SetManifest(
323 extensions::DictionaryBuilder()
324 .Set("name", "NotificationTest")
325 .Set("version", "1.0")
326 .Set("manifest_version", 2)
327 .Set("description", "Test Extension")
329 extensions::ListBuilder().Append("notifications"))).Build();
331 // Install the extension on the faked extension service, and verify that it
332 // has been added to the extension registry successfully.
333 extension_service
->AddExtension(extension
.get());
334 extensions::ExtensionRegistry
* registry
=
335 extensions::ExtensionRegistry::Get(profile());
337 ASSERT_TRUE(registry
->GetExtensionById(
338 extension
->id(), extensions::ExtensionRegistry::ENABLED
));
340 const int kFakeRenderProcessId
= 42;
342 // Mock that the extension is running in a fake render process id.
343 extensions::ProcessMap::Get(profile())->Insert(extension
->id(),
344 kFakeRenderProcessId
,
345 -1 /* site_instance_id */);
347 // Verify that the service indicates that permission has been granted. We only
348 // check the UI thread-method for now, as that's the one guarding the behavior
349 // in the browser process.
350 EXPECT_EQ(blink::WebNotificationPermissionAllowed
,
351 service()->CheckPermissionOnUIThread(profile(),
353 kFakeRenderProcessId
));
356 #endif // defined(ENABLE_EXTENSIONS)