1// Copyright 2013 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/service_worker/service_worker_dispatcher_host.h"
6
7#include "base/command_line.h"
8#include "base/files/file_path.h"
9#include "base/run_loop.h"
10#include "content/browser/browser_thread_impl.h"
11#include "content/browser/service_worker/embedded_worker_instance.h"
12#include "content/browser/service_worker/embedded_worker_registry.h"
13#include "content/browser/service_worker/embedded_worker_test_helper.h"
14#include "content/browser/service_worker/service_worker_context_core.h"
15#include "content/browser/service_worker/service_worker_context_wrapper.h"
16#include "content/common/service_worker/embedded_worker_messages.h"
17#include "content/common/service_worker/service_worker_messages.h"
18#include "content/public/common/content_switches.h"
19#include "content/public/test/test_browser_thread_bundle.h"
20#include "testing/gtest/include/gtest/gtest.h"
21
22namespace content {
23
24static const int kRenderProcessId = 1;
25
26class TestingServiceWorkerDispatcherHost : public ServiceWorkerDispatcherHost {
27 public:
28  TestingServiceWorkerDispatcherHost(
29      int process_id,
30      ServiceWorkerContextWrapper* context_wrapper,
31      EmbeddedWorkerTestHelper* helper)
32      : ServiceWorkerDispatcherHost(process_id, NULL),
33        bad_messages_received_count_(0),
34        helper_(helper) {
35    Init(context_wrapper);
36  }
37
38  virtual bool Send(IPC::Message* message) OVERRIDE {
39    return helper_->Send(message);
40  }
41
42  IPC::TestSink* ipc_sink() { return helper_->ipc_sink(); }
43
44  virtual void BadMessageReceived() OVERRIDE {
45    ++bad_messages_received_count_;
46  }
47
48  int bad_messages_received_count_;
49
50 protected:
51  EmbeddedWorkerTestHelper* helper_;
52  virtual ~TestingServiceWorkerDispatcherHost() {}
53};
54
55class ServiceWorkerDispatcherHostTest : public testing::Test {
56 protected:
57  ServiceWorkerDispatcherHostTest()
58      : browser_thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP) {}
59
60  virtual void SetUp() {
61    helper_.reset(new EmbeddedWorkerTestHelper(kRenderProcessId));
62    dispatcher_host_ = new TestingServiceWorkerDispatcherHost(
63        kRenderProcessId, context_wrapper(), helper_.get());
64  }
65
66  virtual void TearDown() {
67    helper_.reset();
68  }
69
70  ServiceWorkerContextCore* context() { return helper_->context(); }
71  ServiceWorkerContextWrapper* context_wrapper() {
72    return helper_->context_wrapper();
73  }
74
75  void SendRegister(int64 provider_id, GURL pattern, GURL worker_url) {
76    dispatcher_host_->OnMessageReceived(
77        ServiceWorkerHostMsg_RegisterServiceWorker(
78            -1, -1, provider_id, pattern, worker_url));
79    base::RunLoop().RunUntilIdle();
80  }
81
82  void Register(int64 provider_id,
83                GURL pattern,
84                GURL worker_url,
85                uint32 expected_message) {
86    SendRegister(provider_id, pattern, worker_url);
87    EXPECT_TRUE(dispatcher_host_->ipc_sink()->GetUniqueMessageMatching(
88        expected_message));
89    dispatcher_host_->ipc_sink()->ClearMessages();
90  }
91
92  void SendUnregister(int64 provider_id, GURL pattern) {
93    dispatcher_host_->OnMessageReceived(
94        ServiceWorkerHostMsg_UnregisterServiceWorker(
95            -1, -1, provider_id, pattern));
96    base::RunLoop().RunUntilIdle();
97  }
98
99  void Unregister(int64 provider_id, GURL pattern, uint32 expected_message) {
100    SendUnregister(provider_id, pattern);
101    EXPECT_TRUE(dispatcher_host_->ipc_sink()->GetUniqueMessageMatching(
102        expected_message));
103    dispatcher_host_->ipc_sink()->ClearMessages();
104  }
105
106  void SendGetRegistration(int64 provider_id, GURL document_url) {
107    dispatcher_host_->OnMessageReceived(
108        ServiceWorkerHostMsg_GetRegistration(
109            -1, -1, provider_id, document_url));
110    base::RunLoop().RunUntilIdle();
111  }
112
113  void GetRegistration(int64 provider_id,
114                       GURL document_url,
115                       uint32 expected_message) {
116    SendGetRegistration(provider_id, document_url);
117    EXPECT_TRUE(dispatcher_host_->ipc_sink()->GetUniqueMessageMatching(
118        expected_message));
119    dispatcher_host_->ipc_sink()->ClearMessages();
120  }
121
122  TestBrowserThreadBundle browser_thread_bundle_;
123  scoped_ptr<EmbeddedWorkerTestHelper> helper_;
124  scoped_refptr<TestingServiceWorkerDispatcherHost> dispatcher_host_;
125};
126
127TEST_F(ServiceWorkerDispatcherHostTest, Register_SameOrigin) {
128  const int64 kProviderId = 99;  // Dummy value
129  scoped_ptr<ServiceWorkerProviderHost> host(new ServiceWorkerProviderHost(
130      kRenderProcessId, kProviderId, context()->AsWeakPtr(), NULL));
131  host->SetDocumentUrl(GURL("https://www.example.com/foo"));
132  base::WeakPtr<ServiceWorkerProviderHost> provider_host = host->AsWeakPtr();
133  context()->AddProviderHost(host.Pass());
134
135  Register(kProviderId,
136           GURL("https://www.example.com/"),
137           GURL("https://www.example.com/bar"),
138           ServiceWorkerMsg_ServiceWorkerRegistered::ID);
139}
140
141TEST_F(ServiceWorkerDispatcherHostTest, Register_CrossOrigin) {
142  const int64 kProviderId = 99;  // Dummy value
143  scoped_ptr<ServiceWorkerProviderHost> host(new ServiceWorkerProviderHost(
144      kRenderProcessId, kProviderId, context()->AsWeakPtr(), NULL));
145  host->SetDocumentUrl(GURL("https://www.example.com/foo"));
146  base::WeakPtr<ServiceWorkerProviderHost> provider_host = host->AsWeakPtr();
147  context()->AddProviderHost(host.Pass());
148
149  // Script has a different host
150  SendRegister(kProviderId,
151               GURL("https://www.example.com/"),
152               GURL("https://foo.example.com/bar"));
153  EXPECT_EQ(1, dispatcher_host_->bad_messages_received_count_);
154
155  // Scope has a different host
156  SendRegister(kProviderId,
157               GURL("https://foo.example.com/"),
158               GURL("https://www.example.com/bar"));
159  EXPECT_EQ(2, dispatcher_host_->bad_messages_received_count_);
160
161  // Script has a different port
162  SendRegister(kProviderId,
163               GURL("https://www.example.com/"),
164               GURL("https://www.example.com:8080/bar"));
165  EXPECT_EQ(3, dispatcher_host_->bad_messages_received_count_);
166
167  // Scope has a different transport
168  SendRegister(kProviderId,
169               GURL("wss://www.example.com/"),
170               GURL("https://www.example.com/bar"));
171  EXPECT_EQ(4, dispatcher_host_->bad_messages_received_count_);
172
173  // Script and scope have different hosts
174  SendRegister(kProviderId,
175               GURL("https://foo.example.com/"),
176               GURL("https://foo.example.com/bar"));
177  EXPECT_EQ(5, dispatcher_host_->bad_messages_received_count_);
178
179  // Script and scope URLs are invalid
180  SendRegister(kProviderId,
181               GURL(),
182               GURL("h@ttps://@"));
183  EXPECT_EQ(6, dispatcher_host_->bad_messages_received_count_);
184}
185
186TEST_F(ServiceWorkerDispatcherHostTest, Unregister_SameOrigin) {
187  const int64 kProviderId = 99;  // Dummy value
188  scoped_ptr<ServiceWorkerProviderHost> host(new ServiceWorkerProviderHost(
189      kRenderProcessId, kProviderId, context()->AsWeakPtr(), NULL));
190  host->SetDocumentUrl(GURL("http://www.example.com/foo"));
191  base::WeakPtr<ServiceWorkerProviderHost> provider_host = host->AsWeakPtr();
192  context()->AddProviderHost(host.Pass());
193
194  Unregister(kProviderId,
195             GURL("http://www.example.com/"),
196             ServiceWorkerMsg_ServiceWorkerUnregistered::ID);
197}
198
199TEST_F(ServiceWorkerDispatcherHostTest, Unregister_CrossOrigin) {
200  const int64 kProviderId = 99;  // Dummy value
201  scoped_ptr<ServiceWorkerProviderHost> host(new ServiceWorkerProviderHost(
202      kRenderProcessId, kProviderId, context()->AsWeakPtr(), NULL));
203  host->SetDocumentUrl(GURL("http://www.example.com/foo"));
204  base::WeakPtr<ServiceWorkerProviderHost> provider_host = host->AsWeakPtr();
205  context()->AddProviderHost(host.Pass());
206
207  SendUnregister(kProviderId, GURL("http://foo.example.com/"));
208  EXPECT_EQ(1, dispatcher_host_->bad_messages_received_count_);
209}
210
211TEST_F(ServiceWorkerDispatcherHostTest, EarlyContextDeletion) {
212  helper_->ShutdownContext();
213
214  // Let the shutdown reach the simulated IO thread.
215  base::RunLoop().RunUntilIdle();
216
217  Register(-1,
218           GURL(),
219           GURL(),
220           ServiceWorkerMsg_ServiceWorkerRegistrationError::ID);
221}
222
223TEST_F(ServiceWorkerDispatcherHostTest, ProviderCreatedAndDestroyed) {
224  const int kProviderId = 1001;  // Test with a value != kRenderProcessId.
225
226  dispatcher_host_->OnMessageReceived(
227      ServiceWorkerHostMsg_ProviderCreated(kProviderId));
228  EXPECT_TRUE(context()->GetProviderHost(kRenderProcessId, kProviderId));
229
230  // Two with the same ID should be seen as a bad message.
231  dispatcher_host_->OnMessageReceived(
232      ServiceWorkerHostMsg_ProviderCreated(kProviderId));
233  EXPECT_EQ(1, dispatcher_host_->bad_messages_received_count_);
234
235  dispatcher_host_->OnMessageReceived(
236      ServiceWorkerHostMsg_ProviderDestroyed(kProviderId));
237  EXPECT_FALSE(context()->GetProviderHost(kRenderProcessId, kProviderId));
238
239  // Destroying an ID that does not exist warrants a bad message.
240  dispatcher_host_->OnMessageReceived(
241      ServiceWorkerHostMsg_ProviderDestroyed(kProviderId));
242  EXPECT_EQ(2, dispatcher_host_->bad_messages_received_count_);
243
244  // Deletion of the dispatcher_host should cause providers for that
245  // process to get deleted as well.
246  dispatcher_host_->OnMessageReceived(
247      ServiceWorkerHostMsg_ProviderCreated(kProviderId));
248  EXPECT_TRUE(context()->GetProviderHost(kRenderProcessId, kProviderId));
249  EXPECT_TRUE(dispatcher_host_->HasOneRef());
250  dispatcher_host_ = NULL;
251  EXPECT_FALSE(context()->GetProviderHost(kRenderProcessId, kProviderId));
252}
253
254TEST_F(ServiceWorkerDispatcherHostTest, GetRegistration_SameOrigin) {
255  const int64 kProviderId = 99;  // Dummy value
256  scoped_ptr<ServiceWorkerProviderHost> host(new ServiceWorkerProviderHost(
257      kRenderProcessId, kProviderId, context()->AsWeakPtr(), NULL));
258  host->SetDocumentUrl(GURL("https://www.example.com/foo"));
259  base::WeakPtr<ServiceWorkerProviderHost> provider_host = host->AsWeakPtr();
260  context()->AddProviderHost(host.Pass());
261
262  GetRegistration(kProviderId,
263                  GURL("https://www.example.com/"),
264                  ServiceWorkerMsg_DidGetRegistration::ID);
265}
266
267TEST_F(ServiceWorkerDispatcherHostTest, GetRegistration_CrossOrigin) {
268  const int64 kProviderId = 99;  // Dummy value
269  scoped_ptr<ServiceWorkerProviderHost> host(new ServiceWorkerProviderHost(
270      kRenderProcessId, kProviderId, context()->AsWeakPtr(), NULL));
271  host->SetDocumentUrl(GURL("https://www.example.com/foo"));
272  base::WeakPtr<ServiceWorkerProviderHost> provider_host = host->AsWeakPtr();
273  context()->AddProviderHost(host.Pass());
274
275  SendGetRegistration(kProviderId, GURL("https://foo.example.com/"));
276  EXPECT_EQ(1, dispatcher_host_->bad_messages_received_count_);
277}
278
279TEST_F(ServiceWorkerDispatcherHostTest, GetRegistration_EarlyContextDeletion) {
280  helper_->ShutdownContext();
281
282  // Let the shutdown reach the simulated IO thread.
283  base::RunLoop().RunUntilIdle();
284
285  GetRegistration(-1,
286                  GURL(),
287                  ServiceWorkerMsg_ServiceWorkerGetRegistrationError::ID);
288}
289
290}  // namespace content
291