Bug 615189 - clean up FireAccessibleFocusEvent, r=fer, a=davidb

This commit is contained in:
Alexander Surkov 2010-12-12 09:47:30 +08:00
Родитель 4a403e1281
Коммит d5f44d3067
3 изменённых файлов: 77 добавлений и 110 удалений

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

@ -1160,9 +1160,10 @@ nsDocAccessible::ARIAAttributeChanged(nsIContent* aContent, nsIAtom* aAttribute)
// to the element with the id that activedescendant points to
nsCOMPtr<nsINode> focusedNode = GetCurrentFocus();
if (nsCoreUtils::GetRoleContent(focusedNode) == aContent) {
nsAccessible* focusedAcc = GetAccService()->GetAccessible(focusedNode);
nsRefPtr<nsRootAccessible> rootAcc = GetRootAccessible();
if (rootAcc) {
rootAcc->FireAccessibleFocusEvent(nsnull, focusedNode, nsnull, PR_TRUE);
if (rootAcc && focusedAcc) {
rootAcc->FireAccessibleFocusEvent(focusedAcc, nsnull, PR_TRUE);
}
}
return;

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

@ -314,83 +314,56 @@ nsRootAccessible::GetCaretAccessible()
return mCaretAccessible;
}
PRBool
nsRootAccessible::FireAccessibleFocusEvent(nsAccessible *aAccessible,
nsINode *aNode,
nsIDOMEvent *aFocusEvent,
void
nsRootAccessible::FireAccessibleFocusEvent(nsAccessible* aFocusAccessible,
nsIContent* aRealFocusContent,
PRBool aForceEvent,
EIsFromUserInput aIsFromUserInput)
{
// Implementors: only fire delayed/async events from this method.
if (mCaretAccessible) {
nsCOMPtr<nsIDOMNSEvent> nsevent(do_QueryInterface(aFocusEvent));
if (nsevent) {
// Use the originally focused node where the selection lives.
// For example, use the anonymous HTML:input instead of the containing
// XUL:textbox. In this case, sometimes it is a later focus event
// which points to the actual anonymous child with focus, so to be safe
// we need to reset the selection listener every time.
// This happens because when some bindings handle focus, they retarget
// focus to the appropriate child inside of themselves, but DOM focus
// stays outside on that binding parent.
nsCOMPtr<nsIDOMEventTarget> domEventTarget;
nsevent->GetOriginalTarget(getter_AddRefs(domEventTarget));
nsCOMPtr<nsIContent> realFocusedNode(do_QueryInterface(domEventTarget));
if (!realFocusedNode) {
// When FireCurrentFocusEvent() synthesizes a focus event,
// the orignal target does not exist, so use the passed-in node
// which is the relevant focused node
realFocusedNode = do_QueryInterface(aNode);
}
if (realFocusedNode) {
mCaretAccessible->SetControlSelectionListener(realFocusedNode);
}
}
}
// Set selection listener for focused element.
if (mCaretAccessible && aRealFocusContent)
mCaretAccessible->SetControlSelectionListener(aRealFocusContent);
// Check for aria-activedescendant, which changes which element has focus
nsINode *finalFocusNode = aNode;
nsAccessible *finalFocusAccessible = aAccessible;
nsAccessible* focusAccessible = aFocusAccessible;
nsIContent *finalFocusContent = nsCoreUtils::GetRoleContent(finalFocusNode);
if (finalFocusContent) {
nsAutoString id;
if (finalFocusContent->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_activedescendant, id)) {
nsIDocument *doc = aNode->GetOwnerDoc();
finalFocusNode = doc->GetElementById(id);
if (!finalFocusNode) {
// If aria-activedescendant is set to nonexistant ID, then treat as focus
// on the activedescendant container (which has real DOM focus)
finalFocusNode = aNode;
}
finalFocusAccessible = nsnull;
}
}
// Fire focus only if it changes, but always fire focus events when aForceEvent == PR_TRUE
if (gLastFocusedNode == finalFocusNode && !aForceEvent) {
return PR_FALSE;
}
if (!finalFocusAccessible) {
finalFocusAccessible = GetAccService()->GetAccessible(finalFocusNode);
// Check for aria-activedescendant, which changes which element has focus.
// For activedescendant, the ARIA spec does not require that the user agent
// checks whether finalFocusNode is actually a DOM descendant of the element
// checks whether pointed node is actually a DOM descendant of the element
// with the aria-activedescendant attribute.
if (!finalFocusAccessible) {
return PR_FALSE;
nsIContent* content = focusAccessible->GetContent();
if (content) {
nsAutoString id;
if (content->GetAttr(kNameSpaceID_None,
nsAccessibilityAtoms::aria_activedescendant, id)) {
nsIDocument* DOMDoc = content->GetOwnerDoc();
nsIContent* activeDescendantContent = DOMDoc->GetElementById(id);
// If aria-activedescendant is set to nonexistant ID, then treat as focus
// on the activedescendant container (which has real DOM focus).
if (activeDescendantContent) {
focusAccessible =
GetAccService()->GetAccessible(activeDescendantContent);
}
}
}
gLastFocusedAccessiblesState = nsAccUtils::State(finalFocusAccessible);
PRUint32 role = finalFocusAccessible->Role();
if (role == nsIAccessibleRole::ROLE_MENUITEM) {
if (!mCurrentARIAMenubar) { // Entering menus
// The natural role is the role that this type of element normally has
if (role != finalFocusAccessible->NativeRole()) { // Must be a DHTML menuitem
nsAccessible *menuBarAccessible =
nsAccUtils::GetAncestorWithRole(finalFocusAccessible,
// Fire focus only if it changes, but always fire focus events when
// aForceEvent == PR_TRUE
nsINode* focusNode = focusAccessible->GetNode();
if (gLastFocusedNode == focusNode && !aForceEvent)
return;
gLastFocusedAccessiblesState = nsAccUtils::State(focusAccessible);
// Fire menu start/end events for ARIA menus.
if (focusAccessible->ARIARole() == nsIAccessibleRole::ROLE_MENUITEM) {
// The focus is inside a menu.
if (!mCurrentARIAMenubar) {
// Entering ARIA menu. Fire menu start event.
nsAccessible* menuBarAccessible =
nsAccUtils::GetAncestorWithRole(focusAccessible,
nsIAccessibleRole::ROLE_MENUBAR);
if (menuBarAccessible) {
mCurrentARIAMenubar = menuBarAccessible->GetNode();
@ -399,15 +372,14 @@ nsRootAccessible::FireAccessibleFocusEvent(nsAccessible *aAccessible,
new AccEvent(nsIAccessibleEvent::EVENT_MENU_START,
menuBarAccessible, aIsFromUserInput,
AccEvent::eAllowDupes);
if (menuStartEvent) {
if (menuStartEvent)
FireDelayedAccessibleEvent(menuStartEvent);
}
}
}
}
}
}
else if (mCurrentARIAMenubar) {
// Focus left a menu. Fire menu end event.
nsRefPtr<AccEvent> menuEndEvent =
new AccEvent(nsIAccessibleEvent::EVENT_MENU_END, mCurrentARIAMenubar,
aIsFromUserInput, AccEvent::eAllowDupes);
@ -417,29 +389,15 @@ nsRootAccessible::FireAccessibleFocusEvent(nsAccessible *aAccessible,
mCurrentARIAMenubar = nsnull;
}
nsCOMPtr<nsIContent> focusContent = do_QueryInterface(finalFocusNode);
nsIFrame *focusFrame = nsnull;
if (focusContent) {
nsIPresShell *shell = nsCoreUtils::GetPresShellFor(finalFocusNode);
NS_ASSERTION(shell, "No pres shell for final focus node!");
if (!shell)
return PR_FALSE;
focusFrame = focusContent->GetPrimaryFrame();
}
NS_IF_RELEASE(gLastFocusedNode);
gLastFocusedNode = finalFocusNode;
gLastFocusedNode = focusNode;
NS_IF_ADDREF(gLastFocusedNode);
// Coalesce focus events from the same document, because DOM focus event might
// be fired for the document node and then for the focused DOM element.
FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_FOCUS,
finalFocusNode, AccEvent::eCoalesceFromSameDocument,
focusNode, AccEvent::eCoalesceFromSameDocument,
aIsFromUserInput);
return PR_TRUE;
}
void
@ -545,7 +503,7 @@ nsRootAccessible::HandleEvent(nsIDOMEvent* aEvent)
nsEventShell::FireEvent(accEvent);
if (isEnabled)
FireAccessibleFocusEvent(accessible, targetNode, aEvent);
FireAccessibleFocusEvent(accessible, targetContent);
return NS_OK;
}
@ -649,7 +607,7 @@ nsRootAccessible::HandleEvent(nsIDOMEvent* aEvent)
}
}
}
FireAccessibleFocusEvent(accessible, focusedItem, aEvent);
FireAccessibleFocusEvent(accessible, targetContent);
}
else if (eventType.EqualsLiteral("blur")) {
NS_IF_RELEASE(gLastFocusedNode);
@ -720,7 +678,7 @@ nsRootAccessible::HandleEvent(nsIDOMEvent* aEvent)
}
if (fireFocus) {
// Always asynch, always from user input.
FireAccessibleFocusEvent(accessible, targetNode, aEvent, PR_TRUE,
FireAccessibleFocusEvent(accessible, targetContent, PR_TRUE,
eFromUserInput);
}
}

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

