Home | History | Annotate | Download | only in test
      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