Check USB device path access when prompting users to select a device.
[chromium-blink-merge.git] / chrome / browser / hang_monitor / hung_window_detector.cc
blob89d8be87e870ec8475c0980644f66c360186bbd7
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 namespace {
16 // How long do we wait for the terminated thread or process to die (in ms)
17 static const int kTerminateTimeout = 2000;
19 } // namespace
21 const wchar_t HungWindowDetector::kHungChildWindowTimeout[] =
22 L"Chrome_HungChildWindowTimeout";
24 HungWindowDetector::HungWindowDetector(HungWindowNotification* notification)
25 : notification_(notification),
26 top_level_window_(NULL),
27 message_response_timeout_(0),
28 enumerating_(false) {
29 DCHECK(NULL != notification_);
31 // NOTE: It is the caller's responsibility to make sure that
32 // callbacks on this object have been stopped before
33 // destroying this object
34 HungWindowDetector::~HungWindowDetector() {
37 bool HungWindowDetector::Initialize(HWND top_level_window,
38 int message_response_timeout) {
39 if (NULL == notification_) {
40 return false;
42 if (NULL == top_level_window) {
43 return false;
45 // It is OK to call Initialize on this object repeatedly
46 // with different top lebel HWNDs and timeout values each time.
47 // And we do not need a lock for this because we are just
48 // swapping DWORDs.
49 top_level_window_ = top_level_window;
50 message_response_timeout_ = message_response_timeout;
51 return true;
54 void HungWindowDetector::OnTick() {
55 do {
56 base::AutoLock lock(hang_detection_lock_);
57 // If we already are checking for hung windows on another thread,
58 // don't do this again.
59 if (enumerating_) {
60 return;
62 enumerating_ = true;
63 } while (false); // To scope the AutoLock
65 EnumChildWindows(top_level_window_, ChildWndEnumProc,
66 reinterpret_cast<LPARAM>(this));
68 // The window shouldn't be disabled unless we're showing a modal dialog.
69 // If we're not, then reenable the window.
70 if (!::IsWindowEnabled(top_level_window_) &&
71 !::GetWindow(top_level_window_, GW_ENABLEDPOPUP)) {
72 ::EnableWindow(top_level_window_, TRUE);
75 enumerating_ = false;
78 bool HungWindowDetector::CheckChildWindow(HWND child_window) {
79 // It can happen that the window is DOA. It specifically happens
80 // when we have just killed a plugin process and the enum is still
81 // enumerating windows from that process.
82 if (!IsWindow(child_window)) {
83 return true;
86 DWORD top_level_window_thread_id =
87 GetWindowThreadProcessId(top_level_window_, NULL);
89 DWORD child_window_process_id = 0;
90 DWORD child_window_thread_id =
91 GetWindowThreadProcessId(child_window, &child_window_process_id);
92 bool continue_hang_detection = true;
94 if (top_level_window_thread_id != child_window_thread_id) {
95 // The message timeout for a child window starts of with a default
96 // value specified by the message_response_timeout_ member. It is
97 // tracked by a property on the child window.
98 #pragma warning(disable:4311)
99 int child_window_message_timeout =
100 reinterpret_cast<int>(GetProp(child_window, kHungChildWindowTimeout));
101 #pragma warning(default:4311)
102 if (!child_window_message_timeout) {
103 child_window_message_timeout = message_response_timeout_;
106 DWORD_PTR result = 0;
107 if (0 == SendMessageTimeout(child_window,
108 WM_NULL,
111 SMTO_BLOCK,
112 child_window_message_timeout,
113 &result)) {
114 HungWindowNotification::ActionOnHungWindow action =
115 HungWindowNotification::HUNG_WINDOW_IGNORE;
116 #pragma warning(disable:4312)
117 SetProp(child_window, kHungChildWindowTimeout,
118 reinterpret_cast<HANDLE>(child_window_message_timeout));
119 #pragma warning(default:4312)
120 continue_hang_detection =
121 notification_->OnHungWindowDetected(child_window, top_level_window_,
122 &action);
123 // Make sure this window still a child of our top-level parent
124 if (!IsChild(top_level_window_, child_window)) {
125 return continue_hang_detection;
128 if (action == HungWindowNotification::HUNG_WINDOW_TERMINATE_PROCESS) {
129 RemoveProp(child_window, kHungChildWindowTimeout);
130 CHandle child_process(OpenProcess(PROCESS_ALL_ACCESS,
131 FALSE,
132 child_window_process_id));
134 if (NULL == child_process.m_h) {
135 return continue_hang_detection;
137 // Before swinging the axe, do some sanity checks to make
138 // sure this window still belongs to the same process
139 DWORD process_id_check = 0;
140 GetWindowThreadProcessId(child_window, &process_id_check);
141 if (process_id_check != child_window_process_id) {
142 return continue_hang_detection;
145 // Before terminating the process we try collecting a dump. Which
146 // a transient thread in the child process will do for us.
147 CrashDumpAndTerminateHungChildProcess(child_process);
148 child_process.Close();
150 } else {
151 RemoveProp(child_window, kHungChildWindowTimeout);
155 return continue_hang_detection;
158 BOOL CALLBACK HungWindowDetector::ChildWndEnumProc(HWND child_window,
159 LPARAM param) {
160 HungWindowDetector* detector_instance =
161 reinterpret_cast<HungWindowDetector*>(param);
162 if (NULL == detector_instance) {
163 NOTREACHED();
164 return FALSE;
167 return detector_instance->CheckChildWindow(child_window);