1 // Copyright 2020 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
7 #include "base/functional/bind.h"
8 #include "build/build_config.h"
9 #include "chrome/browser/ui/browser.h"
10 #include "chrome/common/privacy_budget/scoped_privacy_budget_config.h"
11 #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"
21 #if BUILDFLAG(IS_ANDROID)
22 #include "chrome/test/base/android/android_browser_test.h"
24 #include "chrome/test/base/in_process_browser_test.h"
29 class UkmWorkerBrowserTest : public PlatformBrowserTest {
31 UkmWorkerBrowserTest()
32 : privacy_budget_config_(
33 test::ScopedPrivacyBudgetConfig::Presets::kEnableRandomSampling) {}
35 void SetUpOnMainThread() override {
36 test_ukm_recorder_ = std::make_unique<ukm::TestAutoSetUkmRecorder>();
39 content::WebContents* web_contents() {
40 return chrome_test_utils::GetActiveWebContents(this);
43 const ukm::TestAutoSetUkmRecorder& test_ukm_recorder() const {
44 return *test_ukm_recorder_;
48 test::ScopedPrivacyBudgetConfig privacy_budget_config_;
49 std::unique_ptr<ukm::TestAutoSetUkmRecorder> test_ukm_recorder_;
54 IN_PROC_BROWSER_TEST_F(UkmWorkerBrowserTest,
55 SharedWorker_DocumentClientIdIsPlumbed) {
56 using DocumentCreatedEntry = ukm::builders::DocumentCreated;
57 using AddedEntry = ukm::builders::Worker_ClientAdded;
59 embedded_test_server()->RegisterRequestHandler(base::BindRepeating(
60 [](const net::test_server::HttpRequest& request)
61 -> std::unique_ptr<net::test_server::HttpResponse> {
62 if (request.GetURL().path() != "/shared_worker_script")
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'); };)");
70 ASSERT_TRUE(embedded_test_server()->Start());
71 content::DOMMessageQueue messages(web_contents());
73 ASSERT_TRUE(content::NavigateToURL(
74 web_contents(), embedded_test_server()->GetURL(
75 "/workers/create_shared_worker.html?worker_url=/"
76 "shared_worker_script")));
78 // Wait until the worker script is loaded and executed, to ensure the UKM is
80 EXPECT_EQ("DONE", content::EvalJs(
81 browser()->tab_strip_model()->GetActiveWebContents(),
82 "waitForMessage();"));
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;
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);
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);
104 EXPECT_EQ(static_cast<int64_t>(WorkerType::kSharedWorker), worker_type);
107 IN_PROC_BROWSER_TEST_F(UkmWorkerBrowserTest,
108 ServiceWorker_DocumentClientIdIsPlumbed) {
109 using DocumentCreatedEntry = ukm::builders::DocumentCreated;
110 using AddedEntry = ukm::builders::Worker_ClientAdded;
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")));
117 // Wait until the worker script is loaded and executed, to ensure the UKM is
119 EXPECT_EQ("DONE", EvalJs(web_contents(),
120 "register('fetch_event_respond_with_fetch.js');"));
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;
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);
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);
142 EXPECT_EQ(static_cast<int64_t>(WorkerType::kServiceWorker), worker_type);
145 IN_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');"));
154 // Wait until the worker script is loaded and executed, to ensure the UKM is
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;
166 // Check that we only have the single Worker.ClientAdded event (for the
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());
174 IN_PROC_BROWSER_TEST_F(UkmWorkerBrowserTest,
175 ServiceWorker_SharedWorkerClientIdIsPlumbed) {
176 using AddedEntry = ukm::builders::Worker_ClientAdded;
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")
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'); };)");
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")));
195 // Wait for the service worker to load.
196 EXPECT_EQ("DONE", EvalJs(web_contents(),
197 "register('fetch_event_respond_with_fetch.js');"));
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;
210 // Check that we have a Worker.ClientAdded event for all three pairs:
211 // document-shared worker, document-service worker, and shared worker-service
213 std::vector<const ukm::mojom::UkmEntry*> connected_entries =
214 test_ukm_recorder().GetEntriesByName(AddedEntry::kEntryName);
215 ASSERT_EQ(3u, connected_entries.size());
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;
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);
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);
241 // Check the events contain the expected information without enforcing any
243 ukm::SourceId service_worker_source_id = connected_entries[0]->source_id;
244 EXPECT_EQ(service_worker_source_id, connected_entries[1]->source_id);
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));
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);
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}));