1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
|
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <windows.h>
#include "chrome/browser/hang_monitor/hung_plugin_action.h"
#include "base/metrics/histogram.h"
#include "base/version.h"
#include "chrome/browser/ui/simple_message_box.h"
#include "chrome/common/logging_chrome.h"
#include "chrome/grit/generated_resources.h"
#include "content/public/browser/plugin_service.h"
#include "content/public/common/webplugininfo.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/win/hwnd_util.h"
namespace {
const wchar_t kGTalkPluginName[] = L"Google Talk Plugin";
const int kGTalkPluginLogMinVersion = 26; // For version 2.6 and below.
enum GTalkPluginLogVersion {
GTALK_PLUGIN_VERSION_MIN = 0,
GTALK_PLUGIN_VERSION_27,
GTALK_PLUGIN_VERSION_28,
GTALK_PLUGIN_VERSION_29,
GTALK_PLUGIN_VERSION_30,
GTALK_PLUGIN_VERSION_31,
GTALK_PLUGIN_VERSION_32,
GTALK_PLUGIN_VERSION_33,
GTALK_PLUGIN_VERSION_34,
GTALK_PLUGIN_VERSION_MAX
};
// Converts the version string of Google Talk Plugin to a version enum. The
// version format is "major(1 digit).minor(1 digit).sub(1 or 2 digits)",
// for example, "2.7.10" and "2.8.1". Converts the string to a number as
// 10 * major + minor - kGTalkPluginLogMinVersion.
GTalkPluginLogVersion GetGTalkPluginVersion(const base::string16& version) {
int gtalk_plugin_version = GTALK_PLUGIN_VERSION_MIN;
Version plugin_version;
content::WebPluginInfo::CreateVersionFromString(version, &plugin_version);
if (plugin_version.IsValid() && plugin_version.components().size() >= 2) {
gtalk_plugin_version = 10 * plugin_version.components()[0] +
plugin_version.components()[1] - kGTalkPluginLogMinVersion;
}
if (gtalk_plugin_version < GTALK_PLUGIN_VERSION_MIN)
return GTALK_PLUGIN_VERSION_MIN;
if (gtalk_plugin_version > GTALK_PLUGIN_VERSION_MAX)
return GTALK_PLUGIN_VERSION_MAX;
return static_cast<GTalkPluginLogVersion>(gtalk_plugin_version);
}
} // namespace
HungPluginAction::HungPluginAction() : current_hung_plugin_window_(NULL) {
}
HungPluginAction::~HungPluginAction() {
}
bool HungPluginAction::OnHungWindowDetected(HWND hung_window,
HWND top_level_window,
ActionOnHungWindow* action) {
if (NULL == action) {
return false;
}
if (!IsWindow(hung_window)) {
return false;
}
bool continue_hang_detection = true;
DWORD hung_window_process_id = 0;
DWORD top_level_window_process_id = 0;
GetWindowThreadProcessId(hung_window, &hung_window_process_id);
GetWindowThreadProcessId(top_level_window, &top_level_window_process_id);
*action = HungWindowNotification::HUNG_WINDOW_IGNORE;
if (top_level_window_process_id != hung_window_process_id) {
base::string16 plugin_name;
base::string16 plugin_version;
content::PluginService::GetInstance()->GetPluginInfoFromWindow(
hung_window, &plugin_name, &plugin_version);
if (plugin_name.empty()) {
plugin_name = l10n_util::GetStringUTF16(IDS_UNKNOWN_PLUGIN_NAME);
} else if (kGTalkPluginName == plugin_name) {
UMA_HISTOGRAM_ENUMERATION("GTalkPlugin.Hung",
GetGTalkPluginVersion(plugin_version),
GTALK_PLUGIN_VERSION_MAX + 1);
}
if (logging::DialogsAreSuppressed()) {
NOTREACHED() << "Terminated a hung plugin process.";
*action = HungWindowNotification::HUNG_WINDOW_TERMINATE_PROCESS;
} else {
const base::string16 title = l10n_util::GetStringUTF16(
IDS_BROWSER_HANGMONITOR_TITLE);
const base::string16 message = l10n_util::GetStringFUTF16(
IDS_BROWSER_HANGMONITOR, plugin_name);
// Before displaying the message box, invoke SendMessageCallback on the
// hung window. If the callback ever hits, the window is not hung anymore
// and we can dismiss the message box.
SendMessageCallback(hung_window,
WM_NULL,
0,
0,
HungWindowResponseCallback,
reinterpret_cast<ULONG_PTR>(this));
current_hung_plugin_window_ = hung_window;
if (chrome::ShowMessageBox(
NULL, title, message, chrome::MESSAGE_BOX_TYPE_QUESTION) ==
chrome::MESSAGE_BOX_RESULT_YES) {
*action = HungWindowNotification::HUNG_WINDOW_TERMINATE_PROCESS;
} else {
// If the user choses to ignore the hung window warning, the
// message timeout for this window should be doubled. We only
// double the timeout property on the window if the property
// exists. The property is deleted if the window becomes
// responsive.
continue_hang_detection = false;
#pragma warning(disable:4311)
int child_window_message_timeout =
reinterpret_cast<int>(GetProp(
hung_window, HungWindowDetector::kHungChildWindowTimeout));
#pragma warning(default:4311)
if (child_window_message_timeout) {
child_window_message_timeout *= 2;
#pragma warning(disable:4312)
SetProp(hung_window, HungWindowDetector::kHungChildWindowTimeout,
reinterpret_cast<HANDLE>(child_window_message_timeout));
#pragma warning(default:4312)
}
}
current_hung_plugin_window_ = NULL;
}
}
if (HungWindowNotification::HUNG_WINDOW_TERMINATE_PROCESS == *action) {
// Enable the top-level window just in case the plugin had been
// displaying a modal box that had disabled the top-level window
EnableWindow(top_level_window, TRUE);
}
return continue_hang_detection;
}
void HungPluginAction::OnWindowResponsive(HWND window) {
if (window == current_hung_plugin_window_) {
// The message timeout for this window should fallback to the default
// timeout as this window is now responsive.
RemoveProp(window, HungWindowDetector::kHungChildWindowTimeout);
// The monitored plugin recovered. Let's dismiss the message box.
EnumThreadWindows(GetCurrentThreadId(),
reinterpret_cast<WNDENUMPROC>(DismissMessageBox),
NULL);
}
}
// static
BOOL CALLBACK HungPluginAction::DismissMessageBox(HWND window, LPARAM ignore) {
base::string16 class_name = gfx::GetClassName(window);
// #32770 is the dialog window class which is the window class of
// the message box being displayed.
if (class_name == L"#32770") {
EndDialog(window, IDNO);
return FALSE;
}
return TRUE;
}
// static
void CALLBACK HungPluginAction::HungWindowResponseCallback(HWND target_window,
UINT message,
ULONG_PTR data,
LRESULT result) {
HungPluginAction* instance = reinterpret_cast<HungPluginAction*>(data);
DCHECK(NULL != instance);
if (NULL != instance) {
instance->OnWindowResponsive(target_window);
}
}
|