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