Commit 94a7a2a2 authored by Jacques Newman's avatar Jacques Newman Committed by Commit Bot

UIA ITextProvider::RangeFromPoint Implementation

Added test for NearestTextIndexTestData.

Bug: 928948
Change-Id: I38676dee2e60aee221166dc765a22514fd47ef2d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1572918
Commit-Queue: Jacques Newman <janewman@microsoft.com>
Reviewed-by: default avatarDominic Mazzoni <dmazzoni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#655008}
parent 8bce8a84
...@@ -1609,4 +1609,32 @@ int AXPlatformNodeBase::FindTextBoundary( ...@@ -1609,4 +1609,32 @@ int AXPlatformNodeBase::FindTextBoundary(
boundary, offset, direction, affinity)); boundary, offset, direction, affinity));
} }
int AXPlatformNodeBase::NearestTextIndexToPoint(gfx::Point point) {
// For text objects, find the text position nearest to the point.The nearest
// index of a non-text object is implicitly 0. Text fields such as textarea
// have an embedded div inside them that holds all the text,
// GetRangeBoundsRect will correctly handle these nodes
int nearest_index = 0;
const AXCoordinateSystem coordinate_system = AXCoordinateSystem::kScreen;
const AXClippingBehavior clipping_behavior = AXClippingBehavior::kUnclipped;
// Manhattan Distance is used to provide faster distance estimates.
// get the distance from the point to the bounds of each character.
float shortest_distance = GetDelegate()
->GetInnerTextRangeBoundsRect(
0, 1, coordinate_system, clipping_behavior)
.ManhattanDistanceToPoint(point);
for (int i = 1, text_length = GetInnerText().length(); i < text_length; ++i) {
float current_distance =
GetDelegate()
->GetInnerTextRangeBoundsRect(i, i + 1, coordinate_system,
clipping_behavior)
.ManhattanDistanceToPoint(point);
if (current_distance < shortest_distance) {
shortest_distance = current_distance;
nearest_index = i;
}
}
return nearest_index;
}
} // namespace ui } // namespace ui
...@@ -207,6 +207,13 @@ class AX_EXPORT AXPlatformNodeBase : public AXPlatformNode { ...@@ -207,6 +207,13 @@ class AX_EXPORT AXPlatformNodeBase : public AXPlatformNode {
// Returns true if this node can be scrolled in the vertical direction. // Returns true if this node can be scrolled in the vertical direction.
bool IsVerticallyScrollable() const; bool IsVerticallyScrollable() const;
// Returns true if this node has role of StaticText, LineBreak, or
// InlineTextBox
bool IsTextOnlyObject() const;
// Returns true if the node is an editable text field.
bool IsPlainTextField() const;
bool HasFocus(); bool HasFocus();
// Returns the text of this node and represent the text of descendant nodes // Returns the text of this node and represent the text of descendant nodes
...@@ -252,6 +259,12 @@ class AX_EXPORT AXPlatformNodeBase : public AXPlatformNode { ...@@ -252,6 +259,12 @@ class AX_EXPORT AXPlatformNodeBase : public AXPlatformNode {
}; };
bool ScrollToNode(ScrollType scroll_type); bool ScrollToNode(ScrollType scroll_type);
// Return the nearest text index to a point in screen coordinates for an
// accessibility node. If the node is not a text only node, the implicit
// nearest index is zero. Note this will only find the index of text on the
// input node. The node's subtree will not be searched.
int NearestTextIndexToPoint(gfx::Point point);
// //
// Delegate. This is a weak reference which owns |this|. // Delegate. This is a weak reference which owns |this|.
// //
...@@ -259,8 +272,6 @@ class AX_EXPORT AXPlatformNodeBase : public AXPlatformNode { ...@@ -259,8 +272,6 @@ class AX_EXPORT AXPlatformNodeBase : public AXPlatformNode {
protected: protected:
bool IsDocument() const; bool IsDocument() const;
bool IsTextOnlyObject() const;
bool IsPlainTextField() const;
// Is in a focused textfield with a related suggestion popup available, // Is in a focused textfield with a related suggestion popup available,
// such as for the Autofill feature. The suggestion popup can be either hidden // such as for the Autofill feature. The suggestion popup can be either hidden
// and available or already visible. This indicates next down arrow key will // and available or already visible. This indicates next down arrow key will
......
...@@ -224,7 +224,30 @@ STDMETHODIMP AXPlatformNodeTextProviderWin::RangeFromPoint( ...@@ -224,7 +224,30 @@ STDMETHODIMP AXPlatformNodeTextProviderWin::RangeFromPoint(
UiaPoint uia_point, UiaPoint uia_point,
ITextRangeProvider** range) { ITextRangeProvider** range) {
WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_TEXT_RANGEFROMPOINT); WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_TEXT_RANGEFROMPOINT);
return E_NOTIMPL; UIA_VALIDATE_TEXTPROVIDER_CALL();
*range = nullptr;
// Retrieve the closest accessibility node via hit testing the point. No
// coordinate unit conversion is needed, hit testing input is also in screen
// coordinates.
gfx::NativeViewAccessible nearest_native_view_accessible =
owner()->GetDelegate()->HitTestSync(uia_point.x, uia_point.y);
DCHECK(nearest_native_view_accessible);
AXPlatformNodeWin* nearest_node = static_cast<AXPlatformNodeWin*>(
AXPlatformNode::FromNativeViewAccessible(nearest_native_view_accessible));
DCHECK(nearest_node);
AXNodePosition::AXPositionInstance start, end;
start = nearest_node->GetDelegate()->CreateTextPositionAt(
nearest_node->NearestTextIndexToPoint(
gfx::Point(uia_point.x, uia_point.y)));
DCHECK(!start->IsNullPosition());
end = start->Clone();
*range = AXPlatformNodeTextRangeProviderWin::CreateTextRangeProvider(
nearest_node, std::move(start), std::move(end));
return S_OK;
} }
STDMETHODIMP AXPlatformNodeTextProviderWin::get_DocumentRange( STDMETHODIMP AXPlatformNodeTextProviderWin::get_DocumentRange(
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "ui/accessibility/platform/ax_fragment_root_win.h" #include "ui/accessibility/platform/ax_fragment_root_win.h"
#include "ui/accessibility/platform/ax_platform_node_textprovider_win.h" #include "ui/accessibility/platform/ax_platform_node_textprovider_win.h"
#include "ui/accessibility/platform/ax_platform_node_textrangeprovider_win.h" #include "ui/accessibility/platform/ax_platform_node_textrangeprovider_win.h"
#include "ui/accessibility/platform/test_ax_node_wrapper.h"
#include "ui/base/win/accessibility_misc_utils.h" #include "ui/base/win/accessibility_misc_utils.h"
using Microsoft::WRL::ComPtr; using Microsoft::WRL::ComPtr;
...@@ -175,6 +176,62 @@ TEST_F(AXPlatformNodeTextProviderTest, TestITextProviderRangeFromChild) { ...@@ -175,6 +176,62 @@ TEST_F(AXPlatformNodeTextProviderTest, TestITextProviderRangeFromChild) {
other_root_node_raw.Get(), &text_range_provider)); other_root_node_raw.Get(), &text_range_provider));
} }
TEST_F(AXPlatformNodeTextProviderTest, TestNearestTextIndexToPoint) {
ui::AXNodeData text_data;
text_data.id = 2;
text_data.role = ax::mojom::Role::kInlineTextBox;
text_data.SetName("text");
// spacing: "t-e-x---t--"
text_data.AddIntListAttribute(ax::mojom::IntListAttribute::kCharacterOffsets,
{2, 2, 4, 2});
ui::AXNodeData root_data;
root_data.id = 1;
root_data.role = ax::mojom::Role::kStaticText;
root_data.relative_bounds.bounds = gfx::RectF(1, 1, 2, 2);
root_data.child_ids.push_back(2);
Init(root_data, text_data);
AXNode* root_node = GetRootNode();
AXNodePosition::SetTreeForTesting(tree_.get());
AXNode* text_node = root_node->children()[0];
struct NearestTextIndexTestData {
AXNode* node;
struct point_offset_expected_index_pair {
int point_offset_x;
int expected_index;
};
std::vector<point_offset_expected_index_pair> test_data;
};
NearestTextIndexTestData nodes[] = {
{text_node,
{{0, 0}, {2, 0}, {3, 1}, {4, 1}, {5, 2}, {8, 2}, {9, 3}, {10, 3}}},
{root_node,
{{0, 0}, {2, 0}, {3, 0}, {4, 0}, {5, 0}, {8, 0}, {9, 0}, {10, 0}}}};
for (auto data : nodes) {
ComPtr<IRawElementProviderSimple> element_provider =
QueryInterfaceFromNode<IRawElementProviderSimple>(data.node);
ComPtr<ITextProvider> text_provider;
EXPECT_HRESULT_SUCCEEDED(element_provider->GetPatternProvider(
UIA_TextPatternId, &text_provider));
// get internal implementation to access helper for testing
ComPtr<AXPlatformNodeTextProviderWin> platform_text_provider;
EXPECT_HRESULT_SUCCEEDED(
text_provider->QueryInterface(IID_PPV_ARGS(&platform_text_provider)));
ComPtr<AXPlatformNodeWin> platform_node;
EXPECT_HRESULT_SUCCEEDED(
element_provider->QueryInterface(IID_PPV_ARGS(&platform_node)));
for (auto pair : data.test_data) {
EXPECT_EQ(pair.expected_index, platform_node->NearestTextIndexToPoint(
gfx::Point(pair.point_offset_x, 0)));
}
}
}
TEST_F(AXPlatformNodeTextProviderTest, TestITextProviderDocumentRange) { TEST_F(AXPlatformNodeTextProviderTest, TestITextProviderDocumentRange) {
ui::AXNodeData text_data; ui::AXNodeData text_data;
text_data.id = 2; text_data.id = 2;
......
...@@ -145,6 +145,41 @@ gfx::Rect TestAXNodeWrapper::GetBoundsRect( ...@@ -145,6 +145,41 @@ gfx::Rect TestAXNodeWrapper::GetBoundsRect(
} }
} }
gfx::Rect TestAXNodeWrapper::GetInnerTextRangeBoundsRect(
const int start_offset,
const int end_offset,
const AXCoordinateSystem coordinate_system,
const AXClippingBehavior clipping_behavior,
AXOffscreenResult* offscreen_result) const {
switch (coordinate_system) {
case AXCoordinateSystem::kScreen: {
gfx::RectF bounds = GetData().relative_bounds.bounds;
bounds.Offset(g_offset);
if (GetData().HasIntListAttribute(
ax::mojom::IntListAttribute::kCharacterOffsets)) {
const std::vector<int32_t>& offsets = GetData().GetIntListAttribute(
ax::mojom::IntListAttribute::kCharacterOffsets);
int32_t x = bounds.x();
int32_t width = 0;
for (int i = 0; i < static_cast<int>(offsets.size()); i++) {
if (i < start_offset)
x += offsets[i];
else if (i < end_offset)
width += offsets[i];
else
break;
}
bounds = gfx::RectF(x, bounds.y(), width, bounds.height());
}
return gfx::ToEnclosingRect(bounds);
}
case AXCoordinateSystem::kRootFrame:
case AXCoordinateSystem::kFrame:
NOTIMPLEMENTED();
return gfx::Rect();
}
}
gfx::Rect TestAXNodeWrapper::GetHypertextRangeBoundsRect( gfx::Rect TestAXNodeWrapper::GetHypertextRangeBoundsRect(
const int start_offset, const int start_offset,
const int end_offset, const int end_offset,
......
...@@ -59,6 +59,12 @@ class TestAXNodeWrapper : public AXPlatformNodeDelegateBase { ...@@ -59,6 +59,12 @@ class TestAXNodeWrapper : public AXPlatformNodeDelegateBase {
gfx::Rect GetBoundsRect(const AXCoordinateSystem coordinate_system, gfx::Rect GetBoundsRect(const AXCoordinateSystem coordinate_system,
const AXClippingBehavior clipping_behavior, const AXClippingBehavior clipping_behavior,
AXOffscreenResult* offscreen_result) const override; AXOffscreenResult* offscreen_result) const override;
gfx::Rect GetInnerTextRangeBoundsRect(
const int start_offset,
const int end_offset,
const AXCoordinateSystem coordinate_system,
const AXClippingBehavior clipping_behavior,
AXOffscreenResult* offscreen_result) const override;
gfx::Rect GetHypertextRangeBoundsRect( gfx::Rect GetHypertextRangeBoundsRect(
const int start_offset, const int start_offset,
const int end_offset, const int end_offset,
......
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