Bug 559764 - make input@type=range accessible, r=tbsaunde, roc, smaug

This commit is contained in:
Alexander Surkov 2013-04-28 09:54:54 +09:00
parent 1a836e54f6
commit c58428f754
12 changed files with 290 additions and 24 deletions

View File

@ -34,6 +34,7 @@ enum AccType {
eHTMLSelectListType, eHTMLSelectListType,
eHTMLMediaType, eHTMLMediaType,
eHTMLRadioButtonType, eHTMLRadioButtonType,
eHTMLRangeType,
eHTMLTableType, eHTMLTableType,
eHTMLTableCellType, eHTMLTableCellType,
eHTMLTableRowType, eHTMLTableRowType,

View File

@ -425,6 +425,20 @@ nsAccessibilityService::TreeViewChanged(nsIPresShell* aPresShell,
} }
} }
void
nsAccessibilityService::RangeValueChanged(nsIPresShell* aPresShell,
nsIContent* aContent)
{
DocAccessible* document = GetDocAccessible(aPresShell);
if (document) {
Accessible* accessible = document->GetAccessible(aContent);
if (accessible) {
document->FireDelayedEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE,
accessible);
}
}
}
void void
nsAccessibilityService::UpdateListBullet(nsIPresShell* aPresShell, nsAccessibilityService::UpdateListBullet(nsIPresShell* aPresShell,
nsIContent* aHTMLListItemContent, nsIContent* aHTMLListItemContent,
@ -1509,6 +1523,9 @@ nsAccessibilityService::CreateAccessibleByFrameType(nsIFrame* aFrame,
case eHTMLRadioButtonType: case eHTMLRadioButtonType:
newAcc = new HTMLRadioButtonAccessible(aContent, document); newAcc = new HTMLRadioButtonAccessible(aContent, document);
break; break;
case eHTMLRangeType:
newAcc = new HTMLRangeAccessible(aContent, document);
break;
case eHTMLTableType: case eHTMLTableType:
newAcc = new HTMLTableAccessibleWrap(aContent, document); newAcc = new HTMLTableAccessibleWrap(aContent, document);
break; break;

View File

@ -99,6 +99,11 @@ public:
void TreeViewChanged(nsIPresShell* aPresShell, nsIContent* aContent, void TreeViewChanged(nsIPresShell* aPresShell, nsIContent* aContent,
nsITreeView* aView); nsITreeView* aView);
/**
* Notify of input@type="element" value change.
*/
void RangeValueChanged(nsIPresShell* aPresShell, nsIContent* aContent);
/** /**
* Update list bullet accessible. * Update list bullet accessible.
*/ */

View File

@ -543,6 +543,91 @@ HTMLFileInputAccessible::HandleAccEvent(AccEvent* aEvent)
return NS_OK; return NS_OK;
} }
////////////////////////////////////////////////////////////////////////////////
// HTMLRangeAccessible
////////////////////////////////////////////////////////////////////////////////
NS_IMPL_ISUPPORTS_INHERITED1(HTMLRangeAccessible, LeafAccessible,
nsIAccessibleValue)
role
HTMLRangeAccessible::NativeRole()
{
return roles::SLIDER;
}
bool
HTMLRangeAccessible::IsWidget() const
{
return true;
}
void
HTMLRangeAccessible::Value(nsString& aValue)
{
LeafAccessible::Value(aValue);
if (!aValue.IsEmpty())
return;
HTMLInputElement::FromContent(mContent)->GetValue(aValue);
}
NS_IMETHODIMP
HTMLRangeAccessible::GetMaximumValue(double* aMaximumValue)
{
nsresult rv = LeafAccessible::GetMaximumValue(aMaximumValue);
if (rv != NS_OK_NO_ARIA_VALUE)
return rv;
*aMaximumValue = HTMLInputElement::FromContent(mContent)->GetMaximum();
return NS_OK;
}
NS_IMETHODIMP
HTMLRangeAccessible::GetMinimumValue(double* aMinimumValue)
{
nsresult rv = LeafAccessible::GetMinimumValue(aMinimumValue);
if (rv != NS_OK_NO_ARIA_VALUE)
return rv;
*aMinimumValue = HTMLInputElement::FromContent(mContent)->GetMinimum();
return NS_OK;
}
NS_IMETHODIMP
HTMLRangeAccessible::GetMinimumIncrement(double* aMinimumIncrement)
{
nsresult rv = LeafAccessible::GetMinimumIncrement(aMinimumIncrement);
if (rv != NS_OK_NO_ARIA_VALUE)
return rv;
*aMinimumIncrement = HTMLInputElement::FromContent(mContent)->GetStep();
return NS_OK;
}
NS_IMETHODIMP
HTMLRangeAccessible::GetCurrentValue(double* aCurrentValue)
{
nsresult rv = LeafAccessible::GetCurrentValue(aCurrentValue);
if (rv != NS_OK_NO_ARIA_VALUE)
return rv;
*aCurrentValue = HTMLInputElement::FromContent(mContent)->GetValueAsDouble();
return NS_OK;
}
NS_IMETHODIMP
HTMLRangeAccessible::SetCurrentValue(double aValue)
{
ErrorResult er;
HTMLInputElement::FromContent(mContent)->SetValueAsNumber(aValue, er);
return er.ErrorCode();
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// HTMLGroupboxAccessible // HTMLGroupboxAccessible
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View File

@ -145,6 +145,31 @@ public:
virtual nsresult HandleAccEvent(AccEvent* aAccEvent); virtual nsresult HandleAccEvent(AccEvent* aAccEvent);
}; };
/**
* Used for input@type="range" element.
*/
class HTMLRangeAccessible : public LeafAccessible
{
public:
HTMLRangeAccessible(nsIContent* aContent, DocAccessible* aDoc) :
LeafAccessible(aContent, aDoc)
{
mStateFlags |= eHasNumericValue;
}
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIACCESSIBLEVALUE
// Accessible
virtual void Value(nsString& aValue);
virtual mozilla::a11y::role NativeRole();
// Widgets
virtual bool IsWidget() const;
};
/** /**
* Accessible for HTML fieldset element. * Accessible for HTML fieldset element.
*/ */

