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