blob: cbac30fa7a12db927ba6a15173ba1181e03e0723 [file] [log] [blame]
// Copyright 2018 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 <stdint.h>
#include <memory>
#include <vector>
#include "base/bind.h"
#include "base/callback_forward.h"
#include "base/callback_helpers.h"
#include "base/macros.h"
#include "base/memory/scoped_refptr.h"
#include "base/optional.h"
#include "base/run_loop.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_simple_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "content/browser/notifications/blink_notification_service_impl.h"
#include "content/browser/notifications/platform_notification_context_impl.h"
#include "content/browser/service_worker/embedded_worker_test_helper.h"
#include "content/browser/service_worker/service_worker_context_wrapper.h"
#include "content/common/service_worker/service_worker_types.h"
#include "content/public/browser/permission_type.h"
#include "content/public/common/content_features.h"
#include "content/public/test/mock_permission_manager.h"
#include "content/public/test/test_browser_context.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "content/public/test/test_utils.h"
#include "content/test/mock_platform_notification_service.h"
#include "content/test/test_content_browser_client.h"
#include "mojo/core/embedder/embedder.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "mojo/public/cpp/bindings/interface_request.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/notifications/notification_constants.h"
#include "third_party/blink/public/common/notifications/notification_resources.h"
#include "third_party/blink/public/mojom/notifications/notification_service.mojom.h"
#include "third_party/blink/public/mojom/permissions/permission_status.mojom.h"
#include "third_party/skia/include/core/SkBitmap.h"
using ::testing::Return;
using ::testing::_;
namespace content {
namespace {
const char kTestOrigin[] = "https://example.com";
const char kTestServiceWorkerUrl[] = "https://example.com/sw.js";
const char kBadMessageImproperNotificationImage[] =
"Received an unexpected message with image while notification images are "
"disabled.";
const char kBadMessageInvalidNotificationTriggerTimestamp[] =
"Received an invalid notification trigger timestamp.";
SkBitmap CreateBitmap(int width, int height, SkColor color) {
SkBitmap bitmap;
bitmap.allocN32Pixels(width, height);
bitmap.eraseColor(color);
return bitmap;
}
class MockNonPersistentNotificationListener
: public blink::mojom::NonPersistentNotificationListener {
public:
MockNonPersistentNotificationListener() : binding_(this) {}
~MockNonPersistentNotificationListener() override = default;
blink::mojom::NonPersistentNotificationListenerPtr GetPtr() {
blink::mojom::NonPersistentNotificationListenerPtr ptr;
binding_.Bind(mojo::MakeRequest(&ptr));
return ptr;
}
// NonPersistentNotificationListener interface.
void OnShow() override {}
void OnClick(OnClickCallback completed_closure) override {
std::move(completed_closure).Run();
}
void OnClose(OnCloseCallback completed_closure) override {
std::move(completed_closure).Run();
}
private:
mojo::Binding<blink::mojom::NonPersistentNotificationListener> binding_;
};
// This is for overriding the Platform Notification Service with a mock one.
class NotificationBrowserClient : public TestContentBrowserClient {
public:
NotificationBrowserClient(
MockPlatformNotificationService* mock_platform_service)
: platform_notification_service_(mock_platform_service) {}
PlatformNotificationService* GetPlatformNotificationService(
BrowserContext* browser_context) override {
return platform_notification_service_;
}
private:
MockPlatformNotificationService* platform_notification_service_;
};
} // anonymous namespace
class BlinkNotificationServiceImplTest : public ::testing::Test {
public:
// Using REAL_IO_THREAD would give better coverage for thread safety, but
// at time of writing EmbeddedWorkerTestHelper didn't seem to support that.
BlinkNotificationServiceImplTest()
: thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP),
embedded_worker_helper_(
std::make_unique<EmbeddedWorkerTestHelper>(base::FilePath())),
mock_platform_service_(&browser_context_),
notification_browser_client_(&mock_platform_service_) {
SetBrowserClientForTesting(&notification_browser_client_);
}
~BlinkNotificationServiceImplTest() override = default;
// ::testing::Test overrides.
void SetUp() override {
notification_context_ = new PlatformNotificationContextImpl(
base::FilePath(), &browser_context_,
embedded_worker_helper_->context_wrapper());
notification_context_->Initialize();
// Wait for notification context to be initialized to avoid TSAN detecting
// a memory race in tests - in production the PlatformNotificationContext
// will be initialized long before it is read from so this is fine.
RunAllTasksUntilIdle();
notification_service_ = std::make_unique<BlinkNotificationServiceImpl>(
notification_context_.get(), &browser_context_,
embedded_worker_helper_->context_wrapper(),
url::Origin::Create(GURL(kTestOrigin)),
mojo::MakeRequest(&notification_service_ptr_));
// Provide a mock permission manager to the |browser_context_|.
browser_context_.SetPermissionControllerDelegate(
std::make_unique<testing::NiceMock<MockPermissionManager>>());
mojo::core::SetDefaultProcessErrorCallback(base::AdaptCallbackForRepeating(
base::BindOnce(&BlinkNotificationServiceImplTest::OnMojoError,
base::Unretained(this))));
}
void TearDown() override {
mojo::core::SetDefaultProcessErrorCallback(
mojo::core::ProcessErrorCallback());
embedded_worker_helper_.reset();
// Give pending shutdown operations a chance to finish.
base::RunLoop().RunUntilIdle();
}
void RegisterServiceWorker(
scoped_refptr<ServiceWorkerRegistration>* service_worker_registration) {
int64_t service_worker_registration_id =
blink::mojom::kInvalidServiceWorkerRegistrationId;
blink::mojom::ServiceWorkerRegistrationOptions options;
options.scope = GURL(kTestOrigin);
{
base::RunLoop run_loop;
embedded_worker_helper_->context()->RegisterServiceWorker(
GURL(kTestServiceWorkerUrl), options,
base::BindOnce(
&BlinkNotificationServiceImplTest::DidRegisterServiceWorker,
base::Unretained(this), &service_worker_registration_id,
run_loop.QuitClosure()));
run_loop.Run();
}
if (service_worker_registration_id ==
blink::mojom::kInvalidServiceWorkerRegistrationId) {
ADD_FAILURE() << "Could not obtain a valid Service Worker registration";
}
{
base::RunLoop run_loop;
embedded_worker_helper_->context()->storage()->FindRegistrationForId(
service_worker_registration_id, GURL(kTestOrigin),
base::BindOnce(&BlinkNotificationServiceImplTest::
DidFindServiceWorkerRegistration,
base::Unretained(this), service_worker_registration,
run_loop.QuitClosure()));
run_loop.Run();
}
// Wait for the worker to be activated.
base::RunLoop().RunUntilIdle();
if (!*service_worker_registration) {
ADD_FAILURE() << "Could not find the new Service Worker registration.";
}
}
void DidRegisterServiceWorker(int64_t* out_service_worker_registration_id,
base::OnceClosure quit_closure,
blink::ServiceWorkerStatusCode status,
const std::string& status_message,
int64_t service_worker_registration_id) {
DCHECK(out_service_worker_registration_id);
EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk, status)
<< blink::ServiceWorkerStatusToString(status);
*out_service_worker_registration_id = service_worker_registration_id;
std::move(quit_closure).Run();
}
void DidFindServiceWorkerRegistration(
scoped_refptr<ServiceWorkerRegistration>* out_service_worker_registration,
base::OnceClosure quit_closure,
blink::ServiceWorkerStatusCode status,
scoped_refptr<ServiceWorkerRegistration> service_worker_registration) {
DCHECK(out_service_worker_registration);
EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk, status)
<< blink::ServiceWorkerStatusToString(status);
*out_service_worker_registration = service_worker_registration;
std::move(quit_closure).Run();
}
void DidGetPermissionStatus(
base::OnceClosure quit_closure,
blink::mojom::PermissionStatus permission_status) {
permission_callback_result_ = permission_status;
std::move(quit_closure).Run();
}
blink::mojom::PermissionStatus GetPermissionCallbackResult() {
return permission_callback_result_;
}
void DidDisplayPersistentNotification(
base::OnceClosure quit_closure,
blink::mojom::PersistentNotificationError error) {
display_persistent_callback_result_ = error;
std::move(quit_closure).Run();
}
void DidGetNotifications(
base::OnceClosure quit_closure,
const std::vector<std::string>& notification_ids,
const std::vector<blink::PlatformNotificationData>& notification_datas) {
get_notifications_callback_result_ = notification_ids;
std::move(quit_closure).Run();
}
void DidGetNotificationDataFromContext(
base::OnceClosure quit_closure,
bool success,
const std::vector<NotificationDatabaseData>& notification_datas) {
get_notifications_data_ = notification_datas;
std::move(quit_closure).Run();
}
void DidGetNotificationResourcesFromContext(
base::OnceClosure quit_closure,
bool success,
const blink::NotificationResources& notification_resources) {
if (success) {
get_notification_resources_ = notification_resources;
} else {
get_notification_resources_ = base::nullopt;
}
std::move(quit_closure).Run();
}
void DidGetDisplayedNotifications(base::OnceClosure quit_closure,
std::set<std::string> notification_ids,
bool supports_synchronization) {
get_displayed_callback_result_ = std::move(notification_ids);
std::move(quit_closure).Run();
}
void DidReadNotificationData(base::OnceClosure quit_closure,
bool success,
const NotificationDatabaseData& data) {
read_notification_data_callback_result_ = success;
std::move(quit_closure).Run();
}
void DisplayNonPersistentNotification(
const std::string& token,
const blink::PlatformNotificationData& platform_notification_data,
const blink::NotificationResources& notification_resources,
blink::mojom::NonPersistentNotificationListenerPtr event_listener_ptr) {
notification_service_ptr_->DisplayNonPersistentNotification(
token, platform_notification_data, notification_resources,
std::move(event_listener_ptr));
// TODO(https://crbug.com/787459): Pass a callback to
// DisplayNonPersistentNotification instead of waiting for all tasks to run
// here; a callback parameter will be needed anyway to enable
// non-persistent notification event acknowledgements - see bug.
RunAllTasksUntilIdle();
}
void DisplayPersistentNotificationSync(
int64_t service_worker_registration_id,
const blink::PlatformNotificationData& platform_notification_data,
const blink::NotificationResources& notification_resources) {
base::RunLoop run_loop;
notification_service_ptr_.set_connection_error_handler(
run_loop.QuitClosure());
notification_service_ptr_->DisplayPersistentNotification(
service_worker_registration_id, platform_notification_data,
notification_resources,
base::BindOnce(
&BlinkNotificationServiceImplTest::DidDisplayPersistentNotification,
base::Unretained(this), run_loop.QuitClosure()));
run_loop.Run();
}
std::vector<std::string> GetNotificationsSync(
int64_t service_worker_registration_id,
const std::string& filter_tag,
bool include_triggered) {
base::RunLoop run_loop;
notification_service_->GetNotifications(
service_worker_registration_id, filter_tag, include_triggered,
base::BindOnce(&BlinkNotificationServiceImplTest::DidGetNotifications,
base::Unretained(this), run_loop.QuitClosure()));
run_loop.Run();
return get_notifications_callback_result_;
}
size_t CountDisplayedNotificationsSync(int64_t service_worker_registration_id,
const std::string& filter_tag) {
return GetNotificationsSync(service_worker_registration_id, filter_tag,
/* include_triggered= */ false)
.size();
}
size_t CountScheduledNotificationsSync(int64_t service_worker_registration_id,
const std::string& filter_tag) {
return GetNotificationsSync(service_worker_registration_id, filter_tag,
/* include_triggered= */ true)
.size();
}
std::vector<NotificationDatabaseData> GetNotificationDataFromContextSync(
int64_t service_worker_registration_id,
const std::string& filter_tag,
bool include_triggered) {
base::RunLoop run_loop;
notification_context_->ReadAllNotificationDataForServiceWorkerRegistration(
GURL(kTestOrigin), service_worker_registration_id,
base::AdaptCallbackForRepeating(
base::BindOnce(&BlinkNotificationServiceImplTest::
DidGetNotificationDataFromContext,
base::Unretained(this), run_loop.QuitClosure())));
run_loop.Run();
return get_notifications_data_;
}
base::Optional<blink::NotificationResources>
GetNotificationResourcesFromContextSync(const std::string& notification_id) {
base::RunLoop run_loop;
notification_context_->ReadNotificationResources(
notification_id, GURL(kTestOrigin),
base::AdaptCallbackForRepeating(
base::BindOnce(&BlinkNotificationServiceImplTest::
DidGetNotificationResourcesFromContext,
base::Unretained(this), run_loop.QuitClosure())));
run_loop.Run();
return get_notification_resources_;
}
// Synchronous wrapper of
// PlatformNotificationService::GetDisplayedNotifications
std::set<std::string> GetDisplayedNotifications() {
base::RunLoop run_loop;
mock_platform_service_.GetDisplayedNotifications(
base::BindOnce(
&BlinkNotificationServiceImplTest::DidGetDisplayedNotifications,
base::Unretained(this), run_loop.QuitClosure()));
run_loop.Run();
return get_displayed_callback_result_;
}
// Synchronous wrapper of
// PlatformNotificationContext::ReadNotificationData
bool ReadNotificationData(const std::string& notification_id) {
base::RunLoop run_loop;
notification_context_->ReadNotificationDataAndRecordInteraction(
notification_id, GURL(kTestOrigin),
PlatformNotificationContext::Interaction::NONE,
base::AdaptCallbackForRepeating(base::BindOnce(
&BlinkNotificationServiceImplTest::DidReadNotificationData,
base::Unretained(this), run_loop.QuitClosure())));
run_loop.Run();
return read_notification_data_callback_result_;
}
// Updates the permission status for the |kTestOrigin| to the given
// |permission_status| through the PermissionManager.
void SetPermissionStatus(blink::mojom::PermissionStatus permission_status) {
MockPermissionManager* mock_permission_manager =
static_cast<MockPermissionManager*>(
browser_context_.GetPermissionControllerDelegate());
ON_CALL(*mock_permission_manager,
GetPermissionStatus(PermissionType::NOTIFICATIONS, _, _))
.WillByDefault(Return(permission_status));
}
protected:
void OnMojoError(const std::string& error) { bad_messages_.push_back(error); }
TestBrowserThreadBundle thread_bundle_; // Must be first member.
std::unique_ptr<EmbeddedWorkerTestHelper> embedded_worker_helper_;
std::unique_ptr<BlinkNotificationServiceImpl> notification_service_;
blink::mojom::NotificationServicePtr notification_service_ptr_;
TestBrowserContext browser_context_;
scoped_refptr<PlatformNotificationContextImpl> notification_context_;
MockPlatformNotificationService mock_platform_service_;
MockNonPersistentNotificationListener non_persistent_notification_listener_;
blink::mojom::PersistentNotificationError display_persistent_callback_result_;
std::vector<std::string> bad_messages_;
private:
NotificationBrowserClient notification_browser_client_;
blink::mojom::PermissionStatus permission_callback_result_ =
blink::mojom::PermissionStatus::ASK;
std::set<std::string> get_displayed_callback_result_;
std::vector<std::string> get_notifications_callback_result_;
std::vector<NotificationDatabaseData> get_notifications_data_;
base::Optional<blink::NotificationResources> get_notification_resources_;
bool read_notification_data_callback_result_ = false;
DISALLOW_COPY_AND_ASSIGN(BlinkNotificationServiceImplTest);
};
TEST_F(BlinkNotificationServiceImplTest, GetPermissionStatus) {
SetPermissionStatus(blink::mojom::PermissionStatus::GRANTED);
{
base::RunLoop run_loop;
notification_service_->GetPermissionStatus(base::BindOnce(
&BlinkNotificationServiceImplTest::DidGetPermissionStatus,
base::Unretained(this), run_loop.QuitClosure()));
run_loop.Run();
}
EXPECT_EQ(blink::mojom::PermissionStatus::GRANTED,
GetPermissionCallbackResult());
SetPermissionStatus(blink::mojom::PermissionStatus::DENIED);
{
base::RunLoop run_loop;
notification_service_->GetPermissionStatus(base::BindOnce(
&BlinkNotificationServiceImplTest::DidGetPermissionStatus,
base::Unretained(this), run_loop.QuitClosure()));
run_loop.Run();
}
EXPECT_EQ(blink::mojom::PermissionStatus::DENIED,
GetPermissionCallbackResult());
SetPermissionStatus(blink::mojom::PermissionStatus::ASK);
{
base::RunLoop run_loop;
notification_service_->GetPermissionStatus(base::BindOnce(
&BlinkNotificationServiceImplTest::DidGetPermissionStatus,
base::Unretained(this), run_loop.QuitClosure()));
run_loop.Run();
}
EXPECT_EQ(blink::mojom::PermissionStatus::ASK, GetPermissionCallbackResult());
}
TEST_F(BlinkNotificationServiceImplTest,
DisplayNonPersistentNotificationWithPermission) {
SetPermissionStatus(blink::mojom::PermissionStatus::GRANTED);
DisplayNonPersistentNotification(
"token", blink::PlatformNotificationData(),
blink::NotificationResources(),
non_persistent_notification_listener_.GetPtr());
EXPECT_EQ(1u, GetDisplayedNotifications().size());
}
TEST_F(BlinkNotificationServiceImplTest,
DisplayNonPersistentNotificationWithoutPermission) {
SetPermissionStatus(blink::mojom::PermissionStatus::DENIED);
DisplayNonPersistentNotification(
"token", blink::PlatformNotificationData(),
blink::NotificationResources(),
non_persistent_notification_listener_.GetPtr());
EXPECT_EQ(0u, GetDisplayedNotifications().size());
}
TEST_F(BlinkNotificationServiceImplTest,
DisplayNonPersistentNotificationWithContentImageSwitchOn) {
SetPermissionStatus(blink::mojom::PermissionStatus::GRANTED);
blink::NotificationResources resources;
resources.image = CreateBitmap(200, 100, SK_ColorMAGENTA);
DisplayNonPersistentNotification(
"token", blink::PlatformNotificationData(), resources,
non_persistent_notification_listener_.GetPtr());
EXPECT_EQ(1u, GetDisplayedNotifications().size());
}
TEST_F(BlinkNotificationServiceImplTest,
DisplayNonPersistentNotificationWithContentImageSwitchOff) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndDisableFeature(
features::kNotificationContentImage);
SetPermissionStatus(blink::mojom::PermissionStatus::GRANTED);
ASSERT_TRUE(bad_messages_.empty());
blink::NotificationResources resources;
resources.image = CreateBitmap(200, 100, SK_ColorMAGENTA);
DisplayNonPersistentNotification(
"token", blink::PlatformNotificationData(), resources,
non_persistent_notification_listener_.GetPtr());
EXPECT_EQ(1u, bad_messages_.size());
EXPECT_EQ(kBadMessageImproperNotificationImage, bad_messages_[0]);
}
TEST_F(BlinkNotificationServiceImplTest,
DisplayPersistentNotificationWithContentImageSwitchOn) {
SetPermissionStatus(blink::mojom::PermissionStatus::GRANTED);
scoped_refptr<ServiceWorkerRegistration> registration;
RegisterServiceWorker(&registration);
blink::NotificationResources resources;
resources.image = CreateBitmap(200, 100, SK_ColorMAGENTA);
DisplayPersistentNotificationSync(
registration->id(), blink::PlatformNotificationData(), resources);
EXPECT_EQ(blink::mojom::PersistentNotificationError::NONE,
display_persistent_callback_result_);
// Wait for service to receive the Display call.
RunAllTasksUntilIdle();
EXPECT_EQ(1u, GetDisplayedNotifications().size());
}
TEST_F(BlinkNotificationServiceImplTest,
DisplayPersistentNotificationWithContentImageSwitchOff) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndDisableFeature(
features::kNotificationContentImage);
SetPermissionStatus(blink::mojom::PermissionStatus::GRANTED);
scoped_refptr<ServiceWorkerRegistration> registration;
RegisterServiceWorker(&registration);
ASSERT_TRUE(bad_messages_.empty());
blink::NotificationResources resources;
resources.image = CreateBitmap(200, 100, SK_ColorMAGENTA);
DisplayPersistentNotificationSync(
registration->id(), blink::PlatformNotificationData(), resources);
EXPECT_EQ(1u, bad_messages_.size());
EXPECT_EQ(kBadMessageImproperNotificationImage, bad_messages_[0]);
}
TEST_F(BlinkNotificationServiceImplTest,
DisplayPersistentNotificationWithPermission) {
SetPermissionStatus(blink::mojom::PermissionStatus::GRANTED);
scoped_refptr<ServiceWorkerRegistration> registration;
RegisterServiceWorker(&registration);
DisplayPersistentNotificationSync(registration->id(),
blink::PlatformNotificationData(),
blink::NotificationResources());
EXPECT_EQ(blink::mojom::PersistentNotificationError::NONE,
display_persistent_callback_result_);
// Wait for service to receive the Display call.
RunAllTasksUntilIdle();
EXPECT_EQ(1u, GetDisplayedNotifications().size());
}
TEST_F(BlinkNotificationServiceImplTest, CloseDisplayedPersistentNotification) {
SetPermissionStatus(blink::mojom::PermissionStatus::GRANTED);
scoped_refptr<ServiceWorkerRegistration> registration;
RegisterServiceWorker(&registration);
DisplayPersistentNotificationSync(registration->id(),
blink::PlatformNotificationData(),
blink::NotificationResources());
ASSERT_EQ(blink::mojom::PersistentNotificationError::NONE,
display_persistent_callback_result_);
// Wait for service to receive the Display call.
RunAllTasksUntilIdle();
std::set<std::string> notification_ids = GetDisplayedNotifications();
ASSERT_EQ(1u, notification_ids.size());
notification_service_->ClosePersistentNotification(*notification_ids.begin());
// Wait for service to receive the Close call.
RunAllTasksUntilIdle();
EXPECT_EQ(0u, GetDisplayedNotifications().size());
}
TEST_F(BlinkNotificationServiceImplTest,
ClosePersistentNotificationDeletesFromDatabase) {
SetPermissionStatus(blink::mojom::PermissionStatus::GRANTED);
scoped_refptr<ServiceWorkerRegistration> registration;
RegisterServiceWorker(&registration);
DisplayPersistentNotificationSync(registration->id(),
blink::PlatformNotificationData(),
blink::NotificationResources());
ASSERT_EQ(blink::mojom::PersistentNotificationError::NONE,
display_persistent_callback_result_);
// Wait for service to receive the Display call.
RunAllTasksUntilIdle();
std::set<std::string> notification_ids = GetDisplayedNotifications();
ASSERT_EQ(1u, notification_ids.size());
std::string notification_id = *notification_ids.begin();
// Check data was indeed written.
ASSERT_EQ(true /* success */, ReadNotificationData(notification_id));
notification_service_->ClosePersistentNotification(notification_id);
// Wait for service to receive the Close call.
RunAllTasksUntilIdle();
// Data should now be deleted.
EXPECT_EQ(false /* success */, ReadNotificationData(notification_id));
}
TEST_F(BlinkNotificationServiceImplTest,
DisplayPersistentNotificationWithoutPermission) {
SetPermissionStatus(blink::mojom::PermissionStatus::DENIED);
scoped_refptr<ServiceWorkerRegistration> registration;
RegisterServiceWorker(&registration);
DisplayPersistentNotificationSync(registration->id(),
blink::PlatformNotificationData(),
blink::NotificationResources());
EXPECT_EQ(blink::mojom::PersistentNotificationError::PERMISSION_DENIED,
display_persistent_callback_result_);
// Give Service a chance to receive any unexpected Display calls.
RunAllTasksUntilIdle();
EXPECT_EQ(0u, GetDisplayedNotifications().size());
}
TEST_F(BlinkNotificationServiceImplTest,
DisplayMultiplePersistentNotifications) {
SetPermissionStatus(blink::mojom::PermissionStatus::GRANTED);
scoped_refptr<ServiceWorkerRegistration> registration;
RegisterServiceWorker(&registration);
DisplayPersistentNotificationSync(registration->id(),
blink::PlatformNotificationData(),
blink::NotificationResources());
DisplayPersistentNotificationSync(registration->id(),
blink::PlatformNotificationData(),
blink::NotificationResources());
// Wait for service to receive all the Display calls.
RunAllTasksUntilIdle();
EXPECT_EQ(2u, GetDisplayedNotifications().size());
}
TEST_F(BlinkNotificationServiceImplTest, GetNotifications) {
SetPermissionStatus(blink::mojom::PermissionStatus::GRANTED);
scoped_refptr<ServiceWorkerRegistration> registration;
RegisterServiceWorker(&registration);
EXPECT_EQ(0u, CountDisplayedNotificationsSync(registration->id(),
/* filter_tag= */ ""));
DisplayPersistentNotificationSync(registration->id(),
blink::PlatformNotificationData(),
blink::NotificationResources());
// Wait for service to receive all the Display calls.
RunAllTasksUntilIdle();
EXPECT_EQ(1u, CountDisplayedNotificationsSync(registration->id(),
/* filter_tag= */ ""));
}
TEST_F(BlinkNotificationServiceImplTest, GetNotificationsWithoutPermission) {
SetPermissionStatus(blink::mojom::PermissionStatus::GRANTED);
scoped_refptr<ServiceWorkerRegistration> registration;
RegisterServiceWorker(&registration);
DisplayPersistentNotificationSync(registration->id(),
blink::PlatformNotificationData(),
blink::NotificationResources());
// Wait for service to receive all the Display calls.
RunAllTasksUntilIdle();
SetPermissionStatus(blink::mojom::PermissionStatus::DENIED);
EXPECT_EQ(0u, CountDisplayedNotificationsSync(registration->id(),
/* filter_tag= */ ""));
}
TEST_F(BlinkNotificationServiceImplTest, GetNotificationsWithFilter) {
SetPermissionStatus(blink::mojom::PermissionStatus::GRANTED);
scoped_refptr<ServiceWorkerRegistration> registration;
RegisterServiceWorker(&registration);
blink::PlatformNotificationData platform_notification_data;
platform_notification_data.tag = "tagA";
blink::PlatformNotificationData other_platform_notification_data;
other_platform_notification_data.tag = "tagB";
DisplayPersistentNotificationSync(registration->id(),
platform_notification_data,
blink::NotificationResources());
DisplayPersistentNotificationSync(registration->id(),
other_platform_notification_data,
blink::NotificationResources());
// Wait for service to receive all the Display calls.
RunAllTasksUntilIdle();
EXPECT_EQ(2u, CountDisplayedNotificationsSync(registration->id(), ""));
EXPECT_EQ(1u, CountDisplayedNotificationsSync(registration->id(), "tagA"));
EXPECT_EQ(1u, CountDisplayedNotificationsSync(registration->id(), "tagB"));
EXPECT_EQ(0u, CountDisplayedNotificationsSync(registration->id(), "tagC"));
EXPECT_EQ(0u, CountDisplayedNotificationsSync(registration->id(), "tag"));
}
TEST_F(BlinkNotificationServiceImplTest, GetTriggeredNotificationsWithFilter) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(features::kNotificationTriggers);
SetPermissionStatus(blink::mojom::PermissionStatus::GRANTED);
scoped_refptr<ServiceWorkerRegistration> registration;
RegisterServiceWorker(&registration);
base::Time timestamp = base::Time::Now() + base::TimeDelta::FromSeconds(10);
blink::PlatformNotificationData platform_notification_data;
platform_notification_data.tag = "tagA";
platform_notification_data.show_trigger_timestamp = timestamp;
blink::PlatformNotificationData other_platform_notification_data;
other_platform_notification_data.tag = "tagB";
other_platform_notification_data.show_trigger_timestamp = timestamp;
blink::PlatformNotificationData displayed_notification_data;
displayed_notification_data.tag = "tagC";
DisplayPersistentNotificationSync(registration->id(),
platform_notification_data,
blink::NotificationResources());
DisplayPersistentNotificationSync(registration->id(),
other_platform_notification_data,
blink::NotificationResources());
// Wait for service to receive all the Display calls.
RunAllTasksUntilIdle();
EXPECT_EQ(0u, CountDisplayedNotificationsSync(registration->id(), ""));
EXPECT_EQ(2u, CountScheduledNotificationsSync(registration->id(), ""));
EXPECT_EQ(1u, CountScheduledNotificationsSync(registration->id(), "tagA"));
EXPECT_EQ(1u, CountScheduledNotificationsSync(registration->id(), "tagB"));
EXPECT_EQ(0u, CountScheduledNotificationsSync(registration->id(), "tagC"));
EXPECT_EQ(0u, CountScheduledNotificationsSync(registration->id(), "tag"));
}
TEST_F(BlinkNotificationServiceImplTest, ResourcesStoredForTriggered) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(features::kNotificationTriggers);
SetPermissionStatus(blink::mojom::PermissionStatus::GRANTED);
scoped_refptr<ServiceWorkerRegistration> registration;
RegisterServiceWorker(&registration);
base::Time timestamp = base::Time::Now() + base::TimeDelta::FromSeconds(10);
blink::PlatformNotificationData scheduled_notification_data;
scheduled_notification_data.tag = "tagA";
scheduled_notification_data.show_trigger_timestamp = timestamp;
blink::NotificationResources resources;
resources.notification_icon = CreateBitmap(10, 10, SK_ColorMAGENTA);
blink::PlatformNotificationData displayed_notification_data;
displayed_notification_data.tag = "tagB";
DisplayPersistentNotificationSync(registration->id(),
scheduled_notification_data, resources);
DisplayPersistentNotificationSync(registration->id(),
displayed_notification_data, resources);
// Wait for service to receive all the Display calls.
RunAllTasksUntilIdle();
auto notification_data =
GetNotificationDataFromContextSync(registration->id(), "", true);
EXPECT_EQ(2u, notification_data.size());
auto notification_a = notification_data[0].notification_data.tag == "tagA"
? notification_data[0]
: notification_data[1];
auto notification_b = notification_data[0].notification_data.tag == "tagB"
? notification_data[0]
: notification_data[1];
auto stored_resources_a =
GetNotificationResourcesFromContextSync(notification_a.notification_id);
auto stored_resources_b =
GetNotificationResourcesFromContextSync(notification_b.notification_id);
EXPECT_TRUE(stored_resources_a.has_value());
EXPECT_EQ(10, stored_resources_a.value().notification_icon.width());
EXPECT_FALSE(stored_resources_b.has_value());
}
TEST_F(BlinkNotificationServiceImplTest, NotCallingDisplayForTriggered) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(features::kNotificationTriggers);
SetPermissionStatus(blink::mojom::PermissionStatus::GRANTED);
scoped_refptr<ServiceWorkerRegistration> registration;
RegisterServiceWorker(&registration);
base::Time timestamp = base::Time::Now() + base::TimeDelta::FromSeconds(10);
blink::PlatformNotificationData scheduled_notification_data;
scheduled_notification_data.show_trigger_timestamp = timestamp;
blink::NotificationResources resources;
DisplayPersistentNotificationSync(registration->id(),
scheduled_notification_data, resources);
// Wait for service to receive all the Display calls.
RunAllTasksUntilIdle();
EXPECT_EQ(0u, GetDisplayedNotifications().size());
}
TEST_F(BlinkNotificationServiceImplTest, RejectsTriggerTimestampOverAYear) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(features::kNotificationTriggers);
ASSERT_TRUE(bad_messages_.empty());
SetPermissionStatus(blink::mojom::PermissionStatus::GRANTED);
scoped_refptr<ServiceWorkerRegistration> registration;
RegisterServiceWorker(&registration);
base::Time timestamp = base::Time::Now() +
blink::kMaxNotificationShowTriggerDelay +
base::TimeDelta::FromDays(1);
blink::PlatformNotificationData scheduled_notification_data;
scheduled_notification_data.show_trigger_timestamp = timestamp;
blink::NotificationResources resources;
DisplayPersistentNotificationSync(registration->id(),
scheduled_notification_data, resources);
EXPECT_EQ(1u, bad_messages_.size());
EXPECT_EQ(kBadMessageInvalidNotificationTriggerTimestamp, bad_messages_[0]);
}
} // namespace content