Commit 01174698 authored by Nektarios Paisios's avatar Nektarios Paisios Committed by Commit Bot

Implemented tree order comparison operators for AXObject

Will be used by AXSelectionBehavior in an upcoming patch which will determine if setting the selection on an AXRange that corresponds to an invalid DOM range should extend or shrink the range.
R=dmazzoni@chromium.org

Change-Id: I04d2e5aeb747aafb6e6f88e7fc5bd9304ba50d63
Reviewed-on: https://chromium-review.googlesource.com/952484
Commit-Queue: Nektarios Paisios <nektar@chromium.org>
Reviewed-by: default avatarDominic Mazzoni <dmazzoni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#542821}
parent 21a15b28
......@@ -1127,6 +1127,7 @@ bool AXObject::CanSetSelectedAttribute() const {
return IsSubWidget(RoleValue()) && Restriction() != kDisabled;
}
// static
bool AXObject::IsSubWidget(AccessibilityRole role) {
switch (role) {
case kCellRole:
......@@ -1634,9 +1635,8 @@ int AXObject::IndexInParent() const {
int child_count = siblings.size();
for (int index = 0; index < child_count; ++index) {
if (siblings[index].Get() == this) {
if (siblings[index].Get() == this)
return index;
}
}
return 0;
}
......@@ -2500,11 +2500,13 @@ int AXObject::LineForPosition(const VisiblePosition& position) const {
return line_count;
}
// static
bool AXObject::IsARIAControl(AccessibilityRole aria_role) {
return IsARIAInput(aria_role) || aria_role == kButtonRole ||
aria_role == kComboBoxMenuButtonRole || aria_role == kSliderRole;
}
// static
bool AXObject::IsARIAInput(AccessibilityRole aria_role) {
return aria_role == kRadioButtonRole || aria_role == kCheckBoxRole ||
aria_role == kTextFieldRole || aria_role == kSwitchRole ||
......@@ -2731,12 +2733,14 @@ AccessibilityRole AXObject::ButtonRoleType() const {
return kButtonRole;
}
// static
const AtomicString& AXObject::RoleName(AccessibilityRole role) {
static const Vector<AtomicString>* role_name_vector = CreateRoleNameVector();
return role_name_vector->at(role);
}
// static
const AtomicString& AXObject::InternalRoleName(AccessibilityRole role) {
static const Vector<AtomicString>* internal_role_name_vector =
CreateInternalRoleNameVector();
......@@ -2744,10 +2748,102 @@ const AtomicString& AXObject::InternalRoleName(AccessibilityRole role) {
return internal_role_name_vector->at(role);
}
// static
const AXObject* AXObject::LowestCommonAncestor(const AXObject& first,
const AXObject& second,
int* index_in_ancestor1,
int* index_in_ancestor2) {
*index_in_ancestor1 = -1;
*index_in_ancestor2 = -1;
if (first.IsDetached() || second.IsDetached())
return nullptr;
if (first == second)
return &first;
HeapVector<Member<const AXObject>> ancestors1;
ancestors1.push_back(&first);
while (ancestors1.back())
ancestors1.push_back(ancestors1.back()->ParentObjectUnignored());
HeapVector<Member<const AXObject>> ancestors2;
ancestors2.push_back(&second);
while (ancestors2.back())
ancestors2.push_back(ancestors2.back()->ParentObjectUnignored());
const AXObject* common_ancestor = nullptr;
while (!ancestors1.IsEmpty() && !ancestors2.IsEmpty() &&
ancestors1.back() == ancestors2.back()) {
common_ancestor = ancestors1.back();
ancestors1.pop_back();
ancestors2.pop_back();
}
if (common_ancestor) {
if (!ancestors1.IsEmpty())
*index_in_ancestor1 = ancestors1.back()->IndexInParent();
if (!ancestors2.IsEmpty())
*index_in_ancestor2 = ancestors2.back()->IndexInParent();
}
return common_ancestor;
}
VisiblePosition AXObject::VisiblePositionForIndex(int) const {
return VisiblePosition();
}
bool operator==(const AXObject& first, const AXObject& second) {
if (first.IsDetached() || second.IsDetached())
return false;
if (&first == &second) {
DCHECK_EQ(first.AXObjectID(), second.AXObjectID());
return true;
}
return false;
}
bool operator!=(const AXObject& first, const AXObject& second) {
return !(first == second);
}
bool operator<(const AXObject& first, const AXObject& second) {
if (first.IsDetached() || second.IsDetached())
return false;
int index_in_ancestor1, index_in_ancestor2;
const AXObject* ancestor = AXObject::LowestCommonAncestor(
first, second, &index_in_ancestor1, &index_in_ancestor2);
DCHECK_GE(index_in_ancestor1, -1);
DCHECK_GE(index_in_ancestor2, -1);
if (!ancestor)
return false;
return index_in_ancestor1 < index_in_ancestor2;
}
bool operator<=(const AXObject& first, const AXObject& second) {
return first == second || first < second;
}
bool operator>(const AXObject& first, const AXObject& second) {
if (first.IsDetached() || second.IsDetached())
return false;
int index_in_ancestor1, index_in_ancestor2;
const AXObject* ancestor = AXObject::LowestCommonAncestor(
first, second, &index_in_ancestor1, &index_in_ancestor2);
DCHECK_GE(index_in_ancestor1, -1);
DCHECK_GE(index_in_ancestor2, -1);
if (!ancestor)
return false;
return index_in_ancestor1 > index_in_ancestor2;
}
bool operator>=(const AXObject& first, const AXObject& second) {
return first == second || first > second;
}
std::ostream& operator<<(std::ostream& stream, const AXObject& obj) {
return stream << AXObject::InternalRoleName(obj.RoleValue()) << ": "
<< obj.ComputedName();
......
......@@ -766,7 +766,7 @@ class MODULES_EXPORT AXObject : public GarbageCollectedFinalized<AXObject> {
// Static helper functions.
static bool IsARIAControl(AccessibilityRole);
static bool IsARIAInput(AccessibilityRole);
// Is this a widget that requires container widget
// Is this a widget that requires container widget.
static bool IsSubWidget(AccessibilityRole);
static AccessibilityRole AriaRoleToWebCoreRole(const String&);
static const AtomicString& RoleName(AccessibilityRole);
......@@ -774,6 +774,15 @@ class MODULES_EXPORT AXObject : public GarbageCollectedFinalized<AXObject> {
static void AccessibleNodeListToElementVector(const AccessibleNodeList&,
HeapVector<Member<Element>>&);
// Given two AX objects, returns the lowest common ancestor and the child
// indices in that ancestor corresponding to the branch under which each
// object is to be found. If the lowest common ancestor is the same as either
// of the objects, the corresponding index is set to -1 to indicate this.
static const AXObject* LowestCommonAncestor(const AXObject& first,
const AXObject& second,
int* index_in_ancestor1,
int* index_in_ancestor2);
protected:
AXID id_;
AXObjectVector children_;
......@@ -866,6 +875,12 @@ class MODULES_EXPORT AXObject : public GarbageCollectedFinalized<AXObject> {
DISALLOW_COPY_AND_ASSIGN(AXObject);
};
MODULES_EXPORT bool operator==(const AXObject& first, const AXObject& second);
MODULES_EXPORT bool operator!=(const AXObject& first, const AXObject& second);
MODULES_EXPORT bool operator<(const AXObject& first, const AXObject& second);
MODULES_EXPORT bool operator<=(const AXObject& first, const AXObject& second);
MODULES_EXPORT bool operator>(const AXObject& first, const AXObject& second);
MODULES_EXPORT bool operator>=(const AXObject& first, const AXObject& second);
MODULES_EXPORT std::ostream& operator<<(std::ostream&, const AXObject&);
#define DEFINE_AX_OBJECT_TYPE_CASTS(thisType, predicate) \
......
......@@ -78,4 +78,46 @@ TEST_F(AccessibilityTest, SimpleTreeNavigation) {
br->PreviousSibling()->RoleValue());
}
TEST_F(AccessibilityTest, ComparisonOperators) {
SetBodyInnerHTML(R"HTML(<input id='input' type='text' value='value'>"
R"<p id='paragraph'>hello<br id='br'>there</p>"
R"<button id='button'>button</button>)HTML");
const AXObject* root = GetAXRootObject();
ASSERT_NE(nullptr, root);
const AXObject* input = GetAXObjectByElementId("input");
ASSERT_NE(nullptr, input);
const AXObject* paragraph = GetAXObjectByElementId("paragraph");
ASSERT_NE(nullptr, paragraph);
const AXObject* br = GetAXObjectByElementId("br");
ASSERT_NE(nullptr, br);
const AXObject* button = GetAXObjectByElementId("button");
ASSERT_NE(nullptr, button);
EXPECT_TRUE(*root == *root);
EXPECT_FALSE(*root != *root);
EXPECT_FALSE(*root < *root);
EXPECT_TRUE(*root <= *root);
EXPECT_FALSE(*root > *root);
EXPECT_TRUE(*root >= *root);
EXPECT_TRUE(*input > *root);
EXPECT_TRUE(*input >= *root);
EXPECT_FALSE(*input < *root);
EXPECT_FALSE(*input <= *root);
EXPECT_TRUE(*input != *root);
EXPECT_TRUE(*input < *paragraph);
EXPECT_TRUE(*br > *input);
EXPECT_TRUE(*paragraph < *br);
EXPECT_TRUE(*br >= *paragraph);
EXPECT_TRUE(*paragraph < *button);
EXPECT_TRUE(*button > *br);
EXPECT_FALSE(*button < *button);
EXPECT_TRUE(*button <= *button);
EXPECT_TRUE(*button >= *button);
EXPECT_FALSE(*button > *button);
}
} // namespace blink
......@@ -61,10 +61,9 @@ std::ostringstream& AccessibilityTest::PrintAXTreeHelper(
stream << std::string(level * 2, '+');
stream << *root << std::endl;
for (const auto child : const_cast<AXObject*>(root)->Children()) {
for (const Member<AXObject> child : root->Children()) {
DCHECK(child);
PrintAXTreeHelper(stream, child.Get(), level + 1);
stream << std::endl;
}
return stream;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment