Bug 699017 - aria-required attribute on file input not read by JAWS, r=tbsaunde, marcoz, roc

This commit is contained in:
Alexander Surkov 2011-11-30 20:36:20 +08:00
parent 8b5b5eab37
commit d58d4c93bf
13 changed files with 225 additions and 20 deletions

View File

@ -347,6 +347,20 @@ struct nsARIAMap
*/
static nsAttributeCharacteristics gWAIUnivAttrMap[];
static PRUint32 gWAIUnivAttrMapLength;
/**
* Return accessible state from ARIA universal states applied to the given
* element.
*/
static PRUint64 UniversalStatesFor(nsIContent* aContent)
{
PRUint64 state = 0;
PRUint32 index = 0;
while (nsStateMapEntry::MapToStates(aContent, &state, gWAIUnivStateMap[index]))
index++;
return state;
}
};
#endif

View File

@ -286,6 +286,16 @@ nsAccessibilityService::CreateHTMLCanvasAccessible(nsIContent* aContent,
return accessible;
}
already_AddRefed<nsAccessible>
nsAccessibilityService::CreateHTMLFileInputAccessible(nsIContent* aContent,
nsIPresShell* aPresShell)
{
nsCOMPtr<nsIWeakReference> weakShell(do_GetWeakReference(aPresShell));
nsAccessible* accessible = new nsHTMLFileInputAccessible(aContent, weakShell);
NS_IF_ADDREF(accessible);
return accessible;
}
already_AddRefed<nsAccessible>
nsAccessibilityService::CreateHTMLImageAccessible(nsIContent* aContent,
nsIPresShell* aPresShell)

View File

@ -91,6 +91,8 @@ public:
CreateHTMLCheckboxAccessible(nsIContent* aContent, nsIPresShell* aPresShell);
virtual already_AddRefed<nsAccessible>
CreateHTMLComboboxAccessible(nsIContent* aContent, nsIPresShell* aPresShell);
already_AddRefed<nsAccessible>
CreateHTMLFileInputAccessible(nsIContent* aContent, nsIPresShell* aPresShell);
virtual already_AddRefed<nsAccessible>
CreateHTMLGroupboxAccessible(nsIContent* aContent, nsIPresShell* aPresShell);
virtual already_AddRefed<nsAccessible>

View File

