1 // Copyright (c) 2012 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/worker/websharedworker_stub.h"
6
7 #include "base/command_line.h"
8 #include "base/compiler_specific.h"
9 #include "content/child/child_process.h"
10 #include "content/child/child_thread.h"
11 #include "content/child/fileapi/file_system_dispatcher.h"
12 #include "content/child/shared_worker_devtools_agent.h"
13 #include "content/child/webmessageportchannel_impl.h"
14 #include "content/common/worker_messages.h"
15 #include "content/public/common/content_switches.h"
16 #include "content/worker/worker_thread.h"
17 #include "third_party/WebKit/public/platform/WebString.h"
18 #include "third_party/WebKit/public/platform/WebURL.h"
19 #include "third_party/WebKit/public/web/WebSharedWorker.h"
20
21 namespace content {
22
WebSharedWorkerStub(const GURL & url,const base::string16 & name,const base::string16 & content_security_policy,blink::WebContentSecurityPolicyType security_policy_type,bool pause_on_start,int route_id)23 WebSharedWorkerStub::WebSharedWorkerStub(
24 const GURL& url,
25 const base::string16& name,
26 const base::string16& content_security_policy,
27 blink::WebContentSecurityPolicyType security_policy_type,
28 bool pause_on_start,
29 int route_id)
30 : route_id_(route_id),
31 client_(route_id, this),
32 running_(false),
33 url_(url) {
34
35 WorkerThread* worker_thread = WorkerThread::current();
36 DCHECK(worker_thread);
37 worker_thread->AddWorkerStub(this);
38 // Start processing incoming IPCs for this worker.
39 worker_thread->GetRouter()->AddRoute(route_id_, this);
40
41 // TODO(atwilson): Add support for NaCl when they support MessagePorts.
42 impl_ = blink::WebSharedWorker::create(client());
43 if (pause_on_start) {
44 // Pause worker context when it starts and wait until either DevTools client
45 // is attached or explicit resume notification is received.
46 impl_->pauseWorkerContextOnStart();
47 }
48
49 worker_devtools_agent_.reset(new SharedWorkerDevToolsAgent(route_id, impl_));
50 client()->set_devtools_agent(worker_devtools_agent_.get());
51 impl_->startWorkerContext(url_, name,
52 content_security_policy, security_policy_type);
53 }
54
~WebSharedWorkerStub()55 WebSharedWorkerStub::~WebSharedWorkerStub() {
56 impl_->clientDestroyed();
57 WorkerThread* worker_thread = WorkerThread::current();
58 DCHECK(worker_thread);
59 worker_thread->RemoveWorkerStub(this);
60 worker_thread->GetRouter()->RemoveRoute(route_id_);
61 }
62
Shutdown()63 void WebSharedWorkerStub::Shutdown() {
64 // The worker has exited - free ourselves and the client.
65 delete this;
66 }
67
EnsureWorkerContextTerminates()68 void WebSharedWorkerStub::EnsureWorkerContextTerminates() {
69 client_.EnsureWorkerContextTerminates();
70 }
71
OnMessageReceived(const IPC::Message & message)72 bool WebSharedWorkerStub::OnMessageReceived(const IPC::Message& message) {
73 if (worker_devtools_agent_->OnMessageReceived(message))
74 return true;
75
76 bool handled = true;
77 IPC_BEGIN_MESSAGE_MAP(WebSharedWorkerStub, message)
78 IPC_MESSAGE_HANDLER(WorkerMsg_TerminateWorkerContext,
79 OnTerminateWorkerContext)
80 IPC_MESSAGE_HANDLER(WorkerMsg_Connect, OnConnect)
81 IPC_MESSAGE_UNHANDLED(handled = false)
82 IPC_END_MESSAGE_MAP()
83 return handled;
84 }
85
OnChannelError()86 void WebSharedWorkerStub::OnChannelError() {
87 OnTerminateWorkerContext();
88 }
89
url()90 const GURL& WebSharedWorkerStub::url() {
91 return url_;
92 }
93
OnConnect(int sent_message_port_id,int routing_id)94 void WebSharedWorkerStub::OnConnect(int sent_message_port_id, int routing_id) {
95 WebMessagePortChannelImpl* channel =
96 new WebMessagePortChannelImpl(routing_id,
97 sent_message_port_id,
98 base::MessageLoopProxy::current().get());
99 if (running_) {
100 impl_->connect(channel);
101 WorkerThread::current()->Send(
102 new WorkerHostMsg_WorkerConnected(channel->message_port_id(),
103 route_id_));
104 } else {
105 // If two documents try to load a SharedWorker at the same time, the
106 // WorkerMsg_Connect for one of the documents can come in before the
107 // worker is started. Just queue up the connect and deliver it once the
108 // worker starts.
109 pending_channels_.push_back(channel);
110 }
111 }
112
OnTerminateWorkerContext()113 void WebSharedWorkerStub::OnTerminateWorkerContext() {
114 running_ = false;
115 // Call the client to make sure context exits.
116 EnsureWorkerContextTerminates();
117 // This may call "delete this" via WorkerScriptLoadFailed and Shutdown.
118 impl_->terminateWorkerContext();
119 }
120
WorkerScriptLoaded()121 void WebSharedWorkerStub::WorkerScriptLoaded() {
122 running_ = true;
123 // Process any pending connections.
124 for (PendingChannelList::const_iterator iter = pending_channels_.begin();
125 iter != pending_channels_.end();
126 ++iter) {
127 impl_->connect(*iter);
128 WorkerThread::current()->Send(
129 new WorkerHostMsg_WorkerConnected((*iter)->message_port_id(),
130 route_id_));
131 }
132 pending_channels_.clear();
133 }
134
WorkerScriptLoadFailed()135 void WebSharedWorkerStub::WorkerScriptLoadFailed() {
136 for (PendingChannelList::const_iterator iter = pending_channels_.begin();
137 iter != pending_channels_.end();
138 ++iter) {
139 blink::WebMessagePortChannel* channel = *iter;
140 channel->destroy();
141 }
142 pending_channels_.clear();
143 Shutdown();
144 }
145
146 } // namespace content
147