Popular sites on the NTP: check that experiment group StartsWith (rather than IS...
[chromium-blink-merge.git] / chrome / browser / ui / cocoa / task_manager_mac.mm
blob050eb732f67d36b545dbf5fdfc96bf3709ef2399
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/ui/cocoa/task_manager_mac.h"
7 #include <algorithm>
8 #include <vector>
10 #include "base/mac/bundle_locations.h"
11 #include "base/prefs/pref_service.h"
12 #include "base/strings/sys_string_conversions.h"
13 #include "chrome/browser/browser_process.h"
14 #import "chrome/browser/ui/cocoa/window_size_autosaver.h"
15 #include "chrome/browser/ui/host_desktop.h"
16 #include "chrome/common/pref_names.h"
17 #include "chrome/grit/generated_resources.h"
18 #include "third_party/skia/include/core/SkBitmap.h"
19 #include "ui/base/l10n/l10n_util_mac.h"
20 #include "ui/gfx/image/image_skia.h"
22 namespace {
24 // Width of "a" and most other letters/digits in "small" table views.
25 const int kCharWidth = 6;
27 // Some of the strings below have spaces at the end or are missing letters, to
28 // make the columns look nicer, and to take potentially longer localized strings
29 // into account.
30 const struct ColumnWidth {
31   int columnId;
32   int minWidth;
33   int maxWidth;  // If this is -1, 1.5*minColumWidth is used as max width.
34 } columnWidths[] = {
35   // Note that arraysize includes the trailing \0. That's intended.
36   { IDS_TASK_MANAGER_TASK_COLUMN, 120, 600 },
37   { IDS_TASK_MANAGER_PROFILE_NAME_COLUMN, 60, 200 },
38   { IDS_TASK_MANAGER_PHYSICAL_MEM_COLUMN,
39       arraysize("800 MiB") * kCharWidth, -1 },
40   { IDS_TASK_MANAGER_SHARED_MEM_COLUMN,
41       arraysize("800 MiB") * kCharWidth, -1 },
42   { IDS_TASK_MANAGER_PRIVATE_MEM_COLUMN,
43       arraysize("800 MiB") * kCharWidth, -1 },
44   { IDS_TASK_MANAGER_CPU_COLUMN,
45       arraysize("99.9") * kCharWidth, -1 },
46   { IDS_TASK_MANAGER_NET_COLUMN,
47       arraysize("150 kiB/s") * kCharWidth, -1 },
48   { IDS_TASK_MANAGER_PROCESS_ID_COLUMN,
49       arraysize("73099  ") * kCharWidth, -1 },
50   { IDS_TASK_MANAGER_WEBCORE_IMAGE_CACHE_COLUMN,
51       arraysize("2000.0K (2000.0 live)") * kCharWidth, -1 },
52   { IDS_TASK_MANAGER_WEBCORE_SCRIPTS_CACHE_COLUMN,
53       arraysize("2000.0K (2000.0 live)") * kCharWidth, -1 },
54   { IDS_TASK_MANAGER_WEBCORE_CSS_CACHE_COLUMN,
55       arraysize("2000.0K (2000.0 live)") * kCharWidth, -1 },
56   { IDS_TASK_MANAGER_VIDEO_MEMORY_COLUMN,
57       arraysize("2000.0K") * kCharWidth, -1 },
58   { IDS_TASK_MANAGER_SQLITE_MEMORY_USED_COLUMN,
59       arraysize("800 kB") * kCharWidth, -1 },
60   { IDS_TASK_MANAGER_JAVASCRIPT_MEMORY_ALLOCATED_COLUMN,
61       arraysize("2000.0K (2000.0 live)") * kCharWidth, -1 },
62   { IDS_TASK_MANAGER_NACL_DEBUG_STUB_PORT_COLUMN,
63       arraysize("32767") * kCharWidth, -1 },
64   { IDS_TASK_MANAGER_IDLE_WAKEUPS_COLUMN,
65       arraysize("idlewakeups") * kCharWidth, -1 },
68 class SortHelper {
69  public:
70   SortHelper(TaskManagerModel* model, NSSortDescriptor* column)
71       : sort_column_([[column key] intValue]),
72         ascending_([column ascending]),
73         model_(model) {}
75   bool operator()(int a, int b) {
76     TaskManagerModel::GroupRange group_range1 =
77         model_->GetGroupRangeForResource(a);
78     TaskManagerModel::GroupRange group_range2 =
79         model_->GetGroupRangeForResource(b);
80     if (group_range1 == group_range2) {
81       // The two rows are in the same group, sort so that items in the same
82       // group always appear in the same order. |ascending_| is intentionally
83       // ignored.
84       return a < b;
85     }
86     // Sort by the first entry of each of the groups.
87     int cmp_result = model_->CompareValues(
88         group_range1.first, group_range2.first, sort_column_);
89     if (!ascending_)
90       cmp_result = -cmp_result;
91     return cmp_result < 0;
92   }
93  private:
94   int sort_column_;
95   bool ascending_;
96   TaskManagerModel* model_;  // weak;
99 }  // namespace
101 @interface TaskManagerWindowController (Private)
102 - (NSTableColumn*)addColumnWithId:(int)columnId visible:(BOOL)isVisible;
103 - (void)setUpTableColumns;
104 - (void)setUpTableHeaderContextMenu;
105 - (void)toggleColumn:(id)sender;
106 - (void)adjustSelectionAndEndProcessButton;
107 - (void)deselectRows;
108 @end
110 ////////////////////////////////////////////////////////////////////////////////
111 // TaskManagerWindowController implementation:
113 @implementation TaskManagerWindowController
115 - (id)initWithTaskManagerObserver:(TaskManagerMac*)taskManagerObserver {
116   NSString* nibpath = [base::mac::FrameworkBundle()
117                         pathForResource:@"TaskManager"
118                                  ofType:@"nib"];
119   if ((self = [super initWithWindowNibPath:nibpath owner:self])) {
120     taskManagerObserver_ = taskManagerObserver;
121     taskManager_ = taskManagerObserver_->task_manager();
122     model_ = taskManager_->model();
124     if (g_browser_process && g_browser_process->local_state()) {
125       size_saver_.reset([[WindowSizeAutosaver alloc]
126           initWithWindow:[self window]
127              prefService:g_browser_process->local_state()
128                     path:prefs::kTaskManagerWindowPlacement]);
129     }
130     [self showWindow:self];
131   }
132   return self;
135 - (void)sortShuffleArray {
136   viewToModelMap_.resize(model_->ResourceCount());
137   for (size_t i = 0; i < viewToModelMap_.size(); ++i)
138     viewToModelMap_[i] = i;
140   std::sort(viewToModelMap_.begin(), viewToModelMap_.end(),
141             SortHelper(model_, currentSortDescriptor_.get()));
143   modelToViewMap_.resize(viewToModelMap_.size());
144   for (size_t i = 0; i < viewToModelMap_.size(); ++i)
145     modelToViewMap_[viewToModelMap_[i]] = i;
148 - (void)reloadData {
149   // Store old view indices, and the model indices they map to.
150   NSIndexSet* viewSelection = [tableView_ selectedRowIndexes];
151   std::vector<int> modelSelection;
152   for (NSUInteger i = [viewSelection lastIndex];
153        i != NSNotFound;
154        i = [viewSelection indexLessThanIndex:i]) {
155     modelSelection.push_back(viewToModelMap_[i]);
156   }
158   // Sort.
159   [self sortShuffleArray];
161   // Use the model indices to get the new view indices of the selection, and
162   // set selection to that. This assumes that no rows were added or removed
163   // (in that case, the selection is cleared before -reloadData is called).
164   if (!modelSelection.empty())
165     DCHECK_EQ([tableView_ numberOfRows], model_->ResourceCount());
166   NSMutableIndexSet* indexSet = [NSMutableIndexSet indexSet];
167   for (size_t i = 0; i < modelSelection.size(); ++i)
168     [indexSet addIndex:modelToViewMap_[modelSelection[i]]];
169   [tableView_ selectRowIndexes:indexSet byExtendingSelection:NO];
171   [tableView_ reloadData];
172   [self adjustSelectionAndEndProcessButton];
175 - (IBAction)statsLinkClicked:(id)sender {
176   TaskManager::GetInstance()->OpenAboutMemory(chrome::HOST_DESKTOP_TYPE_NATIVE);
179 - (IBAction)killSelectedProcesses:(id)sender {
180   NSIndexSet* selection = [tableView_ selectedRowIndexes];
181   for (NSUInteger i = [selection lastIndex];
182        i != NSNotFound;
183        i = [selection indexLessThanIndex:i]) {
184     taskManager_->KillProcess(viewToModelMap_[i]);
185   }
188 - (void)selectDoubleClickedTab:(id)sender {
189   NSInteger row = [tableView_ clickedRow];
190   if (row < 0)
191     return;  // Happens e.g. if the table header is double-clicked.
192   taskManager_->ActivateProcess(viewToModelMap_[row]);
195 - (NSTableView*)tableView {
196   return tableView_;
199 - (void)awakeFromNib {
200   [self setUpTableColumns];
201   [self setUpTableHeaderContextMenu];
202   [self adjustSelectionAndEndProcessButton];
204   [tableView_ setDoubleAction:@selector(selectDoubleClickedTab:)];
205   [tableView_ setIntercellSpacing:NSMakeSize(0.0, 0.0)];
206   [tableView_ sizeToFit];
209 - (void)dealloc {
210   [tableView_ setDelegate:nil];
211   [tableView_ setDataSource:nil];
212   [super dealloc];
215 // Adds a column which has the given string id as title. |isVisible| specifies
216 // if the column is initially visible.
217 - (NSTableColumn*)addColumnWithId:(int)columnId visible:(BOOL)isVisible {
218   base::scoped_nsobject<NSTableColumn> column([[NSTableColumn alloc]
219       initWithIdentifier:[NSString stringWithFormat:@"%d", columnId]]);
221   NSTextAlignment textAlignment =
222       (columnId == IDS_TASK_MANAGER_TASK_COLUMN ||
223        columnId == IDS_TASK_MANAGER_PROFILE_NAME_COLUMN) ?
224           NSLeftTextAlignment : NSRightTextAlignment;
226   [[column.get() headerCell]
227       setStringValue:l10n_util::GetNSStringWithFixup(columnId)];
228   [[column.get() headerCell] setAlignment:textAlignment];
229   [[column.get() dataCell] setAlignment:textAlignment];
231   NSFont* font = [NSFont systemFontOfSize:[NSFont smallSystemFontSize]];
232   [[column.get() dataCell] setFont:font];
234   [column.get() setHidden:!isVisible];
235   [column.get() setEditable:NO];
237   // The page column should by default be sorted ascending.
238   BOOL ascending = columnId == IDS_TASK_MANAGER_TASK_COLUMN;
240   base::scoped_nsobject<NSSortDescriptor> sortDescriptor(
241       [[NSSortDescriptor alloc]
242           initWithKey:[NSString stringWithFormat:@"%d", columnId]
243             ascending:ascending]);
244   [column.get() setSortDescriptorPrototype:sortDescriptor.get()];
246   // Default values, only used in release builds if nobody notices the DCHECK
247   // during development when adding new columns.
248   int minWidth = 200, maxWidth = 400;
250   size_t i;
251   for (i = 0; i < arraysize(columnWidths); ++i) {
252     if (columnWidths[i].columnId == columnId) {
253       minWidth = columnWidths[i].minWidth;
254       maxWidth = columnWidths[i].maxWidth;
255       if (maxWidth < 0)
256         maxWidth = 3 * minWidth / 2;  // *1.5 for ints.
257       break;
258     }
259   }
260   DCHECK(i < arraysize(columnWidths)) << "Could not find " << columnId;
261   [column.get() setMinWidth:minWidth];
262   [column.get() setMaxWidth:maxWidth];
263   [column.get() setResizingMask:NSTableColumnAutoresizingMask |
264                                 NSTableColumnUserResizingMask];
266   [tableView_ addTableColumn:column.get()];
267   return column.get();  // Now retained by |tableView_|.
270 // Adds all the task manager's columns to the table.
271 - (void)setUpTableColumns {
272   for (NSTableColumn* column in [tableView_ tableColumns])
273     [tableView_ removeTableColumn:column];
274   NSTableColumn* nameColumn = [self addColumnWithId:IDS_TASK_MANAGER_TASK_COLUMN
275                                             visible:YES];
276   // |nameColumn| displays an icon for every row -- this is done by an
277   // NSButtonCell.
278   base::scoped_nsobject<NSButtonCell> nameCell(
279       [[NSButtonCell alloc] initTextCell:@""]);
280   [nameCell.get() setImagePosition:NSImageLeft];
281   [nameCell.get() setButtonType:NSSwitchButton];
282   [nameCell.get() setAlignment:[[nameColumn dataCell] alignment]];
283   [nameCell.get() setFont:[[nameColumn dataCell] font]];
284   [nameColumn setDataCell:nameCell.get()];
286   // Initially, sort on the tab name.
287   [tableView_ setSortDescriptors:
288       [NSArray arrayWithObject:[nameColumn sortDescriptorPrototype]]];
289   [self addColumnWithId:IDS_TASK_MANAGER_PROFILE_NAME_COLUMN visible:NO];
290   [self addColumnWithId:IDS_TASK_MANAGER_PHYSICAL_MEM_COLUMN visible:YES];
291   [self addColumnWithId:IDS_TASK_MANAGER_SHARED_MEM_COLUMN visible:NO];
292   [self addColumnWithId:IDS_TASK_MANAGER_PRIVATE_MEM_COLUMN visible:NO];
293   [self addColumnWithId:IDS_TASK_MANAGER_CPU_COLUMN visible:YES];
294   [self addColumnWithId:IDS_TASK_MANAGER_NET_COLUMN visible:YES];
295   [self addColumnWithId:IDS_TASK_MANAGER_PROCESS_ID_COLUMN visible:YES];
296   [self addColumnWithId:IDS_TASK_MANAGER_WEBCORE_IMAGE_CACHE_COLUMN
297                 visible:NO];
298   [self addColumnWithId:IDS_TASK_MANAGER_WEBCORE_SCRIPTS_CACHE_COLUMN
299                 visible:NO];
300   [self addColumnWithId:IDS_TASK_MANAGER_WEBCORE_CSS_CACHE_COLUMN visible:NO];
301   [self addColumnWithId:IDS_TASK_MANAGER_VIDEO_MEMORY_COLUMN visible:NO];
302   [self addColumnWithId:IDS_TASK_MANAGER_SQLITE_MEMORY_USED_COLUMN visible:NO];
303   [self addColumnWithId:IDS_TASK_MANAGER_JAVASCRIPT_MEMORY_ALLOCATED_COLUMN
304                 visible:NO];
305   [self addColumnWithId:IDS_TASK_MANAGER_NACL_DEBUG_STUB_PORT_COLUMN
306                 visible:NO];
307   [self addColumnWithId:IDS_TASK_MANAGER_IDLE_WAKEUPS_COLUMN
308                 visible:NO];
311 // Creates a context menu for the table header that allows the user to toggle
312 // which columns should be shown and which should be hidden (like e.g.
313 // Task Manager.app's table header context menu).
314 - (void)setUpTableHeaderContextMenu {
315   base::scoped_nsobject<NSMenu> contextMenu(
316       [[NSMenu alloc] initWithTitle:@"Task Manager context menu"]);
317   for (NSTableColumn* column in [tableView_ tableColumns]) {
318     NSMenuItem* item = [contextMenu.get()
319         addItemWithTitle:[[column headerCell] stringValue]
320                   action:@selector(toggleColumn:)
321            keyEquivalent:@""];
322     [item setTarget:self];
323     [item setRepresentedObject:column];
324     [item setState:[column isHidden] ? NSOffState : NSOnState];
325   }
326   [[tableView_ headerView] setMenu:contextMenu.get()];
329 // Callback for the table header context menu. Toggles visibility of the table
330 // column associated with the clicked menu item.
331 - (void)toggleColumn:(id)item {
332   DCHECK([item isKindOfClass:[NSMenuItem class]]);
333   if (![item isKindOfClass:[NSMenuItem class]])
334     return;
336   NSTableColumn* column = [item representedObject];
337   DCHECK(column);
338   NSInteger oldState = [item state];
339   NSInteger newState = oldState == NSOnState ? NSOffState : NSOnState;
341   // If hiding the column, make sure at least one column will remain visible.
342   if (newState == NSOffState) {
343     // Find the first column that will be visible after hiding |column|.
344     NSTableColumn* firstRemainingVisibleColumn = nil;
346     for (NSTableColumn* nextColumn in [tableView_ tableColumns]) {
347       if (nextColumn != column && ![nextColumn isHidden]) {
348         firstRemainingVisibleColumn = nextColumn;
349         break;
350       }
351     }
353     // If no column will be visible, abort the toggle. This will basically cause
354     // the toggle operation to silently fail. The other way to ensure at least
355     // one visible column is to disable the menu item corresponding to the last
356     // remaining visible column. That would place the menu in a weird state to
357     // the user, where there's one item somewhere that's grayed out with no
358     // clear explanation of why. It will be rare for a user to try hiding all
359     // columns, but we still want to guard against it. If they are really intent
360     // on hiding the last visible column (perhaps they plan to choose another
361     // one after that to be visible), odds are they will try making another
362     // column visible and then hiding the one column that would not hide.
363     if (firstRemainingVisibleColumn == nil) {
364       return;
365     }
367     // If |column| is being used to sort the table (i.e. it's the primary sort
368     // column), make the first remaining visible column the new primary sort
369     // column.
370     int primarySortColumnId = [[currentSortDescriptor_.get() key] intValue];
371     DCHECK(primarySortColumnId);
372     int columnId = [[column identifier] intValue];
374     if (primarySortColumnId == columnId) {
375       NSSortDescriptor* newSortDescriptor =
376           [firstRemainingVisibleColumn sortDescriptorPrototype];
377       [tableView_ setSortDescriptors:
378           [NSArray arrayWithObject:newSortDescriptor]];
379     }
380   }
382   // Make the change.
383   [column setHidden:newState == NSOffState];
384   [item setState:newState];
386   [tableView_ sizeToFit];
387   [tableView_ setNeedsDisplay];
390 // This function appropriately sets the enabled states on the table's editing
391 // buttons.
392 - (void)adjustSelectionAndEndProcessButton {
393   bool selectionContainsBrowserProcess = false;
395   // If a row is selected, make sure that all rows belonging to the same process
396   // are selected as well. Also, check if the selection contains the browser
397   // process.
398   NSIndexSet* selection = [tableView_ selectedRowIndexes];
399   for (NSUInteger i = [selection lastIndex];
400        i != NSNotFound;
401        i = [selection indexLessThanIndex:i]) {
402     int modelIndex = viewToModelMap_[i];
403     if (taskManager_->IsBrowserProcess(modelIndex))
404       selectionContainsBrowserProcess = true;
406     TaskManagerModel::GroupRange rangePair =
407         model_->GetGroupRangeForResource(modelIndex);
408     NSMutableIndexSet* indexSet = [NSMutableIndexSet indexSet];
409     for (int j = 0; j < rangePair.second; ++j)
410       [indexSet addIndex:modelToViewMap_[rangePair.first + j]];
411     [tableView_ selectRowIndexes:indexSet byExtendingSelection:YES];
412   }
414   bool enabled = [selection count] > 0 && !selectionContainsBrowserProcess;
415   [endProcessButton_ setEnabled:enabled];
418 - (void)deselectRows {
419   [tableView_ deselectAll:self];
422 // Table view delegate methods.
424 // The selection is being changed by mouse (drag/click).
425 - (void)tableViewSelectionIsChanging:(NSNotification*)aNotification {
426   [self adjustSelectionAndEndProcessButton];
429 // The selection is being changed by keyboard (arrows).
430 - (void)tableViewSelectionDidChange:(NSNotification*)aNotification {
431   [self adjustSelectionAndEndProcessButton];
434 - (void)windowWillClose:(NSNotification*)notification {
435   if (taskManagerObserver_) {
436     taskManagerObserver_->WindowWasClosed();
437     taskManagerObserver_ = nil;
438   }
439   [self autorelease];
442 @end
444 @implementation TaskManagerWindowController (NSTableDataSource)
446 - (NSInteger)numberOfRowsInTableView:(NSTableView*)tableView {
447   DCHECK(tableView == tableView_ || tableView_ == nil);
448   return model_->ResourceCount();
451 - (NSString*)modelTextForRow:(int)row column:(int)columnId {
452   DCHECK_LT(static_cast<size_t>(row), viewToModelMap_.size());
453   return base::SysUTF16ToNSString(
454       model_->GetResourceById(viewToModelMap_[row], columnId));
457 - (id)tableView:(NSTableView*)tableView
458     objectValueForTableColumn:(NSTableColumn*)tableColumn
459                           row:(NSInteger)rowIndex {
460   // NSButtonCells expect an on/off state as objectValue. Their title is set
461   // in |tableView:dataCellForTableColumn:row:| below.
462   if ([[tableColumn identifier] intValue] == IDS_TASK_MANAGER_TASK_COLUMN) {
463     return [NSNumber numberWithInt:NSOffState];
464   }
466   return [self modelTextForRow:rowIndex
467                         column:[[tableColumn identifier] intValue]];
470 - (NSCell*)tableView:(NSTableView*)tableView
471     dataCellForTableColumn:(NSTableColumn*)tableColumn
472                        row:(NSInteger)rowIndex {
473   NSCell* cell = [tableColumn dataCellForRow:rowIndex];
475   // Set the favicon and title for the task in the name column.
476   if ([[tableColumn identifier] intValue] == IDS_TASK_MANAGER_TASK_COLUMN) {
477     DCHECK([cell isKindOfClass:[NSButtonCell class]]);
478     NSButtonCell* buttonCell = static_cast<NSButtonCell*>(cell);
479     NSString* title = [self modelTextForRow:rowIndex
480                                     column:[[tableColumn identifier] intValue]];
481     [buttonCell setTitle:title];
482     [buttonCell setImage:
483         taskManagerObserver_->GetImageForRow(viewToModelMap_[rowIndex])];
484     [buttonCell setRefusesFirstResponder:YES];  // Don't push in like a button.
485     [buttonCell setHighlightsBy:NSNoCellMask];
486   }
488   return cell;
491 - (void)           tableView:(NSTableView*)tableView
492     sortDescriptorsDidChange:(NSArray*)oldDescriptors {
493   NSArray* newDescriptors = [tableView sortDescriptors];
494   if ([newDescriptors count] < 1) {
495     currentSortDescriptor_.reset(nil);
496   } else {
497     currentSortDescriptor_.reset([[newDescriptors objectAtIndex:0] retain]);
498   }
500   [self reloadData];  // Sorts.
503 @end
505 ////////////////////////////////////////////////////////////////////////////////
506 // TaskManagerMac implementation:
508 TaskManagerMac::TaskManagerMac(TaskManager* task_manager)
509   : task_manager_(task_manager),
510     model_(task_manager->model()),
511     icon_cache_(this) {
512   window_controller_ =
513       [[TaskManagerWindowController alloc] initWithTaskManagerObserver:this];
514   model_->AddObserver(this);
517 // static
518 TaskManagerMac* TaskManagerMac::instance_ = NULL;
520 TaskManagerMac::~TaskManagerMac() {
521   if (this == instance_) {
522     // Do not do this when running in unit tests: |StartUpdating()| never got
523     // called in that case.
524     task_manager_->OnWindowClosed();
525   }
526   model_->RemoveObserver(this);
529 ////////////////////////////////////////////////////////////////////////////////
530 // TaskManagerMac, TaskManagerModelObserver implementation:
532 void TaskManagerMac::OnModelChanged() {
533   icon_cache_.OnModelChanged();
534   [window_controller_ deselectRows];
535   [window_controller_ reloadData];
538 void TaskManagerMac::OnItemsChanged(int start, int length) {
539   icon_cache_.OnItemsChanged(start, length);
540   [window_controller_ reloadData];
543 void TaskManagerMac::OnItemsAdded(int start, int length) {
544   icon_cache_.OnItemsAdded(start, length);
545   [window_controller_ deselectRows];
546   [window_controller_ reloadData];
549 void TaskManagerMac::OnItemsRemoved(int start, int length) {
550   icon_cache_.OnItemsRemoved(start, length);
551   [window_controller_ deselectRows];
552   [window_controller_ reloadData];
555 NSImage* TaskManagerMac::GetImageForRow(int row) {
556   return icon_cache_.GetImageForRow(row);
559 ////////////////////////////////////////////////////////////////////////////////
560 // TaskManagerMac, public:
562 void TaskManagerMac::WindowWasClosed() {
563   delete this;
564   instance_ = NULL;  // |instance_| is static
567 int TaskManagerMac::RowCount() const {
568   return model_->ResourceCount();
571 gfx::ImageSkia TaskManagerMac::GetIcon(int r) const {
572   return model_->GetResourceIcon(r);
575 // static
576 void TaskManagerMac::Show() {
577   if (instance_) {
578     [[instance_->window_controller_ window]
579       makeKeyAndOrderFront:instance_->window_controller_];
580     return;
581   }
582   // Create a new instance.
583   instance_ = new TaskManagerMac(TaskManager::GetInstance());
584   instance_->model_->StartUpdating();
587 // static
588 void TaskManagerMac::Hide() {
589   if (instance_)
590     [instance_->window_controller_ close];
593 namespace chrome {
595 // Declared in browser_dialogs.h.
596 void ShowTaskManager(Browser* browser) {
597   TaskManagerMac::Show();
600 void HideTaskManager() {
601   TaskManagerMac::Hide();
604 }  // namespace chrome