@ -1601,11 +1601,7 @@ void
nsAccessible::ApplyARIAState(PRUint64* aState)
{
// Test for universal states first
PRUint32 index = 0;
while (nsStateMapEntry::MapToStates(mContent, aState,
nsARIAMap::gWAIUnivStateMap[index])) {
++ index;
}
*aState |= nsARIAMap::UniversalStatesFor(mContent);
if (mRoleMapEntry) {

View File

@ -424,6 +424,8 @@ public:
inline bool IsHyperText() const { return mFlags & eHyperTextAccessible; }
nsHyperTextAccessible* AsHyperText();
inline bool IsHTMLFileInput() const { return mFlags & eHTMLFileInputAccessible; }
inline bool IsHTMLListItem() const { return mFlags & eHTMLListItemAccessible; }
nsHTMLLIAccessible* AsHTMLListItem();
@ -645,12 +647,13 @@ protected:
eComboboxAccessible = 1 << 5,
eDocAccessible = 1 << 6,
eHyperTextAccessible = 1 << 7,
eHTMLListItemAccessible = 1 << 8,
eListControlAccessible = 1 << 9,
eMenuButtonAccessible = 1 << 10,
eMenuPopupAccessible = 1 << 11,
eRootAccessible = 1 << 12,
eTextLeafAccessible = 1 << 13
eHTMLFileInputAccessible = 1 << 8,
eHTMLListItemAccessible = 1 << 9,
eListControlAccessible = 1 << 10,
eMenuButtonAccessible = 1 << 11,
eMenuPopupAccessible = 1 << 12,
eRootAccessible = 1 << 13,
eTextLeafAccessible = 1 << 14
};
//////////////////////////////////////////////////////////////////////////////

View File

@ -272,6 +272,25 @@ nsHTMLButtonAccessible::DoAction(PRUint8 aIndex)
return NS_OK;
}
PRUint64
nsHTMLButtonAccessible::State()
{
PRUint64 state = nsHyperTextAccessibleWrap::State();
if (state == states::DEFUNCT)
return state;
// Inherit states from input@type="file" suitable for the button. Note,
// no special processing for unavailable state since inheritance is supplied
// other code paths.
if (mParent && mParent->IsHTMLFileInput()) {
PRUint64 parentState = mParent->State();
state |= parentState & (states::BUSY | states::REQUIRED |
states::HASPOPUP | states::INVALID);
}
return state;
}
PRUint64
nsHTMLButtonAccessible::NativeState()
{
@ -477,7 +496,25 @@ nsHTMLTextFieldAccessible::ApplyARIAState(PRUint64* aState)
nsHyperTextAccessibleWrap::ApplyARIAState(aState);
nsStateMapEntry::MapToStates(mContent, aState, eARIAAutoComplete);
}
PRUint64
nsHTMLTextFieldAccessible::State()
{
PRUint64 state = nsHyperTextAccessibleWrap::State();
if (state & states::DEFUNCT)
return state;
// Inherit states from input@type="file" suitable for the button. Note,
// no special processing for unavailable state since inheritance is supplied
// by other code paths.
if (mParent && mParent->IsHTMLFileInput()) {
PRUint64 parentState = mParent->State();
state |= parentState & (states::BUSY | states::REQUIRED |
states::HASPOPUP | states::INVALID);
}
return state;
}
PRUint64
@ -612,6 +649,61 @@ nsHTMLTextFieldAccessible::ContainerWidget() const
}
////////////////////////////////////////////////////////////////////////////////
// nsHTMLGroupboxAccessible
////////////////////////////////////////////////////////////////////////////////
nsHTMLFileInputAccessible::
nsHTMLFileInputAccessible(nsIContent* aContent, nsIWeakReference* aShell) :
nsHyperTextAccessibleWrap(aContent, aShell)
{
mFlags |= eHTMLFileInputAccessible;
}
PRUint32
nsHTMLFileInputAccessible::NativeRole()
{
// JAWS wants a text container, others don't mind. No specific role in
// AT APIs.
return nsIAccessibleRole::ROLE_TEXT_CONTAINER;
}
nsresult
nsHTMLFileInputAccessible::HandleAccEvent(AccEvent* aEvent)
{
nsresult rv = nsHyperTextAccessibleWrap::HandleAccEvent(aEvent);
NS_ENSURE_SUCCESS(rv, rv);
// Redirect state change events for inherited states to child controls. Note,
// unavailable state is not redirected. That's a standard for unavailable
// state handling.
AccStateChangeEvent* event = downcast_accEvent(aEvent);
if (event &&
(event->GetState() == states::BUSY ||
event->GetState() == states::REQUIRED ||
event->GetState() == states::HASPOPUP ||
event->GetState() == states::INVALID)) {
nsAccessible* input = GetChildAt(0);
if (input && input->Role() == nsIAccessibleRole::ROLE_ENTRY) {
nsRefPtr<AccStateChangeEvent> childEvent =
new AccStateChangeEvent(input, event->GetState(),
event->IsStateEnabled(),
(event->IsFromUserInput() ? eFromUserInput : eNoUserInput));
nsEventShell::FireEvent(childEvent);
}
nsAccessible* button = GetChildAt(1);
if (button && button->Role() == nsIAccessibleRole::ROLE_PUSHBUTTON) {
nsRefPtr<AccStateChangeEvent> childEvent =
new AccStateChangeEvent(button, event->GetState(),
event->IsStateEnabled(),
(event->IsFromUserInput() ? eFromUserInput : eNoUserInput));
nsEventShell::FireEvent(childEvent);
}
}
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////////
// nsHTMLGroupboxAccessible
////////////////////////////////////////////////////////////////////////////////

View File

@ -109,6 +109,7 @@ public:
// nsAccessible
virtual nsresult GetNameInternal(nsAString& aName);
virtual PRUint32 NativeRole();
virtual PRUint64 State();
virtual PRUint64 NativeState();
// ActionAccessible
@ -171,6 +172,7 @@ public:
virtual void ApplyARIAState(PRUint64* aState);
virtual nsresult GetNameInternal(nsAString& aName);
virtual PRUint32 NativeRole();
virtual PRUint64 State();
virtual PRUint64 NativeState();
// ActionAccessible
@ -182,6 +184,19 @@ public:
};
/**
* Accessible for input@type="file" element.
*/
class nsHTMLFileInputAccessible : public nsHyperTextAccessibleWrap
{
public:
nsHTMLFileInputAccessible(nsIContent* aContent, nsIWeakReference* aShell);
// nsAccessible
virtual PRUint32 NativeRole();
virtual nsresult HandleAccEvent(AccEvent* aAccEvent);
};
/**
* Accessible for HTML fieldset element.
*/

View File

@ -160,12 +160,8 @@ nsHyperTextAccessible::NativeRole()
// Treat block frames as paragraphs
nsIFrame *frame = GetFrame();
if (frame && frame->GetType() == nsGkAtoms::blockFrame &&
frame->GetContent()->Tag() != nsGkAtoms::input) {
// An html:input @type="file" is the only input that is exposed as a
// blockframe. It must be exposed as ROLE_TEXT_CONTAINER for JAWS.
if (frame && frame->GetType() == nsGkAtoms::blockFrame)
return nsIAccessibleRole::ROLE_PARAGRAPH;
}
return nsIAccessibleRole::ROLE_TEXT_CONTAINER; // In ATK this works
}

View File

