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 749