task_manager_view.cc revision 868fa2fe829687343ffae624259930155e16dbd8
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/strings/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( 216 const ViewHierarchyChangedDetails& details) 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 virtual bool UseNewStyleForThisDialog() const OVERRIDE; 231 232 // views::TableViewObserver: 233 virtual void OnSelectionChanged() OVERRIDE; 234 virtual void OnDoubleClick() OVERRIDE; 235 virtual void OnKeyDown(ui::KeyboardCode keycode) OVERRIDE; 236 237 // views::LinkListener: 238 virtual void LinkClicked(views::Link* source, int event_flags) OVERRIDE; 239 240 // Called by the column picker to pick up any new stat counters that 241 // may have appeared since last time. 242 void UpdateStatsCounters(); 243 244 // views::ContextMenuController: 245 virtual void ShowContextMenuForView(views::View* source, 246 const gfx::Point& point) OVERRIDE; 247 248 // ui::SimpleMenuModel::Delegate: 249 virtual bool IsCommandIdChecked(int id) const OVERRIDE; 250 virtual bool IsCommandIdEnabled(int id) const OVERRIDE; 251 virtual bool GetAcceleratorForCommandId( 252 int command_id, 253 ui::Accelerator* accelerator) OVERRIDE; 254 virtual void ExecuteCommand(int id, int event_flags) OVERRIDE; 255 256 private: 257 // Creates the child controls. 258 void Init(); 259 260 // Initializes the state of the always-on-top setting as the window is shown. 261 void InitAlwaysOnTopState(); 262 263 // Activates the tab associated with the focused row. 264 void ActivateFocusedTab(); 265 266 // Adds an always on top item to the window's system menu. 267 void AddAlwaysOnTopSystemMenuItem(); 268 269 // Restores saved always on top state from a previous session. 270 bool GetSavedAlwaysOnTopState(bool* always_on_top) const; 271 272 views::LabelButton* purge_memory_button_; 273 views::LabelButton* kill_button_; 274 views::Link* about_memory_link_; 275 views::TableView* tab_table_; 276 views::View* tab_table_parent_; 277 278 TaskManager* task_manager_; 279 280 TaskManagerModel* model_; 281 282 // all possible columns, not necessarily visible 283 std::vector<ui::TableColumn> columns_; 284 285 scoped_ptr<TaskManagerTableModel> table_model_; 286 287 // True when the Task Manager window should be shown on top of other windows. 288 bool is_always_on_top_; 289 290 // True when the Task Manager should highlight background resources. 291 const bool highlight_background_resources_; 292 293 // The host desktop type this task manager belongs to. 294 const chrome::HostDesktopType desktop_type_; 295 296 // We need to own the text of the menu, the Windows API does not copy it. 297 string16 always_on_top_menu_text_; 298 299 // An open Task manager window. There can only be one open at a time. This 300 // is reset to NULL when the window is closed. 301 static TaskManagerView* instance_; 302 303 scoped_ptr<views::MenuRunner> menu_runner_; 304 305 DISALLOW_COPY_AND_ASSIGN(TaskManagerView); 306}; 307 308// static 309TaskManagerView* TaskManagerView::instance_ = NULL; 310 311 312TaskManagerView::TaskManagerView(bool highlight_background_resources, 313 chrome::HostDesktopType desktop_type) 314 : purge_memory_button_(NULL), 315 kill_button_(NULL), 316 about_memory_link_(NULL), 317 tab_table_(NULL), 318 tab_table_parent_(NULL), 319 task_manager_(TaskManager::GetInstance()), 320 model_(TaskManager::GetInstance()->model()), 321 is_always_on_top_(false), 322 highlight_background_resources_(highlight_background_resources), 323 desktop_type_(desktop_type) { 324 Init(); 325} 326 327TaskManagerView::~TaskManagerView() { 328 // Delete child views now, while our table model still exists. 329 RemoveAllChildViews(true); 330} 331 332void TaskManagerView::Init() { 333 table_model_.reset(new TaskManagerTableModel(model_)); 334 335 // Page column has no header label. 336 columns_.push_back(ui::TableColumn(IDS_TASK_MANAGER_TASK_COLUMN, 337 ui::TableColumn::LEFT, -1, 1)); 338 columns_.back().sortable = true; 339 columns_.push_back(ui::TableColumn(IDS_TASK_MANAGER_PROFILE_NAME_COLUMN, 340 ui::TableColumn::LEFT, -1, 0)); 341 columns_.back().sortable = true; 342 columns_.push_back(ui::TableColumn(IDS_TASK_MANAGER_PHYSICAL_MEM_COLUMN, 343 ui::TableColumn::RIGHT, -1, 0)); 344 columns_.back().sortable = true; 345 columns_.push_back(ui::TableColumn(IDS_TASK_MANAGER_SHARED_MEM_COLUMN, 346 ui::TableColumn::RIGHT, -1, 0)); 347 columns_.back().sortable = true; 348 columns_.push_back(ui::TableColumn(IDS_TASK_MANAGER_PRIVATE_MEM_COLUMN, 349 ui::TableColumn::RIGHT, -1, 0)); 350 columns_.back().sortable = true; 351 columns_.push_back(ui::TableColumn(IDS_TASK_MANAGER_CPU_COLUMN, 352 ui::TableColumn::RIGHT, -1, 0)); 353 columns_.back().sortable = true; 354 columns_.push_back(ui::TableColumn(IDS_TASK_MANAGER_NET_COLUMN, 355 ui::TableColumn::RIGHT, -1, 0)); 356 columns_.back().sortable = true; 357 columns_.push_back(ui::TableColumn(IDS_TASK_MANAGER_PROCESS_ID_COLUMN, 358 ui::TableColumn::RIGHT, -1, 0)); 359 columns_.back().sortable = true; 360#if defined(OS_WIN) 361 columns_.push_back(ui::TableColumn(IDS_TASK_MANAGER_GDI_HANDLES_COLUMN, 362 ui::TableColumn::RIGHT, -1, 0)); 363 columns_.back().sortable = true; 364 columns_.push_back(ui::TableColumn(IDS_TASK_MANAGER_USER_HANDLES_COLUMN, 365 ui::TableColumn::RIGHT, -1, 0)); 366 columns_.back().sortable = true; 367#endif 368 columns_.push_back(ui::TableColumn( 369 IDS_TASK_MANAGER_WEBCORE_IMAGE_CACHE_COLUMN, 370 ui::TableColumn::RIGHT, -1, 0)); 371 columns_.back().sortable = true; 372 columns_.push_back(ui::TableColumn( 373 IDS_TASK_MANAGER_WEBCORE_SCRIPTS_CACHE_COLUMN, 374 ui::TableColumn::RIGHT, -1, 0)); 375 columns_.back().sortable = true; 376 columns_.push_back(ui::TableColumn(IDS_TASK_MANAGER_WEBCORE_CSS_CACHE_COLUMN, 377 ui::TableColumn::RIGHT, -1, 0)); 378 columns_.back().sortable = true; 379 columns_.push_back(ui::TableColumn(IDS_TASK_MANAGER_FPS_COLUMN, 380 ui::TableColumn::RIGHT, -1, 0)); 381 columns_.back().sortable = true; 382 columns_.push_back(ui::TableColumn(IDS_TASK_MANAGER_VIDEO_MEMORY_COLUMN, 383 ui::TableColumn::RIGHT, -1, 0)); 384 columns_.back().sortable = true; 385 columns_.push_back(ui::TableColumn(IDS_TASK_MANAGER_SQLITE_MEMORY_USED_COLUMN, 386 ui::TableColumn::RIGHT, -1, 0)); 387 columns_.back().sortable = true; 388 columns_.push_back( 389 ui::TableColumn(IDS_TASK_MANAGER_JAVASCRIPT_MEMORY_ALLOCATED_COLUMN, 390 ui::TableColumn::RIGHT, -1, 0)); 391 columns_.back().sortable = true; 392 393 tab_table_ = new views::TableView( 394 table_model_.get(), columns_, views::ICON_AND_TEXT, false); 395 tab_table_->SetGrouper(table_model_.get()); 396 if (highlight_background_resources_) { 397 scoped_ptr<BackgroundPainter> painter( 398 new BackgroundPainter(table_model_.get())); 399 tab_table_->SetRowBackgroundPainter( 400 painter.PassAs<views::TableViewRowBackgroundPainter>()); 401 } 402 403 // Hide some columns by default 404 tab_table_->SetColumnVisibility(IDS_TASK_MANAGER_PROFILE_NAME_COLUMN, false); 405 tab_table_->SetColumnVisibility(IDS_TASK_MANAGER_SHARED_MEM_COLUMN, false); 406 tab_table_->SetColumnVisibility(IDS_TASK_MANAGER_PRIVATE_MEM_COLUMN, false); 407 tab_table_->SetColumnVisibility(IDS_TASK_MANAGER_WEBCORE_IMAGE_CACHE_COLUMN, 408 false); 409 tab_table_->SetColumnVisibility(IDS_TASK_MANAGER_WEBCORE_SCRIPTS_CACHE_COLUMN, 410 false); 411 tab_table_->SetColumnVisibility(IDS_TASK_MANAGER_WEBCORE_CSS_CACHE_COLUMN, 412 false); 413 tab_table_->SetColumnVisibility(IDS_TASK_MANAGER_VIDEO_MEMORY_COLUMN, 414 false); 415 tab_table_->SetColumnVisibility(IDS_TASK_MANAGER_SQLITE_MEMORY_USED_COLUMN, 416 false); 417 tab_table_->SetColumnVisibility( 418 IDS_TASK_MANAGER_JAVASCRIPT_MEMORY_ALLOCATED_COLUMN, false); 419 tab_table_->SetColumnVisibility(IDS_TASK_MANAGER_GOATS_TELEPORTED_COLUMN, 420 false); 421 tab_table_->SetColumnVisibility(IDS_TASK_MANAGER_GDI_HANDLES_COLUMN, false); 422 tab_table_->SetColumnVisibility(IDS_TASK_MANAGER_USER_HANDLES_COLUMN, false); 423 424 UpdateStatsCounters(); 425 tab_table_->SetObserver(this); 426 tab_table_->set_context_menu_controller(this); 427 set_context_menu_controller(this); 428 // If we're running with --purge-memory-button, add a "Purge memory" button. 429 if (CommandLine::ForCurrentProcess()->HasSwitch( 430 switches::kPurgeMemoryButton)) { 431 purge_memory_button_ = new views::LabelButton(this, 432 l10n_util::GetStringUTF16(IDS_TASK_MANAGER_PURGE_MEMORY)); 433 purge_memory_button_->SetStyle(views::Button::STYLE_NATIVE_TEXTBUTTON); 434 } 435 kill_button_ = new views::LabelButton(this, 436 l10n_util::GetStringUTF16(IDS_TASK_MANAGER_KILL)); 437 kill_button_->SetStyle(views::Button::STYLE_NATIVE_TEXTBUTTON); 438 about_memory_link_ = new views::Link( 439 l10n_util::GetStringUTF16(IDS_TASK_MANAGER_ABOUT_MEMORY_LINK)); 440 about_memory_link_->set_listener(this); 441 442 // Makes sure our state is consistent. 443 OnSelectionChanged(); 444} 445 446void TaskManagerView::UpdateStatsCounters() { 447 base::StatsTable* stats = base::StatsTable::current(); 448 if (stats != NULL) { 449 int max = stats->GetMaxCounters(); 450 // skip the first row (it's header data) 451 for (int i = 1; i < max; i++) { 452 const char* row = stats->GetRowName(i); 453 if (row != NULL && row[0] != '\0' && !tab_table_->HasColumn(i)) { 454 // TODO(erikkay): Use l10n to get display names for stats. Right 455 // now we're just displaying the internal counter name. Perhaps 456 // stat names not in the string table would be filtered out. 457 ui::TableColumn col; 458 col.id = i; 459 col.title = ASCIIToUTF16(row); 460 col.alignment = ui::TableColumn::RIGHT; 461 // TODO(erikkay): Width is hard-coded right now, so many column 462 // names are clipped. 463 col.width = 90; 464 col.sortable = true; 465 columns_.push_back(col); 466 tab_table_->AddColumn(col); 467 } 468 } 469 } 470} 471 472void TaskManagerView::ViewHierarchyChanged( 473 const ViewHierarchyChangedDetails& details) { 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 (details.child == this) { 480 if (details.is_add) { 481 details.parent->AddChildView(about_memory_link_); 482 if (purge_memory_button_) 483 details.parent->AddChildView(purge_memory_button_); 484 details.parent->AddChildView(kill_button_); 485 tab_table_parent_ = tab_table_->CreateParentIfNecessary(); 486 AddChildView(tab_table_parent_); 487 } else { 488 details.parent->RemoveChildView(kill_button_); 489 if (purge_memory_button_) 490 details.parent->RemoveChildView(purge_memory_button_); 491 details.parent->RemoveChildView(about_memory_link_); 492 } 493 } 494} 495 496void TaskManagerView::Layout() { 497 gfx::Size size = kill_button_->GetPreferredSize(); 498 gfx::Rect parent_bounds = parent()->GetContentsBounds(); 499 const int horizontal_margin = views::kPanelHorizMargin; 500 const int vertical_margin = views::kButtonVEdgeMargin; 501 int x = width() - size.width() - horizontal_margin; 502 int y_buttons = parent_bounds.bottom() - size.height() - vertical_margin; 503 kill_button_->SetBounds(x, y_buttons, size.width(), size.height()); 504 505 if (purge_memory_button_) { 506 size = purge_memory_button_->GetPreferredSize(); 507 purge_memory_button_->SetBounds( 508 kill_button_->x() - size.width() - 509 views::kUnrelatedControlHorizontalSpacing, 510 y_buttons, size.width(), size.height()); 511 } 512 513 size = about_memory_link_->GetPreferredSize(); 514 about_memory_link_->SetBounds( 515 horizontal_margin, 516 y_buttons + (kill_button_->height() - size.height()) / 2, 517 size.width(), size.height()); 518 519 gfx::Rect rect = GetLocalBounds(); 520 rect.Inset(horizontal_margin, views::kPanelVertMargin); 521 rect.Inset(0, 0, 0, 522 kill_button_->height() + views::kUnrelatedControlVerticalSpacing); 523 tab_table_parent_->SetBoundsRect(rect); 524} 525 526gfx::Size TaskManagerView::GetPreferredSize() { 527 return gfx::Size(460, 270); 528} 529 530// static 531void TaskManagerView::Show(bool highlight_background_resources, 532 Browser* browser) { 533#if defined(OS_WIN) 534 // In Windows Metro it's not good to open this native window. 535 DCHECK(!win8::IsSingleWindowMetroMode()); 536#endif 537 // In ash we can come here through the ChromeShellDelegate. If there is no 538 // browser window at that time of the call, browser could be passed as NULL. 539 const chrome::HostDesktopType desktop_type = 540 browser ? browser->host_desktop_type() : chrome::HOST_DESKTOP_TYPE_ASH; 541 542 if (instance_) { 543 if (instance_->highlight_background_resources_ != 544 highlight_background_resources || 545 instance_->desktop_type_ != desktop_type) { 546 instance_->GetWidget()->Close(); 547 } else { 548 // If there's a Task manager window open already, just activate it. 549 instance_->GetWidget()->Activate(); 550 return; 551 } 552 } 553 instance_ = new TaskManagerView(highlight_background_resources, desktop_type); 554 gfx::NativeWindow window = 555 browser ? browser->window()->GetNativeWindow() : NULL; 556#if defined(USE_ASH) 557 if (!window) 558 window = ash::wm::GetActiveWindow(); 559#endif 560 DialogDelegate::CreateDialogWidget(instance_, window, NULL); 561 instance_->InitAlwaysOnTopState(); 562 instance_->model_->StartUpdating(); 563 instance_->GetWidget()->Show(); 564 565 // Set the initial focus to the list of tasks. 566 views::FocusManager* focus_manager = instance_->GetFocusManager(); 567 if (focus_manager) 568 focus_manager->SetFocusedView(instance_->tab_table_); 569} 570 571// ButtonListener implementation. 572void TaskManagerView::ButtonPressed( 573 views::Button* sender, 574 const ui::Event& event) { 575 if (purge_memory_button_ && (sender == purge_memory_button_)) { 576 MemoryPurger::PurgeAll(); 577 } else { 578 typedef ui::ListSelectionModel::SelectedIndices SelectedIndices; 579 DCHECK_EQ(kill_button_, sender); 580 SelectedIndices selection(tab_table_->selection_model().selected_indices()); 581 for (SelectedIndices::const_reverse_iterator i = selection.rbegin(); 582 i != selection.rend(); ++i) { 583 task_manager_->KillProcess(*i); 584 } 585 } 586} 587 588// DialogDelegate implementation. 589bool TaskManagerView::CanResize() const { 590 return true; 591} 592 593bool TaskManagerView::CanMaximize() const { 594 return true; 595} 596 597bool TaskManagerView::ExecuteWindowsCommand(int command_id) { 598#if defined(OS_WIN) && !defined(USE_AURA) 599 if (command_id == IDC_ALWAYS_ON_TOP) { 600 is_always_on_top_ = !is_always_on_top_; 601 602 // Change the menu check state. 603 HMENU system_menu = GetSystemMenu(GetWidget()->GetNativeWindow(), FALSE); 604 MENUITEMINFO menu_info; 605 memset(&menu_info, 0, sizeof(MENUITEMINFO)); 606 menu_info.cbSize = sizeof(MENUITEMINFO); 607 BOOL r = GetMenuItemInfo(system_menu, IDC_ALWAYS_ON_TOP, 608 FALSE, &menu_info); 609 DCHECK(r); 610 menu_info.fMask = MIIM_STATE; 611 if (is_always_on_top_) 612 menu_info.fState = MFS_CHECKED; 613 r = SetMenuItemInfo(system_menu, IDC_ALWAYS_ON_TOP, FALSE, &menu_info); 614 615 // Now change the actual window's behavior. 616 GetWidget()->SetAlwaysOnTop(is_always_on_top_); 617 618 // Save the state. 619 if (g_browser_process->local_state()) { 620 DictionaryPrefUpdate update(g_browser_process->local_state(), 621 GetWindowName().c_str()); 622 DictionaryValue* window_preferences = update.Get(); 623 window_preferences->SetBoolean("always_on_top", is_always_on_top_); 624 } 625 return true; 626 } 627#endif 628 return false; 629} 630 631string16 TaskManagerView::GetWindowTitle() const { 632 return l10n_util::GetStringUTF16(IDS_TASK_MANAGER_TITLE); 633} 634 635std::string TaskManagerView::GetWindowName() const { 636 return prefs::kTaskManagerWindowPlacement; 637} 638 639int TaskManagerView::GetDialogButtons() const { 640 return ui::DIALOG_BUTTON_NONE; 641} 642 643void TaskManagerView::WindowClosing() { 644 // Now that the window is closed, we can allow a new one to be opened. 645 // (WindowClosing comes in asynchronously from the call to Close() and we 646 // may have already opened a new instance). 647 if (instance_ == this) 648 instance_ = NULL; 649 task_manager_->OnWindowClosed(); 650} 651 652bool TaskManagerView::UseNewStyleForThisDialog() const { 653 return false; 654} 655 656// views::TableViewObserver implementation. 657void TaskManagerView::OnSelectionChanged() { 658 const ui::ListSelectionModel::SelectedIndices& selection( 659 tab_table_->selection_model().selected_indices()); 660 bool selection_contains_browser_process = false; 661 for (size_t i = 0; i < selection.size(); ++i) { 662 if (task_manager_->IsBrowserProcess(selection[i])) { 663 selection_contains_browser_process = true; 664 break; 665 } 666 } 667 kill_button_->SetEnabled(!selection_contains_browser_process && 668 !selection.empty()); 669} 670 671void TaskManagerView::OnDoubleClick() { 672 ActivateFocusedTab(); 673} 674 675void TaskManagerView::OnKeyDown(ui::KeyboardCode keycode) { 676 if (keycode == ui::VKEY_RETURN) 677 ActivateFocusedTab(); 678} 679 680void TaskManagerView::LinkClicked(views::Link* source, int event_flags) { 681 DCHECK_EQ(about_memory_link_, source); 682 task_manager_->OpenAboutMemory(desktop_type_); 683} 684 685void TaskManagerView::ShowContextMenuForView(views::View* source, 686 const gfx::Point& point) { 687 UpdateStatsCounters(); 688 ui::SimpleMenuModel menu_model(this); 689 for (std::vector<ui::TableColumn>::iterator i(columns_.begin()); 690 i != columns_.end(); ++i) { 691 menu_model.AddCheckItem(i->id, l10n_util::GetStringUTF16(i->id)); 692 } 693 menu_runner_.reset(new views::MenuRunner(&menu_model)); 694 if (menu_runner_->RunMenuAt(GetWidget(), NULL, gfx::Rect(point, gfx::Size()), 695 views::MenuItemView::TOPLEFT, 696 views::MenuRunner::CONTEXT_MENU) == 697 views::MenuRunner::MENU_DELETED) 698 return; 699} 700 701bool TaskManagerView::IsCommandIdChecked(int id) const { 702 return tab_table_->IsColumnVisible(id); 703} 704 705bool TaskManagerView::IsCommandIdEnabled(int id) const { 706 return true; 707} 708 709bool TaskManagerView::GetAcceleratorForCommandId( 710 int command_id, 711 ui::Accelerator* accelerator) { 712 return false; 713} 714 715void TaskManagerView::ExecuteCommand(int id, int event_flags) { 716 tab_table_->SetColumnVisibility(id, !tab_table_->IsColumnVisible(id)); 717} 718 719void TaskManagerView::InitAlwaysOnTopState() { 720 is_always_on_top_ = false; 721 if (GetSavedAlwaysOnTopState(&is_always_on_top_)) 722 GetWidget()->SetAlwaysOnTop(is_always_on_top_); 723 AddAlwaysOnTopSystemMenuItem(); 724} 725 726void TaskManagerView::ActivateFocusedTab() { 727 const int active_row = tab_table_->selection_model().active(); 728 if (active_row != -1) 729 task_manager_->ActivateProcess(active_row); 730} 731 732void TaskManagerView::AddAlwaysOnTopSystemMenuItem() { 733#if defined(OS_WIN) && !defined(USE_AURA) 734 // The Win32 API requires that we own the text. 735 always_on_top_menu_text_ = l10n_util::GetStringUTF16(IDS_ALWAYS_ON_TOP); 736 737 // Let's insert a menu to the window. 738 HMENU system_menu = ::GetSystemMenu(GetWidget()->GetNativeWindow(), FALSE); 739 int index = ::GetMenuItemCount(system_menu) - 1; 740 if (index < 0) { 741 // Paranoia check. 742 NOTREACHED(); 743 index = 0; 744 } 745 // First we add the separator. 746 MENUITEMINFO menu_info; 747 memset(&menu_info, 0, sizeof(MENUITEMINFO)); 748 menu_info.cbSize = sizeof(MENUITEMINFO); 749 menu_info.fMask = MIIM_FTYPE; 750 menu_info.fType = MFT_SEPARATOR; 751 ::InsertMenuItem(system_menu, index, TRUE, &menu_info); 752 753 // Then the actual menu. 754 menu_info.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING | MIIM_STATE; 755 menu_info.fType = MFT_STRING; 756 menu_info.fState = MFS_ENABLED; 757 if (is_always_on_top_) 758 menu_info.fState |= MFS_CHECKED; 759 menu_info.wID = IDC_ALWAYS_ON_TOP; 760 menu_info.dwTypeData = const_cast<wchar_t*>(always_on_top_menu_text_.c_str()); 761 ::InsertMenuItem(system_menu, index, TRUE, &menu_info); 762#endif 763} 764 765bool TaskManagerView::GetSavedAlwaysOnTopState(bool* always_on_top) const { 766 if (!g_browser_process->local_state()) 767 return false; 768 769 const DictionaryValue* dictionary = 770 g_browser_process->local_state()->GetDictionary(GetWindowName().c_str()); 771 return dictionary && 772 dictionary->GetBoolean("always_on_top", always_on_top) && always_on_top; 773} 774 775} // namespace 776 777namespace chrome { 778 779// Declared in browser_dialogs.h so others don't need to depend on our header. 780void ShowTaskManager(Browser* browser, bool highlight_background_resources) { 781 TaskManagerView::Show(highlight_background_resources, browser); 782} 783 784} // namespace chrome 785