@ -1355,9 +1355,10 @@ function stateChangeChecker(aState, aIsExtraState, aIsEnabled,
if (!event)
return;
is(event.state, aState, "Wrong state of the statechange event.");
is(event.isExtraState(), aIsExtraState,
"Wrong extra state bit of the statechange event.");
isState(event.state, aState, aIsExtraState,
"Wrong state of the statechange event.");
is(event.isEnabled(), aIsEnabled,
"Wrong state of statechange event state");

View File

@ -70,12 +70,38 @@
};
}
function stateChangeOnFileInput(aID, aAttr, aValue,
aState, aIsExtraState, aIsEnabled)
{
this.fileControlNode = getNode(aID);
this.fileControl = getAccessible(this.fileControlNode);
this.textEntry = this.fileControl.firstChild;
this.browseButton = this.fileControl.lastChild;
this.invoke = function stateChangeOnFileInput_invoke()
{
this.fileControlNode.setAttribute(aAttr, aValue);
}
this.eventSeq = [
new stateChangeChecker(aState, aIsExtraState, aIsEnabled, this.fileControl),
new stateChangeChecker(aState, aIsExtraState, aIsEnabled, this.textEntry),
new stateChangeChecker(aState, aIsExtraState, aIsEnabled, this.browseButton)
];
this.getID = function stateChangeOnFileInput_getID()
{
return "inherited state change on file input on attribute '" + aAttr + "' change";
}
}
////////////////////////////////////////////////////////////////////////////
// Do tests
var gQueue = null;
// var gA11yEventDumpID = "eventdump"; // debug stuff
//gA11yEventDumpToConsole = true; // debug stuff
function doTests()
{
@ -88,6 +114,14 @@
// invalid state change
gQueue.push(new invalidInput("email"));
// file input inherited state changes
gQueue.push(new stateChangeOnFileInput("file", "aria-busy", "true",
STATE_BUSY, false, true));
gQueue.push(new stateChangeOnFileInput("file", "aria-required", "true",
STATE_REQUIRED, false, true));
gQueue.push(new stateChangeOnFileInput("file", "aria-invalid", "true",
STATE_INVALID, false, true));
gQueue.invoke(); // Will call SimpleTest.finish();
}
@ -108,6 +142,11 @@
title="Fire a11y event based on HTML5 constraint validation">
Mozilla Bug 555728
</a>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=699017"
title="File input control should be propogate states to descendants">
Mozilla Bug 699017
</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
@ -120,6 +159,8 @@
<input id="email" type='email'>
<input id="file" type="file">
<div id="eventdump"></div>
</body>
</html>

View File

@ -97,6 +97,14 @@
// disabled, too. See bug 429285.
testAriaDisabledTree("group");
// universal ARIA properties inherited from file input control
var fileTextField = getAccessible("fileinput").firstChild;
testStates(fileTextField,
STATE_BUSY | STATE_UNAVAILABLE | STATE_REQUIRED | STATE_HASPOPUP | STATE_INVALID);
var fileBrowseButton = getAccessible("fileinput").lastChild;
testStates(fileBrowseButton,
STATE_BUSY | STATE_UNAVAILABLE | STATE_REQUIRED | STATE_HASPOPUP | STATE_INVALID);
// offscreen test
testStates("aria_offscreen_textbox", STATE_OFFSCREEN);
@ -171,6 +179,11 @@
title="aria-orientation should be applied to separator and slider roles">
Mozilla Bug 681674
</a>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=699017"
title="File input control should be propogate states to descendants">
Mozilla Bug 699017
</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
@ -215,7 +228,15 @@
</div>
<div role="slider" tabindex="0">A slider</div>
</div>
<!-- universal ARIA properties should be inherited by text field of file input -->
<input type="file" id="fileinput"
aria-busy="true"
aria-disabled="true"
aria-required="true"
aria-haspopup="true"
aria-invalid="true">
<div id="offscreen_log" role="log" class="offscreen">
<div id="aria_offscreen_textbox" role="textbox" aria-readonly="true">This text should be offscreen</div>
</div>

View File

@ -36,6 +36,12 @@
testStates("f_input", STATE_UNAVAILABLE);
testStates("f_input_disabled", STATE_UNAVAILABLE);
// inherited from file control
var fileTextField = getAccessible("file").firstChild;
testStates(fileTextField, STATE_UNAVAILABLE | STATE_REQUIRED);
var fileBrowseButton = getAccessible("file").lastChild;
testStates(fileBrowseButton, STATE_UNAVAILABLE | STATE_REQUIRED);
/**
* maxlength doesn't make the element invalid until bug 613016 and bug 613019
* are fixed. Commenting out related lines and adding a todo to make sure
@ -110,6 +116,11 @@
title="Add accessibility support for @list on HTML input and for HTML datalist">
Mozilla Bug 559766
</a>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=699017"
title="File input control should be propogate states to descendants">
Mozilla Bug 699017
</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
@ -146,6 +157,9 @@
<input id="f_input_disabled" disabled>
</fieldset>
<!-- inherited from input@type="file" -->
<input id="file" type="file" required disabled>
<!-- invalid/valid -->
<input id="maxlength" maxlength="1">
<input id="maxlength2" maxlength="100" value="foo">

View File

@ -747,8 +747,8 @@ nsFileControlFrame::CreateAccessible()
if (!accService)
return nsnull;
return accService->CreateHyperTextAccessible(mContent,
PresContext()->PresShell());
return accService->CreateHTMLFileInputAccessible(mContent,
PresContext()->PresShell());
}
#endif