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

Added a test for shrinking or extending the selection in ARIA hidden and one for list bullets

R=dmazzoni@chromium.org

Change-Id: Icecff9eece2167e4678dca0aba1f0d0e08a457b0
Reviewed-on: https://chromium-review.googlesource.com/1175032Reviewed-by: default avatarDominic Mazzoni <dmazzoni@chromium.org>
Commit-Queue: Nektarios Paisios <nektar@chromium.org>
Cr-Commit-Position: refs/heads/master@{#583391}
parent e57528e9
......@@ -71,6 +71,54 @@ const AXSelection AXSelection::Builder::Build() {
return selection_;
}
// static
const AXSelection AXSelection::FromSelection(
const SelectionInDOMTree& selection,
const AXSelectionBehavior selection_behavior) {
if (selection.IsNone())
return {};
DCHECK(selection.AssertValid());
const Position dom_base = selection.Base();
const Position dom_extent = selection.Extent();
const TextAffinity base_affinity = TextAffinity::kDownstream;
const TextAffinity extent_affinity = selection.Affinity();
AXPositionAdjustmentBehavior base_adjustment =
AXPositionAdjustmentBehavior::kMoveRight;
AXPositionAdjustmentBehavior extent_adjustment =
AXPositionAdjustmentBehavior::kMoveRight;
switch (selection_behavior) {
case AXSelectionBehavior::kShrinkToValidDOMRange:
if (selection.IsBaseFirst()) {
base_adjustment = AXPositionAdjustmentBehavior::kMoveRight;
extent_adjustment = AXPositionAdjustmentBehavior::kMoveLeft;
} else {
base_adjustment = AXPositionAdjustmentBehavior::kMoveLeft;
extent_adjustment = AXPositionAdjustmentBehavior::kMoveRight;
}
break;
case AXSelectionBehavior::kExtendToValidDOMRange:
if (selection.IsBaseFirst()) {
base_adjustment = AXPositionAdjustmentBehavior::kMoveLeft;
extent_adjustment = AXPositionAdjustmentBehavior::kMoveRight;
} else {
base_adjustment = AXPositionAdjustmentBehavior::kMoveRight;
extent_adjustment = AXPositionAdjustmentBehavior::kMoveLeft;
}
break;
}
const auto ax_base =
AXPosition::FromPosition(dom_base, base_affinity, base_adjustment);
const auto ax_extent =
AXPosition::FromPosition(dom_extent, extent_affinity, extent_adjustment);
AXSelection::Builder selection_builder;
selection_builder.SetBase(ax_base).SetExtent(ax_extent);
return selection_builder.Build();
}
AXSelection::AXSelection() : base_(), extent_() {
#if DCHECK_IS_ON()
dom_tree_version_ = 0;
......@@ -104,9 +152,9 @@ const SelectionInDOMTree AXSelection::AsSelection(
return {};
AXPositionAdjustmentBehavior base_adjustment =
AXPositionAdjustmentBehavior::kMoveLeft;
AXPositionAdjustmentBehavior::kMoveRight;
AXPositionAdjustmentBehavior extent_adjustment =
AXPositionAdjustmentBehavior::kMoveLeft;
AXPositionAdjustmentBehavior::kMoveRight;
switch (selection_behavior) {
case AXSelectionBehavior::kShrinkToValidDOMRange:
if (base_ <= extent_) {
......
......@@ -31,6 +31,10 @@ class MODULES_EXPORT AXSelection final {
public:
class Builder;
static const AXSelection FromSelection(
const SelectionInDOMTree&,
const AXSelectionBehavior = AXSelectionBehavior::kExtendToValidDOMRange);
AXSelection(const AXSelection&) = default;
AXSelection& operator=(const AXSelection&) = default;
~AXSelection() = default;
......
......@@ -6,8 +6,10 @@
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/renderer/core/dom/node.h"
#include "third_party/blink/renderer/core/editing/frame_selection.h"
#include "third_party/blink/renderer/core/editing/position.h"
#include "third_party/blink/renderer/core/editing/selection_template.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/modules/accessibility/ax_object.h"
#include "third_party/blink/renderer/modules/accessibility/ax_position.h"
#include "third_party/blink/renderer/modules/accessibility/testing/accessibility_selection_test.h"
......@@ -20,9 +22,11 @@ namespace blink {
TEST_F(AccessibilitySelectionTest, SetSelectionInText) {
SetBodyInnerHTML(R"HTML(<p id='paragraph'>Hello</p>)HTML");
const Node* text = GetElementById("paragraph")->firstChild();
ASSERT_NE(nullptr, text);
ASSERT_TRUE(text->IsTextNode());
const AXObject* ax_static_text =
GetAXObjectByElementId("paragraph")->FirstChild();
ASSERT_NE(nullptr, ax_static_text);
......@@ -45,9 +49,11 @@ TEST_F(AccessibilitySelectionTest, SetSelectionInText) {
TEST_F(AccessibilitySelectionTest, SetSelectionInTextWithWhiteSpace) {
SetBodyInnerHTML(R"HTML(<p id='paragraph'> Hello</p>)HTML");
const Node* text = GetElementById("paragraph")->firstChild();
ASSERT_NE(nullptr, text);
ASSERT_TRUE(text->IsTextNode());
const AXObject* ax_static_text =
GetAXObjectByElementId("paragraph")->FirstChild();
ASSERT_NE(nullptr, ax_static_text);
......@@ -75,10 +81,55 @@ TEST_F(AccessibilitySelectionTest, SetSelectionInTextWithWhiteSpace) {
// |AXSelection| to valid endpoints.
//
TEST_F(AccessibilitySelectionTest, SetSelectionInARIAHidden) {
SetSelectionText(R"HTML(
<div role="main">
<p>Before aria-hidden.</p>
^<p aria-hidden="true">Aria-hidden 1.</p>
<p>In the middle of aria-hidden.</p>
<p aria-hidden="true">Aria-hidden 2.</p>|
<p>After aria-hidden.</p>
</div>
)HTML");
const SelectionInDOMTree selection =
GetFrame().Selection().GetSelectionInDOMTree();
const auto ax_selection_shrink = AXSelection::FromSelection(
selection, AXSelectionBehavior::kShrinkToValidDOMRange);
EXPECT_EQ("", GetSelectionText(ax_selection_shrink));
const auto ax_selection_extend = AXSelection::FromSelection(
selection, AXSelectionBehavior::kExtendToValidDOMRange);
EXPECT_EQ("", GetSelectionText(ax_selection_extend));
}
//
// Set selection tests.
// Setting the selection from an |AXSelection| that has endpoints which are not
// present in the layout tree should shring the selection to visible endpoints.
//
TEST_F(AccessibilitySelectionTest, SetSelectionAroundListBullet) {
SetSelectionText(R"HTML(
<div role="main">
<ul>
^<li>Item 1.</li>
<li>Item 2.</li>|
</ul>
</div>
)HTML");
const SelectionInDOMTree selection =
GetFrame().Selection().GetSelectionInDOMTree();
const auto ax_selection_shrink = AXSelection::FromSelection(
selection, AXSelectionBehavior::kShrinkToValidDOMRange);
EXPECT_EQ("", GetSelectionText(ax_selection_shrink));
const auto ax_selection_extend = AXSelection::FromSelection(
selection, AXSelectionBehavior::kExtendToValidDOMRange);
EXPECT_EQ("", GetSelectionText(ax_selection_extend));
}
} // namespace blink
......@@ -4,6 +4,8 @@
#include "third_party/blink/renderer/modules/accessibility/testing/accessibility_selection_test.h"
#include "third_party/blink/renderer/core/dom/character_data.h"
#include "third_party/blink/renderer/core/dom/container_node.h"
#include "third_party/blink/renderer/core/dom/node.h"
#include "third_party/blink/renderer/core/html/html_element.h"
#include "third_party/blink/renderer/modules/accessibility/ax_object.h"
......@@ -144,7 +146,9 @@ class AXSelectionSerializer final {
// Deserializes an HTML snippet with or without selection markers to an
// accessibility tree. A '^' could be present at the selection anchor offset and
// a '|' at the focus offset. If multiple markers are present, the deserializer
// will DCHECK. If there are no markers, no selection will be made.
// will DCHECK. If there are no markers, no selection will be made. We don't
// allow '^' and '|' markers to appear in anything other than the contents of an
// HTML node, e.g. they are not permitted in aria-labels.
class AXSelectionDeserializer final {
STACK_ALLOCATED();
......@@ -156,11 +160,12 @@ class AXSelectionDeserializer final {
// Creates an accessibility tree rooted at the given HTML element from the
// provided HTML snippet, selects the part of the tree indicated by the
// selection markers in the snippet if present, and returns the tree's root.
AXObject* Deserialize(const std::string& html_snippet, HTMLElement& element) {
const AXSelection Deserialize(const std::string& html_snippet,
HTMLElement& element) {
element.SetInnerHTMLFromString(String::FromUTF8(html_snippet.c_str()));
AXObject* root = ax_object_cache_->GetOrCreate(&element);
if (!root)
return nullptr;
return AXSelection::Builder().Build();
FindSelectionMarkers(*root);
if (base_ && extent_) {
......@@ -168,33 +173,56 @@ class AXSelectionDeserializer final {
AXSelection ax_selection =
builder.SetBase(base_).SetExtent(extent_).Build();
ax_selection.Select();
return ax_selection;
}
return root;
return AXSelection::Builder().Build();
}
private:
void HandleCharacterData(const AXObject& text_object) {
CharacterData* const node = ToCharacterData(text_object.GetNode());
int base_offset = -1;
int extent_offset = -1;
String name = text_object.ComputedName();
for (unsigned i = 0; i < name.length(); ++i) {
const UChar character = name[i];
StringBuilder builder;
for (unsigned i = 0; i < node->length(); ++i) {
const UChar character = node->data()[i];
if (character == '^') {
DCHECK_EQ(base_offset, -1) << text_object;
DCHECK_EQ(base_offset, -1) << "Only one '^' is allowed " << text_object;
base_offset = static_cast<int>(i);
continue;
}
if (character == '|') {
DCHECK_EQ(extent_offset, -1) << text_object;
DCHECK_EQ(extent_offset, -1)
<< "Only one '|' is allowed " << text_object;
extent_offset = static_cast<int>(i);
continue;
}
builder.Append(character);
}
LOG(ERROR) << "Nektar\n" << base_offset << extent_offset;
if (base_offset == -1 && extent_offset == -1)
return;
// Remove the markers, otherwise they would be duplicated if the AX
// selection is re-serialized.
node->setData(builder.ToString());
if (node->length() == 0) {
// Since the text object contains only selection markers, this indicates
// that this is a request for a non-text selection.
if (base_offset >= 0)
base_ = AXPosition::CreatePositionBeforeObject(text_object);
if (extent_offset >= 0)
extent_ = AXPosition::CreatePositionBeforeObject(text_object);
ContainerNode* const parent_node = node->parentNode();
DCHECK(parent_node);
parent_node->removeChild(node);
return;
}
if (base_offset >= 0)
base_ = AXPosition::CreatePositionInTextObject(text_object, base_offset);
if (extent_offset >= 0) {
......@@ -245,16 +273,16 @@ std::string AccessibilitySelectionTest::GetSelectionText(
return AXSelectionSerializer(selection).Serialize(subtree);
}
AXObject* AccessibilitySelectionTest::SetSelectionText(
const AXSelection AccessibilitySelectionTest::SetSelectionText(
const std::string& selection_text) const {
HTMLElement* body = GetDocument().body();
if (!body)
return nullptr;
return AXSelection::Builder().Build();
return AXSelectionDeserializer(GetAXObjectCache())
.Deserialize(selection_text, *body);
}
AXObject* AccessibilitySelectionTest::SetSelectionText(
const AXSelection AccessibilitySelectionTest::SetSelectionText(
const std::string& selection_text,
HTMLElement& element) const {
return AXSelectionDeserializer(GetAXObjectCache())
......
......@@ -35,11 +35,11 @@ class AccessibilitySelectionTest : public AccessibilityTest {
// Sets |selection_text| as inner HTML of the document body and returns the
// root of the accessibility tree at body.
AXObject* SetSelectionText(const std::string& selection_text) const;
const AXSelection SetSelectionText(const std::string& selection_text) const;
// Sets |selection_text| as inner HTML of |element| and returns the root of
// the accessibility subtree at |element|.
AXObject* SetSelectionText(const std::string& selection_text,
const AXSelection SetSelectionText(const std::string& selection_text,
HTMLElement& element) const;
};
......
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