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.
5 #include "content/browser/worker_host/worker_process_host.h"
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/frame_host/render_frame_host_delegate.h"
27 #include "content/browser/frame_host/render_frame_host_impl.h"
28 #include "content/browser/indexed_db/indexed_db_dispatcher_host.h"
29 #include "content/browser/loader/resource_message_filter.h"
30 #include "content/browser/message_port_message_filter.h"
31 #include "content/browser/message_port_service.h"
32 #include "content/browser/mime_registry_message_filter.h"
33 #include "content/browser/quota_dispatcher_host.h"
34 #include "content/browser/renderer_host/database_message_filter.h"
35 #include "content/browser/renderer_host/file_utilities_message_filter.h"
36 #include "content/browser/renderer_host/render_view_host_delegate.h"
37 #include "content/browser/renderer_host/render_view_host_impl.h"
38 #include "content/browser/renderer_host/socket_stream_dispatcher_host.h"
39 #include "content/browser/resource_context_impl.h"
40 #include "content/browser/worker_host/worker_message_filter.h"
41 #include "content/browser/worker_host/worker_service_impl.h"
42 #include "content/common/child_process_host_impl.h"
43 #include "content/common/view_messages.h"
44 #include "content/common/worker_messages.h"
45 #include "content/public/browser/browser_thread.h"
46 #include "content/public/browser/content_browser_client.h"
47 #include "content/public/browser/user_metrics.h"
48 #include "content/public/common/content_switches.h"
49 #include "content/public/common/result_codes.h"
50 #include "ipc/ipc_switches.h"
51 #include "net/base/mime_util.h"
52 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
53 #include "net/url_request/url_request_context_getter.h"
54 #include "ui/base/ui_base_switches.h"
55 #include "webkit/browser/fileapi/file_system_context.h"
56 #include "webkit/browser/fileapi/sandbox_file_system_backend.h"
57 #include "webkit/common/resource_type.h"
60 #include "content/common/sandbox_win.h"
61 #include "content/public/common/sandboxed_process_launcher_delegate.h"
68 // NOTE: changes to this class need to be reviewed by the security team.
69 class WorkerSandboxedProcessLauncherDelegate
70 : public content::SandboxedProcessLauncherDelegate
{
72 WorkerSandboxedProcessLauncherDelegate() {}
73 virtual ~WorkerSandboxedProcessLauncherDelegate() {}
75 virtual void PreSpawnTarget(sandbox::TargetPolicy
* policy
,
77 AddBaseHandleClosePolicy(policy
);
84 // Notifies RenderViewHost that one or more worker objects crashed.
85 void WorkerCrashCallback(int render_process_unique_id
, int render_frame_id
) {
86 RenderFrameHostImpl
* host
=
87 RenderFrameHostImpl::FromID(render_process_unique_id
, render_frame_id
);
89 host
->delegate()->WorkerCrashed();
92 WorkerProcessHost::WorkerProcessHost(
93 ResourceContext
* resource_context
,
94 const WorkerStoragePartition
& partition
)
95 : resource_context_(resource_context
),
96 partition_(partition
),
97 process_launched_(false) {
98 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
99 DCHECK(resource_context_
);
101 new BrowserChildProcessHostImpl(PROCESS_TYPE_WORKER
, this));
104 WorkerProcessHost::~WorkerProcessHost() {
105 // If we crashed, tell the RenderViewHosts.
106 for (Instances::iterator i
= instances_
.begin(); i
!= instances_
.end(); ++i
) {
107 const WorkerDocumentSet::DocumentInfoSet
& parents
=
108 i
->worker_document_set()->documents();
109 for (WorkerDocumentSet::DocumentInfoSet::const_iterator parent_iter
=
110 parents
.begin(); parent_iter
!= parents
.end(); ++parent_iter
) {
111 BrowserThread::PostTask(
112 BrowserThread::UI
, FROM_HERE
,
113 base::Bind(&WorkerCrashCallback
, parent_iter
->render_process_id(),
114 parent_iter
->render_frame_id()));
116 WorkerServiceImpl::GetInstance()->NotifyWorkerDestroyed(
117 this, i
->worker_route_id());
120 ChildProcessSecurityPolicyImpl::GetInstance()->Remove(
121 process_
->GetData().id
);
124 bool WorkerProcessHost::Send(IPC::Message
* message
) {
125 return process_
->Send(message
);
128 bool WorkerProcessHost::Init(int render_process_id
) {
129 std::string channel_id
= process_
->GetHost()->CreateChannel();
130 if (channel_id
.empty())
133 #if defined(OS_LINUX)
134 int flags
= ChildProcessHost::CHILD_ALLOW_SELF
;
136 int flags
= ChildProcessHost::CHILD_NORMAL
;
139 base::FilePath exe_path
= ChildProcessHost::GetChildPath(flags
);
140 if (exe_path
.empty())
143 CommandLine
* cmd_line
= new CommandLine(exe_path
);
144 cmd_line
->AppendSwitchASCII(switches::kProcessType
, switches::kWorkerProcess
);
145 cmd_line
->AppendSwitchASCII(switches::kProcessChannelID
, channel_id
);
146 std::string locale
= GetContentClient()->browser()->GetApplicationLocale();
147 cmd_line
->AppendSwitchASCII(switches::kLang
, locale
);
149 static const char* const kSwitchNames
[] = {
150 switches::kDisableApplicationCache
,
151 switches::kDisableDatabases
,
153 switches::kDisableDesktopNotifications
,
155 switches::kDisableFileSystem
,
156 switches::kDisableSeccompFilterSandbox
,
157 switches::kEnableExperimentalWebPlatformFeatures
,
158 switches::kEnableServiceWorker
,
159 #if defined(OS_MACOSX)
160 switches::kEnableSandboxLogging
,
162 switches::kJavaScriptFlags
,
165 cmd_line
->CopySwitchesFrom(*CommandLine::ForCurrentProcess(), kSwitchNames
,
166 arraysize(kSwitchNames
));
168 #if defined(OS_POSIX)
169 bool use_zygote
= true;
171 if (CommandLine::ForCurrentProcess()->HasSwitch(
172 switches::kWaitForDebuggerChildren
)) {
173 // Look to pass-on the kWaitForDebugger flag.
174 std::string value
= CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
175 switches::kWaitForDebuggerChildren
);
176 if (value
.empty() || value
== switches::kWorkerProcess
) {
177 cmd_line
->AppendSwitch(switches::kWaitForDebugger
);
182 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kDebugChildren
)) {
183 // Look to pass-on the kDebugOnStart flag.
184 std::string value
= CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
185 switches::kDebugChildren
);
186 if (value
.empty() || value
== switches::kWorkerProcess
) {
187 // launches a new xterm, and runs the worker process in gdb, reading
188 // optional commands from gdb_chrome file in the working directory.
189 cmd_line
->PrependWrapper("xterm -e gdb -x gdb_chrome --args");
197 new WorkerSandboxedProcessLauncherDelegate
,
198 #elif defined(OS_POSIX)
200 base::EnvironmentMap(),
204 ChildProcessSecurityPolicyImpl::GetInstance()->AddWorker(
205 process_
->GetData().id
, render_process_id
);
206 CreateMessageFilters(render_process_id
);
211 void WorkerProcessHost::CreateMessageFilters(int render_process_id
) {
212 ChromeBlobStorageContext
* blob_storage_context
=
213 GetChromeBlobStorageContextForResourceContext(resource_context_
);
214 StreamContext
* stream_context
=
215 GetStreamContextForResourceContext(resource_context_
);
217 net::URLRequestContextGetter
* url_request_context
=
218 partition_
.url_request_context();
220 ResourceMessageFilter::GetContextsCallback
get_contexts_callback(
221 base::Bind(&WorkerProcessHost::GetContexts
,
222 base::Unretained(this)));
224 ResourceMessageFilter
* resource_message_filter
= new ResourceMessageFilter(
225 process_
->GetData().id
, PROCESS_TYPE_WORKER
,
226 partition_
.appcache_service(),
227 blob_storage_context
,
228 partition_
.filesystem_context(),
229 get_contexts_callback
);
230 process_
->AddFilter(resource_message_filter
);
232 MessagePortMessageFilter
* message_port_message_filter
=
233 new MessagePortMessageFilter(
234 base::Bind(&WorkerServiceImpl::next_worker_route_id
,
235 base::Unretained(WorkerServiceImpl::GetInstance())));
236 process_
->AddFilter(message_port_message_filter
);
237 worker_message_filter_
= new WorkerMessageFilter(render_process_id
,
240 message_port_message_filter
);
241 process_
->AddFilter(worker_message_filter_
.get());
242 process_
->AddFilter(new AppCacheDispatcherHost(
243 partition_
.appcache_service(), process_
->GetData().id
));
244 process_
->AddFilter(new FileAPIMessageFilter(
245 process_
->GetData().id
,
247 partition_
.filesystem_context(),
248 blob_storage_context
,
250 process_
->AddFilter(new FileUtilitiesMessageFilter(
251 process_
->GetData().id
));
252 process_
->AddFilter(new MimeRegistryMessageFilter());
253 process_
->AddFilter(new DatabaseMessageFilter(partition_
.database_tracker()));
254 process_
->AddFilter(new QuotaDispatcherHost(
255 process_
->GetData().id
,
256 partition_
.quota_manager(),
257 GetContentClient()->browser()->CreateQuotaPermissionContext()));
259 SocketStreamDispatcherHost::GetRequestContextCallback
260 request_context_callback(
261 base::Bind(&WorkerProcessHost::GetRequestContext
,
262 base::Unretained(this)));
264 SocketStreamDispatcherHost
* socket_stream_dispatcher_host
=
265 new SocketStreamDispatcherHost(
267 request_context_callback
,
269 socket_stream_dispatcher_host_
= socket_stream_dispatcher_host
;
270 process_
->AddFilter(socket_stream_dispatcher_host
);
271 process_
->AddFilter(new WorkerDevToolsMessageFilter(process_
->GetData().id
));
273 new IndexedDBDispatcherHost(partition_
.indexed_db_context()));
276 void WorkerProcessHost::CreateWorker(const WorkerInstance
& instance
) {
277 ChildProcessSecurityPolicyImpl::GetInstance()->GrantRequestURL(
278 process_
->GetData().id
, instance
.url());
280 instances_
.push_back(instance
);
282 WorkerProcessMsg_CreateWorker_Params params
;
283 params
.url
= instance
.url();
284 params
.name
= instance
.name();
285 params
.content_security_policy
= instance
.content_security_policy();
286 params
.security_policy_type
= instance
.security_policy_type();
287 params
.route_id
= instance
.worker_route_id();
288 params
.creator_process_id
= instance
.parent_process_id();
289 params
.shared_worker_appcache_id
= instance
.main_resource_appcache_id();
290 Send(new WorkerProcessMsg_CreateWorker(params
));
294 // Walk all pending filters and let them know the worker has been created
295 // (could be more than one in the case where we had to queue up worker
296 // creation because the worker process limit was reached).
297 for (WorkerInstance::FilterList::const_iterator i
=
298 instance
.filters().begin();
299 i
!= instance
.filters().end(); ++i
) {
301 i
->first
->Send(new ViewMsg_WorkerCreated(i
->second
));
305 bool WorkerProcessHost::FilterMessage(const IPC::Message
& message
,
306 WorkerMessageFilter
* filter
) {
307 for (Instances::iterator i
= instances_
.begin(); i
!= instances_
.end(); ++i
) {
308 if (!i
->closed() && i
->HasFilter(filter
, message
.routing_id())) {
309 RelayMessage(message
, worker_message_filter_
.get(), i
->worker_route_id());
317 void WorkerProcessHost::OnProcessLaunched() {
318 process_launched_
= true;
320 WorkerServiceImpl::GetInstance()->NotifyWorkerProcessCreated();
323 bool WorkerProcessHost::OnMessageReceived(const IPC::Message
& message
) {
324 bool msg_is_ok
= true;
326 IPC_BEGIN_MESSAGE_MAP_EX(WorkerProcessHost
, message
, msg_is_ok
)
327 IPC_MESSAGE_HANDLER(WorkerHostMsg_WorkerContextClosed
,
328 OnWorkerContextClosed
)
329 IPC_MESSAGE_HANDLER(WorkerProcessHostMsg_AllowDatabase
, OnAllowDatabase
)
330 IPC_MESSAGE_HANDLER(WorkerProcessHostMsg_AllowFileSystem
, OnAllowFileSystem
)
331 IPC_MESSAGE_HANDLER(WorkerProcessHostMsg_AllowIndexedDB
, OnAllowIndexedDB
)
332 IPC_MESSAGE_HANDLER(WorkerProcessHostMsg_ForceKillWorker
,
333 OnForceKillWorkerProcess
)
334 IPC_MESSAGE_UNHANDLED(handled
= false)
335 IPC_END_MESSAGE_MAP_EX()
339 RecordAction(UserMetricsAction("BadMessageTerminate_WPH"));
341 process_
->GetData().handle
, RESULT_CODE_KILLED_BAD_MESSAGE
, false);
347 if (message
.type() == WorkerHostMsg_WorkerContextDestroyed::ID
) {
348 WorkerServiceImpl::GetInstance()->NotifyWorkerDestroyed(
349 this, message
.routing_id());
352 for (Instances::iterator i
= instances_
.begin(); i
!= instances_
.end(); ++i
) {
353 if (i
->worker_route_id() == message
.routing_id()) {
354 if (message
.type() == WorkerHostMsg_WorkerContextDestroyed::ID
) {
364 // Sent to notify the browser process when a worker context invokes close(), so
365 // no new connections are sent to shared workers.
366 void WorkerProcessHost::OnWorkerContextClosed(int worker_route_id
) {
367 for (Instances::iterator i
= instances_
.begin(); i
!= instances_
.end(); ++i
) {
368 if (i
->worker_route_id() == worker_route_id
) {
369 // Set the closed flag - this will stop any further messages from
370 // being sent to the worker (messages can still be sent from the worker,
371 // for exception reporting, etc).
378 void WorkerProcessHost::OnAllowDatabase(int worker_route_id
,
380 const base::string16
& name
,
381 const base::string16
& display_name
,
382 unsigned long estimated_size
,
384 *result
= GetContentClient()->browser()->AllowWorkerDatabase(
385 url
, name
, display_name
, estimated_size
, resource_context_
,
386 GetRenderFrameIDsForWorker(worker_route_id
));
389 void WorkerProcessHost::OnAllowFileSystem(int worker_route_id
,
392 *result
= GetContentClient()->browser()->AllowWorkerFileSystem(
393 url
, resource_context_
, GetRenderFrameIDsForWorker(worker_route_id
));
396 void WorkerProcessHost::OnAllowIndexedDB(int worker_route_id
,
398 const base::string16
& name
,
400 *result
= GetContentClient()->browser()->AllowWorkerIndexedDB(
401 url
, name
, resource_context_
,
402 GetRenderFrameIDsForWorker(worker_route_id
));
405 void WorkerProcessHost::OnForceKillWorkerProcess() {
406 if (process_
&& process_launched_
)
408 process_
->GetData().handle
, RESULT_CODE_NORMAL_EXIT
, false);
410 RecordAction(UserMetricsAction("WorkerProcess_BadProcessToKill"));
413 void WorkerProcessHost::RelayMessage(
414 const IPC::Message
& message
,
415 WorkerMessageFilter
* filter
,
417 if (message
.type() == WorkerMsg_Connect::ID
) {
418 // Crack the SharedWorker Connect message to setup routing for the port.
419 int sent_message_port_id
;
421 if (!WorkerMsg_Connect::Read(
422 &message
, &sent_message_port_id
, &new_routing_id
)) {
425 new_routing_id
= filter
->GetNextRoutingID();
426 MessagePortService::GetInstance()->UpdateMessagePort(
427 sent_message_port_id
,
428 filter
->message_port_message_filter(),
431 // Resend the message with the new routing id.
432 filter
->Send(new WorkerMsg_Connect(
433 route_id
, sent_message_port_id
, new_routing_id
));
435 // Send any queued messages for the sent port.
436 MessagePortService::GetInstance()->SendQueuedMessagesIfPossible(
437 sent_message_port_id
);
439 IPC::Message
* new_message
= new IPC::Message(message
);
440 new_message
->set_routing_id(route_id
);
441 filter
->Send(new_message
);
442 if (message
.type() == WorkerMsg_StartWorkerContext::ID
) {
443 WorkerDevToolsManager::GetInstance()->WorkerContextStarted(
450 void WorkerProcessHost::ShutdownSocketStreamDispatcherHostIfNecessary() {
451 if (!instances_
.size() && socket_stream_dispatcher_host_
.get()) {
452 // We can assume that this object is going to delete, because
453 // currently a WorkerInstance will never be added to a WorkerProcessHost
454 // once it is initialized.
456 // SocketStreamDispatcherHost should be notified now that the worker
457 // process will shutdown soon.
458 socket_stream_dispatcher_host_
->Shutdown();
459 socket_stream_dispatcher_host_
= NULL
;
463 void WorkerProcessHost::FilterShutdown(WorkerMessageFilter
* filter
) {
464 for (Instances::iterator i
= instances_
.begin(); i
!= instances_
.end();) {
465 bool shutdown
= false;
466 i
->RemoveFilters(filter
);
468 i
->worker_document_set()->RemoveAll(filter
);
469 if (i
->worker_document_set()->IsEmpty()) {
473 Send(new WorkerMsg_TerminateWorkerContext(i
->worker_route_id()));
474 i
= instances_
.erase(i
);
479 ShutdownSocketStreamDispatcherHostIfNecessary();
482 bool WorkerProcessHost::CanShutdown() {
483 return instances_
.empty();
486 void WorkerProcessHost::UpdateTitle() {
487 std::set
<std::string
> titles
;
488 for (Instances::iterator i
= instances_
.begin(); i
!= instances_
.end(); ++i
) {
489 // Allow the embedder first crack at special casing the title.
490 std::string title
= GetContentClient()->browser()->
491 GetWorkerProcessTitle(i
->url(), resource_context_
);
494 title
= net::registry_controlled_domains::GetDomainAndRegistry(
496 net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES
);
499 // Use the host name if the domain is empty, i.e. localhost or IP address.
501 title
= i
->url().host();
503 // If the host name is empty, i.e. file url, use the path.
505 title
= i
->url().path();
506 titles
.insert(title
);
509 std::string display_title
;
510 for (std::set
<std::string
>::iterator i
= titles
.begin();
511 i
!= titles
.end(); ++i
) {
512 if (!display_title
.empty())
513 display_title
+= ", ";
517 process_
->SetName(base::UTF8ToUTF16(display_title
));
520 void WorkerProcessHost::DocumentDetached(WorkerMessageFilter
* filter
,
521 unsigned long long document_id
) {
522 // Walk all instances and remove the document from their document set.
523 for (Instances::iterator i
= instances_
.begin(); i
!= instances_
.end();) {
524 i
->worker_document_set()->Remove(filter
, document_id
);
525 if (i
->worker_document_set()->IsEmpty()) {
526 // This worker has no more associated documents - shut it down.
527 Send(new WorkerMsg_TerminateWorkerContext(i
->worker_route_id()));
528 i
= instances_
.erase(i
);
533 ShutdownSocketStreamDispatcherHostIfNecessary();
536 void WorkerProcessHost::TerminateWorker(int worker_route_id
) {
537 Send(new WorkerMsg_TerminateWorkerContext(worker_route_id
));
540 void WorkerProcessHost::SetBackgrounded(bool backgrounded
) {
541 process_
->SetBackgrounded(backgrounded
);
544 const ChildProcessData
& WorkerProcessHost::GetData() {
545 return process_
->GetData();
548 std::vector
<std::pair
<int, int> > WorkerProcessHost::GetRenderFrameIDsForWorker(
549 int worker_route_id
) {
550 std::vector
<std::pair
<int, int> > result
;
551 WorkerProcessHost::Instances::const_iterator i
;
552 for (i
= instances_
.begin(); i
!= instances_
.end(); ++i
) {
553 if (i
->worker_route_id() != worker_route_id
)
555 const WorkerDocumentSet::DocumentInfoSet
& documents
=
556 i
->worker_document_set()->documents();
557 for (WorkerDocumentSet::DocumentInfoSet::const_iterator doc
=
558 documents
.begin(); doc
!= documents
.end(); ++doc
) {
560 std::make_pair(doc
->render_process_id(), doc
->render_frame_id()));
567 void WorkerProcessHost::GetContexts(const ResourceHostMsg_Request
& request
,
568 ResourceContext
** resource_context
,
569 net::URLRequestContext
** request_context
) {
570 *resource_context
= resource_context_
;
571 *request_context
= partition_
.url_request_context()->GetURLRequestContext();
574 net::URLRequestContext
* WorkerProcessHost::GetRequestContext(
575 ResourceType::Type resource_type
) {
576 return partition_
.url_request_context()->GetURLRequestContext();
579 WorkerProcessHost::WorkerInstance::WorkerInstance(
581 const base::string16
& name
,
582 const base::string16
& content_security_policy
,
583 blink::WebContentSecurityPolicyType security_policy_type
,
585 int parent_process_id
,
586 int64 main_resource_appcache_id
,
587 ResourceContext
* resource_context
,
588 const WorkerStoragePartition
& partition
)
592 content_security_policy_(content_security_policy
),
593 security_policy_type_(security_policy_type
),
594 worker_route_id_(worker_route_id
),
595 parent_process_id_(parent_process_id
),
596 main_resource_appcache_id_(main_resource_appcache_id
),
597 worker_document_set_(new WorkerDocumentSet()),
598 resource_context_(resource_context
),
599 partition_(partition
) {
600 DCHECK(resource_context_
);
603 WorkerProcessHost::WorkerInstance::WorkerInstance(
606 const base::string16
& name
,
607 ResourceContext
* resource_context
,
608 const WorkerStoragePartition
& partition
)
612 worker_route_id_(MSG_ROUTING_NONE
),
613 parent_process_id_(0),
614 main_resource_appcache_id_(0),
615 worker_document_set_(new WorkerDocumentSet()),
616 resource_context_(resource_context
),
617 partition_(partition
) {
618 DCHECK(resource_context_
);
621 WorkerProcessHost::WorkerInstance::~WorkerInstance() {
624 // Compares an instance based on the algorithm in the WebWorkers spec - an
625 // instance matches if the origins of the URLs match, and:
626 // a) the names are non-empty and equal
628 // b) the names are both empty, and the urls are equal
629 bool WorkerProcessHost::WorkerInstance::Matches(
630 const GURL
& match_url
,
631 const base::string16
& match_name
,
632 const WorkerStoragePartition
& partition
,
633 ResourceContext
* resource_context
) const {
634 // Only match open shared workers.
638 // ResourceContext equivalence is being used as a proxy to ensure we only
639 // matched shared workers within the same BrowserContext.
640 if (resource_context_
!= resource_context
)
643 // We must be in the same storage partition otherwise sharing will violate
645 if (!partition_
.Equals(partition
))
648 if (url_
.GetOrigin() != match_url
.GetOrigin())
651 if (name_
.empty() && match_name
.empty())
652 return url_
== match_url
;
654 return name_
== match_name
;
657 void WorkerProcessHost::WorkerInstance::AddFilter(WorkerMessageFilter
* filter
,
660 if (!HasFilter(filter
, route_id
)) {
661 FilterInfo
info(filter
, route_id
);
662 filters_
.push_back(info
);
666 void WorkerProcessHost::WorkerInstance::RemoveFilter(
667 WorkerMessageFilter
* filter
, int route_id
) {
668 for (FilterList::iterator i
= filters_
.begin(); i
!= filters_
.end();) {
669 if (i
->first
== filter
&& i
->second
== route_id
)
670 i
= filters_
.erase(i
);
674 // Should not be duplicate copies in the filter set.
675 DCHECK(!HasFilter(filter
, route_id
));
678 void WorkerProcessHost::WorkerInstance::RemoveFilters(
679 WorkerMessageFilter
* filter
) {
680 for (FilterList::iterator i
= filters_
.begin(); i
!= filters_
.end();) {
681 if (i
->first
== filter
)
682 i
= filters_
.erase(i
);
688 bool WorkerProcessHost::WorkerInstance::HasFilter(
689 WorkerMessageFilter
* filter
, int route_id
) const {
690 for (FilterList::const_iterator i
= filters_
.begin(); i
!= filters_
.end();
692 if (i
->first
== filter
&& i
->second
== route_id
)
698 bool WorkerProcessHost::WorkerInstance::FrameIsParent(
699 int render_process_id
, int render_frame_id
) const {
700 const WorkerDocumentSet::DocumentInfoSet
& parents
=
701 worker_document_set()->documents();
702 for (WorkerDocumentSet::DocumentInfoSet::const_iterator parent_iter
=
704 parent_iter
!= parents
.end(); ++parent_iter
) {
705 if (parent_iter
->render_process_id() == render_process_id
&&
706 parent_iter
->render_frame_id() == render_frame_id
) {
713 WorkerProcessHost::WorkerInstance::FilterInfo
714 WorkerProcessHost::WorkerInstance::GetFilter() const {
715 DCHECK(NumFilters() == 1);
716 return *filters_
.begin();
719 } // namespace content