Bug 378468 - correct handling of show/hiden/reorder events, r=aaronlev

This commit is contained in:
surkov.alexander%gmail.com 2007-06-30 02:49:32 +00:00
Родитель 51a8f1bf15
Коммит 94124b2d2f
6 изменённых файлов: 128 добавлений и 110 удалений

Просмотреть файл

@ -1276,40 +1276,12 @@ nsAccessibleWrap::FireAccessibleEvent(nsIAccessibleEvent *aEvent)
*(gint *)eventData);
break;
// Is a superclass of ATK event children_changed
case nsIAccessibleEvent::EVENT_REORDER:
AtkChildrenChange *pAtkChildrenChange;
case nsIAccessibleEvent::EVENT_SHOW:
return FireAtkShowHideEvent(aEvent, atkObj, PR_TRUE);
MAI_LOG_DEBUG(("\n\nReceived: EVENT_REORDER(children_change)\n"));
case nsIAccessibleEvent::EVENT_HIDE:
return FireAtkShowHideEvent(aEvent, atkObj, PR_FALSE);
pAtkChildrenChange = NS_REINTERPRET_CAST(AtkChildrenChange *,
eventData);
nsAccessibleWrap *childAccWrap;
if (pAtkChildrenChange && pAtkChildrenChange->child) {
childAccWrap = NS_STATIC_CAST(nsAccessibleWrap *,
pAtkChildrenChange->child);
g_signal_emit_by_name (atkObj,
pAtkChildrenChange->add ? \
"children_changed::add" : \
"children_changed::remove",
pAtkChildrenChange->index,
childAccWrap->GetAtkObject(),
NULL);
}
else {
//
// EVENT_REORDER is normally fired by "HTML Document".
//
// In GOK, [only] "children_changed::add" can cause foreground
// window accessible to update it children, which will
// refresh "UI-Grab" window.
//
g_signal_emit_by_name (atkObj,
"children_changed::add",
-1, NULL, NULL);
}
break;
/*
* Because dealing with menu is very different between nsIAccessible
* and ATK, and the menu activity is important, specially transfer the
@ -1368,21 +1340,17 @@ nsAccessibleWrap::FireAccessibleEvent(nsIAccessibleEvent *aEvent)
} break;
case nsIAccessibleEvent::EVENT_MENUPOPUP_START:
// fire extra focus event, then go down to EVENT_SHOW
atk_focus_tracker_notify(atkObj);
case nsIAccessibleEvent::EVENT_SHOW:
MAI_LOG_DEBUG(("\n\nReceived: EVENT_SHOW\n"));
MAI_LOG_DEBUG(("\n\nReceived: EVENT_MENUPOPUP_START\n"));
atk_focus_tracker_notify(atkObj); // fire extra focus event
atk_object_notify_state_change(atkObj, ATK_STATE_VISIBLE, PR_TRUE);
atk_object_notify_state_change(atkObj, ATK_STATE_SHOWING, PR_TRUE);
break;
case nsIAccessibleEvent::EVENT_HIDE:
case nsIAccessibleEvent::EVENT_MENUPOPUP_END:
MAI_LOG_DEBUG(("\n\nReceived: EVENT_HIDE\n"));
MAI_LOG_DEBUG(("\n\nReceived: EVENT_MENUPOPUP_END\n"));
atk_object_notify_state_change(atkObj, ATK_STATE_VISIBLE, PR_FALSE);
atk_object_notify_state_change(atkObj, ATK_STATE_SHOWING, PR_FALSE);
break;
break;
}
return NS_OK;
@ -1528,3 +1496,37 @@ nsAccessibleWrap::FireAtkPropChangedEvent(nsIAccessibleEvent *aEvent,
return NS_OK;
}
nsresult
nsAccessibleWrap::FireAtkShowHideEvent(nsIAccessibleEvent *aEvent,
AtkObject *aObject, PRBool aIsAdded)
{
if (aIsAdded)
MAI_LOG_DEBUG(("\n\nReceived: EVENT_SHOW\n"));
else
MAI_LOG_DEBUG(("\n\nReceived: EVENT_HIDE\n"));
nsCOMPtr<nsIAccessible> accessible;
aEvent->GetAccessible(getter_AddRefs(accessible));
NS_ENSURE_STATE(accessible);
nsCOMPtr<nsIAccessible> parentAccessible;
accessible->GetParent(getter_AddRefs(parentAccessible));
NS_ENSURE_STATE(parentAccessible);
PRInt32 indexInParent = -1;
accessible->GetIndexInParent(&indexInParent);
AtkObject *parentObject = GetAtkObject(parentAccessible);
NS_ENSURE_STATE(parentObject);
g_signal_emit_by_name(parentObject,
aIsAdded ? \
"children_changed::add" : \
"children_changed::remove",
indexInParent,
aObject,
NULL);
return NS_OK;
}

Просмотреть файл

@ -121,6 +121,8 @@ protected:
AtkObject *aObject);
nsresult FireAtkPropChangedEvent(nsIAccessibleEvent *aEvent,
AtkObject *aObject);
nsresult FireAtkShowHideEvent(nsIAccessibleEvent *aEvent,
AtkObject *aObject, PRBool aIsAdded);
AtkObject *mAtkObject;

Просмотреть файл

@ -22,6 +22,7 @@
* Contributor(s):
* Kyle Yuan (kyle.yuan@sun.com)
* John Sun (john.sun@sun.com)
* Alexander Surkov <surkov.alexander@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
@ -128,18 +129,13 @@ private:
PRInt32 mCaretOffset;
};
// XXX todo: We might want to use XPCOM interfaces instead of structs
// e.g., nsAccessibleTextChangeEvent: public nsIAccessibleTextChangeEvent
struct AtkChildrenChange {
PRInt32 index; // index of child in parent
nsIAccessible *child;
PRBool add; // true for add, false for delete
};
// XXX todo: We might want to use XPCOM interfaces instead of struct
// e.g., nsAccessibleTableChangeEvent: public nsIAccessibleTableChangeEvent
struct AtkTableChange {
PRUint32 index; // the start row/column after which the rows are inserted/deleted.
PRUint32 count; // the number of inserted/deleted rows/columns
};
#endif
#endif

Просмотреть файл

@ -1294,6 +1294,9 @@ void nsDocAccessible::FlushEventsCallback(nsITimer *aTimer, void *aClosure)
void nsDocAccessible::RefreshNodes(nsIDOMNode *aStartNode, PRUint32 aChangeEvent)
{
NS_ASSERTION(aChangeEvent != nsIAccessibleEvent::EVENT_SHOW,
"nsDocAccessible::RefreshNodes isn't supposed to work with show event.");
nsCOMPtr<nsIDOMNode> iterNode(aStartNode), nextNode;
nsCOMPtr<nsIAccessNode> accessNode;
@ -1306,27 +1309,24 @@ void nsDocAccessible::RefreshNodes(nsIDOMNode *aStartNode, PRUint32 aChangeEvent
// Don't shutdown our doc object!
if (accessNode != NS_STATIC_CAST(nsIAccessNode*, this)) {
if (aChangeEvent != nsIAccessibleEvent::EVENT_SHOW) {
nsCOMPtr<nsIAccessible> accessible(do_QueryInterface(accessNode));
if (accessible) {
// Fire menupopupend events for menu popups that go away
PRUint32 role, event = 0;
accessible->GetFinalRole(&role);
if (role == nsIAccessibleRole::ROLE_MENUPOPUP) {
nsCOMPtr<nsIDOMNode> domNode;
accessNode->GetDOMNode(getter_AddRefs(domNode));
nsCOMPtr<nsIDOMXULPopupElement> popup(do_QueryInterface(domNode));
if (!popup) {
// Popup elements already fire these via DOMMenuInactive
// handling in nsRootAccessible::HandleEvent
event = nsIAccessibleEvent::EVENT_MENUPOPUP_END;
}
}
if (event) {
FireToolkitEvent(event, accessible, nsnull);
nsCOMPtr<nsIAccessible> accessible(do_QueryInterface(accessNode));
if (accessible) {
// Fire menupopupend events for menu popups that go away
PRUint32 role = Role(accessible);
if (role == nsIAccessibleRole::ROLE_MENUPOPUP) {
nsCOMPtr<nsIDOMNode> domNode;
accessNode->GetDOMNode(getter_AddRefs(domNode));
nsCOMPtr<nsIDOMXULPopupElement> popup(do_QueryInterface(domNode));
if (!popup) {
// Popup elements already fire these via DOMMenuInactive
// handling in nsRootAccessible::HandleEvent
FireToolkitEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_END,
accessible, nsnull);
}
}
}
void *uniqueID;
accessNode->GetUniqueID(&uniqueID);
nsCOMPtr<nsPIAccessNode> privateAccessNode(do_QueryInterface(accessNode));
@ -1403,6 +1403,8 @@ NS_IMETHODIMP nsDocAccessible::InvalidateCacheSubtree(nsIContent *aChild,
}
nsCOMPtr<nsPIAccessible> privateChildAccessible =
do_QueryInterface(childAccessible);
NS_ENSURE_STATE(privateChildAccessible);
#ifdef DEBUG_A11Y
nsAutoString localName;
childNode->GetLocalName(localName);
@ -1418,13 +1420,12 @@ NS_IMETHODIMP nsDocAccessible::InvalidateCacheSubtree(nsIContent *aChild,
}
#endif
if (aChangeEventType == nsIAccessibleEvent::EVENT_HIDE) {
// Fire EVENT_HIDE or EVENT_MENUPOPUP_END if previous accessible existed
// for node being hidden. Fire this before the accessible goes away
if (privateChildAccessible) {
privateChildAccessible->FireToolkitEvent(nsIAccessibleEvent::EVENT_HIDE,
childAccessible, nsnull);
}
if (aChangeEventType == nsIAccessibleEvent::EVENT_HIDE ||
aChangeEventType == nsIAccessibleEvent::EVENT_REORDER) {
// Fire EVENT_HIDE if previous accessible existed for node being hidden.
// Fire this before the accessible goes away.
privateChildAccessible->FireToolkitEvent(nsIAccessibleEvent::EVENT_HIDE,
childAccessible, nsnull);
}
// Shutdown nsIAccessNode's or nsIAccessibles for any DOM nodes in this subtree
@ -1465,7 +1466,8 @@ NS_IMETHODIMP nsDocAccessible::InvalidateCacheSubtree(nsIContent *aChild,
}
}
if (aChangeEventType == nsIAccessibleEvent::EVENT_SHOW && aChild) {
if (aChild && (aChangeEventType == nsIAccessibleEvent::EVENT_SHOW ||
aChangeEventType == nsIAccessibleEvent::EVENT_REORDER)) {
// Fire EVENT_SHOW, EVENT_MENUPOPUP_START for newly visible content.
// Fire after a short timer, because we want to make sure the view has been
// updated to make this accessible content visible. If we don't wait,

Просмотреть файл

@ -1529,42 +1529,9 @@ nsAccessibleWrap::FireAccessibleEvent(nsIAccessibleEvent *aEvent)
} else {
newAccessible = accessible;
}
HWND hWnd = 0;
nsCOMPtr<nsPIAccessNode> privateAccessNode =
do_QueryInterface(newAccessible);
if (privateAccessNode) {
nsIFrame *frame = privateAccessNode->GetFrame();
if (frame) {
nsIWidget *window = frame->GetWindow();
PRBool isVisible;
window->IsVisible(isVisible);
if (isVisible) {
// Short explanation:
// If HWND for frame is inside a hidden window, fire the event on the
// containing document's visible window.
//
// Long explanation:
// This is really just to fix combo boxes with JAWS. Window-Eyes already worked with
// combo boxes because they use the value change event in the closed combo box
// case. JAWS will only pay attention to the focus events on the list items.
// The JAWS developers haven't fixed that, so we'll use the focus events to make JAWS work.
// However, JAWS is ignoring events on a hidden window. So, in order to fix the bug where
// JAWS doesn't echo the current option as it changes in a closed combo box, we need to use an
// ensure that we never fire an event with an HWND for a hidden window.
hWnd = (HWND)frame->GetWindow()->GetNativeData(NS_NATIVE_WINDOW);
}
}
}
if (!hWnd) {
void* handle = nsnull;
nsCOMPtr<nsIAccessibleDocument> accessibleDoc;
accessNode->GetAccessibleDocument(getter_AddRefs(accessibleDoc));
NS_ENSURE_STATE(accessibleDoc);
accessibleDoc->GetWindowHandle(&handle);
hWnd = (HWND)handle;
}
HWND hWnd = GetHWNDFor(accessible);
NS_ENSURE_TRUE(hWnd, NS_ERROR_FAILURE);
// Gecko uses two windows for every scrollable area. One window contains
// scrollbars and the child window contains only the client area.
@ -1598,6 +1565,54 @@ PRInt32 nsAccessibleWrap::GetChildIDFor(nsIAccessible* aAccessible)
return - NS_PTR_TO_INT32(uniqueID);
}
HWND
nsAccessibleWrap::GetHWNDFor(nsIAccessible *aAccessible)
{
nsCOMPtr<nsIAccessNode> accessNode(do_QueryInterface(aAccessible));
nsCOMPtr<nsPIAccessNode> privateAccessNode(do_QueryInterface(accessNode));
if (!privateAccessNode)
return 0;
HWND hWnd = 0;
nsIFrame *frame = privateAccessNode->GetFrame();
if (frame) {
nsIWidget *window = frame->GetWindow();
PRBool isVisible;
window->IsVisible(isVisible);
if (isVisible) {
// Short explanation:
// If HWND for frame is inside a hidden window, fire the event on the
// containing document's visible window.
//
// Long explanation:
// This is really just to fix combo boxes with JAWS. Window-Eyes already
// worked with combo boxes because they use the value change event in
// the closed combo box case. JAWS will only pay attention to the focus
// events on the list items. The JAWS developers haven't fixed that, so
// we'll use the focus events to make JAWS work. However, JAWS is
// ignoring events on a hidden window. So, in order to fix the bug where
// JAWS doesn't echo the current option as it changes in a closed
// combo box, we need to use an ensure that we never fire an event with
// an HWND for a hidden window.
hWnd = (HWND)frame->GetWindow()->GetNativeData(NS_NATIVE_WINDOW);
}
}
if (!hWnd) {
void* handle = nsnull;
nsCOMPtr<nsIAccessibleDocument> accessibleDoc;
accessNode->GetAccessibleDocument(getter_AddRefs(accessibleDoc));
if (!accessibleDoc)
return 0;
accessibleDoc->GetWindowHandle(&handle);
hWnd = (HWND)handle;
}
return hWnd;
}
IDispatch *nsAccessibleWrap::NativeAccessible(nsIAccessible *aXPAccessible)
{
if (!aXPAccessible) {

Просмотреть файл

@ -295,6 +295,7 @@ class nsAccessibleWrap : public nsAccessible,
// Helper methods
static PRInt32 GetChildIDFor(nsIAccessible* aAccessible);
static HWND GetHWNDFor(nsIAccessible *aAccessible);
/**
* System caret support: update the Windows caret position.