Commit 9a9d421a authored by Nektarios Paisios's avatar Nektarios Paisios Committed by Commit Bot

Implemented support for CSS before and after content

Bug: 852266

R=dmazzoni@chromium.org

Change-Id: I264359f1fa1f345ae41fed35604dd96a49d7d5eb
Reviewed-on: https://chromium-review.googlesource.com/1117335
Commit-Queue: Nektarios Paisios <nektar@chromium.org>
Reviewed-by: default avatarDominic Mazzoni <dmazzoni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#571785}
parent 183b5462
...@@ -986,7 +986,7 @@ IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityBoundsInherits) { ...@@ -986,7 +986,7 @@ IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityBoundsInherits) {
RunHtmlTest(FILE_PATH_LITERAL("bounds-inherits.html")); RunHtmlTest(FILE_PATH_LITERAL("bounds-inherits.html"));
} }
IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibiltyBoundsClips) { IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityBoundsClips) {
RunHtmlTest(FILE_PATH_LITERAL("bounds-clips.html")); RunHtmlTest(FILE_PATH_LITERAL("bounds-clips.html"));
} }
......
...@@ -800,6 +800,18 @@ bool AXObject::IsPasswordFieldAndShouldHideValue() const { ...@@ -800,6 +800,18 @@ bool AXObject::IsPasswordFieldAndShouldHideValue() const {
return IsPasswordField(); return IsPasswordField();
} }
bool AXObject::IsTextObject() const {
// Objects with |kLineBreakRole| are HTML <br> elements and are not backed by
// DOM text nodes. We can't mark them as text objects for that reason.
switch (RoleValue()) {
case kInlineTextBoxRole:
case kStaticTextRole:
return true;
default:
return false;
}
}
bool AXObject::IsClickable() const { bool AXObject::IsClickable() const {
if (IsButton() || IsLink() || IsTextControl()) if (IsButton() || IsLink() || IsTextControl())
return true; return true;
......
...@@ -510,6 +510,7 @@ class MODULES_EXPORT AXObject : public GarbageCollectedFinalized<AXObject> { ...@@ -510,6 +510,7 @@ class MODULES_EXPORT AXObject : public GarbageCollectedFinalized<AXObject> {
virtual bool IsSpinButton() const { return RoleValue() == kSpinButtonRole; } virtual bool IsSpinButton() const { return RoleValue() == kSpinButtonRole; }
bool IsTabItem() const { return RoleValue() == kTabRole; } bool IsTabItem() const { return RoleValue() == kTabRole; }
virtual bool IsTextControl() const { return false; } virtual bool IsTextControl() const { return false; }
bool IsTextObject() const;
bool IsTree() const { return RoleValue() == kTreeRole; } bool IsTree() const { return RoleValue() == kTreeRole; }
virtual bool IsVirtualObject() const { return false; } virtual bool IsVirtualObject() const { return false; }
bool IsWebArea() const { return RoleValue() == kWebAreaRole; } bool IsWebArea() const { return RoleValue() == kWebAreaRole; }
......
...@@ -20,9 +20,12 @@ namespace blink { ...@@ -20,9 +20,12 @@ namespace blink {
// static // static
const AXPosition AXPosition::CreatePositionBeforeObject(const AXObject& child) { const AXPosition AXPosition::CreatePositionBeforeObject(const AXObject& child) {
if (child.IsDetached())
return {};
// If |child| is a text object, make behavior the same as // If |child| is a text object, make behavior the same as
// |CreateFirstPositionInObject| so that equality would hold. // |CreateFirstPositionInObject| so that equality would hold.
if (child.GetNode() && child.GetNode()->IsTextNode()) if (child.IsTextObject())
return CreateFirstPositionInObject(child); return CreateFirstPositionInObject(child);
const AXObject* parent = child.ParentObjectUnignored(); const AXObject* parent = child.ParentObjectUnignored();
...@@ -35,9 +38,12 @@ const AXPosition AXPosition::CreatePositionBeforeObject(const AXObject& child) { ...@@ -35,9 +38,12 @@ const AXPosition AXPosition::CreatePositionBeforeObject(const AXObject& child) {
// static // static
const AXPosition AXPosition::CreatePositionAfterObject(const AXObject& child) { const AXPosition AXPosition::CreatePositionAfterObject(const AXObject& child) {
if (child.IsDetached())
return {};
// If |child| is a text object, make behavior the same as // If |child| is a text object, make behavior the same as
// |CreateLastPositionInObject| so that equality would hold. // |CreateLastPositionInObject| so that equality would hold.
if (child.GetNode() && child.GetNode()->IsTextNode()) if (child.IsTextObject())
return CreateLastPositionInObject(child); return CreateLastPositionInObject(child);
const AXObject* parent = child.ParentObjectUnignored(); const AXObject* parent = child.ParentObjectUnignored();
...@@ -51,7 +57,10 @@ const AXPosition AXPosition::CreatePositionAfterObject(const AXObject& child) { ...@@ -51,7 +57,10 @@ const AXPosition AXPosition::CreatePositionAfterObject(const AXObject& child) {
// static // static
const AXPosition AXPosition::CreateFirstPositionInObject( const AXPosition AXPosition::CreateFirstPositionInObject(
const AXObject& container) { const AXObject& container) {
if (container.GetNode() && container.GetNode()->IsTextNode()) { if (container.IsDetached())
return {};
if (container.IsTextObject()) {
AXPosition position(container); AXPosition position(container);
position.text_offset_or_child_index_ = 0; position.text_offset_or_child_index_ = 0;
DCHECK(position.IsValid()); DCHECK(position.IsValid());
...@@ -71,7 +80,10 @@ const AXPosition AXPosition::CreateFirstPositionInObject( ...@@ -71,7 +80,10 @@ const AXPosition AXPosition::CreateFirstPositionInObject(
// static // static
const AXPosition AXPosition::CreateLastPositionInObject( const AXPosition AXPosition::CreateLastPositionInObject(
const AXObject& container) { const AXObject& container) {
if (container.GetNode() && container.GetNode()->IsTextNode()) { if (container.IsDetached())
return {};
if (container.IsTextObject()) {
AXPosition position(container); AXPosition position(container);
position.text_offset_or_child_index_ = position.MaxTextOffset(); position.text_offset_or_child_index_ = position.MaxTextOffset();
DCHECK(position.IsValid()); DCHECK(position.IsValid());
...@@ -93,8 +105,9 @@ const AXPosition AXPosition::CreatePositionInTextObject( ...@@ -93,8 +105,9 @@ const AXPosition AXPosition::CreatePositionInTextObject(
const AXObject& container, const AXObject& container,
const int offset, const int offset,
const TextAffinity affinity) { const TextAffinity affinity) {
DCHECK(container.GetNode() && container.GetNode()->IsTextNode()) if (container.IsDetached() || !container.IsTextObject())
<< "Text positions should be anchored to a text node."; return {};
AXPosition position(container); AXPosition position(container);
position.text_offset_or_child_index_ = offset; position.text_offset_or_child_index_ = offset;
position.affinity_ = affinity; position.affinity_ = affinity;
...@@ -214,6 +227,16 @@ int AXPosition::MaxTextOffset() const { ...@@ -214,6 +227,16 @@ int AXPosition::MaxTextOffset() const {
return 0; return 0;
} }
if (container_object_->IsAXInlineTextBox() || !container_object_->GetNode()) {
// 1. The |Node| associated with an inline text box contains all the text in
// the static text object parent, whilst the inline text box might contain
// only part of it.
// 2. Some accessibility objects, such as those used for CSS "::before" and
// "::after" content, don't have an associated text node. We retrieve the
// text from the inline text box or layout object itself.
return container_object_->ComputedName().length();
}
// TODO(nektar): Use LayoutNG offset mapping instead of |TextIterator|. // TODO(nektar): Use LayoutNG offset mapping instead of |TextIterator|.
const auto first_position = const auto first_position =
Position::FirstPositionInNode(*container_object_->GetNode()); Position::FirstPositionInNode(*container_object_->GetNode());
...@@ -225,29 +248,38 @@ int AXPosition::MaxTextOffset() const { ...@@ -225,29 +248,38 @@ int AXPosition::MaxTextOffset() const {
bool AXPosition::IsValid() const { bool AXPosition::IsValid() const {
if (!container_object_ || container_object_->IsDetached()) if (!container_object_ || container_object_->IsDetached())
return false; return false;
if (!container_object_->GetNode() || if (!container_object_->GetDocument())
return false;
// Some container objects, such as those for CSS "::before" and "::after"
// text, don't have associated DOM nodes.
if (container_object_->GetNode() &&
!container_object_->GetNode()->isConnected()) { !container_object_->GetNode()->isConnected()) {
return false; return false;
} }
if (!container_object_->GetNode()->IsTextNode()) { if (IsTextPosition()) {
if (text_offset_or_child_index_ > MaxTextOffset())
return false;
} else {
if (text_offset_or_child_index_ > container_object_->ChildCount()) if (text_offset_or_child_index_ > container_object_->ChildCount())
return false; return false;
} }
DCHECK(container_object_->GetNode()->GetDocument().IsActive()); DCHECK(container_object_->GetDocument()->IsActive());
DCHECK(!container_object_->GetNode()->GetDocument().NeedsLayoutTreeUpdate()); DCHECK(!container_object_->GetDocument()->NeedsLayoutTreeUpdate());
#if DCHECK_IS_ON() #if DCHECK_IS_ON()
DCHECK_EQ(container_object_->GetNode()->GetDocument().DomTreeVersion(), DCHECK_EQ(container_object_->GetDocument()->DomTreeVersion(),
dom_tree_version_); dom_tree_version_);
DCHECK_EQ(container_object_->GetNode()->GetDocument().StyleVersion(), DCHECK_EQ(container_object_->GetDocument()->StyleVersion(), style_version_);
style_version_);
#endif // DCHECK_IS_ON() #endif // DCHECK_IS_ON()
return true; return true;
} }
bool AXPosition::IsTextPosition() const { bool AXPosition::IsTextPosition() const {
return container_object_ && container_object_->GetNode()->IsTextNode(); // We don't call |IsValid| from here because |IsValid| uses this method.
if (!container_object_)
return false;
return container_object_->IsTextObject();
} }
const AXPosition AXPosition::CreateNextPosition() const { const AXPosition AXPosition::CreateNextPosition() const {
...@@ -286,7 +318,7 @@ const AXPosition AXPosition::CreatePreviousPosition() const { ...@@ -286,7 +318,7 @@ const AXPosition AXPosition::CreatePreviousPosition() const {
if (container_object_->ChildCount()) { if (container_object_->ChildCount()) {
const AXObject* last_child = container_object_->LastChild(); const AXObject* last_child = container_object_->LastChild();
// Dont skip over any intervening text. // Dont skip over any intervening text.
if (last_child->GetNode() && last_child->GetNode()->IsTextNode()) if (last_child->IsTextObject())
return CreatePositionAfterObject(*last_child); return CreatePositionAfterObject(*last_child);
return CreatePositionBeforeObject(*last_child); return CreatePositionBeforeObject(*last_child);
...@@ -306,10 +338,8 @@ const AXPosition AXPosition::CreatePreviousPosition() const { ...@@ -306,10 +338,8 @@ const AXPosition AXPosition::CreatePreviousPosition() const {
} }
// Dont skip over any intervening text. // Dont skip over any intervening text.
if (object_before_position->GetNode() && if (object_before_position->IsTextObject())
object_before_position->GetNode()->IsTextNode()) {
return CreatePositionAfterObject(*object_before_position); return CreatePositionAfterObject(*object_before_position);
}
return CreatePositionBeforeObject(*object_before_position); return CreatePositionBeforeObject(*object_before_position);
} }
...@@ -365,7 +395,10 @@ const AXPosition AXPosition::AsValidDOMPosition( ...@@ -365,7 +395,10 @@ const AXPosition AXPosition::AsValidDOMPosition(
// object after a tree position are mock or virtual objects, since mock or // object after a tree position are mock or virtual objects, since mock or
// virtual objects will not be present in the DOM tree. Alternatively, in the // virtual objects will not be present in the DOM tree. Alternatively, in the
// case of an "after children" position, we need to check if the last child of // case of an "after children" position, we need to check if the last child of
// the container object is mock or virtual and adjust accordingly. // the container object is mock or virtual and adjust accordingly. Abstract
// inline text boxes and static text nodes for CSS "::before" and "::after"
// positions are also considered to be virtual since they don't have an
// associated DOM node.
// More Explaination: // More Explaination:
// If the child after a tree position doesn't have an associated node in the // If the child after a tree position doesn't have an associated node in the
...@@ -383,7 +416,8 @@ const AXPosition AXPosition::AsValidDOMPosition( ...@@ -383,7 +416,8 @@ const AXPosition AXPosition::AsValidDOMPosition(
DCHECK(container); DCHECK(container);
const AXObject* child = ChildAfterTreePosition(); const AXObject* child = ChildAfterTreePosition();
const AXObject* last_child = container->LastChild(); const AXObject* last_child = container->LastChild();
if (container->IsMockObject() || container->IsVirtualObject() || if ((IsTextPosition() && !container->GetNode()) ||
container->IsMockObject() || container->IsVirtualObject() ||
(!child && last_child && (!child && last_child &&
(!last_child->GetNode() || last_child->IsMockObject() || (!last_child->GetNode() || last_child->IsMockObject() ||
last_child->IsVirtualObject())) || last_child->IsVirtualObject())) ||
...@@ -398,7 +432,7 @@ const AXPosition AXPosition::AsValidDOMPosition( ...@@ -398,7 +432,7 @@ const AXPosition AXPosition::AsValidDOMPosition(
} }
if (container->GetNode()) if (container->GetNode())
return this->AsUnignoredPosition(adjustment_behavior); return *this;
DCHECK(container->IsAXLayoutObject()) DCHECK(container->IsAXLayoutObject())
<< "Non virtual and non mock AX objects that are not associated to a DOM " << "Non virtual and non mock AX objects that are not associated to a DOM "
...@@ -423,7 +457,7 @@ const AXPosition AXPosition::AsValidDOMPosition( ...@@ -423,7 +457,7 @@ const AXPosition AXPosition::AsValidDOMPosition(
const PositionWithAffinity AXPosition::ToPositionWithAffinity( const PositionWithAffinity AXPosition::ToPositionWithAffinity(
const AXPositionAdjustmentBehavior adjustment_behavior) const { const AXPositionAdjustmentBehavior adjustment_behavior) const {
const AXPosition adjusted_position = AsValidDOMPosition(); const AXPosition adjusted_position = AsValidDOMPosition(adjustment_behavior);
if (!adjusted_position.IsValid()) if (!adjusted_position.IsValid())
return {}; return {};
......
...@@ -17,6 +17,20 @@ namespace blink { ...@@ -17,6 +17,20 @@ namespace blink {
namespace { namespace {
constexpr char kCSSBeforeAndAfter[] = R"HTML(
<style>
q::before {
content: "«";
color: blue;
}
q::after {
content: "»";
color: red;
}
</style>
<q id="quote">Hello there,</q> she said.
)HTML";
constexpr char kHTMLTable[] = R"HTML( constexpr char kHTMLTable[] = R"HTML(
<p id="before">Before table.</p> <p id="before">Before table.</p>
<table id="table" border="1"> <table id="table" border="1">
...@@ -163,15 +177,22 @@ TEST_F(AccessibilityTest, PositionBeforeLineBreak) { ...@@ -163,15 +177,22 @@ TEST_F(AccessibilityTest, PositionBeforeLineBreak) {
const AXObject* ax_br = GetAXObjectByElementId("br"); const AXObject* ax_br = GetAXObjectByElementId("br");
ASSERT_NE(nullptr, ax_br); ASSERT_NE(nullptr, ax_br);
ASSERT_EQ(AccessibilityRole::kLineBreakRole, ax_br->RoleValue()); ASSERT_EQ(AccessibilityRole::kLineBreakRole, ax_br->RoleValue());
const AXObject* ax_div = ax_br->ParentObjectUnignored();
ASSERT_NE(nullptr, ax_div);
ASSERT_EQ(AccessibilityRole::kGenericContainerRole, ax_div->RoleValue());
const auto ax_position = AXPosition::CreatePositionBeforeObject(*ax_br); const auto ax_position = AXPosition::CreatePositionBeforeObject(*ax_br);
EXPECT_FALSE(ax_position.IsTextPosition());
EXPECT_EQ(ax_div, ax_position.ContainerObject());
EXPECT_EQ(1, ax_position.ChildIndex());
EXPECT_EQ(ax_br, ax_position.ChildAfterTreePosition());
const auto position = ax_position.ToPositionWithAffinity(); const auto position = ax_position.ToPositionWithAffinity();
EXPECT_EQ(GetDocument().body(), position.AnchorNode()); EXPECT_EQ(GetDocument().body(), position.AnchorNode());
EXPECT_EQ(1, position.GetPosition().OffsetInContainerNode()); EXPECT_EQ(1, position.GetPosition().OffsetInContainerNode());
const auto ax_position_from_dom = AXPosition::FromPosition(position); const auto ax_position_from_dom = AXPosition::FromPosition(position);
EXPECT_EQ(ax_position, ax_position_from_dom); EXPECT_EQ(ax_position, ax_position_from_dom);
EXPECT_EQ(ax_br, ax_position_from_dom.ChildAfterTreePosition());
} }
TEST_F(AccessibilityTest, PositionAfterLineBreak) { TEST_F(AccessibilityTest, PositionAfterLineBreak) {
...@@ -179,18 +200,25 @@ TEST_F(AccessibilityTest, PositionAfterLineBreak) { ...@@ -179,18 +200,25 @@ TEST_F(AccessibilityTest, PositionAfterLineBreak) {
const AXObject* ax_br = GetAXObjectByElementId("br"); const AXObject* ax_br = GetAXObjectByElementId("br");
ASSERT_NE(nullptr, ax_br); ASSERT_NE(nullptr, ax_br);
ASSERT_EQ(AccessibilityRole::kLineBreakRole, ax_br->RoleValue()); ASSERT_EQ(AccessibilityRole::kLineBreakRole, ax_br->RoleValue());
const AXObject* ax_div = ax_br->ParentObjectUnignored();
ASSERT_NE(nullptr, ax_div);
ASSERT_EQ(AccessibilityRole::kGenericContainerRole, ax_div->RoleValue());
const AXObject* ax_static_text = GetAXRootObject()->DeepestLastChild(); const AXObject* ax_static_text = GetAXRootObject()->DeepestLastChild();
ASSERT_NE(nullptr, ax_static_text); ASSERT_NE(nullptr, ax_static_text);
ASSERT_EQ(AccessibilityRole::kStaticTextRole, ax_static_text->RoleValue()); ASSERT_EQ(AccessibilityRole::kStaticTextRole, ax_static_text->RoleValue());
const auto ax_position = AXPosition::CreatePositionAfterObject(*ax_br); const auto ax_position = AXPosition::CreatePositionAfterObject(*ax_br);
EXPECT_FALSE(ax_position.IsTextPosition());
EXPECT_EQ(ax_div, ax_position.ContainerObject());
EXPECT_EQ(2, ax_position.ChildIndex());
EXPECT_EQ(ax_static_text, ax_position.ChildAfterTreePosition());
const auto position = ax_position.ToPositionWithAffinity(); const auto position = ax_position.ToPositionWithAffinity();
EXPECT_EQ(GetDocument().body(), position.AnchorNode()); EXPECT_EQ(GetDocument().body(), position.AnchorNode());
EXPECT_EQ(2, position.GetPosition().OffsetInContainerNode()); EXPECT_EQ(2, position.GetPosition().OffsetInContainerNode());
const auto ax_position_from_dom = AXPosition::FromPosition(position); const auto ax_position_from_dom = AXPosition::FromPosition(position);
EXPECT_EQ(ax_position, ax_position_from_dom); EXPECT_EQ(ax_position, ax_position_from_dom);
EXPECT_EQ(ax_static_text, ax_position_from_dom.ChildAfterTreePosition());
} }
TEST_F(AccessibilityTest, FirstPositionInDivContainer) { TEST_F(AccessibilityTest, FirstPositionInDivContainer) {
...@@ -416,15 +444,22 @@ TEST_F(AccessibilityTest, PositionBeforeLineBreakWithWhiteSpace) { ...@@ -416,15 +444,22 @@ TEST_F(AccessibilityTest, PositionBeforeLineBreakWithWhiteSpace) {
const AXObject* ax_br = GetAXObjectByElementId("br"); const AXObject* ax_br = GetAXObjectByElementId("br");
ASSERT_NE(nullptr, ax_br); ASSERT_NE(nullptr, ax_br);
ASSERT_EQ(AccessibilityRole::kLineBreakRole, ax_br->RoleValue()); ASSERT_EQ(AccessibilityRole::kLineBreakRole, ax_br->RoleValue());
const AXObject* ax_div = ax_br->ParentObjectUnignored();
ASSERT_NE(nullptr, ax_div);
ASSERT_EQ(AccessibilityRole::kGenericContainerRole, ax_div->RoleValue());
const auto ax_position = AXPosition::CreatePositionBeforeObject(*ax_br); const auto ax_position = AXPosition::CreatePositionBeforeObject(*ax_br);
EXPECT_FALSE(ax_position.IsTextPosition());
EXPECT_EQ(ax_div, ax_position.ContainerObject());
EXPECT_EQ(1, ax_position.ChildIndex());
EXPECT_EQ(ax_br, ax_position.ChildAfterTreePosition());
const auto position = ax_position.ToPositionWithAffinity(); const auto position = ax_position.ToPositionWithAffinity();
EXPECT_EQ(GetDocument().body(), position.AnchorNode()); EXPECT_EQ(GetDocument().body(), position.AnchorNode());
EXPECT_EQ(1, position.GetPosition().OffsetInContainerNode()); EXPECT_EQ(1, position.GetPosition().OffsetInContainerNode());
const auto ax_position_from_dom = AXPosition::FromPosition(position); const auto ax_position_from_dom = AXPosition::FromPosition(position);
EXPECT_EQ(ax_position, ax_position_from_dom); EXPECT_EQ(ax_position, ax_position_from_dom);
EXPECT_EQ(ax_br, ax_position_from_dom.ChildAfterTreePosition());
} }
TEST_F(AccessibilityTest, PositionAfterLineBreakWithWhiteSpace) { TEST_F(AccessibilityTest, PositionAfterLineBreakWithWhiteSpace) {
...@@ -432,18 +467,25 @@ TEST_F(AccessibilityTest, PositionAfterLineBreakWithWhiteSpace) { ...@@ -432,18 +467,25 @@ TEST_F(AccessibilityTest, PositionAfterLineBreakWithWhiteSpace) {
const AXObject* ax_br = GetAXObjectByElementId("br"); const AXObject* ax_br = GetAXObjectByElementId("br");
ASSERT_NE(nullptr, ax_br); ASSERT_NE(nullptr, ax_br);
ASSERT_EQ(AccessibilityRole::kLineBreakRole, ax_br->RoleValue()); ASSERT_EQ(AccessibilityRole::kLineBreakRole, ax_br->RoleValue());
const AXObject* ax_div = ax_br->ParentObjectUnignored();
ASSERT_NE(nullptr, ax_div);
ASSERT_EQ(AccessibilityRole::kGenericContainerRole, ax_div->RoleValue());
const AXObject* ax_static_text = GetAXRootObject()->DeepestLastChild(); const AXObject* ax_static_text = GetAXRootObject()->DeepestLastChild();
ASSERT_NE(nullptr, ax_static_text); ASSERT_NE(nullptr, ax_static_text);
ASSERT_EQ(AccessibilityRole::kStaticTextRole, ax_static_text->RoleValue()); ASSERT_EQ(AccessibilityRole::kStaticTextRole, ax_static_text->RoleValue());
const auto ax_position = AXPosition::CreatePositionAfterObject(*ax_br); const auto ax_position = AXPosition::CreatePositionAfterObject(*ax_br);
EXPECT_FALSE(ax_position.IsTextPosition());
EXPECT_EQ(ax_div, ax_position.ContainerObject());
EXPECT_EQ(2, ax_position.ChildIndex());
EXPECT_EQ(ax_static_text, ax_position.ChildAfterTreePosition());
const auto position = ax_position.ToPositionWithAffinity(); const auto position = ax_position.ToPositionWithAffinity();
EXPECT_EQ(GetDocument().body(), position.AnchorNode()); EXPECT_EQ(GetDocument().body(), position.AnchorNode());
EXPECT_EQ(2, position.GetPosition().OffsetInContainerNode()); EXPECT_EQ(2, position.GetPosition().OffsetInContainerNode());
const auto ax_position_from_dom = AXPosition::FromPosition(position); const auto ax_position_from_dom = AXPosition::FromPosition(position);
EXPECT_EQ(ax_position, ax_position_from_dom); EXPECT_EQ(ax_position, ax_position_from_dom);
EXPECT_EQ(ax_static_text, ax_position_from_dom.ChildAfterTreePosition());
} }
TEST_F(AccessibilityTest, FirstPositionInDivContainerWithWhiteSpace) { TEST_F(AccessibilityTest, FirstPositionInDivContainerWithWhiteSpace) {
...@@ -835,7 +877,51 @@ TEST_F(AccessibilityTest, PositionAfterListMarker) { ...@@ -835,7 +877,51 @@ TEST_F(AccessibilityTest, PositionAfterListMarker) {
EXPECT_EQ(ax_text, ax_position_from_dom.ChildAfterTreePosition()); EXPECT_EQ(ax_text, ax_position_from_dom.ChildAfterTreePosition());
} }
TEST_F(AccessibilityTest, PositionInCSSContent) {} TEST_F(AccessibilityTest, PositionInCSSContent) {
SetBodyInnerHTML(kCSSBeforeAndAfter);
const Node* quote = GetElementById("quote");
ASSERT_NE(nullptr, quote);
// CSS text nodes are not in the DOM tree.
const Node* text = quote->firstChild();
ASSERT_NE(nullptr, text);
ASSERT_FALSE(text->IsPseudoElement());
ASSERT_TRUE(text->IsTextNode());
const AXObject* ax_quote = GetAXObjectByElementId("quote");
ASSERT_NE(nullptr, ax_quote);
ASSERT_EQ(AccessibilityRole::kGenericContainerRole, ax_quote->RoleValue());
ASSERT_EQ(3, ax_quote->ChildCount());
const AXObject* ax_css_before = ax_quote->FirstChild();
ASSERT_NE(nullptr, ax_css_before);
ASSERT_EQ(AccessibilityRole::kStaticTextRole, ax_css_before->RoleValue());
const AXObject* ax_text = *(ax_quote->Children().begin() + 1);
ASSERT_NE(nullptr, ax_text);
ASSERT_EQ(AccessibilityRole::kStaticTextRole, ax_text->RoleValue());
const AXObject* ax_css_after = ax_quote->LastChild();
ASSERT_NE(nullptr, ax_css_after);
ASSERT_EQ(AccessibilityRole::kStaticTextRole, ax_css_after->RoleValue());
const auto ax_position_before =
AXPosition::CreateFirstPositionInObject(*ax_css_before);
EXPECT_TRUE(ax_position_before.IsTextPosition());
EXPECT_EQ(0, ax_position_before.TextOffset());
EXPECT_EQ(nullptr, ax_position_before.ChildAfterTreePosition());
const auto position_before = ax_position_before.ToPositionWithAffinity(
AXPositionAdjustmentBehavior::kMoveRight);
EXPECT_EQ(text, position_before.AnchorNode());
EXPECT_EQ(0, position_before.GetPosition().OffsetInContainerNode());
const auto ax_position_after =
AXPosition::CreateLastPositionInObject(*ax_css_after);
EXPECT_TRUE(ax_position_after.IsTextPosition());
EXPECT_EQ(2, ax_position_after.TextOffset());
EXPECT_EQ(nullptr, ax_position_after.ChildAfterTreePosition());
const auto position_after = ax_position_after.ToPositionWithAffinity(
AXPositionAdjustmentBehavior::kMoveLeft);
EXPECT_EQ(text, position_after.AnchorNode());
EXPECT_EQ(12, position_after.GetPosition().OffsetInContainerNode());
}
// //
// Objects deriving from |AXMockObject|, e.g. table columns, are in the // Objects deriving from |AXMockObject|, e.g. table columns, are in the
......
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