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;
|
||||
}
|
||||
|
||||
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;
|
||||
accService->GetAccessibleInShell(aTargetNode, eventShell,
|
||||
getter_AddRefs(accessible));
|
||||
|
||||
if (eventType.EqualsLiteral("popuphiding"))
|
||||
return HandlePopupHidingEvent(aTargetNode, accessible);
|
||||
|
||||
nsCOMPtr<nsPIAccessible> privAcc(do_QueryInterface(accessible));
|
||||
if (!privAcc)
|
||||
return NS_OK;
|
||||
@ -831,22 +820,7 @@ nsresult nsRootAccessible::HandleEventWithTarget(nsIDOMEvent* aEvent,
|
||||
nsAccUtils::FireAccEvent(nsIAccessibleEvent::EVENT_ALERT, accessible);
|
||||
}
|
||||
else if (eventType.EqualsLiteral("popupshown")) {
|
||||
// Don't fire menupopup events for combobox and autocomplete lists
|
||||
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);
|
||||
}
|
||||
HandlePopupShownEvent(accessible);
|
||||
}
|
||||
else if (eventType.EqualsLiteral("DOMMenuInactive")) {
|
||||
if (nsAccUtils::Role(accessible) == nsIAccessibleRole::ROLE_MENUPOPUP) {
|
||||
@ -1103,6 +1077,94 @@ NS_IMETHODIMP nsRootAccessible::FireDocLoadEvents(PRUint32 aEventType)
|
||||
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
|
||||
nsresult
|
||||
nsRootAccessible::HandleTreeRowCountChangedEvent(nsIDOMEvent *aEvent,
|
||||
|
@ -133,15 +133,15 @@ class nsRootAccessible : public nsDocAccessibleWrap,
|
||||
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
|
||||
nsresult HandleTreeRowCountChangedEvent(nsIDOMEvent *aEvent,
|
||||
nsIAccessibleTreeCache *aAccessible);
|
||||
|
||||
/**
|
||||
* Handles 'TreeInvalidated' event. Used in HandleEventWithTarget().
|
||||
*/
|
||||
nsresult HandleTreeInvalidatedEvent(nsIDOMEvent *aEvent,
|
||||
nsIAccessibleTreeCache *aAccessible);
|
||||
|
||||
|
@ -69,6 +69,7 @@ _TEST_FILES =\
|
||||
test_nsIAccessible_actions.html \
|
||||
$(warning test_nsIAccessible_actions.xul temporarily disabled) \
|
||||
test_nsIAccessible_applicationAccessible.html \
|
||||
test_nsIAccessible_comboboxes.xul \
|
||||
test_nsIAccessible_editablebody.html \
|
||||
test_nsIAccessible_editabledoc.html \
|
||||
test_nsIAccessible_name.html \
|
||||
|
@ -4,6 +4,9 @@
|
||||
const nsIAccessibleRetrieval = Components.interfaces.nsIAccessibleRetrieval;
|
||||
|
||||
const nsIAccessibleEvent = Components.interfaces.nsIAccessibleEvent;
|
||||
const nsIAccessibleStateChangeEvent =
|
||||
Components.interfaces.nsIAccessibleStateChangeEvent;
|
||||
|
||||
const nsIAccessibleStates = Components.interfaces.nsIAccessibleStates;
|
||||
const nsIAccessibleRole = Components.interfaces.nsIAccessibleRole;
|
||||
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
|
||||
@ -235,3 +303,18 @@ var gA11yEventObserver =
|
||||
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