task_manager_view.cc revision 72a454cd3513ac24fbdd0e0cb9ad70b86a99b801
1// Copyright (c) 2011 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/command_line.h"
8#include "base/compiler_specific.h"
9#include "base/metrics/stats_table.h"
10#include "base/utf_string_conversions.h"
11#include "chrome/app/chrome_command_ids.h"
12#include "chrome/browser/browser_list.h"
13#include "chrome/browser/browser_process.h"
14#include "chrome/browser/browser_window.h"
15#include "chrome/browser/memory_purger.h"
16#include "chrome/browser/prefs/pref_service.h"
17#include "chrome/browser/ui/views/browser_dialogs.h"
18#include "chrome/common/chrome_switches.h"
19#include "chrome/common/pref_names.h"
20#include "grit/chromium_strings.h"
21#include "grit/generated_resources.h"
22#include "grit/theme_resources.h"
23#include "ui/base/l10n/l10n_util.h"
24#include "ui/base/models/table_model_observer.h"
25#include "views/accelerator.h"
26#include "views/background.h"
27#include "views/controls/button/native_button.h"
28#include "views/controls/link.h"
29#include "views/controls/menu/menu.h"
30#include "views/controls/table/group_table_view.h"
31#include "views/controls/table/table_view_observer.h"
32#include "views/layout/layout_constants.h"
33#include "views/widget/widget.h"
34#include "views/window/dialog_delegate.h"
35#include "views/window/window.h"
36
37// The task manager window default size.
38static const int kDefaultWidth = 460;
39static const int kDefaultHeight = 270;
40
41// Yellow highlight used when highlighting background resources.
42static const SkColor kBackgroundResourceHighlight =
43    SkColorSetRGB(0xff,0xf1,0xcd);
44
45namespace {
46
47////////////////////////////////////////////////////////////////////////////////
48// TaskManagerTableModel class
49////////////////////////////////////////////////////////////////////////////////
50
51class TaskManagerTableModel : public views::GroupTableModel,
52                              public TaskManagerModelObserver {
53 public:
54  explicit TaskManagerTableModel(TaskManagerModel* model)
55      : model_(model),
56        observer_(NULL) {
57    model_->AddObserver(this);
58  }
59
60  ~TaskManagerTableModel() {
61    model_->RemoveObserver(this);
62  }
63
64  // GroupTableModel.
65  int RowCount() OVERRIDE;
66  string16 GetText(int row, int column) OVERRIDE;
67  SkBitmap GetIcon(int row) OVERRIDE;
68  void GetGroupRangeForItem(int item, views::GroupRange* range) OVERRIDE;
69  void SetObserver(ui::TableModelObserver* observer) OVERRIDE;
70  virtual int CompareValues(int row1, int row2, int column_id) OVERRIDE;
71
72  // TaskManagerModelObserver.
73  virtual void OnModelChanged();
74  virtual void OnItemsChanged(int start, int length);
75  virtual void OnItemsAdded(int start, int length);
76  virtual void OnItemsRemoved(int start, int length);
77
78  // Returns true if resource corresponding to |row| is a background resource.
79  bool IsBackgroundResource(int row);
80
81 private:
82  TaskManagerModel* model_;
83  ui::TableModelObserver* observer_;
84};
85
86int TaskManagerTableModel::RowCount() {
87  return model_->ResourceCount();
88}
89
90string16 TaskManagerTableModel::GetText(int row, int col_id) {
91  switch (col_id) {
92    case IDS_TASK_MANAGER_PAGE_COLUMN:  // Process
93      return model_->GetResourceTitle(row);
94
95    case IDS_TASK_MANAGER_NET_COLUMN:  // Net
96      return model_->GetResourceNetworkUsage(row);
97
98    case IDS_TASK_MANAGER_CPU_COLUMN:  // CPU
99      if (!model_->IsResourceFirstInGroup(row))
100        return string16();
101      return model_->GetResourceCPUUsage(row);
102
103    case IDS_TASK_MANAGER_PRIVATE_MEM_COLUMN:  // Memory
104      if (!model_->IsResourceFirstInGroup(row))
105        return string16();
106      return model_->GetResourcePrivateMemory(row);
107
108    case IDS_TASK_MANAGER_SHARED_MEM_COLUMN:  // Memory
109      if (!model_->IsResourceFirstInGroup(row))
110        return string16();
111      return model_->GetResourceSharedMemory(row);
112
113    case IDS_TASK_MANAGER_PHYSICAL_MEM_COLUMN:  // Memory
114      if (!model_->IsResourceFirstInGroup(row))
115        return string16();
116      return model_->GetResourcePhysicalMemory(row);
117
118    case IDS_TASK_MANAGER_PROCESS_ID_COLUMN:
119      if (!model_->IsResourceFirstInGroup(row))
120        return string16();
121      return model_->GetResourceProcessId(row);
122
123    case IDS_TASK_MANAGER_GOATS_TELEPORTED_COLUMN:  // Goats Teleported!
124      return model_->GetResourceGoatsTeleported(row);
125
126    case IDS_TASK_MANAGER_WEBCORE_IMAGE_CACHE_COLUMN:
127      if (!model_->IsResourceFirstInGroup(row))
128        return string16();
129      return model_->GetResourceWebCoreImageCacheSize(row);
130
131    case IDS_TASK_MANAGER_WEBCORE_SCRIPTS_CACHE_COLUMN:
132      if (!model_->IsResourceFirstInGroup(row))
133        return string16();
134      return model_->GetResourceWebCoreScriptsCacheSize(row);
135
136    case IDS_TASK_MANAGER_WEBCORE_CSS_CACHE_COLUMN:
137      if (!model_->IsResourceFirstInGroup(row))
138        return string16();
139      return model_->GetResourceWebCoreCSSCacheSize(row);
140
141    case IDS_TASK_MANAGER_SQLITE_MEMORY_USED_COLUMN:
142      if (!model_->IsResourceFirstInGroup(row))
143        return string16();
144      return model_->GetResourceSqliteMemoryUsed(row);
145
146    case IDS_TASK_MANAGER_JAVASCRIPT_MEMORY_ALLOCATED_COLUMN:
147      if (!model_->IsResourceFirstInGroup(row))
148        return string16();
149      return model_->GetResourceV8MemoryAllocatedSize(row);
150
151    default:
152      NOTREACHED();
153      return string16();
154  }
155}
156
157SkBitmap TaskManagerTableModel::GetIcon(int row) {
158  return model_->GetResourceIcon(row);
159}
160
161
162void TaskManagerTableModel::GetGroupRangeForItem(int item,
163                                                 views::GroupRange* range) {
164  std::pair<int, int> range_pair = model_->GetGroupRangeForResource(item);
165  range->start = range_pair.first;
166  range->length = range_pair.second;
167}
168
169void TaskManagerTableModel::SetObserver(ui::TableModelObserver* observer) {
170  observer_ = observer;
171}
172
173int TaskManagerTableModel::CompareValues(int row1, int row2, int column_id) {
174  return model_->CompareValues(row1, row2, column_id);
175}
176
177void TaskManagerTableModel::OnModelChanged() {
178  if (observer_)
179    observer_->OnModelChanged();
180}
181
182void TaskManagerTableModel::OnItemsChanged(int start, int length) {
183  if (observer_)
184    observer_->OnItemsChanged(start, length);
185}
186
187void TaskManagerTableModel::OnItemsAdded(int start, int length) {
188  if (observer_)
189    observer_->OnItemsAdded(start, length);
190  // There's a bug in the Windows ListView where inserting items with groups
191  // enabled puts them in the wrong position, so we will need to rebuild the
192  // list view in this case.
193  // (see: http://connect.microsoft.com/VisualStudio/feedback/details/115345/).
194  //
195  // Turns out, forcing a list view rebuild causes http://crbug.com/69391
196  // because items are added to the ListView one-at-a-time when initially
197  // displaying the TaskManager, resulting in many ListView rebuilds. So we are
198  // no longer forcing a rebuild for now because the current UI doesn't use
199  // groups - if we are going to add groups in the upcoming TaskManager UI
200  // revamp, we'll need to re-enable this call to OnModelChanged() and also add
201  // code to avoid doing multiple rebuilds on startup (maybe just generate a
202  // single OnModelChanged() call after the initial population).
203
204  // OnModelChanged();
205}
206
207void TaskManagerTableModel::OnItemsRemoved(int start, int length) {
208  if (observer_)
209    observer_->OnItemsRemoved(start, length);
210
211  // We may need to change the indentation of some items if the topmost item
212  // in the group was removed, so update the view.
213  OnModelChanged();
214}
215
216bool TaskManagerTableModel::IsBackgroundResource(int row) {
217  return model_->IsBackgroundResource(row);
218}
219
220// Thin wrapper around GroupTableView to enable setting the background
221// resource highlight color.
222class BackgroundColorGroupTableView : public views::GroupTableView {
223 public:
224  BackgroundColorGroupTableView(TaskManagerTableModel* model,
225                                std::vector<ui::TableColumn> columns,
226                                bool highlight_background_resources)
227      : views::GroupTableView(model, columns, views::ICON_AND_TEXT,
228                              false, true, true, true),
229        model_(model) {
230    SetCustomColorsEnabled(highlight_background_resources);
231  }
232
233  virtual ~BackgroundColorGroupTableView() {}
234
235 private:
236  virtual bool GetCellColors(int model_row,
237                             int column,
238                             ItemColor* foreground,
239                             ItemColor* background,
240                             LOGFONT* logfont) {
241    if (!model_->IsBackgroundResource(model_row))
242      return false;
243
244    // Render background resources with a yellow highlight.
245    background->color_is_set = true;
246    background->color = kBackgroundResourceHighlight;
247    foreground->color_is_set = false;
248    return true;
249  }
250
251  TaskManagerTableModel* model_;
252};
253
254// The Task manager UI container.
255class TaskManagerView : public views::View,
256                        public views::ButtonListener,
257                        public views::DialogDelegate,
258                        public views::TableViewObserver,
259                        public views::LinkController,
260                        public views::ContextMenuController,
261                        public views::Menu::Delegate {
262 public:
263  explicit TaskManagerView(bool highlight_background_resources);
264  virtual ~TaskManagerView();
265
266  // Shows the Task manager window, or re-activates an existing one. If
267  // |highlight_background_resources| is true, highlights the background
268  // resources in the resource display.
269  static void Show(bool highlight_background_resources);
270
271  // views::View
272  virtual void Layout();
273  virtual gfx::Size GetPreferredSize();
274  virtual void ViewHierarchyChanged(bool is_add, views::View* parent,
275                                    views::View* child);
276
277  // ButtonListener implementation.
278  virtual void ButtonPressed(views::Button* sender, const views::Event& event);
279
280  // views::DialogDelegate
281  virtual bool CanResize() const;
282  virtual bool CanMaximize() const;
283  virtual bool ExecuteWindowsCommand(int command_id);
284  virtual std::wstring GetWindowTitle() const;
285  virtual std::wstring GetWindowName() const;
286  virtual int GetDialogButtons() const;
287  virtual void WindowClosing();
288  virtual views::View* GetContentsView();
289
290  // views::TableViewObserver implementation.
291  virtual void OnSelectionChanged();
292  virtual void OnDoubleClick();
293  virtual void OnKeyDown(ui::KeyboardCode keycode);
294
295  // views::LinkController implementation.
296  virtual void LinkActivated(views::Link* source, int event_flags);
297
298  // Called by the column picker to pick up any new stat counters that
299  // may have appeared since last time.
300  void UpdateStatsCounters();
301
302  // Menu::Delegate
303  virtual void ShowContextMenu(views::View* source,
304                               const gfx::Point& p,
305                               bool is_mouse_gesture);
306  virtual bool IsItemChecked(int id) const;
307  virtual void ExecuteCommand(int id);
308
309 private:
310  // Creates the child controls.
311  void Init();
312
313  // Initializes the state of the always-on-top setting as the window is shown.
314  void InitAlwaysOnTopState();
315
316  // Activates the tab associated with the focused row.
317  void ActivateFocusedTab();
318
319  // Adds an always on top item to the window's system menu.
320  void AddAlwaysOnTopSystemMenuItem();
321
322  // Restores saved always on top state from a previous session.
323  bool GetSavedAlwaysOnTopState(bool* always_on_top) const;
324
325  views::NativeButton* purge_memory_button_;
326  views::NativeButton* kill_button_;
327  views::Link* about_memory_link_;
328  views::GroupTableView* tab_table_;
329
330  TaskManager* task_manager_;
331
332  TaskManagerModel* model_;
333
334  // all possible columns, not necessarily visible
335  std::vector<ui::TableColumn> columns_;
336
337  scoped_ptr<TaskManagerTableModel> table_model_;
338
339  // True when the Task Manager window should be shown on top of other windows.
340  bool is_always_on_top_;
341
342  // True when the Task Manager should highlight background resources.
343  bool highlight_background_resources_;
344
345  // We need to own the text of the menu, the Windows API does not copy it.
346  std::wstring always_on_top_menu_text_;
347
348  // An open Task manager window. There can only be one open at a time. This
349  // is reset to NULL when the window is closed.
350  static TaskManagerView* instance_;
351
352  DISALLOW_COPY_AND_ASSIGN(TaskManagerView);
353};
354
355// static
356TaskManagerView* TaskManagerView::instance_ = NULL;
357
358
359TaskManagerView::TaskManagerView(bool highlight_background_resources)
360    : purge_memory_button_(NULL),
361      task_manager_(TaskManager::GetInstance()),
362      model_(TaskManager::GetInstance()->model()),
363      is_always_on_top_(false),
364      highlight_background_resources_(highlight_background_resources) {
365  Init();
366}
367
368TaskManagerView::~TaskManagerView() {
369  // Delete child views now, while our table model still exists.
370  RemoveAllChildViews(true);
371}
372
373void TaskManagerView::Init() {
374  table_model_.reset(new TaskManagerTableModel(model_));
375
376  // Page column has no header label.
377  columns_.push_back(ui::TableColumn(IDS_TASK_MANAGER_PAGE_COLUMN,
378                                     ui::TableColumn::LEFT, -1, 1));
379  columns_.back().sortable = true;
380  columns_.push_back(ui::TableColumn(IDS_TASK_MANAGER_PHYSICAL_MEM_COLUMN,
381                                     ui::TableColumn::RIGHT, -1, 0));
382  columns_.back().sortable = true;
383  columns_.push_back(ui::TableColumn(IDS_TASK_MANAGER_SHARED_MEM_COLUMN,
384                                     ui::TableColumn::RIGHT, -1, 0));
385  columns_.back().sortable = true;
386  columns_.push_back(ui::TableColumn(IDS_TASK_MANAGER_PRIVATE_MEM_COLUMN,
387                                     ui::TableColumn::RIGHT, -1, 0));
388  columns_.back().sortable = true;
389  columns_.push_back(ui::TableColumn(IDS_TASK_MANAGER_CPU_COLUMN,
390                                     ui::TableColumn::RIGHT, -1, 0));
391  columns_.back().sortable = true;
392  columns_.push_back(ui::TableColumn(IDS_TASK_MANAGER_NET_COLUMN,
393                                     ui::TableColumn::RIGHT, -1, 0));
394  columns_.back().sortable = true;
395  columns_.push_back(ui::TableColumn(IDS_TASK_MANAGER_PROCESS_ID_COLUMN,
396                                     ui::TableColumn::RIGHT, -1, 0));
397  columns_.back().sortable = true;
398  columns_.push_back(ui::TableColumn(
399      IDS_TASK_MANAGER_WEBCORE_IMAGE_CACHE_COLUMN,
400      ui::TableColumn::RIGHT, -1, 0));
401  columns_.back().sortable = true;
402  columns_.push_back(ui::TableColumn(
403      IDS_TASK_MANAGER_WEBCORE_SCRIPTS_CACHE_COLUMN,
404      ui::TableColumn::RIGHT, -1, 0));
405  columns_.back().sortable = true;
406  columns_.push_back(ui::TableColumn(IDS_TASK_MANAGER_WEBCORE_CSS_CACHE_COLUMN,
407                                     ui::TableColumn::RIGHT, -1, 0));
408  columns_.back().sortable = true;
409  columns_.push_back(ui::TableColumn(IDS_TASK_MANAGER_SQLITE_MEMORY_USED_COLUMN,
410                                     ui::TableColumn::RIGHT, -1, 0));
411  columns_.back().sortable = true;
412  columns_.push_back(
413      ui::TableColumn(IDS_TASK_MANAGER_JAVASCRIPT_MEMORY_ALLOCATED_COLUMN,
414                      ui::TableColumn::RIGHT, -1, 0));
415  columns_.back().sortable = true;
416
417  tab_table_ = new BackgroundColorGroupTableView(
418      table_model_.get(), columns_, highlight_background_resources_);
419
420  // Hide some columns by default
421  tab_table_->SetColumnVisibility(IDS_TASK_MANAGER_PROCESS_ID_COLUMN, false);
422  tab_table_->SetColumnVisibility(IDS_TASK_MANAGER_SHARED_MEM_COLUMN, false);
423  tab_table_->SetColumnVisibility(IDS_TASK_MANAGER_PRIVATE_MEM_COLUMN, false);
424  tab_table_->SetColumnVisibility(IDS_TASK_MANAGER_WEBCORE_IMAGE_CACHE_COLUMN,
425                                  false);
426  tab_table_->SetColumnVisibility(IDS_TASK_MANAGER_WEBCORE_SCRIPTS_CACHE_COLUMN,
427                                  false);
428  tab_table_->SetColumnVisibility(IDS_TASK_MANAGER_WEBCORE_CSS_CACHE_COLUMN,
429                                  false);
430  tab_table_->SetColumnVisibility(IDS_TASK_MANAGER_SQLITE_MEMORY_USED_COLUMN,
431                                  false);
432  tab_table_->SetColumnVisibility(
433      IDS_TASK_MANAGER_JAVASCRIPT_MEMORY_ALLOCATED_COLUMN, false);
434  tab_table_->SetColumnVisibility(IDS_TASK_MANAGER_GOATS_TELEPORTED_COLUMN,
435                                  false);
436
437  UpdateStatsCounters();
438  tab_table_->SetObserver(this);
439  tab_table_->SetContextMenuController(this);
440  SetContextMenuController(this);
441  // If we're running with --purge-memory-button, add a "Purge memory" button.
442  if (CommandLine::ForCurrentProcess()->HasSwitch(
443      switches::kPurgeMemoryButton)) {
444    purge_memory_button_ = new views::NativeButton(this,
445        UTF16ToWide(l10n_util::GetStringUTF16(IDS_TASK_MANAGER_PURGE_MEMORY)));
446  }
447  kill_button_ = new views::NativeButton(
448      this, UTF16ToWide(l10n_util::GetStringUTF16(IDS_TASK_MANAGER_KILL)));
449  kill_button_->AddAccelerator(views::Accelerator(ui::VKEY_E,
450                                                  false, false, false));
451  kill_button_->SetAccessibleKeyboardShortcut(L"E");
452  about_memory_link_ = new views::Link(UTF16ToWide(
453      l10n_util::GetStringUTF16(IDS_TASK_MANAGER_ABOUT_MEMORY_LINK)));
454  about_memory_link_->SetController(this);
455
456  // Makes sure our state is consistent.
457  OnSelectionChanged();
458}
459
460void TaskManagerView::UpdateStatsCounters() {
461  base::StatsTable* stats = base::StatsTable::current();
462  if (stats != NULL) {
463    int max = stats->GetMaxCounters();
464    // skip the first row (it's header data)
465    for (int i = 1; i < max; i++) {
466      const char* row = stats->GetRowName(i);
467      if (row != NULL && row[0] != '\0' && !tab_table_->HasColumn(i)) {
468        // TODO(erikkay): Use l10n to get display names for stats.  Right
469        // now we're just displaying the internal counter name.  Perhaps
470        // stat names not in the string table would be filtered out.
471        // TODO(erikkay): Width is hard-coded right now, so many column
472        // names are clipped.
473        ui::TableColumn col(i, ASCIIToUTF16(row), ui::TableColumn::RIGHT, 90,
474                            0);
475        col.sortable = true;
476        columns_.push_back(col);
477        tab_table_->AddColumn(col);
478      }
479    }
480  }
481}
482
483void TaskManagerView::ViewHierarchyChanged(bool is_add,
484                                           views::View* parent,
485                                           views::View* child) {
486  // Since we want the Kill button and the Memory Details link to show up in
487  // the same visual row as the close button, which is provided by the
488  // framework, we must add the buttons to the non-client view, which is the
489  // parent of this view. Similarly, when we're removed from the view
490  // hierarchy, we must take care to clean up those items as well.
491  if (child == this) {
492    if (is_add) {
493      parent->AddChildView(about_memory_link_);
494      if (purge_memory_button_)
495        parent->AddChildView(purge_memory_button_);
496      parent->AddChildView(kill_button_);
497      AddChildView(tab_table_);
498    } else {
499      parent->RemoveChildView(kill_button_);
500      if (purge_memory_button_)
501        parent->RemoveChildView(purge_memory_button_);
502      parent->RemoveChildView(about_memory_link_);
503    }
504  }
505}
506
507void TaskManagerView::Layout() {
508  // kPanelHorizMargin is too big.
509  const int kTableButtonSpacing = 12;
510
511  gfx::Size size = kill_button_->GetPreferredSize();
512  int prefered_width = size.width();
513  int prefered_height = size.height();
514
515  tab_table_->SetBounds(x() + kPanelHorizMargin,
516                        y() + kPanelVertMargin,
517                        width() - 2 * kPanelHorizMargin,
518                        height() - 2 * kPanelVertMargin - prefered_height);
519
520  // y-coordinate of button top left.
521  gfx::Rect parent_bounds = parent()->GetContentsBounds();
522  int y_buttons =
523      parent_bounds.bottom() - prefered_height - views::kButtonVEdgeMargin;
524
525  kill_button_->SetBounds(x() + width() - prefered_width - kPanelHorizMargin,
526                          y_buttons,
527                          prefered_width,
528                          prefered_height);
529
530  if (purge_memory_button_) {
531    size = purge_memory_button_->GetPreferredSize();
532    purge_memory_button_->SetBounds(
533        kill_button_->x() - size.width() -
534            views::kUnrelatedControlHorizontalSpacing,
535        y_buttons, size.width(), size.height());
536  }
537
538  size = about_memory_link_->GetPreferredSize();
539  int link_prefered_width = size.width();
540  int link_prefered_height = size.height();
541  // center between the two buttons horizontally, and line up with
542  // bottom of buttons vertically.
543  int link_y_offset = std::max(0, prefered_height - link_prefered_height) / 2;
544  about_memory_link_->SetBounds(
545      x() + kPanelHorizMargin,
546      y_buttons + prefered_height - link_prefered_height - link_y_offset,
547      link_prefered_width,
548      link_prefered_height);
549}
550
551gfx::Size TaskManagerView::GetPreferredSize() {
552  return gfx::Size(kDefaultWidth, kDefaultHeight);
553}
554
555// static
556void TaskManagerView::Show(bool highlight_background_resources) {
557  if (instance_) {
558    if (instance_->highlight_background_resources_ !=
559        highlight_background_resources) {
560      instance_->window()->Close();
561    } else {
562      // If there's a Task manager window open already, just activate it.
563      instance_->window()->Activate();
564      return;
565    }
566  }
567  instance_ = new TaskManagerView(highlight_background_resources);
568  views::Window::CreateChromeWindow(NULL, gfx::Rect(), instance_);
569  instance_->InitAlwaysOnTopState();
570  instance_->model_->StartUpdating();
571  instance_->window()->Show();
572
573  // Set the initial focus to the list of tasks.
574  views::FocusManager* focus_manager = instance_->GetFocusManager();
575  if (focus_manager)
576    focus_manager->SetFocusedView(instance_->tab_table_);
577}
578
579// ButtonListener implementation.
580void TaskManagerView::ButtonPressed(
581    views::Button* sender, const views::Event& event) {
582  if (purge_memory_button_ && (sender == purge_memory_button_)) {
583    MemoryPurger::PurgeAll();
584  } else {
585    DCHECK_EQ(sender, kill_button_);
586    for (views::TableSelectionIterator iter  = tab_table_->SelectionBegin();
587         iter != tab_table_->SelectionEnd(); ++iter)
588      task_manager_->KillProcess(*iter);
589  }
590}
591
592// DialogDelegate implementation.
593bool TaskManagerView::CanResize() const {
594  return true;
595}
596
597bool TaskManagerView::CanMaximize() const {
598  return true;
599}
600
601bool TaskManagerView::ExecuteWindowsCommand(int command_id) {
602  if (command_id == IDC_ALWAYS_ON_TOP) {
603    is_always_on_top_ = !is_always_on_top_;
604
605    // Change the menu check state.
606    HMENU system_menu = GetSystemMenu(GetWindow()->GetNativeWindow(), FALSE);
607    MENUITEMINFO menu_info;
608    memset(&menu_info, 0, sizeof(MENUITEMINFO));
609    menu_info.cbSize = sizeof(MENUITEMINFO);
610    BOOL r = GetMenuItemInfo(system_menu, IDC_ALWAYS_ON_TOP,
611                             FALSE, &menu_info);
612    DCHECK(r);
613    menu_info.fMask = MIIM_STATE;
614    if (is_always_on_top_)
615      menu_info.fState = MFS_CHECKED;
616    r = SetMenuItemInfo(system_menu, IDC_ALWAYS_ON_TOP, FALSE, &menu_info);
617
618    // Now change the actual window's behavior.
619    window()->SetIsAlwaysOnTop(is_always_on_top_);
620
621    // Save the state.
622    if (g_browser_process->local_state()) {
623      DictionaryValue* window_preferences =
624          g_browser_process->local_state()->GetMutableDictionary(
625              WideToUTF8(GetWindowName()).c_str());
626      window_preferences->SetBoolean("always_on_top", is_always_on_top_);
627    }
628    return true;
629  }
630  return false;
631}
632
633std::wstring TaskManagerView::GetWindowTitle() const {
634  return UTF16ToWide(l10n_util::GetStringUTF16(IDS_TASK_MANAGER_TITLE));
635}
636
637std::wstring TaskManagerView::GetWindowName() const {
638  return UTF8ToWide(prefs::kTaskManagerWindowPlacement);
639}
640
641int TaskManagerView::GetDialogButtons() const {
642  return MessageBoxFlags::DIALOGBUTTON_NONE;
643}
644
645void TaskManagerView::WindowClosing() {
646  // Now that the window is closed, we can allow a new one to be opened.
647  instance_ = NULL;
648  task_manager_->OnWindowClosed();
649}
650
651views::View* TaskManagerView::GetContentsView() {
652  return this;
653}
654
655// views::TableViewObserver implementation.
656void TaskManagerView::OnSelectionChanged() {
657  bool selection_contains_browser_process = false;
658  for (views::TableSelectionIterator iter  = tab_table_->SelectionBegin();
659       iter != tab_table_->SelectionEnd(); ++iter) {
660    if (task_manager_->IsBrowserProcess(*iter)) {
661      selection_contains_browser_process = true;
662      break;
663    }
664  }
665  kill_button_->SetEnabled(!selection_contains_browser_process &&
666                           tab_table_->SelectedRowCount() > 0);
667}
668
669void TaskManagerView::OnDoubleClick() {
670  ActivateFocusedTab();
671}
672
673void TaskManagerView::OnKeyDown(ui::KeyboardCode keycode) {
674  if (keycode == ui::VKEY_RETURN)
675    ActivateFocusedTab();
676}
677
678// views::LinkController implementation
679void TaskManagerView::LinkActivated(views::Link* source, int event_flags) {
680  DCHECK(source == about_memory_link_);
681  task_manager_->OpenAboutMemory();
682}
683
684void TaskManagerView::ShowContextMenu(views::View* source,
685                                      const gfx::Point& p,
686                                      bool is_mouse_gesture) {
687  UpdateStatsCounters();
688  scoped_ptr<views::Menu> menu(views::Menu::Create(
689      this, views::Menu::TOPLEFT, source->GetWidget()->GetNativeView()));
690  for (std::vector<ui::TableColumn>::iterator i =
691       columns_.begin(); i != columns_.end(); ++i) {
692    menu->AppendMenuItem(i->id, l10n_util::GetStringUTF16(i->id),
693        views::Menu::CHECKBOX);
694  }
695  menu->RunMenuAt(p.x(), p.y());
696}
697
698bool TaskManagerView::IsItemChecked(int id) const {
699  return tab_table_->IsColumnVisible(id);
700}
701
702void TaskManagerView::ExecuteCommand(int id) {
703  tab_table_->SetColumnVisibility(id, !tab_table_->IsColumnVisible(id));
704}
705
706void TaskManagerView::InitAlwaysOnTopState() {
707  is_always_on_top_ = false;
708  if (GetSavedAlwaysOnTopState(&is_always_on_top_))
709    window()->SetIsAlwaysOnTop(is_always_on_top_);
710  AddAlwaysOnTopSystemMenuItem();
711}
712
713void TaskManagerView::ActivateFocusedTab() {
714  int row_count = tab_table_->RowCount();
715  for (int i = 0; i < row_count; ++i) {
716    if (tab_table_->ItemHasTheFocus(i)) {
717      task_manager_->ActivateProcess(i);
718      break;
719    }
720  }
721}
722
723void TaskManagerView::AddAlwaysOnTopSystemMenuItem() {
724  // The Win32 API requires that we own the text.
725  always_on_top_menu_text_ =
726      UTF16ToWide(l10n_util::GetStringUTF16(IDS_ALWAYS_ON_TOP));
727
728  // Let's insert a menu to the window.
729  HMENU system_menu = ::GetSystemMenu(GetWindow()->GetNativeWindow(), FALSE);
730  int index = ::GetMenuItemCount(system_menu) - 1;
731  if (index < 0) {
732    // Paranoia check.
733    NOTREACHED();
734    index = 0;
735  }
736  // First we add the separator.
737  MENUITEMINFO menu_info;
738  memset(&menu_info, 0, sizeof(MENUITEMINFO));
739  menu_info.cbSize = sizeof(MENUITEMINFO);
740  menu_info.fMask = MIIM_FTYPE;
741  menu_info.fType = MFT_SEPARATOR;
742  ::InsertMenuItem(system_menu, index, TRUE, &menu_info);
743
744  // Then the actual menu.
745  menu_info.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING | MIIM_STATE;
746  menu_info.fType = MFT_STRING;
747  menu_info.fState = MFS_ENABLED;
748  if (is_always_on_top_)
749    menu_info.fState |= MFS_CHECKED;
750  menu_info.wID = IDC_ALWAYS_ON_TOP;
751  menu_info.dwTypeData = const_cast<wchar_t*>(always_on_top_menu_text_.c_str());
752  ::InsertMenuItem(system_menu, index, TRUE, &menu_info);
753}
754
755bool TaskManagerView::GetSavedAlwaysOnTopState(bool* always_on_top) const {
756  if (!g_browser_process->local_state())
757    return false;
758
759  const DictionaryValue* dictionary =
760      g_browser_process->local_state()->GetDictionary(
761          WideToUTF8(GetWindowName()).c_str());
762  return dictionary &&
763      dictionary->GetBoolean("always_on_top", always_on_top) && always_on_top;
764}
765
766}  // namespace
767
768namespace browser {
769
770// Declared in browser_dialogs.h so others don't need to depend on our header.
771void ShowTaskManager() {
772  TaskManagerView::Show(false);
773}
774
775void ShowBackgroundPages() {
776  TaskManagerView::Show(true);
777}
778
779}  // namespace browser
780