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() { ...@@ -71,6 +71,54 @@ const AXSelection AXSelection::Builder::Build() {
return selection_; 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_() { AXSelection::AXSelection() : base_(), extent_() {
#if DCHECK_IS_ON() #if DCHECK_IS_ON()
dom_tree_version_ = 0; dom_tree_version_ = 0;
...@@ -104,9 +152,9 @@ const SelectionInDOMTree AXSelection::AsSelection( ...@@ -104,9 +152,9 @@ const SelectionInDOMTree AXSelection::AsSelection(
return {}; return {};
AXPositionAdjustmentBehavior base_adjustment = AXPositionAdjustmentBehavior base_adjustment =
AXPositionAdjustmentBehavior::kMoveLeft; AXPositionAdjustmentBehavior::kMoveRight;
AXPositionAdjustmentBehavior extent_adjustment = AXPositionAdjustmentBehavior extent_adjustment =
AXPositionAdjustmentBehavior::kMoveLeft; AXPositionAdjustmentBehavior::kMoveRight;
switch (selection_behavior) { switch (selection_behavior) {
case AXSelectionBehavior::kShrinkToValidDOMRange: case AXSelectionBehavior::kShrinkToValidDOMRange:
if (base_ <= extent_) { if (base_ <= extent_) {
......
...@@ -31,6 +31,10 @@ class MODULES_EXPORT AXSelection final { ...@@ -31,6 +31,10 @@ class MODULES_EXPORT AXSelection final {
public: public:
class Builder; class Builder;
static const AXSelection FromSelection(
const SelectionInDOMTree&,
const AXSelectionBehavior = AXSelectionBehavior::kExtendToValidDOMRange);
AXSelection(const AXSelection&) = default; AXSelection(const AXSelection&) = default;
AXSelection& operator=(const AXSelection&) = default; AXSelection& operator=(const AXSelection&) = default;
~AXSelection() = default; ~AXSelection() = default;
......
...@@ -6,8 +6,10 @@ ...@@ -6,8 +6,10 @@
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/renderer/core/dom/node.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/position.h"
#include "third_party/blink/renderer/core/editing/selection_template.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_object.h"
#include "third_party/blink/renderer/modules/accessibility/ax_position.h" #include "third_party/blink/renderer/modules/accessibility/ax_position.h"
#include "third_party/blink/renderer/modules/accessibility/testing/accessibility_selection_test.h" #include "third_party/blink/renderer/modules/accessibility/testing/accessibility_selection_test.h"
...@@ -20,9 +22,11 @@ namespace blink { ...@@ -20,9 +22,11 @@ namespace blink {
TEST_F(AccessibilitySelectionTest, SetSelectionInText) { TEST_F(AccessibilitySelectionTest, SetSelectionInText) {
SetBodyInnerHTML(R"HTML(<p id='paragraph'>Hello</p>)HTML"); SetBodyInnerHTML(R"HTML(<p id='paragraph'>Hello</p>)HTML");
const Node* text = GetElementById("paragraph")->firstChild(); const Node* text = GetElementById("paragraph")->firstChild();
ASSERT_NE(nullptr, text); ASSERT_NE(nullptr, text);
ASSERT_TRUE(text->IsTextNode()); ASSERT_TRUE(text->IsTextNode());
const AXObject* ax_static_text = const AXObject* ax_static_text =
GetAXObjectByElementId("paragraph")->FirstChild(); GetAXObjectByElementId("paragraph")->FirstChild();
ASSERT_NE(nullptr, ax_static_text); ASSERT_NE(nullptr, ax_static_text);
...@@ -45,9 +49,11 @@ TEST_F(AccessibilitySelectionTest, SetSelectionInText) { ...@@ -45,9 +49,11 @@ TEST_F(AccessibilitySelectionTest, SetSelectionInText) {
TEST_F(AccessibilitySelectionTest, SetSelectionInTextWithWhiteSpace) { TEST_F(AccessibilitySelectionTest, SetSelectionInTextWithWhiteSpace) {
SetBodyInnerHTML(R"HTML(<p id='paragraph'> Hello</p>)HTML"); SetBodyInnerHTML(R"HTML(<p id='paragraph'> Hello</p>)HTML");
const Node* text = GetElementById("paragraph")->firstChild(); const Node* text = GetElementById("paragraph")->firstChild();
ASSERT_NE(nullptr, text); ASSERT_NE(nullptr, text);
ASSERT_TRUE(text->IsTextNode()); ASSERT_TRUE(text->IsTextNode());
const AXObject* ax_static_text = const AXObject* ax_static_text =
GetAXObjectByElementId("paragraph")->FirstChild(); GetAXObjectByElementId("paragraph")->FirstChild();
ASSERT_NE(nullptr, ax_static_text); ASSERT_NE(nullptr, ax_static_text);
...@@ -75,10 +81,55 @@ TEST_F(AccessibilitySelectionTest, SetSelectionInTextWithWhiteSpace) { ...@@ -75,10 +81,55 @@ TEST_F(AccessibilitySelectionTest, SetSelectionInTextWithWhiteSpace) {
// |AXSelection| to valid endpoints. // |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. // Set selection tests.
// Setting the selection from an |AXSelection| that has endpoints which are not // Setting the selection from an |AXSelection| that has endpoints which are not
// present in the layout tree should shring the selection to visible endpoints. // 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 } // namespace blink
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
#include "third_party/blink/renderer/modules/accessibility/testing/accessibility_selection_test.h" #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/dom/node.h"
#include "third_party/blink/renderer/core/html/html_element.h" #include "third_party/blink/renderer/core/html/html_element.h"
#include "third_party/blink/renderer/modules/accessibility/ax_object.h" #include "third_party/blink/renderer/modules/accessibility/ax_object.h"
...@@ -144,7 +146,9 @@ class AXSelectionSerializer final { ...@@ -144,7 +146,9 @@ class AXSelectionSerializer final {
// Deserializes an HTML snippet with or without selection markers to an // Deserializes an HTML snippet with or without selection markers to an
// accessibility tree. A '^' could be present at the selection anchor offset and // accessibility tree. A '^' could be present at the selection anchor offset and
// a '|' at the focus offset. If multiple markers are present, the deserializer // 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 { class AXSelectionDeserializer final {
STACK_ALLOCATED(); STACK_ALLOCATED();
...@@ -156,11 +160,12 @@ class AXSelectionDeserializer final { ...@@ -156,11 +160,12 @@ class AXSelectionDeserializer final {
// Creates an accessibility tree rooted at the given HTML element from the // Creates an accessibility tree rooted at the given HTML element from the
// provided HTML snippet, selects the part of the tree indicated by 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. // 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())); element.SetInnerHTMLFromString(String::FromUTF8(html_snippet.c_str()));
AXObject* root = ax_object_cache_->GetOrCreate(&element); AXObject* root = ax_object_cache_->GetOrCreate(&element);
if (!root) if (!root)
return nullptr; return AXSelection::Builder().Build();
FindSelectionMarkers(*root); FindSelectionMarkers(*root);
if (base_ && extent_) { if (base_ && extent_) {
...@@ -168,33 +173,56 @@ class AXSelectionDeserializer final { ...@@ -168,33 +173,56 @@ class AXSelectionDeserializer final {
AXSelection ax_selection = AXSelection ax_selection =
builder.SetBase(base_).SetExtent(extent_).Build(); builder.SetBase(base_).SetExtent(extent_).Build();
ax_selection.Select(); ax_selection.Select();
return ax_selection;
} }
return root; return AXSelection::Builder().Build();
} }
private: private:
void HandleCharacterData(const AXObject& text_object) { void HandleCharacterData(const AXObject& text_object) {
CharacterData* const node = ToCharacterData(text_object.GetNode());
int base_offset = -1; int base_offset = -1;
int extent_offset = -1; int extent_offset = -1;
String name = text_object.ComputedName(); StringBuilder builder;
for (unsigned i = 0; i < name.length(); ++i) { for (unsigned i = 0; i < node->length(); ++i) {
const UChar character = name[i]; const UChar character = node->data()[i];
if (character == '^') { 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); base_offset = static_cast<int>(i);
continue; continue;
} }
if (character == '|') { 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); extent_offset = static_cast<int>(i);
continue; continue;
} }
builder.Append(character);
} }
LOG(ERROR) << "Nektar\n" << base_offset << extent_offset;
if (base_offset == -1 && extent_offset == -1) if (base_offset == -1 && extent_offset == -1)
return; 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) if (base_offset >= 0)
base_ = AXPosition::CreatePositionInTextObject(text_object, base_offset); base_ = AXPosition::CreatePositionInTextObject(text_object, base_offset);
if (extent_offset >= 0) { if (extent_offset >= 0) {
...@@ -245,16 +273,16 @@ std::string AccessibilitySelectionTest::GetSelectionText( ...@@ -245,16 +273,16 @@ std::string AccessibilitySelectionTest::GetSelectionText(
return AXSelectionSerializer(selection).Serialize(subtree); return AXSelectionSerializer(selection).Serialize(subtree);
} }
AXObject* AccessibilitySelectionTest::SetSelectionText( const AXSelection AccessibilitySelectionTest::SetSelectionText(
const std::string& selection_text) const { const std::string& selection_text) const {
HTMLElement* body = GetDocument().body(); HTMLElement* body = GetDocument().body();
if (!body) if (!body)
return nullptr; return AXSelection::Builder().Build();
return AXSelectionDeserializer(GetAXObjectCache()) return AXSelectionDeserializer(GetAXObjectCache())
.Deserialize(selection_text, *body); .Deserialize(selection_text, *body);
} }
AXObject* AccessibilitySelectionTest::SetSelectionText( const AXSelection AccessibilitySelectionTest::SetSelectionText(
const std::string& selection_text, const std::string& selection_text,
HTMLElement& element) const { HTMLElement& element) const {
return AXSelectionDeserializer(GetAXObjectCache()) return AXSelectionDeserializer(GetAXObjectCache())
......
...@@ -35,12 +35,12 @@ class AccessibilitySelectionTest : public AccessibilityTest { ...@@ -35,12 +35,12 @@ class AccessibilitySelectionTest : public AccessibilityTest {
// Sets |selection_text| as inner HTML of the document body and returns the // Sets |selection_text| as inner HTML of the document body and returns the
// root of the accessibility tree at body. // 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 // Sets |selection_text| as inner HTML of |element| and returns the root of
// the accessibility subtree at |element|. // the accessibility subtree at |element|.
AXObject* SetSelectionText(const std::string& selection_text, const AXSelection SetSelectionText(const std::string& selection_text,
HTMLElement& element) const; HTMLElement& element) const;
}; };
} // namespace blink } // namespace blink
......
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