Home | History | Annotate | Download | only in views
      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:
     55   explicit TaskManagerTableModel(TaskManagerModel* model)
     56       : model_(model),
     57         observer_(NULL) {
     58     model_->AddObserver(this);
     59   }
     60 
     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 
     87 int TaskManagerTableModel::RowCount() {
     88   return model_->ResourceCount();
     89 }
     90 
     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 
    158 SkBitmap TaskManagerTableModel::GetIcon(int row) {
    159   return model_->GetResourceIcon(row);
    160 }
    161 
    162 
    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 
    170 void TaskManagerTableModel::SetObserver(ui::TableModelObserver* observer) {
    171   observer_ = observer;
    172 }
    173 
    174 int TaskManagerTableModel::CompareValues(int row1, int row2, int column_id) {
    175   return model_->CompareValues(row1, row2, column_id);
    176 }
    177 
    178 void TaskManagerTableModel::OnModelChanged() {
    179   if (observer_)
    180     observer_->OnModelChanged();
    181 }
    182 
    183 void TaskManagerTableModel::OnItemsChanged(int start, int length) {
    184   if (observer_)
    185     observer_->OnItemsChanged(start, length);
    186 }
    187 
    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 
    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 
    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:
    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 
    234   virtual ~BackgroundColorGroupTableView() {}
    235 
    236  private:
    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 
    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 
    369 TaskManagerView::~TaskManagerView() {
    370   // Delete child views now, while our table model still exists.
    371   RemoveAllChildViews(true);
    372 }
    373 
    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 
    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 
    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 
    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 
    554 gfx::Size TaskManagerView::GetPreferredSize() {
    555   return gfx::Size(kDefaultWidth, kDefaultHeight);
    556 }
    557 
    558 // static
    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.
    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.
    596 bool TaskManagerView::CanResize() const {
    597   return true;
    598 }
    599 
    600 bool TaskManagerView::CanMaximize() const {
    601   return true;
    602 }
    603 
    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 
    636 std::wstring TaskManagerView::GetWindowTitle() const {
    637   return UTF16ToWide(l10n_util::GetStringUTF16(IDS_TASK_MANAGER_TITLE));
    638 }
    639 
    640 std::wstring TaskManagerView::GetWindowName() const {
    641   return UTF8ToWide(prefs::kTaskManagerWindowPlacement);
    642 }
    643 
    644 int TaskManagerView::GetDialogButtons() const {
    645   return MessageBoxFlags::DIALOGBUTTON_NONE;
    646 }
    647 
    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 
    657 views::View* TaskManagerView::GetContentsView() {
    658   return this;
    659 }
    660 
    661 // views::TableViewObserver implementation.
    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 
    675 void TaskManagerView::OnDoubleClick() {
    676   ActivateFocusedTab();
    677 }
    678 
    679 void TaskManagerView::OnKeyDown(ui::KeyboardCode keycode) {
    680   if (keycode == ui::VKEY_RETURN)
    681     ActivateFocusedTab();
    682 }
    683 
    684 // views::LinkController implementation
    685 void TaskManagerView::LinkActivated(views::Link* source, int event_flags) {
    686   DCHECK(source == about_memory_link_);
    687   task_manager_->OpenAboutMemory();
    688 }
    689 
    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 
    704 bool TaskManagerView::IsItemChecked(int id) const {
    705   return tab_table_->IsColumnVisible(id);
    706 }
    707 
    708 void TaskManagerView::ExecuteCommand(int id) {
    709   tab_table_->SetColumnVisibility(id, !tab_table_->IsColumnVisible(id));
    710 }
    711 
    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 
    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 
    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 
    761 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.
    777 void ShowTaskManager() {
    778   TaskManagerView::Show(false);
    779 }
    780 
    781 void ShowBackgroundPages() {
    782   TaskManagerView::Show(true);
    783 }
    784 
    785 }  // namespace browser
    786