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