// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/ipc_status_view.h" #include #include "base/logging.h" #include "base/string_util.h" #include "chrome/app/chrome_dll_resource.h" #include "chrome/common/chrome_constants.h" #include "chrome/common/ipc_logging.h" #include "chrome/common/plugin_messages.h" #include "chrome/common/pref_names.h" #include "chrome/common/pref_service.h" #include "chrome/common/render_messages.h" #ifdef IPC_MESSAGE_LOG_ENABLED using base::Time; namespace { const wchar_t kTitleMsg[] = L"IPC Messages"; const wchar_t kStartLoggingMsg[] = L"Start IPC Logging"; const wchar_t kStopLoggingMsg[] = L"Stop IPC Logging"; const wchar_t kClearMsg[] = L"Clear"; const wchar_t kSettingsMsg[] = L"Filter"; enum { kTimeColumn = 0, kChannelColumn, kMessageColumn, kFlagsColumn, kDispatchColumn, kProcessColumn, kParamsColumn, }; // This class ensures that we have a link dependency on render_messages.cc and // plugin_messages.cc, and at the same time sets up the message logger function // mappings. class RegisterLoggerFuncs { public: RegisterLoggerFuncs() { RenderMessagesInit(); PluginMessagesInit(); } }; RegisterLoggerFuncs g_register_logger_funcs; } // namespace IPCStatusView* IPCStatusView::current_; IPCStatusView::IPCStatusView() : StatusView(TAB_CONTENTS_IPC_STATUS_VIEW) { DCHECK(!current_); current_ = this; settings_dialog_ = NULL; init_done_ = false; view_ = NULL; view_host_ = NULL; plugin_ = NULL; plugin_host_ = NULL; npobject_ = NULL; plugin_process_ = NULL; plugin_process_host_ = NULL; IPC::Logging::current()->SetConsumer(this); } IPCStatusView::~IPCStatusView() { current_ = NULL; IPC::Logging::current()->SetConsumer(NULL); if (settings_dialog_ != NULL) ::DestroyWindow(settings_dialog_); } const std::wstring IPCStatusView::GetDefaultTitle() { return kTitleMsg; } void IPCStatusView::SetActive(bool active) { StatusView::set_is_active(active); if (!disabled_messages_.empty() || !active) return; Profile* current_profile = profile(); if (!current_profile) return; PrefService* prefs = current_profile->GetPrefs(); if (prefs->IsPrefRegistered(prefs::kIpcDisabledMessages)) return; prefs->RegisterListPref(prefs::kIpcDisabledMessages); const ListValue* list = prefs->GetList(prefs::kIpcDisabledMessages); if (!list) return; for (ListValue::const_iterator itr = list->begin(); itr != list->end(); ++itr) { if (!(*itr)->IsType(Value::TYPE_INTEGER)) continue; int value = 0; if (!(*itr)->GetAsInteger(&value)) continue; disabled_messages_.insert(value); } } void IPCStatusView::OnCreate(const CRect& rect) { CreateButton(IDC_START_LOGGING, kStartLoggingMsg); CreateButton(IDC_STOP_LOGGING, kStopLoggingMsg); CreateButton(IDC_CLEAR, kClearMsg); CreateButton(IDC_SETTINGS, kSettingsMsg); // Initialize the list view for messages. // Don't worry about the size, we'll resize when we get WM_SIZE message_list_.Create(GetContainerHWND(), const_cast(rect), NULL, WS_CHILD | WS_VISIBLE | LVS_SORTASCENDING); message_list_.SetViewType(LVS_REPORT); message_list_.SetExtendedListViewStyle(LVS_EX_FULLROWSELECT); int column_index = 0; message_list_.InsertColumn(kTimeColumn, L"time", LVCFMT_LEFT, 80); message_list_.InsertColumn(kChannelColumn, L"channel", LVCFMT_LEFT, 110); message_list_.InsertColumn(kMessageColumn, L"message", LVCFMT_LEFT, 240); message_list_.InsertColumn(kFlagsColumn, L"flags", LVCFMT_LEFT, 50); message_list_.InsertColumn(kDispatchColumn, L"dispatch (ms)", LVCFMT_RIGHT, 80); message_list_.InsertColumn(kProcessColumn, L"process (ms)", LVCFMT_RIGHT, 80); message_list_.InsertColumn(kParamsColumn, L"parameters", LVCFMT_LEFT, 500); } void IPCStatusView::Log(const IPC::LogData& data) { if (disabled_messages_.find(data.type) != disabled_messages_.end()) return; // Message type is filtered out. Time sent = Time::FromInternalValue(data.sent); Time::Exploded exploded; sent.LocalExplode(&exploded); if (exploded.hour > 12) exploded.hour -= 12; std::wstring sent_str = StringPrintf(L"%02d:%02d:%02d.%03d", exploded.hour, exploded.minute, exploded.second, exploded.millisecond); int count = message_list_.GetItemCount(); int index = message_list_.InsertItem(count, sent_str.c_str()); message_list_.SetItemText(index, kTimeColumn, sent_str.c_str()); message_list_.SetItemText(index, kChannelColumn, data.channel.c_str()); std::wstring message_name; IPC::Logging::GetMessageText(data.type, &message_name, NULL, NULL); message_list_.SetItemText(index, kMessageColumn, message_name.c_str()); message_list_.SetItemText(index, kFlagsColumn, data.flags.c_str()); int64 time_to_send = (Time::FromInternalValue(data.receive) - sent).InMilliseconds(); // time can go backwards by a few ms (see Time), don't show that. time_to_send = std::max(static_cast(time_to_send), 0); std::wstring temp = StringPrintf(L"%d", time_to_send); message_list_.SetItemText(index, kDispatchColumn, temp.c_str()); int64 time_to_process = (Time::FromInternalValue(data.dispatch) - Time::FromInternalValue(data.receive)).InMilliseconds(); time_to_process = std::max(static_cast(time_to_process), 0); temp = StringPrintf(L"%d", time_to_process); message_list_.SetItemText(index, kProcessColumn, temp.c_str()); message_list_.SetItemText(index, kParamsColumn, data.params.c_str()); message_list_.EnsureVisible(index, FALSE); } void IPCStatusView::OnSize(const CRect& rect) { message_list_.MoveWindow(rect); } void IPCStatusView::OnStartLogging(UINT code, int button_id, HWND hwnd) { IPC::Logging::current()->Enable(); } void IPCStatusView::OnStopLogging(UINT code, int button_id, HWND hwnd) { IPC::Logging::current()->Disable(); } void IPCStatusView::OnClear(UINT code, int button_id, HWND hwnd) { message_list_.DeleteAllItems(); } INT_PTR CALLBACK IPCStatusView::DialogProc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { switch (msg) { case WM_INITDIALOG: current()->InitDialog(hwnd); return FALSE; // Don't set keyboard focus. case WM_SYSCOMMAND: if (wparam == SC_CLOSE) { current()->CloseDialog(); return FALSE; } break; case WM_NOTIFY: { NMLISTVIEW* info = reinterpret_cast(lparam); if ((wparam == IDC_View || wparam == IDC_ViewHost || wparam == IDC_Plugin || wparam == IDC_PluginHost || wparam == IDC_NPObject || wparam == IDC_PluginProcess || wparam == IDC_PluginProcessHost) && info->hdr.code == LVN_ITEMCHANGED) { if (info->uChanged & LVIF_STATE) { bool checked = (info->uNewState >> 12) == 2; current()->OnCheck(static_cast(info->lParam), checked); } return FALSE; } break; } case WM_COMMAND: if (HIWORD(wparam) == BN_CLICKED) current()->OnButtonClick(LOWORD(wparam)); break; } return FALSE; } void IPCStatusView::InitDialog(HWND hwnd) { CreateColumn(ViewStart, ViewEnd, ::GetDlgItem(hwnd, IDC_View), &view_); CreateColumn(ViewHostStart, ViewHostEnd, ::GetDlgItem(hwnd, IDC_ViewHost), &view_host_); CreateColumn(PluginStart, PluginEnd, ::GetDlgItem(hwnd, IDC_Plugin), &plugin_); CreateColumn(PluginHostStart, PluginHostEnd, ::GetDlgItem(hwnd, IDC_PluginHost), &plugin_host_); CreateColumn(NPObjectStart, NPObjectEnd, ::GetDlgItem(hwnd, IDC_NPObject), &npobject_); CreateColumn(PluginProcessStart, PluginProcessEnd, ::GetDlgItem(hwnd, IDC_PluginProcess), &plugin_process_); CreateColumn(PluginProcessHostStart, PluginProcessHostEnd, ::GetDlgItem(hwnd, IDC_PluginProcessHost), &plugin_process_host_); init_done_ = true; } void IPCStatusView::CreateColumn( uint16 start, uint16 end, HWND hwnd, CListViewCtrl** control) { DCHECK(*control == NULL); *control = new CListViewCtrl(hwnd); CListViewCtrl* control_ptr = *control; control_ptr->SetViewType(LVS_REPORT); control_ptr->SetExtendedListViewStyle(LVS_EX_CHECKBOXES); control_ptr->ModifyStyle(0, LVS_SORTASCENDING | LVS_NOCOLUMNHEADER); control_ptr->InsertColumn(0, L"id", LVCFMT_LEFT, 230); std::set* disabled_messages = ¤t()->disabled_messages_; for (uint16 i = start; i < end; i++) { std::wstring name; IPC::Logging::GetMessageText(i, &name, NULL, NULL); int index = control_ptr->InsertItem( LVIF_TEXT | LVIF_PARAM, 0, name.c_str(), 0, 0, 0, i); control_ptr->SetItemText(index, 0, name.c_str()); if (disabled_messages->find(i) == disabled_messages->end()) control_ptr->SetCheckState(index, TRUE); } } void IPCStatusView::CloseDialog() { delete view_; delete view_host_; delete plugin_host_; delete npobject_; delete plugin_process_; delete plugin_process_host_; view_ = NULL; view_host_ = NULL; plugin_ = NULL; plugin_host_ = NULL; npobject_ = NULL; plugin_process_ = NULL; plugin_process_host_ = NULL; init_done_ = false; ::DestroyWindow(settings_dialog_); settings_dialog_ = NULL; Profile* current_profile = profile(); if (!current_profile) return; PrefService* prefs = current_profile->GetPrefs(); if (!prefs->IsPrefRegistered(prefs::kIpcDisabledMessages)) return; ListValue* list = prefs->GetMutableList(prefs::kIpcDisabledMessages); list->Clear(); for (std::set::const_iterator itr = disabled_messages_.begin(); itr != disabled_messages_.end(); ++itr) { list->Append(Value::CreateIntegerValue(*itr)); } } void IPCStatusView::OnCheck(int id, bool checked) { if (!init_done_) return; if (checked) { disabled_messages_.erase(id); } else { disabled_messages_.insert(id); } } void IPCStatusView::OnButtonClick(int id) { switch(id) { case IDC_ViewAll: CheckButtons(view_, true); break; case IDC_ViewNone: CheckButtons(view_, false); break; case IDC_ViewHostAll: CheckButtons(view_host_, true); break; case IDC_ViewHostNone: CheckButtons(view_host_, false); break; case IDC_PluginAll: CheckButtons(plugin_, true); break; case IDC_PluginNone: CheckButtons(plugin_, false); break; case IDC_PluginHostAll: CheckButtons(plugin_host_, true); break; case IDC_PluginHostNone: CheckButtons(plugin_host_, false); break; case IDC_NPObjectAll: CheckButtons(npobject_, true); break; case IDC_NPObjectNone: CheckButtons(npobject_, false); break; } } void IPCStatusView::CheckButtons(CListViewCtrl* control, bool check) { int count = control->GetItemCount(); for (int i = 0; i < count; ++i) control->SetCheckState(i, check); } void IPCStatusView::OnSettings(UINT code, int button_id, HWND hwnd) { if (settings_dialog_ != NULL) return; HINSTANCE module_handle = GetModuleHandle(chrome::kBrowserResourcesDll); settings_dialog_ = CreateDialog(module_handle, MAKEINTRESOURCE(IDD_IPC_SETTINGS), NULL, IPCStatusView::DialogProc); ::ShowWindow(settings_dialog_, SW_SHOW); } #endif // IPC_MESSAGE_LOG_ENABLED