Home | History | Annotate | Download | only in views
      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