Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / hang_monitor / hung_plugin_action.cc
blob4ce6f14e38498365229177b12a131f894a53dbee
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.
5 #include <windows.h>
7 #include "chrome/browser/hang_monitor/hung_plugin_action.h"
9 #include "base/metrics/histogram.h"
10 #include "base/version.h"
11 #include "chrome/browser/ui/views/simple_message_box_win.h"
12 #include "chrome/common/logging_chrome.h"
13 #include "content/public/browser/plugin_service.h"
14 #include "content/public/common/webplugininfo.h"
15 #include "grit/generated_resources.h"
16 #include "ui/base/l10n/l10n_util.h"
17 #include "ui/gfx/win/hwnd_util.h"
19 namespace {
21 const wchar_t kGTalkPluginName[] = L"Google Talk Plugin";
22 const int kGTalkPluginLogMinVersion = 26; // For version 2.6 and below.
24 enum GTalkPluginLogVersion {
25 GTALK_PLUGIN_VERSION_MIN = 0,
26 GTALK_PLUGIN_VERSION_27,
27 GTALK_PLUGIN_VERSION_28,
28 GTALK_PLUGIN_VERSION_29,
29 GTALK_PLUGIN_VERSION_30,
30 GTALK_PLUGIN_VERSION_31,
31 GTALK_PLUGIN_VERSION_32,
32 GTALK_PLUGIN_VERSION_33,
33 GTALK_PLUGIN_VERSION_34,
34 GTALK_PLUGIN_VERSION_MAX
37 // Converts the version string of Google Talk Plugin to a version enum. The
38 // version format is "major(1 digit).minor(1 digit).sub(1 or 2 digits)",
39 // for example, "2.7.10" and "2.8.1". Converts the string to a number as
40 // 10 * major + minor - kGTalkPluginLogMinVersion.
41 GTalkPluginLogVersion GetGTalkPluginVersion(const base::string16& version) {
42 int gtalk_plugin_version = GTALK_PLUGIN_VERSION_MIN;
43 Version plugin_version;
44 content::WebPluginInfo::CreateVersionFromString(version, &plugin_version);
45 if (plugin_version.IsValid() && plugin_version.components().size() >= 2) {
46 gtalk_plugin_version = 10 * plugin_version.components()[0] +
47 plugin_version.components()[1] - kGTalkPluginLogMinVersion;
50 if (gtalk_plugin_version < GTALK_PLUGIN_VERSION_MIN)
51 return GTALK_PLUGIN_VERSION_MIN;
52 if (gtalk_plugin_version > GTALK_PLUGIN_VERSION_MAX)
53 return GTALK_PLUGIN_VERSION_MAX;
54 return static_cast<GTalkPluginLogVersion>(gtalk_plugin_version);
57 } // namespace
59 HungPluginAction::HungPluginAction() : current_hung_plugin_window_(NULL) {
62 HungPluginAction::~HungPluginAction() {
65 bool HungPluginAction::OnHungWindowDetected(HWND hung_window,
66 HWND top_level_window,
67 ActionOnHungWindow* action) {
68 if (NULL == action) {
69 return false;
71 if (!IsWindow(hung_window)) {
72 return false;
75 bool continue_hang_detection = true;
77 DWORD hung_window_process_id = 0;
78 DWORD top_level_window_process_id = 0;
79 GetWindowThreadProcessId(hung_window, &hung_window_process_id);
80 GetWindowThreadProcessId(top_level_window, &top_level_window_process_id);
82 *action = HungWindowNotification::HUNG_WINDOW_IGNORE;
83 if (top_level_window_process_id != hung_window_process_id) {
84 base::string16 plugin_name;
85 base::string16 plugin_version;
86 GetPluginNameAndVersion(hung_window,
87 top_level_window_process_id,
88 &plugin_name,
89 &plugin_version);
90 if (plugin_name.empty()) {
91 plugin_name = l10n_util::GetStringUTF16(IDS_UNKNOWN_PLUGIN_NAME);
92 } else if (kGTalkPluginName == plugin_name) {
93 UMA_HISTOGRAM_ENUMERATION("GTalkPlugin.Hung",
94 GetGTalkPluginVersion(plugin_version),
95 GTALK_PLUGIN_VERSION_MAX + 1);
98 if (logging::DialogsAreSuppressed()) {
99 NOTREACHED() << "Terminated a hung plugin process.";
100 *action = HungWindowNotification::HUNG_WINDOW_TERMINATE_PROCESS;
101 } else {
102 const base::string16 title = l10n_util::GetStringUTF16(
103 IDS_BROWSER_HANGMONITOR_TITLE);
104 const base::string16 message = l10n_util::GetStringFUTF16(
105 IDS_BROWSER_HANGMONITOR, plugin_name);
106 // Before displaying the message box, invoke SendMessageCallback on the
107 // hung window. If the callback ever hits, the window is not hung anymore
108 // and we can dismiss the message box.
109 SendMessageCallback(hung_window,
110 WM_NULL,
113 HungWindowResponseCallback,
114 reinterpret_cast<ULONG_PTR>(this));
115 current_hung_plugin_window_ = hung_window;
116 // We use chrome::NativeShowMessageBox instead of chrome::ShowMessageBox
117 // because the latter depends on UI-thread classes on Win Aura. See
118 // http://crbug.com/330424.
119 if (chrome::NativeShowMessageBox(
120 NULL, title, message, chrome::MESSAGE_BOX_TYPE_QUESTION) ==
121 chrome::MESSAGE_BOX_RESULT_YES) {
122 *action = HungWindowNotification::HUNG_WINDOW_TERMINATE_PROCESS;
123 } else {
124 // If the user choses to ignore the hung window warning, the
125 // message timeout for this window should be doubled. We only
126 // double the timeout property on the window if the property
127 // exists. The property is deleted if the window becomes
128 // responsive.
129 continue_hang_detection = false;
130 #pragma warning(disable:4311)
131 int child_window_message_timeout =
132 reinterpret_cast<int>(GetProp(
133 hung_window, HungWindowDetector::kHungChildWindowTimeout));
134 #pragma warning(default:4311)
135 if (child_window_message_timeout) {
136 child_window_message_timeout *= 2;
137 #pragma warning(disable:4312)
138 SetProp(hung_window, HungWindowDetector::kHungChildWindowTimeout,
139 reinterpret_cast<HANDLE>(child_window_message_timeout));
140 #pragma warning(default:4312)
143 current_hung_plugin_window_ = NULL;
146 if (HungWindowNotification::HUNG_WINDOW_TERMINATE_PROCESS == *action) {
147 // Enable the top-level window just in case the plugin had been
148 // displaying a modal box that had disabled the top-level window
149 EnableWindow(top_level_window, TRUE);
151 return continue_hang_detection;
154 void HungPluginAction::OnWindowResponsive(HWND window) {
155 if (window == current_hung_plugin_window_) {
156 // The message timeout for this window should fallback to the default
157 // timeout as this window is now responsive.
158 RemoveProp(window, HungWindowDetector::kHungChildWindowTimeout);
159 // The monitored plugin recovered. Let's dismiss the message box.
160 EnumThreadWindows(GetCurrentThreadId(),
161 reinterpret_cast<WNDENUMPROC>(DismissMessageBox),
162 NULL);
166 bool HungPluginAction::GetPluginNameAndVersion(HWND plugin_window,
167 DWORD browser_process_id,
168 base::string16* plugin_name,
169 base::string16* plugin_version) {
170 DCHECK(plugin_name);
171 DCHECK(plugin_version);
172 HWND window_to_check = plugin_window;
173 while (NULL != window_to_check) {
174 DWORD process_id = 0;
175 GetWindowThreadProcessId(window_to_check, &process_id);
176 if (process_id == browser_process_id) {
177 // If we have reached a window the that belongs to the browser process
178 // we have gone too far.
179 return false;
181 if (content::PluginService::GetInstance()->GetPluginInfoFromWindow(
182 window_to_check, plugin_name, plugin_version)) {
183 return true;
185 window_to_check = GetParent(window_to_check);
187 return false;
190 // static
191 BOOL CALLBACK HungPluginAction::DismissMessageBox(HWND window, LPARAM ignore) {
192 base::string16 class_name = gfx::GetClassName(window);
193 // #32770 is the dialog window class which is the window class of
194 // the message box being displayed.
195 if (class_name == L"#32770") {
196 EndDialog(window, IDNO);
197 return FALSE;
199 return TRUE;
202 // static
203 void CALLBACK HungPluginAction::HungWindowResponseCallback(HWND target_window,
204 UINT message,
205 ULONG_PTR data,
206 LRESULT result) {
207 HungPluginAction* instance = reinterpret_cast<HungPluginAction*>(data);
208 DCHECK(NULL != instance);
209 if (NULL != instance) {
210 instance->OnWindowResponsive(target_window);