зеркало из https://github.com/mozilla/pjs.git
Bug 378468 - correct handling of show/hiden/reorder events, r=aaronlev
This commit is contained in:
Родитель
51a8f1bf15
Коммит
94124b2d2f
|
@ -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.
|
||||
|
|
Загрузка…
Ссылка в новой задаче