Home | History | Annotate | Download | only in hang_monitor
      1 // Copyright (c) 2006-2008 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 #include <windows.h>
      6 
      7 #include "chrome/browser/hang_monitor/hung_plugin_action.h"
      8 
      9 #include "chrome/browser/platform_util.h"
     10 #include "chrome/common/logging_chrome.h"
     11 #include "grit/generated_resources.h"
     12 #include "ui/base/l10n/l10n_util.h"
     13 #include "ui/base/win/hwnd_util.h"
     14 #include "webkit/plugins/npapi/webplugin_delegate_impl.h"
     15 
     16 HungPluginAction::HungPluginAction() : current_hung_plugin_window_(NULL) {
     17 }
     18 
     19 HungPluginAction::~HungPluginAction() {
     20 }
     21 
     22 bool HungPluginAction::OnHungWindowDetected(HWND hung_window,
     23                                             HWND top_level_window,
     24                                             ActionOnHungWindow* action) {
     25   if (NULL == action) {
     26     return false;
     27   }
     28   if (!IsWindow(hung_window)) {
     29     return false;
     30   }
     31 
     32   bool continue_hang_detection = true;
     33 
     34   DWORD hung_window_process_id = 0;
     35   DWORD top_level_window_process_id = 0;
     36   GetWindowThreadProcessId(hung_window, &hung_window_process_id);
     37   GetWindowThreadProcessId(top_level_window, &top_level_window_process_id);
     38 
     39   *action = HungWindowNotification::HUNG_WINDOW_IGNORE;
     40   if (top_level_window_process_id != hung_window_process_id) {
     41     if (logging::DialogsAreSuppressed()) {
     42       NOTREACHED() << "Terminated a hung plugin process.";
     43       *action = HungWindowNotification::HUNG_WINDOW_TERMINATE_PROCESS;
     44     } else {
     45       string16 plugin_name;
     46       GetPluginName(hung_window,
     47                     top_level_window_process_id,
     48                     &plugin_name);
     49       if (plugin_name.empty()) {
     50         plugin_name = l10n_util::GetStringUTF16(IDS_UNKNOWN_PLUGIN_NAME);
     51       }
     52       string16 msg = l10n_util::GetStringFUTF16(IDS_BROWSER_HANGMONITOR,
     53                                                 plugin_name);
     54       string16 title = l10n_util::GetStringUTF16(IDS_BROWSER_HANGMONITOR_TITLE);
     55       // Before displaying the message box, invoke SendMessageCallback on the
     56       // hung window. If the callback ever hits, the window is not hung anymore
     57       // and we can dismiss the message box.
     58       SendMessageCallback(hung_window,
     59                           WM_NULL,
     60                           0,
     61                           0,
     62                           HungWindowResponseCallback,
     63                           reinterpret_cast<ULONG_PTR>(this));
     64       current_hung_plugin_window_ = hung_window;
     65       if (platform_util::SimpleYesNoBox(NULL, title, msg)) {
     66         *action = HungWindowNotification::HUNG_WINDOW_TERMINATE_PROCESS;
     67       } else {
     68         // If the user choses to ignore the hung window warning, the
     69         // message timeout for this window should be doubled. We only
     70         // double the timeout property on the window if the property
     71         // exists. The property is deleted if the window becomes
     72         // responsive.
     73         continue_hang_detection = false;
     74 #pragma warning(disable:4311)
     75         int child_window_message_timeout =
     76             reinterpret_cast<int>(GetProp(
     77                 hung_window, HungWindowDetector::kHungChildWindowTimeout));
     78 #pragma warning(default:4311)
     79         if (child_window_message_timeout) {
     80           child_window_message_timeout *= 2;
     81 #pragma warning(disable:4312)
     82           // TODO: this leaks.
     83           SetProp(hung_window, HungWindowDetector::kHungChildWindowTimeout,
     84                   reinterpret_cast<HANDLE>(child_window_message_timeout));
     85 #pragma warning(default:4312)
     86         }
     87       }
     88       current_hung_plugin_window_ = NULL;
     89     }
     90   }
     91   if (HungWindowNotification::HUNG_WINDOW_TERMINATE_PROCESS == *action) {
     92     // Enable the top-level window just in case the plugin had been
     93     // displaying a modal box that had disabled the top-level window
     94     EnableWindow(top_level_window, TRUE);
     95   }
     96   return continue_hang_detection;
     97 }
     98 
     99 void HungPluginAction::OnWindowResponsive(HWND window) {
    100   if (window == current_hung_plugin_window_) {
    101     // The message timeout for this window should fallback to the default
    102     // timeout as this window is now responsive.
    103     RemoveProp(window, HungWindowDetector::kHungChildWindowTimeout);
    104     // The monitored plugin recovered. Let's dismiss the message box.
    105     EnumThreadWindows(GetCurrentThreadId(),
    106                       reinterpret_cast<WNDENUMPROC>(DismissMessageBox),
    107                       NULL);
    108   }
    109 }
    110 
    111 bool HungPluginAction::GetPluginName(HWND plugin_window,
    112                                      DWORD browser_process_id,
    113                                      std::wstring* plugin_name) {
    114   DCHECK(plugin_name);
    115   HWND window_to_check = plugin_window;
    116   while (NULL != window_to_check) {
    117     DWORD process_id = 0;
    118     GetWindowThreadProcessId(window_to_check, &process_id);
    119     if (process_id == browser_process_id) {
    120       // If we have reached a window the that belongs to the browser process
    121       // we have gone too far.
    122       return false;
    123     }
    124     if (webkit::npapi::WebPluginDelegateImpl::GetPluginNameFromWindow(
    125             window_to_check, plugin_name)) {
    126       return true;
    127     }
    128     window_to_check = GetParent(window_to_check);
    129   }
    130   return false;
    131 }
    132 
    133 // static
    134 BOOL CALLBACK HungPluginAction::DismissMessageBox(HWND window, LPARAM ignore) {
    135   string16 class_name = ui::GetClassName(window);
    136   // #32770 is the dialog window class which is the window class of
    137   // the message box being displayed.
    138   if (class_name == L"#32770") {
    139     EndDialog(window, IDNO);
    140     return FALSE;
    141   }
    142   return TRUE;
    143 }
    144 
    145 // static
    146 void CALLBACK HungPluginAction::HungWindowResponseCallback(HWND target_window,
    147                                                            UINT message,
    148                                                            ULONG_PTR data,
    149                                                            LRESULT result) {
    150   HungPluginAction* instance = reinterpret_cast<HungPluginAction*>(data);
    151   DCHECK(NULL != instance);
    152   if (NULL != instance) {
    153     instance->OnWindowResponsive(target_window);
    154   }
    155 }
    156