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;
191 MockDesktopNotificationDelegate
* delegate
192 = new MockDesktopNotificationDelegate();
193 service()->DisplayNotification(profile(),
194 GURL("https://chrome.com/"),
197 make_scoped_ptr(delegate
),
200 ASSERT_EQ(1u, ui_manager()->GetNotificationCount());
202 const Notification
& notification
= ui_manager()->GetNotificationAt(0);
203 EXPECT_EQ("https://chrome.com/", notification
.origin_url().spec());
204 EXPECT_EQ("My notification's title",
205 base::UTF16ToUTF8(notification
.title()));
206 EXPECT_EQ("Hello, world!",
207 base::UTF16ToUTF8(notification
.message()));
209 EXPECT_THAT(notification
.vibration_pattern(),
210 testing::ElementsAreArray(kNotificationVibrationPattern
));
212 EXPECT_TRUE(notification
.silent());
215 TEST_F(PlatformNotificationServiceTest
, DisplayNameForOrigin
) {
216 base::string16 display_name
=
217 service()->DisplayNameForOrigin(profile(), GURL("https://chrome.com/"));
219 EXPECT_EQ(base::ASCIIToUTF16("chrome.com"), display_name
);
221 // TODO(peter): Include unit tests for the extension-name translation
222 // functionality of DisplayNameForOriginInProcessId.
225 TEST_F(PlatformNotificationServiceTest
, TestWebOriginDisplayName
) {
226 std::string
language("en-us");
228 GURL
https_origin("https://mail.google.com/");
229 base::string16 expected_display_name
= base::ASCIIToUTF16("mail.google.com");
230 EXPECT_EQ(expected_display_name
,
231 PlatformNotificationServiceImpl::WebOriginDisplayName(https_origin
,
234 GURL
https_origin_standard_port("https://mail.google.com:443/");
235 expected_display_name
= base::ASCIIToUTF16("mail.google.com");
236 EXPECT_EQ(expected_display_name
,
237 PlatformNotificationServiceImpl::WebOriginDisplayName(
238 https_origin_standard_port
, language
));
240 GURL
https_origin_nonstandard_port("https://mail.google.com:444/");
241 expected_display_name
= base::ASCIIToUTF16("mail.google.com:444");
242 EXPECT_EQ(expected_display_name
,
243 PlatformNotificationServiceImpl::WebOriginDisplayName(
244 https_origin_nonstandard_port
, language
));
246 GURL
http_origin("http://mail.google.com/");
247 expected_display_name
= base::ASCIIToUTF16("http://mail.google.com");
248 EXPECT_EQ(expected_display_name
,
249 PlatformNotificationServiceImpl::WebOriginDisplayName(http_origin
,
252 GURL
http_origin_standard_port("http://mail.google.com:80/");
253 expected_display_name
= base::ASCIIToUTF16("http://mail.google.com");
254 EXPECT_EQ(expected_display_name
,
255 PlatformNotificationServiceImpl::WebOriginDisplayName(
256 http_origin_standard_port
, language
));
258 GURL
http_origin_nonstandard_port("http://mail.google.com:81/");
259 expected_display_name
= base::ASCIIToUTF16("http://mail.google.com:81");
260 EXPECT_EQ(expected_display_name
,
261 PlatformNotificationServiceImpl::WebOriginDisplayName(
262 http_origin_nonstandard_port
, language
));
263 // TODO(dewittj): Add file origin once it's supported.
266 TEST_F(PlatformNotificationServiceTest
, NotificationPermissionLastUsage
) {
267 // Both page and persistent notifications should update the last usage
268 // time of the notification permission for the origin.
269 GURL
origin("https://chrome.com/");
270 base::Time begin_time
;
272 CreateSimplePageNotification();
274 base::Time after_page_notification
=
275 profile()->GetHostContentSettingsMap()->GetLastUsage(
276 origin
, origin
, CONTENT_SETTINGS_TYPE_NOTIFICATIONS
);
277 EXPECT_GT(after_page_notification
, begin_time
);
279 // Ensure that there is at least some time between the two calls.
280 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(1));
282 service()->DisplayPersistentNotification(
283 profile(), 42 /* sw_registration_id */, origin
, SkBitmap(),
284 content::PlatformNotificationData());
286 base::Time after_persistent_notification
=
287 profile()->GetHostContentSettingsMap()->GetLastUsage(
288 origin
, origin
, CONTENT_SETTINGS_TYPE_NOTIFICATIONS
);
289 EXPECT_GT(after_persistent_notification
, after_page_notification
);
292 #if defined(ENABLE_EXTENSIONS)
294 TEST_F(PlatformNotificationServiceTest
, ExtensionPermissionChecks
) {
295 #if defined(OS_CHROMEOS)
296 // The ExtensionService on Chrome OS requires these objects to be initialized.
297 chromeos::ScopedTestDeviceSettingsService test_device_settings_service
;
298 chromeos::ScopedTestCrosSettings test_cros_settings
;
299 chromeos::ScopedTestUserManager test_user_manager
;
302 base::CommandLine
command_line(base::CommandLine::NO_PROGRAM
);
303 extensions::TestExtensionSystem
* test_extension_system
=
304 static_cast<extensions::TestExtensionSystem
*>(
305 extensions::ExtensionSystem::Get(profile()));
307 ExtensionService
* extension_service
=
308 test_extension_system
->CreateExtensionService(
309 &command_line
, base::FilePath() /* install_directory */,
310 false /* autoupdate_enabled*/);
312 // Create a mocked extension that has the notifications API permission.
313 scoped_refptr
<extensions::Extension
> extension
=
314 extensions::ExtensionBuilder().SetManifest(
315 extensions::DictionaryBuilder()
316 .Set("name", "NotificationTest")
317 .Set("version", "1.0")
318 .Set("manifest_version", 2)
319 .Set("description", "Test Extension")
321 extensions::ListBuilder().Append("notifications"))).Build();
323 // Install the extension on the faked extension service, and verify that it
324 // has been added to the extension registry successfully.
325 extension_service
->AddExtension(extension
.get());
326 extensions::ExtensionRegistry
* registry
=
327 extensions::ExtensionRegistry::Get(profile());
329 ASSERT_TRUE(registry
->GetExtensionById(
330 extension
->id(), extensions::ExtensionRegistry::ENABLED
));
332 const int kFakeRenderProcessId
= 42;
334 // Mock that the extension is running in a fake render process id.
335 extensions::ProcessMap::Get(profile())->Insert(extension
->id(),
336 kFakeRenderProcessId
,
337 -1 /* site_instance_id */);
339 // Verify that the service indicates that permission has been granted. We only
340 // check the UI thread-method for now, as that's the one guarding the behavior
341 // in the browser process.
342 EXPECT_EQ(blink::WebNotificationPermissionAllowed
,
343 service()->CheckPermissionOnUIThread(profile(),
345 kFakeRenderProcessId
));
348 #endif // defined(ENABLE_EXTENSIONS)