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/browser/worker_host/worker_process_host.h" 6 7 #include <set> 8 #include <string> 9 #include <vector> 10 11 #include "base/base_switches.h" 12 #include "base/bind.h" 13 #include "base/bind_helpers.h" 14 #include "base/callback.h" 15 #include "base/command_line.h" 16 #include "base/message_loop/message_loop.h" 17 #include "base/strings/string_util.h" 18 #include "base/strings/utf_string_conversions.h" 19 #include "content/browser/appcache/appcache_dispatcher_host.h" 20 #include "content/browser/appcache/chrome_appcache_service.h" 21 #include "content/browser/browser_child_process_host_impl.h" 22 #include "content/browser/child_process_security_policy_impl.h" 23 #include "content/browser/devtools/worker_devtools_manager.h" 24 #include "content/browser/devtools/worker_devtools_message_filter.h" 25 #include "content/browser/fileapi/fileapi_message_filter.h" 26 #include "content/browser/indexed_db/indexed_db_dispatcher_host.h" 27 #include "content/browser/mime_registry_message_filter.h" 28 #include "content/browser/quota_dispatcher_host.h" 29 #include "content/browser/renderer_host/database_message_filter.h" 30 #include "content/browser/renderer_host/file_utilities_message_filter.h" 31 #include "content/browser/renderer_host/render_view_host_delegate.h" 32 #include "content/browser/renderer_host/render_view_host_impl.h" 33 #include "content/browser/renderer_host/socket_stream_dispatcher_host.h" 34 #include "content/browser/resource_context_impl.h" 35 #include "content/browser/worker_host/message_port_service.h" 36 #include "content/browser/worker_host/worker_message_filter.h" 37 #include "content/browser/worker_host/worker_service_impl.h" 38 #include "content/common/child_process_host_impl.h" 39 #include "content/common/view_messages.h" 40 #include "content/common/worker_messages.h" 41 #include "content/public/browser/browser_thread.h" 42 #include "content/public/browser/content_browser_client.h" 43 #include "content/public/browser/user_metrics.h" 44 #include "content/public/common/content_switches.h" 45 #include "content/public/common/result_codes.h" 46 #include "ipc/ipc_switches.h" 47 #include "net/base/mime_util.h" 48 #include "net/base/registry_controlled_domains/registry_controlled_domain.h" 49 #include "net/url_request/url_request_context_getter.h" 50 #include "ui/base/ui_base_switches.h" 51 #include "webkit/browser/fileapi/file_system_context.h" 52 #include "webkit/browser/fileapi/sandbox_file_system_backend.h" 53 #include "webkit/common/resource_type.h" 54 55 #if defined(OS_WIN) 56 #include "content/common/sandbox_win.h" 57 #include "content/public/common/sandboxed_process_launcher_delegate.h" 58 #endif 59 60 namespace content { 61 namespace { 62 63 #if defined(OS_WIN) 64 // NOTE: changes to this class need to be reviewed by the security team. 65 class WorkerSandboxedProcessLauncherDelegate 66 : public content::SandboxedProcessLauncherDelegate { 67 public: 68 WorkerSandboxedProcessLauncherDelegate() {} 69 virtual ~WorkerSandboxedProcessLauncherDelegate() {} 70 71 virtual void PreSpawnTarget(sandbox::TargetPolicy* policy, 72 bool* success) { 73 AddBaseHandleClosePolicy(policy); 74 } 75 }; 76 #endif // OS_WIN 77 78 // Helper class that we pass to SocketStreamDispatcherHost so that it can find 79 // the right net::URLRequestContext for a request. 80 class URLRequestContextSelector 81 : public ResourceMessageFilter::URLRequestContextSelector { 82 public: 83 explicit URLRequestContextSelector( 84 net::URLRequestContextGetter* url_request_context, 85 net::URLRequestContextGetter* media_url_request_context) 86 : url_request_context_(url_request_context), 87 media_url_request_context_(media_url_request_context) { 88 } 89 virtual ~URLRequestContextSelector() {} 90 91 virtual net::URLRequestContext* GetRequestContext( 92 ResourceType::Type resource_type) OVERRIDE { 93 if (resource_type == ResourceType::MEDIA) 94 return media_url_request_context_->GetURLRequestContext(); 95 return url_request_context_->GetURLRequestContext(); 96 } 97 98 private: 99 net::URLRequestContextGetter* url_request_context_; 100 net::URLRequestContextGetter* media_url_request_context_; 101 }; 102 103 } // namespace 104 105 // Notifies RenderViewHost that one or more worker objects crashed. 106 void WorkerCrashCallback(int render_process_unique_id, int render_view_id) { 107 RenderViewHostImpl* host = 108 RenderViewHostImpl::FromID(render_process_unique_id, render_view_id); 109 if (host) 110 host->GetDelegate()->WorkerCrashed(); 111 } 112 113 WorkerProcessHost::WorkerProcessHost( 114 ResourceContext* resource_context, 115 const WorkerStoragePartition& partition) 116 : resource_context_(resource_context), 117 partition_(partition), 118 process_launched_(false) { 119 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 120 DCHECK(resource_context_); 121 process_.reset( 122 new BrowserChildProcessHostImpl(PROCESS_TYPE_WORKER, this)); 123 } 124 125 WorkerProcessHost::~WorkerProcessHost() { 126 // If we crashed, tell the RenderViewHosts. 127 for (Instances::iterator i = instances_.begin(); i != instances_.end(); ++i) { 128 const WorkerDocumentSet::DocumentInfoSet& parents = 129 i->worker_document_set()->documents(); 130 for (WorkerDocumentSet::DocumentInfoSet::const_iterator parent_iter = 131 parents.begin(); parent_iter != parents.end(); ++parent_iter) { 132 BrowserThread::PostTask( 133 BrowserThread::UI, FROM_HERE, 134 base::Bind(&WorkerCrashCallback, parent_iter->render_process_id(), 135 parent_iter->render_view_id())); 136 } 137 WorkerServiceImpl::GetInstance()->NotifyWorkerDestroyed( 138 this, i->worker_route_id()); 139 } 140 141 ChildProcessSecurityPolicyImpl::GetInstance()->Remove( 142 process_->GetData().id); 143 } 144 145 bool WorkerProcessHost::Send(IPC::Message* message) { 146 return process_->Send(message); 147 } 148 149 bool WorkerProcessHost::Init(int render_process_id) { 150 std::string channel_id = process_->GetHost()->CreateChannel(); 151 if (channel_id.empty()) 152 return false; 153 154 #if defined(OS_LINUX) 155 int flags = ChildProcessHost::CHILD_ALLOW_SELF; 156 #else 157 int flags = ChildProcessHost::CHILD_NORMAL; 158 #endif 159 160 base::FilePath exe_path = ChildProcessHost::GetChildPath(flags); 161 if (exe_path.empty()) 162 return false; 163 164 CommandLine* cmd_line = new CommandLine(exe_path); 165 cmd_line->AppendSwitchASCII(switches::kProcessType, switches::kWorkerProcess); 166 cmd_line->AppendSwitchASCII(switches::kProcessChannelID, channel_id); 167 std::string locale = GetContentClient()->browser()->GetApplicationLocale(); 168 cmd_line->AppendSwitchASCII(switches::kLang, locale); 169 170 static const char* const kSwitchNames[] = { 171 switches::kDisableApplicationCache, 172 switches::kDisableDatabases, 173 #if defined(OS_WIN) 174 switches::kDisableDesktopNotifications, 175 #endif 176 switches::kDisableFileSystem, 177 switches::kDisableSeccompFilterSandbox, 178 switches::kEnableExperimentalWebPlatformFeatures, 179 #if defined(OS_MACOSX) 180 switches::kEnableSandboxLogging, 181 #endif 182 }; 183 cmd_line->CopySwitchesFrom(*CommandLine::ForCurrentProcess(), kSwitchNames, 184 arraysize(kSwitchNames)); 185 186 #if defined(OS_POSIX) 187 bool use_zygote = true; 188 189 if (CommandLine::ForCurrentProcess()->HasSwitch( 190 switches::kWaitForDebuggerChildren)) { 191 // Look to pass-on the kWaitForDebugger flag. 192 std::string value = CommandLine::ForCurrentProcess()->GetSwitchValueASCII( 193 switches::kWaitForDebuggerChildren); 194 if (value.empty() || value == switches::kWorkerProcess) { 195 cmd_line->AppendSwitch(switches::kWaitForDebugger); 196 use_zygote = false; 197 } 198 } 199 200 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kDebugChildren)) { 201 // Look to pass-on the kDebugOnStart flag. 202 std::string value = CommandLine::ForCurrentProcess()->GetSwitchValueASCII( 203 switches::kDebugChildren); 204 if (value.empty() || value == switches::kWorkerProcess) { 205 // launches a new xterm, and runs the worker process in gdb, reading 206 // optional commands from gdb_chrome file in the working directory. 207 cmd_line->PrependWrapper("xterm -e gdb -x gdb_chrome --args"); 208 use_zygote = false; 209 } 210 } 211 #endif 212 213 process_->Launch( 214 #if defined(OS_WIN) 215 new WorkerSandboxedProcessLauncherDelegate, 216 #elif defined(OS_POSIX) 217 use_zygote, 218 base::EnvironmentVector(), 219 #endif 220 cmd_line); 221 222 ChildProcessSecurityPolicyImpl::GetInstance()->AddWorker( 223 process_->GetData().id, render_process_id); 224 CreateMessageFilters(render_process_id); 225 226 return true; 227 } 228 229 void WorkerProcessHost::CreateMessageFilters(int render_process_id) { 230 ChromeBlobStorageContext* blob_storage_context = 231 GetChromeBlobStorageContextForResourceContext(resource_context_); 232 StreamContext* stream_context = 233 GetStreamContextForResourceContext(resource_context_); 234 235 net::URLRequestContextGetter* url_request_context = 236 partition_.url_request_context(); 237 net::URLRequestContextGetter* media_url_request_context = 238 partition_.url_request_context(); 239 240 ResourceMessageFilter* resource_message_filter = new ResourceMessageFilter( 241 process_->GetData().id, PROCESS_TYPE_WORKER, resource_context_, 242 partition_.appcache_service(), 243 blob_storage_context, 244 partition_.filesystem_context(), 245 new URLRequestContextSelector(url_request_context, 246 media_url_request_context)); 247 process_->GetHost()->AddFilter(resource_message_filter); 248 249 worker_message_filter_ = new WorkerMessageFilter( 250 render_process_id, resource_context_, partition_, 251 base::Bind(&WorkerServiceImpl::next_worker_route_id, 252 base::Unretained(WorkerServiceImpl::GetInstance()))); 253 process_->GetHost()->AddFilter(worker_message_filter_.get()); 254 process_->GetHost()->AddFilter(new AppCacheDispatcherHost( 255 partition_.appcache_service(), process_->GetData().id)); 256 process_->GetHost()->AddFilter(new FileAPIMessageFilter( 257 process_->GetData().id, 258 url_request_context, 259 partition_.filesystem_context(), 260 blob_storage_context, 261 stream_context)); 262 process_->GetHost()->AddFilter(new FileUtilitiesMessageFilter( 263 process_->GetData().id)); 264 process_->GetHost()->AddFilter(new MimeRegistryMessageFilter()); 265 process_->GetHost()->AddFilter( 266 new DatabaseMessageFilter(partition_.database_tracker())); 267 process_->GetHost()->AddFilter(new QuotaDispatcherHost( 268 process_->GetData().id, 269 partition_.quota_manager(), 270 GetContentClient()->browser()->CreateQuotaPermissionContext())); 271 272 SocketStreamDispatcherHost* socket_stream_dispatcher_host = 273 new SocketStreamDispatcherHost( 274 render_process_id, 275 new URLRequestContextSelector(url_request_context, 276 media_url_request_context), 277 resource_context_); 278 socket_stream_dispatcher_host_ = socket_stream_dispatcher_host; 279 process_->GetHost()->AddFilter(socket_stream_dispatcher_host); 280 process_->GetHost()->AddFilter( 281 new WorkerDevToolsMessageFilter(process_->GetData().id)); 282 process_->GetHost()->AddFilter(new IndexedDBDispatcherHost( 283 process_->GetData().id, partition_.indexed_db_context())); 284 } 285 286 void WorkerProcessHost::CreateWorker(const WorkerInstance& instance) { 287 ChildProcessSecurityPolicyImpl::GetInstance()->GrantRequestURL( 288 process_->GetData().id, instance.url()); 289 290 instances_.push_back(instance); 291 292 WorkerProcessMsg_CreateWorker_Params params; 293 params.url = instance.url(); 294 params.name = instance.name(); 295 params.route_id = instance.worker_route_id(); 296 params.creator_process_id = instance.parent_process_id(); 297 params.shared_worker_appcache_id = instance.main_resource_appcache_id(); 298 Send(new WorkerProcessMsg_CreateWorker(params)); 299 300 UpdateTitle(); 301 302 // Walk all pending filters and let them know the worker has been created 303 // (could be more than one in the case where we had to queue up worker 304 // creation because the worker process limit was reached). 305 for (WorkerInstance::FilterList::const_iterator i = 306 instance.filters().begin(); 307 i != instance.filters().end(); ++i) { 308 CHECK(i->first); 309 i->first->Send(new ViewMsg_WorkerCreated(i->second)); 310 } 311 } 312 313 bool WorkerProcessHost::FilterMessage(const IPC::Message& message, 314 WorkerMessageFilter* filter) { 315 for (Instances::iterator i = instances_.begin(); i != instances_.end(); ++i) { 316 if (!i->closed() && i->HasFilter(filter, message.routing_id())) { 317 RelayMessage(message, worker_message_filter_.get(), i->worker_route_id()); 318 return true; 319 } 320 } 321 322 return false; 323 } 324 325 void WorkerProcessHost::OnProcessLaunched() { 326 process_launched_ = true; 327 328 WorkerServiceImpl::GetInstance()->NotifyWorkerProcessCreated(); 329 } 330 331 bool WorkerProcessHost::OnMessageReceived(const IPC::Message& message) { 332 bool msg_is_ok = true; 333 bool handled = true; 334 IPC_BEGIN_MESSAGE_MAP_EX(WorkerProcessHost, message, msg_is_ok) 335 IPC_MESSAGE_HANDLER(WorkerHostMsg_WorkerContextClosed, 336 OnWorkerContextClosed) 337 IPC_MESSAGE_HANDLER(WorkerProcessHostMsg_AllowDatabase, OnAllowDatabase) 338 IPC_MESSAGE_HANDLER(WorkerProcessHostMsg_AllowFileSystem, OnAllowFileSystem) 339 IPC_MESSAGE_HANDLER(WorkerProcessHostMsg_AllowIndexedDB, OnAllowIndexedDB) 340 IPC_MESSAGE_UNHANDLED(handled = false) 341 IPC_END_MESSAGE_MAP_EX() 342 343 if (!msg_is_ok) { 344 NOTREACHED(); 345 RecordAction(UserMetricsAction("BadMessageTerminate_WPH")); 346 base::KillProcess( 347 process_->GetData().handle, RESULT_CODE_KILLED_BAD_MESSAGE, false); 348 } 349 350 if (handled) 351 return true; 352 353 if (message.type() == WorkerHostMsg_WorkerContextDestroyed::ID) { 354 WorkerServiceImpl::GetInstance()->NotifyWorkerDestroyed( 355 this, message.routing_id()); 356 } 357 358 for (Instances::iterator i = instances_.begin(); i != instances_.end(); ++i) { 359 if (i->worker_route_id() == message.routing_id()) { 360 if (message.type() == WorkerHostMsg_WorkerContextDestroyed::ID) { 361 instances_.erase(i); 362 UpdateTitle(); 363 } 364 return true; 365 } 366 } 367 return false; 368 } 369 370 // Sent to notify the browser process when a worker context invokes close(), so 371 // no new connections are sent to shared workers. 372 void WorkerProcessHost::OnWorkerContextClosed(int worker_route_id) { 373 for (Instances::iterator i = instances_.begin(); i != instances_.end(); ++i) { 374 if (i->worker_route_id() == worker_route_id) { 375 // Set the closed flag - this will stop any further messages from 376 // being sent to the worker (messages can still be sent from the worker, 377 // for exception reporting, etc). 378 i->set_closed(true); 379 break; 380 } 381 } 382 } 383 384 void WorkerProcessHost::OnAllowDatabase(int worker_route_id, 385 const GURL& url, 386 const string16& name, 387 const string16& display_name, 388 unsigned long estimated_size, 389 bool* result) { 390 *result = GetContentClient()->browser()->AllowWorkerDatabase( 391 url, name, display_name, estimated_size, resource_context_, 392 GetRenderViewIDsForWorker(worker_route_id)); 393 } 394 395 void WorkerProcessHost::OnAllowFileSystem(int worker_route_id, 396 const GURL& url, 397 bool* result) { 398 *result = GetContentClient()->browser()->AllowWorkerFileSystem( 399 url, resource_context_, GetRenderViewIDsForWorker(worker_route_id)); 400 } 401 402 void WorkerProcessHost::OnAllowIndexedDB(int worker_route_id, 403 const GURL& url, 404 const string16& name, 405 bool* result) { 406 *result = GetContentClient()->browser()->AllowWorkerIndexedDB( 407 url, name, resource_context_, GetRenderViewIDsForWorker(worker_route_id)); 408 } 409 410 void WorkerProcessHost::RelayMessage( 411 const IPC::Message& message, 412 WorkerMessageFilter* filter, 413 int route_id) { 414 if (message.type() == WorkerMsg_PostMessage::ID) { 415 // We want to send the receiver a routing id for the new channel, so 416 // crack the message first. 417 string16 msg; 418 std::vector<int> sent_message_port_ids; 419 std::vector<int> new_routing_ids; 420 if (!WorkerMsg_PostMessage::Read( 421 &message, &msg, &sent_message_port_ids, &new_routing_ids)) { 422 return; 423 } 424 if (sent_message_port_ids.size() != new_routing_ids.size()) 425 return; 426 427 for (size_t i = 0; i < sent_message_port_ids.size(); ++i) { 428 new_routing_ids[i] = filter->GetNextRoutingID(); 429 MessagePortService::GetInstance()->UpdateMessagePort( 430 sent_message_port_ids[i], filter, new_routing_ids[i]); 431 } 432 433 filter->Send(new WorkerMsg_PostMessage( 434 route_id, msg, sent_message_port_ids, new_routing_ids)); 435 436 // Send any queued messages to the sent message ports. We can only do this 437 // after sending the above message, since it's the one that sets up the 438 // message port route which the queued messages are sent to. 439 for (size_t i = 0; i < sent_message_port_ids.size(); ++i) { 440 MessagePortService::GetInstance()-> 441 SendQueuedMessagesIfPossible(sent_message_port_ids[i]); 442 } 443 } else if (message.type() == WorkerMsg_Connect::ID) { 444 // Crack the SharedWorker Connect message to setup routing for the port. 445 int sent_message_port_id; 446 int new_routing_id; 447 if (!WorkerMsg_Connect::Read( 448 &message, &sent_message_port_id, &new_routing_id)) { 449 return; 450 } 451 new_routing_id = filter->GetNextRoutingID(); 452 MessagePortService::GetInstance()->UpdateMessagePort( 453 sent_message_port_id, filter, new_routing_id); 454 455 // Resend the message with the new routing id. 456 filter->Send(new WorkerMsg_Connect( 457 route_id, sent_message_port_id, new_routing_id)); 458 459 // Send any queued messages for the sent port. 460 MessagePortService::GetInstance()->SendQueuedMessagesIfPossible( 461 sent_message_port_id); 462 } else { 463 IPC::Message* new_message = new IPC::Message(message); 464 new_message->set_routing_id(route_id); 465 filter->Send(new_message); 466 if (message.type() == WorkerMsg_StartWorkerContext::ID) { 467 WorkerDevToolsManager::GetInstance()->WorkerContextStarted( 468 this, route_id); 469 } 470 return; 471 } 472 } 473 474 void WorkerProcessHost::ShutdownSocketStreamDispatcherHostIfNecessary() { 475 if (!instances_.size() && socket_stream_dispatcher_host_.get()) { 476 // We can assume that this object is going to delete, because 477 // currently a WorkerInstance will never be added to a WorkerProcessHost 478 // once it is initialized. 479 480 // SocketStreamDispatcherHost should be notified now that the worker 481 // process will shutdown soon. 482 socket_stream_dispatcher_host_->Shutdown(); 483 socket_stream_dispatcher_host_ = NULL; 484 } 485 } 486 487 void WorkerProcessHost::FilterShutdown(WorkerMessageFilter* filter) { 488 for (Instances::iterator i = instances_.begin(); i != instances_.end();) { 489 bool shutdown = false; 490 i->RemoveFilters(filter); 491 492 i->worker_document_set()->RemoveAll(filter); 493 if (i->worker_document_set()->IsEmpty()) { 494 shutdown = true; 495 } 496 if (shutdown) { 497 Send(new WorkerMsg_TerminateWorkerContext(i->worker_route_id())); 498 i = instances_.erase(i); 499 } else { 500 ++i; 501 } 502 } 503 ShutdownSocketStreamDispatcherHostIfNecessary(); 504 } 505 506 bool WorkerProcessHost::CanShutdown() { 507 return instances_.empty(); 508 } 509 510 void WorkerProcessHost::UpdateTitle() { 511 std::set<std::string> titles; 512 for (Instances::iterator i = instances_.begin(); i != instances_.end(); ++i) { 513 // Allow the embedder first crack at special casing the title. 514 std::string title = GetContentClient()->browser()-> 515 GetWorkerProcessTitle(i->url(), resource_context_); 516 517 if (title.empty()) { 518 title = net::registry_controlled_domains::GetDomainAndRegistry( 519 i->url(), 520 net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES); 521 } 522 523 // Use the host name if the domain is empty, i.e. localhost or IP address. 524 if (title.empty()) 525 title = i->url().host(); 526 527 // If the host name is empty, i.e. file url, use the path. 528 if (title.empty()) 529 title = i->url().path(); 530 titles.insert(title); 531 } 532 533 std::string display_title; 534 for (std::set<std::string>::iterator i = titles.begin(); 535 i != titles.end(); ++i) { 536 if (!display_title.empty()) 537 display_title += ", "; 538 display_title += *i; 539 } 540 541 process_->SetName(UTF8ToUTF16(display_title)); 542 } 543 544 void WorkerProcessHost::DocumentDetached(WorkerMessageFilter* filter, 545 unsigned long long document_id) { 546 // Walk all instances and remove the document from their document set. 547 for (Instances::iterator i = instances_.begin(); i != instances_.end();) { 548 i->worker_document_set()->Remove(filter, document_id); 549 if (i->worker_document_set()->IsEmpty()) { 550 // This worker has no more associated documents - shut it down. 551 Send(new WorkerMsg_TerminateWorkerContext(i->worker_route_id())); 552 i = instances_.erase(i); 553 } else { 554 ++i; 555 } 556 } 557 ShutdownSocketStreamDispatcherHostIfNecessary(); 558 } 559 560 void WorkerProcessHost::TerminateWorker(int worker_route_id) { 561 Send(new WorkerMsg_TerminateWorkerContext(worker_route_id)); 562 } 563 564 void WorkerProcessHost::SetBackgrounded(bool backgrounded) { 565 process_->SetBackgrounded(backgrounded); 566 } 567 568 const ChildProcessData& WorkerProcessHost::GetData() { 569 return process_->GetData(); 570 } 571 572 std::vector<std::pair<int, int> > WorkerProcessHost::GetRenderViewIDsForWorker( 573 int worker_route_id) { 574 std::vector<std::pair<int, int> > result; 575 WorkerProcessHost::Instances::const_iterator i; 576 for (i = instances_.begin(); i != instances_.end(); ++i) { 577 if (i->worker_route_id() != worker_route_id) 578 continue; 579 const WorkerDocumentSet::DocumentInfoSet& documents = 580 i->worker_document_set()->documents(); 581 for (WorkerDocumentSet::DocumentInfoSet::const_iterator doc = 582 documents.begin(); doc != documents.end(); ++doc) { 583 result.push_back( 584 std::make_pair(doc->render_process_id(), doc->render_view_id())); 585 } 586 break; 587 } 588 return result; 589 } 590 591 WorkerProcessHost::WorkerInstance::WorkerInstance( 592 const GURL& url, 593 const string16& name, 594 int worker_route_id, 595 int parent_process_id, 596 int64 main_resource_appcache_id, 597 ResourceContext* resource_context, 598 const WorkerStoragePartition& partition) 599 : url_(url), 600 closed_(false), 601 name_(name), 602 worker_route_id_(worker_route_id), 603 parent_process_id_(parent_process_id), 604 main_resource_appcache_id_(main_resource_appcache_id), 605 worker_document_set_(new WorkerDocumentSet()), 606 resource_context_(resource_context), 607 partition_(partition) { 608 DCHECK(resource_context_); 609 } 610 611 WorkerProcessHost::WorkerInstance::WorkerInstance( 612 const GURL& url, 613 bool shared, 614 const string16& name, 615 ResourceContext* resource_context, 616 const WorkerStoragePartition& partition) 617 : url_(url), 618 closed_(false), 619 name_(name), 620 worker_route_id_(MSG_ROUTING_NONE), 621 parent_process_id_(0), 622 main_resource_appcache_id_(0), 623 worker_document_set_(new WorkerDocumentSet()), 624 resource_context_(resource_context), 625 partition_(partition) { 626 DCHECK(resource_context_); 627 } 628 629 WorkerProcessHost::WorkerInstance::~WorkerInstance() { 630 } 631 632 // Compares an instance based on the algorithm in the WebWorkers spec - an 633 // instance matches if the origins of the URLs match, and: 634 // a) the names are non-empty and equal 635 // -or- 636 // b) the names are both empty, and the urls are equal 637 bool WorkerProcessHost::WorkerInstance::Matches( 638 const GURL& match_url, 639 const string16& match_name, 640 const WorkerStoragePartition& partition, 641 ResourceContext* resource_context) const { 642 // Only match open shared workers. 643 if (closed_) 644 return false; 645 646 // ResourceContext equivalence is being used as a proxy to ensure we only 647 // matched shared workers within the same BrowserContext. 648 if (resource_context_ != resource_context) 649 return false; 650 651 // We must be in the same storage partition otherwise sharing will violate 652 // isolation. 653 if (!partition_.Equals(partition)) 654 return false; 655 656 if (url_.GetOrigin() != match_url.GetOrigin()) 657 return false; 658 659 if (name_.empty() && match_name.empty()) 660 return url_ == match_url; 661 662 return name_ == match_name; 663 } 664 665 void WorkerProcessHost::WorkerInstance::AddFilter(WorkerMessageFilter* filter, 666 int route_id) { 667 CHECK(filter); 668 if (!HasFilter(filter, route_id)) { 669 FilterInfo info(filter, route_id); 670 filters_.push_back(info); 671 } 672 } 673 674 void WorkerProcessHost::WorkerInstance::RemoveFilter( 675 WorkerMessageFilter* filter, int route_id) { 676 for (FilterList::iterator i = filters_.begin(); i != filters_.end();) { 677 if (i->first == filter && i->second == route_id) 678 i = filters_.erase(i); 679 else 680 ++i; 681 } 682 // Should not be duplicate copies in the filter set. 683 DCHECK(!HasFilter(filter, route_id)); 684 } 685 686 void WorkerProcessHost::WorkerInstance::RemoveFilters( 687 WorkerMessageFilter* filter) { 688 for (FilterList::iterator i = filters_.begin(); i != filters_.end();) { 689 if (i->first == filter) 690 i = filters_.erase(i); 691 else 692 ++i; 693 } 694 } 695 696 bool WorkerProcessHost::WorkerInstance::HasFilter( 697 WorkerMessageFilter* filter, int route_id) const { 698 for (FilterList::const_iterator i = filters_.begin(); i != filters_.end(); 699 ++i) { 700 if (i->first == filter && i->second == route_id) 701 return true; 702 } 703 return false; 704 } 705 706 bool WorkerProcessHost::WorkerInstance::RendererIsParent( 707 int render_process_id, int render_view_id) const { 708 const WorkerDocumentSet::DocumentInfoSet& parents = 709 worker_document_set()->documents(); 710 for (WorkerDocumentSet::DocumentInfoSet::const_iterator parent_iter = 711 parents.begin(); 712 parent_iter != parents.end(); ++parent_iter) { 713 if (parent_iter->render_process_id() == render_process_id && 714 parent_iter->render_view_id() == render_view_id) { 715 return true; 716 } 717 } 718 return false; 719 } 720 721 WorkerProcessHost::WorkerInstance::FilterInfo 722 WorkerProcessHost::WorkerInstance::GetFilter() const { 723 DCHECK(NumFilters() == 1); 724 return *filters_.begin(); 725 } 726 727 } // namespace content 728