• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
35 namespace content {
36 
37 namespace {
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.
42 base::LazyInstance<base::ThreadLocalPointer<EmbeddedWorkerContextClient> >::
43     Leaky g_worker_client_tls = LAZY_INSTANCE_INITIALIZER;
44 
CallWorkerContextDestroyedOnMainThread(int embedded_worker_id)45 void 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).
56 class DataSourceExtraData
57     : public blink::WebDataSource::ExtraData,
58       public base::SupportsUserData {
59  public:
DataSourceExtraData()60   DataSourceExtraData() {}
~DataSourceExtraData()61   virtual ~DataSourceExtraData() {}
62 };
63 
64 // Called on the main thread only and blink owns it.
65 class 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.
willSendRequest(blink::WebDataSource * data_source,blink::WebURLRequest & request)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 
84 EmbeddedWorkerContextClient*
ThreadSpecificInstance()85 EmbeddedWorkerContextClient::ThreadSpecificInstance() {
86   return g_worker_client_tls.Pointer()->Get();
87 }
88 
EmbeddedWorkerContextClient(int embedded_worker_id,int64 service_worker_version_id,const GURL & service_worker_scope,const GURL & script_url,int worker_devtools_agent_route_id)89 EmbeddedWorkerContextClient::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 
~EmbeddedWorkerContextClient()113 EmbeddedWorkerContextClient::~EmbeddedWorkerContextClient() {
114 }
115 
OnMessageReceived(const IPC::Message & msg)116 bool 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 
Send(IPC::Message * message)127 void EmbeddedWorkerContextClient::Send(IPC::Message* message) {
128   sender_->Send(message);
129 }
130 
scope() const131 blink::WebURL EmbeddedWorkerContextClient::scope() const {
132   return service_worker_scope_;
133 }
134 
135 blink::WebServiceWorkerCacheStorage*
cacheStorage()136     EmbeddedWorkerContextClient::cacheStorage() {
137   return script_context_->cache_storage();
138 }
139 
didPauseAfterDownload()140 void EmbeddedWorkerContextClient::didPauseAfterDownload() {
141   Send(new EmbeddedWorkerHostMsg_DidPauseAfterDownload(embedded_worker_id_));
142 }
143 
getClients(blink::WebServiceWorkerClientsCallbacks * callbacks)144 void EmbeddedWorkerContextClient::getClients(
145     blink::WebServiceWorkerClientsCallbacks* callbacks) {
146   DCHECK(script_context_);
147   script_context_->GetClientDocuments(callbacks);
148 }
149 
workerReadyForInspection()150 void EmbeddedWorkerContextClient::workerReadyForInspection() {
151   Send(new EmbeddedWorkerHostMsg_WorkerReadyForInspection(embedded_worker_id_));
152 }
153 
workerContextFailedToStart()154 void 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 
workerContextStarted(blink::WebServiceWorkerContextProxy * proxy)164 void 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 
willDestroyWorkerContext()195 void 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 
workerContextDestroyed()206 void 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 
reportException(const blink::WebString & error_message,int line_number,int column_number,const blink::WebString & source_url)217 void 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 
reportConsoleMessage(int source,int level,const blink::WebString & message,int line_number,const blink::WebString & source_url)227 void 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 
dispatchDevToolsMessage(const blink::WebString & message)244 void EmbeddedWorkerContextClient::dispatchDevToolsMessage(
245     const blink::WebString& message) {
246   sender_->Send(new DevToolsClientMsg_DispatchOnInspectorFrontend(
247       worker_devtools_agent_route_id_, message.utf8()));
248 }
249 
saveDevToolsAgentState(const blink::WebString & state)250 void EmbeddedWorkerContextClient::saveDevToolsAgentState(
251     const blink::WebString& state) {
252   sender_->Send(new DevToolsHostMsg_SaveAgentRuntimeState(
253       worker_devtools_agent_route_id_, state.utf8()));
254 }
255 
didHandleActivateEvent(int request_id,blink::WebServiceWorkerEventResult result)256 void EmbeddedWorkerContextClient::didHandleActivateEvent(
257     int request_id,
258     blink::WebServiceWorkerEventResult result) {
259   DCHECK(script_context_);
260   script_context_->DidHandleActivateEvent(request_id, result);
261 }
262 
didHandleInstallEvent(int request_id,blink::WebServiceWorkerEventResult result)263 void EmbeddedWorkerContextClient::didHandleInstallEvent(
264     int request_id,
265     blink::WebServiceWorkerEventResult result) {
266   DCHECK(script_context_);
267   script_context_->DidHandleInstallEvent(request_id, result);
268 }
269 
didHandleFetchEvent(int request_id)270 void 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 
didHandleFetchEvent(int request_id,const blink::WebServiceWorkerResponse & web_response)278 void 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 
didHandleSyncEvent(int request_id)299 void EmbeddedWorkerContextClient::didHandleSyncEvent(int request_id) {
300   DCHECK(script_context_);
301   script_context_->DidHandleSyncEvent(request_id);
302 }
303 
304 blink::WebServiceWorkerNetworkProvider*
createServiceWorkerNetworkProvider(blink::WebDataSource * data_source)305 EmbeddedWorkerContextClient::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 
postMessageToClient(int client_id,const blink::WebString & message,blink::WebMessagePortChannelArray * channels)326 void 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 
OnMessageToWorker(int thread_id,int embedded_worker_id,const IPC::Message & message)335 void 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 
SendWorkerStarted()345 void 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