Disable TabDragController tests that fail with a real compositor.
[chromium-blink-merge.git] / chrome / browser / ui / gtk / task_manager_gtk.cc
blobb1ab64e2c86bd8b5a7b14b99b10a8df9ccb6cb96
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.
5 #include "chrome/browser/ui/gtk/task_manager_gtk.h"
7 #include <gdk/gdkkeysyms.h>
9 #include <algorithm>
10 #include <set>
11 #include <utility>
12 #include <vector>
14 #include "base/auto_reset.h"
15 #include "base/command_line.h"
16 #include "base/logging.h"
17 #include "base/prefs/pref_service.h"
18 #include "base/prefs/scoped_user_pref_update.h"
19 #include "base/strings/utf_string_conversions.h"
20 #include "chrome/browser/browser_process.h"
21 #include "chrome/browser/defaults.h"
22 #include "chrome/browser/memory_purger.h"
23 #include "chrome/browser/ui/gtk/gtk_chrome_link_button.h"
24 #include "chrome/browser/ui/gtk/gtk_theme_service.h"
25 #include "chrome/browser/ui/gtk/gtk_theme_service.h"
26 #include "chrome/browser/ui/gtk/gtk_tree.h"
27 #include "chrome/browser/ui/gtk/gtk_util.h"
28 #include "chrome/browser/ui/gtk/menu_gtk.h"
29 #include "chrome/browser/ui/host_desktop.h"
30 #include "chrome/common/chrome_switches.h"
31 #include "chrome/common/pref_names.h"
32 #include "grit/chromium_strings.h"
33 #include "grit/ui_resources.h"
34 #include "third_party/skia/include/core/SkBitmap.h"
35 #include "ui/base/gtk/gtk_hig_constants.h"
36 #include "ui/base/l10n/l10n_util.h"
37 #include "ui/base/models/simple_menu_model.h"
38 #include "ui/base/resource/resource_bundle.h"
39 #include "ui/gfx/gtk_util.h"
40 #include "ui/gfx/image/image.h"
42 namespace {
44 // The task manager window default size.
45 const int kDefaultWidth = 460;
46 const int kDefaultHeight = 270;
48 // The resource id for the 'End process' button.
49 const gint kTaskManagerResponseKill = 1;
51 // The resource id for the 'Stats for nerds' link button.
52 const gint kTaskManagerAboutMemoryLink = 2;
54 // The resource id for the 'Purge Memory' button
55 const gint kTaskManagerPurgeMemory = 3;
57 enum TaskManagerColumn {
58 kTaskManagerIcon,
59 kTaskManagerTask,
60 kTaskManagerProfileName,
61 kTaskManagerSharedMem,
62 kTaskManagerPrivateMem,
63 kTaskManagerCPU,
64 kTaskManagerNetwork,
65 kTaskManagerProcessID,
66 kTaskManagerJavaScriptMemory,
67 kTaskManagerWebCoreImageCache,
68 kTaskManagerWebCoreScriptsCache,
69 kTaskManagerWebCoreCssCache,
70 kTaskManagerVideoMemory,
71 kTaskManagerFPS,
72 kTaskManagerSqliteMemoryUsed,
73 kTaskManagerNaClDebugStubPort,
74 kTaskManagerGoatsTeleported,
75 kTaskManagerColumnCount,
78 const TaskManagerColumn kTaskManagerLastVisibleColumn =
79 kTaskManagerGoatsTeleported;
81 TaskManagerColumn TaskManagerResourceIDToColumnID(int id) {
82 switch (id) {
83 case IDS_TASK_MANAGER_TASK_COLUMN:
84 return kTaskManagerTask;
85 case IDS_TASK_MANAGER_PROFILE_NAME_COLUMN:
86 return kTaskManagerProfileName;
87 case IDS_TASK_MANAGER_SHARED_MEM_COLUMN:
88 return kTaskManagerSharedMem;
89 case IDS_TASK_MANAGER_PRIVATE_MEM_COLUMN:
90 return kTaskManagerPrivateMem;
91 case IDS_TASK_MANAGER_CPU_COLUMN:
92 return kTaskManagerCPU;
93 case IDS_TASK_MANAGER_NET_COLUMN:
94 return kTaskManagerNetwork;
95 case IDS_TASK_MANAGER_PROCESS_ID_COLUMN:
96 return kTaskManagerProcessID;
97 case IDS_TASK_MANAGER_JAVASCRIPT_MEMORY_ALLOCATED_COLUMN:
98 return kTaskManagerJavaScriptMemory;
99 case IDS_TASK_MANAGER_WEBCORE_IMAGE_CACHE_COLUMN:
100 return kTaskManagerWebCoreImageCache;
101 case IDS_TASK_MANAGER_WEBCORE_SCRIPTS_CACHE_COLUMN:
102 return kTaskManagerWebCoreScriptsCache;
103 case IDS_TASK_MANAGER_WEBCORE_CSS_CACHE_COLUMN:
104 return kTaskManagerWebCoreCssCache;
105 case IDS_TASK_MANAGER_VIDEO_MEMORY_COLUMN:
106 return kTaskManagerVideoMemory;
107 case IDS_TASK_MANAGER_FPS_COLUMN:
108 return kTaskManagerFPS;
109 case IDS_TASK_MANAGER_SQLITE_MEMORY_USED_COLUMN:
110 return kTaskManagerSqliteMemoryUsed;
111 case IDS_TASK_MANAGER_NACL_DEBUG_STUB_PORT_COLUMN:
112 return kTaskManagerNaClDebugStubPort;
113 case IDS_TASK_MANAGER_GOATS_TELEPORTED_COLUMN:
114 return kTaskManagerGoatsTeleported;
115 default:
116 NOTREACHED();
117 return static_cast<TaskManagerColumn>(-1);
121 int TaskManagerColumnIDToResourceID(int id) {
122 switch (id) {
123 case kTaskManagerTask:
124 return IDS_TASK_MANAGER_TASK_COLUMN;
125 case kTaskManagerProfileName:
126 return IDS_TASK_MANAGER_PROFILE_NAME_COLUMN;
127 case kTaskManagerSharedMem:
128 return IDS_TASK_MANAGER_SHARED_MEM_COLUMN;
129 case kTaskManagerPrivateMem:
130 return IDS_TASK_MANAGER_PRIVATE_MEM_COLUMN;
131 case kTaskManagerCPU:
132 return IDS_TASK_MANAGER_CPU_COLUMN;
133 case kTaskManagerNetwork:
134 return IDS_TASK_MANAGER_NET_COLUMN;
135 case kTaskManagerProcessID:
136 return IDS_TASK_MANAGER_PROCESS_ID_COLUMN;
137 case kTaskManagerJavaScriptMemory:
138 return IDS_TASK_MANAGER_JAVASCRIPT_MEMORY_ALLOCATED_COLUMN;
139 case kTaskManagerWebCoreImageCache:
140 return IDS_TASK_MANAGER_WEBCORE_IMAGE_CACHE_COLUMN;
141 case kTaskManagerWebCoreScriptsCache:
142 return IDS_TASK_MANAGER_WEBCORE_SCRIPTS_CACHE_COLUMN;
143 case kTaskManagerWebCoreCssCache:
144 return IDS_TASK_MANAGER_WEBCORE_CSS_CACHE_COLUMN;
145 case kTaskManagerVideoMemory:
146 return IDS_TASK_MANAGER_VIDEO_MEMORY_COLUMN;
147 case kTaskManagerFPS:
148 return IDS_TASK_MANAGER_FPS_COLUMN;
149 case kTaskManagerSqliteMemoryUsed:
150 return IDS_TASK_MANAGER_SQLITE_MEMORY_USED_COLUMN;
151 case kTaskManagerNaClDebugStubPort:
152 return IDS_TASK_MANAGER_NACL_DEBUG_STUB_PORT_COLUMN;
153 case kTaskManagerGoatsTeleported:
154 return IDS_TASK_MANAGER_GOATS_TELEPORTED_COLUMN;
155 default:
156 NOTREACHED();
157 return -1;
161 // Should be used for all gtk_tree_view functions that require a column index on
162 // input.
164 // We need colid - 1 because the gtk_tree_view function is asking for the
165 // column index, not the column id, and both kTaskManagerIcon and
166 // kTaskManagerTask are in the same column index, so all column IDs are off by
167 // one.
168 int TreeViewColumnIndexFromID(TaskManagerColumn colid) {
169 return colid - 1;
172 // Shows or hides a treeview column.
173 void TreeViewColumnSetVisible(GtkWidget* treeview, TaskManagerColumn colid,
174 bool visible) {
175 GtkTreeViewColumn* column = gtk_tree_view_get_column(
176 GTK_TREE_VIEW(treeview), TreeViewColumnIndexFromID(colid));
177 gtk_tree_view_column_set_visible(column, visible);
180 bool TreeViewColumnIsVisible(GtkWidget* treeview, TaskManagerColumn colid) {
181 GtkTreeViewColumn* column = gtk_tree_view_get_column(
182 GTK_TREE_VIEW(treeview), TreeViewColumnIndexFromID(colid));
183 return gtk_tree_view_column_get_visible(column);
186 // The task column is special because it has an icon and it gets special
187 // treatment with respect to resizing the columns.
188 void TreeViewInsertTaskColumn(GtkWidget* treeview, int resid) {
189 int colid = TaskManagerResourceIDToColumnID(resid);
190 GtkTreeViewColumn* column = gtk_tree_view_column_new();
191 gtk_tree_view_column_set_title(column,
192 l10n_util::GetStringUTF8(resid).c_str());
193 gtk_tree_view_set_tooltip_column(GTK_TREE_VIEW(treeview), colid);
194 GtkCellRenderer* image_renderer = gtk_cell_renderer_pixbuf_new();
195 gtk_tree_view_column_pack_start(column, image_renderer, FALSE);
196 gtk_tree_view_column_add_attribute(column, image_renderer,
197 "pixbuf", kTaskManagerIcon);
198 GtkCellRenderer* text_renderer = gtk_cell_renderer_text_new();
199 gtk_tree_view_column_pack_start(column, text_renderer, TRUE);
200 gtk_tree_view_column_add_attribute(column, text_renderer, "markup", colid);
201 gtk_tree_view_column_set_resizable(column, TRUE);
202 // This is temporary: we'll turn expanding off after getting the size.
203 gtk_tree_view_column_set_expand(column, TRUE);
204 gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);
205 gtk_tree_view_column_set_sort_column_id(column, colid);
208 // Inserts a column with a column id of |colid| and |name|.
209 void TreeViewInsertColumnWithName(GtkWidget* treeview,
210 TaskManagerColumn colid, const char* name) {
211 GtkCellRenderer* renderer = gtk_cell_renderer_text_new();
212 gtk_tree_view_insert_column_with_attributes(
213 GTK_TREE_VIEW(treeview), -1,
214 name, renderer,
215 "text", colid,
216 NULL);
217 GtkTreeViewColumn* column = gtk_tree_view_get_column(
218 GTK_TREE_VIEW(treeview), TreeViewColumnIndexFromID(colid));
219 gtk_tree_view_column_set_resizable(column, TRUE);
220 gtk_tree_view_column_set_sort_column_id(column, colid);
223 // Loads the column name from |resid| and uses the corresponding
224 // TaskManagerColumn value as the column id to insert into the treeview.
225 void TreeViewInsertColumn(GtkWidget* treeview, int resid) {
226 TreeViewInsertColumnWithName(treeview, TaskManagerResourceIDToColumnID(resid),
227 l10n_util::GetStringUTF8(resid).c_str());
230 // Set the current width of the column without forcing a fixed or maximum
231 // width as gtk_tree_view_column_set_[fixed|maximum]_width() would. This would
232 // basically be gtk_tree_view_column_set_width() except that there is no such
233 // function. It turns out that other applications have done similar hacks to do
234 // the same thing - search the web for that nonexistent function name! :)
235 void TreeViewColumnSetWidth(GtkTreeViewColumn* column, gint width) {
236 column->width = width;
237 column->resized_width = width;
238 column->use_resized_width = TRUE;
239 // Needed for use_resized_width to be effective.
240 gtk_widget_queue_resize(column->tree_view);
243 } // namespace
245 class TaskManagerGtk::ContextMenuController
246 : public ui::SimpleMenuModel::Delegate {
247 public:
248 explicit ContextMenuController(TaskManagerGtk* task_manager)
249 : task_manager_(task_manager) {
250 menu_model_.reset(new ui::SimpleMenuModel(this));
251 for (int i = kTaskManagerTask; i <= kTaskManagerLastVisibleColumn; i++) {
252 menu_model_->AddCheckItemWithStringId(
253 i, TaskManagerColumnIDToResourceID(i));
255 menu_.reset(new MenuGtk(NULL, menu_model_.get()));
258 virtual ~ContextMenuController() {}
260 void RunMenu(const gfx::Point& point, guint32 event_time) {
261 menu_->PopupAsContext(point, event_time);
264 void Cancel() {
265 task_manager_ = NULL;
266 menu_->Cancel();
269 private:
270 // ui::SimpleMenuModel::Delegate implementation:
271 virtual bool IsCommandIdEnabled(int command_id) const OVERRIDE {
272 if (!task_manager_)
273 return false;
275 return true;
278 virtual bool IsCommandIdChecked(int command_id) const OVERRIDE {
279 if (!task_manager_)
280 return false;
282 TaskManagerColumn colid = static_cast<TaskManagerColumn>(command_id);
283 return TreeViewColumnIsVisible(task_manager_->treeview_, colid);
286 virtual bool GetAcceleratorForCommandId(
287 int command_id,
288 ui::Accelerator* accelerator) OVERRIDE {
289 return false;
292 virtual void ExecuteCommand(int command_id, int event_flags) OVERRIDE {
293 if (!task_manager_)
294 return;
296 TaskManagerColumn colid = static_cast<TaskManagerColumn>(command_id);
297 bool visible = !TreeViewColumnIsVisible(task_manager_->treeview_, colid);
298 TreeViewColumnSetVisible(task_manager_->treeview_, colid, visible);
301 // The model and view for the right click context menu.
302 scoped_ptr<ui::SimpleMenuModel> menu_model_;
303 scoped_ptr<MenuGtk> menu_;
305 // The TaskManager the context menu was brought up for. Set to NULL when the
306 // menu is canceled.
307 TaskManagerGtk* task_manager_;
309 DISALLOW_COPY_AND_ASSIGN(ContextMenuController);
312 TaskManagerGtk::TaskManagerGtk()
313 : task_manager_(TaskManager::GetInstance()),
314 model_(TaskManager::GetInstance()->model()),
315 dialog_(NULL),
316 treeview_(NULL),
317 process_list_(NULL),
318 process_count_(0),
319 ignore_selection_changed_(false) {
320 Init();
323 // static
324 TaskManagerGtk* TaskManagerGtk::instance_ = NULL;
326 TaskManagerGtk::~TaskManagerGtk() {
327 model_->RemoveObserver(this);
328 task_manager_->OnWindowClosed();
330 gtk_accel_group_disconnect_key(accel_group_, GDK_w, GDK_CONTROL_MASK);
331 gtk_window_remove_accel_group(GTK_WINDOW(dialog_), accel_group_);
332 g_object_unref(accel_group_);
333 accel_group_ = NULL;
335 // Disconnect the destroy signal so it doesn't delete |this|.
336 g_signal_handler_disconnect(G_OBJECT(dialog_), destroy_handler_id_);
337 gtk_widget_destroy(dialog_);
340 ////////////////////////////////////////////////////////////////////////////////
341 // TaskManagerGtk, TaskManagerModelObserver implementation:
343 void TaskManagerGtk::OnModelChanged() {
344 // Nothing to do.
347 void TaskManagerGtk::OnItemsChanged(int start, int length) {
348 GtkTreeIter iter;
349 if (!gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(process_list_), &iter,
350 NULL, start)) {
351 NOTREACHED() << "Can't get child " << start <<
352 " from GTK_TREE_MODEL(process_list_)";
355 for (int i = start; i < start + length; i++) {
356 SetRowDataFromModel(i, &iter);
357 if (i != start + length - 1) {
358 if (!gtk_tree_model_iter_next(GTK_TREE_MODEL(process_list_), &iter)) {
359 NOTREACHED() << "Can't get next GtkTreeIter object from process_list_ "
360 "iterator at position " << i;
366 void TaskManagerGtk::OnItemsAdded(int start, int length) {
367 base::AutoReset<bool> autoreset(&ignore_selection_changed_, true);
369 GtkTreeIter iter;
370 if (start == 0) {
371 gtk_list_store_prepend(process_list_, &iter);
372 } else if (start >= process_count_) {
373 gtk_list_store_append(process_list_, &iter);
374 } else {
375 GtkTreeIter sibling;
376 gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(process_list_), &sibling,
377 NULL, start);
378 gtk_list_store_insert_before(process_list_, &iter, &sibling);
381 SetRowDataFromModel(start, &iter);
383 for (int i = start + 1; i < start + length; i++) {
384 gtk_list_store_insert_after(process_list_, &iter, &iter);
385 SetRowDataFromModel(i, &iter);
388 process_count_ += length;
391 void TaskManagerGtk::OnItemsRemoved(int start, int length) {
393 base::AutoReset<bool> autoreset(&ignore_selection_changed_, true);
395 GtkTreeIter iter;
396 gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(process_list_), &iter,
397 NULL, start);
399 for (int i = 0; i < length; i++) {
400 // |iter| is moved to the next valid node when the current node is
401 // removed.
402 gtk_list_store_remove(process_list_, &iter);
405 process_count_ -= length;
408 // It is possible that we have removed the current selection; run selection
409 // changed to detect that case.
410 OnSelectionChanged(gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview_)));
413 ////////////////////////////////////////////////////////////////////////////////
414 // TaskManagerGtk, public:
416 void TaskManagerGtk::Close() {
417 // Blow away our dialog - this will cause TaskManagerGtk to free itself.
418 gtk_widget_destroy(dialog_);
419 DCHECK(!instance_);
422 // static
423 void TaskManagerGtk::Show() {
424 if (instance_) {
425 // If there's a Task manager window open already, just activate it.
426 gtk_util::PresentWindow(instance_->dialog_, 0);
427 } else {
428 instance_ = new TaskManagerGtk();
429 instance_->model_->StartUpdating();
433 ////////////////////////////////////////////////////////////////////////////////
434 // TaskManagerGtk, private:
436 void TaskManagerGtk::Init() {
437 dialog_ = gtk_dialog_new_with_buttons(
438 l10n_util::GetStringUTF8(IDS_TASK_MANAGER_TITLE).c_str(),
439 // Task Manager window is shared between all browsers.
440 NULL,
441 GTK_DIALOG_NO_SEPARATOR,
442 NULL);
444 // Allow browser windows to go in front of the task manager dialog in
445 // metacity.
446 gtk_window_set_type_hint(GTK_WINDOW(dialog_), GDK_WINDOW_TYPE_HINT_NORMAL);
448 if (CommandLine::ForCurrentProcess()->HasSwitch(
449 switches::kPurgeMemoryButton)) {
450 gtk_dialog_add_button(GTK_DIALOG(dialog_),
451 l10n_util::GetStringUTF8(IDS_TASK_MANAGER_PURGE_MEMORY).c_str(),
452 kTaskManagerPurgeMemory);
455 if (browser_defaults::kShowCancelButtonInTaskManager) {
456 gtk_dialog_add_button(GTK_DIALOG(dialog_),
457 l10n_util::GetStringUTF8(IDS_CLOSE).c_str(),
458 GTK_RESPONSE_DELETE_EVENT);
461 gtk_dialog_add_button(GTK_DIALOG(dialog_),
462 l10n_util::GetStringUTF8(IDS_TASK_MANAGER_KILL).c_str(),
463 kTaskManagerResponseKill);
465 // The response button should not be sensitive when the dialog is first opened
466 // because the selection is initially empty.
467 gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog_),
468 kTaskManagerResponseKill, FALSE);
470 GtkWidget* link = gtk_chrome_link_button_new(
471 l10n_util::GetStringUTF8(IDS_TASK_MANAGER_ABOUT_MEMORY_LINK).c_str());
472 gtk_dialog_add_action_widget(GTK_DIALOG(dialog_), link,
473 kTaskManagerAboutMemoryLink);
475 // Setting the link widget to secondary positions the button on the left side
476 // of the action area (vice versa for RTL layout).
477 GtkWidget* action_area = gtk_dialog_get_action_area(GTK_DIALOG(dialog_));
478 gtk_button_box_set_child_secondary(GTK_BUTTON_BOX(action_area), link, TRUE);
480 ConnectAccelerators();
482 GtkWidget* content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog_));
483 gtk_box_set_spacing(GTK_BOX(content_area), ui::kContentAreaSpacing);
485 destroy_handler_id_ = g_signal_connect(dialog_, "destroy",
486 G_CALLBACK(OnDestroyThunk), this);
487 g_signal_connect(dialog_, "response", G_CALLBACK(OnResponseThunk), this);
488 g_signal_connect(dialog_, "button-press-event",
489 G_CALLBACK(OnButtonEventThunk), this);
490 gtk_widget_add_events(dialog_,
491 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
493 // Wrap the treeview widget in a scrolled window in order to have a frame.
494 GtkWidget* scrolled = gtk_scrolled_window_new(NULL, NULL);
495 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled),
496 GTK_SHADOW_ETCHED_IN);
497 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
498 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
500 gtk_container_add(GTK_CONTAINER(content_area), scrolled);
502 CreateTaskManagerTreeview();
503 gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(treeview_), TRUE);
504 g_signal_connect(treeview_, "row-activated",
505 G_CALLBACK(OnRowActivatedThunk), this);
506 g_signal_connect(treeview_, "button-press-event",
507 G_CALLBACK(OnButtonEventThunk), this);
509 // |selection| is owned by |treeview_|.
510 GtkTreeSelection* selection = gtk_tree_view_get_selection(
511 GTK_TREE_VIEW(treeview_));
512 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
513 g_signal_connect(selection, "changed",
514 G_CALLBACK(OnSelectionChangedThunk), this);
516 gtk_container_add(GTK_CONTAINER(scrolled), treeview_);
518 SetInitialDialogSize();
519 gtk_util::ShowDialog(dialog_);
521 // If the model already has resources, we need to add them before we start
522 // observing events.
523 if (model_->ResourceCount() > 0)
524 OnItemsAdded(0, model_->ResourceCount());
526 model_->AddObserver(this);
529 void TaskManagerGtk::SetInitialDialogSize() {
530 // Hook up to the realize event so we can size the task column to the
531 // size of the leftover space after packing the other columns.
532 g_signal_connect(treeview_, "realize",
533 G_CALLBACK(OnTreeViewRealizeThunk), this);
534 // If we previously saved the dialog's bounds, use them.
535 if (g_browser_process->local_state()) {
536 const base::DictionaryValue* placement_pref =
537 g_browser_process->local_state()->GetDictionary(
538 prefs::kTaskManagerWindowPlacement);
539 int top = 0, left = 0, bottom = 1, right = 1;
540 if (placement_pref &&
541 placement_pref->GetInteger("top", &top) &&
542 placement_pref->GetInteger("left", &left) &&
543 placement_pref->GetInteger("bottom", &bottom) &&
544 placement_pref->GetInteger("right", &right)) {
545 gtk_window_resize(GTK_WINDOW(dialog_),
546 std::max(1, right - left),
547 std::max(1, bottom - top));
548 return;
552 // Otherwise, just set a default size (GTK will override this if it's not
553 // large enough to hold the window's contents).
554 gtk_window_set_default_size(
555 GTK_WINDOW(dialog_), kDefaultWidth, kDefaultHeight);
558 void TaskManagerGtk::ConnectAccelerators() {
559 accel_group_ = gtk_accel_group_new();
560 gtk_window_add_accel_group(GTK_WINDOW(dialog_), accel_group_);
562 gtk_accel_group_connect(accel_group_,
563 GDK_w, GDK_CONTROL_MASK, GtkAccelFlags(0),
564 g_cclosure_new(G_CALLBACK(OnGtkAcceleratorThunk),
565 this, NULL));
568 void TaskManagerGtk::CreateTaskManagerTreeview() {
569 process_list_ = gtk_list_store_new(kTaskManagerColumnCount,
570 GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
571 G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
572 G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
573 G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
575 // Support sorting on all columns.
576 process_list_sort_ = gtk_tree_model_sort_new_with_model(
577 GTK_TREE_MODEL(process_list_));
578 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(process_list_sort_),
579 kTaskManagerTask,
580 ComparePage, this, NULL);
581 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(process_list_sort_),
582 kTaskManagerTask,
583 CompareProfileName, this, NULL);
584 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(process_list_sort_),
585 kTaskManagerSharedMem,
586 CompareSharedMemory, this, NULL);
587 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(process_list_sort_),
588 kTaskManagerPrivateMem,
589 ComparePrivateMemory, this, NULL);
590 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(process_list_sort_),
591 kTaskManagerJavaScriptMemory,
592 CompareV8Memory, this, NULL);
593 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(process_list_sort_),
594 kTaskManagerCPU,
595 CompareCPU, this, NULL);
596 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(process_list_sort_),
597 kTaskManagerNetwork,
598 CompareNetwork, this, NULL);
599 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(process_list_sort_),
600 kTaskManagerProcessID,
601 CompareProcessID, this, NULL);
602 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(process_list_sort_),
603 kTaskManagerWebCoreImageCache,
604 CompareWebCoreImageCache, this, NULL);
605 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(process_list_sort_),
606 kTaskManagerWebCoreScriptsCache,
607 CompareWebCoreScriptsCache, this, NULL);
608 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(process_list_sort_),
609 kTaskManagerWebCoreCssCache,
610 CompareWebCoreCssCache, this, NULL);
611 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(process_list_sort_),
612 kTaskManagerVideoMemory,
613 CompareVideoMemory, this, NULL);
614 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(process_list_sort_),
615 kTaskManagerFPS,
616 CompareFPS, this, NULL);
617 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(process_list_sort_),
618 kTaskManagerSqliteMemoryUsed,
619 CompareSqliteMemoryUsed, this, NULL);
620 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(process_list_sort_),
621 kTaskManagerNaClDebugStubPort,
622 CompareNaClDebugStubPort, this, NULL);
623 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(process_list_sort_),
624 kTaskManagerGoatsTeleported,
625 CompareGoatsTeleported, this, NULL);
626 treeview_ = gtk_tree_view_new_with_model(process_list_sort_);
628 // Insert all the columns.
629 TreeViewInsertTaskColumn(treeview_, IDS_TASK_MANAGER_TASK_COLUMN);
630 TreeViewInsertColumn(treeview_, IDS_TASK_MANAGER_PROFILE_NAME_COLUMN);
631 TreeViewInsertColumn(treeview_, IDS_TASK_MANAGER_SHARED_MEM_COLUMN);
632 TreeViewInsertColumn(treeview_, IDS_TASK_MANAGER_PRIVATE_MEM_COLUMN);
633 TreeViewInsertColumn(treeview_, IDS_TASK_MANAGER_CPU_COLUMN);
634 TreeViewInsertColumn(treeview_, IDS_TASK_MANAGER_NET_COLUMN);
635 TreeViewInsertColumn(treeview_, IDS_TASK_MANAGER_PROCESS_ID_COLUMN);
636 TreeViewInsertColumn(treeview_,
637 IDS_TASK_MANAGER_JAVASCRIPT_MEMORY_ALLOCATED_COLUMN);
638 TreeViewInsertColumn(treeview_, IDS_TASK_MANAGER_WEBCORE_IMAGE_CACHE_COLUMN);
639 TreeViewInsertColumn(treeview_,
640 IDS_TASK_MANAGER_WEBCORE_SCRIPTS_CACHE_COLUMN);
641 TreeViewInsertColumn(treeview_, IDS_TASK_MANAGER_WEBCORE_CSS_CACHE_COLUMN);
642 TreeViewInsertColumn(treeview_, IDS_TASK_MANAGER_VIDEO_MEMORY_COLUMN);
643 TreeViewInsertColumn(treeview_, IDS_TASK_MANAGER_FPS_COLUMN);
644 TreeViewInsertColumn(treeview_, IDS_TASK_MANAGER_SQLITE_MEMORY_USED_COLUMN);
645 TreeViewInsertColumn(treeview_, IDS_TASK_MANAGER_NACL_DEBUG_STUB_PORT_COLUMN);
646 TreeViewInsertColumn(treeview_, IDS_TASK_MANAGER_GOATS_TELEPORTED_COLUMN);
648 // Hide some columns by default.
649 TreeViewColumnSetVisible(treeview_, kTaskManagerProfileName, false);
650 TreeViewColumnSetVisible(treeview_, kTaskManagerSharedMem, false);
651 TreeViewColumnSetVisible(treeview_, kTaskManagerJavaScriptMemory, false);
652 TreeViewColumnSetVisible(treeview_, kTaskManagerWebCoreImageCache, false);
653 TreeViewColumnSetVisible(treeview_, kTaskManagerWebCoreScriptsCache, false);
654 TreeViewColumnSetVisible(treeview_, kTaskManagerWebCoreCssCache, false);
655 TreeViewColumnSetVisible(treeview_, kTaskManagerVideoMemory, false);
656 TreeViewColumnSetVisible(treeview_, kTaskManagerSqliteMemoryUsed, false);
657 TreeViewColumnSetVisible(treeview_, kTaskManagerNaClDebugStubPort, false);
658 TreeViewColumnSetVisible(treeview_, kTaskManagerGoatsTeleported, false);
660 g_object_unref(process_list_);
661 g_object_unref(process_list_sort_);
664 std::string TaskManagerGtk::GetModelText(int row, int col_id) {
665 return base::UTF16ToUTF8(model_->GetResourceById(row, col_id));
668 GdkPixbuf* TaskManagerGtk::GetModelIcon(int row) {
669 SkBitmap icon = *model_->GetResourceIcon(row).bitmap();
670 if (icon.pixelRef() ==
671 ui::ResourceBundle::GetSharedInstance().GetImageNamed(
672 IDR_DEFAULT_FAVICON).AsBitmap().pixelRef()) {
673 return static_cast<GdkPixbuf*>(g_object_ref(
674 GtkThemeService::GetDefaultFavicon(true).ToGdkPixbuf()));
677 return gfx::GdkPixbufFromSkBitmap(icon);
680 void TaskManagerGtk::SetRowDataFromModel(int row, GtkTreeIter* iter) {
681 GdkPixbuf* icon = GetModelIcon(row);
682 std::string task = GetModelText(row, IDS_TASK_MANAGER_TASK_COLUMN);
683 std::string profile_name =
684 GetModelText(row, IDS_TASK_MANAGER_PROFILE_NAME_COLUMN);
685 gchar* task_markup = g_markup_escape_text(task.c_str(), task.length());
686 std::string shared_mem =
687 GetModelText(row, IDS_TASK_MANAGER_SHARED_MEM_COLUMN);
688 std::string priv_mem = GetModelText(row, IDS_TASK_MANAGER_PRIVATE_MEM_COLUMN);
689 std::string cpu = GetModelText(row, IDS_TASK_MANAGER_CPU_COLUMN);
690 std::string net = GetModelText(row, IDS_TASK_MANAGER_NET_COLUMN);
691 std::string procid = GetModelText(row, IDS_TASK_MANAGER_PROCESS_ID_COLUMN);
693 // Querying the renderer metrics is slow as it has to do IPC, so only do it
694 // when the columns are visible.
695 std::string javascript_memory;
696 if (TreeViewColumnIsVisible(treeview_, kTaskManagerJavaScriptMemory)) {
697 javascript_memory =
698 GetModelText(row, IDS_TASK_MANAGER_JAVASCRIPT_MEMORY_ALLOCATED_COLUMN);
700 std::string wk_img_cache;
701 if (TreeViewColumnIsVisible(treeview_, kTaskManagerWebCoreImageCache)) {
702 wk_img_cache =
703 GetModelText(row, IDS_TASK_MANAGER_WEBCORE_IMAGE_CACHE_COLUMN);
705 std::string wk_scripts_cache;
706 if (TreeViewColumnIsVisible(treeview_, kTaskManagerWebCoreScriptsCache)) {
707 wk_scripts_cache =
708 GetModelText(row, IDS_TASK_MANAGER_WEBCORE_SCRIPTS_CACHE_COLUMN);
710 std::string wk_css_cache;
711 if (TreeViewColumnIsVisible(treeview_, kTaskManagerWebCoreCssCache)) {
712 wk_css_cache =
713 GetModelText(row, IDS_TASK_MANAGER_WEBCORE_CSS_CACHE_COLUMN);
715 std::string video_memory;
716 if (TreeViewColumnIsVisible(treeview_, kTaskManagerVideoMemory))
717 video_memory = GetModelText(row, IDS_TASK_MANAGER_VIDEO_MEMORY_COLUMN);
718 std::string fps;
719 if (TreeViewColumnIsVisible(treeview_, kTaskManagerFPS))
720 fps = GetModelText(row, IDS_TASK_MANAGER_FPS_COLUMN);
721 std::string sqlite_memory;
722 if (TreeViewColumnIsVisible(treeview_, kTaskManagerSqliteMemoryUsed)) {
723 sqlite_memory =
724 GetModelText(row, IDS_TASK_MANAGER_SQLITE_MEMORY_USED_COLUMN);
726 std::string nacl_debug_stub_port;
727 if (TreeViewColumnIsVisible(treeview_, kTaskManagerNaClDebugStubPort)) {
728 nacl_debug_stub_port =
729 GetModelText(row, IDS_TASK_MANAGER_NACL_DEBUG_STUB_PORT_COLUMN);
732 std::string goats =
733 GetModelText(row, IDS_TASK_MANAGER_GOATS_TELEPORTED_COLUMN);
735 gtk_list_store_set(process_list_, iter,
736 kTaskManagerIcon, icon,
737 kTaskManagerTask, task_markup,
738 kTaskManagerProfileName, profile_name.c_str(),
739 kTaskManagerSharedMem, shared_mem.c_str(),
740 kTaskManagerPrivateMem, priv_mem.c_str(),
741 kTaskManagerCPU, cpu.c_str(),
742 kTaskManagerNetwork, net.c_str(),
743 kTaskManagerProcessID, procid.c_str(),
744 kTaskManagerJavaScriptMemory, javascript_memory.c_str(),
745 kTaskManagerWebCoreImageCache, wk_img_cache.c_str(),
746 kTaskManagerWebCoreScriptsCache, wk_scripts_cache.c_str(),
747 kTaskManagerWebCoreCssCache, wk_css_cache.c_str(),
748 kTaskManagerVideoMemory, video_memory.c_str(),
749 kTaskManagerFPS, fps.c_str(),
750 kTaskManagerSqliteMemoryUsed, sqlite_memory.c_str(),
751 kTaskManagerNaClDebugStubPort,
752 nacl_debug_stub_port.c_str(),
753 kTaskManagerGoatsTeleported, goats.c_str(),
754 -1);
755 g_object_unref(icon);
756 g_free(task_markup);
759 void TaskManagerGtk::KillSelectedProcesses() {
760 GtkTreeSelection* selection = gtk_tree_view_get_selection(
761 GTK_TREE_VIEW(treeview_));
763 GtkTreeModel* model;
764 GList* paths = gtk_tree_selection_get_selected_rows(selection, &model);
765 for (GList* item = paths; item; item = item->next) {
766 GtkTreePath* path = gtk_tree_model_sort_convert_path_to_child_path(
767 GTK_TREE_MODEL_SORT(process_list_sort_),
768 reinterpret_cast<GtkTreePath*>(item->data));
769 int row = gtk_tree::GetRowNumForPath(path);
770 gtk_tree_path_free(path);
771 task_manager_->KillProcess(row);
773 g_list_foreach(paths, reinterpret_cast<GFunc>(gtk_tree_path_free), NULL);
774 g_list_free(paths);
777 void TaskManagerGtk::ShowContextMenu(const gfx::Point& point,
778 guint32 event_time) {
779 if (!menu_controller_.get())
780 menu_controller_.reset(new ContextMenuController(this));
782 menu_controller_->RunMenu(point, event_time);
785 void TaskManagerGtk::OnLinkActivated() {
786 task_manager_->OpenAboutMemory(chrome::HOST_DESKTOP_TYPE_NATIVE);
789 gint TaskManagerGtk::CompareImpl(GtkTreeModel* model, GtkTreeIter* a,
790 GtkTreeIter* b, int id) {
791 int row1 = gtk_tree::GetRowNumForIter(model, b);
792 int row2 = gtk_tree::GetRowNumForIter(model, a);
794 // Otherwise, make sure grouped resources are shown together.
795 TaskManagerModel::GroupRange group_range1 =
796 model_->GetGroupRangeForResource(row1);
797 TaskManagerModel::GroupRange group_range2 =
798 model_->GetGroupRangeForResource(row2);
800 if (group_range1 == group_range2) {
801 // Sort within groups.
802 // We want the first-in-group row at the top, whether we are sorting up or
803 // down.
804 GtkSortType sort_type;
805 gtk_tree_sortable_get_sort_column_id(GTK_TREE_SORTABLE(process_list_sort_),
806 NULL, &sort_type);
807 if (row1 == group_range1.first)
808 return sort_type == GTK_SORT_ASCENDING ? -1 : 1;
809 if (row2 == group_range2.first)
810 return sort_type == GTK_SORT_ASCENDING ? 1 : -1;
812 return model_->CompareValues(row1, row2, id);
813 } else {
814 // Sort between groups.
815 // Compare by the first-in-group rows so that the groups will stay together.
816 return model_->CompareValues(group_range1.first, group_range2.first, id);
820 void TaskManagerGtk::OnDestroy(GtkWidget* dialog) {
821 instance_ = NULL;
822 delete this;
825 void TaskManagerGtk::OnResponse(GtkWidget* dialog, int response_id) {
826 if (response_id == GTK_RESPONSE_DELETE_EVENT) {
827 // Store the dialog's size so we can restore it the next time it's opened.
828 if (g_browser_process->local_state()) {
829 gfx::Rect dialog_bounds = gtk_util::GetDialogBounds(GTK_WIDGET(dialog));
831 DictionaryPrefUpdate update(g_browser_process->local_state(),
832 prefs::kTaskManagerWindowPlacement);
833 base::DictionaryValue* placement_pref = update.Get();
834 // Note that we store left/top for consistency with Windows, but that we
835 // *don't* restore them.
836 placement_pref->SetInteger("left", dialog_bounds.x());
837 placement_pref->SetInteger("top", dialog_bounds.y());
838 placement_pref->SetInteger("right", dialog_bounds.right());
839 placement_pref->SetInteger("bottom", dialog_bounds.bottom());
840 placement_pref->SetBoolean("maximized", false);
843 instance_ = NULL;
844 delete this;
845 } else if (response_id == kTaskManagerResponseKill) {
846 KillSelectedProcesses();
847 } else if (response_id == kTaskManagerAboutMemoryLink) {
848 OnLinkActivated();
849 } else if (response_id == kTaskManagerPurgeMemory) {
850 MemoryPurger::PurgeAll();
854 void TaskManagerGtk::OnTreeViewRealize(GtkTreeView* treeview) {
855 // Five columns show by default: the task column, the memory column, the CPU
856 // column, the network column, and the FPS column. Initially we set the task
857 // tolumn to take all the extra space, with the other columns being sized to
858 // fit the column names. Here we turn off the expand property of the first
859 // column (to make the table behave sanely when the user resizes it), and set
860 // the effective sizes of all five default columns to the automatically chosen
861 // sizes before any rows are added. This causes them to stay at those sizes
862 // even if the data would overflow, preventing a horizontal scroll bar from
863 // appearing due to the row data.
864 static const TaskManagerColumn dfl_columns[] = {kTaskManagerPrivateMem,
865 kTaskManagerCPU,
866 kTaskManagerNetwork,
867 kTaskManagerFPS};
868 GtkTreeViewColumn* column = NULL;
869 gint width;
870 for (size_t i = 0; i < arraysize(dfl_columns); ++i) {
871 column = gtk_tree_view_get_column(treeview,
872 TreeViewColumnIndexFromID(dfl_columns[i]));
873 width = gtk_tree_view_column_get_width(column);
874 TreeViewColumnSetWidth(column, width);
876 // Do the task column separately since it's a little different.
877 column = gtk_tree_view_get_column(treeview,
878 TreeViewColumnIndexFromID(kTaskManagerTask));
879 width = gtk_tree_view_column_get_width(column);
880 // Turn expanding back off to make resizing columns behave sanely.
881 gtk_tree_view_column_set_expand(column, FALSE);
882 TreeViewColumnSetWidth(column, width);
885 void TaskManagerGtk::OnSelectionChanged(GtkTreeSelection* selection) {
886 if (ignore_selection_changed_)
887 return;
888 base::AutoReset<bool> autoreset(&ignore_selection_changed_, true);
890 // The set of groups that should be selected.
891 std::set<TaskManagerModel::GroupRange> ranges;
892 bool selection_contains_browser_process = false;
894 GtkTreeModel* model;
895 GList* paths = gtk_tree_selection_get_selected_rows(selection, &model);
896 for (GList* item = paths; item; item = item->next) {
897 GtkTreePath* path = gtk_tree_model_sort_convert_path_to_child_path(
898 GTK_TREE_MODEL_SORT(process_list_sort_),
899 reinterpret_cast<GtkTreePath*>(item->data));
900 int row = gtk_tree::GetRowNumForPath(path);
901 gtk_tree_path_free(path);
902 if (task_manager_->IsBrowserProcess(row))
903 selection_contains_browser_process = true;
904 ranges.insert(model_->GetGroupRangeForResource(row));
906 g_list_foreach(paths, reinterpret_cast<GFunc>(gtk_tree_path_free), NULL);
907 g_list_free(paths);
909 for (std::set<TaskManagerModel::GroupRange>::iterator iter = ranges.begin();
910 iter != ranges.end(); ++iter) {
911 for (int i = 0; i < iter->second; ++i) {
912 GtkTreePath* child_path = gtk_tree_path_new_from_indices(iter->first + i,
913 -1);
914 GtkTreePath* sort_path = gtk_tree_model_sort_convert_child_path_to_path(
915 GTK_TREE_MODEL_SORT(process_list_sort_), child_path);
916 gtk_tree_selection_select_path(selection, sort_path);
917 gtk_tree_path_free(child_path);
918 gtk_tree_path_free(sort_path);
922 bool sensitive = (paths != NULL) && !selection_contains_browser_process;
923 gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog_),
924 kTaskManagerResponseKill, sensitive);
927 void TaskManagerGtk::OnRowActivated(GtkWidget* widget,
928 GtkTreePath* path,
929 GtkTreeViewColumn* column) {
930 GtkTreePath* child_path = gtk_tree_model_sort_convert_path_to_child_path(
931 GTK_TREE_MODEL_SORT(process_list_sort_), path);
932 int row = gtk_tree::GetRowNumForPath(child_path);
933 gtk_tree_path_free(child_path);
934 task_manager_->ActivateProcess(row);
937 gboolean TaskManagerGtk::OnButtonEvent(GtkWidget* widget,
938 GdkEventButton* event) {
939 // GTK does menu on mouse-up while views does menu on mouse-down,
940 // so this function can be called from either signal.
941 if (event->button == 3) {
942 ShowContextMenu(gfx::Point(event->x_root, event->y_root),
943 event->time);
944 return TRUE;
947 return FALSE;
950 gboolean TaskManagerGtk::OnGtkAccelerator(GtkAccelGroup* accel_group,
951 GObject* acceleratable,
952 guint keyval,
953 GdkModifierType modifier) {
954 if (keyval == GDK_w && modifier == GDK_CONTROL_MASK) {
955 // The GTK_RESPONSE_DELETE_EVENT response must be sent before the widget
956 // is destroyed. The deleted object will receive gtk signals otherwise.
957 gtk_dialog_response(GTK_DIALOG(dialog_), GTK_RESPONSE_DELETE_EVENT);
960 return TRUE;
963 namespace chrome {
965 // Declared in browser_dialogs.h.
966 void ShowTaskManager(Browser* browser) {
967 TaskManagerGtk::Show();
970 } // namespace chrome