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
41 namespace {
42
43 // We don't localize this UI since this is a developer-only feature.
44 const wchar_t kStartTrackingLabel[] = L"Start tracking";
45 const wchar_t kStopTrackingLabel[] = L"Stop tracking";
46 const wchar_t kClearLabel[] = L"Clear";
47 const wchar_t kFilterLabel[] = L"Filter...";
48
49 enum {
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.
60 class RegisterLoggerFuncs {
61 public:
RegisterLoggerFuncs()62 RegisterLoggerFuncs() {
63 IPC::Logging::set_log_function_map(&g_log_function_mapping);
64 }
65 };
66
67 RegisterLoggerFuncs 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.
71 AboutIPCDialog* g_active_dialog = NULL;
72
73 std::set<int> disabled_messages;
74
75 // Settings dialog -------------------------------------------------------------
76
77 bool init_done = false;
78 HWND settings_dialog = NULL;
79 // Settings.
80 CListViewCtrl* messages = NULL;
81
OnCheck(int id,bool checked)82 void 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
InitDialog(HWND hwnd)92 void 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
CloseDialog()121 void 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
OnButtonClick(int id)149 void 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
DialogProc(HWND hwnd,UINT msg,WPARAM wparam,LPARAM lparam)155 INT_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
RunSettingsDialog(HWND parent)185 void 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
AboutIPCDialog()200 AboutIPCDialog::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
~AboutIPCDialog()210 AboutIPCDialog::~AboutIPCDialog() {
211 g_active_dialog = NULL;
212 IPC::Logging::GetInstance()->SetConsumer(NULL);
213 }
214
215 // static
RunDialog()216 void 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
SetupControls()226 void 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
GetPreferredSize()259 gfx::Size AboutIPCDialog::GetPreferredSize() {
260 return gfx::Size(800, 400);
261 }
262
GetContentsView()263 views::View* AboutIPCDialog::GetContentsView() {
264 return this;
265 }
266
GetDialogButtons() const267 int AboutIPCDialog::GetDialogButtons() const {
268 // Don't want OK or Cancel.
269 return 0;
270 }
271
GetWindowTitle() const272 std::wstring AboutIPCDialog::GetWindowTitle() const {
273 return L"about:ipc";
274 }
275
Layout()276 void 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
Log(const IPC::LogData & data)303 void 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
CanResize() const348 bool AboutIPCDialog::CanResize() const {
349 return true;
350 }
351
ButtonPressed(views::Button * button,const views::Event & event)352 void 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
372 namespace browser {
373
ShowAboutIPCDialog()374 void ShowAboutIPCDialog() {
375 AboutIPCDialog::RunDialog();
376 }
377
378 } // namespace browser
379
380 #endif // IPC_MESSAGE_LOG_ENABLED
381