blob: 75cf98ff15d9101f07013a34b591dd58b0015fb9 [file] [log] [blame]
// 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 "base/win/win_util.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"
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 =
l10n_util::GetStringUTF16(IDS_UNKNOWN_PLUGIN_NAME);
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::ShowQuestionMessageBox(NULL, title, message) ==
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;
int child_window_message_timeout = base::win::HandleToUint32(
GetProp(hung_window, HungWindowDetector::kHungChildWindowTimeout));
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);
}
}