View File

@ -8,6 +8,8 @@
<script type="application/javascript" <script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
<script type="application/javascript" <script type="application/javascript"
src="../common.js"></script> src="../common.js"></script>
@ -79,21 +81,46 @@
} }
} }
function changeProcessValue(aID, aValue) { function changeProgressValue(aID, aValue)
this.DOMNode = getNode(aID); {
this.DOMNode = getNode(aID);
this.invoke = function changeProcessValue_invoke() { this.invoke = function changeProgressValue_invoke()
this.DOMNode.value = aValue; {
} this.DOMNode.value = aValue;
}
this.check = function changeProcessValue_check() { this.check = function changeProgressValue_check()
var acc = getAccessible(this.DOMNode); {
is(acc.value, aValue+"%", "Wrong value for " + prettyName(aID)); var acc = getAccessible(this.DOMNode);
} is(acc.value, aValue+"%", "Wrong value for " + prettyName(aID));
}
this.getID = function changeProcessValue_getID() { this.getID = function changeProgressValue_getID()
return prettyName(aID) + " value changed"; {
} return prettyName(aID) + " value changed";
}
}
function changeRangeValue(aID)
{
this.DOMNode = getNode(aID);
this.invoke = function changeRangeValue_invoke()
{
synthesizeMouse(getNode(aID), 5, 5, { });
}
this.finalCheck = function changeRangeValue_finalCheck()
{
var acc = getAccessible(this.DOMNode);
is(acc.value, "0", "Wrong value for " + prettyName(aID));
}
this.getID = function changeRangeValue_getID()
{
return prettyName(aID) + " range value changed";
}
} }
function doTests() function doTests()
@ -104,6 +131,7 @@
testValue("slider_vt", "hi", 0, 0, 3, 0); testValue("slider_vt", "hi", 0, 0, 3, 0);
testValue("scrollbar", "5", 5, 0, 1000, 0); testValue("scrollbar", "5", 5, 0, 1000, 0);
testValue("progress", "22%", 22, 0, 100, 0); testValue("progress", "22%", 22, 0, 100, 0);
testValue("range", "6", 6, 0, 10, 1);
// Test value change events // Test value change events
gQueue = new eventQueue(nsIAccessibleEvent.EVENT_VALUE_CHANGE); gQueue = new eventQueue(nsIAccessibleEvent.EVENT_VALUE_CHANGE);
@ -115,7 +143,8 @@
gQueue.push(new changeValue("combobox", "hello")); gQueue.push(new changeValue("combobox", "hello"));
gQueue.push(new changeProcessValue("progress", "50")); gQueue.push(new changeProgressValue("progress", "50"));
gQueue.push(new changeRangeValue("range"));
gQueue.invoke(); // Will call SimpleTest.finish(); gQueue.invoke(); // Will call SimpleTest.finish();
} }
@ -137,6 +166,11 @@
title="We dont expose new aria role 'scrollbar' and property aria-orientation"> title="We dont expose new aria role 'scrollbar' and property aria-orientation">
Mozilla Bug 529289 Mozilla Bug 529289
</a> </a>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=559764"
title="Make HTML5 input@type=range element accessible">
Mozilla Bug 559764
</a>
<a target="_blank" <a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=703202" href="https://bugzilla.mozilla.org/show_bug.cgi?id=703202"
title="ARIA comboboxes don't fire value change events"> title="ARIA comboboxes don't fire value change events">
@ -175,5 +209,8 @@
<!-- progress bar --> <!-- progress bar -->
<progress id="progress" value="22" max="100"></progress> <progress id="progress" value="22" max="100"></progress>
<!-- input@type="range" -->
<input type="range" id="range" min="0" max="10" value="6">
</body> </body>
</html> </html>

