Home | History | Annotate | Download | only in service_worker
      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 
     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:
     60   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.
     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*
     85 EmbeddedWorkerContextClient::ThreadSpecificInstance() {
     86   return g_worker_client_tls.Pointer()->Get();
     87 }
     88 
     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 
    113 EmbeddedWorkerContextClient::~EmbeddedWorkerContextClient() {
    114 }
    115 
    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 
    127 void EmbeddedWorkerContextClient::Send(IPC::Message* message) {
    128   sender_->Send(message);
    129 }
    130 
    131 blink::WebURL EmbeddedWorkerContextClient::scope() const {
    132   return service_worker_scope_;
    133 }
    134 
    135 blink::WebServiceWorkerCacheStorage*
    136     EmbeddedWorkerContextClient::cacheStorage() {
    137   return script_context_->cache_storage();
    138 }
    139 
    140 void EmbeddedWorkerContextClient::didPauseAfterDownload() {
    141   Send(new EmbeddedWorkerHostMsg_DidPauseAfterDownload(embedded_worker_id_));
    142 }
    143 
    144 void EmbeddedWorkerContextClient::getClients(
    145     blink::WebServiceWorkerClientsCallbacks* callbacks) {
    146   DCHECK(script_context_);
    147   script_context_->GetClientDocuments(callbacks);
    148 }
    149 
    150 void EmbeddedWorkerContextClient::workerReadyForInspection() {
    151   Send(new EmbeddedWorkerHostMsg_WorkerReadyForInspection(embedded_worker_id_));
    152 }
    153 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    299 void EmbeddedWorkerContextClient::didHandleSyncEvent(int request_id) {
    300   DCHECK(script_context_);
    301   script_context_->DidHandleSyncEvent(request_id);
    302 }
    303 
    304 blink::WebServiceWorkerNetworkProvider*
    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 
    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 
    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 
    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