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 { ...@@ -1127,6 +1127,7 @@ bool AXObject::CanSetSelectedAttribute() const {
return IsSubWidget(RoleValue()) && Restriction() != kDisabled; return IsSubWidget(RoleValue()) && Restriction() != kDisabled;
} }
// static
bool AXObject::IsSubWidget(AccessibilityRole role) { bool AXObject::IsSubWidget(AccessibilityRole role) {
switch (role) { switch (role) {
case kCellRole: case kCellRole:
...@@ -1634,9 +1635,8 @@ int AXObject::IndexInParent() const { ...@@ -1634,9 +1635,8 @@ int AXObject::IndexInParent() const {
int child_count = siblings.size(); int child_count = siblings.size();
for (int index = 0; index < child_count; ++index) { for (int index = 0; index < child_count; ++index) {
if (siblings[index].Get() == this) { if (siblings[index].Get() == this)
return index; return index;
}
} }
return 0; return 0;
} }
...@@ -2500,11 +2500,13 @@ int AXObject::LineForPosition(const VisiblePosition& position) const { ...@@ -2500,11 +2500,13 @@ int AXObject::LineForPosition(const VisiblePosition& position) const {
return line_count; return line_count;
} }
// static
bool AXObject::IsARIAControl(AccessibilityRole aria_role) { bool AXObject::IsARIAControl(AccessibilityRole aria_role) {
return IsARIAInput(aria_role) || aria_role == kButtonRole || return IsARIAInput(aria_role) || aria_role == kButtonRole ||
aria_role == kComboBoxMenuButtonRole || aria_role == kSliderRole; aria_role == kComboBoxMenuButtonRole || aria_role == kSliderRole;
} }
// static
bool AXObject::IsARIAInput(AccessibilityRole aria_role) { bool AXObject::IsARIAInput(AccessibilityRole aria_role) {
return aria_role == kRadioButtonRole || aria_role == kCheckBoxRole || return aria_role == kRadioButtonRole || aria_role == kCheckBoxRole ||
aria_role == kTextFieldRole || aria_role == kSwitchRole || aria_role == kTextFieldRole || aria_role == kSwitchRole ||
...@@ -2731,12 +2733,14 @@ AccessibilityRole AXObject::ButtonRoleType() const { ...@@ -2731,12 +2733,14 @@ AccessibilityRole AXObject::ButtonRoleType() const {
return kButtonRole; return kButtonRole;
} }
// static
const AtomicString& AXObject::RoleName(AccessibilityRole role) { const AtomicString& AXObject::RoleName(AccessibilityRole role) {
static const Vector<AtomicString>* role_name_vector = CreateRoleNameVector(); static const Vector<AtomicString>* role_name_vector = CreateRoleNameVector();
return role_name_vector->at(role); return role_name_vector->at(role);
} }
// static
const AtomicString& AXObject::InternalRoleName(AccessibilityRole role) { const AtomicString& AXObject::InternalRoleName(AccessibilityRole role) {
static const Vector<AtomicString>* internal_role_name_vector = static const Vector<AtomicString>* internal_role_name_vector =
CreateInternalRoleNameVector(); CreateInternalRoleNameVector();
...@@ -2744,10 +2748,102 @@ const AtomicString& AXObject::InternalRoleName(AccessibilityRole role) { ...@@ -2744,10 +2748,102 @@ const AtomicString& AXObject::InternalRoleName(AccessibilityRole role) {
return internal_role_name_vector->at(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 { VisiblePosition AXObject::VisiblePositionForIndex(int) const {
return VisiblePosition(); 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) { std::ostream& operator<<(std::ostream& stream, const AXObject& obj) {
return stream << AXObject::InternalRoleName(obj.RoleValue()) << ": " return stream << AXObject::InternalRoleName(obj.RoleValue()) << ": "
<< obj.ComputedName(); << obj.ComputedName();
......
...@@ -766,7 +766,7 @@ class MODULES_EXPORT AXObject : public GarbageCollectedFinalized<AXObject> { ...@@ -766,7 +766,7 @@ class MODULES_EXPORT AXObject : public GarbageCollectedFinalized<AXObject> {
// Static helper functions. // Static helper functions.
static bool IsARIAControl(AccessibilityRole); static bool IsARIAControl(AccessibilityRole);
static bool IsARIAInput(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 bool IsSubWidget(AccessibilityRole);
static AccessibilityRole AriaRoleToWebCoreRole(const String&); static AccessibilityRole AriaRoleToWebCoreRole(const String&);
static const AtomicString& RoleName(AccessibilityRole); static const AtomicString& RoleName(AccessibilityRole);
...@@ -774,6 +774,15 @@ class MODULES_EXPORT AXObject : public GarbageCollectedFinalized<AXObject> { ...@@ -774,6 +774,15 @@ class MODULES_EXPORT AXObject : public GarbageCollectedFinalized<AXObject> {
static void AccessibleNodeListToElementVector(const AccessibleNodeList&, static void AccessibleNodeListToElementVector(const AccessibleNodeList&,
HeapVector<Member<Element>>&); 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: protected:
AXID id_; AXID id_;
AXObjectVector children_; AXObjectVector children_;
...@@ -866,6 +875,12 @@ class MODULES_EXPORT AXObject : public GarbageCollectedFinalized<AXObject> { ...@@ -866,6 +875,12 @@ class MODULES_EXPORT AXObject : public GarbageCollectedFinalized<AXObject> {
DISALLOW_COPY_AND_ASSIGN(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&); MODULES_EXPORT std::ostream& operator<<(std::ostream&, const AXObject&);
#define DEFINE_AX_OBJECT_TYPE_CASTS(thisType, predicate) \ #define DEFINE_AX_OBJECT_TYPE_CASTS(thisType, predicate) \
......
...@@ -78,4 +78,46 @@ TEST_F(AccessibilityTest, SimpleTreeNavigation) { ...@@ -78,4 +78,46 @@ TEST_F(AccessibilityTest, SimpleTreeNavigation) {
br->PreviousSibling()->RoleValue()); 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 } // namespace blink
...@@ -61,10 +61,9 @@ std::ostringstream& AccessibilityTest::PrintAXTreeHelper( ...@@ -61,10 +61,9 @@ std::ostringstream& AccessibilityTest::PrintAXTreeHelper(
stream << std::string(level * 2, '+'); stream << std::string(level * 2, '+');
stream << *root << std::endl; stream << *root << std::endl;
for (const auto child : const_cast<AXObject*>(root)->Children()) { for (const Member<AXObject> child : root->Children()) {
DCHECK(child); DCHECK(child);
PrintAXTreeHelper(stream, child.Get(), level + 1); PrintAXTreeHelper(stream, child.Get(), level + 1);
stream << std::endl;
} }
return stream; 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