1 // Copyright 2018 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
5 #include "content/browser/worker_host/shared_worker_host.h"
6
7 #include <memory>
8 #include <string>
9 #include <utility>
10
11 #include "base/macros.h"
12 #include "base/memory/ptr_util.h"
13 #include "base/run_loop.h"
14 #include "content/browser/appcache/chrome_appcache_service.h"
15 #include "content/browser/navigation_subresource_loader_params.h"
16 #include "content/browser/service_worker/embedded_worker_test_helper.h"
17 #include "content/browser/service_worker/service_worker_main_resource_handle.h"
18 #include "content/browser/worker_host/mock_shared_worker.h"
19 #include "content/browser/worker_host/shared_worker_connector_impl.h"
20 #include "content/browser/worker_host/shared_worker_service_impl.h"
21 #include "content/public/browser/shared_worker_instance.h"
22 #include "content/public/test/browser_task_environment.h"
23 #include "content/public/test/mock_render_process_host.h"
24 #include "content/public/test/test_browser_context.h"
25 #include "content/public/test/test_utils.h"
26 #include "mojo/public/cpp/bindings/pending_associated_receiver.h"
27 #include "mojo/public/cpp/bindings/pending_associated_remote.h"
28 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
29 #include "mojo/public/cpp/test_support/test_utils.h"
30 #include "services/metrics/public/cpp/ukm_source_id.h"
31 #include "services/network/public/cpp/features.h"
32 #include "services/network/public/cpp/not_implemented_url_loader_factory.h"
33 #include "testing/gtest/include/gtest/gtest.h"
34 #include "third_party/blink/public/common/messaging/message_port_channel.h"
35 #include "third_party/blink/public/common/messaging/message_port_descriptor.h"
36 #include "url/origin.h"
37
38 using blink::MessagePortChannel;
39
40 namespace content {
41
42 namespace {
43 const ukm::SourceId kClientUkmSourceId = 12345;
44 } // namespace
45
46 class SharedWorkerHostTest : public testing::Test {
47 public:
48 const GURL kSiteUrl{"http://www.example.com/"};
49 const GURL kWorkerUrl{"http://www.example.com/w.js"};
50
SetUp()51 void SetUp() override {
52 helper_ = std::make_unique<EmbeddedWorkerTestHelper>(base::FilePath());
53 RenderProcessHostImpl::set_render_process_host_factory_for_testing(
54 &mock_render_process_host_factory_);
55 site_instance_ = SiteInstanceImpl::CreateForUrlInfo(
56 &browser_context_,
57 UrlInfo(kWorkerUrl, /*origin_requests_isolation=*/false),
58 CoopCoepCrossOriginIsolatedInfo::CreateNonIsolated());
59 RenderProcessHost* rph = site_instance_->GetProcess();
60
61 std::vector<std::unique_ptr<MockRenderProcessHost>>* processes =
62 mock_render_process_host_factory_.GetProcesses();
63 ASSERT_EQ(processes->size(), 1u);
64 mock_render_process_host_ = processes->at(0).get();
65 ASSERT_EQ(rph, mock_render_process_host_);
66 ASSERT_TRUE(mock_render_process_host_->Init());
67 }
68
SharedWorkerHostTest()69 SharedWorkerHostTest()
70 : service_(nullptr /* storage_partition */,
71 nullptr /* service_worker_context */,
72 nullptr /* appcache_service */) {}
73
CreateHost()74 base::WeakPtr<SharedWorkerHost> CreateHost() {
75 SharedWorkerInstance instance(
76 kWorkerUrl, blink::mojom::ScriptType::kClassic,
77 network::mojom::CredentialsMode::kSameOrigin, "name",
78 url::Origin::Create(kWorkerUrl), /*content_security_policy=*/"",
79 network::mojom::ContentSecurityPolicyType::kReport,
80 network::mojom::IPAddressSpace::kPublic,
81 blink::mojom::SharedWorkerCreationContextType::kSecure);
82 auto host =
83 std::make_unique<SharedWorkerHost>(&service_, instance, site_instance_);
84 auto weak_host = host->AsWeakPtr();
85 service_.worker_hosts_.insert(std::move(host));
86 return weak_host;
87 }
88
StartWorker(SharedWorkerHost * host,mojo::PendingRemote<blink::mojom::SharedWorkerFactory> factory)89 void StartWorker(
90 SharedWorkerHost* host,
91 mojo::PendingRemote<blink::mojom::SharedWorkerFactory> factory) {
92 auto main_script_load_params =
93 blink::mojom::WorkerMainScriptLoadParams::New();
94 main_script_load_params->response_head =
95 network::mojom::URLResponseHead::New();
96 mojo::ScopedDataPipeProducerHandle producer_handle;
97 mojo::ScopedDataPipeConsumerHandle consumer_handle;
98 MojoResult rv =
99 mojo::CreateDataPipe(nullptr, &producer_handle, &consumer_handle);
100 ASSERT_EQ(MOJO_RESULT_OK, rv);
101 main_script_load_params->response_body = std::move(consumer_handle);
102 auto subresource_loader_factories =
103 std::make_unique<blink::PendingURLLoaderFactoryBundle>();
104
105 base::Optional<SubresourceLoaderParams> subresource_loader_params =
106 SubresourceLoaderParams();
107 mojo::PendingRemote<network::mojom::URLLoaderFactory> loader_factory_remote;
108 mojo::MakeSelfOwnedReceiver(
109 std::make_unique<network::NotImplementedURLLoaderFactory>(),
110 loader_factory_remote.InitWithNewPipeAndPassReceiver());
111 subresource_loader_params->pending_appcache_loader_factory =
112 std::move(loader_factory_remote);
113
114 // Set up for service worker.
115 auto service_worker_handle =
116 std::make_unique<ServiceWorkerMainResourceHandle>(
117 helper_->context_wrapper(), base::DoNothing());
118 mojo::PendingAssociatedRemote<blink::mojom::ServiceWorkerContainer>
119 client_remote;
120 mojo::PendingAssociatedReceiver<blink::mojom::ServiceWorkerContainerHost>
121 host_receiver;
122 auto container_info =
123 blink::mojom::ServiceWorkerContainerInfoForClient::New();
124 container_info->client_receiver =
125 client_remote.InitWithNewEndpointAndPassReceiver();
126 host_receiver =
127 container_info->host_remote.InitWithNewEndpointAndPassReceiver();
128
129 helper_->context()->CreateContainerHostForWorker(
130 std::move(host_receiver), mock_render_process_host_->GetID(),
131 std::move(client_remote), ServiceWorkerClientInfo(host->token()));
132 service_worker_handle->OnCreatedContainerHost(std::move(container_info));
133 host->SetServiceWorkerHandle(std::move(service_worker_handle));
134
135 host->Start(std::move(factory), std::move(main_script_load_params),
136 std::move(subresource_loader_factories),
137 nullptr /* controller */,
138 nullptr /* controller_service_worker_object_host */,
139 blink::mojom::FetchClientSettingsObject::New(
140 network::mojom::ReferrerPolicy::kDefault,
141 GURL() /* outgoing_referrer */,
142 blink::mojom::InsecureRequestsPolicy::kDoNotUpgrade),
143 GURL() /* final_response_url */);
144 }
145
AddClient(SharedWorkerHost * host,mojo::PendingRemote<blink::mojom::SharedWorkerClient> client)146 MessagePortChannel AddClient(
147 SharedWorkerHost* host,
148 mojo::PendingRemote<blink::mojom::SharedWorkerClient> client) {
149 GlobalFrameRoutingId dummy_render_frame_host_id(
150 mock_render_process_host_->GetID(), 22);
151
152 blink::MessagePortDescriptorPair port_pair;
153 MessagePortChannel local_port(port_pair.TakePort0());
154 MessagePortChannel remote_port(port_pair.TakePort1());
155 host->AddClient(std::move(client), dummy_render_frame_host_id,
156 std::move(remote_port), kClientUkmSourceId);
157 return local_port;
158 }
159
160 protected:
161 BrowserTaskEnvironment task_environment_;
162 TestBrowserContext browser_context_;
163 MockRenderProcessHostFactory mock_render_process_host_factory_;
164 MockRenderProcessHost* mock_render_process_host_;
165
166 std::unique_ptr<EmbeddedWorkerTestHelper> helper_;
167 scoped_refptr<SiteInstanceImpl> site_instance_;
168
169 SharedWorkerServiceImpl service_;
170
171 DISALLOW_COPY_AND_ASSIGN(SharedWorkerHostTest);
172 };
173
TEST_F(SharedWorkerHostTest,Normal)174 TEST_F(SharedWorkerHostTest, Normal) {
175 base::WeakPtr<SharedWorkerHost> host = CreateHost();
176
177 // Start the worker.
178 mojo::PendingRemote<blink::mojom::SharedWorkerFactory> factory;
179 MockSharedWorkerFactory factory_impl(
180 factory.InitWithNewPipeAndPassReceiver());
181 StartWorker(host.get(), std::move(factory));
182
183 // Add the initiating client.
184 MockSharedWorkerClient client;
185 mojo::PendingRemote<blink::mojom::SharedWorkerClient> remote_client;
186 client.Bind(remote_client.InitWithNewPipeAndPassReceiver());
187 MessagePortChannel local_port =
188 AddClient(host.get(), std::move(remote_client));
189 base::RunLoop().RunUntilIdle();
190
191 // The factory should have gotten the CreateSharedWorker message.
192 mojo::Remote<blink::mojom::SharedWorkerHost> worker_host;
193 mojo::PendingReceiver<blink::mojom::SharedWorker> worker_receiver;
194 EXPECT_TRUE(factory_impl.CheckReceivedCreateSharedWorker(
195 host->instance().url(), host->instance().name(),
196 host->instance().content_security_policy_type(), &worker_host,
197 &worker_receiver));
198 {
199 MockSharedWorker worker(std::move(worker_receiver));
200 base::RunLoop().RunUntilIdle();
201
202 // The worker and client should have gotten initial messages.
203 int connection_request_id;
204 MessagePortChannel port;
205 EXPECT_TRUE(worker.CheckReceivedConnect(&connection_request_id, &port));
206 EXPECT_TRUE(client.CheckReceivedOnCreated());
207 // Simulate events the shared worker would send.
208
209 // Create message pipes. We may need to keep |devtools_agent_receiver| and
210 // |devtools_agent_host_remote| if we want not to invoke connection error
211 // handlers.
212 mojo::PendingRemote<blink::mojom::DevToolsAgent> devtools_agent_remote;
213 mojo::PendingReceiver<blink::mojom::DevToolsAgent> devtools_agent_receiver =
214 devtools_agent_remote.InitWithNewPipeAndPassReceiver();
215 mojo::PendingRemote<blink::mojom::DevToolsAgentHost>
216 devtools_agent_host_remote;
217 worker_host->OnReadyForInspection(
218 std::move(devtools_agent_remote),
219 devtools_agent_host_remote.InitWithNewPipeAndPassReceiver());
220
221 worker_host->OnConnected(connection_request_id);
222 base::RunLoop().RunUntilIdle();
223
224 // The client should be connected.
225 EXPECT_TRUE(
226 client.CheckReceivedOnConnected({} /* expected_used_features */));
227
228 // Close the client. The host should detect that there are no clients left
229 // and ask the worker to terminate.
230 client.Close();
231 base::RunLoop().RunUntilIdle();
232 EXPECT_TRUE(worker.CheckReceivedTerminate());
233
234 // Simulate the worker terminating by breaking the Mojo connection when
235 // |worker| goes out of scope.
236 }
237 base::RunLoop().RunUntilIdle();
238
239 // The host should have self-destructed.
240 EXPECT_FALSE(host);
241 }
242
TEST_F(SharedWorkerHostTest,TerminateBeforeStarting)243 TEST_F(SharedWorkerHostTest, TerminateBeforeStarting) {
244 base::WeakPtr<SharedWorkerHost> host = CreateHost();
245
246 // Add a client.
247 MockSharedWorkerClient client;
248 mojo::PendingRemote<blink::mojom::SharedWorkerClient> remote_client;
249 client.Bind(remote_client.InitWithNewPipeAndPassReceiver());
250 MessagePortChannel local_port =
251 AddClient(host.get(), std::move(remote_client));
252 base::RunLoop().RunUntilIdle();
253
254 // Terminate the host before starting.
255 service_.DestroyHost(host.get());
256 base::RunLoop().RunUntilIdle();
257 EXPECT_FALSE(host);
258
259 // The client should be told startup failed.
260 EXPECT_TRUE(client.CheckReceivedOnScriptLoadFailed());
261 }
262
TEST_F(SharedWorkerHostTest,TerminateAfterStarting)263 TEST_F(SharedWorkerHostTest, TerminateAfterStarting) {
264 base::WeakPtr<SharedWorkerHost> host = CreateHost();
265
266 // Create the factory.
267 mojo::PendingRemote<blink::mojom::SharedWorkerFactory> factory;
268 MockSharedWorkerFactory factory_impl(
269 factory.InitWithNewPipeAndPassReceiver());
270 // Start the worker.
271 StartWorker(host.get(), std::move(factory));
272
273 // Add a client.
274 MockSharedWorkerClient client;
275 mojo::PendingRemote<blink::mojom::SharedWorkerClient> remote_client;
276 client.Bind(remote_client.InitWithNewPipeAndPassReceiver());
277 MessagePortChannel local_port =
278 AddClient(host.get(), std::move(remote_client));
279 base::RunLoop().RunUntilIdle();
280
281 {
282 mojo::Remote<blink::mojom::SharedWorkerHost> worker_host;
283 mojo::PendingReceiver<blink::mojom::SharedWorker> worker_receiver;
284 EXPECT_TRUE(factory_impl.CheckReceivedCreateSharedWorker(
285 host->instance().url(), host->instance().name(),
286 host->instance().content_security_policy_type(), &worker_host,
287 &worker_receiver));
288 MockSharedWorker worker(std::move(worker_receiver));
289
290 // Terminate after starting.
291 service_.DestroyHost(host.get());
292 base::RunLoop().RunUntilIdle();
293
294 EXPECT_TRUE(worker.CheckReceivedTerminate());
295 // We simulate the worker terminating by breaking the Mojo connection when
296 // it goes out of scope.
297 }
298
299 // Simulate the worker in the renderer terminating.
300 base::RunLoop().RunUntilIdle();
301
302 // The client should not have been told startup failed.
303 EXPECT_FALSE(client.CheckReceivedOnScriptLoadFailed());
304 // The host should no longer exist.
305 EXPECT_FALSE(host);
306 }
307
TEST_F(SharedWorkerHostTest,OnContextClosed)308 TEST_F(SharedWorkerHostTest, OnContextClosed) {
309 base::WeakPtr<SharedWorkerHost> host = CreateHost();
310
311 // Start the worker.
312 mojo::PendingRemote<blink::mojom::SharedWorkerFactory> factory;
313 MockSharedWorkerFactory factory_impl(
314 factory.InitWithNewPipeAndPassReceiver());
315 StartWorker(host.get(), std::move(factory));
316
317 // Add a client.
318 MockSharedWorkerClient client;
319 mojo::PendingRemote<blink::mojom::SharedWorkerClient> remote_client;
320 client.Bind(remote_client.InitWithNewPipeAndPassReceiver());
321 MessagePortChannel local_port =
322 AddClient(host.get(), std::move(remote_client));
323 base::RunLoop().RunUntilIdle();
324
325 {
326 mojo::Remote<blink::mojom::SharedWorkerHost> worker_host;
327 mojo::PendingReceiver<blink::mojom::SharedWorker> worker_receiver;
328 EXPECT_TRUE(factory_impl.CheckReceivedCreateSharedWorker(
329 host->instance().url(), host->instance().name(),
330 host->instance().content_security_policy_type(), &worker_host,
331 &worker_receiver));
332 MockSharedWorker worker(std::move(worker_receiver));
333
334 // Simulate the worker calling OnContextClosed().
335 worker_host->OnContextClosed();
336 base::RunLoop().RunUntilIdle();
337
338 // Close the client. The host should detect that there are no clients left
339 // and terminate the worker.
340 client.Close();
341 base::RunLoop().RunUntilIdle();
342 EXPECT_TRUE(worker.CheckReceivedTerminate());
343
344 // Simulate the worker terminating by breaking the Mojo connection when
345 // |worker| goes out of scope.
346 }
347 base::RunLoop().RunUntilIdle();
348
349 // The host should no longer exist.
350 EXPECT_FALSE(host);
351 }
352
353 } // namespace content
354