• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
HungPluginAction()16 HungPluginAction::HungPluginAction() : current_hung_plugin_window_(NULL) {
17 }
18 
~HungPluginAction()19 HungPluginAction::~HungPluginAction() {
20 }
21 
OnHungWindowDetected(HWND hung_window,HWND top_level_window,ActionOnHungWindow * action)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 
OnWindowResponsive(HWND window)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 
GetPluginName(HWND plugin_window,DWORD browser_process_id,std::wstring * plugin_name)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
DismissMessageBox(HWND window,LPARAM ignore)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
HungWindowResponseCallback(HWND target_window,UINT message,ULONG_PTR data,LRESULT result)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