diff options
author | kkania@chromium.org <kkania@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-10-06 17:44:10 +0000 |
---|---|---|
committer | kkania@chromium.org <kkania@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-10-06 17:44:10 +0000 |
commit | 62f0486fab337dba8a359c4e938dc3edd563ca5e (patch) | |
tree | 3865212c64368c88897d5d8bda43ea75e8638010 /chrome_frame/test | |
parent | 4ee5bfeeb8ff41f0d036cd2964929b76cee2d75b (diff) | |
download | chromium_src-62f0486fab337dba8a359c4e938dc3edd563ca5e.zip chromium_src-62f0486fab337dba8a359c4e938dc3edd563ca5e.tar.gz chromium_src-62f0486fab337dba8a359c4e938dc3edd563ca5e.tar.bz2 |
Find additional children of accessible elements that cannot be found using MSAA AccessibleChildren.
BUG=none
TEST=none
Review URL: http://codereview.chromium.org/3579010
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@61671 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome_frame/test')
-rw-r--r-- | chrome_frame/test/chrome_frame_ui_test_utils.cc | 154 | ||||
-rw-r--r-- | chrome_frame/test/chrome_frame_ui_test_utils.h | 15 |
2 files changed, 121 insertions, 48 deletions
diff --git a/chrome_frame/test/chrome_frame_ui_test_utils.cc b/chrome_frame/test/chrome_frame_ui_test_utils.cc index 709d06c..8de7d8e 100644 --- a/chrome_frame/test/chrome_frame_ui_test_utils.cc +++ b/chrome_frame/test/chrome_frame_ui_test_utils.cc @@ -265,63 +265,51 @@ bool AccObject::GetChildren(RefCountedAccObjectVector* client_objects) { } if (child_count == 0) return true; - scoped_array<VARIANT> unscoped_children(new VARIANT[child_count]); - long actual_child_count; // NOLINT + + RefCountedAccObjectVector objects; + // Find children using |AccessibleChildren|. + scoped_array<VARIANT> children(new VARIANT[child_count]); + long found_child_count; // NOLINT if (FAILED(AccessibleChildren(accessible_, 0L, child_count, - unscoped_children.get(), - &actual_child_count))) { + children.get(), + &found_child_count))) { LOG(ERROR) << "Failed to get children of accessible object"; return false; } - if (actual_child_count == 0) - return true; - // Convert the retrieved children array into an array of scoped children. - scoped_array<ScopedVariant> children(new ScopedVariant[child_count]); - for (int i = 0; i < child_count; ++i) { - children[i].Reset(unscoped_children[i]); + if (found_child_count > 0) { + for (int i = 0; i < found_child_count; i++) { + scoped_refptr<AccObject> obj = CreateFromVariant(this, children[i]); + if (obj) + objects.push_back(obj); + ::VariantClear(&children[i]); + } } - RefCountedAccObjectVector objects; - for (int i = 0; i < actual_child_count; i++) { - ScopedComPtr<IDispatch> dispatch; - if (children[i].type() == VT_I4) { - // According to MSDN, a server is allowed to return a full Accessibility - // object using the parent object and the child id. If get_accChild is - // called with the id, the server must return the actual IAccessible - // interface. Do that here to get an actual IAccessible interface if - // possible, since this class operates under the assumption that if the - // child id is not CHILDID_SELF, the object is a simple element. See the - // DCHECK in the constructor. - HRESULT result = accessible_->get_accChild(children[i], - dispatch.Receive()); - if (result == S_FALSE || result == E_NOINTERFACE) { - // The object in question really is a simple element. Add it. - objects.push_back(new AccObject(accessible_, V_I4(&children[i]))); - continue; - } else if (FAILED(result)) { - DLOG(WARNING) << "Failed to determine if child id refers to a full " - << "object. Error: " << result << std::endl - << "Parent object: " << WideToUTF8(GetDescription()) - << std::endl << "Child ID: " << V_I4(&children[i]); - // Disregard this object. - continue; + // In some cases, there are more children which can be found only by using + // the deprecated |accNavigate| method. Many of the menus, such as + // 'Favorites', cannot be found in IE6 using |AccessibileChildren|. Here we + // attempt a best effort at finding some remaining children. + int remaining_child_count = child_count - found_child_count; + scoped_refptr<AccObject> child_object; + if (remaining_child_count > 0) { + GetFromNavigation(NAVDIR_FIRSTCHILD, &child_object); + } + while (remaining_child_count > 0 && child_object) { + // Add to the children list if this child was not found earlier. + bool already_found = false; + for (size_t i = 0; i < objects.size(); ++i) { + if (child_object->Equals(objects[i])) { + already_found = true; + break; } - // The object in question was actually a full object. It is saved in the - // |dispatch| arg and will be added down below. - } else if (children[i].type() == VT_DISPATCH) { - dispatch.Attach(V_DISPATCH(&children[i].Release())); - } else { - DLOG(WARNING) << "Unrecognizable child type, omitting from children"; - continue; } - - scoped_refptr<AccObject> child = CreateFromDispatch(dispatch.get()); - if (child) { - objects.push_back(child); - } else { - LOG(ERROR) << "Failed to create AccObject from IDispatch"; - return false; + if (!already_found) { + objects.push_back(child_object); + remaining_child_count--; } + scoped_refptr<AccObject> next_child_object; + child_object->GetFromNavigation(NAVDIR_NEXT, &next_child_object); + child_object = next_child_object; } client_objects->insert(client_objects->end(), objects.begin(), objects.end()); @@ -340,6 +328,42 @@ bool AccObject::GetChildCount(int* child_count) { return true; } +bool AccObject::GetFromNavigation(long navigation_type, + scoped_refptr<AccObject>* object) { + DCHECK(object); + bool is_child_navigation = navigation_type == NAVDIR_FIRSTCHILD || + navigation_type == NAVDIR_LASTCHILD; + DCHECK(!is_child_navigation || !IsSimpleElement()); + ScopedVariant object_variant; + HRESULT result = accessible_->accNavigate(navigation_type, + child_id_, + object_variant.Receive()); + if (FAILED(result)) { + LOG(WARNING) << "Navigation from accessibility object failed"; + return false; + } + if (result == S_FALSE || object_variant.type() == VT_EMPTY) { + // This indicates that there was no accessibility object found by the + // navigation. + return true; + } + AccObject* navigated_to_object; + if (!is_child_navigation && !IsSimpleElement()) { + scoped_refptr<AccObject> parent = GetParent(); + if (!parent.get()) { + LOG(WARNING) << "Could not get parent for accessibiliy navigation"; + return false; + } + navigated_to_object = CreateFromVariant(parent, object_variant); + } else { + navigated_to_object = CreateFromVariant(this, object_variant); + } + if (!navigated_to_object) + return false; + *object = navigated_to_object; + return true; +} + bool AccObject::GetWindow(HWND* window) { DCHECK(window); return SUCCEEDED(::WindowFromAccessibleObject(accessible_, window)) && window; @@ -410,6 +434,40 @@ std::wstring AccObject::GetTree() { return string_stream.str(); } +// static +AccObject* AccObject::CreateFromVariant(AccObject* object, + const VARIANT& variant) { + IAccessible* accessible = object->accessible_; + if (V_VT(&variant) == VT_I4) { + // According to MSDN, a server is allowed to return a full Accessibility + // object using the parent object and the child id. If get_accChild is + // called with the id, the server must return the actual IAccessible + // interface. Do that here to get an actual IAccessible interface if + // possible, since this class operates under the assumption that if the + // child id is not CHILDID_SELF, the object is a simple element. See the + // DCHECK in the constructor. + ScopedComPtr<IDispatch> dispatch; + HRESULT result = accessible->get_accChild(variant, + dispatch.Receive()); + if (result == S_FALSE || result == E_NOINTERFACE) { + // The object in question really is a simple element. + return new AccObject(accessible, V_I4(&variant)); + } else if (SUCCEEDED(result)) { + // The object in question was actually a full object. + return CreateFromDispatch(dispatch.get()); + } + LOG(WARNING) << "Failed to determine if child id refers to a full " + << "object. Error: " << result << std::endl + << "Parent object: " << WideToUTF8(object->GetDescription()) + << std::endl << "Child ID: " << V_I4(&variant); + return NULL; + } else if (V_VT(&variant) == VT_DISPATCH) { + return CreateFromDispatch(V_DISPATCH(&variant)); + } + LOG(WARNING) << "Unrecognizable child type"; + return NULL; +} + bool AccObject::PostMouseButtonMessages(int button_up, int button_down) { std::wstring class_name; if (!GetWindowClassName(&class_name)) { diff --git a/chrome_frame/test/chrome_frame_ui_test_utils.h b/chrome_frame/test/chrome_frame_ui_test_utils.h index b6b4f6a..0b8c1f2 100644 --- a/chrome_frame/test/chrome_frame_ui_test_utils.h +++ b/chrome_frame/test/chrome_frame_ui_test_utils.h @@ -110,6 +110,14 @@ class AccObject : public base::RefCounted<AccObject> { // Gets the number of children of this object and returns true on success. bool GetChildCount(int* child_count); + // Gets an object resulting from the navigation and returns true if the + // navigation completed successfully. Even if true, |object| may still + // refer to a NULL object if no object was found from the navigation. + // |navigation_type| may be any navigation constant, such as NAVDIR_NEXT. + // Note: This method uses a deprecated IAccessible method. + bool GetFromNavigation(long navigation_type, + scoped_refptr<AccObject>* object); + // Gets the window containing this object and returns true on success. This // method will return false if the object is not contained within a window. bool GetWindow(HWND* window); @@ -136,6 +144,13 @@ class AccObject : public base::RefCounted<AccObject> { friend class base::RefCounted<AccObject>; ~AccObject() {} + // Creates an AccObject using the given variant, returning NULL on failure. + // The variant should be of type |VT_I4| referring to the id of a child of + // |object|, or of type |VT_DISPATCH|. This method is useful for converting + // the variant returned by many of the IAccessible methods into an AccObject. + static AccObject* CreateFromVariant(AccObject* object, + const VARIANT& variant); + // Helper method for posting mouse button messages at this object's location. bool PostMouseButtonMessages(int button_up, int button_down); |