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