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