task_manager_view.cc revision a93a17c8d99d686bd4a1511e5504e5e6cc9fcadf
1f10a79a21002a38e6ebc4ed01dec31872f39aed6Richard Smith// Copyright (c) 2012 The Chromium Authors. All rights reserved. 22d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines// Use of this source code is governed by a BSD-style license that can be 32d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines// found in the LICENSE file. 42d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines 5b04caf1385a4279a7b95d41c3ccefc61842c3633Richard Smith#include "chrome/browser/task_manager/task_manager.h" 6b04caf1385a4279a7b95d41c3ccefc61842c3633Richard Smith 77cbd7e502e993320a3a1578179d336c268b80604Will Dietz#include "base/command_line.h" 87cbd7e502e993320a3a1578179d336c268b80604Will Dietz#include "base/compiler_specific.h" 9b04caf1385a4279a7b95d41c3ccefc61842c3633Richard Smith#include "base/metrics/stats_table.h" 10b04caf1385a4279a7b95d41c3ccefc61842c3633Richard Smith#include "base/prefs/pref_service.h" 11b04caf1385a4279a7b95d41c3ccefc61842c3633Richard Smith#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( 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 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( 472 const ViewHierarchyChangedDetails& details) { 473 // Since we want the Kill button and the Memory Details link to show up in 474 // the same visual row as the close button, which is provided by the 475 // framework, we must add the buttons to the non-client view, which is the 476 // parent of this view. Similarly, when we're removed from the view 477 // hierarchy, we must take care to clean up those items as well. 478 if (details.child == this) { 479 if (details.is_add) { 480 details.parent->AddChildView(about_memory_link_); 481 if (purge_memory_button_) 482 details.parent->AddChildView(purge_memory_button_); 483 details.parent->AddChildView(kill_button_); 484 tab_table_parent_ = tab_table_->CreateParentIfNecessary(); 485 AddChildView(tab_table_parent_); 486 } else { 487 details.parent->RemoveChildView(kill_button_); 488 if (purge_memory_button_) 489 details.parent->RemoveChildView(purge_memory_button_); 490 details.parent->RemoveChildView(about_memory_link_); 491 } 492 } 493} 494 495void TaskManagerView::Layout() { 496 bool new_style = views::DialogDelegate::UseNewStyle(); 497 gfx::Size size = kill_button_->GetPreferredSize(); 498 gfx::Rect parent_bounds = parent()->GetContentsBounds(); 499 const int horizontal_margin = 500 new_style ? views::kButtonHEdgeMarginNew : views::kPanelHorizMargin; 501 const int vertical_margin = 502 new_style ? views::kButtonVEdgeMarginNew : views::kButtonVEdgeMargin; 503 int x = width() - size.width() - horizontal_margin; 504 int y_buttons = parent_bounds.bottom() - size.height() - vertical_margin; 505 kill_button_->SetBounds(x, y_buttons, size.width(), size.height()); 506 507 if (purge_memory_button_) { 508 size = purge_memory_button_->GetPreferredSize(); 509 purge_memory_button_->SetBounds( 510 kill_button_->x() - size.width() - 511 views::kUnrelatedControlHorizontalSpacing, 512 y_buttons, size.width(), size.height()); 513 } 514 515 size = about_memory_link_->GetPreferredSize(); 516 about_memory_link_->SetBounds( 517 horizontal_margin, 518 y_buttons + (kill_button_->height() - size.height()) / 2, 519 size.width(), size.height()); 520 521 gfx::Rect rect = GetLocalBounds(); 522 rect.Inset(horizontal_margin, views::kPanelVertMargin); 523 rect.Inset(0, 0, 0, 524 kill_button_->height() + views::kUnrelatedControlVerticalSpacing); 525 tab_table_parent_->SetBoundsRect(rect); 526} 527 528gfx::Size TaskManagerView::GetPreferredSize() { 529 return gfx::Size(460, 270); 530} 531 532// static 533void TaskManagerView::Show(bool highlight_background_resources, 534 Browser* browser) { 535#if defined(OS_WIN) 536 // In Windows Metro it's not good to open this native window. 537 DCHECK(!win8::IsSingleWindowMetroMode()); 538#endif 539 // In ash we can come here through the ChromeShellDelegate. If there is no 540 // browser window at that time of the call, browser could be passed as NULL. 541 const chrome::HostDesktopType desktop_type = 542 browser ? browser->host_desktop_type() : chrome::HOST_DESKTOP_TYPE_ASH; 543 544 if (instance_) { 545 if (instance_->highlight_background_resources_ != 546 highlight_background_resources || 547 instance_->desktop_type_ != desktop_type) { 548 instance_->GetWidget()->Close(); 549 } else { 550 // If there's a Task manager window open already, just activate it. 551 instance_->GetWidget()->Activate(); 552 return; 553 } 554 } 555 instance_ = new TaskManagerView(highlight_background_resources, desktop_type); 556 gfx::NativeWindow window = 557 browser ? browser->window()->GetNativeWindow() : NULL; 558#if defined(USE_ASH) 559 if (!window) 560 window = ash::wm::GetActiveWindow(); 561#endif 562 DialogDelegate::CreateDialogWidget(instance_, window, NULL); 563 instance_->InitAlwaysOnTopState(); 564 instance_->model_->StartUpdating(); 565 instance_->GetWidget()->Show(); 566 567 // Set the initial focus to the list of tasks. 568 views::FocusManager* focus_manager = instance_->GetFocusManager(); 569 if (focus_manager) 570 focus_manager->SetFocusedView(instance_->tab_table_); 571} 572 573// ButtonListener implementation. 574void TaskManagerView::ButtonPressed( 575 views::Button* sender, 576 const ui::Event& event) { 577 if (purge_memory_button_ && (sender == purge_memory_button_)) { 578 MemoryPurger::PurgeAll(); 579 } else { 580 typedef ui::ListSelectionModel::SelectedIndices SelectedIndices; 581 DCHECK_EQ(kill_button_, sender); 582 SelectedIndices selection(tab_table_->selection_model().selected_indices()); 583 for (SelectedIndices::const_reverse_iterator i = selection.rbegin(); 584 i != selection.rend(); ++i) { 585 task_manager_->KillProcess(*i); 586 } 587 } 588} 589 590// DialogDelegate implementation. 591bool TaskManagerView::CanResize() const { 592 return true; 593} 594 595bool TaskManagerView::CanMaximize() const { 596 return true; 597} 598 599bool TaskManagerView::ExecuteWindowsCommand(int command_id) { 600#if defined(OS_WIN) && !defined(USE_AURA) 601 if (command_id == IDC_ALWAYS_ON_TOP) { 602 is_always_on_top_ = !is_always_on_top_; 603 604 // Change the menu check state. 605 HMENU system_menu = GetSystemMenu(GetWidget()->GetNativeWindow(), FALSE); 606 MENUITEMINFO menu_info; 607 memset(&menu_info, 0, sizeof(MENUITEMINFO)); 608 menu_info.cbSize = sizeof(MENUITEMINFO); 609 BOOL r = GetMenuItemInfo(system_menu, IDC_ALWAYS_ON_TOP, 610 FALSE, &menu_info); 611 DCHECK(r); 612 menu_info.fMask = MIIM_STATE; 613 if (is_always_on_top_) 614 menu_info.fState = MFS_CHECKED; 615 r = SetMenuItemInfo(system_menu, IDC_ALWAYS_ON_TOP, FALSE, &menu_info); 616 617 // Now change the actual window's behavior. 618 GetWidget()->SetAlwaysOnTop(is_always_on_top_); 619 620 // Save the state. 621 if (g_browser_process->local_state()) { 622 DictionaryPrefUpdate update(g_browser_process->local_state(), 623 GetWindowName().c_str()); 624 DictionaryValue* window_preferences = update.Get(); 625 window_preferences->SetBoolean("always_on_top", is_always_on_top_); 626 } 627 return true; 628 } 629#endif 630 return false; 631} 632 633string16 TaskManagerView::GetWindowTitle() const { 634 return l10n_util::GetStringUTF16(IDS_TASK_MANAGER_TITLE); 635} 636 637std::string TaskManagerView::GetWindowName() const { 638 return prefs::kTaskManagerWindowPlacement; 639} 640 641int TaskManagerView::GetDialogButtons() const { 642 return ui::DIALOG_BUTTON_NONE; 643} 644 645void TaskManagerView::WindowClosing() { 646 // Now that the window is closed, we can allow a new one to be opened. 647 // (WindowClosing comes in asynchronously from the call to Close() and we 648 // may have already opened a new instance). 649 if (instance_ == this) 650 instance_ = NULL; 651 task_manager_->OnWindowClosed(); 652} 653 654// views::TableViewObserver implementation. 655void TaskManagerView::OnSelectionChanged() { 656 const ui::ListSelectionModel::SelectedIndices& selection( 657 tab_table_->selection_model().selected_indices()); 658 bool selection_contains_browser_process = false; 659 for (size_t i = 0; i < selection.size(); ++i) { 660 if (task_manager_->IsBrowserProcess(selection[i])) { 661 selection_contains_browser_process = true; 662 break; 663 } 664 } 665 kill_button_->SetEnabled(!selection_contains_browser_process && 666 !selection.empty()); 667} 668 669void TaskManagerView::OnDoubleClick() { 670 ActivateFocusedTab(); 671} 672 673void TaskManagerView::OnKeyDown(ui::KeyboardCode keycode) { 674 if (keycode == ui::VKEY_RETURN) 675 ActivateFocusedTab(); 676} 677 678void TaskManagerView::LinkClicked(views::Link* source, int event_flags) { 679 DCHECK_EQ(about_memory_link_, source); 680 task_manager_->OpenAboutMemory(desktop_type_); 681} 682 683void TaskManagerView::ShowContextMenuForView(views::View* source, 684 const gfx::Point& point) { 685 UpdateStatsCounters(); 686 ui::SimpleMenuModel menu_model(this); 687 for (std::vector<ui::TableColumn>::iterator i(columns_.begin()); 688 i != columns_.end(); ++i) { 689 menu_model.AddCheckItem(i->id, l10n_util::GetStringUTF16(i->id)); 690 } 691 menu_runner_.reset(new views::MenuRunner(&menu_model)); 692 if (menu_runner_->RunMenuAt(GetWidget(), NULL, gfx::Rect(point, gfx::Size()), 693 views::MenuItemView::TOPLEFT, 694 views::MenuRunner::CONTEXT_MENU) == 695 views::MenuRunner::MENU_DELETED) 696 return; 697} 698 699bool TaskManagerView::IsCommandIdChecked(int id) const { 700 return tab_table_->IsColumnVisible(id); 701} 702 703bool TaskManagerView::IsCommandIdEnabled(int id) const { 704 return true; 705} 706 707bool TaskManagerView::GetAcceleratorForCommandId( 708 int command_id, 709 ui::Accelerator* accelerator) { 710 return false; 711} 712 713void TaskManagerView::ExecuteCommand(int id, int event_flags) { 714 tab_table_->SetColumnVisibility(id, !tab_table_->IsColumnVisible(id)); 715} 716 717void TaskManagerView::InitAlwaysOnTopState() { 718 is_always_on_top_ = false; 719 if (GetSavedAlwaysOnTopState(&is_always_on_top_)) 720 GetWidget()->SetAlwaysOnTop(is_always_on_top_); 721 AddAlwaysOnTopSystemMenuItem(); 722} 723 724void TaskManagerView::ActivateFocusedTab() { 725 const int active_row = tab_table_->selection_model().active(); 726 if (active_row != -1) 727 task_manager_->ActivateProcess(active_row); 728} 729 730void TaskManagerView::AddAlwaysOnTopSystemMenuItem() { 731#if defined(OS_WIN) && !defined(USE_AURA) 732 // The Win32 API requires that we own the text. 733 always_on_top_menu_text_ = l10n_util::GetStringUTF16(IDS_ALWAYS_ON_TOP); 734 735 // Let's insert a menu to the window. 736 HMENU system_menu = ::GetSystemMenu(GetWidget()->GetNativeWindow(), FALSE); 737 int index = ::GetMenuItemCount(system_menu) - 1; 738 if (index < 0) { 739 // Paranoia check. 740 NOTREACHED(); 741 index = 0; 742 } 743 // First we add the separator. 744 MENUITEMINFO menu_info; 745 memset(&menu_info, 0, sizeof(MENUITEMINFO)); 746 menu_info.cbSize = sizeof(MENUITEMINFO); 747 menu_info.fMask = MIIM_FTYPE; 748 menu_info.fType = MFT_SEPARATOR; 749 ::InsertMenuItem(system_menu, index, TRUE, &menu_info); 750 751 // Then the actual menu. 752 menu_info.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING | MIIM_STATE; 753 menu_info.fType = MFT_STRING; 754 menu_info.fState = MFS_ENABLED; 755 if (is_always_on_top_) 756 menu_info.fState |= MFS_CHECKED; 757 menu_info.wID = IDC_ALWAYS_ON_TOP; 758 menu_info.dwTypeData = const_cast<wchar_t*>(always_on_top_menu_text_.c_str()); 759 ::InsertMenuItem(system_menu, index, TRUE, &menu_info); 760#endif 761} 762 763bool TaskManagerView::GetSavedAlwaysOnTopState(bool* always_on_top) const { 764 if (!g_browser_process->local_state()) 765 return false; 766 767 const DictionaryValue* dictionary = 768 g_browser_process->local_state()->GetDictionary(GetWindowName().c_str()); 769 return dictionary && 770 dictionary->GetBoolean("always_on_top", always_on_top) && always_on_top; 771} 772 773} // namespace 774 775namespace chrome { 776 777// Declared in browser_dialogs.h so others don't need to depend on our header. 778void ShowTaskManager(Browser* browser, bool highlight_background_resources) { 779 TaskManagerView::Show(highlight_background_resources, browser); 780} 781 782} // namespace chrome 783