Commit 7565b09d authored by Aaron Leventhal's avatar Aaron Leventhal Committed by Commit Bot

Useful result for IAText get_characterExtents() in empty textfield

Products like ZoomText use get_characterExtents with the special IA2_TEXT_OFFSET_CARET to get the
bounds of the character with the caret. Sometimes this results in getting the bounds for the
null character at the end of an editable textfield's text. In this case, get_characterExtents()
should return what the caret bounds would be if placed there.

In order to make this work, we can use the bounds of the anonymous inner editor element within
a textfield, and collapse it to the width of the caret. Normally this is exposed, but in the
case of an empty textfield, we were ignoring it.

This CL also adds a lot more tests for different kinds of empty fields. The plan is to move
the character extents implementation into the ui/accessibility code so that the automation
API support can leverage it.

Bug: 869985

Change-Id: I907a9df834f88aa9bf536e4dbbc9432e33d31202
Reviewed-on: https://chromium-review.googlesource.com/c/1255203
Commit-Queue: Aaron Leventhal <aleventhal@chromium.org>
Reviewed-by: default avatarDominic Mazzoni <dmazzoni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#596513}
parent 09998805
...@@ -78,6 +78,12 @@ class AccessibilityWinBrowserTest : public ContentBrowserTest { ...@@ -78,6 +78,12 @@ class AccessibilityWinBrowserTest : public ContentBrowserTest {
Microsoft::WRL::ComPtr<IAccessibleText>* input_text); Microsoft::WRL::ComPtr<IAccessibleText>* input_text);
void SetUpSingleCharInputField( void SetUpSingleCharInputField(
Microsoft::WRL::ComPtr<IAccessibleText>* input_text); Microsoft::WRL::ComPtr<IAccessibleText>* input_text);
void SetUpSingleCharInputFieldWithPlaceholder(
Microsoft::WRL::ComPtr<IAccessibleText>* input_text);
void SetUpSingleCharTextarea(
Microsoft::WRL::ComPtr<IAccessibleText>* input_text);
void SetUpSingleCharContenteditable(
Microsoft::WRL::ComPtr<IAccessibleText>* input_text);
void SetUpSingleCharRtlInputField( void SetUpSingleCharRtlInputField(
Microsoft::WRL::ComPtr<IAccessibleText>* input_text); Microsoft::WRL::ComPtr<IAccessibleText>* input_text);
void SetUpTextareaField( void SetUpTextareaField(
...@@ -206,6 +212,8 @@ void AccessibilityWinBrowserTest::SetUpScrollableInputField( ...@@ -206,6 +212,8 @@ void AccessibilityWinBrowserTest::SetUpScrollableInputField(
} }
// Loads a page with an input text field and places a single character in it. // Loads a page with an input text field and places a single character in it.
// Also tests with padding, in order to ensure character extent of empty field
// does not erroneously include padding.
void AccessibilityWinBrowserTest::SetUpSingleCharInputField( void AccessibilityWinBrowserTest::SetUpSingleCharInputField(
Microsoft::WRL::ComPtr<IAccessibleText>* input_text) { Microsoft::WRL::ComPtr<IAccessibleText>* input_text) {
ASSERT_NE(nullptr, input_text); ASSERT_NE(nullptr, input_text);
...@@ -214,7 +222,37 @@ void AccessibilityWinBrowserTest::SetUpSingleCharInputField( ...@@ -214,7 +222,37 @@ void AccessibilityWinBrowserTest::SetUpSingleCharInputField(
<html> <html>
<body> <body>
<form> <form>
<input type="text" id="textField" name="name" value="x"> <input type="text" value="x" style="padding:3px">
</form>
</body>
</html>)HTML"));
SetUpInputFieldHelper(input_text);
}
void AccessibilityWinBrowserTest::SetUpSingleCharInputFieldWithPlaceholder(
Microsoft::WRL::ComPtr<IAccessibleText>* input_text) {
ASSERT_NE(nullptr, input_text);
LoadInitialAccessibilityTreeFromHtml(std::string(
R"HTML(<!DOCTYPE html>
<html>
<body>
<form>
<input type="text" value="x" placeholder="placeholder">
</form>
</body>
</html>)HTML"));
SetUpInputFieldHelper(input_text);
}
void AccessibilityWinBrowserTest::SetUpSingleCharTextarea(
Microsoft::WRL::ComPtr<IAccessibleText>* input_text) {
ASSERT_NE(nullptr, input_text);
LoadInitialAccessibilityTreeFromHtml(std::string(
R"HTML(<!DOCTYPE html>
<html>
<body>
<form>
<textarea rows="3" cols="10">x</textarea>
</form> </form>
</body> </body>
</html>)HTML")); </html>)HTML"));
...@@ -274,12 +312,13 @@ void AccessibilityWinBrowserTest::SetUpInputFieldHelper( ...@@ -274,12 +312,13 @@ void AccessibilityWinBrowserTest::SetUpInputFieldHelper(
ax::mojom::Event::kTextSelectionChanged); ax::mojom::Event::kTextSelectionChanged);
std::wstring caret_offset = base::UTF16ToWide( std::wstring caret_offset = base::UTF16ToWide(
base::IntToString16(static_cast<int>(kContentsLength - 1))); base::IntToString16(static_cast<int>(kContentsLength - 1)));
ExecuteScript(std::wstring(L"let textField = document.querySelector('input');" ExecuteScript(
L"textField.focus();" std::wstring(L"let textField = document.querySelector('input,textarea');"
L"textField.setSelectionRange(") + L"textField.focus();"
caret_offset + L"," + caret_offset + L"textField.setSelectionRange(") +
L");" caret_offset + L"," + caret_offset +
L"textField.scrollLeft = 1000;"); L");"
L"textField.scrollLeft = 1000;");
waiter.WaitForNotification(); waiter.WaitForNotification();
} }
...@@ -1433,10 +1472,8 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, ...@@ -1433,10 +1472,8 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
} }
} }
// TODO(accessibility) Support bounds in an empty text field where there are
// no child inline text objects, such that the following test passes.
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
DISABLED_TestCharacterExtentsInEmptyInputField) { TestCharacterExtentsInEmptyInputField) {
Microsoft::WRL::ComPtr<IAccessibleText> input_text; Microsoft::WRL::ComPtr<IAccessibleText> input_text;
SetUpSingleCharInputField(&input_text); SetUpSingleCharInputField(&input_text);
...@@ -1453,6 +1490,60 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, ...@@ -1453,6 +1490,60 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
EXPECT_LT(1, prev_width); EXPECT_LT(1, prev_width);
EXPECT_LT(1, prev_height); EXPECT_LT(1, prev_height);
base::win::ScopedBstr text0;
ASSERT_HRESULT_SUCCEEDED(input_text->get_text(0, -1, text0.Receive()));
// Delete the character in the input field.
AccessibilityNotificationWaiter waiter(
shell()->web_contents(), ui::kAXModeComplete,
ax::mojom::Event::kTextSelectionChanged);
ExecuteScript(std::wstring(L"document.querySelector('input').value='';"));
waiter.WaitForNotification();
ASSERT_HRESULT_SUCCEEDED(input_text->get_nCharacters(&n_characters));
ASSERT_EQ(0, n_characters);
LONG caret_offset;
ASSERT_HRESULT_SUCCEEDED(input_text->get_caretOffset(&caret_offset));
ASSERT_EQ(0, caret_offset);
base::win::ScopedBstr text;
ASSERT_HRESULT_SUCCEEDED(input_text->get_text(0, -1, text.Receive()));
// Now that input is completely empty, the position of the caret should be
// returned for character 0. The x,y position and height should be the same as
// it was as when there was single character, but the width should now be 1.
LONG x, y, width, height;
for (int offset = IA2_TEXT_OFFSET_CARET; offset <= 0; ++offset) {
EXPECT_HRESULT_SUCCEEDED(input_text->get_characterExtents(
offset, IA2_COORDTYPE_SCREEN_RELATIVE, &x, &y, &width, &height));
EXPECT_EQ(prev_x, x);
EXPECT_EQ(prev_y, y);
EXPECT_EQ(1, width);
EXPECT_EQ(prev_height, height);
}
}
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
TestCharacterExtentsInEmptyInputFieldWithPlaceholder) {
Microsoft::WRL::ComPtr<IAccessibleText> input_text;
SetUpSingleCharInputFieldWithPlaceholder(&input_text);
LONG n_characters;
ASSERT_HRESULT_SUCCEEDED(input_text->get_nCharacters(&n_characters));
ASSERT_EQ(1, n_characters);
// Get the rect for the only character.
LONG prev_x, prev_y, prev_width, prev_height;
EXPECT_HRESULT_SUCCEEDED(input_text->get_characterExtents(
0, IA2_COORDTYPE_SCREEN_RELATIVE, &prev_x, &prev_y, &prev_width,
&prev_height));
EXPECT_LT(1, prev_width);
EXPECT_LT(1, prev_height);
base::win::ScopedBstr text0;
ASSERT_HRESULT_SUCCEEDED(input_text->get_text(0, -1, text0.Receive()));
// Delete the character in the input field. // Delete the character in the input field.
AccessibilityNotificationWaiter waiter( AccessibilityNotificationWaiter waiter(
shell()->web_contents(), ui::kAXModeComplete, shell()->web_contents(), ui::kAXModeComplete,
...@@ -1466,6 +1557,117 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, ...@@ -1466,6 +1557,117 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
ASSERT_HRESULT_SUCCEEDED(input_text->get_caretOffset(&caret_offset)); ASSERT_HRESULT_SUCCEEDED(input_text->get_caretOffset(&caret_offset));
ASSERT_EQ(0, caret_offset); ASSERT_EQ(0, caret_offset);
base::win::ScopedBstr text;
ASSERT_HRESULT_SUCCEEDED(input_text->get_text(0, -1, text.Receive()));
// Now that input is completely empty, the position of the caret should be
// returned for character 0. The x,y position and height should be the same as
// it was as when there was single character, but the width should now be 1.
LONG x, y, width, height;
for (int offset = IA2_TEXT_OFFSET_CARET; offset <= 0; ++offset) {
EXPECT_HRESULT_SUCCEEDED(input_text->get_characterExtents(
offset, IA2_COORDTYPE_SCREEN_RELATIVE, &x, &y, &width, &height));
EXPECT_EQ(prev_x, x);
EXPECT_EQ(prev_y, y);
EXPECT_EQ(1, width);
EXPECT_EQ(prev_height, height);
}
}
// TODO(accessibility) empty contenteditable gets height of entire
// contenteditable instead of just 1 line. May be able to use the following
// in Blink to get the height of a line -- it's at least close:
// layout_object->Style()->GetFont().PrimaryFont()->GetFontMetrics().Height()
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
DISABLED_TestCharacterExtentsInEmptyContenteditable) {
Microsoft::WRL::ComPtr<IAccessibleText> input_text;
SetUpSampleParagraphInScrollableEditable(&input_text);
LONG n_characters;
ASSERT_HRESULT_SUCCEEDED(input_text->get_nCharacters(&n_characters));
ASSERT_LT(0, n_characters);
// Get the rect for the only character.
LONG prev_x, prev_y, prev_width, prev_height;
EXPECT_HRESULT_SUCCEEDED(input_text->get_characterExtents(
0, IA2_COORDTYPE_SCREEN_RELATIVE, &prev_x, &prev_y, &prev_width,
&prev_height));
EXPECT_LT(1, prev_width);
EXPECT_LT(1, prev_height);
base::win::ScopedBstr text0;
ASSERT_HRESULT_SUCCEEDED(input_text->get_text(0, -1, text0.Receive()));
// Delete the character in the input field.
AccessibilityNotificationWaiter waiter(shell()->web_contents(),
ui::kAXModeComplete,
ax::mojom::Event::kChildrenChanged);
ExecuteScript(std::wstring(
L"document.querySelector('[contenteditable]').innerText='';"));
waiter.WaitForNotification();
ASSERT_HRESULT_SUCCEEDED(input_text->get_nCharacters(&n_characters));
ASSERT_EQ(0, n_characters);
LONG caret_offset;
ASSERT_HRESULT_SUCCEEDED(input_text->get_caretOffset(&caret_offset));
ASSERT_EQ(0, caret_offset);
base::win::ScopedBstr text;
ASSERT_HRESULT_SUCCEEDED(input_text->get_text(0, -1, text.Receive()));
// Now that input is completely empty, the position of the caret should be
// returned for character 0. The x,y position and height should be the same as
// it was as when there was single character, but the width should now be 1.
LONG x, y, width, height;
for (int offset = IA2_TEXT_OFFSET_CARET; offset <= 0; ++offset) {
EXPECT_HRESULT_SUCCEEDED(input_text->get_characterExtents(
offset, IA2_COORDTYPE_SCREEN_RELATIVE, &x, &y, &width, &height));
EXPECT_EQ(prev_x, x);
EXPECT_EQ(prev_y, y);
EXPECT_EQ(1, width);
EXPECT_EQ(prev_height, height);
}
}
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
TestCharacterExtentsInEmptyTextarea) {
Microsoft::WRL::ComPtr<IAccessibleText> input_text;
SetUpSingleCharTextarea(&input_text);
LONG n_characters;
ASSERT_HRESULT_SUCCEEDED(input_text->get_nCharacters(&n_characters));
ASSERT_EQ(1, n_characters);
// Get the rect for the only character.
LONG prev_x, prev_y, prev_width, prev_height;
EXPECT_HRESULT_SUCCEEDED(input_text->get_characterExtents(
0, IA2_COORDTYPE_SCREEN_RELATIVE, &prev_x, &prev_y, &prev_width,
&prev_height));
EXPECT_LT(1, prev_width);
EXPECT_LT(1, prev_height);
base::win::ScopedBstr text0;
ASSERT_HRESULT_SUCCEEDED(input_text->get_text(0, -1, text0.Receive()));
// Delete the character in the input field.
AccessibilityNotificationWaiter waiter(
shell()->web_contents(), ui::kAXModeComplete,
ax::mojom::Event::kTextSelectionChanged);
ExecuteScript(
std::wstring(L"document.querySelector('textarea').innerText='';"));
waiter.WaitForNotification();
ASSERT_HRESULT_SUCCEEDED(input_text->get_nCharacters(&n_characters));
ASSERT_EQ(0, n_characters);
LONG caret_offset;
ASSERT_HRESULT_SUCCEEDED(input_text->get_caretOffset(&caret_offset));
ASSERT_EQ(0, caret_offset);
base::win::ScopedBstr text;
ASSERT_HRESULT_SUCCEEDED(input_text->get_text(0, -1, text.Receive()));
// Now that input is completely empty, the position of the caret should be // Now that input is completely empty, the position of the caret should be
// returned for character 0. The x,y position and height should be the same as // returned for character 0. The x,y position and height should be the same as
// it was as when there was single character, but the width should now be 1. // it was as when there was single character, but the width should now be 1.
...@@ -1480,10 +1682,8 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, ...@@ -1480,10 +1682,8 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
} }
} }
// TODO(accessibility) Support bounds in an empty text field where there are
// no child inline text objects, such that the following test passes.
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
DISABLED_TestCharacterExtentsInEmptyRtlInputField) { TestCharacterExtentsInEmptyRtlInputField) {
Microsoft::WRL::ComPtr<IAccessibleText> input_text; Microsoft::WRL::ComPtr<IAccessibleText> input_text;
SetUpSingleCharRtlInputField(&input_text); SetUpSingleCharRtlInputField(&input_text);
......
...@@ -511,9 +511,8 @@ gfx::Rect BrowserAccessibility::GetPageBoundsPastEndOfText() const { ...@@ -511,9 +511,8 @@ gfx::Rect BrowserAccessibility::GetPageBoundsPastEndOfText() const {
if (bounds.width() == 0 && bounds.height() == 0) if (bounds.width() == 0 && bounds.height() == 0)
return bounds; // Inline text boxes info not yet available. return bounds; // Inline text boxes info not yet available.
} else { } else {
// TODO(accessibility) Support bounds in an empty text field where there are // Compute bounds of where caret would be, based on bounds of object.
// no child inline text objects. bounds = GetPageBoundsRect();
return bounds;
} }
// Step 2: correct for the thickness of the caret. // Step 2: correct for the thickness of the caret.
......
...@@ -8,3 +8,4 @@ rootWebArea ...@@ -8,3 +8,4 @@ rootWebArea
++++staticText name='Complex button ' ++++staticText name='Complex button '
++++++inlineTextBox name='Complex button ' ++++++inlineTextBox name='Complex button '
++++textField ++++textField
++++++genericContainer
\ No newline at end of file
...@@ -7,5 +7,6 @@ rootWebArea ...@@ -7,5 +7,6 @@ rootWebArea
++++staticText name='Complex ' ++++staticText name='Complex '
++++++inlineTextBox name='Complex ' ++++++inlineTextBox name='Complex '
++++textField ++++textField
++++++genericContainer
++++staticText name=' checkbox' ++++staticText name=' checkbox'
++++++inlineTextBox name=' checkbox' ++++++inlineTextBox name=' checkbox'
\ No newline at end of file
...@@ -3,6 +3,7 @@ rootWebArea ...@@ -3,6 +3,7 @@ rootWebArea
++++staticText name='State' ++++staticText name='State'
++++++inlineTextBox name='State' ++++++inlineTextBox name='State'
++textFieldWithComboBox autoComplete='list' name='State' activedescendantId=listBoxOption restriction=readOnly haspopup=listbox ++textFieldWithComboBox autoComplete='list' name='State' activedescendantId=listBoxOption restriction=readOnly haspopup=listbox
++++genericContainer
++listBox ++listBox
++++listBoxOption name='Alabama' selected=false ++++listBoxOption name='Alabama' selected=false
++++listBoxOption name='Alaska' selected=true ++++listBoxOption name='Alaska' selected=true
\ No newline at end of file
rootWebArea rootWebArea
++textField description='Your username should be your email id' descriptionFrom=relatedElement describedbyIds=tooltip ++textField description='Your username should be your email id' descriptionFrom=relatedElement describedbyIds=tooltip
++++genericContainer
++tooltip name='Your username should be your email id' ++tooltip name='Your username should be your email id'
++++staticText name='Your username should be your email id' ++++staticText name='Your username should be your email id'
++++++inlineTextBox name='Your username should be your email id' ++++++inlineTextBox name='Your username should be your email id'
++textField description='mmddyy' descriptionFrom=relatedElement describedbyIds=null ++textField description='mmddyy' descriptionFrom=relatedElement describedbyIds=null
++++genericContainer
\ No newline at end of file
rootWebArea rootWebArea
++genericContainer ++genericContainer
++++textField detailsId=paragraph ++++textField detailsId=paragraph
++++++genericContainer
++paragraph ++paragraph
++++staticText name='Details' ++++staticText name='Details'
++++++inlineTextBox name='Details' ++++++inlineTextBox name='Details'
...@@ -14,4 +15,4 @@ rootWebArea ...@@ -14,4 +15,4 @@ rootWebArea
++++++inlineTextBox name=' has details' ++++++inlineTextBox name=' has details'
++group ++group
++++staticText name='Text details' ++++staticText name='Text details'
++++++inlineTextBox name='Text details' ++++++inlineTextBox name='Text details'
\ No newline at end of file
rootWebArea rootWebArea
++genericContainer ++genericContainer
++++textField ++++textField
++++++genericContainer
++++textField restriction=disabled ++++textField restriction=disabled
++++++genericContainer
++++textField ++++textField
++++++genericContainer
++genericContainer restriction=disabled ++genericContainer restriction=disabled
++++textField restriction=disabled ++++textField restriction=disabled
++++++genericContainer
++++textField restriction=disabled ++++textField restriction=disabled
++++++genericContainer
++++textField ++++textField
++++++genericContainer
++genericContainer ++genericContainer
++++textField ++++textField
++++++genericContainer
++++textField restriction=disabled ++++textField restriction=disabled
++++++genericContainer
++++textField ++++textField
++++++genericContainer
\ No newline at end of file
rootWebArea rootWebArea
++genericContainer ++genericContainer
++++textField errormessageId=paragraph invalidState=true ++++textField errormessageId=paragraph invalidState=true
++++++genericContainer
++paragraph ++paragraph
++++staticText name='Error' ++++staticText name='Error'
++++++inlineTextBox name='Error' ++++++inlineTextBox name='Error'
\ No newline at end of file
rootWebArea rootWebArea
++textField name='h2' ++textField name='h2'
++++genericContainer
++heading name='h2' hierarchicalLevel=2 ++heading name='h2' hierarchicalLevel=2
++++staticText name='h2' ++++staticText name='h2'
++++++inlineTextBox name='h2' ++++++inlineTextBox name='h2'
\ No newline at end of file
...@@ -6,5 +6,6 @@ rootWebArea ...@@ -6,5 +6,6 @@ rootWebArea
++++++staticText name='Complex ' ++++++staticText name='Complex '
++++++++inlineTextBox name='Complex ' ++++++++inlineTextBox name='Complex '
++++++textField ++++++textField
++++++++genericContainer
++++++staticText name=' menuitem' ++++++staticText name=' menuitem'
++++++++inlineTextBox name=' menuitem' ++++++++inlineTextBox name=' menuitem'
\ No newline at end of file
...@@ -19,6 +19,7 @@ rootWebArea ...@@ -19,6 +19,7 @@ rootWebArea
++textField name='Readonly-true contenteditable textbox' restriction=readOnly ++textField name='Readonly-true contenteditable textbox' restriction=readOnly
++checkBox name='Readonly checkbox' restriction=readOnly ++checkBox name='Readonly checkbox' restriction=readOnly
++textFieldWithComboBox name='Readonly combobox' restriction=readOnly haspopup=listbox ++textFieldWithComboBox name='Readonly combobox' restriction=readOnly haspopup=listbox
++++genericContainer
++listBox name='Readonly listbox' restriction=readOnly ++listBox name='Readonly listbox' restriction=readOnly
++radioGroup name='Readonly radiogroup' restriction=readOnly ++radioGroup name='Readonly radiogroup' restriction=readOnly
++slider horizontal name='Readonly slider' restriction=readOnly ++slider horizontal name='Readonly slider' restriction=readOnly
...@@ -26,4 +27,4 @@ rootWebArea ...@@ -26,4 +27,4 @@ rootWebArea
++menuItemCheckBox name='Readonly menuitemcheckbox' restriction=readOnly ++menuItemCheckBox name='Readonly menuitemcheckbox' restriction=readOnly
++menuItemRadio name='Readonly menuitemradio' restriction=readOnly ++menuItemRadio name='Readonly menuitemradio' restriction=readOnly
++searchBox name='Readonly searchbox' restriction=readOnly ++searchBox name='Readonly searchbox' restriction=readOnly
++switch name='Readonly switch' restriction=readOnly ++switch name='Readonly switch' restriction=readOnly
\ No newline at end of file
rootWebArea rootWebArea
++textField description='Your username should be your email id' descriptionFrom=relatedElement ++textField description='Your username should be your email id' descriptionFrom=relatedElement
++++genericContainer
++tooltip name='Your username should be your email id' ++tooltip name='Your username should be your email id'
++++staticText name='Your username should be your email id' ++++staticText name='Your username should be your email id'
++++++inlineTextBox name='Your username should be your email id' ++++++inlineTextBox name='Your username should be your email id'
\ No newline at end of file
...@@ -4,11 +4,13 @@ rootWebArea ...@@ -4,11 +4,13 @@ rootWebArea
++++++inlineTextBox name='State' ++++++inlineTextBox name='State'
++comboBoxGrouping name='State' haspopup=listbox ++comboBoxGrouping name='State' haspopup=listbox
++++textField activedescendantId=listBoxOption controlsIds=listBox ++++textField activedescendantId=listBoxOption controlsIds=listBox
++++++genericContainer
++listBox ++listBox
++++listBoxOption name='Alabama' selected=false ++++listBoxOption name='Alabama' selected=false
++++listBoxOption name='Alaska' selected=true ++++listBoxOption name='Alaska' selected=true
++comboBoxGrouping name='State' haspopup=listbox ++comboBoxGrouping name='State' haspopup=listbox
++++textField activedescendantId=listBoxOption controlsIds=listBox ++++textField activedescendantId=listBoxOption controlsIds=listBox
++++++genericContainer
++listBox ++listBox
++++listBoxOption name='Alabama' selected=false ++++listBoxOption name='Alabama' selected=false
++++listBoxOption name='Alaska' selected=false ++++listBoxOption name='Alaska' selected=false
\ No newline at end of file
rootWebArea rootWebArea
++textField name='aria-placeholder1' ++textField name='aria-placeholder1'
++++genericContainer
++textField name='placeholder2' ++textField name='placeholder2'
++++genericContainer ++++genericContainer
++textField name='aria-label3' placeholder='placeholder3' ++textField name='aria-label3' placeholder='placeholder3'
++++genericContainer ++++genericContainer
++textField name='aria-label4' placeholder='aria-placeholder4' ++textField name='aria-label4' placeholder='aria-placeholder4'
++++genericContainer
++textField description='aria-description5' name='aria-label5' placeholder='placeholder5' descriptionFrom=relatedElement ++textField description='aria-description5' name='aria-label5' placeholder='placeholder5' descriptionFrom=relatedElement
++++genericContainer ++++genericContainer
++genericContainer ++genericContainer
++++staticText name='aria-description5' ++++staticText name='aria-description5'
++++++inlineTextBox name='aria-description5' ++++++inlineTextBox name='aria-description5'
++textField description='title6' name='aria-placeholder6' descriptionFrom=attribute ++textField description='title6' name='aria-placeholder6' descriptionFrom=attribute
\ No newline at end of file ++++genericContainer
\ No newline at end of file
...@@ -10,8 +10,11 @@ rootWebArea name='Action verbs' ...@@ -10,8 +10,11 @@ rootWebArea name='Action verbs'
++++staticText name='Link' defaultActionVerb=clickAncestor ++++staticText name='Link' defaultActionVerb=clickAncestor
++++++inlineTextBox name='Link' ++++++inlineTextBox name='Link'
++textField defaultActionVerb=activate ++textField defaultActionVerb=activate
++++genericContainer
++searchBox defaultActionVerb=activate ++searchBox defaultActionVerb=activate
++++genericContainer
++textField multiline defaultActionVerb=activate ++textField multiline defaultActionVerb=activate
++++genericContainer
++textField defaultActionVerb=activate ++textField defaultActionVerb=activate
++checkBox defaultActionVerb=check checkedState=false ++checkBox defaultActionVerb=check checkedState=false
++checkBox defaultActionVerb=uncheck checkedState=true ++checkBox defaultActionVerb=uncheck checkedState=true
...@@ -34,4 +37,4 @@ rootWebArea name='Action verbs' ...@@ -34,4 +37,4 @@ rootWebArea name='Action verbs'
++menu ++menu
++++menuItem name='Menu item 1' defaultActionVerb=select ++++menuItem name='Menu item 1' defaultActionVerb=select
++++menuItemCheckBox name='Menu item 2' defaultActionVerb=uncheck checkedState=true ++++menuItemCheckBox name='Menu item 2' defaultActionVerb=uncheck checkedState=true
++++menuItemRadio name='Menu item 3' defaultActionVerb=check checkedState=false ++++menuItemRadio name='Menu item 3' defaultActionVerb=check checkedState=false
\ No newline at end of file
...@@ -4,3 +4,4 @@ rootWebArea focusable ...@@ -4,3 +4,4 @@ rootWebArea focusable
++++++staticText name='Choose a pokemon ' ++++++staticText name='Choose a pokemon '
++++++++inlineTextBox name='Choose a pokemon ' ++++++++inlineTextBox name='Choose a pokemon '
++++++textFieldWithComboBox editable focusable autoComplete='list' name='Choose a pokemon' haspopup=listbox ++++++textFieldWithComboBox editable focusable autoComplete='list' name='Choose a pokemon' haspopup=listbox
++++++++genericContainer
\ No newline at end of file
...@@ -2,9 +2,11 @@ rootWebArea ...@@ -2,9 +2,11 @@ rootWebArea
++genericContainer ++genericContainer
++++spinButton value='1' valueForRange=1.00 ++++spinButton value='1' valueForRange=1.00
++++++genericContainer ++++++genericContainer
++++++++staticText name='1' ++++++++genericContainer
++++++++++inlineTextBox name='1' ++++++++++staticText name='1'
++++++++++++inlineTextBox name='1'
++++spinButton value='6' valueForRange=6.00 minValueForRange=5.00 maxValueForRange=10.00 ++++spinButton value='6' valueForRange=6.00 minValueForRange=5.00 maxValueForRange=10.00
++++++genericContainer ++++++genericContainer
++++++++staticText name='6' ++++++++genericContainer
++++++++++inlineTextBox name='6' ++++++++++staticText name='6'
++++++++++++inlineTextBox name='6'
\ No newline at end of file
rootWebArea rootWebArea
++genericContainer ++genericContainer
++++textField ++++textField
++++++genericContainer
++++button name='Reset' ++++button name='Reset'
\ No newline at end of file
rootWebArea rootWebArea
++form ++form
++++textField ++++textField
++++++genericContainer
++++button name='Submit' ++++button name='Submit'
\ No newline at end of file
rootWebArea rootWebArea
++genericContainer ++genericContainer
++++textFieldWithComboBox autoComplete='list' haspopup=listbox ++++textFieldWithComboBox autoComplete='list' haspopup=listbox
++++++genericContainer
\ No newline at end of file
rootWebArea rootWebArea
++textField name='Title0' ++textField name='Title0'
++++genericContainer
++textField description='Title1' name='Label1' descriptionFrom=attribute ++textField description='Title1' name='Label1' descriptionFrom=attribute
++++genericContainer
++textField description='Title2' name='AriaLabel2' descriptionFrom=attribute ++textField description='Title2' name='AriaLabel2' descriptionFrom=attribute
++++genericContainer
++textField description='Title3' name='LabelledBy3' descriptionFrom=attribute ++textField description='Title3' name='LabelledBy3' descriptionFrom=attribute
++++genericContainer
++textField name='Placeholder4' ++textField name='Placeholder4'
++++genericContainer ++++genericContainer
++textField name='ARIA Placeholder4a' ++textField name='ARIA Placeholder4a'
++++genericContainer
++textField name='Placeholder4b' ++textField name='Placeholder4b'
++++genericContainer ++++genericContainer
++textField description='Title5' name='Placeholder5' descriptionFrom=attribute ++textField description='Title5' name='Placeholder5' descriptionFrom=attribute
++++genericContainer ++++genericContainer
++textField description='DescribedBy6' name='LabelledBy6' descriptionFrom=relatedElement ++textField description='DescribedBy6' name='LabelledBy6' descriptionFrom=relatedElement
\ No newline at end of file ++++genericContainer
\ No newline at end of file
...@@ -4,6 +4,7 @@ rootWebArea focusable ...@@ -4,6 +4,7 @@ rootWebArea focusable
++++++staticText name='l1' ++++++staticText name='l1'
++++++++inlineTextBox name='l1' ++++++++inlineTextBox name='l1'
++++textField focusable name='l1' ++++textField focusable name='l1'
++++++genericContainer
++++labelText ++++labelText
++++++staticText name='l2' ++++++staticText name='l2'
++++++++inlineTextBox name='l2' ++++++++inlineTextBox name='l2'
...@@ -12,6 +13,7 @@ rootWebArea focusable ...@@ -12,6 +13,7 @@ rootWebArea focusable
++++++++staticText name='value' ++++++++staticText name='value'
++++++++++inlineTextBox name='value' ++++++++++inlineTextBox name='value'
++++textField focusable name='l2' ++++textField focusable name='l2'
++++++genericContainer
++++textField focusable name='l2' value='value *' ++++textField focusable name='l2' value='value *'
++++++genericContainer ++++++genericContainer
++++++++staticText name='value *' ++++++++staticText name='value *'
...@@ -21,11 +23,13 @@ rootWebArea focusable ...@@ -21,11 +23,13 @@ rootWebArea focusable
++++++++inlineTextBox name='Email' ++++++++inlineTextBox name='Email'
++++genericContainer ++++genericContainer
++++textField focusable name='Email' ++++textField focusable name='Email'
++++++genericContainer
++++textField focusable name='Email' value='value' ++++textField focusable name='Email' value='value'
++++++genericContainer ++++++genericContainer
++++++++staticText name='value' ++++++++staticText name='value'
++++++++++inlineTextBox name='value' ++++++++++inlineTextBox name='value'
++++textField focusable multiline name='l5' ++++textField focusable multiline name='l5'
++++++genericContainer
++++textField focusable multiline name='l6' value='Value' ++++textField focusable multiline name='l6' value='Value'
++++++genericContainer ++++++genericContainer
++++++++staticText name='Value' ++++++++staticText name='Value'
...@@ -33,4 +37,4 @@ rootWebArea focusable ...@@ -33,4 +37,4 @@ rootWebArea focusable
++++textField focusable description='Description' name='Name' value='value' descriptionFrom=attribute ++++textField focusable description='Description' name='Name' value='value' descriptionFrom=attribute
++++++genericContainer ++++++genericContainer
++++++++staticText name='value' ++++++++staticText name='value'
++++++++++inlineTextBox name='value' ++++++++++inlineTextBox name='value'
\ No newline at end of file
rootWebArea rootWebArea
++genericContainer ++genericContainer
++++textField name='oranges' ++++textField name='oranges'
++++++genericContainer
++++textField name='bananas' ++++textField name='bananas'
++++++genericContainer
\ No newline at end of file
...@@ -7,7 +7,9 @@ rootWebArea ...@@ -7,7 +7,9 @@ rootWebArea
++++++staticText name='Browser: ' ++++++staticText name='Browser: '
++++++++inlineTextBox name='Browser: ' ++++++++inlineTextBox name='Browser: '
++++++textField ++++++textField
++++++++genericContainer
++++++staticText name=' Rendering Engine: ' ++++++staticText name=' Rendering Engine: '
++++++++inlineTextBox name=' ' ++++++++inlineTextBox name=' '
++++++++inlineTextBox name='Rendering Engine: ' ++++++++inlineTextBox name='Rendering Engine: '
++++++textField ++++++textField
\ No newline at end of file ++++++++genericContainer
\ No newline at end of file
...@@ -64,9 +64,11 @@ test(function(t) ...@@ -64,9 +64,11 @@ test(function(t)
var axContainer3 = accessibilityController.accessibleElementById("container3"); var axContainer3 = accessibilityController.accessibleElementById("container3");
assert_equals(axContainer3.childrenCount, 2); assert_equals(axContainer3.childrenCount, 2);
assert_equals(axContainer3.childAtIndex(0).role, "AXRole: AXTextField"); assert_equals(axContainer3.childAtIndex(0).role, "AXRole: AXTextField");
assert_equals(axContainer3.childAtIndex(0).childrenCount, 0); // An input element naturally gets 1 child that corresponds to the anonymous
// inner editor element.
assert_equals(axContainer3.childAtIndex(0).childrenCount, 1);
assert_equals(axContainer3.childAtIndex(1).role, "AXRole: AXMenu"); assert_equals(axContainer3.childAtIndex(1).role, "AXRole: AXMenu");
}, "Aria-owns doesn't create children of an input element."); }, "Aria-owns doesn't create extra children of an input element.");
</script> </script>
<div class="container" id="container4" role="group" aria-label="Container"> <div class="container" id="container4" role="group" aria-label="Container">
......
...@@ -4,7 +4,7 @@ This tests a crashing scenario where an element with a role attribute is a child ...@@ -4,7 +4,7 @@ This tests a crashing scenario where an element with a role attribute is a child
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
PASS input.childrenCount is 0 PASS input.childrenCount is 1
PASS successfullyParsed is true PASS successfullyParsed is true
TEST COMPLETE TEST COMPLETE
......
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
// This line should not crash. // This line should not crash.
var input = accessibilityController.focusedElement; var input = accessibilityController.focusedElement;
shouldBe("input.childrenCount", "0"); shouldBe("input.childrenCount", "1");
} }
</script> </script>
......
...@@ -629,6 +629,30 @@ static bool HasLineBox(const LayoutBlockFlow& block_flow) { ...@@ -629,6 +629,30 @@ static bool HasLineBox(const LayoutBlockFlow& block_flow) {
return false; return false;
} }
// Is this the anonymous placeholder for a text control?
bool AXLayoutObject::IsPlaceholder() const {
AXObject* parent_object = ParentObject();
if (!parent_object)
return false;
LayoutObject* parent_layout_object = parent_object->GetLayoutObject();
if (!parent_layout_object || !parent_layout_object->IsTextControl())
return false;
LayoutTextControl* layout_text_control =
ToLayoutTextControl(parent_layout_object);
DCHECK(layout_text_control);
TextControlElement* text_control_element =
layout_text_control->GetTextControlElement();
if (!text_control_element)
return false;
HTMLElement* placeholder_element = text_control_element->PlaceholderElement();
return GetElement() == static_cast<Element*>(placeholder_element);
}
bool AXLayoutObject::ComputeAccessibilityIsIgnored( bool AXLayoutObject::ComputeAccessibilityIsIgnored(
IgnoredReasons* ignored_reasons) const { IgnoredReasons* ignored_reasons) const {
#if DCHECK_IS_ON() #if DCHECK_IS_ON()
...@@ -687,8 +711,18 @@ bool AXLayoutObject::ComputeAccessibilityIsIgnored( ...@@ -687,8 +711,18 @@ bool AXLayoutObject::ComputeAccessibilityIsIgnored(
// Make sure renderers with layers stay in the tree. // Make sure renderers with layers stay in the tree.
if (GetLayoutObject() && GetLayoutObject()->HasLayer() && GetNode() && if (GetLayoutObject() && GetLayoutObject()->HasLayer() && GetNode() &&
GetNode()->hasChildren()) GetNode()->hasChildren()) {
if (IsPlaceholder()) {
// Placeholder is already exposed via AX attributes, do not expose as
// child of text input. Therefore, if there is a child of a text input,
// it will contain the value.
if (ignored_reasons)
ignored_reasons->push_back(IgnoredReason(kAXPresentational));
return true;
}
return false; return false;
}
// Find out if this element is inside of a label element. If so, it may be // Find out if this element is inside of a label element. If so, it may be
// ignored because it's the label for a checkbox or radio button. // ignored because it's the label for a checkbox or radio button.
...@@ -871,6 +905,14 @@ bool AXLayoutObject::ComputeAccessibilityIsIgnored( ...@@ -871,6 +905,14 @@ bool AXLayoutObject::ComputeAccessibilityIsIgnored(
if (layout_object_->IsPositioned()) if (layout_object_->IsPositioned())
return false; return false;
// Inner editor element of editable area with empty text provides bounds
// used to compute the character extent for index 0. This is the same as
// what the caret's bounds would be if the editable area is focused.
if (ParentObject() && ParentObject()->GetLayoutObject() &&
ParentObject()->GetLayoutObject()->IsTextControl()) {
return false;
}
// Ignore layout objects that are block flows with inline children. These // Ignore layout objects that are block flows with inline children. These
// are usually dummy layout objects that pad out the tree, but there are // are usually dummy layout objects that pad out the tree, but there are
// some exceptions below. // some exceptions below.
......
...@@ -236,6 +236,7 @@ class MODULES_EXPORT AXLayoutObject : public AXNodeObject { ...@@ -236,6 +236,7 @@ class MODULES_EXPORT AXLayoutObject : public AXNodeObject {
bool CanIgnoreTextAsEmpty() const; bool CanIgnoreTextAsEmpty() const;
bool CanIgnoreSpaceNextTo(LayoutObject*, bool is_after) const; bool CanIgnoreSpaceNextTo(LayoutObject*, bool is_after) const;
bool HasAriaCellRole(Element*) const; bool HasAriaCellRole(Element*) const;
bool IsPlaceholder() const;
bool is_autofill_available_; bool is_autofill_available_;
......
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