task_manager.cc revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
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/task_manager/task_manager.h"
6
7#include "base/bind.h"
8#include "base/compiler_specific.h"
9#include "base/i18n/number_formatting.h"
10#include "base/i18n/rtl.h"
11#include "base/prefs/pref_registry_simple.h"
12#include "base/process_util.h"
13#include "base/rand_util.h"
14#include "base/stl_util.h"
15#include "base/stringprintf.h"
16#include "base/strings/string_number_conversions.h"
17#include "base/threading/thread.h"
18#include "base/utf_string_conversions.h"
19#include "chrome/browser/background/background_contents_service.h"
20#include "chrome/browser/background/background_contents_service_factory.h"
21#include "chrome/browser/browser_process.h"
22#include "chrome/browser/extensions/extension_host.h"
23#include "chrome/browser/extensions/extension_process_manager.h"
24#include "chrome/browser/extensions/extension_system.h"
25#include "chrome/browser/profiles/profile_manager.h"
26#include "chrome/browser/task_manager/task_manager_resource_providers.h"
27#include "chrome/browser/task_manager/task_manager_worker_resource_provider.h"
28#include "chrome/browser/ui/browser_finder.h"
29#include "chrome/browser/ui/browser_list.h"
30#include "chrome/browser/ui/browser_navigator.h"
31#include "chrome/common/pref_names.h"
32#include "chrome/common/url_constants.h"
33#include "chrome/common/view_type.h"
34#include "content/public/browser/browser_child_process_host.h"
35#include "content/public/browser/browser_thread.h"
36#include "content/public/browser/gpu_data_manager.h"
37#include "content/public/browser/gpu_data_manager_observer.h"
38#include "content/public/browser/render_view_host.h"
39#include "content/public/browser/resource_request_info.h"
40#include "content/public/browser/web_contents.h"
41#include "content/public/browser/web_contents_delegate.h"
42#include "content/public/common/result_codes.h"
43#include "grit/chromium_strings.h"
44#include "grit/generated_resources.h"
45#include "grit/ui_resources.h"
46#include "third_party/icu/public/i18n/unicode/coll.h"
47#include "ui/base/l10n/l10n_util.h"
48#include "ui/base/resource/resource_bundle.h"
49#include "ui/base/text/bytes_formatting.h"
50#include "ui/gfx/image/image_skia.h"
51
52#if defined(OS_WIN)
53#include "chrome/browser/task_manager/task_manager_os_resources_win.h"
54#endif
55
56using content::BrowserThread;
57using content::OpenURLParams;
58using content::Referrer;
59using content::ResourceRequestInfo;
60using content::WebContents;
61
62namespace {
63
64// The delay between updates of the information (in ms).
65#if defined(OS_MACOSX)
66// Match Activity Monitor's default refresh rate.
67const int kUpdateTimeMs = 2000;
68#else
69const int kUpdateTimeMs = 1000;
70#endif
71
72template <class T>
73int ValueCompare(T value1, T value2) {
74  if (value1 < value2)
75    return -1;
76  if (value1 == value2)
77    return 0;
78  return 1;
79}
80
81// Used when one or both of the results to compare are unavailable.
82int OrderUnavailableValue(bool v1, bool v2) {
83  if (!v1 && !v2)
84    return 0;
85  return v1 ? 1 : -1;
86}
87
88// Used by TaskManagerModel::CompareValues(). See it for details of return
89// value.
90template <class T>
91int ValueCompareMember(const TaskManagerModel* model,
92                       bool (TaskManagerModel::*f)(int, T*) const,
93                       int row1,
94                       int row2) {
95  T value1;
96  T value2;
97  bool value1_valid = (model->*f)(row1, &value1);
98  bool value2_valid = (model->*f)(row2, &value2);
99  return value1_valid && value2_valid ? ValueCompare(value1, value2) :
100      OrderUnavailableValue(value1_valid, value2_valid);
101}
102
103string16 FormatStatsSize(const WebKit::WebCache::ResourceTypeStat& stat) {
104  return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_CACHE_SIZE_CELL_TEXT,
105      ui::FormatBytesWithUnits(stat.size, ui::DATA_UNITS_KIBIBYTE, false),
106      ui::FormatBytesWithUnits(stat.liveSize, ui::DATA_UNITS_KIBIBYTE, false));
107}
108
109// Returns true if the specified id should use the first value in the group.
110bool IsSharedByGroup(int col_id) {
111  switch (col_id) {
112    case IDS_TASK_MANAGER_PRIVATE_MEM_COLUMN:
113    case IDS_TASK_MANAGER_SHARED_MEM_COLUMN:
114    case IDS_TASK_MANAGER_PHYSICAL_MEM_COLUMN:
115    case IDS_TASK_MANAGER_CPU_COLUMN:
116    case IDS_TASK_MANAGER_PROCESS_ID_COLUMN:
117    case IDS_TASK_MANAGER_JAVASCRIPT_MEMORY_ALLOCATED_COLUMN:
118    case IDS_TASK_MANAGER_SQLITE_MEMORY_USED_COLUMN:
119    case IDS_TASK_MANAGER_WEBCORE_IMAGE_CACHE_COLUMN:
120    case IDS_TASK_MANAGER_WEBCORE_SCRIPTS_CACHE_COLUMN:
121    case IDS_TASK_MANAGER_WEBCORE_CSS_CACHE_COLUMN:
122      return true;
123    default:
124      return false;
125  }
126}
127
128}  // namespace
129
130class TaskManagerModelGpuDataManagerObserver
131    : public content::GpuDataManagerObserver {
132 public:
133  TaskManagerModelGpuDataManagerObserver() {
134    content::GpuDataManager::GetInstance()->AddObserver(this);
135  }
136
137  virtual ~TaskManagerModelGpuDataManagerObserver() {
138    content::GpuDataManager::GetInstance()->RemoveObserver(this);
139  }
140
141  static void NotifyVideoMemoryUsageStats(
142      content::GPUVideoMemoryUsageStats video_memory_usage_stats) {
143    TaskManager::GetInstance()->model()->NotifyVideoMemoryUsageStats(
144        video_memory_usage_stats);
145  }
146
147  virtual void OnVideoMemoryUsageStatsUpdate(
148      const content::GPUVideoMemoryUsageStats& video_memory_usage_stats)
149          OVERRIDE {
150    if (BrowserThread::CurrentlyOn(BrowserThread::UI)) {
151      NotifyVideoMemoryUsageStats(video_memory_usage_stats);
152    } else {
153      BrowserThread::PostTask(
154          BrowserThread::UI, FROM_HERE, base::Bind(
155              &TaskManagerModelGpuDataManagerObserver::
156                  NotifyVideoMemoryUsageStats,
157              video_memory_usage_stats));
158    }
159  }
160};
161
162TaskManagerModel::PerResourceValues::PerResourceValues()
163    : is_title_valid(false),
164      is_profile_name_valid(false),
165      network_usage(0),
166      is_process_id_valid(false),
167      process_id(0),
168      is_goats_teleported_valid(false),
169      goats_teleported(0),
170      is_webcore_stats_valid(false),
171      is_fps_valid(false),
172      fps(0),
173      is_sqlite_memory_bytes_valid(false),
174      sqlite_memory_bytes(0),
175      is_v8_memory_valid(false),
176      v8_memory_allocated(0),
177      v8_memory_used(0) {
178}
179
180TaskManagerModel::PerResourceValues::~PerResourceValues() {
181}
182
183TaskManagerModel::PerProcessValues::PerProcessValues()
184    : is_cpu_usage_valid(false),
185      cpu_usage(0),
186      is_private_and_shared_valid(false),
187      private_bytes(0),
188      shared_bytes(0),
189      is_physical_memory_valid(false),
190      physical_memory(0),
191      is_video_memory_valid(false),
192      video_memory(0),
193      video_memory_has_duplicates(false),
194      is_gdi_handles_valid(false),
195      gdi_handles(0),
196      gdi_handles_peak(0),
197      is_user_handles_valid(0),
198      user_handles(0),
199      user_handles_peak(0) {
200}
201
202TaskManagerModel::PerProcessValues::~PerProcessValues() {
203}
204
205////////////////////////////////////////////////////////////////////////////////
206// TaskManagerModel class
207////////////////////////////////////////////////////////////////////////////////
208
209TaskManagerModel::TaskManagerModel(TaskManager* task_manager)
210    : pending_video_memory_usage_stats_update_(false),
211      update_requests_(0),
212      listen_requests_(0),
213      update_state_(IDLE),
214      goat_salt_(base::RandUint64()),
215      last_unique_id_(0) {
216  AddResourceProvider(
217      new TaskManagerBrowserProcessResourceProvider(task_manager));
218  AddResourceProvider(
219      new TaskManagerBackgroundContentsResourceProvider(task_manager));
220  AddResourceProvider(new TaskManagerTabContentsResourceProvider(task_manager));
221  AddResourceProvider(new TaskManagerPanelResourceProvider(task_manager));
222  AddResourceProvider(
223      new TaskManagerChildProcessResourceProvider(task_manager));
224  AddResourceProvider(
225      new TaskManagerExtensionProcessResourceProvider(task_manager));
226  AddResourceProvider(
227      new TaskManagerGuestResourceProvider(task_manager));
228
229#if defined(ENABLE_NOTIFICATIONS)
230  TaskManager::ResourceProvider* provider =
231      TaskManagerNotificationResourceProvider::Create(task_manager);
232  if (provider)
233    AddResourceProvider(provider);
234#endif
235
236  AddResourceProvider(new TaskManagerWorkerResourceProvider(task_manager));
237}
238
239void TaskManagerModel::AddObserver(TaskManagerModelObserver* observer) {
240  observer_list_.AddObserver(observer);
241}
242
243void TaskManagerModel::RemoveObserver(TaskManagerModelObserver* observer) {
244  observer_list_.RemoveObserver(observer);
245}
246
247int TaskManagerModel::ResourceCount() const {
248  return resources_.size();
249}
250
251int TaskManagerModel::GroupCount() const {
252  return group_map_.size();
253}
254
255int64 TaskManagerModel::GetNetworkUsage(int index) const {
256  return GetNetworkUsage(GetResource(index));
257}
258
259double TaskManagerModel::GetCPUUsage(int index) const {
260  return GetCPUUsage(GetResource(index));
261}
262
263base::ProcessId TaskManagerModel::GetProcessId(int index) const {
264  PerResourceValues& values(GetPerResourceValues(index));
265  if (!values.is_process_id_valid) {
266    values.is_process_id_valid = true;
267    values.process_id = base::GetProcId(GetResource(index)->GetProcess());
268  }
269  return values.process_id;
270}
271
272base::ProcessHandle TaskManagerModel::GetProcess(int index) const {
273  return GetResource(index)->GetProcess();
274}
275
276int TaskManagerModel::GetResourceUniqueId(int index) const {
277  return GetResource(index)->get_unique_id();
278}
279
280int TaskManagerModel::GetResourceIndexByUniqueId(const int unique_id) const {
281  for (int resource_index = 0; resource_index < ResourceCount();
282       ++resource_index) {
283    if (GetResourceUniqueId(resource_index) == unique_id)
284      return resource_index;
285  }
286  return -1;
287}
288
289string16 TaskManagerModel::GetResourceById(int index, int col_id) const {
290  if (IsSharedByGroup(col_id) && !IsResourceFirstInGroup(index))
291    return string16();
292
293  switch (col_id) {
294    case IDS_TASK_MANAGER_TASK_COLUMN:
295      return GetResourceTitle(index);
296
297    case IDS_TASK_MANAGER_PROFILE_NAME_COLUMN:
298      return GetResourceProfileName(index);
299
300    case IDS_TASK_MANAGER_NET_COLUMN:
301      return GetResourceNetworkUsage(index);
302
303    case IDS_TASK_MANAGER_CPU_COLUMN:
304      return GetResourceCPUUsage(index);
305
306    case IDS_TASK_MANAGER_PRIVATE_MEM_COLUMN:
307      return GetResourcePrivateMemory(index);
308
309    case IDS_TASK_MANAGER_SHARED_MEM_COLUMN:
310      return GetResourceSharedMemory(index);
311
312    case IDS_TASK_MANAGER_PHYSICAL_MEM_COLUMN:
313      return GetResourcePhysicalMemory(index);
314
315    case IDS_TASK_MANAGER_PROCESS_ID_COLUMN:
316      return GetResourceProcessId(index);
317
318    case IDS_TASK_MANAGER_GDI_HANDLES_COLUMN:
319      return GetResourceGDIHandles(index);
320
321    case IDS_TASK_MANAGER_USER_HANDLES_COLUMN:
322      return GetResourceUSERHandles(index);
323
324    case IDS_TASK_MANAGER_GOATS_TELEPORTED_COLUMN:
325      return GetResourceGoatsTeleported(index);
326
327    case IDS_TASK_MANAGER_WEBCORE_IMAGE_CACHE_COLUMN:
328      return GetResourceWebCoreImageCacheSize(index);
329
330    case IDS_TASK_MANAGER_WEBCORE_SCRIPTS_CACHE_COLUMN:
331      return GetResourceWebCoreScriptsCacheSize(index);
332
333    case IDS_TASK_MANAGER_WEBCORE_CSS_CACHE_COLUMN:
334      return GetResourceWebCoreCSSCacheSize(index);
335
336    case IDS_TASK_MANAGER_FPS_COLUMN:
337      return GetResourceFPS(index);
338
339    case IDS_TASK_MANAGER_VIDEO_MEMORY_COLUMN:
340      return GetResourceVideoMemory(index);
341
342    case IDS_TASK_MANAGER_SQLITE_MEMORY_USED_COLUMN:
343      return GetResourceSqliteMemoryUsed(index);
344
345    case IDS_TASK_MANAGER_JAVASCRIPT_MEMORY_ALLOCATED_COLUMN:
346      return GetResourceV8MemoryAllocatedSize(index);
347
348    default:
349      NOTREACHED();
350      return string16();
351  }
352}
353
354const string16& TaskManagerModel::GetResourceTitle(int index) const {
355  PerResourceValues& values = GetPerResourceValues(index);
356  if (!values.is_title_valid) {
357    values.is_title_valid = true;
358    values.title = GetResource(index)->GetTitle();
359  }
360  return values.title;
361}
362
363const string16& TaskManagerModel::GetResourceProfileName(int index) const {
364  PerResourceValues& values(GetPerResourceValues(index));
365  if (!values.is_profile_name_valid) {
366    values.is_profile_name_valid = true;
367    values.profile_name = GetResource(index)->GetProfileName();
368  }
369  return values.profile_name;
370}
371
372string16 TaskManagerModel::GetResourceNetworkUsage(int index) const {
373  int64 net_usage = GetNetworkUsage(index);
374  if (net_usage == -1)
375    return l10n_util::GetStringUTF16(IDS_TASK_MANAGER_NA_CELL_TEXT);
376  if (net_usage == 0)
377    return ASCIIToUTF16("0");
378  string16 net_byte = ui::FormatSpeed(net_usage);
379  // Force number string to have LTR directionality.
380  return base::i18n::GetDisplayStringInLTRDirectionality(net_byte);
381}
382
383string16 TaskManagerModel::GetResourceCPUUsage(int index) const {
384  return UTF8ToUTF16(base::StringPrintf(
385#if defined(OS_MACOSX)
386      // Activity Monitor shows %cpu with one decimal digit -- be
387      // consistent with that.
388      "%.1f",
389#else
390      "%.0f",
391#endif
392      GetCPUUsage(GetResource(index))));
393}
394
395string16 TaskManagerModel::GetResourcePrivateMemory(int index) const {
396  size_t private_mem;
397  if (!GetPrivateMemory(index, &private_mem))
398    return ASCIIToUTF16("N/A");
399  return GetMemCellText(private_mem);
400}
401
402string16 TaskManagerModel::GetResourceSharedMemory(int index) const {
403  size_t shared_mem;
404  if (!GetSharedMemory(index, &shared_mem))
405    return ASCIIToUTF16("N/A");
406  return GetMemCellText(shared_mem);
407}
408
409string16 TaskManagerModel::GetResourcePhysicalMemory(int index) const {
410  size_t phys_mem;
411  GetPhysicalMemory(index, &phys_mem);
412  return GetMemCellText(phys_mem);
413}
414
415string16 TaskManagerModel::GetResourceProcessId(int index) const {
416  return base::IntToString16(GetProcessId(index));
417}
418
419string16 TaskManagerModel::GetResourceGDIHandles(int index) const {
420  size_t current, peak;
421  GetGDIHandles(index, &current, &peak);
422  return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_HANDLES_CELL_TEXT,
423      base::IntToString16(current), base::IntToString16(peak));
424}
425
426string16 TaskManagerModel::GetResourceUSERHandles(int index) const {
427  size_t current, peak;
428  GetUSERHandles(index, &current, &peak);
429  return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_HANDLES_CELL_TEXT,
430      base::IntToString16(current), base::IntToString16(peak));
431}
432
433string16 TaskManagerModel::GetResourceWebCoreImageCacheSize(
434    int index) const {
435  if (!CacheWebCoreStats(index))
436    return l10n_util::GetStringUTF16(IDS_TASK_MANAGER_NA_CELL_TEXT);
437  return FormatStatsSize(GetPerResourceValues(index).webcore_stats.images);
438}
439
440string16 TaskManagerModel::GetResourceWebCoreScriptsCacheSize(
441    int index) const {
442  if (!CacheWebCoreStats(index))
443    return l10n_util::GetStringUTF16(IDS_TASK_MANAGER_NA_CELL_TEXT);
444  return FormatStatsSize(GetPerResourceValues(index).webcore_stats.scripts);
445}
446
447string16 TaskManagerModel::GetResourceWebCoreCSSCacheSize(
448    int index) const {
449  if (!CacheWebCoreStats(index))
450    return l10n_util::GetStringUTF16(IDS_TASK_MANAGER_NA_CELL_TEXT);
451  return FormatStatsSize(
452      GetPerResourceValues(index).webcore_stats.cssStyleSheets);
453}
454
455string16 TaskManagerModel::GetResourceVideoMemory(int index) const {
456  size_t video_memory;
457  bool has_duplicates;
458  if (!GetVideoMemory(index, &video_memory, &has_duplicates) || !video_memory)
459    return ASCIIToUTF16("N/A");
460  if (has_duplicates) {
461    return ASCIIToUTF16("(") +
462        GetMemCellText(video_memory) +
463        ASCIIToUTF16(")");
464  }
465  return GetMemCellText(video_memory);
466}
467
468string16 TaskManagerModel::GetResourceFPS(
469    int index) const {
470  float fps = 0;
471  if (!GetFPS(index, &fps))
472    return l10n_util::GetStringUTF16(IDS_TASK_MANAGER_NA_CELL_TEXT);
473  return UTF8ToUTF16(base::StringPrintf("%.0f", fps));
474}
475
476string16 TaskManagerModel::GetResourceSqliteMemoryUsed(int index) const {
477  size_t bytes = 0;
478  if (!GetSqliteMemoryUsedBytes(index, &bytes))
479    return l10n_util::GetStringUTF16(IDS_TASK_MANAGER_NA_CELL_TEXT);
480  return GetMemCellText(bytes);
481}
482
483string16 TaskManagerModel::GetResourceGoatsTeleported(int index) const {
484  CHECK_LT(index, ResourceCount());
485  return base::FormatNumber(GetGoatsTeleported(index));
486}
487
488string16 TaskManagerModel::GetResourceV8MemoryAllocatedSize(
489    int index) const {
490  size_t memory_allocated = 0, memory_used = 0;
491  if (!GetV8MemoryUsed(index, &memory_used) ||
492      !GetV8Memory(index, &memory_allocated))
493    return l10n_util::GetStringUTF16(IDS_TASK_MANAGER_NA_CELL_TEXT);
494  return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_CACHE_SIZE_CELL_TEXT,
495      ui::FormatBytesWithUnits(memory_allocated,
496                               ui::DATA_UNITS_KIBIBYTE,
497                               false),
498      ui::FormatBytesWithUnits(memory_used,
499                               ui::DATA_UNITS_KIBIBYTE,
500                               false));
501}
502
503bool TaskManagerModel::GetPrivateMemory(int index, size_t* result) const {
504  *result = 0;
505  base::ProcessHandle handle = GetResource(index)->GetProcess();
506  if (!CachePrivateAndSharedMemory(handle))
507    return false;
508  *result = per_process_cache_[handle].private_bytes;
509  return true;
510}
511
512bool TaskManagerModel::GetSharedMemory(int index, size_t* result) const {
513  *result = 0;
514  base::ProcessHandle handle = GetResource(index)->GetProcess();
515  if (!CachePrivateAndSharedMemory(handle))
516    return false;
517  *result = per_process_cache_[handle].shared_bytes;
518  return true;
519}
520
521bool TaskManagerModel::GetPhysicalMemory(int index, size_t* result) const {
522  *result = 0;
523
524  base::ProcessHandle handle = GetResource(index)->GetProcess();
525  PerProcessValues& values(per_process_cache_[handle]);
526
527  if (!values.is_physical_memory_valid) {
528    base::WorkingSetKBytes ws_usage;
529    MetricsMap::const_iterator iter = metrics_map_.find(handle);
530    if (iter == metrics_map_.end() ||
531        !iter->second->GetWorkingSetKBytes(&ws_usage))
532      return false;
533
534    // Memory = working_set.private + working_set.shareable.
535    // We exclude the shared memory.
536    values.is_physical_memory_valid = true;
537    values.physical_memory = iter->second->GetWorkingSetSize();
538    values.physical_memory -= ws_usage.shared * 1024;
539  }
540  *result = values.physical_memory;
541  return true;
542}
543
544void TaskManagerModel::GetGDIHandles(int index,
545                                     size_t* current,
546                                     size_t* peak) const {
547  *current = 0;
548  *peak = 0;
549#if defined(OS_WIN)
550  base::ProcessHandle handle = GetResource(index)->GetProcess();
551  PerProcessValues& values(per_process_cache_[handle]);
552
553  if (!values.is_gdi_handles_valid) {
554    GetWinGDIHandles(GetResource(index)->GetProcess(),
555                     &values.gdi_handles,
556                     &values.gdi_handles_peak);
557    values.is_gdi_handles_valid = true;
558  }
559  *current = values.gdi_handles;
560  *peak = values.gdi_handles_peak;
561#endif
562}
563
564void TaskManagerModel::GetUSERHandles(int index,
565                                      size_t* current,
566                                      size_t* peak) const {
567  *current = 0;
568  *peak = 0;
569#if defined(OS_WIN)
570  base::ProcessHandle handle = GetResource(index)->GetProcess();
571  PerProcessValues& values(per_process_cache_[handle]);
572
573  if (!values.is_user_handles_valid) {
574    GetWinUSERHandles(GetResource(index)->GetProcess(),
575                      &values.user_handles,
576                      &values.user_handles_peak);
577    values.is_user_handles_valid = true;
578  }
579  *current = values.user_handles;
580  *peak = values.user_handles_peak;
581#endif
582}
583
584bool TaskManagerModel::GetWebCoreCacheStats(
585    int index,
586    WebKit::WebCache::ResourceTypeStats* result) const {
587  if (!CacheWebCoreStats(index))
588    return false;
589  *result = GetPerResourceValues(index).webcore_stats;
590  return true;
591}
592
593bool TaskManagerModel::GetVideoMemory(int index,
594                                      size_t* video_memory,
595                                      bool* has_duplicates) const {
596  *video_memory = 0;
597  *has_duplicates = false;
598
599  base::ProcessId pid = GetProcessId(index);
600  PerProcessValues& values(
601      per_process_cache_[GetResource(index)->GetProcess()]);
602  if (!values.is_video_memory_valid) {
603    content::GPUVideoMemoryUsageStats::ProcessMap::const_iterator i =
604        video_memory_usage_stats_.process_map.find(pid);
605    if (i == video_memory_usage_stats_.process_map.end())
606      return false;
607    values.is_video_memory_valid = true;
608    values.video_memory = i->second.video_memory;
609    values.video_memory_has_duplicates = i->second.has_duplicates;
610  }
611  *video_memory = values.video_memory;
612  *has_duplicates = values.video_memory_has_duplicates;
613  return true;
614}
615
616bool TaskManagerModel::GetFPS(int index, float* result) const {
617  *result = 0;
618  PerResourceValues& values(GetPerResourceValues(index));
619  if (!values.is_fps_valid) {
620    if (!GetResource(index)->ReportsFPS())
621      return false;
622    values.is_fps_valid = true;
623    values.fps = GetResource(index)->GetFPS();
624  }
625  *result = values.fps;
626  return true;
627}
628
629bool TaskManagerModel::GetSqliteMemoryUsedBytes(
630    int index,
631    size_t* result) const {
632  *result = 0;
633  PerResourceValues& values(GetPerResourceValues(index));
634  if (!values.is_sqlite_memory_bytes_valid) {
635    if (!GetResource(index)->ReportsSqliteMemoryUsed())
636      return false;
637    values.is_sqlite_memory_bytes_valid = true;
638    values.sqlite_memory_bytes = GetResource(index)->SqliteMemoryUsedBytes();
639  }
640  *result = values.sqlite_memory_bytes;
641  return true;
642}
643
644bool TaskManagerModel::GetV8Memory(int index, size_t* result) const {
645  *result = 0;
646  if (!CacheV8Memory(index))
647    return false;
648  *result = GetPerResourceValues(index).v8_memory_allocated;
649  return true;
650}
651
652bool TaskManagerModel::GetV8MemoryUsed(int index, size_t* result) const {
653  *result = 0;
654  if (!CacheV8Memory(index))
655    return false;
656  *result = GetPerResourceValues(index).v8_memory_used;
657  return true;
658}
659
660bool TaskManagerModel::CanActivate(int index) const {
661  CHECK_LT(index, ResourceCount());
662  return GetResourceWebContents(index) != NULL;
663}
664
665bool TaskManagerModel::CanInspect(int index) const {
666  return GetResource(index)->CanInspect();
667}
668
669void TaskManagerModel::Inspect(int index) const {
670  CHECK_LT(index, ResourceCount());
671  GetResource(index)->Inspect();
672}
673
674int TaskManagerModel::GetGoatsTeleported(int index) const {
675  PerResourceValues& values(GetPerResourceValues(index));
676  if (!values.is_goats_teleported_valid) {
677    values.is_goats_teleported_valid = true;
678    values.goats_teleported = goat_salt_ * (index + 1);
679    values.goats_teleported = (values.goats_teleported >> 16) & 255;
680  }
681  return values.goats_teleported;
682}
683
684bool TaskManagerModel::IsResourceFirstInGroup(int index) const {
685  TaskManager::Resource* resource = GetResource(index);
686  GroupMap::const_iterator iter = group_map_.find(resource->GetProcess());
687  DCHECK(iter != group_map_.end());
688  const ResourceList* group = iter->second;
689  return ((*group)[0] == resource);
690}
691
692bool TaskManagerModel::IsResourceLastInGroup(int index) const {
693  TaskManager::Resource* resource = GetResource(index);
694  GroupMap::const_iterator iter = group_map_.find(resource->GetProcess());
695  DCHECK(iter != group_map_.end());
696  const ResourceList* group = iter->second;
697  return (group->back() == resource);
698}
699
700bool TaskManagerModel::IsBackgroundResource(int index) const {
701  return GetResource(index)->IsBackground();
702}
703
704gfx::ImageSkia TaskManagerModel::GetResourceIcon(int index) const {
705  gfx::ImageSkia icon = GetResource(index)->GetIcon();
706  if (!icon.isNull())
707    return icon;
708
709  static gfx::ImageSkia* default_icon = ResourceBundle::GetSharedInstance().
710      GetImageSkiaNamed(IDR_DEFAULT_FAVICON);
711  return *default_icon;
712}
713
714TaskManagerModel::GroupRange
715TaskManagerModel::GetGroupRangeForResource(int index) const {
716  TaskManager::Resource* resource = GetResource(index);
717  GroupMap::const_iterator group_iter =
718      group_map_.find(resource->GetProcess());
719  DCHECK(group_iter != group_map_.end());
720  ResourceList* group = group_iter->second;
721  DCHECK(group);
722  if (group->size() == 1) {
723    return std::make_pair(index, 1);
724  } else {
725    for (int i = index; i >= 0; --i) {
726      if (GetResource(i) == (*group)[0])
727        return std::make_pair(i, group->size());
728    }
729    NOTREACHED();
730    return std::make_pair(-1, -1);
731  }
732}
733
734int TaskManagerModel::GetGroupIndexForResource(int index) const {
735  int group_index = -1;
736  for (int i = 0; i <= index; ++i) {
737    if (IsResourceFirstInGroup(i))
738        group_index++;
739  }
740
741  DCHECK_NE(group_index, -1);
742  return group_index;
743}
744
745int TaskManagerModel::GetResourceIndexForGroup(int group_index,
746                                               int index_in_group) const {
747  int group_count = -1;
748  int count_in_group = -1;
749  for (int i = 0; i < ResourceCount(); ++i) {
750    if (IsResourceFirstInGroup(i))
751      group_count++;
752
753    if (group_count == group_index) {
754      count_in_group++;
755      if (count_in_group == index_in_group)
756        return i;
757    } else if (group_count > group_index) {
758      break;
759    }
760  }
761
762  NOTREACHED();
763  return -1;
764}
765
766int TaskManagerModel::CompareValues(int row1, int row2, int col_id) const {
767  CHECK(row1 < ResourceCount() && row2 < ResourceCount());
768  switch (col_id) {
769    case IDS_TASK_MANAGER_TASK_COLUMN: {
770      static icu::Collator* collator = NULL;
771      if (!collator) {
772        UErrorCode create_status = U_ZERO_ERROR;
773        collator = icu::Collator::createInstance(create_status);
774        if (!U_SUCCESS(create_status)) {
775          collator = NULL;
776          NOTREACHED();
777        }
778      }
779      const string16& title1 = GetResourceTitle(row1);
780      const string16& title2 = GetResourceTitle(row2);
781      UErrorCode compare_status = U_ZERO_ERROR;
782      UCollationResult compare_result = collator->compare(
783          static_cast<const UChar*>(title1.c_str()),
784          static_cast<int>(title1.length()),
785          static_cast<const UChar*>(title2.c_str()),
786          static_cast<int>(title2.length()),
787          compare_status);
788      DCHECK(U_SUCCESS(compare_status));
789      return compare_result;
790    }
791
792    case IDS_TASK_MANAGER_PROFILE_NAME_COLUMN: {
793      const string16& profile1 = GetResourceProfileName(row1);
794      const string16& profile2 = GetResourceProfileName(row2);
795      return profile1.compare(0, profile1.length(), profile2, 0,
796                              profile2.length());
797    }
798
799    case IDS_TASK_MANAGER_NET_COLUMN:
800      return ValueCompare(GetNetworkUsage(GetResource(row1)),
801                          GetNetworkUsage(GetResource(row2)));
802
803    case IDS_TASK_MANAGER_CPU_COLUMN:
804      return ValueCompare(GetCPUUsage(GetResource(row1)),
805                          GetCPUUsage(GetResource(row2)));
806
807    case IDS_TASK_MANAGER_PRIVATE_MEM_COLUMN:
808      return ValueCompareMember(
809          this, &TaskManagerModel::GetPrivateMemory, row1, row2);
810
811    case IDS_TASK_MANAGER_SHARED_MEM_COLUMN:
812      return ValueCompareMember(
813          this, &TaskManagerModel::GetSharedMemory, row1, row2);
814
815    case IDS_TASK_MANAGER_PHYSICAL_MEM_COLUMN:
816      return ValueCompareMember(
817          this, &TaskManagerModel::GetPhysicalMemory, row1, row2);
818
819    case IDS_TASK_MANAGER_PROCESS_ID_COLUMN:
820      return ValueCompare(GetProcessId(row1), GetProcessId(row2));
821
822    case IDS_TASK_MANAGER_GDI_HANDLES_COLUMN: {
823      size_t current1, peak1;
824      size_t current2, peak2;
825      GetGDIHandles(row1, &current1, &peak1);
826      GetGDIHandles(row2, &current2, &peak2);
827      return ValueCompare(current1, current2);
828    }
829
830    case IDS_TASK_MANAGER_USER_HANDLES_COLUMN: {
831      size_t current1, peak1;
832      size_t current2, peak2;
833      GetUSERHandles(row1, &current1, &peak1);
834      GetUSERHandles(row2, &current2, &peak2);
835      return ValueCompare(current1, current2);
836    }
837
838    case IDS_TASK_MANAGER_WEBCORE_IMAGE_CACHE_COLUMN:
839    case IDS_TASK_MANAGER_WEBCORE_SCRIPTS_CACHE_COLUMN:
840    case IDS_TASK_MANAGER_WEBCORE_CSS_CACHE_COLUMN: {
841      bool row1_stats_valid = CacheWebCoreStats(row1);
842      bool row2_stats_valid = CacheWebCoreStats(row2);
843      if (row1_stats_valid && row2_stats_valid) {
844        const WebKit::WebCache::ResourceTypeStats& stats1(
845            GetPerResourceValues(row1).webcore_stats);
846        const WebKit::WebCache::ResourceTypeStats& stats2(
847            GetPerResourceValues(row2).webcore_stats);
848        switch (col_id) {
849          case IDS_TASK_MANAGER_WEBCORE_IMAGE_CACHE_COLUMN:
850            return ValueCompare(stats1.images.size, stats2.images.size);
851          case IDS_TASK_MANAGER_WEBCORE_SCRIPTS_CACHE_COLUMN:
852            return ValueCompare(stats1.scripts.size, stats2.scripts.size);
853          case IDS_TASK_MANAGER_WEBCORE_CSS_CACHE_COLUMN:
854            return ValueCompare(stats1.cssStyleSheets.size,
855                                stats2.cssStyleSheets.size);
856          default:
857            NOTREACHED();
858            return 0;
859        }
860      }
861      return OrderUnavailableValue(row1_stats_valid, row2_stats_valid);
862    }
863
864    case IDS_TASK_MANAGER_FPS_COLUMN:
865      return ValueCompareMember(
866          this, &TaskManagerModel::GetFPS, row1, row2);
867
868    case IDS_TASK_MANAGER_VIDEO_MEMORY_COLUMN: {
869      size_t value1;
870      size_t value2;
871      bool has_duplicates;
872      bool value1_valid = GetVideoMemory(row1, &value1, &has_duplicates);
873      bool value2_valid = GetVideoMemory(row2, &value2, &has_duplicates);
874      return value1_valid && value2_valid ? ValueCompare(value1, value2) :
875          OrderUnavailableValue(value1_valid, value2_valid);
876    }
877
878    case IDS_TASK_MANAGER_GOATS_TELEPORTED_COLUMN:
879      return ValueCompare(GetGoatsTeleported(row1), GetGoatsTeleported(row2));
880
881    case IDS_TASK_MANAGER_JAVASCRIPT_MEMORY_ALLOCATED_COLUMN:
882      return ValueCompareMember(
883          this, &TaskManagerModel::GetV8Memory, row1, row2);
884
885    case IDS_TASK_MANAGER_SQLITE_MEMORY_USED_COLUMN:
886      return ValueCompareMember(
887          this, &TaskManagerModel::GetSqliteMemoryUsedBytes, row1, row2);
888
889    default:
890      NOTREACHED();
891      break;
892  }
893  return 0;
894}
895
896int TaskManagerModel::GetUniqueChildProcessId(int index) const {
897  return GetResource(index)->GetUniqueChildProcessId();
898}
899
900TaskManager::Resource::Type TaskManagerModel::GetResourceType(int index) const {
901  return GetResource(index)->GetType();
902}
903
904WebContents* TaskManagerModel::GetResourceWebContents(int index) const {
905  return GetResource(index)->GetWebContents();
906}
907
908const extensions::Extension* TaskManagerModel::GetResourceExtension(
909    int index) const {
910  return GetResource(index)->GetExtension();
911}
912
913void TaskManagerModel::AddResource(TaskManager::Resource* resource) {
914  resource->unique_id_ = ++last_unique_id_;
915
916  base::ProcessHandle process = resource->GetProcess();
917
918  ResourceList* group_entries = NULL;
919  GroupMap::const_iterator group_iter = group_map_.find(process);
920  int new_entry_index = 0;
921  if (group_iter == group_map_.end()) {
922    group_entries = new ResourceList();
923    group_map_[process] = group_entries;
924    group_entries->push_back(resource);
925
926    // Not part of a group, just put at the end of the list.
927    resources_.push_back(resource);
928    new_entry_index = static_cast<int>(resources_.size() - 1);
929  } else {
930    group_entries = group_iter->second;
931    group_entries->push_back(resource);
932
933    // Insert the new entry right after the last entry of its group.
934    ResourceList::iterator iter =
935        std::find(resources_.begin(),
936                  resources_.end(),
937                  (*group_entries)[group_entries->size() - 2]);
938    DCHECK(iter != resources_.end());
939    new_entry_index = static_cast<int>(iter - resources_.begin()) + 1;
940    resources_.insert(++iter, resource);
941  }
942
943  // Create the ProcessMetrics for this process if needed (not in map).
944  if (metrics_map_.find(process) == metrics_map_.end()) {
945    base::ProcessMetrics* pm =
946#if !defined(OS_MACOSX)
947        base::ProcessMetrics::CreateProcessMetrics(process);
948#else
949        base::ProcessMetrics::CreateProcessMetrics(
950            process, content::BrowserChildProcessHost::GetPortProvider());
951#endif
952
953    metrics_map_[process] = pm;
954  }
955
956  // Notify the table that the contents have changed for it to redraw.
957  FOR_EACH_OBSERVER(TaskManagerModelObserver, observer_list_,
958                    OnItemsAdded(new_entry_index, 1));
959}
960
961void TaskManagerModel::RemoveResource(TaskManager::Resource* resource) {
962  base::ProcessHandle process = resource->GetProcess();
963
964  // Find the associated group.
965  GroupMap::iterator group_iter = group_map_.find(process);
966  DCHECK(group_iter != group_map_.end());
967  ResourceList* group_entries = group_iter->second;
968
969  // Remove the entry from the group map.
970  ResourceList::iterator iter = std::find(group_entries->begin(),
971                                          group_entries->end(),
972                                          resource);
973  DCHECK(iter != group_entries->end());
974  group_entries->erase(iter);
975
976  // If there are no more entries for that process, do the clean-up.
977  if (group_entries->empty()) {
978    delete group_entries;
979    group_map_.erase(process);
980
981    // Nobody is using this process, we don't need the process metrics anymore.
982    MetricsMap::iterator pm_iter = metrics_map_.find(process);
983    DCHECK(pm_iter != metrics_map_.end());
984    if (pm_iter != metrics_map_.end()) {
985      delete pm_iter->second;
986      metrics_map_.erase(process);
987    }
988  }
989
990  // Prepare to remove the entry from the model list.
991  iter = std::find(resources_.begin(), resources_.end(), resource);
992  DCHECK(iter != resources_.end());
993  int index = static_cast<int>(iter - resources_.begin());
994
995  // Notify the observers that the contents will change.
996  FOR_EACH_OBSERVER(TaskManagerModelObserver, observer_list_,
997                    OnItemsToBeRemoved(index, 1));
998
999  // Now actually remove the entry from the model list.
1000  resources_.erase(iter);
1001
1002  // Remove the entry from the network maps.
1003  ResourceValueMap::iterator net_iter =
1004      current_byte_count_map_.find(resource);
1005  if (net_iter != current_byte_count_map_.end())
1006    current_byte_count_map_.erase(net_iter);
1007
1008  // Notify the table that the contents have changed.
1009  FOR_EACH_OBSERVER(TaskManagerModelObserver, observer_list_,
1010                    OnItemsRemoved(index, 1));
1011}
1012
1013void TaskManagerModel::StartUpdating() {
1014  // Multiple StartUpdating requests may come in, and we only need to take
1015  // action the first time.
1016  update_requests_++;
1017  if (update_requests_ > 1)
1018    return;
1019  DCHECK_EQ(1, update_requests_);
1020  DCHECK_NE(TASK_PENDING, update_state_);
1021
1022  // If update_state_ is STOPPING, it means a task is still pending.  Setting
1023  // it to TASK_PENDING ensures the tasks keep being posted (by Refresh()).
1024  if (update_state_ == IDLE) {
1025      MessageLoop::current()->PostTask(
1026          FROM_HERE,
1027          base::Bind(&TaskManagerModel::RefreshCallback, this));
1028  }
1029  update_state_ = TASK_PENDING;
1030
1031  // Notify resource providers that we are updating.
1032  StartListening();
1033
1034  if (!resources_.empty()) {
1035    FOR_EACH_OBSERVER(TaskManagerModelObserver, observer_list_,
1036                      OnReadyPeriodicalUpdate());
1037  }
1038}
1039
1040void TaskManagerModel::StopUpdating() {
1041  // Don't actually stop updating until we have heard as many calls as those
1042  // to StartUpdating.
1043  update_requests_--;
1044  if (update_requests_ > 0)
1045    return;
1046  // Make sure that update_requests_ cannot go negative.
1047  CHECK_EQ(0, update_requests_);
1048  DCHECK_EQ(TASK_PENDING, update_state_);
1049  update_state_ = STOPPING;
1050
1051  // Notify resource providers that we are done updating.
1052  StopListening();
1053}
1054
1055void TaskManagerModel::StartListening() {
1056  // Multiple StartListening requests may come in and we only need to take
1057  // action the first time.
1058  listen_requests_++;
1059  if (listen_requests_ > 1)
1060    return;
1061  DCHECK_EQ(1, listen_requests_);
1062
1063  // Notify resource providers that we should start listening to events.
1064  for (ResourceProviderList::iterator iter = providers_.begin();
1065       iter != providers_.end(); ++iter) {
1066    (*iter)->StartUpdating();
1067  }
1068}
1069
1070void TaskManagerModel::StopListening() {
1071  // Don't actually stop listening until we have heard as many calls as those
1072  // to StartListening.
1073  listen_requests_--;
1074  if (listen_requests_ > 0)
1075    return;
1076
1077  DCHECK_EQ(0, listen_requests_);
1078
1079  // Notify resource providers that we are done listening.
1080  for (ResourceProviderList::const_iterator iter = providers_.begin();
1081       iter != providers_.end(); ++iter) {
1082    (*iter)->StopUpdating();
1083  }
1084
1085  // Must clear the resources before the next attempt to start listening.
1086  Clear();
1087}
1088
1089void TaskManagerModel::Clear() {
1090  int size = ResourceCount();
1091  if (size > 0) {
1092    resources_.clear();
1093
1094    // Clear the groups.
1095    STLDeleteValues(&group_map_);
1096
1097    // Clear the process related info.
1098    STLDeleteValues(&metrics_map_);
1099
1100    // Clear the network maps.
1101    current_byte_count_map_.clear();
1102
1103    per_resource_cache_.clear();
1104    per_process_cache_.clear();
1105
1106    FOR_EACH_OBSERVER(TaskManagerModelObserver, observer_list_,
1107                      OnItemsRemoved(0, size));
1108  }
1109  last_unique_id_ = 0;
1110}
1111
1112void TaskManagerModel::ModelChanged() {
1113  // Notify the table that the contents have changed for it to redraw.
1114  FOR_EACH_OBSERVER(TaskManagerModelObserver, observer_list_, OnModelChanged());
1115}
1116
1117void TaskManagerModel::NotifyResourceTypeStats(
1118    base::ProcessId renderer_id,
1119    const WebKit::WebCache::ResourceTypeStats& stats) {
1120  for (ResourceList::iterator it = resources_.begin();
1121       it != resources_.end(); ++it) {
1122    if (base::GetProcId((*it)->GetProcess()) == renderer_id) {
1123      (*it)->NotifyResourceTypeStats(stats);
1124    }
1125  }
1126}
1127
1128void TaskManagerModel::NotifyFPS(base::ProcessId renderer_id,
1129                                 int routing_id,
1130                                 float fps) {
1131  for (ResourceList::iterator it = resources_.begin();
1132       it != resources_.end(); ++it) {
1133    if (base::GetProcId((*it)->GetProcess()) == renderer_id &&
1134        (*it)->GetRoutingID() == routing_id) {
1135      (*it)->NotifyFPS(fps);
1136    }
1137  }
1138}
1139
1140void TaskManagerModel::NotifyVideoMemoryUsageStats(
1141    const content::GPUVideoMemoryUsageStats& video_memory_usage_stats) {
1142  DCHECK(pending_video_memory_usage_stats_update_);
1143  video_memory_usage_stats_ = video_memory_usage_stats;
1144  pending_video_memory_usage_stats_update_ = false;
1145}
1146
1147void TaskManagerModel::NotifyV8HeapStats(base::ProcessId renderer_id,
1148                                         size_t v8_memory_allocated,
1149                                         size_t v8_memory_used) {
1150  for (ResourceList::iterator it = resources_.begin();
1151       it != resources_.end(); ++it) {
1152    if (base::GetProcId((*it)->GetProcess()) == renderer_id) {
1153      (*it)->NotifyV8HeapStats(v8_memory_allocated, v8_memory_used);
1154    }
1155  }
1156}
1157
1158void TaskManagerModel::NotifyBytesRead(const net::URLRequest& request,
1159                                       int byte_count) {
1160  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1161
1162  // Only net::URLRequestJob instances created by the ResourceDispatcherHost
1163  // have an associated ResourceRequestInfo.
1164  const ResourceRequestInfo* info = ResourceRequestInfo::ForRequest(&request);
1165
1166  // have a render view associated.  All other jobs will have -1 returned for
1167  // the render process child and routing ids - the jobs may still match a
1168  // resource based on their origin id, otherwise BytesRead() will attribute
1169  // the activity to the Browser resource.
1170  int render_process_host_child_id = -1, routing_id = -1;
1171  if (info)
1172    info->GetAssociatedRenderView(&render_process_host_child_id, &routing_id);
1173
1174  // Get the origin PID of the request's originator.  This will only be set for
1175  // plugins - for renderer or browser initiated requests it will be zero.
1176  int origin_pid = 0;
1177  if (info)
1178    origin_pid = info->GetOriginPID();
1179
1180  if (bytes_read_buffer_.empty()) {
1181    MessageLoop::current()->PostDelayedTask(
1182        FROM_HERE,
1183        base::Bind(&TaskManagerModel::NotifyMultipleBytesRead, this),
1184        base::TimeDelta::FromSeconds(1));
1185  }
1186
1187  bytes_read_buffer_.push_back(
1188      BytesReadParam(origin_pid, render_process_host_child_id,
1189                     routing_id, byte_count));
1190}
1191
1192TaskManagerModel::~TaskManagerModel() {
1193}
1194
1195void TaskManagerModel::RefreshCallback() {
1196  DCHECK_NE(IDLE, update_state_);
1197
1198  if (update_state_ == STOPPING) {
1199    // We have been asked to stop.
1200    update_state_ = IDLE;
1201    return;
1202  }
1203
1204  Refresh();
1205
1206  // Schedule the next update.
1207  MessageLoop::current()->PostDelayedTask(
1208      FROM_HERE,
1209      base::Bind(&TaskManagerModel::RefreshCallback, this),
1210      base::TimeDelta::FromMilliseconds(kUpdateTimeMs));
1211}
1212
1213void TaskManagerModel::Refresh() {
1214  goat_salt_ = base::RandUint64();
1215
1216  per_resource_cache_.clear();
1217  per_process_cache_.clear();
1218
1219  // Compute the CPU usage values.
1220  // Note that we compute the CPU usage for all resources (instead of doing it
1221  // lazily) as process_util::GetCPUUsage() returns the CPU usage since the last
1222  // time it was called, and not calling it everytime would skew the value the
1223  // next time it is retrieved (as it would be for more than 1 cycle).
1224  for (ResourceList::iterator iter = resources_.begin();
1225       iter != resources_.end(); ++iter) {
1226    base::ProcessHandle process = (*iter)->GetProcess();
1227    PerProcessValues& values(per_process_cache_[process]);
1228    if (values.is_cpu_usage_valid)
1229      continue;
1230
1231    values.is_cpu_usage_valid = true;
1232    MetricsMap::iterator metrics_iter = metrics_map_.find(process);
1233    DCHECK(metrics_iter != metrics_map_.end());
1234    values.cpu_usage = metrics_iter->second->GetCPUUsage();
1235  }
1236
1237  // Send a request to refresh GPU memory consumption values
1238  RefreshVideoMemoryUsageStats();
1239
1240  // Compute the new network usage values.
1241  base::TimeDelta update_time =
1242      base::TimeDelta::FromMilliseconds(kUpdateTimeMs);
1243  for (ResourceValueMap::iterator iter = current_byte_count_map_.begin();
1244       iter != current_byte_count_map_.end(); ++iter) {
1245    PerResourceValues* values = &(per_resource_cache_[iter->first]);
1246    if (update_time > base::TimeDelta::FromSeconds(1))
1247      values->network_usage = iter->second / update_time.InSeconds();
1248    else
1249      values->network_usage = iter->second * (1 / update_time.InSeconds());
1250
1251    // Then we reset the current byte count.
1252    iter->second = 0;
1253  }
1254
1255  // Let resources update themselves if they need to.
1256  for (ResourceList::iterator iter = resources_.begin();
1257       iter != resources_.end(); ++iter) {
1258     (*iter)->Refresh();
1259  }
1260
1261  if (!resources_.empty()) {
1262    FOR_EACH_OBSERVER(TaskManagerModelObserver, observer_list_,
1263                      OnItemsChanged(0, ResourceCount()));
1264  }
1265}
1266
1267void TaskManagerModel::RefreshVideoMemoryUsageStats() {
1268  if (pending_video_memory_usage_stats_update_)
1269    return;
1270
1271  if (!video_memory_usage_stats_observer_.get()) {
1272    video_memory_usage_stats_observer_.reset(
1273        new TaskManagerModelGpuDataManagerObserver());
1274  }
1275  pending_video_memory_usage_stats_update_ = true;
1276  content::GpuDataManager::GetInstance()->RequestVideoMemoryUsageStatsUpdate();
1277}
1278
1279int64 TaskManagerModel::GetNetworkUsageForResource(
1280    TaskManager::Resource* resource) const {
1281  // Returns default of 0 if no network usage.
1282  return per_resource_cache_[resource].network_usage;
1283}
1284
1285void TaskManagerModel::BytesRead(BytesReadParam param) {
1286  if (update_state_ != TASK_PENDING || listen_requests_ == 0) {
1287    // A notification sneaked in while we were stopping the updating, just
1288    // ignore it.
1289    return;
1290  }
1291
1292  // TODO(jcampan): this should be improved once we have a better way of
1293  // linking a network notification back to the object that initiated it.
1294  TaskManager::Resource* resource = NULL;
1295  for (ResourceProviderList::iterator iter = providers_.begin();
1296       iter != providers_.end(); ++iter) {
1297    resource = (*iter)->GetResource(param.origin_pid,
1298                                    param.render_process_host_child_id,
1299                                    param.routing_id);
1300    if (resource)
1301      break;
1302  }
1303
1304  if (resource == NULL) {
1305    // We can't match a resource to the notification.  That might mean the
1306    // tab that started a download was closed, or the request may have had
1307    // no originating resource associated with it in the first place.
1308    // We attribute orphaned/unaccounted activity to the Browser process.
1309    CHECK(param.origin_pid || (param.render_process_host_child_id != -1));
1310    param.origin_pid = 0;
1311    param.render_process_host_child_id = param.routing_id = -1;
1312    BytesRead(param);
1313    return;
1314  }
1315
1316  // We do support network usage, mark the resource as such so it can report 0
1317  // instead of N/A.
1318  if (!resource->SupportNetworkUsage())
1319    resource->SetSupportNetworkUsage();
1320
1321  ResourceValueMap::const_iterator iter_res =
1322      current_byte_count_map_.find(resource);
1323  if (iter_res == current_byte_count_map_.end())
1324    current_byte_count_map_[resource] = param.byte_count;
1325  else
1326    current_byte_count_map_[resource] = iter_res->second + param.byte_count;
1327}
1328
1329void TaskManagerModel::MultipleBytesRead(
1330    const std::vector<BytesReadParam>* params) {
1331  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1332  for (std::vector<BytesReadParam>::const_iterator it = params->begin();
1333       it != params->end(); ++it) {
1334    BytesRead(*it);
1335  }
1336}
1337
1338void TaskManagerModel::NotifyMultipleBytesRead() {
1339  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1340  DCHECK(!bytes_read_buffer_.empty());
1341
1342  std::vector<BytesReadParam>* bytes_read_buffer =
1343      new std::vector<BytesReadParam>;
1344  bytes_read_buffer_.swap(*bytes_read_buffer);
1345  BrowserThread::PostTask(
1346      BrowserThread::UI, FROM_HERE,
1347      base::Bind(&TaskManagerModel::MultipleBytesRead, this,
1348                 base::Owned(bytes_read_buffer)));
1349}
1350
1351int64 TaskManagerModel::GetNetworkUsage(TaskManager::Resource* resource) const {
1352  int64 net_usage = GetNetworkUsageForResource(resource);
1353  if (net_usage == 0 && !resource->SupportNetworkUsage())
1354    return -1;
1355  return net_usage;
1356}
1357
1358double TaskManagerModel::GetCPUUsage(TaskManager::Resource* resource) const {
1359  const PerProcessValues& values(per_process_cache_[resource->GetProcess()]);
1360  // Returns 0 if not valid, which is fine.
1361  return values.cpu_usage;
1362}
1363
1364string16 TaskManagerModel::GetMemCellText(int64 number) const {
1365#if !defined(OS_MACOSX)
1366  string16 str = base::FormatNumber(number / 1024);
1367
1368  // Adjust number string if necessary.
1369  base::i18n::AdjustStringForLocaleDirection(&str);
1370  return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_MEM_CELL_TEXT, str);
1371#else
1372  // System expectation is to show "100 kB", "200 MB", etc.
1373  // TODO(thakis): Switch to metric units (as opposed to powers of two).
1374  return ui::FormatBytes(number);
1375#endif
1376}
1377
1378bool TaskManagerModel::CachePrivateAndSharedMemory(
1379    base::ProcessHandle handle) const {
1380  PerProcessValues& values(per_process_cache_[handle]);
1381  if (values.is_private_and_shared_valid)
1382    return true;
1383
1384  MetricsMap::const_iterator iter = metrics_map_.find(handle);
1385  if (iter == metrics_map_.end() ||
1386      !iter->second->GetMemoryBytes(&values.private_bytes,
1387                                    &values.shared_bytes)) {
1388    return false;
1389  }
1390
1391  values.is_private_and_shared_valid = true;
1392  return true;
1393}
1394
1395bool TaskManagerModel::CacheWebCoreStats(int index) const {
1396  PerResourceValues& values(GetPerResourceValues(index));
1397  if (!values.is_webcore_stats_valid) {
1398    if (!GetResource(index)->ReportsCacheStats())
1399      return false;
1400    values.is_webcore_stats_valid = true;
1401    values.webcore_stats = GetResource(index)->GetWebCoreCacheStats();
1402  }
1403  return true;
1404}
1405
1406bool TaskManagerModel::CacheV8Memory(int index) const {
1407  PerResourceValues& values(GetPerResourceValues(index));
1408  if (!values.is_v8_memory_valid) {
1409    if (!GetResource(index)->ReportsV8MemoryStats())
1410      return false;
1411    values.is_v8_memory_valid = true;
1412    values.v8_memory_allocated = GetResource(index)->GetV8MemoryAllocated();
1413    values.v8_memory_used = GetResource(index)->GetV8MemoryUsed();
1414  }
1415  return true;
1416}
1417
1418void TaskManagerModel::AddResourceProvider(
1419    TaskManager::ResourceProvider* provider) {
1420  DCHECK(provider);
1421  providers_.push_back(provider);
1422}
1423
1424TaskManagerModel::PerResourceValues& TaskManagerModel::GetPerResourceValues(
1425    int index) const {
1426  return per_resource_cache_[GetResource(index)];
1427}
1428
1429TaskManager::Resource* TaskManagerModel::GetResource(int index) const {
1430  CHECK_GE(index, 0);
1431  CHECK_LT(index, static_cast<int>(resources_.size()));
1432  return resources_[index];
1433}
1434
1435////////////////////////////////////////////////////////////////////////////////
1436// TaskManager class
1437////////////////////////////////////////////////////////////////////////////////
1438
1439int TaskManager::Resource::GetRoutingID() const { return 0; }
1440
1441bool TaskManager::Resource::ReportsCacheStats() const { return false; }
1442
1443WebKit::WebCache::ResourceTypeStats
1444TaskManager::Resource::GetWebCoreCacheStats() const {
1445  return WebKit::WebCache::ResourceTypeStats();
1446}
1447
1448bool TaskManager::Resource::ReportsFPS() const { return false; }
1449
1450float TaskManager::Resource::GetFPS() const { return 0.0f; }
1451
1452bool TaskManager::Resource::ReportsSqliteMemoryUsed() const { return false; }
1453
1454size_t TaskManager::Resource::SqliteMemoryUsedBytes() const { return 0; }
1455
1456const extensions::Extension* TaskManager::Resource::GetExtension() const {
1457  return NULL;
1458}
1459
1460bool TaskManager::Resource::ReportsV8MemoryStats() const { return false; }
1461
1462size_t TaskManager::Resource::GetV8MemoryAllocated() const { return 0; }
1463
1464size_t TaskManager::Resource::GetV8MemoryUsed() const { return 0; }
1465
1466bool TaskManager::Resource::CanInspect() const { return false; }
1467
1468content::WebContents* TaskManager::Resource::GetWebContents() const {
1469  return NULL;
1470}
1471
1472bool TaskManager::Resource::IsBackground() const { return false; }
1473
1474// static
1475void TaskManager::RegisterPrefs(PrefRegistrySimple* registry) {
1476  registry->RegisterDictionaryPref(prefs::kTaskManagerWindowPlacement);
1477}
1478
1479bool TaskManager::IsBrowserProcess(int index) const {
1480  // If some of the selection is out of bounds, ignore. This may happen when
1481  // killing a process that manages several pages.
1482  return index < model_->ResourceCount() &&
1483      model_->GetProcess(index) == base::GetCurrentProcessHandle();
1484}
1485
1486void TaskManager::KillProcess(int index) {
1487  base::ProcessHandle process = model_->GetProcess(index);
1488  DCHECK(process);
1489  if (process != base::GetCurrentProcessHandle())
1490    base::KillProcess(process, content::RESULT_CODE_KILLED, false);
1491}
1492
1493void TaskManager::ActivateProcess(int index) {
1494  // GetResourceWebContents returns a pointer to the relevant web contents for
1495  // the resource.  If the index doesn't correspond to any web contents
1496  // (i.e. refers to the Browser process or a plugin), GetWebContents will
1497  // return NULL.
1498  WebContents* chosen_web_contents = model_->GetResourceWebContents(index);
1499  if (chosen_web_contents && chosen_web_contents->GetDelegate())
1500    chosen_web_contents->GetDelegate()->ActivateContents(chosen_web_contents);
1501}
1502
1503void TaskManager::AddResource(Resource* resource) {
1504  model_->AddResource(resource);
1505}
1506
1507void TaskManager::RemoveResource(Resource* resource) {
1508  model_->RemoveResource(resource);
1509}
1510
1511void TaskManager::OnWindowClosed() {
1512  model_->StopUpdating();
1513}
1514
1515void TaskManager::ModelChanged() {
1516  model_->ModelChanged();
1517}
1518
1519// static
1520TaskManager* TaskManager::GetInstance() {
1521  return Singleton<TaskManager>::get();
1522}
1523
1524void TaskManager::OpenAboutMemory(chrome::HostDesktopType desktop_type) {
1525  Browser* browser = chrome::FindOrCreateTabbedBrowser(
1526      ProfileManager::GetDefaultProfileOrOffTheRecord(), desktop_type);
1527  chrome::NavigateParams params(browser, GURL(chrome::kChromeUIMemoryURL),
1528                                content::PAGE_TRANSITION_LINK);
1529  params.disposition = NEW_FOREGROUND_TAB;
1530  chrome::Navigate(&params);
1531}
1532
1533namespace {
1534
1535// Counts the number of extension background pages associated with this profile.
1536int CountExtensionBackgroundPagesForProfile(Profile* profile) {
1537  int count = 0;
1538  ExtensionProcessManager* manager =
1539      extensions::ExtensionSystem::Get(profile)->process_manager();
1540  if (!manager)
1541    return count;
1542
1543  const ExtensionProcessManager::ExtensionHostSet& background_hosts =
1544      manager->background_hosts();
1545  for (ExtensionProcessManager::const_iterator iter = background_hosts.begin();
1546       iter != background_hosts.end(); ++iter) {
1547    ++count;
1548  }
1549  return count;
1550}
1551
1552}  // namespace
1553
1554// static
1555int TaskManager::GetBackgroundPageCount() {
1556  int count = 0;
1557  ProfileManager* profile_manager = g_browser_process->profile_manager();
1558  if (!profile_manager)  // Null when running unit tests.
1559    return count;
1560  std::vector<Profile*> profiles(profile_manager->GetLoadedProfiles());
1561  for (std::vector<Profile*>::const_iterator iter = profiles.begin();
1562       iter != profiles.end();
1563       ++iter) {
1564    Profile* profile = *iter;
1565    // Count the number of Background Contents (background pages for hosted
1566    // apps). Incognito windows do not support hosted apps, so just check the
1567    // main profile.
1568    BackgroundContentsService* background_contents_service =
1569        BackgroundContentsServiceFactory::GetForProfile(profile);
1570    if (background_contents_service)
1571      count += background_contents_service->GetBackgroundContents().size();
1572
1573    // Count the number of extensions with background pages (including
1574    // incognito).
1575    count += CountExtensionBackgroundPagesForProfile(profile);
1576    if (profile->HasOffTheRecordProfile()) {
1577      count += CountExtensionBackgroundPagesForProfile(
1578          profile->GetOffTheRecordProfile());
1579    }
1580  }
1581  return count;
1582}
1583
1584TaskManager::TaskManager()
1585    : ALLOW_THIS_IN_INITIALIZER_LIST(model_(new TaskManagerModel(this))) {
1586}
1587
1588TaskManager::~TaskManager() {
1589}
1590