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>
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"
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
{
60 kTaskManagerProfileName
,
61 kTaskManagerSharedMem
,
62 kTaskManagerPrivateMem
,
65 kTaskManagerProcessID
,
66 kTaskManagerJavaScriptMemory
,
67 kTaskManagerWebCoreImageCache
,
68 kTaskManagerWebCoreScriptsCache
,
69 kTaskManagerWebCoreCssCache
,
70 kTaskManagerVideoMemory
,
72 kTaskManagerSqliteMemoryUsed
,
73 kTaskManagerNaClDebugStubPort
,
74 kTaskManagerGoatsTeleported
,
75 kTaskManagerColumnCount
,
78 const TaskManagerColumn kTaskManagerLastVisibleColumn
=
79 kTaskManagerGoatsTeleported
;
81 TaskManagerColumn
TaskManagerResourceIDToColumnID(int 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
;
117 return static_cast<TaskManagerColumn
>(-1);
121 int TaskManagerColumnIDToResourceID(int 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
;
161 // Should be used for all gtk_tree_view functions that require a column index on
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
168 int TreeViewColumnIndexFromID(TaskManagerColumn colid
) {
172 // Shows or hides a treeview column.
173 void TreeViewColumnSetVisible(GtkWidget
* treeview
, TaskManagerColumn colid
,
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,
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
);
245 class TaskManagerGtk::ContextMenuController
246 : public ui::SimpleMenuModel::Delegate
{
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
);
265 task_manager_
= NULL
;
270 // ui::SimpleMenuModel::Delegate implementation:
271 virtual bool IsCommandIdEnabled(int command_id
) const OVERRIDE
{
278 virtual bool IsCommandIdChecked(int command_id
) const OVERRIDE
{
282 TaskManagerColumn colid
= static_cast<TaskManagerColumn
>(command_id
);
283 return TreeViewColumnIsVisible(task_manager_
->treeview_
, colid
);
286 virtual bool GetAcceleratorForCommandId(
288 ui::Accelerator
* accelerator
) OVERRIDE
{
292 virtual void ExecuteCommand(int command_id
, int event_flags
) OVERRIDE
{
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
307 TaskManagerGtk
* task_manager_
;
309 DISALLOW_COPY_AND_ASSIGN(ContextMenuController
);
312 TaskManagerGtk::TaskManagerGtk()
313 : task_manager_(TaskManager::GetInstance()),
314 model_(TaskManager::GetInstance()->model()),
319 ignore_selection_changed_(false) {
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_
);
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() {
347 void TaskManagerGtk::OnItemsChanged(int start
, int length
) {
349 if (!gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(process_list_
), &iter
,
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);
371 gtk_list_store_prepend(process_list_
, &iter
);
372 } else if (start
>= process_count_
) {
373 gtk_list_store_append(process_list_
, &iter
);
376 gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(process_list_
), &sibling
,
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);
396 gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(process_list_
), &iter
,
399 for (int i
= 0; i
< length
; i
++) {
400 // |iter| is moved to the next valid node when the current node is
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_
);
423 void TaskManagerGtk::Show() {
425 // If there's a Task manager window open already, just activate it.
426 gtk_util::PresentWindow(instance_
->dialog_
, 0);
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.
441 GTK_DIALOG_NO_SEPARATOR
,
444 // Allow browser windows to go in front of the task manager dialog in
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
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
));
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
),
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_
),
580 ComparePage
, this, NULL
);
581 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(process_list_sort_
),
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_
),
595 CompareCPU
, this, NULL
);
596 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(process_list_sort_
),
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_
),
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
)) {
698 GetModelText(row
, IDS_TASK_MANAGER_JAVASCRIPT_MEMORY_ALLOCATED_COLUMN
);
700 std::string wk_img_cache
;
701 if (TreeViewColumnIsVisible(treeview_
, kTaskManagerWebCoreImageCache
)) {
703 GetModelText(row
, IDS_TASK_MANAGER_WEBCORE_IMAGE_CACHE_COLUMN
);
705 std::string wk_scripts_cache
;
706 if (TreeViewColumnIsVisible(treeview_
, kTaskManagerWebCoreScriptsCache
)) {
708 GetModelText(row
, IDS_TASK_MANAGER_WEBCORE_SCRIPTS_CACHE_COLUMN
);
710 std::string wk_css_cache
;
711 if (TreeViewColumnIsVisible(treeview_
, kTaskManagerWebCoreCssCache
)) {
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
);
719 if (TreeViewColumnIsVisible(treeview_
, kTaskManagerFPS
))
720 fps
= GetModelText(row
, IDS_TASK_MANAGER_FPS_COLUMN
);
721 std::string sqlite_memory
;
722 if (TreeViewColumnIsVisible(treeview_
, kTaskManagerSqliteMemoryUsed
)) {
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
);
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(),
755 g_object_unref(icon
);
759 void TaskManagerGtk::KillSelectedProcesses() {
760 GtkTreeSelection
* selection
= gtk_tree_view_get_selection(
761 GTK_TREE_VIEW(treeview_
));
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
);
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
804 GtkSortType sort_type
;
805 gtk_tree_sortable_get_sort_column_id(GTK_TREE_SORTABLE(process_list_sort_
),
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
);
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
) {
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);
845 } else if (response_id
== kTaskManagerResponseKill
) {
846 KillSelectedProcesses();
847 } else if (response_id
== kTaskManagerAboutMemoryLink
) {
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
,
868 GtkTreeViewColumn
* column
= NULL
;
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_
)
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;
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
);
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
,
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
,
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
),
950 gboolean
TaskManagerGtk::OnGtkAccelerator(GtkAccelGroup
* accel_group
,
951 GObject
* acceleratable
,
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
);
965 // Declared in browser_dialogs.h.
966 void ShowTaskManager(Browser
* browser
) {
967 TaskManagerGtk::Show();
970 } // namespace chrome