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