mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 467057 - xul menulist doesn't fire expand/collapse state change events, r=marcoz, aaronlev
This commit is contained in:
parent
f27dd1d882
commit
ec5bc8b940
@ -649,24 +649,13 @@ nsresult nsRootAccessible::HandleEventWithTarget(nsIDOMEvent* aEvent,
|
|||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (eventType.EqualsLiteral("popuphiding")) {
|
|
||||||
// If accessible focus was on or inside popup that closes,
|
|
||||||
// then restore it to true current focus.
|
|
||||||
// This is the case when we've been getting DOMMenuItemActive events
|
|
||||||
// inside of a combo box that closes. The real focus is on the combo box.
|
|
||||||
// It's also the case when a popup gets focus in ATK -- when it closes
|
|
||||||
// we need to fire an event to restore focus to where it was
|
|
||||||
if (!gLastFocusedNode ||
|
|
||||||
!nsCoreUtils::IsAncestorOf(aTargetNode, gLastFocusedNode)) {
|
|
||||||
return NS_OK; // And was not focused on an item inside the popup
|
|
||||||
}
|
|
||||||
// Focus was on or inside of a popup that's being hidden
|
|
||||||
FireCurrentFocusEvent();
|
|
||||||
}
|
|
||||||
|
|
||||||
nsCOMPtr<nsIAccessible> accessible;
|
nsCOMPtr<nsIAccessible> accessible;
|
||||||
accService->GetAccessibleInShell(aTargetNode, eventShell,
|
accService->GetAccessibleInShell(aTargetNode, eventShell,
|
||||||
getter_AddRefs(accessible));
|
getter_AddRefs(accessible));
|
||||||
|
|
||||||
|
if (eventType.EqualsLiteral("popuphiding"))
|
||||||
|
return HandlePopupHidingEvent(aTargetNode, accessible);
|
||||||
|
|
||||||
nsCOMPtr<nsPIAccessible> privAcc(do_QueryInterface(accessible));
|
nsCOMPtr<nsPIAccessible> privAcc(do_QueryInterface(accessible));
|
||||||
if (!privAcc)
|
if (!privAcc)
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
@ -831,22 +820,7 @@ nsresult nsRootAccessible::HandleEventWithTarget(nsIDOMEvent* aEvent,
|
|||||||
nsAccUtils::FireAccEvent(nsIAccessibleEvent::EVENT_ALERT, accessible);
|
nsAccUtils::FireAccEvent(nsIAccessibleEvent::EVENT_ALERT, accessible);
|
||||||
}
|
}
|
||||||
else if (eventType.EqualsLiteral("popupshown")) {
|
else if (eventType.EqualsLiteral("popupshown")) {
|
||||||
// Don't fire menupopup events for combobox and autocomplete lists
|
HandlePopupShownEvent(accessible);
|
||||||
PRUint32 role = nsAccUtils::Role(accessible);
|
|
||||||
PRInt32 event = 0;
|
|
||||||
if (role == nsIAccessibleRole::ROLE_MENUPOPUP) {
|
|
||||||
event = nsIAccessibleEvent::EVENT_MENUPOPUP_START;
|
|
||||||
}
|
|
||||||
else if (role == nsIAccessibleRole::ROLE_TOOLTIP) {
|
|
||||||
// There is a single <xul:tooltip> node which Mozilla moves around.
|
|
||||||
// The accessible for it stays the same no matter where it moves.
|
|
||||||
// AT's expect to get an EVENT_SHOW for the tooltip.
|
|
||||||
// In event callback the tooltip's accessible will be ready.
|
|
||||||
event = nsIAccessibleEvent::EVENT_ASYNCH_SHOW;
|
|
||||||
}
|
|
||||||
if (event) {
|
|
||||||
nsAccUtils::FireAccEvent(event, accessible);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (eventType.EqualsLiteral("DOMMenuInactive")) {
|
else if (eventType.EqualsLiteral("DOMMenuInactive")) {
|
||||||
if (nsAccUtils::Role(accessible) == nsIAccessibleRole::ROLE_MENUPOPUP) {
|
if (nsAccUtils::Role(accessible) == nsIAccessibleRole::ROLE_MENUPOPUP) {
|
||||||
@ -1103,6 +1077,94 @@ NS_IMETHODIMP nsRootAccessible::FireDocLoadEvents(PRUint32 aEventType)
|
|||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
nsRootAccessible::HandlePopupShownEvent(nsIAccessible *aAccessible)
|
||||||
|
{
|
||||||
|
PRUint32 role = nsAccUtils::Role(aAccessible);
|
||||||
|
|
||||||
|
if (role == nsIAccessibleRole::ROLE_MENUPOPUP) {
|
||||||
|
// Don't fire menupopup events for combobox and autocomplete lists.
|
||||||
|
return nsAccUtils::FireAccEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_START,
|
||||||
|
aAccessible);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (role == nsIAccessibleRole::ROLE_TOOLTIP) {
|
||||||
|
// There is a single <xul:tooltip> node which Mozilla moves around.
|
||||||
|
// The accessible for it stays the same no matter where it moves.
|
||||||
|
// AT's expect to get an EVENT_SHOW for the tooltip.
|
||||||
|
// In event callback the tooltip's accessible will be ready.
|
||||||
|
return nsAccUtils::FireAccEvent(nsIAccessibleEvent::EVENT_ASYNCH_SHOW,
|
||||||
|
aAccessible);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (role == nsIAccessibleRole::ROLE_COMBOBOX_LIST) {
|
||||||
|
// Fire expanded state change event for comboboxes and autocompeletes.
|
||||||
|
nsCOMPtr<nsIAccessible> comboboxAcc;
|
||||||
|
nsresult rv = aAccessible->GetParent(getter_AddRefs(comboboxAcc));
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
PRUint32 comboboxRole = nsAccUtils::Role(comboboxAcc);
|
||||||
|
if (comboboxRole == nsIAccessibleRole::ROLE_COMBOBOX ||
|
||||||
|
comboboxRole == nsIAccessibleRole::ROLE_AUTOCOMPLETE) {
|
||||||
|
nsCOMPtr<nsIAccessibleStateChangeEvent> event =
|
||||||
|
new nsAccStateChangeEvent(comboboxAcc,
|
||||||
|
nsIAccessibleStates::STATE_EXPANDED,
|
||||||
|
PR_FALSE, PR_TRUE);
|
||||||
|
NS_ENSURE_TRUE(event, NS_ERROR_OUT_OF_MEMORY);
|
||||||
|
|
||||||
|
nsCOMPtr<nsPIAccessible> pComboboxAcc(do_QueryInterface(comboboxAcc));
|
||||||
|
|
||||||
|
return pComboboxAcc->FireAccessibleEvent(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
nsRootAccessible::HandlePopupHidingEvent(nsIDOMNode *aNode,
|
||||||
|
nsIAccessible *aAccessible)
|
||||||
|
{
|
||||||
|
// If accessible focus was on or inside popup that closes, then restore it
|
||||||
|
// to true current focus. This is the case when we've been getting
|
||||||
|
// DOMMenuItemActive events inside of a combo box that closes. The real focus
|
||||||
|
// is on the combo box. It's also the case when a popup gets focus in ATK --
|
||||||
|
// when it closes we need to fire an event to restore focus to where it was.
|
||||||
|
if (gLastFocusedNode &&
|
||||||
|
nsCoreUtils::IsAncestorOf(aNode, gLastFocusedNode)) {
|
||||||
|
// Focus was on or inside of a popup that's being hidden
|
||||||
|
FireCurrentFocusEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fire expanded state change event for comboboxes and autocompletes.
|
||||||
|
if (!aAccessible)
|
||||||
|
return NS_OK;
|
||||||
|
|
||||||
|
PRUint32 role = nsAccUtils::Role(aAccessible);
|
||||||
|
if (role != nsIAccessibleRole::ROLE_COMBOBOX_LIST)
|
||||||
|
return NS_OK;
|
||||||
|
|
||||||
|
nsCOMPtr<nsIAccessible> comboboxAcc;
|
||||||
|
nsresult rv = aAccessible->GetParent(getter_AddRefs(comboboxAcc));
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
PRUint32 comboboxRole = nsAccUtils::Role(comboboxAcc);
|
||||||
|
if (comboboxRole == nsIAccessibleRole::ROLE_COMBOBOX ||
|
||||||
|
comboboxRole == nsIAccessibleRole::ROLE_AUTOCOMPLETE) {
|
||||||
|
nsCOMPtr<nsIAccessibleStateChangeEvent> event =
|
||||||
|
new nsAccStateChangeEvent(comboboxAcc,
|
||||||
|
nsIAccessibleStates::STATE_EXPANDED,
|
||||||
|
PR_FALSE, PR_FALSE);
|
||||||
|
NS_ENSURE_TRUE(event, NS_ERROR_OUT_OF_MEMORY);
|
||||||
|
|
||||||
|
nsCOMPtr<nsPIAccessible> pComboboxAcc(do_QueryInterface(comboboxAcc));
|
||||||
|
|
||||||
|
return pComboboxAcc->FireAccessibleEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef MOZ_XUL
|
#ifdef MOZ_XUL
|
||||||
nsresult
|
nsresult
|
||||||
nsRootAccessible::HandleTreeRowCountChangedEvent(nsIDOMEvent *aEvent,
|
nsRootAccessible::HandleTreeRowCountChangedEvent(nsIDOMEvent *aEvent,
|
||||||
|
@ -133,15 +133,15 @@ class nsRootAccessible : public nsDocAccessibleWrap,
|
|||||||
void GetChromeEventHandler(nsIDOMEventTarget **aChromeTarget);
|
void GetChromeEventHandler(nsIDOMEventTarget **aChromeTarget);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles 'TreeRowCountChanged' event. Used in HandleEventWithTarget().
|
* Used in HandleEventWithTarget().
|
||||||
*/
|
*/
|
||||||
|
nsresult HandlePopupShownEvent(nsIAccessible *aAccessible);
|
||||||
|
nsresult HandlePopupHidingEvent(nsIDOMNode *aNode,
|
||||||
|
nsIAccessible *aAccessible);
|
||||||
|
|
||||||
#ifdef MOZ_XUL
|
#ifdef MOZ_XUL
|
||||||
nsresult HandleTreeRowCountChangedEvent(nsIDOMEvent *aEvent,
|
nsresult HandleTreeRowCountChangedEvent(nsIDOMEvent *aEvent,
|
||||||
nsIAccessibleTreeCache *aAccessible);
|
nsIAccessibleTreeCache *aAccessible);
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles 'TreeInvalidated' event. Used in HandleEventWithTarget().
|
|
||||||
*/
|
|
||||||
nsresult HandleTreeInvalidatedEvent(nsIDOMEvent *aEvent,
|
nsresult HandleTreeInvalidatedEvent(nsIDOMEvent *aEvent,
|
||||||
nsIAccessibleTreeCache *aAccessible);
|
nsIAccessibleTreeCache *aAccessible);
|
||||||
|
|
||||||
|
@ -69,6 +69,7 @@ _TEST_FILES =\
|
|||||||
test_nsIAccessible_actions.html \
|
test_nsIAccessible_actions.html \
|
||||||
$(warning test_nsIAccessible_actions.xul temporarily disabled) \
|
$(warning test_nsIAccessible_actions.xul temporarily disabled) \
|
||||||
test_nsIAccessible_applicationAccessible.html \
|
test_nsIAccessible_applicationAccessible.html \
|
||||||
|
test_nsIAccessible_comboboxes.xul \
|
||||||
test_nsIAccessible_editablebody.html \
|
test_nsIAccessible_editablebody.html \
|
||||||
test_nsIAccessible_editabledoc.html \
|
test_nsIAccessible_editabledoc.html \
|
||||||
test_nsIAccessible_name.html \
|
test_nsIAccessible_name.html \
|
||||||
|
@ -4,6 +4,9 @@
|
|||||||
const nsIAccessibleRetrieval = Components.interfaces.nsIAccessibleRetrieval;
|
const nsIAccessibleRetrieval = Components.interfaces.nsIAccessibleRetrieval;
|
||||||
|
|
||||||
const nsIAccessibleEvent = Components.interfaces.nsIAccessibleEvent;
|
const nsIAccessibleEvent = Components.interfaces.nsIAccessibleEvent;
|
||||||
|
const nsIAccessibleStateChangeEvent =
|
||||||
|
Components.interfaces.nsIAccessibleStateChangeEvent;
|
||||||
|
|
||||||
const nsIAccessibleStates = Components.interfaces.nsIAccessibleStates;
|
const nsIAccessibleStates = Components.interfaces.nsIAccessibleStates;
|
||||||
const nsIAccessibleRole = Components.interfaces.nsIAccessibleRole;
|
const nsIAccessibleRole = Components.interfaces.nsIAccessibleRole;
|
||||||
const nsIAccessibleTypes = Components.interfaces.nsIAccessibleTypes;
|
const nsIAccessibleTypes = Components.interfaces.nsIAccessibleTypes;
|
||||||
@ -195,6 +198,71 @@ function unregisterA11yEventListener(aEventType, aEventHandler)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates event queue for the given event type. The queue consists of invoker
|
||||||
|
* objects, each of them generates the event of the event type. When queue is
|
||||||
|
* started then every invoker object is asked to generate event after timeout.
|
||||||
|
* When event is caught then current invoker object is asked to check wether
|
||||||
|
* event was handled correctly.
|
||||||
|
*
|
||||||
|
* Invoker interface is:
|
||||||
|
* var invoker = {
|
||||||
|
* invoke: function(){}, // generates event for the DOM node
|
||||||
|
* check: function(aEvent){}, // checks event for correctness
|
||||||
|
* DOMNode getter() {} // DOM node event is generated for
|
||||||
|
* };
|
||||||
|
*
|
||||||
|
* @param aEventType the given event type
|
||||||
|
*/
|
||||||
|
function eventQueue(aEventType)
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Add invoker object into queue.
|
||||||
|
*/
|
||||||
|
this.push = function eventQueue_push(aEventInvoker)
|
||||||
|
{
|
||||||
|
this.mInvokers.push(aEventInvoker);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start the queue processing.
|
||||||
|
*/
|
||||||
|
this.invoke = function eventQueue_invoke()
|
||||||
|
{
|
||||||
|
window.setTimeout(
|
||||||
|
function(aQueue)
|
||||||
|
{
|
||||||
|
if (aQueue.mIndex == aQueue.mInvokers.length - 1) {
|
||||||
|
unregisterA11yEventListener(aQueue.mEventType, aQueue.mEventHandler);
|
||||||
|
|
||||||
|
for (var idx = 0; idx < aQueue.mInvokers.length; idx++)
|
||||||
|
ok(aQueue.mInvokers[idx].wasCaught, "test " + idx + " failed.");
|
||||||
|
|
||||||
|
SimpleTest.finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
aQueue.mInvokers[++aQueue.mIndex].invoke();
|
||||||
|
|
||||||
|
aQueue.invoke();
|
||||||
|
},
|
||||||
|
100, this
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.getInvoker = function eventQueue_getInvoker()
|
||||||
|
{
|
||||||
|
return this.mInvokers[this.mIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
this.mEventType = aEventType;
|
||||||
|
this.mEventHandler = new eventHandlerForEventQueue(this);
|
||||||
|
|
||||||
|
registerA11yEventListener(this.mEventType, this.mEventHandler);
|
||||||
|
|
||||||
|
this.mInvokers = new Array();
|
||||||
|
this.mIndex = -1;
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// Private
|
// Private
|
||||||
@ -235,3 +303,18 @@ var gA11yEventObserver =
|
|||||||
listenersArray[index].handleEvent(event);
|
listenersArray[index].handleEvent(event);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function eventHandlerForEventQueue(aQueue)
|
||||||
|
{
|
||||||
|
this.handleEvent = function eventHandlerForEventQueue_handleEvent(aEvent)
|
||||||
|
{
|
||||||
|
var invoker = this.mQueue.getInvoker();
|
||||||
|
if (aEvent.DOMNode == invoker.DOMNode) {
|
||||||
|
invoker.check(aEvent);
|
||||||
|
invoker.wasCaught = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.mQueue = aQueue;
|
||||||
|
}
|
||||||
|
|
||||||
|
102
accessible/tests/mochitest/test_nsIAccessible_comboboxes.xul
Normal file
102
accessible/tests/mochitest/test_nsIAccessible_comboboxes.xul
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
<?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="nsIAccessible interface for xul:menulist test.">
|
||||||
|
|
||||||
|
<script type="application/javascript"
|
||||||
|
src="chrome://mochikit/content/MochiKit/packed.js"></script>
|
||||||
|
<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="chrome://mochikit/content/a11y/accessible/common.js" />
|
||||||
|
|
||||||
|
<script type="application/javascript">
|
||||||
|
<![CDATA[
|
||||||
|
function openHideCombobox(aComboboxNode, aIsOpen)
|
||||||
|
{
|
||||||
|
this.invoke = function invoke()
|
||||||
|
{
|
||||||
|
synthesizeMouse(this.DOMNode, 5, 5, {});
|
||||||
|
}
|
||||||
|
this.check = function check(aEvent)
|
||||||
|
{
|
||||||
|
aEvent.QueryInterface(nsIAccessibleStateChangeEvent);
|
||||||
|
|
||||||
|
is(aEvent.state, nsIAccessibleStates.STATE_EXPANDED,
|
||||||
|
"Wrong state change event is handled.");
|
||||||
|
is(aEvent.isEnabled(), this.mIsOpen,
|
||||||
|
"Wrong value of state expanded.");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.DOMNode = aComboboxNode;
|
||||||
|
this.mIsOpen = aIsOpen;
|
||||||
|
}
|
||||||
|
|
||||||
|
var gQueue = null;
|
||||||
|
function doTest()
|
||||||
|
{
|
||||||
|
gQueue = new eventQueue(nsIAccessibleEvent.EVENT_STATE_CHANGE);
|
||||||
|
|
||||||
|
var menulist = document.getElementById("menulist");
|
||||||
|
gQueue.push(new openHideCombobox(menulist, true));
|
||||||
|
gQueue.push(new openHideCombobox(menulist, false));
|
||||||
|
|
||||||
|
// XXX: searchbar doesn't fire state change events because accessible
|
||||||
|
// parent of combobox_list accessible is pushbutton accessible.
|
||||||
|
//var searchbar = document.getElementById("searchbar");
|
||||||
|
//gQueue.push(new openHideCombobox(searchbar, true));
|
||||||
|
//gQueue.push(new openHideCombobox(searchbar, false));
|
||||||
|
|
||||||
|
gQueue.invoke(); // Will call SimpleTest.finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is the hack needed for searchbar work outside of browser.
|
||||||
|
function getBrowser()
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
mCurrentBrowser: { engines: new Array() }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
SimpleTest.waitForExplicitFinish();
|
||||||
|
addLoadEvent(doTest);
|
||||||
|
]]>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<hbox style="overflow: auto;" flex="1">
|
||||||
|
<body xmlns="http://www.w3.org/1999/xhtml">
|
||||||
|
<a target="_blank"
|
||||||
|
href="https://bugzilla.mozilla.org/show_bug.cgi?id=467057"
|
||||||
|
title="xul menulist doesn't fire expand/collapse state change events">
|
||||||
|
Mozilla Bug 467057
|
||||||
|
</a>
|
||||||
|
<p id="display"></p>
|
||||||
|
<div id="content" style="display: none">
|
||||||
|
</div>
|
||||||
|
<pre id="test">
|
||||||
|
</pre>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
<vbox flex="1">
|
||||||
|
<menulist id="menulist">
|
||||||
|
<menupopup>
|
||||||
|
<menuitem label="item1"/>
|
||||||
|
<menuitem label="item2"/>
|
||||||
|
<menuitem label="item3"/>
|
||||||
|
</menupopup>
|
||||||
|
</menulist>
|
||||||
|
|
||||||
|
<searchbar id="searchbar"/>
|
||||||
|
</vbox>
|
||||||
|
</hbox>
|
||||||
|
|
||||||
|
</window>
|
||||||
|
|
Loading…
Reference in New Issue
Block a user