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 "chrome/browser/task_manager/worker_resource_provider.h"
9 #include "base/basictypes.h"
10 #include "base/strings/string16.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "chrome/browser/devtools/devtools_window.h"
13 #include "chrome/browser/profiles/profile_manager.h"
14 #include "chrome/browser/task_manager/resource_provider.h"
15 #include "chrome/browser/task_manager/task_manager.h"
16 #include "content/public/browser/browser_thread.h"
17 #include "content/public/browser/child_process_data.h"
18 #include "content/public/browser/devtools_agent_host.h"
19 #include "content/public/browser/worker_service.h"
20 #include "content/public/common/process_type.h"
21 #include "grit/generated_resources.h"
22 #include "grit/theme_resources.h"
23 #include "ui/base/l10n/l10n_util.h"
24 #include "ui/base/resource/resource_bundle.h"
25 #include "ui/gfx/image/image_skia.h"
27 using content::BrowserThread
;
28 using content::DevToolsAgentHost
;
29 using content::WorkerService
;
31 namespace task_manager
{
33 // Objects of this class are created on the IO thread and then passed to the UI
34 // thread where they are passed to the task manager. All methods must be called
35 // only on the UI thread. Destructor may be called on any thread.
36 class SharedWorkerResource
: public Resource
{
38 SharedWorkerResource(const GURL
& url
,
39 const base::string16
& name
,
42 base::ProcessHandle process_handle
);
43 virtual ~SharedWorkerResource();
45 bool Matches(int process_id
, int routing_id
) const;
47 void UpdateProcessHandle(base::ProcessHandle handle
);
48 base::ProcessHandle
handle() const { return handle_
; }
49 int process_id() const { return process_id_
; }
53 virtual base::string16
GetTitle() const OVERRIDE
;
54 virtual base::string16
GetProfileName() const OVERRIDE
;
55 virtual gfx::ImageSkia
GetIcon() const OVERRIDE
;
56 virtual base::ProcessHandle
GetProcess() const OVERRIDE
;
57 virtual int GetUniqueChildProcessId() const OVERRIDE
;
58 virtual Type
GetType() const OVERRIDE
;
59 virtual bool CanInspect() const OVERRIDE
;
60 virtual void Inspect() const OVERRIDE
;
62 virtual bool SupportNetworkUsage() const OVERRIDE
;
63 virtual void SetSupportNetworkUsage() OVERRIDE
;
67 base::string16 title_
;
68 base::ProcessHandle handle_
;
70 static gfx::ImageSkia
* default_icon_
;
72 DISALLOW_COPY_AND_ASSIGN(SharedWorkerResource
);
75 gfx::ImageSkia
* SharedWorkerResource::default_icon_
= NULL
;
77 SharedWorkerResource::SharedWorkerResource(
79 const base::string16
& name
,
82 base::ProcessHandle process_handle
)
83 : process_id_(process_id
),
84 routing_id_(routing_id
),
85 handle_(process_handle
) {
86 title_
= base::UTF8ToUTF16(url
.spec());
88 title_
+= base::ASCIIToUTF16(" (") + name
+ base::ASCIIToUTF16(")");
91 SharedWorkerResource::~SharedWorkerResource() {
94 bool SharedWorkerResource::Matches(int process_id
,
95 int routing_id
) const {
96 return process_id_
== process_id
&& routing_id_
== routing_id
;
99 void SharedWorkerResource::UpdateProcessHandle(base::ProcessHandle handle
) {
103 base::string16
SharedWorkerResource::GetTitle() const {
104 return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_WORKER_PREFIX
, title_
);
107 base::string16
SharedWorkerResource::GetProfileName() const {
108 return base::string16();
111 gfx::ImageSkia
SharedWorkerResource::GetIcon() const {
112 if (!default_icon_
) {
113 ResourceBundle
& rb
= ResourceBundle::GetSharedInstance();
114 default_icon_
= rb
.GetImageSkiaNamed(IDR_PLUGINS_FAVICON
);
115 // TODO(jabdelmalek): use different icon for web workers.
117 return *default_icon_
;
120 base::ProcessHandle
SharedWorkerResource::GetProcess() const {
124 int SharedWorkerResource::GetUniqueChildProcessId() const {
128 Resource::Type
SharedWorkerResource::GetType() const {
132 bool SharedWorkerResource::CanInspect() const {
136 void SharedWorkerResource::Inspect() const {
137 // TODO(yurys): would be better to get profile from one of the tabs connected
139 Profile
* profile
= ProfileManager::GetLastUsedProfile();
142 scoped_refptr
<DevToolsAgentHost
> agent_host(
143 DevToolsAgentHost::GetForWorker(process_id_
, routing_id_
));
144 DevToolsWindow::OpenDevToolsWindowForWorker(profile
, agent_host
.get());
147 bool SharedWorkerResource::SupportNetworkUsage() const {
151 void SharedWorkerResource::SetSupportNetworkUsage() {
155 // This class is needed to ensure that all resources in WorkerResourceList are
156 // deleted if corresponding task is posted to but not executed on the UI
158 class WorkerResourceProvider::WorkerResourceListHolder
{
160 WorkerResourceListHolder() {
163 ~WorkerResourceListHolder() {
164 STLDeleteElements(&resources_
);
167 WorkerResourceList
* resources() {
172 WorkerResourceList resources_
;
176 WorkerResourceProvider::
177 WorkerResourceProvider(TaskManager
* task_manager
)
179 task_manager_(task_manager
) {
182 WorkerResourceProvider::~WorkerResourceProvider() {
183 DeleteAllResources();
186 Resource
* WorkerResourceProvider::GetResource(
193 void WorkerResourceProvider::StartUpdating() {
196 // Get existing workers.
197 BrowserThread::PostTask(
198 BrowserThread::IO
, FROM_HERE
, base::Bind(
199 &WorkerResourceProvider::StartObservingWorkers
,
202 BrowserChildProcessObserver::Add(this);
205 void WorkerResourceProvider::StopUpdating() {
208 launching_workers_
.clear();
209 DeleteAllResources();
210 BrowserThread::PostTask(
211 BrowserThread::IO
, FROM_HERE
, base::Bind(
212 &WorkerResourceProvider::StopObservingWorkers
,
215 BrowserChildProcessObserver::Remove(this);
218 void WorkerResourceProvider::BrowserChildProcessHostConnected(
219 const content::ChildProcessData
& data
) {
222 if (data
.process_type
!= content::PROCESS_TYPE_WORKER
)
225 ProcessIdToWorkerResources::iterator
it(launching_workers_
.find(data
.id
));
226 if (it
== launching_workers_
.end())
228 WorkerResourceList
& resources
= it
->second
;
229 for (WorkerResourceList::iterator r
= resources
.begin();
230 r
!= resources
.end(); ++r
) {
231 (*r
)->UpdateProcessHandle(data
.handle
);
232 task_manager_
->AddResource(*r
);
234 launching_workers_
.erase(it
);
237 void WorkerResourceProvider::BrowserChildProcessHostDisconnected(
238 const content::ChildProcessData
& data
) {
241 if (data
.process_type
!= content::PROCESS_TYPE_WORKER
)
244 // Worker process may be destroyed before WorkerMsg_TerminateWorkerContex
245 // message is handled and WorkerDestroyed is fired. In this case we won't
246 // get WorkerDestroyed notification and have to clear resources for such
247 // workers here when the worker process has been destroyed.
248 for (WorkerResourceList::iterator it
= resources_
.begin();
249 it
!= resources_
.end();) {
250 if ((*it
)->process_id() == data
.id
) {
251 task_manager_
->RemoveResource(*it
);
253 it
= resources_
.erase(it
);
258 DCHECK(!ContainsKey(launching_workers_
, data
.id
));
261 void WorkerResourceProvider::WorkerCreated(
263 const base::string16
& name
,
266 SharedWorkerResource
* resource
= new SharedWorkerResource(
267 url
, name
, process_id
, route_id
, base::kNullProcessHandle
);
268 BrowserThread::PostTask(
269 BrowserThread::UI
, FROM_HERE
,
270 base::Bind(&WorkerResourceProvider::NotifyWorkerCreated
,
271 this, base::Owned(new WorkerResourceHolder(resource
))));
274 void WorkerResourceProvider::WorkerDestroyed(int process_id
, int route_id
) {
275 BrowserThread::PostTask(
276 BrowserThread::UI
, FROM_HERE
, base::Bind(
277 &WorkerResourceProvider::NotifyWorkerDestroyed
,
278 this, process_id
, route_id
));
281 void WorkerResourceProvider::NotifyWorkerCreated(
282 WorkerResourceHolder
* resource_holder
) {
283 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
286 AddResource(resource_holder
->release());
289 void WorkerResourceProvider::NotifyWorkerDestroyed(
290 int process_id
, int routing_id
) {
291 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
294 for (WorkerResourceList::iterator it
= resources_
.begin();
295 it
!=resources_
.end(); ++it
) {
296 if ((*it
)->Matches(process_id
, routing_id
)) {
297 task_manager_
->RemoveResource(*it
);
299 resources_
.erase(it
);
305 void WorkerResourceProvider::StartObservingWorkers() {
306 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
308 scoped_ptr
<WorkerResourceListHolder
> holder(new WorkerResourceListHolder
);
309 std::vector
<WorkerService::WorkerInfo
> worker_info
=
310 WorkerService::GetInstance()->GetWorkers();
312 for (size_t i
= 0; i
< worker_info
.size(); ++i
) {
313 holder
->resources()->push_back(new SharedWorkerResource(
314 worker_info
[i
].url
, worker_info
[i
].name
, worker_info
[i
].process_id
,
315 worker_info
[i
].route_id
, worker_info
[i
].handle
));
318 BrowserThread::PostTask(
319 BrowserThread::UI
, FROM_HERE
,
321 &WorkerResourceProvider::AddWorkerResourceList
,
322 this, base::Owned(holder
.release())));
324 WorkerService::GetInstance()->AddObserver(this);
327 void WorkerResourceProvider::StopObservingWorkers() {
328 WorkerService::GetInstance()->RemoveObserver(this);
331 void WorkerResourceProvider::AddWorkerResourceList(
332 WorkerResourceListHolder
* resource_list_holder
) {
333 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
336 WorkerResourceList
* resources
= resource_list_holder
->resources();
337 for (WorkerResourceList::iterator it
= resources
->begin();
338 it
!=resources
->end(); ++it
) {
344 void WorkerResourceProvider::AddResource(SharedWorkerResource
* resource
) {
346 resources_
.push_back(resource
);
347 if (resource
->handle() == base::kNullProcessHandle
) {
348 int process_id
= resource
->process_id();
349 launching_workers_
[process_id
].push_back(resource
);
351 task_manager_
->AddResource(resource
);
355 void WorkerResourceProvider::DeleteAllResources() {
356 STLDeleteElements(&resources_
);
359 } // namespace task_manager