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// Need to include this before any other file because it defines 6// IPC_MESSAGE_LOG_ENABLED. We need to use it to define 7// IPC_MESSAGE_MACROS_LOG_ENABLED so render_messages.h will generate the 8// ViewMsgLog et al. functions. 9#include "ipc/ipc_message.h" 10 11#ifdef IPC_MESSAGE_LOG_ENABLED 12#define IPC_MESSAGE_MACROS_LOG_ENABLED 13 14#include "chrome/browser/ui/views/about_ipc_dialog.h" 15 16#include <set> 17 18#include "base/string_util.h" 19#include "base/threading/thread.h" 20#include "base/time.h" 21#include "base/utf_string_conversions.h" 22#include "chrome/app/chrome_command_ids.h" 23#include "chrome/app/chrome_dll_resource.h" 24#include "chrome/browser/browser_process.h" 25#include "chrome/browser/ui/browser_dialogs.h" 26#include "chrome/common/chrome_constants.h" 27#include "chrome/common/devtools_messages.h" 28#include "chrome/common/render_messages.h" 29#include "content/common/plugin_messages.h" 30#include "net/url_request/url_request.h" 31#include "net/url_request/url_request_job.h" 32#include "net/url_request/url_request_job_tracker.h" 33#include "views/controls/button/text_button.h" 34#include "views/controls/native/native_view_host.h" 35#include "views/layout/grid_layout.h" 36#include "views/layout/layout_constants.h" 37#include "views/widget/root_view.h" 38#include "views/widget/widget.h" 39#include "views/window/window.h" 40 41namespace { 42 43// We don't localize this UI since this is a developer-only feature. 44const wchar_t kStartTrackingLabel[] = L"Start tracking"; 45const wchar_t kStopTrackingLabel[] = L"Stop tracking"; 46const wchar_t kClearLabel[] = L"Clear"; 47const wchar_t kFilterLabel[] = L"Filter..."; 48 49enum { 50 kTimeColumn = 0, 51 kChannelColumn, 52 kMessageColumn, 53 kFlagsColumn, 54 kDispatchColumn, 55 kProcessColumn, 56 kParamsColumn, 57}; 58 59// This class registers the browser IPC logger functions with IPC::Logging. 60class RegisterLoggerFuncs { 61 public: 62 RegisterLoggerFuncs() { 63 IPC::Logging::set_log_function_map(&g_log_function_mapping); 64 } 65}; 66 67RegisterLoggerFuncs g_register_logger_funcs; 68 69// The singleton dialog box. This is non-NULL when a dialog is active so we 70// know not to create a new one. 71AboutIPCDialog* g_active_dialog = NULL; 72 73std::set<int> disabled_messages; 74 75// Settings dialog ------------------------------------------------------------- 76 77bool init_done = false; 78HWND settings_dialog = NULL; 79// Settings. 80CListViewCtrl* messages = NULL; 81 82void OnCheck(int id, bool checked) { 83 if (!init_done) 84 return; 85 86 if (checked) 87 disabled_messages.erase(id); 88 else 89 disabled_messages.insert(id); 90} 91 92void InitDialog(HWND hwnd) { 93 messages = new CListViewCtrl(::GetDlgItem(hwnd, IDC_Messages)); 94 95 messages->SetViewType(LVS_REPORT); 96 messages->SetExtendedListViewStyle(LVS_EX_CHECKBOXES); 97 messages->ModifyStyle(0, LVS_SORTASCENDING | LVS_NOCOLUMNHEADER); 98 messages->InsertColumn(0, L"id", LVCFMT_LEFT, 230); 99 100 LogFunctionMap* log_functions = IPC::Logging::log_function_map(); 101 for (LogFunctionMap::iterator i = log_functions->begin(); 102 i != log_functions->end(); ++i) { 103 std::string name; 104 (*i->second)(&name, NULL, NULL); 105 if (name.empty()) 106 continue; // Will happen if the message file isn't included above. 107 std::wstring wname = UTF8ToWide(name); 108 109 int index = messages->InsertItem( 110 LVIF_TEXT | LVIF_PARAM, 0, wname.c_str(), 0, 0, 0, i->first); 111 112 messages->SetItemText(index, 0, wname.c_str()); 113 114 if (disabled_messages.find(i->first) == disabled_messages.end()) 115 messages->SetCheckState(index, TRUE); 116 } 117 118 init_done = true; 119} 120 121void CloseDialog() { 122 delete messages; 123 messages = NULL; 124 125 init_done = false; 126 127 ::DestroyWindow(settings_dialog); 128 settings_dialog = NULL; 129 130 /* The old version of this code stored the last settings in the preferences. 131 But with this dialog, there currently isn't an easy way to get the profile 132 to save in the preferences. 133 Profile* current_profile = profile(); 134 if (!current_profile) 135 return; 136 PrefService* prefs = current_profile->GetPrefs(); 137 if (!prefs->FindPreference(prefs::kIpcDisabledMessages)) 138 return; 139 ListValue* list = prefs->GetMutableList(prefs::kIpcDisabledMessages); 140 list->Clear(); 141 for (std::set<int>::const_iterator itr = disabled_messages_.begin(); 142 itr != disabled_messages_.end(); 143 ++itr) { 144 list->Append(Value::CreateIntegerValue(*itr)); 145 } 146 */ 147} 148 149void OnButtonClick(int id) { 150 int count = messages->GetItemCount(); 151 for (int i = 0; i < count; ++i) 152 messages->SetCheckState(i, id == IDC_MessagesAll); 153} 154 155INT_PTR CALLBACK DialogProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { 156 switch (msg) { 157 case WM_INITDIALOG: 158 InitDialog(hwnd); 159 return FALSE; // Don't set keyboard focus. 160 case WM_SYSCOMMAND: 161 if (wparam == SC_CLOSE) { 162 CloseDialog(); 163 return FALSE; 164 } 165 break; 166 case WM_NOTIFY: { 167 NMLISTVIEW* info = reinterpret_cast<NM_LISTVIEW*>(lparam); 168 if (wparam == IDC_Messages && info->hdr.code == LVN_ITEMCHANGED) { 169 if (info->uChanged & LVIF_STATE) { 170 bool checked = (info->uNewState >> 12) == 2; 171 OnCheck(static_cast<int>(info->lParam), checked); 172 } 173 return FALSE; 174 } 175 break; 176 } 177 case WM_COMMAND: 178 if (HIWORD(wparam) == BN_CLICKED) 179 OnButtonClick(LOWORD(wparam)); 180 break; 181 } 182 return FALSE; 183} 184 185void RunSettingsDialog(HWND parent) { 186 if (settings_dialog) 187 return; 188 HINSTANCE module_handle = GetModuleHandle(chrome::kBrowserResourcesDll); 189 settings_dialog = CreateDialog(module_handle, 190 MAKEINTRESOURCE(IDD_IPC_SETTINGS), 191 NULL, 192 &DialogProc); 193 ::ShowWindow(settings_dialog, SW_SHOW); 194} 195 196} // namespace 197 198// AboutIPCDialog -------------------------------------------------------------- 199 200AboutIPCDialog::AboutIPCDialog() 201 : track_toggle_(NULL), 202 clear_button_(NULL), 203 filter_button_(NULL), 204 table_(NULL), 205 tracking_(false) { 206 SetupControls(); 207 IPC::Logging::GetInstance()->SetConsumer(this); 208} 209 210AboutIPCDialog::~AboutIPCDialog() { 211 g_active_dialog = NULL; 212 IPC::Logging::GetInstance()->SetConsumer(NULL); 213} 214 215// static 216void AboutIPCDialog::RunDialog() { 217 if (!g_active_dialog) { 218 g_active_dialog = new AboutIPCDialog; 219 views::Window::CreateChromeWindow(NULL, gfx::Rect(), 220 g_active_dialog)->Show(); 221 } else { 222 // TODO(brettw) it would be nice to focus the existing window. 223 } 224} 225 226void AboutIPCDialog::SetupControls() { 227 views::GridLayout* layout = views::GridLayout::CreatePanel(this); 228 SetLayoutManager(layout); 229 230 track_toggle_ = new views::TextButton(this, kStartTrackingLabel); 231 clear_button_ = new views::TextButton(this, kClearLabel); 232 filter_button_ = new views::TextButton(this, kFilterLabel); 233 234 table_ = new views::NativeViewHost; 235 236 static const int first_column_set = 1; 237 views::ColumnSet* column_set = layout->AddColumnSet(first_column_set); 238 column_set->AddColumn(views::GridLayout::CENTER, views::GridLayout::CENTER, 239 33.33f, views::GridLayout::FIXED, 0, 0); 240 column_set->AddColumn(views::GridLayout::CENTER, views::GridLayout::CENTER, 241 33.33f, views::GridLayout::FIXED, 0, 0); 242 column_set->AddColumn(views::GridLayout::CENTER, views::GridLayout::CENTER, 243 33.33f, views::GridLayout::FIXED, 0, 0); 244 245 static const int table_column_set = 2; 246 column_set = layout->AddColumnSet(table_column_set); 247 column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 248 100.0f, views::GridLayout::FIXED, 0, 0); 249 250 layout->StartRow(0, first_column_set); 251 layout->AddView(track_toggle_); 252 layout->AddView(clear_button_); 253 layout->AddView(filter_button_); 254 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing); 255 layout->StartRow(1.0f, table_column_set); 256 layout->AddView(table_); 257} 258 259gfx::Size AboutIPCDialog::GetPreferredSize() { 260 return gfx::Size(800, 400); 261} 262 263views::View* AboutIPCDialog::GetContentsView() { 264 return this; 265} 266 267int AboutIPCDialog::GetDialogButtons() const { 268 // Don't want OK or Cancel. 269 return 0; 270} 271 272std::wstring AboutIPCDialog::GetWindowTitle() const { 273 return L"about:ipc"; 274} 275 276void AboutIPCDialog::Layout() { 277 if (!message_list_.m_hWnd) { 278 HWND parent_window = GetRootView()->GetWidget()->GetNativeView(); 279 280 RECT rect = {0, 0, 10, 10}; 281 HWND list_hwnd = message_list_.Create(parent_window, 282 rect, NULL, WS_CHILD | WS_VISIBLE | LVS_SORTASCENDING); 283 message_list_.SetViewType(LVS_REPORT); 284 message_list_.SetExtendedListViewStyle(LVS_EX_FULLROWSELECT); 285 286 int column_index = 0; 287 message_list_.InsertColumn(kTimeColumn, L"time", LVCFMT_LEFT, 80); 288 message_list_.InsertColumn(kChannelColumn, L"channel", LVCFMT_LEFT, 110); 289 message_list_.InsertColumn(kMessageColumn, L"message", LVCFMT_LEFT, 240); 290 message_list_.InsertColumn(kFlagsColumn, L"flags", LVCFMT_LEFT, 50); 291 message_list_.InsertColumn(kDispatchColumn, L"dispatch (ms)", LVCFMT_RIGHT, 292 80); 293 message_list_.InsertColumn(kProcessColumn, L"process (ms)", LVCFMT_RIGHT, 294 80); 295 message_list_.InsertColumn(kParamsColumn, L"parameters", LVCFMT_LEFT, 500); 296 297 table_->Attach(list_hwnd); 298 } 299 300 View::Layout(); 301} 302 303void AboutIPCDialog::Log(const IPC::LogData& data) { 304 if (disabled_messages.find(data.type) != disabled_messages.end()) 305 return; // Message type is filtered out. 306 307 base::Time sent = base::Time::FromInternalValue(data.sent); 308 base::Time::Exploded exploded; 309 sent.LocalExplode(&exploded); 310 if (exploded.hour > 12) 311 exploded.hour -= 12; 312 313 std::wstring sent_str = StringPrintf(L"%02d:%02d:%02d.%03d", 314 exploded.hour, exploded.minute, exploded.second, exploded.millisecond); 315 316 int count = message_list_.GetItemCount(); 317 int index = message_list_.InsertItem(count, sent_str.c_str()); 318 319 message_list_.SetItemText(index, kTimeColumn, sent_str.c_str()); 320 message_list_.SetItemText(index, kChannelColumn, 321 ASCIIToWide(data.channel).c_str()); 322 323 std::string message_name; 324 IPC::Logging::GetMessageText(data.type, &message_name, NULL, NULL); 325 message_list_.SetItemText(index, kMessageColumn, 326 UTF8ToWide(message_name).c_str()); 327 message_list_.SetItemText(index, kFlagsColumn, 328 UTF8ToWide(data.flags).c_str()); 329 330 int64 time_to_send = (base::Time::FromInternalValue(data.receive) - 331 sent).InMilliseconds(); 332 // time can go backwards by a few ms (see Time), don't show that. 333 time_to_send = std::max(static_cast<int>(time_to_send), 0); 334 std::wstring temp = StringPrintf(L"%d", time_to_send); 335 message_list_.SetItemText(index, kDispatchColumn, temp.c_str()); 336 337 int64 time_to_process = (base::Time::FromInternalValue(data.dispatch) - 338 base::Time::FromInternalValue(data.receive)).InMilliseconds(); 339 time_to_process = std::max(static_cast<int>(time_to_process), 0); 340 temp = StringPrintf(L"%d", time_to_process); 341 message_list_.SetItemText(index, kProcessColumn, temp.c_str()); 342 343 message_list_.SetItemText(index, kParamsColumn, 344 UTF8ToWide(data.params).c_str()); 345 message_list_.EnsureVisible(index, FALSE); 346} 347 348bool AboutIPCDialog::CanResize() const { 349 return true; 350} 351 352void AboutIPCDialog::ButtonPressed( 353 views::Button* button, const views::Event& event) { 354 if (button == track_toggle_) { 355 if (tracking_) { 356 track_toggle_->SetText(kStartTrackingLabel); 357 tracking_ = false; 358 g_browser_process->SetIPCLoggingEnabled(false); 359 } else { 360 track_toggle_->SetText(kStopTrackingLabel); 361 tracking_ = true; 362 g_browser_process->SetIPCLoggingEnabled(true); 363 } 364 track_toggle_->SchedulePaint(); 365 } else if (button == clear_button_) { 366 message_list_.DeleteAllItems(); 367 } else if (button == filter_button_) { 368 RunSettingsDialog(GetRootView()->GetWidget()->GetNativeView()); 369 } 370} 371 372namespace browser { 373 374void ShowAboutIPCDialog() { 375 AboutIPCDialog::RunDialog(); 376} 377 378} // namespace browser 379 380#endif // IPC_MESSAGE_LOG_ENABLED 381