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 "chrome/browser/extensions/api/processes/processes_api.h" 6 7 #include "base/callback.h" 8 #include "base/json/json_writer.h" 9 #include "base/lazy_instance.h" 10 #include "base/message_loop/message_loop.h" 11 #include "base/metrics/histogram.h" 12 #include "base/strings/string_number_conversions.h" 13 #include "base/strings/utf_string_conversions.h" 14 #include "base/values.h" 15 #include "chrome/browser/chrome_notification_types.h" 16 #include "chrome/browser/extensions/api/processes/processes_api_constants.h" 17 #include "chrome/browser/extensions/api/tabs/tabs_constants.h" 18 #include "chrome/browser/extensions/extension_service.h" 19 #include "chrome/browser/extensions/extension_tab_util.h" 20 #include "chrome/browser/profiles/profile.h" 21 #include "chrome/browser/task_manager/resource_provider.h" 22 #include "chrome/browser/task_manager/task_manager.h" 23 #include "content/public/browser/browser_context.h" 24 #include "content/public/browser/notification_details.h" 25 #include "content/public/browser/notification_service.h" 26 #include "content/public/browser/notification_source.h" 27 #include "content/public/browser/notification_types.h" 28 #include "content/public/browser/render_process_host.h" 29 #include "content/public/browser/render_view_host.h" 30 #include "content/public/browser/render_widget_host.h" 31 #include "content/public/browser/render_widget_host_iterator.h" 32 #include "content/public/browser/web_contents.h" 33 #include "content/public/common/result_codes.h" 34 #include "extensions/browser/event_router.h" 35 #include "extensions/browser/extension_function_registry.h" 36 #include "extensions/browser/extension_function_util.h" 37 #include "extensions/common/error_utils.h" 38 39 namespace extensions { 40 41 namespace keys = processes_api_constants; 42 namespace errors = processes_api_constants; 43 44 namespace { 45 46 #if defined(ENABLE_TASK_MANAGER) 47 48 base::DictionaryValue* CreateCacheData( 49 const blink::WebCache::ResourceTypeStat& stat) { 50 51 base::DictionaryValue* cache = new base::DictionaryValue(); 52 cache->SetDouble(keys::kCacheSize, static_cast<double>(stat.size)); 53 cache->SetDouble(keys::kCacheLiveSize, static_cast<double>(stat.liveSize)); 54 return cache; 55 } 56 57 void SetProcessType(base::DictionaryValue* result, 58 TaskManagerModel* model, 59 int index) { 60 // Determine process type. 61 std::string type = keys::kProcessTypeOther; 62 task_manager::Resource::Type resource_type = model->GetResourceType(index); 63 switch (resource_type) { 64 case task_manager::Resource::BROWSER: 65 type = keys::kProcessTypeBrowser; 66 break; 67 case task_manager::Resource::RENDERER: 68 type = keys::kProcessTypeRenderer; 69 break; 70 case task_manager::Resource::EXTENSION: 71 type = keys::kProcessTypeExtension; 72 break; 73 case task_manager::Resource::NOTIFICATION: 74 type = keys::kProcessTypeNotification; 75 break; 76 case task_manager::Resource::PLUGIN: 77 type = keys::kProcessTypePlugin; 78 break; 79 case task_manager::Resource::WORKER: 80 type = keys::kProcessTypeWorker; 81 break; 82 case task_manager::Resource::NACL: 83 type = keys::kProcessTypeNacl; 84 break; 85 case task_manager::Resource::UTILITY: 86 type = keys::kProcessTypeUtility; 87 break; 88 case task_manager::Resource::GPU: 89 type = keys::kProcessTypeGPU; 90 break; 91 case task_manager::Resource::ZYGOTE: 92 case task_manager::Resource::SANDBOX_HELPER: 93 case task_manager::Resource::UNKNOWN: 94 type = keys::kProcessTypeOther; 95 break; 96 default: 97 NOTREACHED() << "Unknown resource type."; 98 } 99 result->SetString(keys::kTypeKey, type); 100 } 101 102 base::ListValue* GetTabsForProcess(int process_id) { 103 base::ListValue* tabs_list = new base::ListValue(); 104 105 // The tabs list only makes sense for render processes, so if we don't find 106 // one, just return the empty list. 107 content::RenderProcessHost* rph = 108 content::RenderProcessHost::FromID(process_id); 109 if (rph == NULL) 110 return tabs_list; 111 112 int tab_id = -1; 113 // We need to loop through all the RVHs to ensure we collect the set of all 114 // tabs using this renderer process. 115 scoped_ptr<content::RenderWidgetHostIterator> widgets( 116 content::RenderWidgetHost::GetRenderWidgetHosts()); 117 while (content::RenderWidgetHost* widget = widgets->GetNextHost()) { 118 if (widget->GetProcess()->GetID() != process_id) 119 continue; 120 if (!widget->IsRenderView()) 121 continue; 122 123 content::RenderViewHost* host = content::RenderViewHost::From(widget); 124 content::WebContents* contents = 125 content::WebContents::FromRenderViewHost(host); 126 if (contents) { 127 tab_id = ExtensionTabUtil::GetTabId(contents); 128 if (tab_id != -1) 129 tabs_list->Append(new base::FundamentalValue(tab_id)); 130 } 131 } 132 133 return tabs_list; 134 } 135 136 // This function creates a Process object to be returned to the extensions 137 // using these APIs. For memory details, which are not added by this function, 138 // the callers need to use AddMemoryDetails. 139 base::DictionaryValue* CreateProcessFromModel(int process_id, 140 TaskManagerModel* model, 141 int index, 142 bool include_optional) { 143 base::DictionaryValue* result = new base::DictionaryValue(); 144 size_t mem; 145 146 result->SetInteger(keys::kIdKey, process_id); 147 result->SetInteger(keys::kOsProcessIdKey, model->GetProcessId(index)); 148 SetProcessType(result, model, index); 149 result->SetString(keys::kTitleKey, model->GetResourceTitle(index)); 150 result->SetString(keys::kProfileKey, 151 model->GetResourceProfileName(index)); 152 result->SetInteger(keys::kNaClDebugPortKey, 153 model->GetNaClDebugStubPort(index)); 154 155 result->Set(keys::kTabsListKey, GetTabsForProcess(process_id)); 156 157 // If we don't need to include the optional properties, just return now. 158 if (!include_optional) 159 return result; 160 161 result->SetDouble(keys::kCpuKey, model->GetCPUUsage(index)); 162 163 if (model->GetV8Memory(index, &mem)) 164 result->SetDouble(keys::kJsMemoryAllocatedKey, 165 static_cast<double>(mem)); 166 167 if (model->GetV8MemoryUsed(index, &mem)) 168 result->SetDouble(keys::kJsMemoryUsedKey, 169 static_cast<double>(mem)); 170 171 if (model->GetSqliteMemoryUsedBytes(index, &mem)) 172 result->SetDouble(keys::kSqliteMemoryKey, 173 static_cast<double>(mem)); 174 175 blink::WebCache::ResourceTypeStats cache_stats; 176 if (model->GetWebCoreCacheStats(index, &cache_stats)) { 177 result->Set(keys::kImageCacheKey, 178 CreateCacheData(cache_stats.images)); 179 result->Set(keys::kScriptCacheKey, 180 CreateCacheData(cache_stats.scripts)); 181 result->Set(keys::kCssCacheKey, 182 CreateCacheData(cache_stats.cssStyleSheets)); 183 } 184 185 // Network is reported by the TaskManager per resource (tab), not per 186 // process, therefore we need to iterate through the group of resources 187 // and aggregate the data. 188 int64 net = 0; 189 int length = model->GetGroupRangeForResource(index).second; 190 for (int i = 0; i < length; ++i) 191 net += model->GetNetworkUsage(index + i); 192 result->SetDouble(keys::kNetworkKey, static_cast<double>(net)); 193 194 return result; 195 } 196 197 // Since memory details are expensive to gather, we don't do it by default. 198 // This function is a helper to add memory details data to an existing 199 // Process object representation. 200 void AddMemoryDetails(base::DictionaryValue* result, 201 TaskManagerModel* model, 202 int index) { 203 size_t mem; 204 int64 pr_mem = model->GetPrivateMemory(index, &mem) ? 205 static_cast<int64>(mem) : -1; 206 result->SetDouble(keys::kPrivateMemoryKey, static_cast<double>(pr_mem)); 207 } 208 209 #endif // defined(ENABLE_TASK_MANAGER) 210 211 } // namespace 212 213 ProcessesEventRouter::ProcessesEventRouter(content::BrowserContext* context) 214 : browser_context_(context), listeners_(0), task_manager_listening_(false) { 215 #if defined(ENABLE_TASK_MANAGER) 216 model_ = TaskManager::GetInstance()->model(); 217 model_->AddObserver(this); 218 219 registrar_.Add(this, content::NOTIFICATION_RENDER_WIDGET_HOST_HANG, 220 content::NotificationService::AllSources()); 221 registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED, 222 content::NotificationService::AllSources()); 223 #endif // defined(ENABLE_TASK_MANAGER) 224 } 225 226 ProcessesEventRouter::~ProcessesEventRouter() { 227 #if defined(ENABLE_TASK_MANAGER) 228 registrar_.Remove(this, content::NOTIFICATION_RENDER_WIDGET_HOST_HANG, 229 content::NotificationService::AllSources()); 230 registrar_.Remove(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED, 231 content::NotificationService::AllSources()); 232 233 if (task_manager_listening_) 234 model_->StopListening(); 235 236 model_->RemoveObserver(this); 237 #endif // defined(ENABLE_TASK_MANAGER) 238 } 239 240 void ProcessesEventRouter::ListenerAdded() { 241 #if defined(ENABLE_TASK_MANAGER) 242 // The task manager has its own ref count to balance other callers of 243 // StartUpdating/StopUpdating. 244 model_->StartUpdating(); 245 #endif // defined(ENABLE_TASK_MANAGER) 246 ++listeners_; 247 } 248 249 void ProcessesEventRouter::ListenerRemoved() { 250 DCHECK_GT(listeners_, 0); 251 --listeners_; 252 #if defined(ENABLE_TASK_MANAGER) 253 // The task manager has its own ref count to balance other callers of 254 // StartUpdating/StopUpdating. 255 model_->StopUpdating(); 256 #endif // defined(ENABLE_TASK_MANAGER) 257 } 258 259 void ProcessesEventRouter::StartTaskManagerListening() { 260 #if defined(ENABLE_TASK_MANAGER) 261 if (!task_manager_listening_) { 262 model_->StartListening(); 263 task_manager_listening_ = true; 264 } 265 #endif // defined(ENABLE_TASK_MANAGER) 266 } 267 268 void ProcessesEventRouter::Observe( 269 int type, 270 const content::NotificationSource& source, 271 const content::NotificationDetails& details) { 272 273 switch (type) { 274 case content::NOTIFICATION_RENDER_WIDGET_HOST_HANG: 275 ProcessHangEvent( 276 content::Source<content::RenderWidgetHost>(source).ptr()); 277 break; 278 case content::NOTIFICATION_RENDERER_PROCESS_CLOSED: 279 ProcessClosedEvent( 280 content::Source<content::RenderProcessHost>(source).ptr(), 281 content::Details<content::RenderProcessHost::RendererClosedDetails>( 282 details).ptr()); 283 break; 284 default: 285 NOTREACHED() << "Unexpected observe of type " << type; 286 } 287 return; 288 } 289 290 void ProcessesEventRouter::OnItemsAdded(int start, int length) { 291 #if defined(ENABLE_TASK_MANAGER) 292 DCHECK_EQ(length, 1); 293 int index = start; 294 295 std::string event(keys::kOnCreated); 296 if (!HasEventListeners(event)) 297 return; 298 299 // If the item being added is not the first one in the group, find the base 300 // index and use it for retrieving the process data. 301 if (!model_->IsResourceFirstInGroup(start)) { 302 index = model_->GetGroupIndexForResource(start); 303 } 304 305 scoped_ptr<base::ListValue> args(new base::ListValue()); 306 base::DictionaryValue* process = CreateProcessFromModel( 307 model_->GetUniqueChildProcessId(index), model_, index, false); 308 DCHECK(process != NULL); 309 310 if (process == NULL) 311 return; 312 313 args->Append(process); 314 315 DispatchEvent(keys::kOnCreated, args.Pass()); 316 #endif // defined(ENABLE_TASK_MANAGER) 317 } 318 319 void ProcessesEventRouter::OnItemsChanged(int start, int length) { 320 #if defined(ENABLE_TASK_MANAGER) 321 // If we don't have any listeners, return immediately. 322 if (listeners_ == 0) 323 return; 324 325 if (!model_) 326 return; 327 328 // We need to know which type of onUpdated events to fire and whether to 329 // collect memory or not. 330 std::string updated_event(keys::kOnUpdated); 331 std::string updated_event_memory(keys::kOnUpdatedWithMemory); 332 bool updated = HasEventListeners(updated_event); 333 bool updated_memory = HasEventListeners(updated_event_memory); 334 335 DCHECK(updated || updated_memory); 336 337 IDMap<base::DictionaryValue> processes_map; 338 for (int i = start; i < start + length; i++) { 339 if (model_->IsResourceFirstInGroup(i)) { 340 int id = model_->GetUniqueChildProcessId(i); 341 base::DictionaryValue* process = CreateProcessFromModel(id, model_, i, 342 true); 343 processes_map.AddWithID(process, i); 344 } 345 } 346 347 int id; 348 std::string idkey(keys::kIdKey); 349 base::DictionaryValue* processes = new base::DictionaryValue(); 350 351 if (updated) { 352 IDMap<base::DictionaryValue>::iterator it(&processes_map); 353 for (; !it.IsAtEnd(); it.Advance()) { 354 if (!it.GetCurrentValue()->GetInteger(idkey, &id)) 355 continue; 356 357 // Store each process indexed by the string version of its id. 358 processes->Set(base::IntToString(id), it.GetCurrentValue()); 359 } 360 361 scoped_ptr<base::ListValue> args(new base::ListValue()); 362 args->Append(processes); 363 DispatchEvent(keys::kOnUpdated, args.Pass()); 364 } 365 366 if (updated_memory) { 367 IDMap<base::DictionaryValue>::iterator it(&processes_map); 368 for (; !it.IsAtEnd(); it.Advance()) { 369 if (!it.GetCurrentValue()->GetInteger(idkey, &id)) 370 continue; 371 372 AddMemoryDetails(it.GetCurrentValue(), model_, it.GetCurrentKey()); 373 374 // Store each process indexed by the string version of its id if we didn't 375 // already insert it as part of the onUpdated processing above. 376 if (!updated) 377 processes->Set(base::IntToString(id), it.GetCurrentValue()); 378 } 379 380 scoped_ptr<base::ListValue> args(new base::ListValue()); 381 args->Append(processes); 382 DispatchEvent(keys::kOnUpdatedWithMemory, args.Pass()); 383 } 384 #endif // defined(ENABLE_TASK_MANAGER) 385 } 386 387 void ProcessesEventRouter::OnItemsToBeRemoved(int start, int length) { 388 #if defined(ENABLE_TASK_MANAGER) 389 DCHECK_EQ(length, 1); 390 391 // Process exit for renderer processes has the data about exit code and 392 // termination status, therefore we will rely on notifications and not on 393 // the Task Manager data. We do use the rest of this method for non-renderer 394 // processes. 395 if (model_->GetResourceType(start) == task_manager::Resource::RENDERER) 396 return; 397 398 // The callback function parameters. 399 scoped_ptr<base::ListValue> args(new base::ListValue()); 400 401 // First arg: The id of the process that was closed. 402 args->Append(new base::FundamentalValue( 403 model_->GetUniqueChildProcessId(start))); 404 405 // Second arg: The exit type for the process. 406 args->Append(new base::FundamentalValue(0)); 407 408 // Third arg: The exit code for the process. 409 args->Append(new base::FundamentalValue(0)); 410 411 DispatchEvent(keys::kOnExited, args.Pass()); 412 #endif // defined(ENABLE_TASK_MANAGER) 413 } 414 415 void ProcessesEventRouter::ProcessHangEvent(content::RenderWidgetHost* widget) { 416 #if defined(ENABLE_TASK_MANAGER) 417 std::string event(keys::kOnUnresponsive); 418 if (!HasEventListeners(event)) 419 return; 420 421 base::DictionaryValue* process = NULL; 422 int count = model_->ResourceCount(); 423 int id = widget->GetProcess()->GetID(); 424 425 for (int i = 0; i < count; ++i) { 426 if (model_->IsResourceFirstInGroup(i)) { 427 if (id == model_->GetUniqueChildProcessId(i)) { 428 process = CreateProcessFromModel(id, model_, i, false); 429 break; 430 } 431 } 432 } 433 434 if (process == NULL) 435 return; 436 437 scoped_ptr<base::ListValue> args(new base::ListValue()); 438 args->Append(process); 439 440 DispatchEvent(keys::kOnUnresponsive, args.Pass()); 441 #endif // defined(ENABLE_TASK_MANAGER) 442 } 443 444 void ProcessesEventRouter::ProcessClosedEvent( 445 content::RenderProcessHost* rph, 446 content::RenderProcessHost::RendererClosedDetails* details) { 447 #if defined(ENABLE_TASK_MANAGER) 448 // The callback function parameters. 449 scoped_ptr<base::ListValue> args(new base::ListValue()); 450 451 // First arg: The id of the process that was closed. 452 args->Append(new base::FundamentalValue(rph->GetID())); 453 454 // Second arg: The exit type for the process. 455 args->Append(new base::FundamentalValue(details->status)); 456 457 // Third arg: The exit code for the process. 458 args->Append(new base::FundamentalValue(details->exit_code)); 459 460 DispatchEvent(keys::kOnExited, args.Pass()); 461 #endif // defined(ENABLE_TASK_MANAGER) 462 } 463 464 void ProcessesEventRouter::DispatchEvent( 465 const std::string& event_name, 466 scoped_ptr<base::ListValue> event_args) { 467 EventRouter* event_router = EventRouter::Get(browser_context_); 468 if (event_router) { 469 scoped_ptr<extensions::Event> event(new extensions::Event( 470 event_name, event_args.Pass())); 471 event_router->BroadcastEvent(event.Pass()); 472 } 473 } 474 475 bool ProcessesEventRouter::HasEventListeners(const std::string& event_name) { 476 EventRouter* event_router = EventRouter::Get(browser_context_); 477 return event_router && event_router->HasEventListener(event_name); 478 } 479 480 ProcessesAPI::ProcessesAPI(content::BrowserContext* context) 481 : browser_context_(context) { 482 EventRouter* event_router = EventRouter::Get(browser_context_); 483 event_router->RegisterObserver(this, processes_api_constants::kOnUpdated); 484 event_router->RegisterObserver(this, 485 processes_api_constants::kOnUpdatedWithMemory); 486 ExtensionFunctionRegistry* registry = 487 ExtensionFunctionRegistry::GetInstance(); 488 registry->RegisterFunction<extensions::GetProcessIdForTabFunction>(); 489 registry->RegisterFunction<extensions::TerminateFunction>(); 490 registry->RegisterFunction<extensions::GetProcessInfoFunction>(); 491 } 492 493 ProcessesAPI::~ProcessesAPI() { 494 } 495 496 void ProcessesAPI::Shutdown() { 497 EventRouter::Get(browser_context_)->UnregisterObserver(this); 498 } 499 500 static base::LazyInstance<BrowserContextKeyedAPIFactory<ProcessesAPI> > 501 g_factory = LAZY_INSTANCE_INITIALIZER; 502 503 // static 504 BrowserContextKeyedAPIFactory<ProcessesAPI>* 505 ProcessesAPI::GetFactoryInstance() { 506 return g_factory.Pointer(); 507 } 508 509 // static 510 ProcessesAPI* ProcessesAPI::Get(content::BrowserContext* context) { 511 return BrowserContextKeyedAPIFactory<ProcessesAPI>::Get(context); 512 } 513 514 ProcessesEventRouter* ProcessesAPI::processes_event_router() { 515 if (!processes_event_router_) 516 processes_event_router_.reset(new ProcessesEventRouter(browser_context_)); 517 return processes_event_router_.get(); 518 } 519 520 void ProcessesAPI::OnListenerAdded(const EventListenerInfo& details) { 521 // We lazily tell the TaskManager to start updating when listeners to the 522 // processes.onUpdated or processes.onUpdatedWithMemory events arrive. 523 processes_event_router()->ListenerAdded(); 524 } 525 526 void ProcessesAPI::OnListenerRemoved(const EventListenerInfo& details) { 527 // If a processes.onUpdated or processes.onUpdatedWithMemory event listener 528 // is removed (or a process with one exits), then we let the extension API 529 // know that it has one fewer listener. 530 processes_event_router()->ListenerRemoved(); 531 } 532 533 GetProcessIdForTabFunction::GetProcessIdForTabFunction() : tab_id_(-1) { 534 } 535 536 bool GetProcessIdForTabFunction::RunAsync() { 537 #if defined(ENABLE_TASK_MANAGER) 538 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &tab_id_)); 539 540 // Add a reference, which is balanced in GetProcessIdForTab to keep the object 541 // around and allow for the callback to be invoked. 542 AddRef(); 543 544 // If the task manager is already listening, just post a task to execute 545 // which will invoke the callback once we have returned from this function. 546 // Otherwise, wait for the notification that the task manager is done with 547 // the data gathering. 548 if (ProcessesAPI::Get(GetProfile()) 549 ->processes_event_router() 550 ->is_task_manager_listening()) { 551 base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind( 552 &GetProcessIdForTabFunction::GetProcessIdForTab, this)); 553 } else { 554 TaskManager::GetInstance()->model()->RegisterOnDataReadyCallback( 555 base::Bind(&GetProcessIdForTabFunction::GetProcessIdForTab, this)); 556 557 ProcessesAPI::Get(GetProfile()) 558 ->processes_event_router() 559 ->StartTaskManagerListening(); 560 } 561 562 return true; 563 #else 564 error_ = errors::kExtensionNotSupported; 565 return false; 566 #endif // defined(ENABLE_TASK_MANAGER) 567 } 568 569 void GetProcessIdForTabFunction::GetProcessIdForTab() { 570 content::WebContents* contents = NULL; 571 int tab_index = -1; 572 if (!ExtensionTabUtil::GetTabById(tab_id_, 573 GetProfile(), 574 include_incognito(), 575 NULL, 576 NULL, 577 &contents, 578 &tab_index)) { 579 error_ = ErrorUtils::FormatErrorMessage( 580 extensions::tabs_constants::kTabNotFoundError, 581 base::IntToString(tab_id_)); 582 SetResult(new base::FundamentalValue(-1)); 583 SendResponse(false); 584 } else { 585 int process_id = contents->GetRenderProcessHost()->GetID(); 586 SetResult(new base::FundamentalValue(process_id)); 587 SendResponse(true); 588 } 589 590 // Balance the AddRef in the RunAsync. 591 Release(); 592 } 593 594 TerminateFunction::TerminateFunction() : process_id_(-1) { 595 } 596 597 bool TerminateFunction::RunAsync() { 598 #if defined(ENABLE_TASK_MANAGER) 599 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &process_id_)); 600 601 // Add a reference, which is balanced in TerminateProcess to keep the object 602 // around and allow for the callback to be invoked. 603 AddRef(); 604 605 // If the task manager is already listening, just post a task to execute 606 // which will invoke the callback once we have returned from this function. 607 // Otherwise, wait for the notification that the task manager is done with 608 // the data gathering. 609 if (ProcessesAPI::Get(GetProfile()) 610 ->processes_event_router() 611 ->is_task_manager_listening()) { 612 base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind( 613 &TerminateFunction::TerminateProcess, this)); 614 } else { 615 TaskManager::GetInstance()->model()->RegisterOnDataReadyCallback( 616 base::Bind(&TerminateFunction::TerminateProcess, this)); 617 618 ProcessesAPI::Get(GetProfile()) 619 ->processes_event_router() 620 ->StartTaskManagerListening(); 621 } 622 623 return true; 624 #else 625 error_ = errors::kExtensionNotSupported; 626 return false; 627 #endif // defined(ENABLE_TASK_MANAGER) 628 } 629 630 631 void TerminateFunction::TerminateProcess() { 632 #if defined(ENABLE_TASK_MANAGER) 633 TaskManagerModel* model = TaskManager::GetInstance()->model(); 634 635 int count = model->ResourceCount(); 636 bool killed = false; 637 bool found = false; 638 639 for (int i = 0; i < count; ++i) { 640 if (model->IsResourceFirstInGroup(i)) { 641 if (process_id_ == model->GetUniqueChildProcessId(i)) { 642 found = true; 643 killed = base::KillProcess(model->GetProcess(i), 644 content::RESULT_CODE_KILLED, true); 645 UMA_HISTOGRAM_COUNTS("ChildProcess.KilledByExtensionAPI", 1); 646 break; 647 } 648 } 649 } 650 651 if (!found) { 652 error_ = ErrorUtils::FormatErrorMessage(errors::kProcessNotFound, 653 base::IntToString(process_id_)); 654 SendResponse(false); 655 } else { 656 SetResult(new base::FundamentalValue(killed)); 657 SendResponse(true); 658 } 659 660 // Balance the AddRef in the RunAsync. 661 Release(); 662 #else 663 error_ = errors::kExtensionNotSupported; 664 SendResponse(false); 665 #endif // defined(ENABLE_TASK_MANAGER) 666 } 667 668 GetProcessInfoFunction::GetProcessInfoFunction() 669 #if defined(ENABLE_TASK_MANAGER) 670 : memory_(false) 671 #endif 672 { 673 } 674 675 GetProcessInfoFunction::~GetProcessInfoFunction() { 676 } 677 678 bool GetProcessInfoFunction::RunAsync() { 679 #if defined(ENABLE_TASK_MANAGER) 680 base::Value* processes = NULL; 681 682 EXTENSION_FUNCTION_VALIDATE(args_->Get(0, &processes)); 683 EXTENSION_FUNCTION_VALIDATE(args_->GetBoolean(1, &memory_)); 684 685 EXTENSION_FUNCTION_VALIDATE(extensions::ReadOneOrMoreIntegers( 686 processes, &process_ids_)); 687 688 // Add a reference, which is balanced in GatherProcessInfo to keep the object 689 // around and allow for the callback to be invoked. 690 AddRef(); 691 692 // If the task manager is already listening, just post a task to execute 693 // which will invoke the callback once we have returned from this function. 694 // Otherwise, wait for the notification that the task manager is done with 695 // the data gathering. 696 if (ProcessesAPI::Get(GetProfile()) 697 ->processes_event_router() 698 ->is_task_manager_listening()) { 699 base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind( 700 &GetProcessInfoFunction::GatherProcessInfo, this)); 701 } else { 702 TaskManager::GetInstance()->model()->RegisterOnDataReadyCallback( 703 base::Bind(&GetProcessInfoFunction::GatherProcessInfo, this)); 704 705 ProcessesAPI::Get(GetProfile()) 706 ->processes_event_router() 707 ->StartTaskManagerListening(); 708 } 709 return true; 710 711 #else 712 error_ = errors::kExtensionNotSupported; 713 return false; 714 #endif // defined(ENABLE_TASK_MANAGER) 715 } 716 717 void GetProcessInfoFunction::GatherProcessInfo() { 718 #if defined(ENABLE_TASK_MANAGER) 719 TaskManagerModel* model = TaskManager::GetInstance()->model(); 720 base::DictionaryValue* processes = new base::DictionaryValue(); 721 722 // If there are no process IDs specified, it means we need to return all of 723 // the ones we know of. 724 if (process_ids_.size() == 0) { 725 int resources = model->ResourceCount(); 726 for (int i = 0; i < resources; ++i) { 727 if (model->IsResourceFirstInGroup(i)) { 728 int id = model->GetUniqueChildProcessId(i); 729 base::DictionaryValue* d = CreateProcessFromModel(id, model, i, false); 730 if (memory_) 731 AddMemoryDetails(d, model, i); 732 processes->Set(base::IntToString(id), d); 733 } 734 } 735 } else { 736 int resources = model->ResourceCount(); 737 for (int i = 0; i < resources; ++i) { 738 if (model->IsResourceFirstInGroup(i)) { 739 int id = model->GetUniqueChildProcessId(i); 740 std::vector<int>::iterator proc_id = std::find(process_ids_.begin(), 741 process_ids_.end(), id); 742 if (proc_id != process_ids_.end()) { 743 base::DictionaryValue* d = 744 CreateProcessFromModel(id, model, i, false); 745 if (memory_) 746 AddMemoryDetails(d, model, i); 747 processes->Set(base::IntToString(id), d); 748 749 process_ids_.erase(proc_id); 750 if (process_ids_.size() == 0) 751 break; 752 } 753 } 754 } 755 DCHECK_EQ(process_ids_.size(), 0U); 756 } 757 758 SetResult(processes); 759 SendResponse(true); 760 761 // Balance the AddRef in the RunAsync. 762 Release(); 763 #endif // defined(ENABLE_TASK_MANAGER) 764 } 765 766 } // namespace extensions 767