Commit 28dfa4c3 authored by Akihiro Ota's avatar Akihiro Ota Committed by Commit Bot

Set isContentInvalid attribute of NodeAccessibilityInfo

Added functionality to set the isContentInvalid attribute of
NodeAccessibilityInfo, with the larger goal of exposing this
information to TalkBack. Added a test to check the presence of this
attribute in an invalid editText object.

R=dmazzoni@chromium.org

Bug: 847233
Change-Id: Ib5ddc3287d719dce184e57af825df5720be0c5db
Reviewed-on: https://chromium-review.googlesource.com/c/1320219Reviewed-by: default avatarDominic Mazzoni <dmazzoni@chromium.org>
Reviewed-by: default avatarJay Civelli <jcivelli@chromium.org>
Commit-Queue: Akihiro Ota <akihiroota@chromium.org>
Cr-Commit-Position: refs/heads/master@{#606924}
parent 83efe502
......@@ -977,6 +977,19 @@ below:
<ph name="QUANTITY">$1<ex>42.0</ex></ph> PB
</message>
<message name="CONTENT_INVALID_TRUE" desc="Error message for aria-invalid = true">
Invalid
</message>
<message name="CONTENT_INVALID_SPELLING" desc="Error message for aria-invalid = spelling">
Invalid spelling
</message>
<message name="CONTENT_INVALID_GRAMMAR" desc="Error message for aria-invalid = grammar">
Invalid grammar
</message>
</messages>
</release>
</grit>
......@@ -1655,4 +1655,35 @@ int BrowserAccessibilityAndroid::CountChildrenWithRole(
return count;
}
base::string16 BrowserAccessibilityAndroid::GetContentInvalidErrorMessage()
const {
content::ContentClient* content_client = content::GetContentClient();
int message_id = -1;
switch (GetData().GetInvalidState()) {
case ax::mojom::InvalidState::kNone:
case ax::mojom::InvalidState::kFalse:
// No error message necessary
break;
case ax::mojom::InvalidState::kTrue:
case ax::mojom::InvalidState::kOther:
message_id = CONTENT_INVALID_TRUE;
break;
case ax::mojom::InvalidState::kSpelling:
message_id = CONTENT_INVALID_SPELLING;
break;
case ax::mojom::InvalidState::kGrammar:
message_id = CONTENT_INVALID_GRAMMAR;
break;
}
if (message_id != -1)
return content_client->GetLocalizedString(message_id);
return base::string16();
}
} // namespace content
......@@ -82,6 +82,8 @@ class CONTENT_EXPORT BrowserAccessibilityAndroid : public BrowserAccessibility {
std::string GetRoleString() const;
base::string16 GetContentInvalidErrorMessage() const;
base::string16 GetRoleDescription() const;
int GetItemIndex() const;
......
......@@ -719,12 +719,14 @@ jboolean WebContentsAccessibilityAndroid::PopulateAccessibilityNodeInfo(
base::android::ConvertUTF16ToJavaString(env, node->GetHint()),
node->GetIntAttribute(ax::mojom::IntAttribute::kTextSelStart),
node->GetIntAttribute(ax::mojom::IntAttribute::kTextSelEnd),
node->HasImage());
node->HasImage(), node->IsContentInvalid());
Java_WebContentsAccessibilityImpl_setAccessibilityNodeInfoLollipopAttributes(
env, obj, info, node->CanOpenPopup(), node->IsContentInvalid(),
node->IsDismissable(), node->IsMultiLine(), node->AndroidInputType(),
node->AndroidLiveRegionType());
node->AndroidLiveRegionType(),
base::android::ConvertUTF16ToJavaString(
env, node->GetContentInvalidErrorMessage()));
bool has_character_locations = node->HasCharacterLocations();
Java_WebContentsAccessibilityImpl_setAccessibilityNodeInfoOAttributes(
......
......@@ -34,7 +34,8 @@ public class KitKatWebContentsAccessibility extends WebContentsAccessibilityImpl
@Override
protected void setAccessibilityNodeInfoKitKatAttributes(AccessibilityNodeInfo node,
boolean isRoot, boolean isEditableText, String role, String roleDescription,
String hint, int selectionStartIndex, int selectionEndIndex, boolean hasImage) {
String hint, int selectionStartIndex, int selectionEndIndex, boolean hasImage,
boolean contentInvalid) {
Bundle bundle = node.getExtras();
bundle.putCharSequence("AccessibilityNodeInfo.chromeRole", role);
bundle.putCharSequence("AccessibilityNodeInfo.roleDescription", roleDescription);
......@@ -48,6 +49,8 @@ public class KitKatWebContentsAccessibility extends WebContentsAccessibilityImpl
node.setEditable(true);
node.setTextSelection(selectionStartIndex, selectionEndIndex);
}
node.setContentInvalid(contentInvalid);
}
@Override
......
......@@ -56,13 +56,14 @@ public class LollipopWebContentsAccessibility extends KitKatWebContentsAccessibi
@Override
protected void setAccessibilityNodeInfoLollipopAttributes(AccessibilityNodeInfo node,
boolean canOpenPopup, boolean contentInvalid, boolean dismissable, boolean multiLine,
int inputType, int liveRegion) {
int inputType, int liveRegion, String errorMessage) {
node.setCanOpenPopup(canOpenPopup);
node.setContentInvalid(contentInvalid);
node.setDismissable(contentInvalid);
node.setMultiLine(multiLine);
node.setInputType(inputType);
node.setLiveRegion(liveRegion);
node.setError(errorMessage);
}
@Override
......
......@@ -41,9 +41,11 @@ public class OWebContentsAccessibility extends LollipopWebContentsAccessibility
@Override
protected void setAccessibilityNodeInfoKitKatAttributes(AccessibilityNodeInfo node,
boolean isRoot, boolean isEditableText, String role, String roleDescription,
String hint, int selectionStartIndex, int selectionEndIndex, boolean hasImage) {
String hint, int selectionStartIndex, int selectionEndIndex, boolean hasImage,
boolean contentInvalid) {
super.setAccessibilityNodeInfoKitKatAttributes(node, isRoot, isEditableText, role,
roleDescription, hint, selectionStartIndex, selectionEndIndex, hasImage);
roleDescription, hint, selectionStartIndex, selectionEndIndex, hasImage,
contentInvalid);
node.setHintText(hint);
}
......
......@@ -1210,14 +1210,15 @@ public class WebContentsAccessibilityImpl extends AccessibilityNodeProvider
@CalledByNative
protected void setAccessibilityNodeInfoKitKatAttributes(AccessibilityNodeInfo node,
boolean isRoot, boolean isEditableText, String role, String roleDescription,
String hint, int selectionStartIndex, int selectionEndIndex, boolean hasImage) {
String hint, int selectionStartIndex, int selectionEndIndex, boolean hasImage,
boolean contentInvalid) {
// Requires KitKat or higher.
}
@CalledByNative
protected void setAccessibilityNodeInfoLollipopAttributes(AccessibilityNodeInfo node,
boolean canOpenPopup, boolean contentInvalid, boolean dismissable, boolean multiLine,
int inputType, int liveRegion) {
int inputType, int liveRegion, String errorMessage) {
// Requires Lollipop or higher.
}
......
......@@ -384,4 +384,94 @@ public class WebContentsAccessibilityTest {
}
Assert.fail("ACTION_SET_TEXT not found");
}
/**
* Tests presence of ContentInvalid attribute and correctness of
* error message given aria-invalid = true
**/
@Test
@MediumTest
@MinAndroidSdkLevel(Build.VERSION_CODES.LOLLIPOP)
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public void testEditTextFieldAriaInvalidTrueErrorMessage() throws Throwable {
final String data = "<form>\n"
+ " First name:<br>\n"
+ " <input id='fn' type='text' aria-invalid='true'><br>\n"
+ "<input type='submit'><br>"
+ "</form>";
mActivityTestRule.launchContentShellWithUrl(UrlUtils.encodeHtmlDataUri(data));
mActivityTestRule.waitForActiveShellToBeDoneLoading();
AccessibilityNodeProvider provider = enableAccessibilityAndWaitForNodeProvider();
int textNodeVirtualViewId = waitForNodeWithClassName(provider, "android.widget.EditText");
AccessibilityNodeInfo textNode =
provider.createAccessibilityNodeInfo(textNodeVirtualViewId);
Assert.assertNotEquals(textNode, null);
Assert.assertEquals(textNode.isContentInvalid(), true);
Assert.assertEquals(textNode.getError(), "Invalid");
}
/**
* Tests presence of ContentInvalid attribute and correctness of
* error message given aria-invalid = spelling
**/
@Test
@MediumTest
@MinAndroidSdkLevel(Build.VERSION_CODES.LOLLIPOP)
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public void testEditTextFieldAriaInvalidSpellingErrorMessage() throws Throwable {
final String data = "<input type='text' aria-invalid='spelling'><br>\n";
mActivityTestRule.launchContentShellWithUrl(UrlUtils.encodeHtmlDataUri(data));
mActivityTestRule.waitForActiveShellToBeDoneLoading();
AccessibilityNodeProvider provider = enableAccessibilityAndWaitForNodeProvider();
int textNodeVirtualViewId = waitForNodeWithClassName(provider, "android.widget.EditText");
AccessibilityNodeInfo textNode =
provider.createAccessibilityNodeInfo(textNodeVirtualViewId);
Assert.assertNotEquals(textNode, null);
Assert.assertEquals(textNode.isContentInvalid(), true);
Assert.assertEquals(textNode.getError(), "Invalid spelling");
}
/**
* Tests presence of ContentInvalid attribute and correctness of
* error message given aria-invalid = grammar
**/
@Test
@MediumTest
@MinAndroidSdkLevel(Build.VERSION_CODES.LOLLIPOP)
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public void testEditTextFieldAriaInvalidGrammarErrorMessage() throws Throwable {
final String data = "<input type='text' aria-invalid='grammar'><br>\n";
mActivityTestRule.launchContentShellWithUrl(UrlUtils.encodeHtmlDataUri(data));
mActivityTestRule.waitForActiveShellToBeDoneLoading();
AccessibilityNodeProvider provider = enableAccessibilityAndWaitForNodeProvider();
int textNodeVirtualViewId = waitForNodeWithClassName(provider, "android.widget.EditText");
AccessibilityNodeInfo textNode =
provider.createAccessibilityNodeInfo(textNodeVirtualViewId);
Assert.assertNotEquals(textNode, null);
Assert.assertEquals(textNode.isContentInvalid(), true);
Assert.assertEquals(textNode.getError(), "Invalid grammar");
}
/**
* Tests ContentInvalid is false and empty error message for well-formed input
**/
@Test
@MediumTest
@MinAndroidSdkLevel(Build.VERSION_CODES.LOLLIPOP)
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public void testEditTextFieldValidNoErrorMessage() throws Throwable {
final String data = "<input type='text'><br>\n";
mActivityTestRule.launchContentShellWithUrl(UrlUtils.encodeHtmlDataUri(data));
mActivityTestRule.waitForActiveShellToBeDoneLoading();
AccessibilityNodeProvider provider = enableAccessibilityAndWaitForNodeProvider();
int textNodeVirtualViewId = waitForNodeWithClassName(provider, "android.widget.EditText");
AccessibilityNodeInfo textNode =
provider.createAccessibilityNodeInfo(textNodeVirtualViewId);
Assert.assertNotEquals(textNode, null);
Assert.assertEquals(textNode.isContentInvalid(), false);
Assert.assertEquals(textNode.getError(), "");
}
}
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