Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 #include "components/browser_watcher/window_hang_monitor_win.h" | 4 #include "components/browser_watcher/window_hang_monitor_win.h" |
| 5 | 5 |
| 6 #include "base/callback.h" | 6 #include "base/callback.h" |
| 7 #include "base/location.h" | 7 #include "base/location.h" |
| 8 #include "base/logging.h" | 8 #include "base/logging.h" |
| 9 #include "base/message_loop/message_loop.h" | 9 #include "base/message_loop/message_loop.h" |
| 10 #include "base/win/message_window.h" | 10 #include "base/win/message_window.h" |
| 11 | 11 |
| 12 namespace browser_watcher { | 12 namespace browser_watcher { |
| 13 | 13 |
| 14 namespace { | 14 namespace { |
| 15 | 15 |
| 16 HWND FindNamedWindowForProcess(const base::string16 name, base::ProcessId pid) { | 16 // Returns the class name for the |window|. |
| 17 HWND candidate = base::win::MessageWindow::FindWindow(name); | 17 base::string16 GetWindowClassName(HWND window) { |
| 18 if (candidate) { | 18 const int kClassNameLength = MAX_PATH; |
| 19 wchar_t class_name[kClassNameLength]; | |
| 20 int nb_copied = ::GetClassName(window, class_name, kClassNameLength); | |
| 21 if (nb_copied) { | |
| 22 return base::string16(class_name); | |
| 23 } | |
| 24 return base::string16(); | |
| 25 } | |
| 26 | |
| 27 // Returns the Chrome message window handle for the specified |pid| or nullptr | |
| 28 // if not found. | |
| 29 HWND FindChromeMessageWindow(base::ProcessId pid) { | |
|
Sigurður Ásgeirsson
2016/01/11 19:42:59
now that I think about it, I'm not sure this is "g
Patrick Monette
2016/01/20 17:21:35
So I'm now also check that the name of the window
| |
| 30 HWND candidate = ::FindWindowEx(HWND_MESSAGE, nullptr, nullptr, nullptr); | |
| 31 while (candidate) { | |
| 19 DWORD actual_process_id = 0; | 32 DWORD actual_process_id = 0; |
| 20 ::GetWindowThreadProcessId(candidate, &actual_process_id); | 33 ::GetWindowThreadProcessId(candidate, &actual_process_id); |
| 21 if (actual_process_id == pid) | 34 if (GetWindowClassName(candidate) == L"Chrome_MessageWindow" && |
|
Sigurður Ásgeirsson
2016/01/11 19:42:59
doesn't matter a whole lot, but you can avoid a bu
Patrick Monette
2016/01/20 17:21:35
Done.
| |
| 35 actual_process_id == pid) { | |
| 22 return candidate; | 36 return candidate; |
| 37 } | |
| 38 candidate = ::GetNextWindow(candidate, GW_HWNDNEXT); | |
| 23 } | 39 } |
| 24 return nullptr; | 40 return nullptr; |
| 25 } | 41 } |
| 26 | 42 |
| 27 } // namespace | 43 } // namespace |
| 28 | 44 |
| 29 WindowHangMonitor::WindowHangMonitor(base::TimeDelta ping_interval, | 45 WindowHangMonitor::WindowHangMonitor(base::TimeDelta ping_interval, |
| 30 base::TimeDelta timeout, | 46 base::TimeDelta timeout, |
| 31 const WindowEventCallback& callback) | 47 const WindowEventCallback& callback) |
| 32 : callback_(callback), | 48 : callback_(callback), |
| 33 ping_interval_(ping_interval), | 49 ping_interval_(ping_interval), |
| 34 hang_timeout_(timeout), | 50 hang_timeout_(timeout), |
| 35 timer_(false /* don't retain user task */, false /* don't repeat */), | 51 timer_(false /* don't retain user task */, false /* don't repeat */), |
| 36 outstanding_ping_(nullptr) { | 52 outstanding_ping_(nullptr) { |
| 37 } | 53 } |
| 38 | 54 |
| 39 WindowHangMonitor::~WindowHangMonitor() { | 55 WindowHangMonitor::~WindowHangMonitor() { |
| 40 if (outstanding_ping_) { | 56 if (outstanding_ping_) { |
| 41 // We have an outstanding ping, disable it and leak it intentionally as | 57 // We have an outstanding ping, disable it and leak it intentionally as |
| 42 // if the callback arrives eventually, it'll cause a use-after-free. | 58 // if the callback arrives eventually, it'll cause a use-after-free. |
| 43 outstanding_ping_->monitor = nullptr; | 59 outstanding_ping_->monitor = nullptr; |
| 44 outstanding_ping_ = nullptr; | 60 outstanding_ping_ = nullptr; |
| 45 } | 61 } |
| 46 } | 62 } |
| 47 | 63 |
| 48 void WindowHangMonitor::Initialize(base::Process process, | 64 void WindowHangMonitor::Initialize(base::Process process) { |
| 49 const base::string16& window_name) { | |
| 50 window_name_ = window_name; | |
| 51 window_process_ = process.Pass(); | 65 window_process_ = process.Pass(); |
| 52 timer_.SetTaskRunner(base::MessageLoop::current()->task_runner()); | 66 timer_.SetTaskRunner(base::MessageLoop::current()->task_runner()); |
| 53 | 67 |
| 54 ScheduleFindWindow(); | 68 ScheduleFindWindow(); |
| 55 } | 69 } |
| 56 | 70 |
| 57 void WindowHangMonitor::ScheduleFindWindow() { | 71 void WindowHangMonitor::ScheduleFindWindow() { |
| 58 // TODO(erikwright): We could reduce the polling by using WaitForInputIdle, | 72 // TODO(erikwright): We could reduce the polling by using WaitForInputIdle, |
| 59 // but it is hard to test (requiring a non-Console executable). | 73 // but it is hard to test (requiring a non-Console executable). |
| 60 timer_.Start( | 74 timer_.Start( |
| 61 FROM_HERE, ping_interval_, | 75 FROM_HERE, ping_interval_, |
| 62 base::Bind(&WindowHangMonitor::PollForWindow, base::Unretained(this))); | 76 base::Bind(&WindowHangMonitor::PollForWindow, base::Unretained(this))); |
| 63 } | 77 } |
| 64 | 78 |
| 65 void WindowHangMonitor::PollForWindow() { | 79 void WindowHangMonitor::PollForWindow() { |
| 66 int exit_code = 0; | 80 int exit_code = 0; |
| 67 if (window_process_.WaitForExitWithTimeout(base::TimeDelta(), &exit_code)) { | 81 if (window_process_.WaitForExitWithTimeout(base::TimeDelta(), &exit_code)) { |
| 68 callback_.Run(WINDOW_NOT_FOUND); | 82 callback_.Run(WINDOW_NOT_FOUND); |
| 69 return; | 83 return; |
| 70 } | 84 } |
| 71 | 85 |
| 72 HWND hwnd = FindNamedWindowForProcess(window_name_, window_process_.Pid()); | 86 HWND hwnd = FindChromeMessageWindow(window_process_.Pid()); |
| 73 if (hwnd) { | 87 if (hwnd) { |
| 74 // Sends a ping and schedules a timeout task. Upon receiving a ping response | 88 // Sends a ping and schedules a timeout task. Upon receiving a ping response |
| 75 // further pings will be scheduled ad infinitum. Will signal any failure now | 89 // further pings will be scheduled ad infinitum. Will signal any failure now |
| 76 // or later via the callback. | 90 // or later via the callback. |
| 77 SendPing(hwnd); | 91 SendPing(hwnd); |
| 78 } else { | 92 } else { |
| 79 ScheduleFindWindow(); | 93 ScheduleFindWindow(); |
| 80 } | 94 } |
| 81 } | 95 } |
| 82 | 96 |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 122 void WindowHangMonitor::OnHangTimeout(HWND hwnd) { | 136 void WindowHangMonitor::OnHangTimeout(HWND hwnd) { |
| 123 DCHECK(window_process_.IsValid()); | 137 DCHECK(window_process_.IsValid()); |
| 124 | 138 |
| 125 if (outstanding_ping_) { | 139 if (outstanding_ping_) { |
| 126 // The ping is still outstanding, the window is hung or has vanished. | 140 // The ping is still outstanding, the window is hung or has vanished. |
| 127 // Orphan the outstanding ping. If the callback arrives late, it will | 141 // Orphan the outstanding ping. If the callback arrives late, it will |
| 128 // delete it, or if the callback never arrives it'll leak. | 142 // delete it, or if the callback never arrives it'll leak. |
| 129 outstanding_ping_->monitor = NULL; | 143 outstanding_ping_->monitor = NULL; |
| 130 outstanding_ping_ = NULL; | 144 outstanding_ping_ = NULL; |
| 131 | 145 |
| 132 if (hwnd != | 146 if (hwnd != FindChromeMessageWindow(window_process_.Pid())) { |
| 133 FindNamedWindowForProcess(window_name_, window_process_.Pid())) { | |
| 134 // The window vanished. | 147 // The window vanished. |
| 135 callback_.Run(WINDOW_VANISHED); | 148 callback_.Run(WINDOW_VANISHED); |
| 136 } else { | 149 } else { |
| 137 // The window hung. | 150 // The window hung. |
| 138 callback_.Run(WINDOW_HUNG); | 151 callback_.Run(WINDOW_HUNG); |
| 139 } | 152 } |
| 140 } else { | 153 } else { |
| 141 // No ping outstanding, window is not yet hung. Schedule the next retry. | 154 // No ping outstanding, window is not yet hung. Schedule the next retry. |
| 142 timer_.Start( | 155 timer_.Start( |
| 143 FROM_HERE, hang_timeout_ - ping_interval_, | 156 FROM_HERE, hang_timeout_ - ping_interval_, |
| 144 base::Bind(&WindowHangMonitor::OnRetryTimeout, base::Unretained(this))); | 157 base::Bind(&WindowHangMonitor::OnRetryTimeout, base::Unretained(this))); |
| 145 } | 158 } |
| 146 } | 159 } |
| 147 | 160 |
| 148 void WindowHangMonitor::OnRetryTimeout() { | 161 void WindowHangMonitor::OnRetryTimeout() { |
| 149 DCHECK(window_process_.IsValid()); | 162 DCHECK(window_process_.IsValid()); |
| 150 DCHECK(!outstanding_ping_); | 163 DCHECK(!outstanding_ping_); |
| 151 // We can't simply hold onto the previously located HWND due to potential | 164 // We can't simply hold onto the previously located HWND due to potential |
| 152 // aliasing. | 165 // aliasing. |
| 153 // 1. The window handle might have been re-assigned to a different window | 166 // 1. The window handle might have been re-assigned to a different window |
| 154 // from the time we found it to the point where we query for its owning | 167 // from the time we found it to the point where we query for its owning |
| 155 // process. | 168 // process. |
| 156 // 2. The window handle might have been re-assigned to a different process | 169 // 2. The window handle might have been re-assigned to a different process |
| 157 // at any point after we found it. | 170 // at any point after we found it. |
| 158 HWND hwnd = FindNamedWindowForProcess(window_name_, window_process_.Pid()); | 171 HWND hwnd = FindChromeMessageWindow(window_process_.Pid()); |
| 159 if (hwnd) | 172 if (hwnd) |
| 160 SendPing(hwnd); | 173 SendPing(hwnd); |
| 161 else | 174 else |
| 162 callback_.Run(WINDOW_VANISHED); | 175 callback_.Run(WINDOW_VANISHED); |
| 163 } | 176 } |
| 164 | 177 |
| 165 } // namespace browser_watcher | 178 } // namespace browser_watcher |
| OLD | NEW |