Home | History | Annotate | Download | only in renderer
      1 // Copyright (c) 2011 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/websharedworker_proxy.h"
      6 
      7 #include "content/child/child_thread.h"
      8 #include "content/child/webmessageportchannel_impl.h"
      9 #include "content/common/view_messages.h"
     10 #include "content/common/worker_messages.h"
     11 #include "third_party/WebKit/public/platform/WebURL.h"
     12 #include "third_party/WebKit/public/web/WebSharedWorkerClient.h"
     13 
     14 namespace content {
     15 
     16 WebSharedWorkerProxy::WebSharedWorkerProxy(ChildThread* child_thread,
     17                                            unsigned long long document_id,
     18                                            bool exists,
     19                                            int route_id,
     20                                            int render_view_route_id)
     21     : route_id_(exists ? route_id : MSG_ROUTING_NONE),
     22       render_view_route_id_(render_view_route_id),
     23       child_thread_(child_thread),
     24       document_id_(document_id),
     25       pending_route_id_(route_id),
     26       connect_listener_(NULL) {
     27   if (route_id_ != MSG_ROUTING_NONE)
     28     child_thread_->AddRoute(route_id_, this);
     29 }
     30 
     31 WebSharedWorkerProxy::~WebSharedWorkerProxy() {
     32   Disconnect();
     33 
     34   // Free up any unsent queued messages.
     35   for (size_t i = 0; i < queued_messages_.size(); ++i)
     36     delete queued_messages_[i];
     37 }
     38 
     39 void WebSharedWorkerProxy::Disconnect() {
     40   if (route_id_ == MSG_ROUTING_NONE)
     41     return;
     42 
     43   // So the messages from WorkerContext (like WorkerContextDestroyed) do not
     44   // come after nobody is listening. Since Worker and WorkerContext can
     45   // terminate independently, already sent messages may still be in the pipe.
     46   child_thread_->RemoveRoute(route_id_);
     47 
     48   route_id_ = MSG_ROUTING_NONE;
     49 }
     50 
     51 void WebSharedWorkerProxy::CreateWorkerContext(
     52     const GURL& script_url,
     53     bool is_shared,
     54     const string16& name,
     55     const string16& user_agent,
     56     const string16& source_code,
     57     const string16& content_security_policy,
     58     WebKit::WebContentSecurityPolicyType policy_type,
     59     int pending_route_id,
     60     int64 script_resource_appcache_id) {
     61   DCHECK(route_id_ == MSG_ROUTING_NONE);
     62   ViewHostMsg_CreateWorker_Params params;
     63   params.url = script_url;
     64   params.name = name;
     65   params.document_id = document_id_;
     66   params.render_view_route_id = render_view_route_id_;
     67   params.route_id = pending_route_id;
     68   params.script_resource_appcache_id = script_resource_appcache_id;
     69   IPC::Message* create_message = new ViewHostMsg_CreateWorker(
     70       params, &route_id_);
     71   child_thread_->Send(create_message);
     72   if (route_id_ == MSG_ROUTING_NONE)
     73     return;
     74 
     75   child_thread_->AddRoute(route_id_, this);
     76 
     77   // We make sure that the start message is the first, since postMessage or
     78   // connect might have already been called.
     79   queued_messages_.insert(queued_messages_.begin(),
     80       new WorkerMsg_StartWorkerContext(
     81           route_id_, script_url, user_agent, source_code,
     82           content_security_policy, policy_type));
     83 }
     84 
     85 bool WebSharedWorkerProxy::IsStarted() {
     86   // Worker is started if we have a route ID and there are no queued messages
     87   // (meaning we've sent the WorkerMsg_StartWorkerContext already).
     88   return (route_id_ != MSG_ROUTING_NONE && queued_messages_.empty());
     89 }
     90 
     91 bool WebSharedWorkerProxy::Send(IPC::Message* message) {
     92   // It's possible that messages will be sent before the worker is created, in
     93   // which case route_id_ will be none.  Or the worker object can be interacted
     94   // with before the browser process told us that it started, in which case we
     95   // also want to queue the message.
     96   if (!IsStarted()) {
     97     queued_messages_.push_back(message);
     98     return true;
     99   }
    100 
    101   // For now we proxy all messages to the worker process through the browser.
    102   // Revisit if we find this slow.
    103   // TODO(jabdelmalek): handle sync messages if we need them.
    104   IPC::Message* wrapped_msg = new ViewHostMsg_ForwardToWorker(*message);
    105   delete message;
    106   return child_thread_->Send(wrapped_msg);
    107 }
    108 
    109 void WebSharedWorkerProxy::SendQueuedMessages() {
    110   DCHECK(queued_messages_.size());
    111   std::vector<IPC::Message*> queued_messages = queued_messages_;
    112   queued_messages_.clear();
    113   for (size_t i = 0; i < queued_messages.size(); ++i) {
    114     queued_messages[i]->set_routing_id(route_id_);
    115     Send(queued_messages[i]);
    116   }
    117 }
    118 
    119 bool WebSharedWorkerProxy::isStarted() {
    120   return IsStarted();
    121 }
    122 
    123 void WebSharedWorkerProxy::startWorkerContext(
    124     const WebKit::WebURL& script_url,
    125     const WebKit::WebString& name,
    126     const WebKit::WebString& user_agent,
    127     const WebKit::WebString& source_code,
    128     const WebKit::WebString& content_security_policy,
    129     WebKit::WebContentSecurityPolicyType policy_type,
    130     long long script_resource_appcache_id) {
    131   DCHECK(!isStarted());
    132   CreateWorkerContext(
    133       script_url, true, name, user_agent, source_code, content_security_policy,
    134       policy_type, pending_route_id_, script_resource_appcache_id);
    135 }
    136 
    137 void WebSharedWorkerProxy::terminateWorkerContext() {
    138   // This API should only be invoked from worker context.
    139   NOTREACHED();
    140 }
    141 
    142 void WebSharedWorkerProxy::clientDestroyed() {
    143   // This API should only be invoked from worker context.
    144   NOTREACHED();
    145 }
    146 
    147 void WebSharedWorkerProxy::connect(WebKit::WebMessagePortChannel* channel,
    148                                    ConnectListener* listener) {
    149   WebMessagePortChannelImpl* webchannel =
    150         static_cast<WebMessagePortChannelImpl*>(channel);
    151 
    152   int message_port_id = webchannel->message_port_id();
    153   DCHECK(message_port_id != MSG_ROUTING_NONE);
    154   webchannel->QueueMessages();
    155 
    156   Send(new WorkerMsg_Connect(route_id_, message_port_id, MSG_ROUTING_NONE));
    157   if (HasQueuedMessages()) {
    158     connect_listener_ = listener;
    159   } else {
    160     listener->connected();
    161     // The listener may free this object, so do not access the object after
    162     // this point.
    163   }
    164 }
    165 
    166 bool WebSharedWorkerProxy::OnMessageReceived(const IPC::Message& message) {
    167   bool handled = true;
    168   IPC_BEGIN_MESSAGE_MAP(WebSharedWorkerProxy, message)
    169     IPC_MESSAGE_HANDLER(ViewMsg_WorkerCreated, OnWorkerCreated)
    170     IPC_MESSAGE_UNHANDLED(handled = false)
    171   IPC_END_MESSAGE_MAP()
    172   return handled;
    173 }
    174 
    175 void WebSharedWorkerProxy::OnWorkerCreated() {
    176   // The worker is created - now send off the CreateWorkerContext message and
    177   // any other queued messages
    178   SendQueuedMessages();
    179 
    180   // Inform any listener that the pending connect event has been sent
    181   // (this can result in this object being freed).
    182   if (connect_listener_) {
    183     connect_listener_->connected();
    184   }
    185 }
    186 
    187 }  // namespace content
    188