Bug 706067 - Make takeFocus work on widget items, r=marcoz, tbsaunde

This commit is contained in:
Alexander Surkov 2011-12-08 20:20:15 +08:00
parent dcf8e4c27d
commit aef288f412
11 changed files with 218 additions and 27 deletions

View File

@ -1066,7 +1066,6 @@ NS_IMETHODIMP nsAccessible::TakeSelection()
return NS_ERROR_FAILURE;
}
/* void takeFocus (); */
NS_IMETHODIMP
nsAccessible::TakeFocus()
{
@ -1078,30 +1077,16 @@ nsAccessible::TakeFocus()
nsIContent* focusContent = mContent;
// If the current element can't take real DOM focus and if it has an ID and
// an ancestor with an aria-activedescendant attribute present, then set DOM
// focus to that ancestor and set aria-activedescendant on the ancestor to
// the ID of the desired element.
// If the accessible focus is managed by container widget then focus the
// widget and set the accessible as its current item.
if (!frame->IsFocusable()) {
nsAutoString id;
if (nsCoreUtils::GetID(mContent, id)) {
nsIContent* ancestorContent = mContent;
while ((ancestorContent = ancestorContent->GetParent()) &&
!ancestorContent->HasAttr(kNameSpaceID_None,
nsGkAtoms::aria_activedescendant));
if (ancestorContent) {
nsCOMPtr<nsIPresShell> presShell(do_QueryReferent(mWeakShell));
if (presShell) {
nsIFrame *frame = ancestorContent->GetPrimaryFrame();
if (frame && frame->IsFocusable()) {
focusContent = ancestorContent;
focusContent->SetAttr(kNameSpaceID_None,
nsGkAtoms::aria_activedescendant,
id, true);
}
}
nsAccessible* widget = ContainerWidget();
if (widget && widget->AreItemsOperable()) {
nsIContent* widgetElm = widget->GetContent();
nsIFrame* widgetFrame = widgetElm->GetPrimaryFrame();
if (widgetFrame && widgetFrame->IsFocusable()) {
focusContent = widgetElm;
widget->SetCurrentItem(this);
}
}
}
@ -2937,6 +2922,18 @@ nsAccessible::CurrentItem()
return nsnull;
}
void
nsAccessible::SetCurrentItem(nsAccessible* aItem)
{
nsIAtom* id = aItem->GetContent()->GetID();
if (id) {
nsAutoString idStr;
id->ToString(idStr);
mContent->SetAttr(kNameSpaceID_None,
nsGkAtoms::aria_activedescendant, idStr, true);
}
}
nsAccessible*
nsAccessible::ContainerWidget() const
{

View File

@ -588,6 +588,11 @@ public:
*/
virtual nsAccessible* CurrentItem();
/**
* Set the current item of the widget.
*/
virtual void SetCurrentItem(nsAccessible* aItem);
/**
* Return container widget this accessible belongs to.
*/

View File

@ -151,6 +151,14 @@ nsHTMLSelectListAccessible::CurrentItem()
return nsnull;
}
void
nsHTMLSelectListAccessible::SetCurrentItem(nsAccessible* aItem)
{
aItem->GetContent()->SetAttr(kNameSpaceID_None,
nsGkAtoms::selected, NS_LITERAL_STRING("true"),
true);
}
////////////////////////////////////////////////////////////////////////////////
// nsHTMLSelectListAccessible: nsAccessible protected
@ -686,6 +694,13 @@ nsHTMLComboboxAccessible::CurrentItem()
return AreItemsOperable() ? mListAccessible->CurrentItem() : nsnull;
}
void
nsHTMLComboboxAccessible::SetCurrentItem(nsAccessible* aItem)
{
if (AreItemsOperable())
mListAccessible->SetCurrentItem(aItem);
}
////////////////////////////////////////////////////////////////////////////////
// nsHTMLComboboxAccessible: protected

View File

@ -85,6 +85,7 @@ public:
virtual bool IsActiveWidget() const;
virtual bool AreItemsOperable() const;
virtual nsAccessible* CurrentItem();
virtual void SetCurrentItem(nsAccessible* aItem);
protected:
@ -209,6 +210,7 @@ public:
virtual bool IsActiveWidget() const;
virtual bool AreItemsOperable() const;
virtual nsAccessible* CurrentItem();
virtual void SetCurrentItem(nsAccessible* aItem);
protected:
// nsAccessible

View File

@ -302,6 +302,22 @@ nsXULSelectableAccessible::CurrentItem()
return nsnull;
}
void
nsXULSelectableAccessible::SetCurrentItem(nsAccessible* aItem)
{
if (!mSelectControl)
return;
nsCOMPtr<nsIDOMXULSelectControlItemElement> itemElm =
do_QueryInterface(aItem->GetContent());
nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelectControl =
do_QueryInterface(mSelectControl);
if (multiSelectControl)
multiSelectControl->SetCurrentItem(itemElm);
else
mSelectControl->SetSelectedItem(itemElm);
}
////////////////////////////////////////////////////////////////////////////////
// nsXULMenuitemAccessible
////////////////////////////////////////////////////////////////////////////////
@ -893,3 +909,9 @@ nsXULMenubarAccessible::CurrentItem()
}
return nsnull;
}
void
nsXULMenubarAccessible::SetCurrentItem(nsAccessible* aItem)
{
NS_ERROR("nsXULMenubarAccessible::SetCurrentItem not implemented");
}

View File

