Home | History | Annotate | Download | only in views
      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:
     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 
     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 
     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 
    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 
    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 
    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 
    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 
    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 
    210 AboutIPCDialog::~AboutIPCDialog() {
    211   g_active_dialog = NULL;
    212   IPC::Logging::GetInstance()->SetConsumer(NULL);
    213 }
    214 
    215 // static
    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 
    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 
    259 gfx::Size AboutIPCDialog::GetPreferredSize() {
    260   return gfx::Size(800, 400);
    261 }
    262 
    263 views::View* AboutIPCDialog::GetContentsView() {
    264   return this;
    265 }
    266 
    267 int AboutIPCDialog::GetDialogButtons() const {
    268   // Don't want OK or Cancel.
    269   return 0;
    270 }
    271 
    272 std::wstring AboutIPCDialog::GetWindowTitle() const {
    273   return L"about:ipc";
    274 }
    275 
    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 
    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 
    348 bool AboutIPCDialog::CanResize() const {
    349   return true;
    350 }
    351 
    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 
    374 void ShowAboutIPCDialog() {
    375   AboutIPCDialog::RunDialog();
    376 }
    377 
    378 } // namespace browser
    379 
    380 #endif  // IPC_MESSAGE_LOG_ENABLED
    381