service_worker_dispatcher_host.cc revision f8ee788a64d60abd8f2d742a5fdedde054ecd910
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/logging.h"
8#include "base/strings/utf_string_conversions.h"
9#include "content/browser/message_port_message_filter.h"
10#include "content/browser/message_port_service.h"
11#include "content/browser/service_worker/embedded_worker_registry.h"
12#include "content/browser/service_worker/service_worker_context_core.h"
13#include "content/browser/service_worker/service_worker_context_wrapper.h"
14#include "content/browser/service_worker/service_worker_handle.h"
15#include "content/browser/service_worker/service_worker_registration.h"
16#include "content/browser/service_worker/service_worker_utils.h"
17#include "content/common/service_worker/embedded_worker_messages.h"
18#include "content/common/service_worker/service_worker_messages.h"
19#include "ipc/ipc_message_macros.h"
20#include "third_party/WebKit/public/platform/WebServiceWorkerError.h"
21#include "url/gurl.h"
22
23using blink::WebServiceWorkerError;
24
25namespace content {
26
27namespace {
28
29const char kDisabledErrorMessage[] =
30    "ServiceWorker is disabled";
31const char kDomainMismatchErrorMessage[] =
32    "Scope and scripts do not have the same origin";
33
34const uint32 kFilteredMessageClasses[] = {
35  ServiceWorkerMsgStart,
36  EmbeddedWorkerMsgStart,
37};
38
39}  // namespace
40
41ServiceWorkerDispatcherHost::ServiceWorkerDispatcherHost(
42    int render_process_id,
43    MessagePortMessageFilter* message_port_message_filter)
44    : BrowserMessageFilter(kFilteredMessageClasses,
45                           arraysize(kFilteredMessageClasses)),
46      render_process_id_(render_process_id),
47      message_port_message_filter_(message_port_message_filter),
48      channel_ready_(false) {
49}
50
51ServiceWorkerDispatcherHost::~ServiceWorkerDispatcherHost() {
52  if (GetContext()) {
53    GetContext()->RemoveAllProviderHostsForProcess(render_process_id_);
54    GetContext()->embedded_worker_registry()->RemoveChildProcessSender(
55        render_process_id_);
56  }
57}
58
59void ServiceWorkerDispatcherHost::Init(
60    ServiceWorkerContextWrapper* context_wrapper) {
61  if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
62    BrowserThread::PostTask(
63        BrowserThread::IO, FROM_HERE,
64        base::Bind(&ServiceWorkerDispatcherHost::Init,
65                   this, make_scoped_refptr(context_wrapper)));
66    return;
67  }
68  context_wrapper_ = context_wrapper;
69  GetContext()->embedded_worker_registry()->AddChildProcessSender(
70      render_process_id_, this);
71}
72
73void ServiceWorkerDispatcherHost::OnFilterAdded(IPC::Sender* sender) {
74  BrowserMessageFilter::OnFilterAdded(sender);
75  channel_ready_ = true;
76  std::vector<IPC::Message*> messages;
77  pending_messages_.release(&messages);
78  for (size_t i = 0; i < messages.size(); ++i) {
79    BrowserMessageFilter::Send(messages[i]);
80  }
81}
82
83void ServiceWorkerDispatcherHost::OnDestruct() const {
84  BrowserThread::DeleteOnIOThread::Destruct(this);
85}
86
87bool ServiceWorkerDispatcherHost::OnMessageReceived(
88    const IPC::Message& message) {
89  bool handled = true;
90  IPC_BEGIN_MESSAGE_MAP(ServiceWorkerDispatcherHost, message)
91    IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_RegisterServiceWorker,
92                        OnRegisterServiceWorker)
93    IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_UnregisterServiceWorker,
94                        OnUnregisterServiceWorker)
95    IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_ProviderCreated,
96                        OnProviderCreated)
97    IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_ProviderDestroyed,
98                        OnProviderDestroyed)
99    IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_SetVersionId,
100                        OnSetHostedVersionId)
101    IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_PostMessageToWorker,
102                        OnPostMessageToWorker)
103    IPC_MESSAGE_HANDLER(EmbeddedWorkerHostMsg_WorkerScriptLoaded,
104                        OnWorkerScriptLoaded)
105    IPC_MESSAGE_HANDLER(EmbeddedWorkerHostMsg_WorkerScriptLoadFailed,
106                        OnWorkerScriptLoadFailed)
107    IPC_MESSAGE_HANDLER(EmbeddedWorkerHostMsg_WorkerStarted,
108                        OnWorkerStarted)
109    IPC_MESSAGE_HANDLER(EmbeddedWorkerHostMsg_WorkerStopped,
110                        OnWorkerStopped)
111    IPC_MESSAGE_HANDLER(EmbeddedWorkerHostMsg_ReportException,
112                        OnReportException)
113    IPC_MESSAGE_HANDLER(EmbeddedWorkerHostMsg_ReportConsoleMessage,
114                        OnReportConsoleMessage)
115    IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_IncrementServiceWorkerRefCount,
116                        OnIncrementServiceWorkerRefCount)
117    IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_DecrementServiceWorkerRefCount,
118                        OnDecrementServiceWorkerRefCount)
119    IPC_MESSAGE_UNHANDLED(handled = false)
120  IPC_END_MESSAGE_MAP()
121
122  if (!handled && GetContext()) {
123    handled =
124        GetContext()->embedded_worker_registry()->OnMessageReceived(message);
125    if (!handled)
126      BadMessageReceived();
127  }
128
129  return handled;
130}
131
132bool ServiceWorkerDispatcherHost::Send(IPC::Message* message) {
133  if (channel_ready_) {
134    BrowserMessageFilter::Send(message);
135    // Don't bother passing through Send()'s result: it's not reliable.
136    return true;
137  }
138
139  pending_messages_.push_back(message);
140  return true;
141}
142
143void ServiceWorkerDispatcherHost::RegisterServiceWorkerHandle(
144    scoped_ptr<ServiceWorkerHandle> handle) {
145  int handle_id = handle->handle_id();
146  handles_.AddWithID(handle.release(), handle_id);
147}
148
149void ServiceWorkerDispatcherHost::OnRegisterServiceWorker(
150    int thread_id,
151    int request_id,
152    int provider_id,
153    const GURL& pattern,
154    const GURL& script_url) {
155  if (!GetContext() || !ServiceWorkerUtils::IsFeatureEnabled()) {
156    Send(new ServiceWorkerMsg_ServiceWorkerRegistrationError(
157        thread_id,
158        request_id,
159        WebServiceWorkerError::ErrorTypeDisabled,
160        base::ASCIIToUTF16(kDisabledErrorMessage)));
161    return;
162  }
163
164  // TODO(alecflett): This check is insufficient for release. Add a
165  // ServiceWorker-specific policy query in
166  // ChildProcessSecurityImpl. See http://crbug.com/311631.
167  if (pattern.GetOrigin() != script_url.GetOrigin()) {
168    Send(new ServiceWorkerMsg_ServiceWorkerRegistrationError(
169        thread_id,
170        request_id,
171        WebServiceWorkerError::ErrorTypeSecurity,
172        base::ASCIIToUTF16(kDomainMismatchErrorMessage)));
173    return;
174  }
175
176  ServiceWorkerProviderHost* provider_host = GetContext()->GetProviderHost(
177      render_process_id_, provider_id);
178  if (!provider_host) {
179    BadMessageReceived();
180    return;
181  }
182  if (!provider_host->IsContextAlive()) {
183    Send(new ServiceWorkerMsg_ServiceWorkerRegistrationError(
184        thread_id,
185        request_id,
186        WebServiceWorkerError::ErrorTypeDisabled,
187        base::ASCIIToUTF16(kDisabledErrorMessage)));
188    return;
189  }
190
191  GetContext()->RegisterServiceWorker(
192      pattern,
193      script_url,
194      render_process_id_,
195      provider_host,
196      base::Bind(&ServiceWorkerDispatcherHost::RegistrationComplete,
197                 this,
198                 thread_id,
199                 request_id));
200}
201
202void ServiceWorkerDispatcherHost::OnUnregisterServiceWorker(
203    int thread_id,
204    int request_id,
205    int provider_id,
206    const GURL& pattern) {
207  // TODO(alecflett): This check is insufficient for release. Add a
208  // ServiceWorker-specific policy query in
209  // ChildProcessSecurityImpl. See http://crbug.com/311631.
210  if (!GetContext() || !ServiceWorkerUtils::IsFeatureEnabled()) {
211    Send(new ServiceWorkerMsg_ServiceWorkerRegistrationError(
212        thread_id,
213        request_id,
214        blink::WebServiceWorkerError::ErrorTypeDisabled,
215        base::ASCIIToUTF16(kDisabledErrorMessage)));
216    return;
217  }
218
219  ServiceWorkerProviderHost* provider_host = GetContext()->GetProviderHost(
220      render_process_id_, provider_id);
221  if (!provider_host) {
222    BadMessageReceived();
223    return;
224  }
225  if (!provider_host->IsContextAlive()) {
226    Send(new ServiceWorkerMsg_ServiceWorkerRegistrationError(
227        thread_id,
228        request_id,
229        blink::WebServiceWorkerError::ErrorTypeDisabled,
230        base::ASCIIToUTF16(kDisabledErrorMessage)));
231    return;
232  }
233
234  GetContext()->UnregisterServiceWorker(
235      pattern,
236      base::Bind(&ServiceWorkerDispatcherHost::UnregistrationComplete,
237                 this,
238                 thread_id,
239                 request_id));
240}
241
242void ServiceWorkerDispatcherHost::OnPostMessageToWorker(
243    int handle_id,
244    const base::string16& message,
245    const std::vector<int>& sent_message_port_ids) {
246  if (!GetContext() || !ServiceWorkerUtils::IsFeatureEnabled())
247    return;
248
249  ServiceWorkerHandle* handle = handles_.Lookup(handle_id);
250  if (!handle) {
251    BadMessageReceived();
252    return;
253  }
254
255  std::vector<int> new_routing_ids;
256  message_port_message_filter_->UpdateMessagePortsWithNewRoutes(
257      sent_message_port_ids, &new_routing_ids);
258  handle->version()->SendMessage(
259      ServiceWorkerMsg_MessageToWorker(message,
260                                       sent_message_port_ids,
261                                       new_routing_ids),
262      base::Bind(&ServiceWorkerUtils::NoOpStatusCallback));
263}
264
265void ServiceWorkerDispatcherHost::OnProviderCreated(int provider_id) {
266  if (!GetContext())
267    return;
268  if (GetContext()->GetProviderHost(render_process_id_, provider_id)) {
269    BadMessageReceived();
270    return;
271  }
272  scoped_ptr<ServiceWorkerProviderHost> provider_host(
273      new ServiceWorkerProviderHost(
274          render_process_id_, provider_id, GetContext()->AsWeakPtr(), this));
275  GetContext()->AddProviderHost(provider_host.Pass());
276}
277
278void ServiceWorkerDispatcherHost::OnProviderDestroyed(int provider_id) {
279  if (!GetContext())
280    return;
281  if (!GetContext()->GetProviderHost(render_process_id_, provider_id)) {
282    BadMessageReceived();
283    return;
284  }
285  GetContext()->RemoveProviderHost(render_process_id_, provider_id);
286}
287
288void ServiceWorkerDispatcherHost::OnSetHostedVersionId(
289    int provider_id, int64 version_id) {
290  if (!GetContext())
291    return;
292  ServiceWorkerProviderHost* provider_host =
293      GetContext()->GetProviderHost(render_process_id_, provider_id);
294  if (!provider_host) {
295    BadMessageReceived();
296    return;
297  }
298  if (!provider_host->IsContextAlive())
299    return;
300  if (!provider_host->SetHostedVersionId(version_id))
301    BadMessageReceived();
302}
303
304void ServiceWorkerDispatcherHost::RegistrationComplete(
305    int thread_id,
306    int request_id,
307    ServiceWorkerStatusCode status,
308    int64 registration_id,
309    int64 version_id) {
310  if (!GetContext())
311    return;
312
313  if (status != SERVICE_WORKER_OK) {
314    SendRegistrationError(thread_id, request_id, status);
315    return;
316  }
317
318  ServiceWorkerVersion* version = GetContext()->GetLiveVersion(version_id);
319  DCHECK(version);
320  DCHECK_EQ(registration_id, version->registration_id());
321  scoped_ptr<ServiceWorkerHandle> handle =
322      ServiceWorkerHandle::Create(GetContext()->AsWeakPtr(),
323                                  this, thread_id, version);
324  Send(new ServiceWorkerMsg_ServiceWorkerRegistered(
325      thread_id, request_id, handle->GetObjectInfo()));
326  RegisterServiceWorkerHandle(handle.Pass());
327}
328
329// TODO(nhiroki): These message handlers that take |embedded_worker_id| as an
330// input should check if the worker refers to the live context. If the context
331// was deleted, handle the messege gracefully (http://crbug.com/371675).
332void ServiceWorkerDispatcherHost::OnWorkerScriptLoaded(int embedded_worker_id) {
333  if (!GetContext())
334    return;
335  GetContext()->embedded_worker_registry()->OnWorkerScriptLoaded(
336      render_process_id_, embedded_worker_id);
337}
338
339void ServiceWorkerDispatcherHost::OnWorkerScriptLoadFailed(
340    int embedded_worker_id) {
341  if (!GetContext())
342    return;
343  GetContext()->embedded_worker_registry()->OnWorkerScriptLoadFailed(
344      render_process_id_, embedded_worker_id);
345}
346
347void ServiceWorkerDispatcherHost::OnWorkerStarted(
348    int thread_id, int embedded_worker_id) {
349  if (!GetContext())
350    return;
351  GetContext()->embedded_worker_registry()->OnWorkerStarted(
352      render_process_id_, thread_id, embedded_worker_id);
353}
354
355void ServiceWorkerDispatcherHost::OnWorkerStopped(int embedded_worker_id) {
356  if (!GetContext())
357    return;
358  GetContext()->embedded_worker_registry()->OnWorkerStopped(
359      render_process_id_, embedded_worker_id);
360}
361
362void ServiceWorkerDispatcherHost::OnReportException(
363    int embedded_worker_id,
364    const base::string16& error_message,
365    int line_number,
366    int column_number,
367    const GURL& source_url) {
368  if (!GetContext())
369    return;
370  GetContext()->embedded_worker_registry()->OnReportException(
371      embedded_worker_id,
372      error_message,
373      line_number,
374      column_number,
375      source_url);
376}
377
378void ServiceWorkerDispatcherHost::OnReportConsoleMessage(
379    int embedded_worker_id,
380    const EmbeddedWorkerHostMsg_ReportConsoleMessage_Params& params) {
381  if (!GetContext())
382    return;
383  GetContext()->embedded_worker_registry()->OnReportConsoleMessage(
384      embedded_worker_id,
385      params.source_identifier,
386      params.message_level,
387      params.message,
388      params.line_number,
389      params.source_url);
390}
391
392void ServiceWorkerDispatcherHost::OnIncrementServiceWorkerRefCount(
393    int handle_id) {
394  ServiceWorkerHandle* handle = handles_.Lookup(handle_id);
395  if (!handle) {
396    BadMessageReceived();
397    return;
398  }
399  handle->IncrementRefCount();
400}
401
402void ServiceWorkerDispatcherHost::OnDecrementServiceWorkerRefCount(
403    int handle_id) {
404  ServiceWorkerHandle* handle = handles_.Lookup(handle_id);
405  if (!handle) {
406    BadMessageReceived();
407    return;
408  }
409  handle->DecrementRefCount();
410  if (handle->HasNoRefCount())
411    handles_.Remove(handle_id);
412}
413
414void ServiceWorkerDispatcherHost::UnregistrationComplete(
415    int thread_id,
416    int request_id,
417    ServiceWorkerStatusCode status) {
418  if (status != SERVICE_WORKER_OK) {
419    SendRegistrationError(thread_id, request_id, status);
420    return;
421  }
422
423  Send(new ServiceWorkerMsg_ServiceWorkerUnregistered(thread_id, request_id));
424}
425
426void ServiceWorkerDispatcherHost::SendRegistrationError(
427    int thread_id,
428    int request_id,
429    ServiceWorkerStatusCode status) {
430  base::string16 error_message;
431  blink::WebServiceWorkerError::ErrorType error_type;
432  GetServiceWorkerRegistrationStatusResponse(
433      status, &error_type, &error_message);
434  Send(new ServiceWorkerMsg_ServiceWorkerRegistrationError(
435      thread_id, request_id, error_type, error_message));
436}
437
438ServiceWorkerContextCore* ServiceWorkerDispatcherHost::GetContext() {
439  return context_wrapper_->context();
440}
441
442}  // namespace content
443