@ -67,6 +67,7 @@ public:
// Widgets
virtual nsAccessible* CurrentItem();
virtual void SetCurrentItem(nsAccessible* aItem);
protected:
// nsIDOMXULMultiSelectControlElement inherits from this, so we'll always have
@ -170,6 +171,7 @@ public:
virtual bool IsActiveWidget() const;
virtual bool AreItemsOperable() const;
virtual nsAccessible* CurrentItem();
virtual void SetCurrentItem(nsAccessible* aItem);
};
#endif

View File

@ -279,6 +279,12 @@ nsXULTreeAccessible::CurrentItem()
return nsnull;
}
void
nsXULTreeAccessible::SetCurrentItem(nsAccessible* aItem)
{
NS_ERROR("nsXULTreeAccessible::SetCurrentItem not implemented");
}
already_AddRefed<nsIArray>
nsXULTreeAccessible::SelectedItems()
{

View File

@ -106,6 +106,7 @@ public:
virtual bool IsActiveWidget() const;
virtual bool AreItemsOperable() const;
virtual nsAccessible* CurrentItem();
virtual void SetCurrentItem(nsAccessible* aItem);
virtual nsAccessible* ContainerWidget() const;

View File

@ -48,6 +48,7 @@ include $(topsrcdir)/config/rules.mk
_TEST_FILES =\
test_focusedChild.html \
test_takeFocus.html \
test_takeFocus.xul \
$(NULL)
libs:: $(_TEST_FILES)

View File

@ -18,9 +18,7 @@
<script type="application/javascript">
////////////////////////////////////////////////////////////////////////////
// Test
var gQueue = null;
// Invokers
function takeFocusInvoker(aID)
{
@ -39,6 +37,12 @@
}
}
////////////////////////////////////////////////////////////////////////////
// Test
//gA11yEventDumpToConsole = true; // debug stuff
var gQueue = null;
function doTest()
{
gQueue = new eventQueue();
@ -49,6 +53,7 @@
gQueue.push(new takeFocusInvoker("item2"));
gQueue.push(new takeFocusInvoker("plugin"));
gQueue.push(new takeFocusInvoker(document));
gQueue.push(new takeFocusInvoker("lb_item2"));
gQueue.invoke(); // Will call SimpleTest.finish();
}
@ -75,6 +80,11 @@
title="No focus event fired on document when focus is set to the document while focused in a plugin">
Mozilla Bug 646361
</a>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=706067"
title="Make takeFocus work on widget items">
Mozilla Bug 706067
</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
@ -92,5 +102,10 @@
</div>
<embed id="plugin" type="application/x-test" width="200" height="200" wmode="window"></embed>
<select id="listbox" size="5">
<option id="lb_item1">item1</option>
<option id="lb_item2">item2</option>
</select>
</body>
</html>

View File

@ -0,0 +1,125 @@
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
type="text/css"?>
<?xml-stylesheet href="chrome://browser/content/browser.css"
type="text/css"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
title="Accessible focus testing">
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
<script type="application/javascript"
src="../common.js" />
<script type="application/javascript"
src="../states.js" />
<script type="application/javascript"
src="../events.js" />
<script type="application/javascript"
src="../treeview.js" />
<script type="application/javascript">
<![CDATA[
////////////////////////////////////////////////////////////////////////////
// Invokers
function setTreeView(aTreeID, aView)
{
this.DOMNode = getNode(aTreeID);
this.eventSeq = [
new invokerChecker(EVENT_REORDER, this.DOMNode)
];
this.invoke = function setTreeView_invoke()
{
this.DOMNode.treeBoxObject.view = aView;
}
this.getID = function setTreeView_getID()
{ return "set tree view for " + prettyName(aTreeID); }
};
function takeFocusInvoker(aID, aArgConverterFunc)
{
this.targetFunc = aArgConverterFunc ? aArgConverterFunc : getAccessible;
this.eventSeq = [ new focusChecker(this.targetFunc, aID) ];
this.invoke = function takeFocusInvoker_invoke()
{
this.targetFunc.call(null, aID).takeFocus();
}
this.getID = function takeFocusInvoker_getID()
{
return "takeFocus for " + prettyName(aID);
}
}
function getLastChild(aID)
{
return getAccessible(aID).lastChild;
}
////////////////////////////////////////////////////////////////////////////
// Tests
//gA11yEventDumpID = "eventdump"; // debug stuff
//gA11yEventDumpToConsole = true; // debug stuff
var gQueue = null;
function doTests()
{
// Test focus events.
gQueue = new eventQueue();
gQueue.push(new setTreeView("tree", new nsTableTreeView(5)));
gQueue.push(new takeFocusInvoker("tree", getLastChild));
gQueue.push(new takeFocusInvoker("listitem2"));
gQueue.invoke(); // Will call SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
addA11yLoadEvent(doTests);
]]>
</script>
<hbox flex="1" style="overflow: auto;">
<body xmlns="http://www.w3.org/1999/xhtml">
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=706067"
title="Make takeFocus work on widget items">
Mozilla Bug 706067
</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
</pre>
</body>
<vbox flex="1">
<tree id="tree" flex="1">
<treecols>
<treecol id="col1" flex="1" primary="true" label="column"/>
<treecol id="col2" flex="1" label="column 2"/>
</treecols>
<treechildren id="treechildren"/>
</tree>
<listbox id="listbox">
<listitem id="listitem1">item1</listitem>
<listitem id="listitem2">item2</listitem>
</listbox>
<vbox id="eventdump"/>
</vbox>
</hbox>
</window>