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