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