Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / content / browser / shared_worker / shared_worker_host.cc
1 // Copyright 2014 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/browser/shared_worker/shared_worker_host.h"
6
7 #include "base/metrics/histogram.h"
8 #include "content/browser/devtools/embedded_worker_devtools_manager.h"
9 #include "content/browser/frame_host/render_frame_host_delegate.h"
10 #include "content/browser/frame_host/render_frame_host_impl.h"
11 #include "content/browser/message_port_service.h"
12 #include "content/browser/shared_worker/shared_worker_instance.h"
13 #include "content/browser/shared_worker/shared_worker_message_filter.h"
14 #include "content/browser/shared_worker/shared_worker_service_impl.h"
15 #include "content/browser/shared_worker/worker_document_set.h"
16 #include "content/common/view_messages.h"
17 #include "content/common/worker_messages.h"
18 #include "content/public/browser/browser_thread.h"
19 #include "content/public/browser/content_browser_client.h"
20 #include "content/public/browser/render_process_host.h"
21 #include "content/public/common/content_client.h"
22
23 namespace content {
24 namespace {
25
26 // Notifies RenderViewHost that one or more worker objects crashed.
27 void WorkerCrashCallback(int render_process_unique_id, int render_frame_id) {
28   RenderFrameHostImpl* host =
29       RenderFrameHostImpl::FromID(render_process_unique_id, render_frame_id);
30   if (host)
31     host->delegate()->WorkerCrashed(host);
32 }
33
34 void NotifyWorkerContextStarted(int worker_process_id, int worker_route_id) {
35   if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
36     BrowserThread::PostTask(
37         BrowserThread::UI,
38         FROM_HERE,
39         base::Bind(
40             NotifyWorkerContextStarted, worker_process_id, worker_route_id));
41     return;
42   }
43   EmbeddedWorkerDevToolsManager::GetInstance()->WorkerContextStarted(
44       worker_process_id, worker_route_id);
45 }
46
47 void NotifyWorkerDestroyed(int worker_process_id, int worker_route_id) {
48   if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
49     BrowserThread::PostTask(
50         BrowserThread::UI,
51         FROM_HERE,
52         base::Bind(NotifyWorkerDestroyed, worker_process_id, worker_route_id));
53     return;
54   }
55   EmbeddedWorkerDevToolsManager::GetInstance()->WorkerDestroyed(
56       worker_process_id, worker_route_id);
57 }
58
59 }  // namespace
60
61 SharedWorkerHost::SharedWorkerHost(SharedWorkerInstance* instance,
62                                    SharedWorkerMessageFilter* filter,
63                                    int worker_route_id)
64     : instance_(instance),
65       worker_document_set_(new WorkerDocumentSet()),
66       weak_factory_(this),
67       container_render_filter_(filter),
68       worker_process_id_(filter->render_process_id()),
69       worker_route_id_(worker_route_id),
70       load_failed_(false),
71       closed_(false),
72       creation_time_(base::TimeTicks::Now()) {
73   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
74 }
75
76 SharedWorkerHost::~SharedWorkerHost() {
77   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
78   UMA_HISTOGRAM_LONG_TIMES("SharedWorker.TimeToDeleted",
79                            base::TimeTicks::Now() - creation_time_);
80   // If we crashed, tell the RenderViewHosts.
81   if (instance_ && !load_failed_) {
82     const WorkerDocumentSet::DocumentInfoSet& parents =
83         worker_document_set_->documents();
84     for (WorkerDocumentSet::DocumentInfoSet::const_iterator parent_iter =
85              parents.begin();
86          parent_iter != parents.end();
87          ++parent_iter) {
88       BrowserThread::PostTask(BrowserThread::UI,
89                               FROM_HERE,
90                               base::Bind(&WorkerCrashCallback,
91                                          parent_iter->render_process_id(),
92                                          parent_iter->render_frame_id()));
93     }
94   }
95   if (!closed_)
96     NotifyWorkerDestroyed(worker_process_id_, worker_route_id_);
97   SharedWorkerServiceImpl::GetInstance()->NotifyWorkerDestroyed(
98       worker_process_id_, worker_route_id_);
99 }
100
101 bool SharedWorkerHost::Send(IPC::Message* message) {
102   if (!container_render_filter_) {
103     delete message;
104     return false;
105   }
106   return container_render_filter_->Send(message);
107 }
108
109 void SharedWorkerHost::Start(bool pause_on_start) {
110   WorkerProcessMsg_CreateWorker_Params params;
111   params.url = instance_->url();
112   params.name = instance_->name();
113   params.content_security_policy = instance_->content_security_policy();
114   params.security_policy_type = instance_->security_policy_type();
115   params.pause_on_start = pause_on_start;
116   params.route_id = worker_route_id_;
117   Send(new WorkerProcessMsg_CreateWorker(params));
118
119   for (FilterList::const_iterator i = filters_.begin(); i != filters_.end();
120        ++i) {
121     i->filter()->Send(new ViewMsg_WorkerCreated(i->route_id()));
122   }
123 }
124
125 bool SharedWorkerHost::FilterMessage(const IPC::Message& message,
126                                      SharedWorkerMessageFilter* filter) {
127   if (!instance_)
128     return false;
129
130   if (!closed_ && HasFilter(filter, message.routing_id())) {
131     RelayMessage(message, filter);
132     return true;
133   }
134
135   return false;
136 }
137
138 void SharedWorkerHost::FilterShutdown(SharedWorkerMessageFilter* filter) {
139   if (!instance_)
140     return;
141   RemoveFilters(filter);
142   worker_document_set_->RemoveAll(filter);
143   if (worker_document_set_->IsEmpty()) {
144     // This worker has no more associated documents - shut it down.
145     Send(new WorkerMsg_TerminateWorkerContext(worker_route_id_));
146   }
147 }
148
149 void SharedWorkerHost::DocumentDetached(SharedWorkerMessageFilter* filter,
150                                         unsigned long long document_id) {
151   if (!instance_)
152     return;
153   // Walk all instances and remove the document from their document set.
154   worker_document_set_->Remove(filter, document_id);
155   if (worker_document_set_->IsEmpty()) {
156     // This worker has no more associated documents - shut it down.
157     Send(new WorkerMsg_TerminateWorkerContext(worker_route_id_));
158   }
159 }
160
161 void SharedWorkerHost::WorkerContextClosed() {
162   if (!instance_)
163     return;
164   // Set the closed flag - this will stop any further messages from
165   // being sent to the worker (messages can still be sent from the worker,
166   // for exception reporting, etc).
167   closed_ = true;
168   NotifyWorkerDestroyed(worker_process_id_, worker_route_id_);
169 }
170
171 void SharedWorkerHost::WorkerContextDestroyed() {
172   if (!instance_)
173     return;
174   instance_.reset();
175   worker_document_set_ = NULL;
176 }
177
178 void SharedWorkerHost::WorkerScriptLoaded() {
179   UMA_HISTOGRAM_TIMES("SharedWorker.TimeToScriptLoaded",
180                       base::TimeTicks::Now() - creation_time_);
181   NotifyWorkerContextStarted(worker_process_id_, worker_route_id_);
182 }
183
184 void SharedWorkerHost::WorkerScriptLoadFailed() {
185   UMA_HISTOGRAM_TIMES("SharedWorker.TimeToScriptLoadFailed",
186                       base::TimeTicks::Now() - creation_time_);
187   if (!instance_)
188     return;
189   load_failed_ = true;
190   for (FilterList::const_iterator i = filters_.begin(); i != filters_.end();
191        ++i) {
192     i->filter()->Send(new ViewMsg_WorkerScriptLoadFailed(i->route_id()));
193   }
194 }
195
196 void SharedWorkerHost::WorkerConnected(int message_port_id) {
197   if (!instance_)
198     return;
199   for (FilterList::const_iterator i = filters_.begin(); i != filters_.end();
200        ++i) {
201     if (i->message_port_id() != message_port_id)
202       continue;
203     i->filter()->Send(new ViewMsg_WorkerConnected(i->route_id()));
204     return;
205   }
206 }
207
208 void SharedWorkerHost::AllowDatabase(const GURL& url,
209                                      const base::string16& name,
210                                      const base::string16& display_name,
211                                      unsigned long estimated_size,
212                                      bool* result) {
213   if (!instance_)
214     return;
215   *result = GetContentClient()->browser()->AllowWorkerDatabase(
216       url,
217       name,
218       display_name,
219       estimated_size,
220       instance_->resource_context(),
221       GetRenderFrameIDsForWorker());
222 }
223
224 void SharedWorkerHost::AllowFileSystem(const GURL& url,
225                                        scoped_ptr<IPC::Message> reply_msg) {
226   if (!instance_)
227     return;
228   GetContentClient()->browser()->AllowWorkerFileSystem(
229       url,
230       instance_->resource_context(),
231       GetRenderFrameIDsForWorker(),
232       base::Bind(&SharedWorkerHost::AllowFileSystemResponse,
233                  weak_factory_.GetWeakPtr(),
234                  base::Passed(&reply_msg)));
235 }
236
237 void SharedWorkerHost::AllowFileSystemResponse(
238     scoped_ptr<IPC::Message> reply_msg,
239     bool allowed) {
240   WorkerProcessHostMsg_RequestFileSystemAccessSync::WriteReplyParams(
241       reply_msg.get(),
242       allowed);
243   Send(reply_msg.release());
244 }
245
246 void SharedWorkerHost::AllowIndexedDB(const GURL& url,
247                                       const base::string16& name,
248                                       bool* result) {
249   if (!instance_)
250     return;
251   *result = GetContentClient()->browser()->AllowWorkerIndexedDB(
252       url, name, instance_->resource_context(), GetRenderFrameIDsForWorker());
253 }
254
255 void SharedWorkerHost::RelayMessage(
256     const IPC::Message& message,
257     SharedWorkerMessageFilter* incoming_filter) {
258   if (!instance_)
259     return;
260   if (message.type() == WorkerMsg_Connect::ID) {
261     // Crack the SharedWorker Connect message to setup routing for the port.
262     WorkerMsg_Connect::Param param;
263     if (!WorkerMsg_Connect::Read(&message, &param))
264       return;
265     int sent_message_port_id = param.a;
266     int new_routing_id = param.b;
267
268     DCHECK(container_render_filter_);
269     new_routing_id = container_render_filter_->GetNextRoutingID();
270     MessagePortService::GetInstance()->UpdateMessagePort(
271         sent_message_port_id,
272         container_render_filter_->message_port_message_filter(),
273         new_routing_id);
274     SetMessagePortID(
275         incoming_filter, message.routing_id(), sent_message_port_id);
276     // Resend the message with the new routing id.
277     Send(new WorkerMsg_Connect(
278         worker_route_id_, sent_message_port_id, new_routing_id));
279
280     // Send any queued messages for the sent port.
281     MessagePortService::GetInstance()->SendQueuedMessagesIfPossible(
282         sent_message_port_id);
283   } else {
284     IPC::Message* new_message = new IPC::Message(message);
285     new_message->set_routing_id(worker_route_id_);
286     Send(new_message);
287     return;
288   }
289 }
290
291 void SharedWorkerHost::TerminateWorker() {
292   Send(new WorkerMsg_TerminateWorkerContext(worker_route_id_));
293 }
294
295 std::vector<std::pair<int, int> >
296 SharedWorkerHost::GetRenderFrameIDsForWorker() {
297   std::vector<std::pair<int, int> > result;
298   if (!instance_)
299     return result;
300   const WorkerDocumentSet::DocumentInfoSet& documents =
301       worker_document_set_->documents();
302   for (WorkerDocumentSet::DocumentInfoSet::const_iterator doc =
303            documents.begin();
304        doc != documents.end();
305        ++doc) {
306     result.push_back(
307         std::make_pair(doc->render_process_id(), doc->render_frame_id()));
308   }
309   return result;
310 }
311
312 void SharedWorkerHost::AddFilter(SharedWorkerMessageFilter* filter,
313                                  int route_id) {
314   CHECK(filter);
315   if (!HasFilter(filter, route_id)) {
316     FilterInfo info(filter, route_id);
317     filters_.push_back(info);
318   }
319 }
320
321 void SharedWorkerHost::RemoveFilters(SharedWorkerMessageFilter* filter) {
322   for (FilterList::iterator i = filters_.begin(); i != filters_.end();) {
323     if (i->filter() == filter)
324       i = filters_.erase(i);
325     else
326       ++i;
327   }
328 }
329
330 bool SharedWorkerHost::HasFilter(SharedWorkerMessageFilter* filter,
331                                  int route_id) const {
332   for (FilterList::const_iterator i = filters_.begin(); i != filters_.end();
333        ++i) {
334     if (i->filter() == filter && i->route_id() == route_id)
335       return true;
336   }
337   return false;
338 }
339
340 void SharedWorkerHost::SetMessagePortID(SharedWorkerMessageFilter* filter,
341                                         int route_id,
342                                         int message_port_id) {
343   for (FilterList::iterator i = filters_.begin(); i != filters_.end(); ++i) {
344     if (i->filter() == filter && i->route_id() == route_id) {
345       i->set_message_port_id(message_port_id);
346       return;
347     }
348   }
349 }
350
351 }  // namespace content