Audio indicator in each tab
[chromium-blink-merge.git] / chrome_frame / test / win_event_receiver.cc
blob7277b4986f52ec5cbd4040f97178364a78e10d5a
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_frame/test/win_event_receiver.h"
7 #include "base/bind.h"
8 #include "base/logging.h"
9 #include "base/memory/weak_ptr.h"
10 #include "base/message_loop.h"
11 #include "base/win/object_watcher.h"
12 #include "base/string_util.h"
13 #include "chrome_frame/function_stub.h"
15 // WinEventReceiver methods
16 WinEventReceiver::WinEventReceiver()
17 : listener_(NULL),
18 hook_(NULL),
19 hook_stub_(NULL) {
22 WinEventReceiver::~WinEventReceiver() {
23 StopReceivingEvents();
26 void WinEventReceiver::SetListenerForEvent(WinEventListener* listener,
27 DWORD event) {
28 SetListenerForEvents(listener, event, event);
31 void WinEventReceiver::SetListenerForEvents(WinEventListener* listener,
32 DWORD event_min, DWORD event_max) {
33 DCHECK(listener != NULL);
34 StopReceivingEvents();
36 listener_ = listener;
38 InitializeHook(event_min, event_max);
41 void WinEventReceiver::StopReceivingEvents() {
42 if (hook_) {
43 ::UnhookWinEvent(hook_);
44 hook_ = NULL;
45 FunctionStub::Destroy(hook_stub_);
46 hook_stub_ = NULL;
50 bool WinEventReceiver::InitializeHook(DWORD event_min, DWORD event_max) {
51 DCHECK(hook_ == NULL);
52 DCHECK(hook_stub_ == NULL);
53 hook_stub_ = FunctionStub::Create(reinterpret_cast<uintptr_t>(this),
54 WinEventHook);
55 // Don't use WINEVENT_SKIPOWNPROCESS here because we fake generate an event
56 // in the mock IE event sink (IA2_EVENT_DOCUMENT_LOAD_COMPLETE) that we want
57 // to catch.
58 hook_ = SetWinEventHook(event_min, event_max, NULL,
59 reinterpret_cast<WINEVENTPROC>(hook_stub_->code()), 0,
60 0, WINEVENT_OUTOFCONTEXT);
61 LOG_IF(ERROR, hook_ == NULL) << "Unable to SetWinEvent hook";
62 return hook_ != NULL;
65 // static
66 void WinEventReceiver::WinEventHook(WinEventReceiver* me, HWINEVENTHOOK hook,
67 DWORD event, HWND hwnd, LONG object_id,
68 LONG child_id, DWORD event_thread_id,
69 DWORD event_time) {
70 DCHECK(me->listener_ != NULL);
71 me->listener_->OnEventReceived(event, hwnd, object_id, child_id);
74 // Notifies WindowWatchdog when the process owning a given window exits.
76 // If the process terminates before its handle may be obtained, this class will
77 // still properly notifyy the WindowWatchdog.
79 // Notification is always delivered via a message loop task in the message loop
80 // that is active when the instance is constructed.
81 class WindowWatchdog::ProcessExitObserver
82 : public base::win::ObjectWatcher::Delegate {
83 public:
84 // Initiates the process watch. Will always return without notifying the
85 // watchdog.
86 ProcessExitObserver(WindowWatchdog* window_watchdog, HWND hwnd);
87 virtual ~ProcessExitObserver();
89 // base::ObjectWatcher::Delegate implementation
90 virtual void OnObjectSignaled(HANDLE process_handle);
92 private:
93 WindowWatchdog* window_watchdog_;
94 HANDLE process_handle_;
95 HWND hwnd_;
97 base::WeakPtrFactory<ProcessExitObserver> weak_factory_;
98 base::win::ObjectWatcher object_watcher_;
100 DISALLOW_COPY_AND_ASSIGN(ProcessExitObserver);
103 WindowWatchdog::ProcessExitObserver::ProcessExitObserver(
104 WindowWatchdog* window_watchdog, HWND hwnd)
105 : window_watchdog_(window_watchdog),
106 process_handle_(NULL),
107 hwnd_(hwnd),
108 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {
109 DWORD pid = 0;
110 ::GetWindowThreadProcessId(hwnd, &pid);
111 if (pid != 0) {
112 process_handle_ = ::OpenProcess(SYNCHRONIZE, FALSE, pid);
115 if (process_handle_ != NULL) {
116 object_watcher_.StartWatching(process_handle_, this);
117 } else {
118 // Process is gone, so the window must be gone too. Notify our observer!
119 MessageLoop::current()->PostTask(
120 FROM_HERE, base::Bind(&ProcessExitObserver::OnObjectSignaled,
121 weak_factory_.GetWeakPtr(), HANDLE(NULL)));
125 WindowWatchdog::ProcessExitObserver::~ProcessExitObserver() {
126 if (process_handle_ != NULL) {
127 ::CloseHandle(process_handle_);
131 void WindowWatchdog::ProcessExitObserver::OnObjectSignaled(
132 HANDLE process_handle) {
133 window_watchdog_->OnHwndProcessExited(hwnd_);
136 WindowWatchdog::WindowWatchdog() {}
138 void WindowWatchdog::AddObserver(WindowObserver* observer,
139 const std::string& caption_pattern,
140 const std::string& class_name_pattern) {
141 if (observers_.empty()) {
142 // SetListenerForEvents takes an event_min and event_max.
143 // EVENT_OBJECT_DESTROY, EVENT_OBJECT_SHOW, and EVENT_OBJECT_HIDE are
144 // consecutive, in that order; hence we supply only DESTROY and HIDE to
145 // denote exactly the required set.
146 win_event_receiver_.SetListenerForEvents(
147 this, EVENT_OBJECT_DESTROY, EVENT_OBJECT_HIDE);
150 ObserverEntry new_entry = {
151 observer,
152 caption_pattern,
153 class_name_pattern,
154 OpenWindowList() };
156 observers_.push_back(new_entry);
159 void WindowWatchdog::RemoveObserver(WindowObserver* observer) {
160 for (ObserverEntryList::iterator i = observers_.begin();
161 i != observers_.end(); ) {
162 i = (observer == i->observer) ? observers_.erase(i) : ++i;
165 if (observers_.empty())
166 win_event_receiver_.StopReceivingEvents();
169 std::string WindowWatchdog::GetWindowCaption(HWND hwnd) {
170 std::string caption;
171 int len = ::GetWindowTextLength(hwnd) + 1;
172 if (len > 1)
173 ::GetWindowTextA(hwnd, WriteInto(&caption, len), len);
174 return caption;
177 bool WindowWatchdog::MatchingWindow(const ObserverEntry& entry,
178 const std::string& caption,
179 const std::string& class_name) {
180 bool should_match_caption = !entry.caption_pattern.empty();
181 bool should_match_class = !entry.class_name_pattern.empty();
183 if (should_match_caption &&
184 MatchPattern(caption, entry.caption_pattern) &&
185 !should_match_class) {
186 return true;
188 if (should_match_class &&
189 MatchPattern(class_name, entry.class_name_pattern)) {
190 return true;
192 return false;
195 void WindowWatchdog::HandleOnOpen(HWND hwnd) {
196 std::string caption = GetWindowCaption(hwnd);
197 char class_name[MAX_PATH] = {0};
198 GetClassNameA(hwnd, class_name, arraysize(class_name));
200 // Instantiated only if there is at least one interested observer. Each
201 // interested observer will maintain a reference to this object, such that it
202 // is deleted when the last observer disappears.
203 linked_ptr<ProcessExitObserver> process_exit_observer;
205 // Identify the interested observers and mark them as watching this HWND for
206 // close.
207 ObserverEntryList interested_observers;
208 for (ObserverEntryList::iterator entry_iter = observers_.begin();
209 entry_iter != observers_.end(); ++entry_iter) {
210 if (MatchingWindow(*entry_iter, caption, class_name)) {
211 if (process_exit_observer == NULL) {
212 process_exit_observer.reset(new ProcessExitObserver(this, hwnd));
215 entry_iter->open_windows.push_back(
216 OpenWindowEntry(hwnd, process_exit_observer));
218 interested_observers.push_back(*entry_iter);
222 // Notify the interested observers in a separate pass in case AddObserver or
223 // RemoveObserver is called as a side-effect of the notification.
224 for (ObserverEntryList::iterator entry_iter = interested_observers.begin();
225 entry_iter != interested_observers.end(); ++entry_iter) {
226 entry_iter->observer->OnWindowOpen(hwnd);
230 void WindowWatchdog::HandleOnClose(HWND hwnd) {
231 // Identify the interested observers, reaping OpenWindow entries as
232 // appropriate
233 ObserverEntryList interested_observers;
234 for (ObserverEntryList::iterator entry_iter = observers_.begin();
235 entry_iter != observers_.end(); ++entry_iter) {
236 size_t num_open_windows = entry_iter->open_windows.size();
238 OpenWindowList::iterator window_iter = entry_iter->open_windows.begin();
239 while (window_iter != entry_iter->open_windows.end()) {
240 if (hwnd == window_iter->first) {
241 window_iter = entry_iter->open_windows.erase(window_iter);
242 } else {
243 ++window_iter;
247 if (num_open_windows != entry_iter->open_windows.size()) {
248 interested_observers.push_back(*entry_iter);
252 // Notify the interested observers in a separate pass in case AddObserver or
253 // RemoveObserver is called as a side-effect of the notification.
254 for (ObserverEntryList::iterator entry_iter = interested_observers.begin();
255 entry_iter != interested_observers.end(); ++entry_iter) {
256 entry_iter->observer->OnWindowClose(hwnd);
260 void WindowWatchdog::OnEventReceived(
261 DWORD event, HWND hwnd, LONG object_id, LONG child_id) {
262 // We need to look for top level windows and a natural check is for
263 // WS_CHILD. Instead, checking for WS_CAPTION allows us to filter
264 // out other stray popups
265 if (event == EVENT_OBJECT_SHOW) {
266 HandleOnOpen(hwnd);
267 } else {
268 DCHECK(event == EVENT_OBJECT_DESTROY || event == EVENT_OBJECT_HIDE);
269 HandleOnClose(hwnd);
273 void WindowWatchdog::OnHwndProcessExited(HWND hwnd) {
274 HandleOnClose(hwnd);