summaryrefslogtreecommitdiffstats
path: root/chrome_frame/test/win_event_receiver.cc
blob: b967d5c1df25daadcfd48db05fb09e32f73f4e31 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome_frame/test/win_event_receiver.h"

#include "base/logging.h"
#include "base/string_util.h"

#include "chrome_frame/function_stub.h"

#include "third_party/xulrunner-sdk/win/include/accessibility/AccessibleEventId.h"

// WinEventReceiver methods
WinEventReceiver::WinEventReceiver()
    : listener_(NULL),
      hook_(NULL),
      hook_stub_(NULL) {
}

WinEventReceiver::~WinEventReceiver() {
  StopReceivingEvents();
}

void WinEventReceiver::SetListenerForEvent(WinEventListener* listener,
                                           DWORD event) {
  SetListenerForEvents(listener, event, event);
}

void WinEventReceiver::SetListenerForEvents(WinEventListener* listener,
                                            DWORD event_min, DWORD event_max) {
  DCHECK(listener != NULL);
  StopReceivingEvents();

  listener_ = listener;

  InitializeHook(event_min, event_max);
}

void WinEventReceiver::StopReceivingEvents() {
  if (hook_) {
    ::UnhookWinEvent(hook_);
    hook_ = NULL;
    FunctionStub::Destroy(hook_stub_);
    hook_stub_ = NULL;
  }
}

bool WinEventReceiver::InitializeHook(DWORD event_min, DWORD event_max) {
  DCHECK(hook_ == NULL);
  DCHECK(hook_stub_ == NULL);
  hook_stub_ = FunctionStub::Create(reinterpret_cast<uintptr_t>(this),
                                    WinEventHook);
  // Don't use WINEVENT_SKIPOWNPROCESS here because we fake generate an event
  // in the mock IE event sink (IA2_EVENT_DOCUMENT_LOAD_COMPLETE) that we want
  // to catch.
  hook_ = SetWinEventHook(event_min, event_max, NULL,
                          reinterpret_cast<WINEVENTPROC>(hook_stub_->code()), 0,
                          0, WINEVENT_OUTOFCONTEXT);
  DLOG_IF(ERROR, hook_ == NULL) << "Unable to SetWinEvent hook";
  return hook_ != NULL;
}

// static
void WinEventReceiver::WinEventHook(WinEventReceiver* me, HWINEVENTHOOK hook,
                                    DWORD event, HWND hwnd, LONG object_id,
                                    LONG child_id, DWORD event_thread_id,
                                    DWORD event_time) {
  DCHECK(me->listener_ != NULL);
  me->listener_->OnEventReceived(event, hwnd, object_id, child_id);
}

// WindowWatchdog methods
void WindowWatchdog::AddObserver(WindowObserver* observer,
                                 const std::string& window_class) {
  if (observers_.empty())
    win_event_receiver_.SetListenerForEvent(this, EVENT_OBJECT_SHOW);

  WindowObserverEntry new_entry = { observer, window_class };
  observers_.push_back(new_entry);
}

void WindowWatchdog::RemoveObserver(WindowObserver* observer) {
  for (ObserverMap::iterator i = observers_.begin(); i != observers_.end();) {
    i = (observer == i->observer) ? observers_.erase(i) : ++i;
  }

  if (observers_.empty())
    win_event_receiver_.StopReceivingEvents();
}

void WindowWatchdog::OnEventReceived(DWORD event, HWND hwnd, LONG object_id,
                                     LONG child_id) {
  // We need to look for top level windows and a natural check is for
  // WS_CHILD. Instead, checking for WS_CAPTION allows us to filter
  // out other stray popups
  if (!(WS_CAPTION & GetWindowLong(hwnd, GWL_STYLE)))
    return;

  char class_name[MAX_PATH] = {0};
  ::GetClassNameA(hwnd, class_name, arraysize(class_name));

  ObserverMap interested_observers;
  for (ObserverMap::iterator i = observers_.begin();
      i != observers_.end(); i++) {
    if (0 == lstrcmpA(i->window_class.c_str(), class_name)) {
      interested_observers.push_back(*i);
    }
  }

  std::string caption;
  int len = ::GetWindowTextLength(hwnd) + 1;
  ::GetWindowTextA(hwnd, WriteInto(&caption, len), len);

  for (ObserverMap::iterator i = interested_observers.begin();
      i != interested_observers.end(); i++) {
    i->observer->OnWindowDetected(hwnd, caption);
  }
}

// AccessibilityEventListener methods
AccessibilityEventObserver::AccessibilityEventObserver() {
  event_receiver_.SetListenerForEvents(this, EVENT_SYSTEM_MENUPOPUPSTART,
                                       IA2_EVENT_DOCUMENT_LOAD_COMPLETE);
}

void AccessibilityEventObserver::OnEventReceived(DWORD event,
                                                 HWND hwnd,
                                                 LONG object_id,
                                                 LONG child_id) {
  switch (event) {
    case EVENT_SYSTEM_MENUPOPUPSTART:
      OnMenuPopup(hwnd);
      break;
    case IA2_EVENT_DOCUMENT_LOAD_COMPLETE:
      OnAccDocLoad(hwnd);
      break;
  }
}