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