blob: 4674eb0739408fbd5b6715d0f1bede55210284fd [file] [log] [blame]
Avi Drissman4e1b7bc32022-09-15 14:03:501// Copyright 2018 The Chromium Authors
Sharon Yangaddbe492018-08-01 18:16:432// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "content/browser/notifications/notification_storage.h"
6
Avi Drissmanadac21992023-01-11 23:46:397#include "base/functional/bind.h"
Sharon Yangaddbe492018-08-01 18:16:438#include "base/run_loop.h"
Claudio DeSouzae843ed62023-04-17 10:58:439#include "base/uuid.h"
Sharon Yangaddbe492018-08-01 18:16:4310#include "content/browser/service_worker/embedded_worker_test_helper.h"
David Sandersc7dd8a82025-07-02 16:59:4411#include "content/browser/service_worker/service_worker_context_core.h"
Sharon Yangaddbe492018-08-01 18:16:4312#include "content/browser/service_worker/service_worker_registration.h"
Gabriel Charettec7108742019-08-23 03:31:4013#include "content/public/test/browser_task_environment.h"
Sharon Yangaddbe492018-08-01 18:16:4314#include "content/public/test/test_browser_context.h"
Sharon Yangaddbe492018-08-01 18:16:4315#include "testing/gtest/include/gtest/gtest.h"
16#include "third_party/blink/public/common/service_worker/service_worker_status_code.h"
Steven Binglerfdc1ded2021-05-27 19:02:1517#include "third_party/blink/public/common/storage_key/storage_key.h"
Hans Wennborg78b52182021-06-15 13:42:1518#include "third_party/blink/public/mojom/service_worker/service_worker_registration_options.mojom.h"
Sharon Yangaddbe492018-08-01 18:16:4319#include "url/gurl.h"
20
21namespace content {
22
23class NotificationStorageTest : public ::testing::Test {
24 public:
25 NotificationStorageTest()
Gabriel Charette798fde72019-08-20 22:24:0426 : task_environment_(BrowserTaskEnvironment::IO_MAINLOOP),
Nidhi Jaju96d38092020-09-11 07:01:3027 url_(GURL("https://example.com")),
28 origin_(url::Origin::Create(url_)),
Sharon Yangaddbe492018-08-01 18:16:4329 success_(false),
30 service_worker_registration_id_(
31 blink::mojom::kInvalidServiceWorkerRegistrationId) {
32 helper_ = std::make_unique<EmbeddedWorkerTestHelper>(base::FilePath());
33 storage_ =
34 std::make_unique<NotificationStorage>(helper_->context_wrapper());
35 }
36
37 void DidRegisterServiceWorker(base::OnceClosure quit_closure,
38 blink::ServiceWorkerStatusCode status,
39 const std::string& status_message,
40 int64_t service_worker_registration_id) {
41 DCHECK(service_worker_registration_id_);
42 EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk, status) << status_message;
43
44 service_worker_registration_id_ = service_worker_registration_id;
45
46 std::move(quit_closure).Run();
47 }
48
49 void DidFindServiceWorkerRegistration(
50 scoped_refptr<ServiceWorkerRegistration>* out_service_worker_registration,
51 base::OnceClosure quit_closure,
52 blink::ServiceWorkerStatusCode status,
53 scoped_refptr<ServiceWorkerRegistration> service_worker_registration) {
54 DCHECK(out_service_worker_registration);
55 EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk, status)
56 << blink::ServiceWorkerStatusToString(status);
57
58 *out_service_worker_registration = service_worker_registration;
59
60 std::move(quit_closure).Run();
61 }
62
63 // Registers a Service Worker for the testing origin and returns its
64 // |service_worker_registration_id|. If registration failed, this will be
65 // blink::mojom::kInvalidServiceWorkerRegistrationId. The
66 // ServiceWorkerRegistration will be kept alive for the test's lifetime.
67 int64_t RegisterServiceWorker() {
Nidhi Jaju96d38092020-09-11 07:01:3068 GURL script_url = url_;
Ari Chivukulac81e13e2023-02-15 20:44:5769 const blink::StorageKey key = blink::StorageKey::CreateFirstParty(origin_);
Sharon Yangaddbe492018-08-01 18:16:4370 {
71 blink::mojom::ServiceWorkerRegistrationOptions options;
Nidhi Jaju96d38092020-09-11 07:01:3072 options.scope = url_;
Sharon Yangaddbe492018-08-01 18:16:4373 base::RunLoop run_loop;
74 helper_->context()->RegisterServiceWorker(
Steven Bingler4155171d2021-05-14 17:24:5175 script_url, key, options,
76 blink::mojom::FetchClientSettingsObject::New(),
Sharon Yangaddbe492018-08-01 18:16:4377 base::BindOnce(&NotificationStorageTest::DidRegisterServiceWorker,
Arthur Hemeryea98960f2021-06-04 11:07:5378 base::Unretained(this), run_loop.QuitClosure()),
Jonathan Haof6cd7692022-08-26 09:18:2179 /*requesting_frame_id=*/GlobalRenderFrameHostId(),
80 PolicyContainerPolicies());
Sharon Yangaddbe492018-08-01 18:16:4381 run_loop.Run();
82 }
83
84 if (service_worker_registration_id_ ==
85 blink::mojom::kInvalidServiceWorkerRegistrationId) {
86 ADD_FAILURE() << "Could not obtain a valid Service Worker registration";
87 return blink::mojom::kInvalidServiceWorkerRegistrationId;
88 }
89
90 scoped_refptr<ServiceWorkerRegistration> service_worker_registration;
91
92 {
93 base::RunLoop run_loop;
Minoru Chikamune2873ad42025-06-06 03:45:2794 helper_->context()->registry().FindRegistrationForId(
Steven Bingler4155171d2021-05-14 17:24:5195 service_worker_registration_id_, key,
Sharon Yangaddbe492018-08-01 18:16:4396 base::BindOnce(
97 &NotificationStorageTest::DidFindServiceWorkerRegistration,
98 base::Unretained(this), &service_worker_registration,
99 run_loop.QuitClosure()));
100 run_loop.Run();
101 }
102
103 // Wait for the worker to be activated.
Gabriel Charette798fde72019-08-20 22:24:04104 task_environment_.RunUntilIdle();
Sharon Yangaddbe492018-08-01 18:16:43105
106 if (!service_worker_registration) {
107 ADD_FAILURE() << "Could not find the new Service Worker registration.";
108 return blink::mojom::kInvalidServiceWorkerRegistrationId;
109 }
110
111 service_worker_registrations_.push_back(
112 std::move(service_worker_registration));
113
114 return service_worker_registration_id_;
115 }
116
117 void DidWriteNotificationDataSynchronous(base::OnceClosure quit_closure,
118 bool success,
119 const std::string& notification_id) {
120 success_ = success;
121 notification_id_ = notification_id;
122 std::move(quit_closure).Run();
123 }
124
125 void WriteNotificationDataSynchronous(const NotificationDatabaseData& data) {
126 base::RunLoop run_loop;
127 storage_->WriteNotificationData(
danakjf4b9e942019-11-29 15:43:04128 data, base::BindOnce(
Sharon Yangaddbe492018-08-01 18:16:43129 &NotificationStorageTest::DidWriteNotificationDataSynchronous,
130 base::Unretained(this), run_loop.QuitClosure()));
131 run_loop.Run();
132 }
133
134 void DidReadNotificationDataAndRecordInteractionSynchronous(
135 base::OnceClosure quit_closure,
136 bool success,
137 const NotificationDatabaseData& data) {
138 success_ = success;
139 out_data_ = data;
140 std::move(quit_closure).Run();
141 }
142
143 NotificationDatabaseData ReadNotificationDataAndRecordInteractionSynchronous(
144 int64_t service_worker_registration_id,
145 const std::string& notification_id,
146 PlatformNotificationContext::Interaction interaction) {
147 base::RunLoop run_loop;
148 storage_->ReadNotificationDataAndRecordInteraction(
149 service_worker_registration_id, notification_id, interaction,
danakjf4b9e942019-11-29 15:43:04150 base::BindOnce(
151 &NotificationStorageTest::
152 DidReadNotificationDataAndRecordInteractionSynchronous,
153 base::Unretained(this), run_loop.QuitClosure()));
Sharon Yangaddbe492018-08-01 18:16:43154 run_loop.Run();
155 return out_data_;
156 }
157
158 // Generates a random notification ID. The format of the ID is opaque.
Claudio DeSouzae843ed62023-04-17 10:58:43159 std::string GenerateNotificationId() {
160 return base::Uuid::GenerateRandomV4().AsLowercaseString();
161 }
Sharon Yangaddbe492018-08-01 18:16:43162
163 protected:
Gabriel Charette798fde72019-08-20 22:24:04164 BrowserTaskEnvironment task_environment_; // Must be first member
Sharon Yangaddbe492018-08-01 18:16:43165 std::unique_ptr<EmbeddedWorkerTestHelper> helper_;
Nidhi Jaju96d38092020-09-11 07:01:30166 GURL url_;
167 url::Origin origin_;
Sharon Yangaddbe492018-08-01 18:16:43168 TestBrowserContext browser_context_;
169 bool success_;
170 int64_t service_worker_registration_id_;
171 NotificationDatabaseData out_data_;
172
173 private:
174 std::unique_ptr<NotificationStorage> storage_;
175 std::string notification_id_;
176
177 // Vector of ServiceWorkerRegistration instances that have to be kept alive
178 // for the lifetime of this test.
179 std::vector<scoped_refptr<ServiceWorkerRegistration>>
180 service_worker_registrations_;
181};
182
183TEST_F(NotificationStorageTest, WriteReadNotification) {
184 NotificationDatabaseData data;
185 data.notification_id = GenerateNotificationId();
Nidhi Jaju96d38092020-09-11 07:01:30186 data.origin = url_;
Sharon Yangaddbe492018-08-01 18:16:43187 data.service_worker_registration_id = RegisterServiceWorker();
188 ASSERT_NE(blink::mojom::kInvalidServiceWorkerRegistrationId,
189 data.service_worker_registration_id);
190 WriteNotificationDataSynchronous(data);
191 ASSERT_TRUE(success_);
192
193 NotificationDatabaseData read_data =
194 ReadNotificationDataAndRecordInteractionSynchronous(
195 data.service_worker_registration_id, data.notification_id,
196 PlatformNotificationContext::Interaction::NONE);
197 ASSERT_TRUE(success_);
198 EXPECT_EQ(data.origin, read_data.origin);
199 EXPECT_EQ(data.notification_id, read_data.notification_id);
200 EXPECT_EQ(data.service_worker_registration_id,
201 read_data.service_worker_registration_id);
202}
203
204TEST_F(NotificationStorageTest, ReadInvalidNotification) {
205 int64_t service_worker_registration_id = RegisterServiceWorker();
206 ASSERT_NE(blink::mojom::kInvalidServiceWorkerRegistrationId,
207 service_worker_registration_id);
208 ReadNotificationDataAndRecordInteractionSynchronous(
209 service_worker_registration_id, "bad_id",
210 PlatformNotificationContext::Interaction::NONE);
211 ASSERT_FALSE(success_);
212}
213
214TEST_F(NotificationStorageTest, ReadAndUpdateInteraction) {
215 NotificationDatabaseData data, read_data;
216 data.notification_id = GenerateNotificationId();
Nidhi Jaju96d38092020-09-11 07:01:30217 data.origin = url_;
Sharon Yangaddbe492018-08-01 18:16:43218 data.service_worker_registration_id = RegisterServiceWorker();
219 ASSERT_NE(blink::mojom::kInvalidServiceWorkerRegistrationId,
220 data.service_worker_registration_id);
221
222 WriteNotificationDataSynchronous(data);
223 ASSERT_TRUE(success_);
224
225 // Check that the time deltas have not yet been set.
226 EXPECT_FALSE(read_data.time_until_first_click_millis.has_value());
227 EXPECT_FALSE(read_data.time_until_last_click_millis.has_value());
228 EXPECT_FALSE(read_data.time_until_close_millis.has_value());
229
230 // Check that when a notification has an interaction, the appropriate field is
231 // updated on the read.
232 read_data = ReadNotificationDataAndRecordInteractionSynchronous(
233 data.service_worker_registration_id, data.notification_id,
234 PlatformNotificationContext::Interaction::CLICKED);
235 ASSERT_TRUE(success_);
236 EXPECT_EQ(1, read_data.num_clicks);
237
238 read_data = ReadNotificationDataAndRecordInteractionSynchronous(
239 data.service_worker_registration_id, data.notification_id,
240 PlatformNotificationContext::Interaction::ACTION_BUTTON_CLICKED);
241 ASSERT_TRUE(success_);
242 EXPECT_EQ(1, read_data.num_action_button_clicks);
243
244 read_data = ReadNotificationDataAndRecordInteractionSynchronous(
245 data.service_worker_registration_id, data.notification_id,
246 PlatformNotificationContext::Interaction::ACTION_BUTTON_CLICKED);
247 ASSERT_TRUE(success_);
248 EXPECT_EQ(2, read_data.num_action_button_clicks);
249
250 // Check that the click timestamps are correctly updated.
251 EXPECT_TRUE(read_data.time_until_first_click_millis.has_value());
252 EXPECT_TRUE(read_data.time_until_last_click_millis.has_value());
253
254 // Check that when a read with a CLOSED interaction occurs, the correct
255 // field is updated.
256 read_data = ReadNotificationDataAndRecordInteractionSynchronous(
257 data.service_worker_registration_id, data.notification_id,
258 PlatformNotificationContext::Interaction::CLOSED);
259 ASSERT_TRUE(success_);
260 EXPECT_EQ(true, read_data.time_until_close_millis.has_value());
261}
262
263} // namespace content