MD Downloads: UI review feedback
[chromium-blink-merge.git] / chrome / browser / hang_monitor / hung_window_detector.cc
blobd58e36a47cfb6976f9fc537c9babda723e0c161d
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 "chrome/browser/hang_monitor/hung_window_detector.h"
7 #include <windows.h>
8 #include <atlbase.h>
10 #include "base/logging.h"
11 #include "chrome/browser/hang_monitor/hang_crash_dump_win.h"
12 #include "content/public/common/result_codes.h"
14 const wchar_t HungWindowDetector::kHungChildWindowTimeout[] =
15 L"Chrome_HungChildWindowTimeout";
17 HungWindowDetector::HungWindowDetector(HungWindowNotification* notification)
18 : notification_(notification),
19 top_level_window_(NULL),
20 message_response_timeout_(0),
21 enumerating_(false) {
22 DCHECK(NULL != notification_);
24 // NOTE: It is the caller's responsibility to make sure that
25 // callbacks on this object have been stopped before
26 // destroying this object
27 HungWindowDetector::~HungWindowDetector() {
30 bool HungWindowDetector::Initialize(HWND top_level_window,
31 int message_response_timeout) {
32 if (NULL == notification_) {
33 return false;
35 if (NULL == top_level_window) {
36 return false;
38 // It is OK to call Initialize on this object repeatedly
39 // with different top lebel HWNDs and timeout values each time.
40 // And we do not need a lock for this because we are just
41 // swapping DWORDs.
42 top_level_window_ = top_level_window;
43 message_response_timeout_ = message_response_timeout;
44 return true;
47 void HungWindowDetector::OnTick() {
48 do {
49 base::AutoLock lock(hang_detection_lock_);
50 // If we already are checking for hung windows on another thread,
51 // don't do this again.
52 if (enumerating_) {
53 return;
55 enumerating_ = true;
56 } while (false); // To scope the AutoLock
58 EnumChildWindows(top_level_window_, ChildWndEnumProc,
59 reinterpret_cast<LPARAM>(this));
61 // The window shouldn't be disabled unless we're showing a modal dialog.
62 // If we're not, then reenable the window.
63 if (!::IsWindowEnabled(top_level_window_) &&
64 !::GetWindow(top_level_window_, GW_ENABLEDPOPUP)) {
65 ::EnableWindow(top_level_window_, TRUE);
68 enumerating_ = false;
71 bool HungWindowDetector::CheckChildWindow(HWND child_window) {
72 // It can happen that the window is DOA. It specifically happens
73 // when we have just killed a plugin process and the enum is still
74 // enumerating windows from that process.
75 if (!IsWindow(child_window)) {
76 return true;
79 DWORD top_level_window_thread_id =
80 GetWindowThreadProcessId(top_level_window_, NULL);
82 DWORD child_window_process_id = 0;
83 DWORD child_window_thread_id =
84 GetWindowThreadProcessId(child_window, &child_window_process_id);
85 bool continue_hang_detection = true;
87 if (top_level_window_thread_id != child_window_thread_id) {
88 // The message timeout for a child window starts of with a default
89 // value specified by the message_response_timeout_ member. It is
90 // tracked by a property on the child window.
91 #pragma warning(disable:4311)
92 int child_window_message_timeout =
93 reinterpret_cast<int>(GetProp(child_window, kHungChildWindowTimeout));
94 #pragma warning(default:4311)
95 if (!child_window_message_timeout) {
96 child_window_message_timeout = message_response_timeout_;
99 DWORD_PTR result = 0;
100 if (0 == SendMessageTimeout(child_window,
101 WM_NULL,
104 SMTO_BLOCK,
105 child_window_message_timeout,
106 &result)) {
107 HungWindowNotification::ActionOnHungWindow action =
108 HungWindowNotification::HUNG_WINDOW_IGNORE;
109 #pragma warning(disable:4312)
110 SetProp(child_window, kHungChildWindowTimeout,
111 reinterpret_cast<HANDLE>(child_window_message_timeout));
112 #pragma warning(default:4312)
113 continue_hang_detection =
114 notification_->OnHungWindowDetected(child_window, top_level_window_,
115 &action);
116 // Make sure this window still a child of our top-level parent
117 if (!IsChild(top_level_window_, child_window)) {
118 return continue_hang_detection;
121 if (action == HungWindowNotification::HUNG_WINDOW_TERMINATE_PROCESS) {
122 RemoveProp(child_window, kHungChildWindowTimeout);
123 CHandle child_process(OpenProcess(PROCESS_ALL_ACCESS,
124 FALSE,
125 child_window_process_id));
127 if (NULL == child_process.m_h) {
128 return continue_hang_detection;
130 // Before swinging the axe, do some sanity checks to make
131 // sure this window still belongs to the same process
132 DWORD process_id_check = 0;
133 GetWindowThreadProcessId(child_window, &process_id_check);
134 if (process_id_check != child_window_process_id) {
135 return continue_hang_detection;
138 // Before terminating the process we try collecting a dump. Which
139 // a transient thread in the child process will do for us.
140 CrashDumpAndTerminateHungChildProcess(child_process);
141 child_process.Close();
143 } else {
144 RemoveProp(child_window, kHungChildWindowTimeout);
148 return continue_hang_detection;
151 BOOL CALLBACK HungWindowDetector::ChildWndEnumProc(HWND child_window,
152 LPARAM param) {
153 HungWindowDetector* detector_instance =
154 reinterpret_cast<HungWindowDetector*>(param);
155 if (NULL == detector_instance) {
156 NOTREACHED();
157 return FALSE;
160 return detector_instance->CheckChildWindow(child_window);