View File

@ -59,6 +59,11 @@
}; };
testAccessibleTree("image_submit", accTree); testAccessibleTree("image_submit", accTree);
// input@type="range"
accTree = { SLIDER: [ ] };
testAccessibleTree("range", accTree);
// output
accTree = { accTree = {
role: ROLE_SECTION, role: ROLE_SECTION,
children: [ children: [
@ -81,17 +86,22 @@
<a target="_blank" <a target="_blank"
title="Fix O(n^2) access to all the children of a container" title="Fix O(n^2) access to all the children of a container"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=342045"> href="https://bugzilla.mozilla.org/show_bug.cgi?id=342045">
Mozilla Bug 342045 Bug 342045
</a> </a>
<a target="_blank" <a target="_blank"
title="add test for role of input type='image'" title="add test for role of input type='image'"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=524521"> href="https://bugzilla.mozilla.org/show_bug.cgi?id=524521">
Mozilla Bug 524521 Bug 524521
</a> </a>
<a target="_blank" <a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=558036" href="https://bugzilla.mozilla.org/show_bug.cgi?id=558036"
title="make HTML <output> accessible"> title="make HTML <output> accessible">
Mozilla Bug 558036 Bug 558036
</a>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=559764"
title="make HTML5 input@type=range element accessible">
Bug 559764
</a> </a>
<p id="display"></p> <p id="display"></p>
<div id="content" style="display: none"></div> <div id="content" style="display: none"></div>
@ -105,6 +115,8 @@
<input type="submit" id="submit"> <input type="submit" id="submit">
<input type="image" id="image_submit"> <input type="image" id="image_submit">
<input type="range" id="range">
<output id="output">1337</output> <output id="output">1337</output>
</body> </body>
</html> </html>

View File

@ -15,6 +15,7 @@ MOCHITEST_A11Y_FILES =\
test_general.html \ test_general.html \
test_progress.html \ test_progress.html \
test_progress.xul \ test_progress.xul \
test_range.html \
$(NULL) $(NULL)
include $(topsrcdir)/config/rules.mk include $(topsrcdir)/config/rules.mk

View File

@ -0,0 +1,59 @@
<html>
<head>
<title>nsIAccessible value testing for input@type=range element</title>
<link rel="stylesheet" type="text/css"
href="chrome://mochikit/content/tests/SimpleTest/test.css" />
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript"
src="../common.js"></script>
<script type="application/javascript"
src="../value.js"></script>
<script type="application/javascript"
src="chrome://mochikit/content/chrome-harness.js"></script>
<script type="application/javascript">
function doTest()
{
// HTML5 progress element tests
testValue("range", "50", 50, 0, 100, 1);
testValue("range_value", "1", 1, 0, 100, 1);
testValue("range_step", "50", 50, 0, 100, 1);
testValue("range_min42", "71", 71, 42, 100, 1);
testValue("range_max42", "21", 21, 0, 42, 1);
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
addA11yLoadEvent(doTest);
</script>
</head>
<body>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=559764"
title="make HTML5 input@type=range element accessible">
Bug 559764
</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
</pre>
<!-- HTML5 input@type=range element -->
<input type="range" id="range">
<input type="range" id="range_value" value="1">
<input type="range" id="range_step" step="1">
<input type="range" id="range_min42" min="42">
<input type="range" id="range_max42" max="42">
</body>
</html>

View File

@ -602,6 +602,14 @@ public:
aRv = ApplyStep(-aN); aRv = ApplyStep(-aN);
} }
/**
* Returns the current step value.
* Returns kStepAny if the current step is "any" string.
*
* @return the current step value.
*/
double GetStep() const;
void GetValidationMessage(nsAString& aValidationMessage, ErrorResult& aRv); void GetValidationMessage(nsAString& aValidationMessage, ErrorResult& aRv);
// XPCOM GetCustomVisibility() is OK // XPCOM GetCustomVisibility() is OK
@ -1027,14 +1035,6 @@ protected:
*/ */
double GetStepScaleFactor() const; double GetStepScaleFactor() const;
/**
* Returns the current step value.
* Returns kStepAny if the current step is "any" string.
*
* @return the current step value.
*/
double GetStep() const;
/** /**
* Return the base used to compute if a value matches step. * Return the base used to compute if a value matches step.
* Basically, it's the min attribute if present and a default value otherwise. * Basically, it's the min attribute if present and a default value otherwise.

View File

@ -26,6 +26,10 @@
#include <algorithm> #include <algorithm>
#ifdef ACCESSIBILITY
#include "nsAccessibilityService.h"
#endif
#define LONG_SIDE_TO_SHORT_SIDE_RATIO 10 #define LONG_SIDE_TO_SHORT_SIDE_RATIO 10
using namespace mozilla; using namespace mozilla;
@ -349,6 +353,14 @@ nsRangeFrame::ReflowAnonymousContent(nsPresContext* aPresContext,
return NS_OK; return NS_OK;
} }
#ifdef ACCESSIBILITY
a11y::AccType
nsRangeFrame::AccessibleType()
{
return a11y::eHTMLRangeType;
}
#endif
double double
nsRangeFrame::GetValueAsFractionOfRange() nsRangeFrame::GetValueAsFractionOfRange()
{ {
@ -489,6 +501,14 @@ nsRangeFrame::UpdateForValueChange()
// theming is applied, so we just repaint the entire range. // theming is applied, so we just repaint the entire range.
InvalidateFrame(); InvalidateFrame();
} }
#ifdef ACCESSIBILITY
nsAccessibilityService* accService = nsIPresShell::AccService();
if (accService) {
accService->RangeValueChanged(PresContext()->PresShell(), mContent);
}
#endif
SchedulePaint(); SchedulePaint();
} }

View File

@ -52,6 +52,10 @@ public:
virtual bool IsLeaf() const MOZ_OVERRIDE { return true; } virtual bool IsLeaf() const MOZ_OVERRIDE { return true; }
#ifdef ACCESSIBILITY
virtual mozilla::a11y::AccType AccessibleType() MOZ_OVERRIDE;
#endif
// nsIAnonymousContentCreator // nsIAnonymousContentCreator
virtual nsresult CreateAnonymousContent(nsTArray<ContentInfo>& aElements) MOZ_OVERRIDE; virtual nsresult CreateAnonymousContent(nsTArray<ContentInfo>& aElements) MOZ_OVERRIDE;
virtual void AppendAnonymousContentTo(nsBaseContentList& aElements, virtual void AppendAnonymousContentTo(nsBaseContentList& aElements,