OLD | NEW |
---|---|
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include <windows.h> | 5 #include <windows.h> |
6 | 6 |
7 #include "chrome/browser/hang_monitor/hung_plugin_action.h" | 7 #include "chrome/browser/hang_monitor/hung_plugin_action.h" |
8 | 8 |
9 #include "base/metrics/histogram.h" | |
10 #include "base/version.h" | |
9 #include "chrome/browser/simple_message_box.h" | 11 #include "chrome/browser/simple_message_box.h" |
10 #include "chrome/common/logging_chrome.h" | 12 #include "chrome/common/logging_chrome.h" |
11 #include "grit/generated_resources.h" | 13 #include "grit/generated_resources.h" |
12 #include "ui/base/l10n/l10n_util.h" | 14 #include "ui/base/l10n/l10n_util.h" |
13 #include "ui/base/win/hwnd_util.h" | 15 #include "ui/base/win/hwnd_util.h" |
16 #include "webkit/plugins/npapi/plugin_group.h" | |
14 #include "webkit/plugins/npapi/webplugin_delegate_impl.h" | 17 #include "webkit/plugins/npapi/webplugin_delegate_impl.h" |
15 | 18 |
19 namespace { | |
20 | |
21 const wchar_t kGTalkPluginName[] = L"Google Talk Plugin"; | |
22 const int kMaxGTalkPluginVersion = 10000; | |
23 | |
24 // Converts the version string of Google Talk Plugin to a number. The | |
25 // version format is "major(1 digit).minor(1 digit).sub(1 or 2 digits)", | |
26 // for example, "2.7.10" and "2.8.1". Converts the string to a number as | |
27 // 1000 * major + 100 * minor + sub. | |
28 int GetGTalkPluginVersion(const string16& version) { | |
29 int gtalk_plugin_version = 0; | |
30 scoped_ptr<Version> plugin_version( | |
31 webkit::npapi::PluginGroup::CreateVersionFromString(version)); | |
32 if (plugin_version.get() && plugin_version->components().size() >= 3) { | |
33 gtalk_plugin_version = plugin_version->components()[0] * 1000 + | |
34 plugin_version->components()[1] * 100 + | |
35 plugin_version->components()[2]; | |
36 if (gtalk_plugin_version > kMaxGTalkPluginVersion) { | |
37 gtalk_plugin_version = kMaxGTalkPluginVersion; | |
38 } | |
39 } | |
40 return gtalk_plugin_version; | |
41 } | |
42 | |
43 } // namespace | |
44 | |
16 HungPluginAction::HungPluginAction() : current_hung_plugin_window_(NULL) { | 45 HungPluginAction::HungPluginAction() : current_hung_plugin_window_(NULL) { |
17 } | 46 } |
18 | 47 |
19 HungPluginAction::~HungPluginAction() { | 48 HungPluginAction::~HungPluginAction() { |
20 } | 49 } |
21 | 50 |
22 bool HungPluginAction::OnHungWindowDetected(HWND hung_window, | 51 bool HungPluginAction::OnHungWindowDetected(HWND hung_window, |
23 HWND top_level_window, | 52 HWND top_level_window, |
24 ActionOnHungWindow* action) { | 53 ActionOnHungWindow* action) { |
25 if (NULL == action) { | 54 if (NULL == action) { |
26 return false; | 55 return false; |
27 } | 56 } |
28 if (!IsWindow(hung_window)) { | 57 if (!IsWindow(hung_window)) { |
29 return false; | 58 return false; |
30 } | 59 } |
31 | 60 |
32 bool continue_hang_detection = true; | 61 bool continue_hang_detection = true; |
33 | 62 |
34 DWORD hung_window_process_id = 0; | 63 DWORD hung_window_process_id = 0; |
35 DWORD top_level_window_process_id = 0; | 64 DWORD top_level_window_process_id = 0; |
36 GetWindowThreadProcessId(hung_window, &hung_window_process_id); | 65 GetWindowThreadProcessId(hung_window, &hung_window_process_id); |
37 GetWindowThreadProcessId(top_level_window, &top_level_window_process_id); | 66 GetWindowThreadProcessId(top_level_window, &top_level_window_process_id); |
38 | 67 |
68 bool is_gtalk_plugin = false; | |
69 int gtalk_plugin_version = 0; | |
39 *action = HungWindowNotification::HUNG_WINDOW_IGNORE; | 70 *action = HungWindowNotification::HUNG_WINDOW_IGNORE; |
40 if (top_level_window_process_id != hung_window_process_id) { | 71 if (top_level_window_process_id != hung_window_process_id) { |
72 string16 plugin_name; | |
73 string16 plugin_version; | |
74 GetPluginNameAndVersion(hung_window, | |
75 top_level_window_process_id, | |
76 &plugin_name, | |
77 &plugin_version); | |
78 if (plugin_name.empty()) { | |
79 plugin_name = l10n_util::GetStringUTF16(IDS_UNKNOWN_PLUGIN_NAME); | |
80 } | |
81 | |
82 is_gtalk_plugin = (kGTalkPluginName == plugin_name); | |
83 if (is_gtalk_plugin) { | |
84 gtalk_plugin_version = GetGTalkPluginVersion(plugin_version); | |
85 } | |
86 | |
41 if (logging::DialogsAreSuppressed()) { | 87 if (logging::DialogsAreSuppressed()) { |
42 NOTREACHED() << "Terminated a hung plugin process."; | 88 NOTREACHED() << "Terminated a hung plugin process."; |
43 *action = HungWindowNotification::HUNG_WINDOW_TERMINATE_PROCESS; | 89 *action = HungWindowNotification::HUNG_WINDOW_TERMINATE_PROCESS; |
44 } else { | 90 } 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, | 91 string16 msg = l10n_util::GetStringFUTF16(IDS_BROWSER_HANGMONITOR, |
53 plugin_name); | 92 plugin_name); |
54 string16 title = l10n_util::GetStringUTF16(IDS_BROWSER_HANGMONITOR_TITLE); | 93 string16 title = l10n_util::GetStringUTF16(IDS_BROWSER_HANGMONITOR_TITLE); |
55 // Before displaying the message box, invoke SendMessageCallback on the | 94 // Before displaying the message box, invoke SendMessageCallback on the |
56 // hung window. If the callback ever hits, the window is not hung anymore | 95 // hung window. If the callback ever hits, the window is not hung anymore |
57 // and we can dismiss the message box. | 96 // and we can dismiss the message box. |
58 SendMessageCallback(hung_window, | 97 SendMessageCallback(hung_window, |
59 WM_NULL, | 98 WM_NULL, |
60 0, | 99 0, |
61 0, | 100 0, |
(...skipping 22 matching lines...) Expand all Loading... | |
84 #pragma warning(default:4312) | 123 #pragma warning(default:4312) |
85 } | 124 } |
86 } | 125 } |
87 current_hung_plugin_window_ = NULL; | 126 current_hung_plugin_window_ = NULL; |
88 } | 127 } |
89 } | 128 } |
90 if (HungWindowNotification::HUNG_WINDOW_TERMINATE_PROCESS == *action) { | 129 if (HungWindowNotification::HUNG_WINDOW_TERMINATE_PROCESS == *action) { |
91 // Enable the top-level window just in case the plugin had been | 130 // Enable the top-level window just in case the plugin had been |
92 // displaying a modal box that had disabled the top-level window | 131 // displaying a modal box that had disabled the top-level window |
93 EnableWindow(top_level_window, TRUE); | 132 EnableWindow(top_level_window, TRUE); |
133 | |
134 UMA_HISTOGRAM_COUNTS("Plugin.Hung_Terminate_Process", 1); | |
135 if (is_gtalk_plugin) { | |
136 UMA_HISTOGRAM_ENUMERATION("GTalkPlugin.Hung_Terminate_Process", | |
jar (doing other things)
2012/04/03 19:20:39
We don't have a sparse histogram, and so the cost
| |
137 gtalk_plugin_version, kMaxGTalkPluginVersion + 1); | |
138 } | |
139 } else { | |
140 UMA_HISTOGRAM_COUNTS("Plugin.Hung_Ignore", 1); | |
141 if (is_gtalk_plugin) { | |
142 UMA_HISTOGRAM_ENUMERATION("GTalkPlugin.Hung_Ignore", | |
143 gtalk_plugin_version, kMaxGTalkPluginVersion + 1); | |
jar (doing other things)
2012/04/03 19:20:39
This would be another 120KB :-/.
| |
144 } | |
94 } | 145 } |
95 return continue_hang_detection; | 146 return continue_hang_detection; |
96 } | 147 } |
97 | 148 |
98 void HungPluginAction::OnWindowResponsive(HWND window) { | 149 void HungPluginAction::OnWindowResponsive(HWND window) { |
99 if (window == current_hung_plugin_window_) { | 150 if (window == current_hung_plugin_window_) { |
100 // The message timeout for this window should fallback to the default | 151 // The message timeout for this window should fallback to the default |
101 // timeout as this window is now responsive. | 152 // timeout as this window is now responsive. |
102 RemoveProp(window, HungWindowDetector::kHungChildWindowTimeout); | 153 RemoveProp(window, HungWindowDetector::kHungChildWindowTimeout); |
103 // The monitored plugin recovered. Let's dismiss the message box. | 154 // The monitored plugin recovered. Let's dismiss the message box. |
104 EnumThreadWindows(GetCurrentThreadId(), | 155 EnumThreadWindows(GetCurrentThreadId(), |
105 reinterpret_cast<WNDENUMPROC>(DismissMessageBox), | 156 reinterpret_cast<WNDENUMPROC>(DismissMessageBox), |
106 NULL); | 157 NULL); |
107 } | 158 } |
108 } | 159 } |
109 | 160 |
110 bool HungPluginAction::GetPluginName(HWND plugin_window, | 161 bool HungPluginAction::GetPluginNameAndVersion(HWND plugin_window, |
111 DWORD browser_process_id, | 162 DWORD browser_process_id, |
112 std::wstring* plugin_name) { | 163 std::wstring* plugin_name, |
164 std::wstring* plugin_version) { | |
113 DCHECK(plugin_name); | 165 DCHECK(plugin_name); |
166 DCHECK(plugin_version); | |
114 HWND window_to_check = plugin_window; | 167 HWND window_to_check = plugin_window; |
115 while (NULL != window_to_check) { | 168 while (NULL != window_to_check) { |
116 DWORD process_id = 0; | 169 DWORD process_id = 0; |
117 GetWindowThreadProcessId(window_to_check, &process_id); | 170 GetWindowThreadProcessId(window_to_check, &process_id); |
118 if (process_id == browser_process_id) { | 171 if (process_id == browser_process_id) { |
119 // If we have reached a window the that belongs to the browser process | 172 // If we have reached a window the that belongs to the browser process |
120 // we have gone too far. | 173 // we have gone too far. |
121 return false; | 174 return false; |
122 } | 175 } |
123 if (webkit::npapi::WebPluginDelegateImpl::GetPluginNameFromWindow( | 176 if (webkit::npapi::WebPluginDelegateImpl::GetPluginNameFromWindow( |
124 window_to_check, plugin_name)) { | 177 window_to_check, plugin_name)) { |
178 webkit::npapi::WebPluginDelegateImpl::GetPluginVersionFromWindow( | |
179 window_to_check, plugin_version); | |
125 return true; | 180 return true; |
126 } | 181 } |
127 window_to_check = GetParent(window_to_check); | 182 window_to_check = GetParent(window_to_check); |
128 } | 183 } |
129 return false; | 184 return false; |
130 } | 185 } |
131 | 186 |
132 // static | 187 // static |
133 BOOL CALLBACK HungPluginAction::DismissMessageBox(HWND window, LPARAM ignore) { | 188 BOOL CALLBACK HungPluginAction::DismissMessageBox(HWND window, LPARAM ignore) { |
134 string16 class_name = ui::GetClassName(window); | 189 string16 class_name = ui::GetClassName(window); |
(...skipping 10 matching lines...) Expand all Loading... | |
145 void CALLBACK HungPluginAction::HungWindowResponseCallback(HWND target_window, | 200 void CALLBACK HungPluginAction::HungWindowResponseCallback(HWND target_window, |
146 UINT message, | 201 UINT message, |
147 ULONG_PTR data, | 202 ULONG_PTR data, |
148 LRESULT result) { | 203 LRESULT result) { |
149 HungPluginAction* instance = reinterpret_cast<HungPluginAction*>(data); | 204 HungPluginAction* instance = reinterpret_cast<HungPluginAction*>(data); |
150 DCHECK(NULL != instance); | 205 DCHECK(NULL != instance); |
151 if (NULL != instance) { | 206 if (NULL != instance) { |
152 instance->OnWindowResponsive(target_window); | 207 instance->OnWindowResponsive(target_window); |
153 } | 208 } |
154 } | 209 } |
OLD | NEW |