blob: 201c755c9fdaa1f0976cb5da4d4f094b34b3735f [file] [log] [blame]
cfredric04985842020-08-08 01:44:471// Copyright 2020 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.
4
Alex Turnerd6e0c302020-10-22 15:22:275#include <algorithm>
6
cfredric04985842020-08-08 01:44:477#include "base/bind.h"
8#include "build/build_config.h"
9#include "chrome/browser/ui/browser.h"
Alex Turnerd6e0c302020-10-22 15:22:2710#include "chrome/common/privacy_budget/scoped_privacy_budget_config.h"
cfredric04985842020-08-08 01:44:4711#include "chrome/test/base/chrome_test_utils.h"
12#include "components/ukm/test_ukm_recorder.h"
13#include "content/public/browser/worker_type.h"
14#include "content/public/test/browser_test.h"
15#include "content/public/test/browser_test_utils.h"
16#include "net/test/embedded_test_server/http_request.h"
17#include "net/test/embedded_test_server/http_response.h"
18#include "services/metrics/public/cpp/ukm_builders.h"
19#include "testing/gtest/include/gtest/gtest.h"
20
21#if defined(OS_ANDROID)
22#include "chrome/test/base/android/android_browser_test.h"
23#else
24#include "chrome/test/base/in_process_browser_test.h"
25#endif
26
27namespace {
28
29class UkmWorkerBrowserTest : public PlatformBrowserTest {
30 public:
Alex Turnerd6e0c302020-10-22 15:22:2731 UkmWorkerBrowserTest() {
32 privacy_budget_config_.Apply(test::ScopedPrivacyBudgetConfig::Parameters());
33 }
34
cfredric04985842020-08-08 01:44:4735 void SetUpOnMainThread() override {
36 test_ukm_recorder_ = std::make_unique<ukm::TestAutoSetUkmRecorder>();
37 }
38
39 content::WebContents* web_contents() {
40 return chrome_test_utils::GetActiveWebContents(this);
41 }
42
43 const ukm::TestAutoSetUkmRecorder& test_ukm_recorder() const {
44 return *test_ukm_recorder_;
45 }
46
47 private:
Alex Turnerd6e0c302020-10-22 15:22:2748 test::ScopedPrivacyBudgetConfig privacy_budget_config_;
cfredric04985842020-08-08 01:44:4749 std::unique_ptr<ukm::TestAutoSetUkmRecorder> test_ukm_recorder_;
50};
51
52} // namespace
53
Alex Turnerd6e0c302020-10-22 15:22:2754IN_PROC_BROWSER_TEST_F(UkmWorkerBrowserTest,
55 SharedWorker_DocumentClientIdIsPlumbed) {
cfredric04985842020-08-08 01:44:4756 using DocumentCreatedEntry = ukm::builders::DocumentCreated;
57 using AddedEntry = ukm::builders::Worker_ClientAdded;
58
59 embedded_test_server()->RegisterRequestHandler(base::BindRepeating(
60 [](const net::test_server::HttpRequest& request)
61 -> std::unique_ptr<net::test_server::HttpResponse> {
Alex Turnerd6e0c302020-10-22 15:22:2762 if (request.GetURL().path() != "/shared_worker_script")
cfredric04985842020-08-08 01:44:4763 return nullptr;
64 auto response = std::make_unique<net::test_server::BasicHttpResponse>();
65 response->set_content_type("text/javascript");
66 response->set_content(
67 R"(self.onconnect = e => { e.ports[0].postMessage('DONE'); };)");
68 return response;
69 }));
70 ASSERT_TRUE(embedded_test_server()->Start());
71 content::DOMMessageQueue messages;
72
73 ASSERT_TRUE(content::NavigateToURL(
Alex Turnerd6e0c302020-10-22 15:22:2774 web_contents(), embedded_test_server()->GetURL(
75 "/workers/create_shared_worker.html?worker_url=/"
76 "shared_worker_script")));
cfredric04985842020-08-08 01:44:4777
78 // Wait until the worker script is loaded and executed, to ensure the UKM is
79 // logged.
80 EXPECT_EQ("DONE", content::EvalJs(
81 browser()->tab_strip_model()->GetActiveWebContents(),
82 "waitForMessage();"));
83
84 std::vector<const ukm::mojom::UkmEntry*> doc_created_entries =
85 test_ukm_recorder().GetEntriesByName(DocumentCreatedEntry::kEntryName);
86 EXPECT_EQ(1u, doc_created_entries.size());
87 const ukm::SourceId document_source_id = doc_created_entries[0]->source_id;
88
89 // Check that we got the WorkerClientConnected event.
90 std::vector<const ukm::mojom::UkmEntry*> connected_entries =
91 test_ukm_recorder().GetEntriesByName(AddedEntry::kEntryName);
92 EXPECT_EQ(1u, connected_entries.size());
93 const ukm::SourceId client_source_id = *test_ukm_recorder().GetEntryMetric(
94 connected_entries[0], AddedEntry::kClientSourceIdName);
95 const ukm::SourceId worker_source_id = connected_entries[0]->source_id;
96 const int64_t worker_type = *test_ukm_recorder().GetEntryMetric(
97 connected_entries[0], AddedEntry::kWorkerTypeName);
98
99 // Check that we have two source IDs in play (namely that of the
100 // client/document, and the SharedWorker) and that they are different.
101 EXPECT_EQ(document_source_id, client_source_id);
102 EXPECT_NE(worker_source_id, client_source_id);
103
104 EXPECT_EQ(static_cast<int64_t>(WorkerType::kSharedWorker), worker_type);
105}
Alex Turnerd6e0c302020-10-22 15:22:27106
107IN_PROC_BROWSER_TEST_F(UkmWorkerBrowserTest,
108 ServiceWorker_DocumentClientIdIsPlumbed) {
109 using DocumentCreatedEntry = ukm::builders::DocumentCreated;
110 using AddedEntry = ukm::builders::Worker_ClientAdded;
111
112 ASSERT_TRUE(embedded_test_server()->Start());
113 ASSERT_TRUE(content::NavigateToURL(
114 web_contents(), embedded_test_server()->GetURL(
115 "/service_worker/create_service_worker.html")));
116
117 // Wait until the worker script is loaded and executed, to ensure the UKM is
118 // logged.
119 EXPECT_EQ("DONE", EvalJs(web_contents(),
120 "register('fetch_event_respond_with_fetch.js');"));
121
122 std::vector<const ukm::mojom::UkmEntry*> doc_created_entries =
123 test_ukm_recorder().GetEntriesByName(DocumentCreatedEntry::kEntryName);
124 ASSERT_EQ(1u, doc_created_entries.size());
125 const ukm::SourceId document_source_id = doc_created_entries[0]->source_id;
126
127 // Check that we got the Worker.ClientAdded event.
128 std::vector<const ukm::mojom::UkmEntry*> connected_entries =
129 test_ukm_recorder().GetEntriesByName(AddedEntry::kEntryName);
130 ASSERT_EQ(1u, connected_entries.size());
131 const ukm::SourceId client_source_id = *test_ukm_recorder().GetEntryMetric(
132 connected_entries[0], AddedEntry::kClientSourceIdName);
133 const ukm::SourceId worker_source_id = connected_entries[0]->source_id;
134 const int64_t worker_type = *test_ukm_recorder().GetEntryMetric(
135 connected_entries[0], AddedEntry::kWorkerTypeName);
136
137 // Check that we have two source IDs in play (namely that of the
138 // client/document, and the ServiceWorker) and that they are different.
139 EXPECT_EQ(document_source_id, client_source_id);
140 EXPECT_NE(worker_source_id, client_source_id);
141
142 EXPECT_EQ(static_cast<int64_t>(WorkerType::kServiceWorker), worker_type);
143}
144
145IN_PROC_BROWSER_TEST_F(UkmWorkerBrowserTest,
146 ServiceWorker_DedicatedWorkerClientIdIsIgnored) {
147 ASSERT_TRUE(embedded_test_server()->Start());
148 ASSERT_TRUE(content::NavigateToURL(
149 web_contents(), embedded_test_server()->GetURL(
150 "/service_worker/create_service_worker.html")));
151 EXPECT_EQ("DONE", EvalJs(web_contents(),
152 "register('fetch_event_respond_with_fetch.js');"));
153
154 // Wait until the worker script is loaded and executed, to ensure the UKM is
155 // logged.
156 EXPECT_EQ("loaded", EvalJs(web_contents(), R"SCRIPT(
157 const worker = new Worker('../workers/dedicated_worker.js');
158 const onmessage_promise = new Promise(r => worker.onmessage = r);
159 async function waitForMessage() {
160 const message = await onmessage_promise;
161 return message.data;
162 }
163 waitForMessage();
164 )SCRIPT"));
165
166 // Check that we only have the single Worker.ClientAdded event (for the
167 // document).
168 std::vector<const ukm::mojom::UkmEntry*> connected_entries =
169 test_ukm_recorder().GetEntriesByName(
170 ukm::builders::Worker_ClientAdded::kEntryName);
171 EXPECT_EQ(1u, connected_entries.size());
172}
173
174IN_PROC_BROWSER_TEST_F(UkmWorkerBrowserTest,
175 ServiceWorker_SharedWorkerClientIdIsPlumbed) {
176 using AddedEntry = ukm::builders::Worker_ClientAdded;
177
178 embedded_test_server()->RegisterRequestHandler(base::BindRepeating(
179 [](const net::test_server::HttpRequest& request)
180 -> std::unique_ptr<net::test_server::HttpResponse> {
181 if (request.GetURL().path() != "/shared_worker_script")
182 return nullptr;
183 auto response = std::make_unique<net::test_server::BasicHttpResponse>();
184 response->set_content_type("text/javascript");
185 response->set_content(
186 R"(self.onconnect = e => { e.ports[0].postMessage('DONE'); };)");
187 return response;
188 }));
189
190 ASSERT_TRUE(embedded_test_server()->Start());
191 ASSERT_TRUE(content::NavigateToURL(
192 web_contents(), embedded_test_server()->GetURL(
193 "/service_worker/create_service_worker.html")));
194
195 // Wait for the service worker to load.
196 EXPECT_EQ("DONE", EvalJs(web_contents(),
197 "register('fetch_event_respond_with_fetch.js');"));
198
199 // Wait for the shared worker to load.
200 EXPECT_EQ("DONE", EvalJs(web_contents(), R"SCRIPT(
201 const worker = new SharedWorker('/shared_worker_script');
202 const onmessage_promise = new Promise(r => worker.port.onmessage = r);
203 async function waitForMessage() {
204 const message = await onmessage_promise;
205 return message.data;
206 }
207 waitForMessage();
208 )SCRIPT"));
209
210 // Check that we have a Worker.ClientAdded event for all three pairs:
211 // document-shared worker, document-service worker, and shared worker-service
212 // worker.
213 std::vector<const ukm::mojom::UkmEntry*> connected_entries =
214 test_ukm_recorder().GetEntriesByName(AddedEntry::kEntryName);
215 ASSERT_EQ(3u, connected_entries.size());
216
217 // Get the document and shared worker ids from the shared worker event.
218 ukm::SourceId document_source_id = ukm::kInvalidSourceId;
219 ukm::SourceId shared_worker_source_id = ukm::kInvalidSourceId;
220 int shared_worker_event_index;
221 for (int i = 0; i < 3; i++) {
222 const int64_t worker_type = *test_ukm_recorder().GetEntryMetric(
223 connected_entries[i], AddedEntry::kWorkerTypeName);
224 if (worker_type == static_cast<int64_t>(WorkerType::kSharedWorker)) {
225 EXPECT_EQ(document_source_id, ukm::kInvalidSourceId);
226 EXPECT_EQ(shared_worker_source_id, ukm::kInvalidSourceId);
227 document_source_id = *test_ukm_recorder().GetEntryMetric(
228 connected_entries[i], AddedEntry::kClientSourceIdName);
229 shared_worker_source_id = connected_entries[i]->source_id;
230 shared_worker_event_index = i;
231 }
232 }
233 ASSERT_NE(document_source_id, ukm::kInvalidSourceId);
234 ASSERT_NE(shared_worker_source_id, ukm::kInvalidSourceId);
235 EXPECT_NE(document_source_id, shared_worker_source_id);
236
237 // Remove the shared worker event to leave just the service worker events.
238 connected_entries.erase(connected_entries.begin() +
239 shared_worker_event_index);
240
241 // Check the events contain the expected information without enforcing any
242 // ordering.
243 ukm::SourceId service_worker_source_id = connected_entries[0]->source_id;
244 EXPECT_EQ(service_worker_source_id, connected_entries[1]->source_id);
245
246 EXPECT_EQ(*test_ukm_recorder().GetEntryMetric(connected_entries[0],
247 AddedEntry::kWorkerTypeName),
248 static_cast<int64_t>(WorkerType::kServiceWorker));
249 EXPECT_EQ(*test_ukm_recorder().GetEntryMetric(connected_entries[1],
250 AddedEntry::kWorkerTypeName),
251 static_cast<int64_t>(WorkerType::kServiceWorker));
252
253 ukm::SourceId client_source_id_1 = *test_ukm_recorder().GetEntryMetric(
254 connected_entries[0], AddedEntry::kClientSourceIdName);
255 ukm::SourceId client_source_id_2 = *test_ukm_recorder().GetEntryMetric(
256 connected_entries[1], AddedEntry::kClientSourceIdName);
257
258 EXPECT_EQ(
259 std::set<ukm::SourceId>({document_source_id, shared_worker_source_id}),
260 std::set<ukm::SourceId>({client_source_id_1, client_source_id_2}));
261}