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