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