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/renderer/service_worker/embedded_worker_context_client.h"
6
7#include <map>
8#include <string>
9
10#include "base/debug/trace_event.h"
11#include "base/lazy_instance.h"
12#include "base/message_loop/message_loop_proxy.h"
13#include "base/pickle.h"
14#include "base/strings/string16.h"
15#include "base/strings/utf_string_conversions.h"
16#include "base/threading/thread_local.h"
17#include "content/child/request_extra_data.h"
18#include "content/child/service_worker/service_worker_network_provider.h"
19#include "content/child/thread_safe_sender.h"
20#include "content/child/worker_task_runner.h"
21#include "content/child/worker_thread_task_runner.h"
22#include "content/common/devtools_messages.h"
23#include "content/common/service_worker/embedded_worker_messages.h"
24#include "content/common/service_worker/service_worker_types.h"
25#include "content/public/renderer/document_state.h"
26#include "content/renderer/render_thread_impl.h"
27#include "content/renderer/service_worker/embedded_worker_dispatcher.h"
28#include "content/renderer/service_worker/service_worker_script_context.h"
29#include "ipc/ipc_message_macros.h"
30#include "third_party/WebKit/public/platform/WebServiceWorkerResponse.h"
31#include "third_party/WebKit/public/platform/WebString.h"
32#include "third_party/WebKit/public/web/WebDataSource.h"
33#include "third_party/WebKit/public/web/WebServiceWorkerNetworkProvider.h"
34
35namespace content {
36
37namespace {
38
39// For now client must be a per-thread instance.
40// TODO(kinuko): This needs to be refactored when we start using thread pool
41// or having multiple clients per one thread.
42base::LazyInstance<base::ThreadLocalPointer<EmbeddedWorkerContextClient> >::
43    Leaky g_worker_client_tls = LAZY_INSTANCE_INITIALIZER;
44
45void CallWorkerContextDestroyedOnMainThread(int embedded_worker_id) {
46  if (!RenderThreadImpl::current() ||
47      !RenderThreadImpl::current()->embedded_worker_dispatcher())
48    return;
49  RenderThreadImpl::current()->embedded_worker_dispatcher()->
50      WorkerContextDestroyed(embedded_worker_id);
51}
52
53// We store an instance of this class in the "extra data" of the WebDataSource
54// and attach a ServiceWorkerNetworkProvider to it as base::UserData.
55// (see createServiceWorkerNetworkProvider).
56class DataSourceExtraData
57    : public blink::WebDataSource::ExtraData,
58      public base::SupportsUserData {
59 public:
60  DataSourceExtraData() {}
61  virtual ~DataSourceExtraData() {}
62};
63
64// Called on the main thread only and blink owns it.
65class WebServiceWorkerNetworkProviderImpl
66    : public blink::WebServiceWorkerNetworkProvider {
67 public:
68  // Blink calls this method for each request starting with the main script,
69  // we tag them with the provider id.
70  virtual void willSendRequest(
71      blink::WebDataSource* data_source,
72      blink::WebURLRequest& request) {
73    ServiceWorkerNetworkProvider* provider =
74        ServiceWorkerNetworkProvider::FromDocumentState(
75            static_cast<DataSourceExtraData*>(data_source->extraData()));
76    scoped_ptr<RequestExtraData> extra_data(new RequestExtraData);
77    extra_data->set_service_worker_provider_id(provider->provider_id());
78    request.setExtraData(extra_data.release());
79  }
80};
81
82}  // namespace
83
84EmbeddedWorkerContextClient*
85EmbeddedWorkerContextClient::ThreadSpecificInstance() {
86  return g_worker_client_tls.Pointer()->Get();
87}
88
89EmbeddedWorkerContextClient::EmbeddedWorkerContextClient(
90    int embedded_worker_id,
91    int64 service_worker_version_id,
92    const GURL& service_worker_scope,
93    const GURL& script_url,
94    int worker_devtools_agent_route_id)
95    : embedded_worker_id_(embedded_worker_id),
96      service_worker_version_id_(service_worker_version_id),
97      service_worker_scope_(service_worker_scope),
98      script_url_(script_url),
99      worker_devtools_agent_route_id_(worker_devtools_agent_route_id),
100      sender_(ChildThread::current()->thread_safe_sender()),
101      main_thread_proxy_(base::MessageLoopProxy::current()),
102      weak_factory_(this) {
103  TRACE_EVENT_ASYNC_BEGIN0("ServiceWorker",
104                           "EmbeddedWorkerContextClient::StartingWorkerContext",
105                           this);
106  TRACE_EVENT_ASYNC_STEP_INTO0(
107      "ServiceWorker",
108      "EmbeddedWorkerContextClient::StartingWorkerContext",
109      this,
110      "PrepareWorker");
111}
112
113EmbeddedWorkerContextClient::~EmbeddedWorkerContextClient() {
114}
115
116bool EmbeddedWorkerContextClient::OnMessageReceived(
117    const IPC::Message& msg) {
118  bool handled = true;
119  IPC_BEGIN_MESSAGE_MAP(EmbeddedWorkerContextClient, msg)
120    IPC_MESSAGE_HANDLER(EmbeddedWorkerContextMsg_MessageToWorker,
121                        OnMessageToWorker)
122    IPC_MESSAGE_UNHANDLED(handled = false)
123  IPC_END_MESSAGE_MAP()
124  return handled;
125}
126
127void EmbeddedWorkerContextClient::Send(IPC::Message* message) {
128  sender_->Send(message);
129}
130
131blink::WebURL EmbeddedWorkerContextClient::scope() const {
132  return service_worker_scope_;
133}
134
135blink::WebServiceWorkerCacheStorage*
136    EmbeddedWorkerContextClient::cacheStorage() {
137  return script_context_->cache_storage();
138}
139
140void EmbeddedWorkerContextClient::didPauseAfterDownload() {
141  Send(new EmbeddedWorkerHostMsg_DidPauseAfterDownload(embedded_worker_id_));
142}
143
144void EmbeddedWorkerContextClient::getClients(
145    blink::WebServiceWorkerClientsCallbacks* callbacks) {
146  DCHECK(script_context_);
147  script_context_->GetClientDocuments(callbacks);
148}
149
150void EmbeddedWorkerContextClient::workerReadyForInspection() {
151  Send(new EmbeddedWorkerHostMsg_WorkerReadyForInspection(embedded_worker_id_));
152}
153
154void EmbeddedWorkerContextClient::workerContextFailedToStart() {
155  DCHECK(main_thread_proxy_->RunsTasksOnCurrentThread());
156  DCHECK(!script_context_);
157
158  Send(new EmbeddedWorkerHostMsg_WorkerScriptLoadFailed(embedded_worker_id_));
159
160  RenderThreadImpl::current()->embedded_worker_dispatcher()->
161      WorkerContextDestroyed(embedded_worker_id_);
162}
163
164void EmbeddedWorkerContextClient::workerContextStarted(
165    blink::WebServiceWorkerContextProxy* proxy) {
166  DCHECK(!worker_task_runner_.get());
167  worker_task_runner_ = new WorkerThreadTaskRunner(
168      WorkerTaskRunner::Instance()->CurrentWorkerId());
169  DCHECK_NE(0, WorkerTaskRunner::Instance()->CurrentWorkerId());
170  // g_worker_client_tls.Pointer()->Get() could return NULL if this context
171  // gets deleted before workerContextStarted() is called.
172  DCHECK(g_worker_client_tls.Pointer()->Get() == NULL);
173  DCHECK(!script_context_);
174  g_worker_client_tls.Pointer()->Set(this);
175  script_context_.reset(new ServiceWorkerScriptContext(this, proxy));
176
177  Send(new EmbeddedWorkerHostMsg_WorkerScriptLoaded(
178      embedded_worker_id_,
179      WorkerTaskRunner::Instance()->CurrentWorkerId()));
180
181  // Schedule a task to send back WorkerStarted asynchronously,
182  // so that at the time we send it we can be sure that the worker
183  // script has been evaluated and worker run loop has been started.
184  worker_task_runner_->PostTask(
185      FROM_HERE,
186      base::Bind(&EmbeddedWorkerContextClient::SendWorkerStarted,
187                 weak_factory_.GetWeakPtr()));
188  TRACE_EVENT_ASYNC_STEP_INTO0(
189      "ServiceWorker",
190      "EmbeddedWorkerContextClient::StartingWorkerContext",
191      this,
192      "ExecuteScript");
193}
194
195void EmbeddedWorkerContextClient::willDestroyWorkerContext() {
196  // At this point OnWorkerRunLoopStopped is already called, so
197  // worker_task_runner_->RunsTasksOnCurrentThread() returns false
198  // (while we're still on the worker thread).
199  script_context_.reset();
200
201  // This also lets the message filter stop dispatching messages to
202  // this client.
203  g_worker_client_tls.Pointer()->Set(NULL);
204}
205
206void EmbeddedWorkerContextClient::workerContextDestroyed() {
207  DCHECK(g_worker_client_tls.Pointer()->Get() == NULL);
208
209  // Now we should be able to free the WebEmbeddedWorker container on the
210  // main thread.
211  main_thread_proxy_->PostTask(
212      FROM_HERE,
213      base::Bind(&CallWorkerContextDestroyedOnMainThread,
214                 embedded_worker_id_));
215}
216
217void EmbeddedWorkerContextClient::reportException(
218    const blink::WebString& error_message,
219    int line_number,
220    int column_number,
221    const blink::WebString& source_url) {
222  Send(new EmbeddedWorkerHostMsg_ReportException(
223      embedded_worker_id_, error_message, line_number,
224      column_number, GURL(source_url)));
225}
226
227void EmbeddedWorkerContextClient::reportConsoleMessage(
228    int source,
229    int level,
230    const blink::WebString& message,
231    int line_number,
232    const blink::WebString& source_url) {
233  EmbeddedWorkerHostMsg_ReportConsoleMessage_Params params;
234  params.source_identifier = source;
235  params.message_level = level;
236  params.message = message;
237  params.line_number = line_number;
238  params.source_url = GURL(source_url);
239
240  Send(new EmbeddedWorkerHostMsg_ReportConsoleMessage(
241      embedded_worker_id_, params));
242}
243
244void EmbeddedWorkerContextClient::dispatchDevToolsMessage(
245    const blink::WebString& message) {
246  sender_->Send(new DevToolsClientMsg_DispatchOnInspectorFrontend(
247      worker_devtools_agent_route_id_, message.utf8()));
248}
249
250void EmbeddedWorkerContextClient::saveDevToolsAgentState(
251    const blink::WebString& state) {
252  sender_->Send(new DevToolsHostMsg_SaveAgentRuntimeState(
253      worker_devtools_agent_route_id_, state.utf8()));
254}
255
256void EmbeddedWorkerContextClient::didHandleActivateEvent(
257    int request_id,
258    blink::WebServiceWorkerEventResult result) {
259  DCHECK(script_context_);
260  script_context_->DidHandleActivateEvent(request_id, result);
261}
262
263void EmbeddedWorkerContextClient::didHandleInstallEvent(
264    int request_id,
265    blink::WebServiceWorkerEventResult result) {
266  DCHECK(script_context_);
267  script_context_->DidHandleInstallEvent(request_id, result);
268}
269
270void EmbeddedWorkerContextClient::didHandleFetchEvent(int request_id) {
271  DCHECK(script_context_);
272  script_context_->DidHandleFetchEvent(
273      request_id,
274      SERVICE_WORKER_FETCH_EVENT_RESULT_FALLBACK,
275      ServiceWorkerResponse());
276}
277
278void EmbeddedWorkerContextClient::didHandleFetchEvent(
279    int request_id,
280    const blink::WebServiceWorkerResponse& web_response) {
281  DCHECK(script_context_);
282  ServiceWorkerHeaderMap headers;
283  const blink::WebVector<blink::WebString>& header_keys =
284      web_response.getHeaderKeys();
285  for (size_t i = 0; i < header_keys.size(); ++i) {
286    const base::string16& key = header_keys[i];
287    headers[base::UTF16ToUTF8(key)] =
288        base::UTF16ToUTF8(web_response.getHeader(key));
289  }
290  ServiceWorkerResponse response(web_response.url(),
291                                 web_response.status(),
292                                 web_response.statusText().utf8(),
293                                 headers,
294                                 web_response.blobUUID().utf8());
295  script_context_->DidHandleFetchEvent(
296      request_id, SERVICE_WORKER_FETCH_EVENT_RESULT_RESPONSE, response);
297}
298
299void EmbeddedWorkerContextClient::didHandleSyncEvent(int request_id) {
300  DCHECK(script_context_);
301  script_context_->DidHandleSyncEvent(request_id);
302}
303
304blink::WebServiceWorkerNetworkProvider*
305EmbeddedWorkerContextClient::createServiceWorkerNetworkProvider(
306    blink::WebDataSource* data_source) {
307  // Create a content::ServiceWorkerNetworkProvider for this data source so
308  // we can observe its requests.
309  scoped_ptr<ServiceWorkerNetworkProvider> provider(
310      new ServiceWorkerNetworkProvider());
311
312  // Tell the network provider about which version to load.
313  provider->SetServiceWorkerVersionId(service_worker_version_id_);
314
315  // The provider is kept around for the lifetime of the DataSource
316  // and ownership is transferred to the DataSource.
317  DataSourceExtraData* extra_data = new DataSourceExtraData();
318  data_source->setExtraData(extra_data);
319  ServiceWorkerNetworkProvider::AttachToDocumentState(
320      extra_data, provider.Pass());
321
322  // Blink is responsible for deleting the returned object.
323  return new WebServiceWorkerNetworkProviderImpl();
324}
325
326void EmbeddedWorkerContextClient::postMessageToClient(
327    int client_id,
328    const blink::WebString& message,
329    blink::WebMessagePortChannelArray* channels) {
330  DCHECK(script_context_);
331  script_context_->PostMessageToDocument(client_id, message,
332                                         make_scoped_ptr(channels));
333}
334
335void EmbeddedWorkerContextClient::OnMessageToWorker(
336    int thread_id,
337    int embedded_worker_id,
338    const IPC::Message& message) {
339  if (!script_context_)
340    return;
341  DCHECK_EQ(embedded_worker_id_, embedded_worker_id);
342  script_context_->OnMessageReceived(message);
343}
344
345void EmbeddedWorkerContextClient::SendWorkerStarted() {
346  DCHECK(worker_task_runner_->RunsTasksOnCurrentThread());
347  TRACE_EVENT_ASYNC_END0("ServiceWorker",
348                         "EmbeddedWorkerContextClient::StartingWorkerContext",
349                         this);
350  Send(new EmbeddedWorkerHostMsg_WorkerStarted(embedded_worker_id_));
351}
352
353}  // namespace content
354