@ -91,20 +91,28 @@ public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_ROOTACCESSIBLE_IMPL_CID)
/**
* Fire an accessible focus event for the current focusAccssible
* and attach a new selection listener, if necessary.
* Fire an accessible focus event for the focused accessible and attach a new
* selection listener to real focused element, if necessary.
*
* @param aFocusAccessible [in] the accessible which has received focus
* @param aFocusNode [in] the DOM node which has received focus
* @param aFocusEvent [in] DOM focus event that caused
* the node/accessible to receive focus
* @param aForceEvent [in] fire a focus event even if the last focused
* item was the same
* @return boolean -- was a focus event actually fired
* @param aRealFocusContent [in] the actual DOM element which has received
* focus (see @note section)
* @param aForceEvent [in, optional] fire a focus event even if
* the last focused item was the same
* @param aIsFromUserInput [in, optional] specifies whether the event is
* from user input
*
* @note Use the originally focused node where the selection lives as real
* focus node. For example, use the anonymous HTML:input instead of
* the containing XUL:textbox. In this case, sometimes it is a later
* focus event which points to the actual anonymous child with focus,
* so to be safe we need to reset the selection listener every time.
* This happens because when some bindings handle focus, they
* retarget focus to the appropriate child inside of themselves, but
* DOM focus stays outside on that binding parent.
*/
PRBool FireAccessibleFocusEvent(nsAccessible *aFocusAccessible,
nsINode *aFocusNode,
nsIDOMEvent *aFocusEvent,
void FireAccessibleFocusEvent(nsAccessible* aFocusAccessible,
nsIContent* aRealFocusContent,
PRBool aForceEvent = PR_FALSE,
EIsFromUserInput aIsFromUserInput = eAutoDetect);