OLD | NEW |
| (Empty) |
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_service_impl.h" | |
6 | |
7 #include <string> | |
8 | |
9 #include "base/command_line.h" | |
10 #include "base/logging.h" | |
11 #include "base/threading/thread.h" | |
12 #include "content/browser/devtools/worker_devtools_manager.h" | |
13 #include "content/browser/renderer_host/render_widget_host_impl.h" | |
14 #include "content/browser/shared_worker/shared_worker_service_impl.h" | |
15 #include "content/browser/worker_host/worker_message_filter.h" | |
16 #include "content/browser/worker_host/worker_process_host.h" | |
17 #include "content/common/view_messages.h" | |
18 #include "content/common/worker_messages.h" | |
19 #include "content/public/browser/child_process_data.h" | |
20 #include "content/public/browser/notification_service.h" | |
21 #include "content/public/browser/notification_types.h" | |
22 #include "content/public/browser/render_frame_host.h" | |
23 #include "content/public/browser/render_process_host.h" | |
24 #include "content/public/browser/render_view_host.h" | |
25 #include "content/public/browser/render_widget_host.h" | |
26 #include "content/public/browser/render_widget_host_iterator.h" | |
27 #include "content/public/browser/render_widget_host_view.h" | |
28 #include "content/public/browser/resource_context.h" | |
29 #include "content/public/browser/web_contents.h" | |
30 #include "content/public/browser/worker_service_observer.h" | |
31 #include "content/public/common/content_switches.h" | |
32 #include "content/public/common/process_type.h" | |
33 | |
34 namespace content { | |
35 | |
36 namespace { | |
37 void AddRenderFrameID(std::set<std::pair<int, int> >* visible_frame_ids, | |
38 RenderFrameHost* rfh) { | |
39 visible_frame_ids->insert( | |
40 std::pair<int, int>(rfh->GetProcess()->GetID(), | |
41 rfh->GetRoutingID())); | |
42 } | |
43 } | |
44 | |
45 const int WorkerServiceImpl::kMaxWorkersWhenSeparate = 64; | |
46 const int WorkerServiceImpl::kMaxWorkersPerFrameWhenSeparate = 16; | |
47 | |
48 class WorkerPrioritySetter | |
49 : public NotificationObserver, | |
50 public base::RefCountedThreadSafe<WorkerPrioritySetter, | |
51 BrowserThread::DeleteOnUIThread> { | |
52 public: | |
53 WorkerPrioritySetter(); | |
54 | |
55 // Posts a task to the UI thread to register to receive notifications. | |
56 void Initialize(); | |
57 | |
58 // Invoked by WorkerServiceImpl when a worker process is created. | |
59 void NotifyWorkerProcessCreated(); | |
60 | |
61 private: | |
62 friend class base::RefCountedThreadSafe<WorkerPrioritySetter>; | |
63 friend struct BrowserThread::DeleteOnThread<BrowserThread::UI>; | |
64 friend class base::DeleteHelper<WorkerPrioritySetter>; | |
65 virtual ~WorkerPrioritySetter(); | |
66 | |
67 // Posts a task to perform a worker priority update. | |
68 void PostTaskToGatherAndUpdateWorkerPriorities(); | |
69 | |
70 // Gathers up a list of the visible tabs and then updates priorities for | |
71 // all the shared workers. | |
72 void GatherVisibleIDsAndUpdateWorkerPriorities(); | |
73 | |
74 // Registers as an observer to receive notifications about | |
75 // widgets being shown. | |
76 void RegisterObserver(); | |
77 | |
78 // Sets priorities for shared workers given a set of visible frames (as a | |
79 // std::set of std::pair<render_process, render_frame> ids. | |
80 void UpdateWorkerPrioritiesFromVisibleSet( | |
81 const std::set<std::pair<int, int> >* visible); | |
82 | |
83 // Called to refresh worker priorities when focus changes between tabs. | |
84 void OnRenderWidgetVisibilityChanged(std::pair<int, int>); | |
85 | |
86 // NotificationObserver implementation. | |
87 virtual void Observe(int type, | |
88 const NotificationSource& source, | |
89 const NotificationDetails& details) OVERRIDE; | |
90 | |
91 NotificationRegistrar registrar_; | |
92 }; | |
93 | |
94 WorkerPrioritySetter::WorkerPrioritySetter() { | |
95 } | |
96 | |
97 WorkerPrioritySetter::~WorkerPrioritySetter() { | |
98 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
99 } | |
100 | |
101 void WorkerPrioritySetter::Initialize() { | |
102 BrowserThread::PostTask( | |
103 BrowserThread::UI, FROM_HERE, | |
104 base::Bind(&WorkerPrioritySetter::RegisterObserver, this)); | |
105 } | |
106 | |
107 void WorkerPrioritySetter::NotifyWorkerProcessCreated() { | |
108 PostTaskToGatherAndUpdateWorkerPriorities(); | |
109 } | |
110 | |
111 void WorkerPrioritySetter::PostTaskToGatherAndUpdateWorkerPriorities() { | |
112 BrowserThread::PostTask( | |
113 BrowserThread::UI, FROM_HERE, | |
114 base::Bind( | |
115 &WorkerPrioritySetter::GatherVisibleIDsAndUpdateWorkerPriorities, | |
116 this)); | |
117 } | |
118 | |
119 void WorkerPrioritySetter::GatherVisibleIDsAndUpdateWorkerPriorities() { | |
120 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
121 std::set<std::pair<int, int> >* visible_frame_ids = | |
122 new std::set<std::pair<int, int> >(); | |
123 | |
124 // Gather up all the visible renderer process/view pairs | |
125 scoped_ptr<RenderWidgetHostIterator> widgets( | |
126 RenderWidgetHost::GetRenderWidgetHosts()); | |
127 while (RenderWidgetHost* widget = widgets->GetNextHost()) { | |
128 if (widget->GetProcess()->VisibleWidgetCount() == 0) | |
129 continue; | |
130 if (!widget->IsRenderView()) | |
131 continue; | |
132 | |
133 RenderWidgetHostView* widget_view = widget->GetView(); | |
134 if (!widget_view || !widget_view->IsShowing()) | |
135 continue; | |
136 RenderViewHost* rvh = RenderViewHost::From(widget); | |
137 WebContents* web_contents = WebContents::FromRenderViewHost(rvh); | |
138 if (!web_contents) | |
139 continue; | |
140 web_contents->ForEachFrame( | |
141 base::Bind(&AddRenderFrameID, visible_frame_ids)); | |
142 } | |
143 | |
144 BrowserThread::PostTask( | |
145 BrowserThread::IO, FROM_HERE, | |
146 base::Bind(&WorkerPrioritySetter::UpdateWorkerPrioritiesFromVisibleSet, | |
147 this, base::Owned(visible_frame_ids))); | |
148 } | |
149 | |
150 void WorkerPrioritySetter::UpdateWorkerPrioritiesFromVisibleSet( | |
151 const std::set<std::pair<int, int> >* visible_frame_ids) { | |
152 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
153 | |
154 for (WorkerProcessHostIterator iter; !iter.Done(); ++iter) { | |
155 if (!iter->process_launched()) | |
156 continue; | |
157 bool throttle = true; | |
158 | |
159 for (WorkerProcessHost::Instances::const_iterator instance = | |
160 iter->instances().begin(); instance != iter->instances().end(); | |
161 ++instance) { | |
162 | |
163 // This code assumes one worker per process | |
164 WorkerProcessHost::Instances::const_iterator first_instance = | |
165 iter->instances().begin(); | |
166 if (first_instance == iter->instances().end()) | |
167 continue; | |
168 | |
169 WorkerDocumentSet::DocumentInfoSet::const_iterator info = | |
170 first_instance->worker_document_set()->documents().begin(); | |
171 | |
172 for (; info != first_instance->worker_document_set()->documents().end(); | |
173 ++info) { | |
174 std::pair<int, int> id( | |
175 info->render_process_id(), info->render_frame_id()); | |
176 if (visible_frame_ids->find(id) != visible_frame_ids->end()) { | |
177 throttle = false; | |
178 break; | |
179 } | |
180 } | |
181 | |
182 if (!throttle ) { | |
183 break; | |
184 } | |
185 } | |
186 | |
187 iter->SetBackgrounded(throttle); | |
188 } | |
189 } | |
190 | |
191 void WorkerPrioritySetter::OnRenderWidgetVisibilityChanged( | |
192 std::pair<int, int> id) { | |
193 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
194 std::set<std::pair<int, int> > visible_frame_ids; | |
195 | |
196 visible_frame_ids.insert(id); | |
197 | |
198 UpdateWorkerPrioritiesFromVisibleSet(&visible_frame_ids); | |
199 } | |
200 | |
201 void WorkerPrioritySetter::RegisterObserver() { | |
202 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
203 registrar_.Add(this, NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED, | |
204 NotificationService::AllBrowserContextsAndSources()); | |
205 registrar_.Add(this, NOTIFICATION_RENDERER_PROCESS_CREATED, | |
206 NotificationService::AllBrowserContextsAndSources()); | |
207 } | |
208 | |
209 void WorkerPrioritySetter::Observe(int type, | |
210 const NotificationSource& source, const NotificationDetails& details) { | |
211 if (type == NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED) { | |
212 bool visible = *Details<bool>(details).ptr(); | |
213 | |
214 if (visible) { | |
215 int render_widget_id = | |
216 Source<RenderWidgetHost>(source).ptr()->GetRoutingID(); | |
217 int render_process_pid = | |
218 Source<RenderWidgetHost>(source).ptr()->GetProcess()->GetID(); | |
219 | |
220 BrowserThread::PostTask( | |
221 BrowserThread::IO, FROM_HERE, | |
222 base::Bind(&WorkerPrioritySetter::OnRenderWidgetVisibilityChanged, | |
223 this, std::pair<int, int>(render_process_pid, render_widget_id))); | |
224 } | |
225 } | |
226 else if (type == NOTIFICATION_RENDERER_PROCESS_CREATED) { | |
227 PostTaskToGatherAndUpdateWorkerPriorities(); | |
228 } | |
229 } | |
230 | |
231 WorkerService* WorkerService::GetInstance() { | |
232 if (EmbeddedSharedWorkerEnabled()) | |
233 return SharedWorkerServiceImpl::GetInstance(); | |
234 else | |
235 return WorkerServiceImpl::GetInstance(); | |
236 } | |
237 | |
238 bool WorkerService::EmbeddedSharedWorkerEnabled() { | |
239 static bool disabled = CommandLine::ForCurrentProcess()->HasSwitch( | |
240 switches::kDisableEmbeddedSharedWorker); | |
241 return !disabled; | |
242 } | |
243 | |
244 WorkerServiceImpl* WorkerServiceImpl::GetInstance() { | |
245 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
246 return Singleton<WorkerServiceImpl>::get(); | |
247 } | |
248 | |
249 WorkerServiceImpl::WorkerServiceImpl() | |
250 : priority_setter_(new WorkerPrioritySetter()), | |
251 next_worker_route_id_(0) { | |
252 priority_setter_->Initialize(); | |
253 } | |
254 | |
255 WorkerServiceImpl::~WorkerServiceImpl() { | |
256 // The observers in observers_ can't be used here because they might be | |
257 // gone already. | |
258 } | |
259 | |
260 void WorkerServiceImpl::PerformTeardownForTesting() { | |
261 priority_setter_ = NULL; | |
262 } | |
263 | |
264 void WorkerServiceImpl::OnWorkerMessageFilterClosing( | |
265 WorkerMessageFilter* filter) { | |
266 for (WorkerProcessHostIterator iter; !iter.Done(); ++iter) { | |
267 iter->FilterShutdown(filter); | |
268 } | |
269 | |
270 // See if that process had any queued workers. | |
271 for (WorkerProcessHost::Instances::iterator i = queued_workers_.begin(); | |
272 i != queued_workers_.end();) { | |
273 i->RemoveFilters(filter); | |
274 if (i->NumFilters() == 0) { | |
275 i = queued_workers_.erase(i); | |
276 } else { | |
277 ++i; | |
278 } | |
279 } | |
280 | |
281 // Either a worker proceess has shut down, in which case we can start one of | |
282 // the queued workers, or a renderer has shut down, in which case it doesn't | |
283 // affect anything. We call this function in both scenarios because then we | |
284 // don't have to keep track which filters are from worker processes. | |
285 TryStartingQueuedWorker(); | |
286 } | |
287 | |
288 void WorkerServiceImpl::CreateWorker( | |
289 const ViewHostMsg_CreateWorker_Params& params, | |
290 int route_id, | |
291 WorkerMessageFilter* filter, | |
292 ResourceContext* resource_context, | |
293 const WorkerStoragePartition& partition, | |
294 bool* url_mismatch) { | |
295 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
296 *url_mismatch = false; | |
297 WorkerProcessHost::WorkerInstance* existing_instance = | |
298 FindSharedWorkerInstance( | |
299 params.url, params.name, partition, resource_context); | |
300 if (existing_instance) { | |
301 if (params.url != existing_instance->url()) { | |
302 *url_mismatch = true; | |
303 return; | |
304 } | |
305 if (existing_instance->load_failed()) { | |
306 filter->Send(new ViewMsg_WorkerScriptLoadFailed(route_id)); | |
307 return; | |
308 } | |
309 existing_instance->AddFilter(filter, route_id); | |
310 existing_instance->worker_document_set()->Add( | |
311 filter, params.document_id, filter->render_process_id(), | |
312 params.render_frame_route_id); | |
313 filter->Send(new ViewMsg_WorkerCreated(route_id)); | |
314 return; | |
315 } | |
316 for (WorkerProcessHost::Instances::iterator i = queued_workers_.begin(); | |
317 i != queued_workers_.end(); ++i) { | |
318 if (i->Matches(params.url, params.name, partition, resource_context) && | |
319 params.url != i->url()) { | |
320 *url_mismatch = true; | |
321 return; | |
322 } | |
323 } | |
324 | |
325 // Generate a unique route id for the browser-worker communication that's | |
326 // unique among all worker processes. That way when the worker process sends | |
327 // a wrapped IPC message through us, we know which WorkerProcessHost to give | |
328 // it to. | |
329 WorkerProcessHost::WorkerInstance instance( | |
330 params.url, | |
331 params.name, | |
332 params.content_security_policy, | |
333 params.security_policy_type, | |
334 next_worker_route_id(), | |
335 params.render_frame_route_id, | |
336 resource_context, | |
337 partition); | |
338 instance.AddFilter(filter, route_id); | |
339 instance.worker_document_set()->Add( | |
340 filter, params.document_id, filter->render_process_id(), | |
341 params.render_frame_route_id); | |
342 | |
343 CreateWorkerFromInstance(instance); | |
344 } | |
345 | |
346 void WorkerServiceImpl::ForwardToWorker(const IPC::Message& message, | |
347 WorkerMessageFilter* filter) { | |
348 for (WorkerProcessHostIterator iter; !iter.Done(); ++iter) { | |
349 if (iter->FilterMessage(message, filter)) | |
350 return; | |
351 } | |
352 | |
353 // TODO(jabdelmalek): tell filter that callee is gone | |
354 } | |
355 | |
356 void WorkerServiceImpl::DocumentDetached(unsigned long long document_id, | |
357 WorkerMessageFilter* filter) { | |
358 // Any associated shared workers can be shut down. | |
359 for (WorkerProcessHostIterator iter; !iter.Done(); ++iter) | |
360 iter->DocumentDetached(filter, document_id); | |
361 | |
362 // Remove any queued shared workers for this document. | |
363 for (WorkerProcessHost::Instances::iterator iter = queued_workers_.begin(); | |
364 iter != queued_workers_.end();) { | |
365 | |
366 iter->worker_document_set()->Remove(filter, document_id); | |
367 if (iter->worker_document_set()->IsEmpty()) { | |
368 iter = queued_workers_.erase(iter); | |
369 continue; | |
370 } | |
371 ++iter; | |
372 } | |
373 } | |
374 | |
375 bool WorkerServiceImpl::CreateWorkerFromInstance( | |
376 WorkerProcessHost::WorkerInstance instance) { | |
377 if (!CanCreateWorkerProcess(instance)) { | |
378 queued_workers_.push_back(instance); | |
379 return true; | |
380 } | |
381 | |
382 // Remove any queued instances of this worker and copy over the filter to | |
383 // this instance. | |
384 for (WorkerProcessHost::Instances::iterator iter = queued_workers_.begin(); | |
385 iter != queued_workers_.end();) { | |
386 if (iter->Matches(instance.url(), instance.name(), | |
387 instance.partition(), instance.resource_context())) { | |
388 DCHECK(iter->NumFilters() == 1); | |
389 DCHECK_EQ(instance.url(), iter->url()); | |
390 WorkerProcessHost::WorkerInstance::FilterInfo filter_info = | |
391 iter->GetFilter(); | |
392 instance.AddFilter(filter_info.filter(), filter_info.route_id()); | |
393 iter = queued_workers_.erase(iter); | |
394 } else { | |
395 ++iter; | |
396 } | |
397 } | |
398 | |
399 WorkerMessageFilter* first_filter = instance.filters().begin()->filter(); | |
400 WorkerProcessHost* worker = new WorkerProcessHost( | |
401 instance.resource_context(), instance.partition()); | |
402 // TODO(atwilson): This won't work if the message is from a worker process. | |
403 // We don't support that yet though (this message is only sent from | |
404 // renderers) but when we do, we'll need to add code to pass in the current | |
405 // worker's document set for nested workers. | |
406 if (!worker->Init(first_filter->render_process_id(), | |
407 instance.render_frame_id())) { | |
408 delete worker; | |
409 return false; | |
410 } | |
411 | |
412 worker->CreateWorker( | |
413 instance, | |
414 WorkerDevToolsManager::GetInstance()->WorkerCreated(worker, instance)); | |
415 FOR_EACH_OBSERVER( | |
416 WorkerServiceObserver, observers_, | |
417 WorkerCreated(instance.url(), instance.name(), worker->GetData().id, | |
418 instance.worker_route_id())); | |
419 return true; | |
420 } | |
421 | |
422 bool WorkerServiceImpl::CanCreateWorkerProcess( | |
423 const WorkerProcessHost::WorkerInstance& instance) { | |
424 // Worker can be fired off if *any* parent has room. | |
425 const WorkerDocumentSet::DocumentInfoSet& parents = | |
426 instance.worker_document_set()->documents(); | |
427 | |
428 for (WorkerDocumentSet::DocumentInfoSet::const_iterator parent_iter = | |
429 parents.begin(); | |
430 parent_iter != parents.end(); ++parent_iter) { | |
431 bool hit_total_worker_limit = false; | |
432 if (FrameCanCreateWorkerProcess(parent_iter->render_process_id(), | |
433 parent_iter->render_frame_id(), | |
434 &hit_total_worker_limit)) { | |
435 return true; | |
436 } | |
437 // Return false if already at the global worker limit (no need to continue | |
438 // checking parent tabs). | |
439 if (hit_total_worker_limit) | |
440 return false; | |
441 } | |
442 // If we've reached here, none of the parent tabs is allowed to create an | |
443 // instance. | |
444 return false; | |
445 } | |
446 | |
447 bool WorkerServiceImpl::FrameCanCreateWorkerProcess( | |
448 int render_process_id, | |
449 int render_frame_id, | |
450 bool* hit_total_worker_limit) { | |
451 int total_workers = 0; | |
452 int workers_per_tab = 0; | |
453 *hit_total_worker_limit = false; | |
454 for (WorkerProcessHostIterator iter; !iter.Done(); ++iter) { | |
455 for (WorkerProcessHost::Instances::const_iterator cur_instance = | |
456 iter->instances().begin(); | |
457 cur_instance != iter->instances().end(); ++cur_instance) { | |
458 total_workers++; | |
459 if (total_workers >= kMaxWorkersWhenSeparate) { | |
460 *hit_total_worker_limit = true; | |
461 return false; | |
462 } | |
463 if (cur_instance->FrameIsParent(render_process_id, render_frame_id)) { | |
464 workers_per_tab++; | |
465 if (workers_per_tab >= kMaxWorkersPerFrameWhenSeparate) | |
466 return false; | |
467 } | |
468 } | |
469 } | |
470 | |
471 return true; | |
472 } | |
473 | |
474 void WorkerServiceImpl::TryStartingQueuedWorker() { | |
475 if (queued_workers_.empty()) | |
476 return; | |
477 | |
478 for (WorkerProcessHost::Instances::iterator i = queued_workers_.begin(); | |
479 i != queued_workers_.end();) { | |
480 if (CanCreateWorkerProcess(*i)) { | |
481 WorkerProcessHost::WorkerInstance instance = *i; | |
482 queued_workers_.erase(i); | |
483 CreateWorkerFromInstance(instance); | |
484 | |
485 // CreateWorkerFromInstance can modify the queued_workers_ list when it | |
486 // coalesces queued instances after starting a shared worker, so we | |
487 // have to rescan the list from the beginning (our iterator is now | |
488 // invalid). This is not a big deal as having any queued workers will be | |
489 // rare in practice so the list will be small. | |
490 i = queued_workers_.begin(); | |
491 } else { | |
492 ++i; | |
493 } | |
494 } | |
495 } | |
496 | |
497 bool WorkerServiceImpl::GetRendererForWorker(int worker_process_id, | |
498 int* render_process_id, | |
499 int* render_frame_id) const { | |
500 for (WorkerProcessHostIterator iter; !iter.Done(); ++iter) { | |
501 if (iter.GetData().id != worker_process_id) | |
502 continue; | |
503 | |
504 // This code assumes one worker per process, see function comment in header! | |
505 WorkerProcessHost::Instances::const_iterator first_instance = | |
506 iter->instances().begin(); | |
507 if (first_instance == iter->instances().end()) | |
508 return false; | |
509 | |
510 WorkerDocumentSet::DocumentInfoSet::const_iterator info = | |
511 first_instance->worker_document_set()->documents().begin(); | |
512 *render_process_id = info->render_process_id(); | |
513 *render_frame_id = info->render_frame_id(); | |
514 return true; | |
515 } | |
516 return false; | |
517 } | |
518 | |
519 const WorkerProcessHost::WorkerInstance* WorkerServiceImpl::FindWorkerInstance( | |
520 int worker_process_id) { | |
521 for (WorkerProcessHostIterator iter; !iter.Done(); ++iter) { | |
522 if (iter.GetData().id != worker_process_id) | |
523 continue; | |
524 | |
525 WorkerProcessHost::Instances::const_iterator instance = | |
526 iter->instances().begin(); | |
527 return instance == iter->instances().end() ? NULL : &*instance; | |
528 } | |
529 return NULL; | |
530 } | |
531 | |
532 bool WorkerServiceImpl::TerminateWorker(int process_id, int route_id) { | |
533 for (WorkerProcessHostIterator iter; !iter.Done(); ++iter) { | |
534 if (iter.GetData().id == process_id) { | |
535 iter->TerminateWorker(route_id); | |
536 return true; | |
537 } | |
538 } | |
539 return false; | |
540 } | |
541 | |
542 std::vector<WorkerService::WorkerInfo> WorkerServiceImpl::GetWorkers() { | |
543 std::vector<WorkerService::WorkerInfo> results; | |
544 for (WorkerProcessHostIterator iter; !iter.Done(); ++iter) { | |
545 const WorkerProcessHost::Instances& instances = (*iter)->instances(); | |
546 for (WorkerProcessHost::Instances::const_iterator i = instances.begin(); | |
547 i != instances.end(); ++i) { | |
548 WorkerService::WorkerInfo info; | |
549 info.url = i->url(); | |
550 info.name = i->name(); | |
551 info.route_id = i->worker_route_id(); | |
552 info.process_id = iter.GetData().id; | |
553 info.handle = iter.GetData().handle; | |
554 results.push_back(info); | |
555 } | |
556 } | |
557 return results; | |
558 } | |
559 | |
560 void WorkerServiceImpl::AddObserver(WorkerServiceObserver* observer) { | |
561 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
562 observers_.AddObserver(observer); | |
563 } | |
564 | |
565 void WorkerServiceImpl::RemoveObserver(WorkerServiceObserver* observer) { | |
566 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
567 observers_.RemoveObserver(observer); | |
568 } | |
569 | |
570 void WorkerServiceImpl::NotifyWorkerDestroyed( | |
571 WorkerProcessHost* process, | |
572 int worker_route_id) { | |
573 WorkerDevToolsManager::GetInstance()->WorkerDestroyed( | |
574 process, worker_route_id); | |
575 FOR_EACH_OBSERVER(WorkerServiceObserver, observers_, | |
576 WorkerDestroyed(process->GetData().id, worker_route_id)); | |
577 } | |
578 | |
579 void WorkerServiceImpl::NotifyWorkerProcessCreated() { | |
580 priority_setter_->NotifyWorkerProcessCreated(); | |
581 } | |
582 | |
583 WorkerProcessHost::WorkerInstance* WorkerServiceImpl::FindSharedWorkerInstance( | |
584 const GURL& url, | |
585 const base::string16& name, | |
586 const WorkerStoragePartition& partition, | |
587 ResourceContext* resource_context) { | |
588 for (WorkerProcessHostIterator iter; !iter.Done(); ++iter) { | |
589 for (WorkerProcessHost::Instances::iterator instance_iter = | |
590 iter->mutable_instances().begin(); | |
591 instance_iter != iter->mutable_instances().end(); | |
592 ++instance_iter) { | |
593 if (instance_iter->Matches(url, name, partition, resource_context)) | |
594 return &(*instance_iter); | |
595 } | |
596 } | |
597 return NULL; | |
598 } | |
599 | |
600 } // namespace content | |
OLD | NEW |