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