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 23namespace content { 24namespace { 25 26// Notifies RenderViewHost that one or more worker objects crashed. 27void 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 34void 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 48void 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 61void 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 75SharedWorkerHost::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 90SharedWorkerHost::~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 115bool 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 123void 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 139bool 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 152void 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 163void 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 175void 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 185void SharedWorkerHost::WorkerContextDestroyed() { 186 if (!instance_) 187 return; 188 instance_.reset(); 189 worker_document_set_ = NULL; 190} 191 192void SharedWorkerHost::WorkerReadyForInspection() { 193 NotifyWorkerReadyForInspection(worker_process_id_, worker_route_id_); 194} 195 196void SharedWorkerHost::WorkerScriptLoaded() { 197 UMA_HISTOGRAM_TIMES("SharedWorker.TimeToScriptLoaded", 198 base::TimeTicks::Now() - creation_time_); 199 NotifyWorkerContextStarted(worker_process_id_, worker_route_id_); 200} 201 202void 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 214void 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 226void 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 242void 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 255void 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 264void 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 273void 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, ¶m)) 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 309void SharedWorkerHost::TerminateWorker() { 310 Send(new WorkerMsg_TerminateWorkerContext(worker_route_id_)); 311} 312 313std::vector<std::pair<int, int> > 314SharedWorkerHost::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 330void 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 339void 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 348bool 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 358void 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