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
21namespace content {
22
23WebSharedWorkerStub::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
55WebSharedWorkerStub::~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
63void WebSharedWorkerStub::Shutdown() {
64  // The worker has exited - free ourselves and the client.
65  delete this;
66}
67
68void WebSharedWorkerStub::EnsureWorkerContextTerminates() {
69  client_.EnsureWorkerContextTerminates();
70}
71
72bool 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
86void WebSharedWorkerStub::OnChannelError() {
87    OnTerminateWorkerContext();
88}
89
90const GURL& WebSharedWorkerStub::url() {
91  return url_;
92}
93
94void 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
113void 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
121void 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
135void 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