summaryrefslogtreecommitdiffstats
path: root/chrome/browser/hang_monitor
diff options
context:
space:
mode:
authorinitial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98>2008-07-26 23:55:29 +0000
committerinitial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98>2008-07-26 23:55:29 +0000
commit09911bf300f1a419907a9412154760efd0b7abc3 (patch)
treef131325fb4e2ad12c6d3504ab75b16dd92facfed /chrome/browser/hang_monitor
parent586acc5fe142f498261f52c66862fa417c3d52d2 (diff)
downloadchromium_src-09911bf300f1a419907a9412154760efd0b7abc3.zip
chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.gz
chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.bz2
Add chrome to the repository.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@15 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/hang_monitor')
-rw-r--r--chrome/browser/hang_monitor/hung_plugin_action.cc185
-rw-r--r--chrome/browser/hang_monitor/hung_plugin_action.h71
-rw-r--r--chrome/browser/hang_monitor/hung_window_detector.cc201
-rw-r--r--chrome/browser/hang_monitor/hung_window_detector.h116
4 files changed, 573 insertions, 0 deletions
diff --git a/chrome/browser/hang_monitor/hung_plugin_action.cc b/chrome/browser/hang_monitor/hung_plugin_action.cc
new file mode 100644
index 0000000..34781bb
--- /dev/null
+++ b/chrome/browser/hang_monitor/hung_plugin_action.cc
@@ -0,0 +1,185 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <windows.h>
+
+#include "chrome/browser/hang_monitor/hung_plugin_action.h"
+
+#include "base/win_util.h"
+#include "chrome/common/l10n_util.h"
+#include "chrome/common/logging_chrome.h"
+#include "chrome/common/win_util.h"
+#include "webkit/glue/plugins/webplugin_delegate_impl.h"
+
+#include "generated_resources.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;
+
+ if (WebPluginDelegateImpl::IsDummyActivationWindow(hung_window)) {
+ return continue_hang_detection;
+ }
+
+ 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) {
+ if (logging::DialogsAreSuppressed()) {
+ NOTREACHED() << "Terminated a hung plugin process.";
+ *action = HungWindowNotification::HUNG_WINDOW_TERMINATE_PROCESS;
+ } else {
+ std::wstring plugin_name;
+ GetPluginName(hung_window,
+ top_level_window_process_id,
+ &plugin_name);
+ if (plugin_name.empty()) {
+ plugin_name = l10n_util::GetString(IDS_UNKNOWN_PLUGIN_NAME);
+ }
+ std::wstring msg = l10n_util::GetStringF(IDS_BROWSER_HANGMONITOR,
+ plugin_name);
+ std::wstring title = l10n_util::GetString(IDS_BROWSER_HANGMONITOR_TITLE);
+ // 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;
+ const UINT mb_flags = MB_YESNO | MB_ICONQUESTION | MB_SETFOREGROUND;
+ if (IDYES == win_util::MessageBox(NULL, msg, title, mb_flags)) {
+ *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);
+ }
+}
+
+bool HungPluginAction::GetPluginName(HWND plugin_window,
+ DWORD browser_process_id,
+ std::wstring* plugin_name) {
+ DCHECK(plugin_name);
+ HWND window_to_check = plugin_window;
+ while (NULL != window_to_check) {
+ DWORD process_id = 0;
+ GetWindowThreadProcessId(window_to_check, &process_id);
+ if (process_id == browser_process_id) {
+ // If we have reached a window the that belongs to the browser process
+ // we have gone too far.
+ return false;
+ }
+ if (WebPluginDelegateImpl::GetPluginNameFromWindow(window_to_check,
+ plugin_name)) {
+ return true;
+ }
+ window_to_check = GetParent(window_to_check);
+ }
+ return false;
+}
+
+// static
+BOOL CALLBACK HungPluginAction::DismissMessageBox(HWND window, LPARAM ignore) {
+ std::wstring class_name = win_util::GetClassNameW(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);
+ }
+}
diff --git a/chrome/browser/hang_monitor/hung_plugin_action.h b/chrome/browser/hang_monitor/hung_plugin_action.h
new file mode 100644
index 0000000..ac15a47
--- /dev/null
+++ b/chrome/browser/hang_monitor/hung_plugin_action.h
@@ -0,0 +1,71 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CHROME_BROWSER_HANG_MONITOR_HUNG_WINDOW_DETECTOR_H__
+#define CHROME_BROWSER_HANG_MONITOR_HUNG_WINDOW_DETECTOR_H__
+
+#include "chrome/browser/hang_monitor/hung_window_detector.h"
+// This class provides an implementation the
+// HungWindowDetector::HungWindowNotification callback interface.
+// It checks to see if the hung window belongs to a process different
+// from that of the browser process and, if so, it returns an action
+// of HungWindowNotification::HUNG_WINDOW_TERMINATE_PROCESS.
+// Note: We can write other action classes that implement the same
+// interface and switch the action done on hung plugins based on user
+// preferences.
+class HungPluginAction : public HungWindowDetector::HungWindowNotification {
+ public:
+ HungPluginAction();
+ ~HungPluginAction();
+ // HungWindowNotification implementation
+ virtual bool OnHungWindowDetected(HWND hung_window,
+ HWND top_level_window,
+ ActionOnHungWindow* action);
+
+ protected:
+ void OnWindowResponsive(HWND window);
+
+ // The callback function for the SendMessageCallback API
+ static void CALLBACK HungWindowResponseCallback(HWND target_window,
+ UINT message,
+ ULONG_PTR data,
+ LRESULT result);
+
+ static BOOL CALLBACK DismissMessageBox(HWND window, LPARAM ignore);
+
+ protected:
+ bool GetPluginName(HWND plugin_window,
+ DWORD browser_process_id,
+ std::wstring *plugin_name);
+ // The currently hung plugin window that we are prompting the user about
+ HWND current_hung_plugin_window_;
+};
+
+#endif // CHROME_BROWSER_HANG_MONITOR_HUNG_WINDOW_DETECTOR_H__
+
diff --git a/chrome/browser/hang_monitor/hung_window_detector.cc b/chrome/browser/hang_monitor/hung_window_detector.cc
new file mode 100644
index 0000000..653d0d3
--- /dev/null
+++ b/chrome/browser/hang_monitor/hung_window_detector.cc
@@ -0,0 +1,201 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "chrome/browser/hang_monitor/hung_window_detector.h"
+
+#include <windows.h>
+
+#include "base/logging.h"
+#include "chrome/app/result_codes.h"
+
+// How long do we wait for the terminated thread or process to die (in ms)
+static const int kTerminateTimeout = 2000;
+
+const wchar_t HungWindowDetector::kHungChildWindowTimeout[] =
+ L"Chrome_HungChildWindowTimeout";
+
+HungWindowDetector::HungWindowDetector(HungWindowNotification* notification)
+ : notification_(notification),
+ top_level_window_(NULL),
+ enumerating_(false) {
+ DCHECK(NULL != notification_);
+}
+// NOTE: It is the caller's responsibility to make sure that
+// callbacks on this object have been stopped before
+// destroying this object
+HungWindowDetector::~HungWindowDetector() {
+}
+
+bool HungWindowDetector::Initialize(HWND top_level_window,
+ int message_response_timeout) {
+ if (NULL == notification_) {
+ return false;
+ }
+ if (NULL == top_level_window) {
+ return false;
+ }
+ // It is OK to call Initialize on this object repeatedly
+ // with different top lebel HWNDs and timeout values each time.
+ // And we do not need a lock for this because we are just
+ // swapping DWORDs.
+ top_level_window_ = top_level_window;
+ message_response_timeout_ = message_response_timeout;
+ return true;
+}
+
+void HungWindowDetector::OnTick() {
+ do {
+ AutoLock lock(hang_detection_lock_);
+ // If we already are checking for hung windows on another thread,
+ // don't do this again.
+ if (enumerating_) {
+ return;
+ }
+ enumerating_ = true;
+ } while (false); // To scope the AutoLock
+
+ EnumChildWindows(top_level_window_, ChildWndEnumProc,
+ reinterpret_cast<LPARAM>(this));
+ enumerating_ = false;
+}
+
+bool HungWindowDetector::CheckChildWindow(HWND child_window) {
+ // It can happen that the window is DOA. It specifically happens
+ // when we have just killed a plugin process and the enum is still
+ // enumerating windows from that process.
+ if (!IsWindow(child_window)) {
+ return true;
+ }
+
+ DWORD top_level_window_thread_id =
+ GetWindowThreadProcessId(top_level_window_, NULL);
+
+ DWORD child_window_process_id = 0;
+ DWORD child_window_thread_id =
+ GetWindowThreadProcessId(child_window, &child_window_process_id);
+ bool continue_hang_detection = true;
+
+ if (top_level_window_thread_id != child_window_thread_id) {
+ // The message timeout for a child window starts of with a default
+ // value specified by the message_response_timeout_ member. It is
+ // tracked by a property on the child window.
+#pragma warning(disable:4311)
+ int child_window_message_timeout =
+ reinterpret_cast<int>(GetProp(child_window, kHungChildWindowTimeout));
+#pragma warning(default:4311)
+ if (!child_window_message_timeout) {
+ child_window_message_timeout = message_response_timeout_;
+ }
+
+ DWORD result = 0;
+ if ((0 == SendMessageTimeout(child_window,
+ WM_NULL,
+ 0,
+ 0,
+ SMTO_BLOCK,
+ child_window_message_timeout,
+ &result)) ||
+ IsHungAppWindow(child_window)) {
+ HungWindowNotification::ActionOnHungWindow action =
+ HungWindowNotification::HUNG_WINDOW_IGNORE;
+#pragma warning(disable:4312)
+ SetProp(child_window, kHungChildWindowTimeout,
+ reinterpret_cast<HANDLE>(child_window_message_timeout));
+#pragma warning(default:4312)
+ continue_hang_detection =
+ notification_->OnHungWindowDetected(child_window, top_level_window_,
+ &action);
+ // Make sure this window still a child of our top-level parent
+ if (!IsChild(top_level_window_, child_window)) {
+ return continue_hang_detection;
+ }
+
+ switch (action) {
+ case HungWindowNotification::HUNG_WINDOW_TERMINATE_THREAD: {
+ CHandle child_thread(OpenThread(THREAD_TERMINATE,
+ FALSE,
+ child_window_thread_id));
+ if (NULL == child_thread.m_h) {
+ break;
+ }
+ // Before swinging the axe, do some sanity checks to make
+ // sure this window still belongs to the same thread
+ if (GetWindowThreadProcessId(child_window, NULL) !=
+ child_window_thread_id) {
+ break;
+ }
+ TerminateThread(child_thread, 0);
+ WaitForSingleObject(child_thread, kTerminateTimeout);
+ child_thread.Close();
+ break;
+ }
+ case HungWindowNotification::HUNG_WINDOW_TERMINATE_PROCESS: {
+ CHandle child_process(OpenProcess(PROCESS_TERMINATE,
+ FALSE,
+ child_window_process_id));
+
+ if (NULL == child_process.m_h) {
+ break;
+ }
+ // Before swinging the axe, do some sanity checks to make
+ // sure this window still belongs to the same process
+ DWORD process_id_check = 0;
+ GetWindowThreadProcessId(child_window, &process_id_check);
+ if (process_id_check != child_window_process_id) {
+ break;
+ }
+ TerminateProcess(child_process, ResultCodes::HUNG);
+ WaitForSingleObject(child_process, kTerminateTimeout);
+ child_process.Close();
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+ } else {
+ RemoveProp(child_window, kHungChildWindowTimeout);
+ }
+ }
+
+ return continue_hang_detection;
+}
+
+BOOL CALLBACK HungWindowDetector::ChildWndEnumProc(HWND child_window,
+ LPARAM param) {
+ HungWindowDetector* detector_instance =
+ reinterpret_cast<HungWindowDetector*>(param);
+ if (NULL == detector_instance) {
+ NOTREACHED();
+ return FALSE;
+ }
+
+ return detector_instance->CheckChildWindow(child_window);
+}
+
diff --git a/chrome/browser/hang_monitor/hung_window_detector.h b/chrome/browser/hang_monitor/hung_window_detector.h
new file mode 100644
index 0000000..4de7beb
--- /dev/null
+++ b/chrome/browser/hang_monitor/hung_window_detector.h
@@ -0,0 +1,116 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CHROME_BROWSER_HUNG_WINDOW_DETECTOR_H__
+#define CHROME_BROWSER_HUNG_WINDOW_DETECTOR_H__
+
+#include "base/lock.h"
+#include "chrome/common/worker_thread_ticker.h"
+
+// This class provides the following functionality:
+// Given a top-level window handle, it enumerates all descendant windows
+// of that window and, on finding a window that belongs to a different
+// thread from that of the top-level window, it tests to see if that window
+// is responding to messages. It does this test by first calling the
+// IsHungAppWindow API and, additionally (since the IsHungAppWindow API
+// does not deal correctly with suspended threads), send a dummy message
+// (WM_NULL) to the window and verifies that the call does not timeout.
+// This class is typically used in conjunction with the WorkerThreadTicker
+// class so that the checking can happen on a periodic basis.
+// If a hung window is detected it calls back the specified implementation of
+// the HungWindowNotification interface. Currently this class only supports
+// a single callback but it can be extended later to support multiple
+// callbacks.
+class HungWindowDetector : public WorkerThreadTicker::Callback {
+ public:
+ // This property specifies the message timeout for a child window.
+ static const wchar_t kHungChildWindowTimeout[];
+ // This is the notification callback interface that is used to notify
+ // callers about a non-responsive window
+ class HungWindowNotification {
+ public:
+ enum ActionOnHungWindow {
+ HUNG_WINDOW_IGNORE,
+ HUNG_WINDOW_TERMINATE_THREAD,
+ HUNG_WINDOW_TERMINATE_PROCESS,
+ };
+
+ // This callback method is invoked when a hung window is detected.
+ // A return value of false indicates that we should stop enumerating the
+ // child windows of the browser window to check if they are hung.
+ virtual bool OnHungWindowDetected(HWND hung_window, HWND top_level_window,
+ ActionOnHungWindow* action) = 0;
+ };
+
+ // The notification object is not owned by this class. It is assumed that
+ // this pointer will be valid throughout the lifetime of this class.
+ // Ownership of this pointer is not transferred to this class.
+ // Note that the Initialize method needs to be called to initiate monitoring
+ // of hung windows.
+ HungWindowDetector(HungWindowNotification* notification);
+ ~HungWindowDetector();
+
+ // This method initialized the monitoring of hung windows. All descendant
+ // windows of the passed-in top-level window which belong to a thread
+ // different from that of the top-level window are monitored. The
+ // message_response_timeout parameter indicates how long this class must
+ // wait for a window to respond to a sent message before it is considered
+ // to be non-responsive.
+ // Initialize can be called multiple times to change the actual window to
+ // be monitored as well as the message response timeout
+ bool Initialize(HWND top_level_window,
+ int message_response_timeout);
+
+ // Implementation of the WorkerThreadTicker::Callback interface
+ virtual void OnTick();
+
+ private:
+ // Helper function that checks whether the specified child window is hung.
+ // If so, it invokes the HungWindowNotification interface implementation
+ bool CheckChildWindow(HWND child_window);
+
+ static BOOL CALLBACK ChildWndEnumProc(HWND child_window, LPARAM param);
+
+ // Pointer to the HungWindowNotification callback interface. This class does
+ // not RefCount this pointer and it is assumed that the pointer will be valid
+ // throughout the lifetime of this class.
+ HungWindowNotification* notification_;
+ HWND top_level_window_;
+
+ // How long do we wait before we consider a window hung (in ms)
+ int message_response_timeout_;
+ Lock hang_detection_lock_;
+ // Indicates if this object is currently enumerating hung windows
+ bool enumerating_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(HungWindowDetector);
+};
+
+
+#endif // CHROME_BROWSER_HUNG_WINDOW_DETECTOR_H__