- add sources.
[platform/framework/web/crosswalk.git] / src / chrome_frame / test / chrome_frame_ui_test_utils.cc
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.
4
5 #include "chrome_frame/test/chrome_frame_ui_test_utils.h"
6
7 #include <windows.h>
8
9 #include <sstream>
10 #include <stack>
11
12 #include "base/bind.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/message_loop/message_loop.h"
15 #include "base/path_service.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/strings/utf_string_conversions.h"
19 #include "base/win/scoped_bstr.h"
20 #include "chrome/common/chrome_paths.h"
21 #include "chrome_frame/test/win_event_receiver.h"
22 #include "chrome_frame/utils.h"
23 #include "testing/gtest/include/gtest/gtest.h"
24 #include "third_party/iaccessible2/ia2_api_all.h"
25 #include "ui/gfx/point.h"
26 #include "ui/gfx/rect.h"
27
28 namespace chrome_frame_test {
29
30 // Timeout for waiting on Chrome to create the accessibility tree for the DOM.
31 const int kChromeDOMAccessibilityTreeTimeoutMs = 10 * 1000;
32
33 // Timeout for waiting on a menu to popup.
34 const int kMenuPopupTimeoutMs = 10 * 1000;
35
36 // AccObject methods
37 AccObject::AccObject(IAccessible* accessible, int child_id)
38     : accessible_(accessible), child_id_(child_id) {
39   DCHECK(accessible);
40   if (child_id != CHILDID_SELF) {
41     base::win::ScopedComPtr<IDispatch> dispatch;
42     // This class does not support referring to a full MSAA object using the
43     // parent object and the child id.
44     HRESULT result = accessible_->get_accChild(child_id_, dispatch.Receive());
45     if (result != S_FALSE && result != E_NOINTERFACE) {
46       LOG(ERROR) << "AccObject created which refers to full MSAA object using "
47                     "parent object and child id. This should NOT be done.";
48     }
49     DCHECK(result == S_FALSE || result == E_NOINTERFACE);
50   }
51 }
52
53 // static
54 AccObject* AccObject::CreateFromWindow(HWND hwnd) {
55   base::win::ScopedComPtr<IAccessible> accessible;
56   ::AccessibleObjectFromWindow(hwnd, OBJID_CLIENT,
57       IID_IAccessible, reinterpret_cast<void**>(accessible.Receive()));
58   if (accessible)
59     return new AccObject(accessible);
60   return NULL;
61 }
62
63 // static
64 AccObject* AccObject::CreateFromEvent(HWND hwnd, LONG object_id,
65                                       LONG child_id) {
66   base::win::ScopedComPtr<IAccessible> accessible;
67   base::win::ScopedVariant acc_child_id;
68   ::AccessibleObjectFromEvent(hwnd, object_id, child_id, accessible.Receive(),
69                               acc_child_id.Receive());
70   if (accessible && acc_child_id.type() == VT_I4)
71     return new AccObject(accessible, V_I4(&acc_child_id));
72   return NULL;
73 }
74
75 // static
76 AccObject* AccObject::CreateFromDispatch(IDispatch* dispatch) {
77   if (dispatch) {
78     base::win::ScopedComPtr<IAccessible> accessible;
79     accessible.QueryFrom(dispatch);
80     if (accessible)
81       return new AccObject(accessible);
82   }
83   return NULL;
84 }
85
86 // static
87 AccObject* AccObject::CreateFromPoint(int x, int y) {
88   base::win::ScopedComPtr<IAccessible> accessible;
89   base::win::ScopedVariant child_id;
90   POINT point = {x, y};
91   ::AccessibleObjectFromPoint(point, accessible.Receive(), child_id.Receive());
92   if (accessible && child_id.type() == VT_I4)
93     return new AccObject(accessible, V_I4(&child_id));
94   return NULL;
95 }
96
97 bool AccObject::DoDefaultAction() {
98   // Prevent clients from using this method to try to select menu items, which
99   // does not work with a locked desktop.
100   std::wstring class_name;
101   if (GetWindowClassName(&class_name)) {
102     DCHECK(class_name != L"#32768") << "Do not use DoDefaultAction with menus";
103   }
104
105   HRESULT result = accessible_->accDoDefaultAction(child_id_);
106   EXPECT_HRESULT_SUCCEEDED(result)
107       << "Could not do default action for AccObject: " << GetDescription();
108   return SUCCEEDED(result);
109 }
110
111 bool AccObject::LeftClick() {
112   return PostMouseClickAtCenter(WM_LBUTTONDOWN, WM_LBUTTONUP);
113 }
114
115 bool AccObject::RightClick() {
116   return PostMouseClickAtCenter(WM_RBUTTONDOWN, WM_RBUTTONUP);
117 }
118
119 bool AccObject::Focus() {
120   EXPECT_HRESULT_SUCCEEDED(
121       accessible_->accSelect(SELFLAG_TAKEFOCUS, child_id_));
122
123   // Double check that the object actually received focus. In some cases
124   // the parent object must have the focus first.
125   bool did_focus = false;
126   base::win::ScopedVariant focused;
127   if (SUCCEEDED(accessible_->get_accFocus(focused.Receive()))) {
128     if (focused.type() != VT_EMPTY)
129       did_focus = true;
130   }
131   EXPECT_TRUE(did_focus) << "Could not focus AccObject: " << GetDescription();
132   return did_focus;
133 }
134
135 bool AccObject::Select() {
136   // SELFLAG_TAKESELECTION needs to be combined with the focus in order to
137   // take effect.
138   int selection_flag = SELFLAG_TAKEFOCUS | SELFLAG_TAKESELECTION;
139   EXPECT_HRESULT_SUCCEEDED(accessible_->accSelect(selection_flag, child_id_));
140
141   // Double check that the object actually received selection.
142   bool did_select = false;
143   base::win::ScopedVariant selected;
144   if (SUCCEEDED(accessible_->get_accSelection(selected.Receive()))) {
145     if (selected.type() != VT_EMPTY)
146       did_select = true;
147   }
148   EXPECT_TRUE(did_select) << "Could not select AccObject: " << GetDescription();
149   return did_select;
150 }
151
152 bool AccObject::SetValue(const std::wstring& value) {
153   base::win::ScopedBstr value_bstr(value.c_str());
154   EXPECT_HRESULT_SUCCEEDED(accessible_->put_accValue(child_id_, value_bstr));
155
156   // Double check that the object's value has actually changed. Some objects'
157   // values can not be changed.
158   bool did_set_value = false;
159   std::wstring actual_value = L"-";
160   if (GetValue(&actual_value) && value == actual_value) {
161     did_set_value = true;
162   }
163   EXPECT_TRUE(did_set_value) << "Could not set value for AccObject: "
164                              << GetDescription();
165   return did_set_value;
166 }
167
168 bool AccObject::GetName(std::wstring* name) {
169   DCHECK(name);
170   base::win::ScopedBstr name_bstr;
171   HRESULT result = accessible_->get_accName(child_id_, name_bstr.Receive());
172   if (SUCCEEDED(result))
173     name->assign(name_bstr, name_bstr.Length());
174   return SUCCEEDED(result);
175 }
176
177 bool AccObject::GetRoleText(std::wstring* role_text) {
178   DCHECK(role_text);
179   base::win::ScopedVariant role_variant;
180   if (SUCCEEDED(accessible_->get_accRole(child_id_, role_variant.Receive()))) {
181     if (role_variant.type() == VT_I4) {
182       wchar_t role_text_array[50];
183       UINT characters = ::GetRoleText(V_I4(&role_variant), role_text_array,
184                                       arraysize(role_text_array));
185       if (characters) {
186         *role_text = role_text_array;
187         return true;
188       } else {
189         LOG(ERROR) << "GetRoleText failed for role: " << V_I4(&role_variant);
190       }
191     } else if (role_variant.type() == VT_BSTR) {
192       *role_text = V_BSTR(&role_variant);
193       return true;
194     } else {
195       LOG(ERROR) << "Role was unexpected variant type: "
196                  << role_variant.type();
197     }
198   }
199   return false;
200 }
201
202 bool AccObject::GetValue(std::wstring* value) {
203   DCHECK(value);
204   base::win::ScopedBstr value_bstr;
205   HRESULT result = accessible_->get_accValue(child_id_, value_bstr.Receive());
206   if (SUCCEEDED(result))
207     value->assign(value_bstr, value_bstr.Length());
208   return SUCCEEDED(result);
209 }
210
211 bool AccObject::GetState(int* state) {
212   DCHECK(state);
213   base::win::ScopedVariant state_variant;
214   if (SUCCEEDED(accessible_->get_accState(child_id_,
215                                           state_variant.Receive()))) {
216     if (state_variant.type() == VT_I4) {
217       *state = V_I4(&state_variant);
218       return true;
219     }
220   }
221   return false;
222 }
223
224 bool AccObject::GetLocation(gfx::Rect* location) {
225   DCHECK(location);
226   long left, top, width, height;  // NOLINT
227   HRESULT result = accessible_->accLocation(&left, &top, &width, &height,
228                                             child_id_);
229   if (SUCCEEDED(result))
230     *location = gfx::Rect(left, top, width, height);
231   return SUCCEEDED(result);
232 }
233
234 bool AccObject::GetLocationInClient(gfx::Rect* client_location) {
235   DCHECK(client_location);
236   gfx::Rect location;
237   if (!GetLocation(&location))
238     return false;
239   HWND container_window = NULL;
240   if (!GetWindow(&container_window))
241     return false;
242   POINT offset = {0, 0};
243   if (!::ScreenToClient(container_window, &offset)) {
244     LOG(ERROR) << "Could not convert from screen to client coordinates for "
245                   "window containing accessibility object: "
246                << GetDescription();
247     return false;
248   }
249   location.Offset(offset.x, offset.y);
250   *client_location = location;
251   return true;
252 }
253
254 AccObject* AccObject::GetParent() {
255   if (IsSimpleElement())
256     return new AccObject(accessible_);
257   base::win::ScopedComPtr<IDispatch> dispatch;
258   if (FAILED(accessible_->get_accParent(dispatch.Receive())))
259     return NULL;
260   return AccObject::CreateFromDispatch(dispatch.get());
261 }
262
263 bool AccObject::GetChildren(RefCountedAccObjectVector* client_objects) {
264   DCHECK(client_objects);
265   int child_count;
266   if (!GetChildCount(&child_count)) {
267     LOG(ERROR) << "Failed to get child count of AccObject";
268     return false;
269   }
270   if (child_count == 0)
271     return true;
272
273   RefCountedAccObjectVector objects;
274   // Find children using |AccessibleChildren|.
275   scoped_ptr<VARIANT[]> children(new VARIANT[child_count]);
276   long found_child_count;  // NOLINT
277   if (FAILED(AccessibleChildren(accessible_, 0L, child_count,
278                                 children.get(),
279                                 &found_child_count))) {
280     LOG(ERROR) << "Failed to get children of accessible object";
281     return false;
282   }
283   if (found_child_count > 0) {
284     for (int i = 0; i < found_child_count; i++) {
285       scoped_refptr<AccObject> obj = CreateFromVariant(this, children[i]);
286       if (obj)
287         objects.push_back(obj);
288       ::VariantClear(&children[i]);
289     }
290   }
291
292   // In some cases, there are more children which can be found only by using
293   // the deprecated |accNavigate| method. Many of the menus, such as
294   // 'Favorites', cannot be found in IE6 using |AccessibileChildren|. Here we
295   // attempt a best effort at finding some remaining children.
296   int remaining_child_count = child_count - found_child_count;
297   scoped_refptr<AccObject> child_object;
298   if (remaining_child_count > 0) {
299     GetFromNavigation(NAVDIR_FIRSTCHILD, &child_object);
300   }
301   while (remaining_child_count > 0 && child_object) {
302     // Add to the children list if this child was not found earlier.
303     bool already_found = false;
304     for (size_t i = 0; i < objects.size(); ++i) {
305       if (child_object->Equals(objects[i])) {
306         already_found = true;
307         break;
308       }
309     }
310     if (!already_found) {
311       objects.push_back(child_object);
312       remaining_child_count--;
313     }
314     scoped_refptr<AccObject> next_child_object;
315     child_object->GetFromNavigation(NAVDIR_NEXT, &next_child_object);
316     child_object = next_child_object;
317   }
318
319   client_objects->insert(client_objects->end(), objects.begin(), objects.end());
320   return true;
321 }
322
323 bool AccObject::GetChildCount(int* child_count) {
324   DCHECK(child_count);
325   *child_count = 0;
326   if (!IsSimpleElement()) {
327     long long_child_count;  // NOLINT
328     if (FAILED(accessible_->get_accChildCount(&long_child_count)))
329       return false;
330     *child_count = static_cast<int>(long_child_count);
331   }
332   return true;
333 }
334
335 bool AccObject::GetFromNavigation(long navigation_type,
336                                   scoped_refptr<AccObject>* object) {
337   DCHECK(object);
338   bool is_child_navigation = navigation_type == NAVDIR_FIRSTCHILD ||
339                              navigation_type == NAVDIR_LASTCHILD;
340   DCHECK(!is_child_navigation || !IsSimpleElement());
341   base::win::ScopedVariant object_variant;
342   HRESULT result = accessible_->accNavigate(navigation_type,
343                                             child_id_,
344                                             object_variant.Receive());
345   if (FAILED(result)) {
346     LOG(WARNING) << "Navigation from accessibility object failed";
347     return false;
348   }
349   if (result == S_FALSE || object_variant.type() == VT_EMPTY) {
350     // This indicates that there was no accessibility object found by the
351     // navigation.
352     return true;
353   }
354   AccObject* navigated_to_object;
355   if (!is_child_navigation && !IsSimpleElement()) {
356     scoped_refptr<AccObject> parent = GetParent();
357     if (!parent.get()) {
358       LOG(WARNING) << "Could not get parent for accessibiliy navigation";
359       return false;
360     }
361     navigated_to_object = CreateFromVariant(parent, object_variant);
362   } else {
363     navigated_to_object = CreateFromVariant(this, object_variant);
364   }
365   if (!navigated_to_object)
366     return false;
367   *object = navigated_to_object;
368   return true;
369 }
370
371 bool AccObject::GetWindow(HWND* window) {
372   DCHECK(window);
373   return SUCCEEDED(::WindowFromAccessibleObject(accessible_, window)) && window;
374 }
375
376 bool AccObject::GetWindowClassName(std::wstring* class_name) {
377   DCHECK(class_name);
378   HWND container_window = NULL;
379   if (GetWindow(&container_window)) {
380     wchar_t class_arr[MAX_PATH];
381     if (::GetClassName(container_window, class_arr, arraysize(class_arr))) {
382       *class_name = class_arr;
383       return true;
384     }
385   }
386   return false;
387 }
388
389 bool AccObject::GetSelectionRange(int* start_offset, int* end_offset) {
390   DCHECK(start_offset);
391   DCHECK(end_offset);
392   base::win::ScopedComPtr<IAccessibleText> accessible_text;
393   HRESULT hr = DoQueryService(IID_IAccessibleText,
394                               accessible_,
395                               accessible_text.Receive());
396   if (FAILED(hr)) {
397     LOG(ERROR) << "Could not get IAccessibleText interface. Error: " << hr
398                << "\nIs IAccessible2Proxy.dll registered?";
399     return false;
400   }
401
402   LONG selection_count = 0;
403   accessible_text->get_nSelections(&selection_count);
404   LONG start = 0, end = 0;
405   if (selection_count > 0) {
406     if (FAILED(accessible_text->get_selection(0, &start, &end))) {
407       LOG(WARNING) << "Could not get first selection";
408       return false;
409     }
410   }
411   *start_offset = start;
412   *end_offset = end;
413   return true;
414 }
415
416 bool AccObject::GetSelectedText(std::wstring* text) {
417   DCHECK(text);
418   int start = 0, end = 0;
419   if (!GetSelectionRange(&start, &end))
420     return false;
421   base::win::ScopedComPtr<IAccessibleText> accessible_text;
422   HRESULT hr = DoQueryService(IID_IAccessibleText,
423                               accessible_,
424                               accessible_text.Receive());
425   if (FAILED(hr)) {
426     LOG(ERROR) << "Could not get IAccessibleText interface. Error: " << hr
427                << "\nIs IAccessible2Proxy.dll registered?";
428     return false;
429   }
430   base::win::ScopedBstr text_bstr;
431   if (FAILED(accessible_text->get_text(start, end, text_bstr.Receive()))) {
432     LOG(WARNING) << "Could not get text from selection range";
433     return false;
434   }
435   text->assign(text_bstr, text_bstr.Length());
436   return true;
437 }
438
439 bool AccObject::IsSimpleElement() {
440   return V_I4(&child_id_) != CHILDID_SELF;
441 }
442
443 bool AccObject::Equals(AccObject* other) {
444   if (other) {
445     DCHECK(child_id_.type() == VT_I4 && other->child_id_.type() == VT_I4);
446     return accessible_.get() == other->accessible_.get() &&
447         V_I4(&child_id_) == V_I4(&other->child_id_);
448   }
449   return false;
450 }
451
452 std::wstring AccObject::GetDescription() {
453   std::wstring name = L"-", role_text = L"-", value = L"-";
454   if (GetName(&name))
455     name = L"'" + name + L"'";
456   if (GetRoleText(&role_text))
457     role_text = L"'" + role_text + L"'";
458   if (GetValue(&value))
459     value = L"'" + value + L"'";
460   int state = 0;
461   GetState(&state);
462   return base::StringPrintf(L"[%ls, %ls, %ls, 0x%x]", name.c_str(),
463                             role_text.c_str(), value.c_str(), state);
464 }
465
466 std::wstring AccObject::GetTree() {
467   std::wostringstream string_stream;
468   string_stream << L"Accessibility object tree:" << std::endl;
469   string_stream << L"[name, role_text, value, state]" << std::endl;
470
471   std::stack<std::pair<scoped_refptr<AccObject>, int> > pairs;
472   pairs.push(std::make_pair(this, 0));
473   while (!pairs.empty()) {
474     scoped_refptr<AccObject> object = pairs.top().first;
475     int depth = pairs.top().second;
476     pairs.pop();
477
478     for (int i = 0; i < depth; ++i)
479       string_stream << L"    ";
480     string_stream << object->GetDescription() << std::endl;
481
482     RefCountedAccObjectVector children;
483     if (object->GetChildren(&children)) {
484       for (int i = static_cast<int>(children.size()) - 1; i >= 0; --i)
485         pairs.push(std::make_pair(children[i], depth + 1));
486     }
487   }
488   return string_stream.str();
489 }
490
491 // static
492 AccObject* AccObject::CreateFromVariant(AccObject* object,
493                                         const VARIANT& variant) {
494   IAccessible* accessible = object->accessible_;
495   if (V_VT(&variant) == VT_I4) {
496     // According to MSDN, a server is allowed to return a full Accessibility
497     // object using the parent object and the child id. If get_accChild is
498     // called with the id, the server must return the actual IAccessible
499     // interface. Do that here to get an actual IAccessible interface if
500     // possible, since this class operates under the assumption that if the
501     // child id is not CHILDID_SELF, the object is a simple element. See the
502     // DCHECK in the constructor.
503     base::win::ScopedComPtr<IDispatch> dispatch;
504     HRESULT result = accessible->get_accChild(variant,
505                                               dispatch.Receive());
506     if (result == S_FALSE || result == E_NOINTERFACE) {
507       // The object in question really is a simple element.
508       return new AccObject(accessible, V_I4(&variant));
509     } else if (SUCCEEDED(result)) {
510       // The object in question was actually a full object.
511       return CreateFromDispatch(dispatch.get());
512     }
513     VLOG(1) << "Failed to determine if child id refers to a full "
514             << "object. Error: " << result << std::endl
515             << "Parent object: " << WideToUTF8(object->GetDescription())
516             << std::endl << "Child ID: " << V_I4(&variant);
517     return NULL;
518   } else if (V_VT(&variant) == VT_DISPATCH) {
519     return CreateFromDispatch(V_DISPATCH(&variant));
520   }
521   LOG(WARNING) << "Unrecognizable child type";
522   return NULL;
523 }
524
525 bool AccObject::PostMouseClickAtCenter(int button_down, int button_up) {
526    std::wstring class_name;
527   if (!GetWindowClassName(&class_name)) {
528     LOG(ERROR) << "Could not get class name of window for accessibility "
529                << "object: " << GetDescription();
530     return false;
531   }
532   gfx::Rect location;
533   if (class_name == L"#32768") {
534     // For some reason, it seems that menus expect screen coordinates.
535     if (!GetLocation(&location))
536       return false;
537   } else {
538     if (!GetLocationInClient(&location))
539       return false;
540   }
541
542   gfx::Point center = location.CenterPoint();
543   return PostMouseButtonMessages(button_down, button_up,
544                                  center.x(), center.y());
545 }
546
547 bool AccObject::PostMouseButtonMessages(
548     int button_down, int button_up, int x, int y) {
549   HWND container_window;
550   if (!GetWindow(&container_window))
551     return false;
552
553   LPARAM coordinates = MAKELPARAM(x, y);
554   ::PostMessage(container_window, button_down, 0, coordinates);
555   ::PostMessage(container_window, button_up, 0, coordinates);
556   return true;
557 }
558
559 // AccObjectMatcher methods
560 AccObjectMatcher::AccObjectMatcher(const std::wstring& name,
561                                    const std::wstring& role_text,
562                                    const std::wstring& value)
563     : name_(name), role_text_(role_text), value_(value) {
564 }
565
566 bool AccObjectMatcher::FindHelper(AccObject* object,
567                                   scoped_refptr<AccObject>* match) const {
568   if (DoesMatch(object)) {
569     *match = object;
570   } else {
571     // Try to match the children of |object|.
572     AccObject::RefCountedAccObjectVector children;
573     if (!object->GetChildren(&children)) {
574       LOG(ERROR) << "Could not get children of AccObject";
575       return false;
576     }
577     for (size_t i = 0; i < children.size(); ++i) {
578       if (!FindHelper(children[i], match)) {
579         return false;
580       }
581       if (*match)
582         break;
583     }
584   }
585   return true;
586 }
587
588 bool AccObjectMatcher::Find(AccObject* object,
589                             scoped_refptr<AccObject>* match) const {
590   DCHECK(object);
591   DCHECK(match);
592   *match = NULL;
593   return FindHelper(object, match);
594 }
595
596 bool AccObjectMatcher::FindInWindow(HWND hwnd,
597                                     scoped_refptr<AccObject>* match) const {
598   scoped_refptr<AccObject> object(AccObject::CreateFromWindow(hwnd));
599   if (!object) {
600     VLOG(1) << "Failed to get accessible object from window";
601     return false;
602   }
603   return Find(object.get(), match);
604 }
605
606 bool AccObjectMatcher::DoesMatch(AccObject* object) const {
607   DCHECK(object);
608   bool does_match = true;
609   std::wstring name, role_text, value;
610   if (name_.length()) {
611     object->GetName(&name);
612     does_match = MatchPattern(StringToUpperASCII(name),
613                               StringToUpperASCII(name_));
614   }
615   if (does_match && role_text_.length()) {
616     object->GetRoleText(&role_text);
617     does_match = MatchPattern(role_text, role_text_);
618   }
619   if (does_match && value_.length()) {
620     object->GetValue(&value);
621     does_match = MatchPattern(value, value_);
622   }
623   return does_match;
624 }
625
626 std::wstring AccObjectMatcher::GetDescription() const {
627   std::wostringstream ss;
628   ss << L"[";
629   if (name_.length())
630     ss << L"Name: '" << name_ << L"', ";
631   if (role_text_.length())
632     ss << L"Role: '" << role_text_ << L"', ";
633   if (value_.length())
634     ss << L"Value: '" << value_ << L"'";
635   ss << L"]";
636   return ss.str();
637 }
638
639 // AccEventObserver methods
640 AccEventObserver::AccEventObserver()
641     : event_handler_(new EventHandler(this)),
642       is_watching_(false) {
643   event_receiver_.SetListenerForEvents(this, EVENT_SYSTEM_MENUPOPUPSTART,
644                                        EVENT_OBJECT_VALUECHANGE);
645 }
646
647 AccEventObserver::~AccEventObserver() {
648   event_handler_->observer_ = NULL;
649 }
650
651 void AccEventObserver::WatchForOneValueChange(const AccObjectMatcher& matcher) {
652   is_watching_ = true;
653   watching_for_matcher_ = matcher;
654 }
655
656 void AccEventObserver::OnEventReceived(DWORD event,
657                                        HWND hwnd,
658                                        LONG object_id,
659                                        LONG child_id) {
660   // Process events in a separate task to stop reentrancy problems.
661   DCHECK(base::MessageLoop::current());
662   base::MessageLoop::current()->PostTask(
663       FROM_HERE,  base::Bind(&EventHandler::Handle, event_handler_.get(), event,
664                              hwnd, object_id, child_id));
665 }
666
667 // AccEventObserver::EventHandler methods
668 AccEventObserver::EventHandler::EventHandler(AccEventObserver* observer)
669     : observer_(observer) {
670 }
671
672 void AccEventObserver::EventHandler::Handle(DWORD event,
673                                             HWND hwnd,
674                                             LONG object_id,
675                                             LONG child_id) {
676   if (!observer_)
677     return;
678
679   switch (event) {
680     case EVENT_SYSTEM_MENUPOPUPSTART:
681       observer_->OnMenuPopup(hwnd);
682       break;
683     case IA2_EVENT_DOCUMENT_LOAD_COMPLETE:
684       observer_->OnAccDocLoad(hwnd);
685       break;
686     case IA2_EVENT_TEXT_CARET_MOVED: {
687       scoped_refptr<AccObject> object(
688           AccObject::CreateFromEvent(hwnd, object_id, child_id));
689       if (object)
690         observer_->OnTextCaretMoved(hwnd, object.get());
691       break;
692     }
693     case EVENT_OBJECT_VALUECHANGE:
694       if (observer_->is_watching_) {
695         scoped_refptr<AccObject> object(
696             AccObject::CreateFromEvent(hwnd, object_id, child_id));
697         if (object) {
698           if (observer_->watching_for_matcher_.DoesMatch(object.get())) {
699             // Stop watching before calling OnAccValueChange in case the
700             // client invokes our watch method during the call.
701             observer_->is_watching_ = false;
702             std::wstring new_value;
703             if (object->GetValue(&new_value)) {
704               observer_->OnAccValueChange(hwnd, object.get(), new_value);
705             }
706           }
707         }
708       }
709       break;
710     default:
711       break;
712   }
713 }
714
715 // Other methods
716 bool FindAccObjectInWindow(HWND hwnd, const AccObjectMatcher& matcher,
717                            scoped_refptr<AccObject>* object) {
718   DCHECK(object);
719   EXPECT_TRUE(matcher.FindInWindow(hwnd, object));
720   EXPECT_TRUE(*object) << "Element not found for matcher: "
721         << matcher.GetDescription();
722   if (!*object)
723     DumpAccessibilityTreeForWindow(hwnd);
724   return *object;
725 }
726
727 void DumpAccessibilityTreeForWindow(HWND hwnd) {
728   scoped_refptr<AccObject> object(AccObject::CreateFromWindow(hwnd));
729   if (object)
730     std::wcout << object->GetTree();
731   else
732     std::cout << "Could not get IAccessible for window" << std::endl;
733 }
734
735 bool IsDesktopUnlocked() {
736   HDESK desk = ::OpenInputDesktop(0, FALSE, DESKTOP_SWITCHDESKTOP);
737   if (desk)
738     ::CloseDesktop(desk);
739   return desk;
740 }
741
742 base::FilePath GetIAccessible2ProxyStubPath() {
743   base::FilePath path;
744   PathService::Get(chrome::DIR_APP, &path);
745   return path.AppendASCII("IAccessible2Proxy.dll");
746 }
747
748 }  // namespace chrome_frame_test