From 03a89342718c716acc90d7ab81ad254f9082f817 Mon Sep 17 00:00:00 2001 From: "aaronleventhal%moonset.net" Date: Fri, 18 Feb 2005 14:34:30 +0000 Subject: [PATCH] Bug 282098. Accessible focus can get trapped inside of popup. r=pkw, sr=jst --- accessible/src/base/nsRootAccessible.cpp | 82 ++++++++++++++++++++++-- accessible/src/base/nsRootAccessible.h | 1 + 2 files changed, 78 insertions(+), 5 deletions(-) diff --git a/accessible/src/base/nsRootAccessible.cpp b/accessible/src/base/nsRootAccessible.cpp index 55100e8b27c6..ba51693faf55 100644 --- a/accessible/src/base/nsRootAccessible.cpp +++ b/accessible/src/base/nsRootAccessible.cpp @@ -55,6 +55,7 @@ #include "nsIDOMXULSelectCntrlItemEl.h" #include "nsIDocument.h" #include "nsIHTMLDocument.h" +#include "nsIFocusController.h" #include "nsIFrame.h" #include "nsIInterfaceRequestorUtils.h" #include "nsIScriptGlobalObject.h" @@ -246,6 +247,9 @@ nsresult nsRootAccessible::AddEventListeners() rv = target->AddEventListener(NS_LITERAL_STRING("popupshown"), NS_STATIC_CAST(nsIDOMXULListener*, this), PR_TRUE); NS_ASSERTION(NS_SUCCEEDED(rv), "failed to register listener"); + rv = target->AddEventListener(NS_LITERAL_STRING("popuphiding"), NS_STATIC_CAST(nsIDOMXULListener*, this), PR_TRUE); + NS_ASSERTION(NS_SUCCEEDED(rv), "failed to register listener"); + rv = target->AddEventListener(NS_LITERAL_STRING("DOMMenuInactive"), NS_STATIC_CAST(nsIDOMXULListener*, this), PR_TRUE); NS_ASSERTION(NS_SUCCEEDED(rv), "failed to register listener"); @@ -288,6 +292,7 @@ nsresult nsRootAccessible::RemoveEventListeners() target->RemoveEventListener(NS_LITERAL_STRING("CheckboxStateChange"), NS_STATIC_CAST(nsIDOMXULListener*, this), PR_TRUE); target->RemoveEventListener(NS_LITERAL_STRING("RadioStateChange"), NS_STATIC_CAST(nsIDOMXULListener*, this), PR_TRUE); target->RemoveEventListener(NS_LITERAL_STRING("popupshown"), NS_STATIC_CAST(nsIDOMXULListener*, this), PR_TRUE); + target->RemoveEventListener(NS_LITERAL_STRING("popuphiding"), NS_STATIC_CAST(nsIDOMXULListener*, this), PR_TRUE); target->RemoveEventListener(NS_LITERAL_STRING("DOMMenuInactive"), NS_STATIC_CAST(nsIDOMXULListener*, this), PR_TRUE); target->RemoveEventListener(NS_LITERAL_STRING("DOMMenuItemActive"), NS_STATIC_CAST(nsIDOMXULListener*, this), PR_TRUE); target->RemoveEventListener(NS_LITERAL_STRING("DOMMenuBarActive"), NS_STATIC_CAST(nsIDOMXULListener*, this), PR_TRUE); @@ -459,6 +464,46 @@ void nsRootAccessible::FireDHTMLFocusRelatedEvents(nsIAccessible *aAccessible, P } } +void nsRootAccessible::FireCurrentFocusEvent() +{ + nsCOMPtr domWin; + GetWindow(getter_AddRefs(domWin)); + nsCOMPtr privateDOMWindow(do_QueryInterface(domWin)); + if (!privateDOMWindow) { + return; + } + nsIFocusController *focusController = privateDOMWindow->GetRootFocusController(); + if (!focusController) { + return; + } + nsCOMPtr focusedElement; + focusController->GetFocusedElement(getter_AddRefs(focusedElement)); + nsCOMPtr focusedNode(do_QueryInterface(focusedElement)); + if (!focusedNode) { + // Document itself may have focus + nsCOMPtr focusedWinInternal; + focusController->GetFocusedWindow(getter_AddRefs(focusedWinInternal)); + if (focusedWinInternal) { + nsCOMPtr focusedDOMDocument; + focusedWinInternal->GetDocument(getter_AddRefs(focusedDOMDocument)); + focusedNode = do_QueryInterface(focusedDOMDocument); + } + if (!focusedNode) { + return; // Could not get a focused document either + } + } + nsCOMPtr eventShell; + GetEventShell(focusedNode, getter_AddRefs(eventShell)); + NS_ASSERTION(eventShell, "No presshell for focused node"); + + nsCOMPtr accessible; + mAccService->GetAccessibleInShell(focusedNode, eventShell, + getter_AddRefs(accessible)); + if (accessible) { + FireAccessibleFocusEvent(accessible, focusedNode); + } +} + void nsRootAccessible::GetEventShell(nsIDOMNode *aNode, nsIPresShell **aEventShell) { // XXX aaronl - this is not ideal. @@ -630,12 +675,23 @@ NS_IMETHODIMP nsRootAccessible::HandleEvent(nsIDOMEvent* aEvent) privAcc->FireToolkitEvent(nsIAccessibleEvent::EVENT_MENUSTART, accessible, nsnull); else if (eventType.LowerCaseEqualsLiteral("dommenubarinactive")) { privAcc->FireToolkitEvent(nsIAccessibleEvent::EVENT_MENUEND, accessible, nsnull); - GetFocusedChild(getter_AddRefs(accessible)); // Returns null if no focus - nsCOMPtr accessNode(do_QueryInterface(accessible)); - if (accessNode) { - accessNode->GetDOMNode(getter_AddRefs(targetNode)); - FireAccessibleFocusEvent(accessible, targetNode); + FireCurrentFocusEvent(); + } + else if (eventType.LowerCaseEqualsLiteral("popuphiding")) { + // If accessible focus was 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. + if (!gLastFocusedNode) { + return NS_OK; } + nsCOMPtr parentOfFocus; + gLastFocusedNode->GetParentNode(getter_AddRefs(parentOfFocus)); + if (parentOfFocus != targetNode) { + return NS_OK; + } + // Focus was inside of popup that's being hidden + FireCurrentFocusEvent(); } else { // Menu popup events @@ -716,6 +772,22 @@ NS_IMETHODIMP nsRootAccessible::HandleEvent(nsIDOMEvent* aEvent) stateData.state = STATE_EXPANDED; privAcc->FireToolkitEvent(nsIAccessibleEvent::EVENT_STATE_CHANGE, accessible, &stateData); } + else if (eventType.LowerCaseEqualsLiteral("popuphiding")) { + // If accessible focus was 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. + if (!gLastFocusedNode) { + return NS_OK; + } + nsCOMPtr parentOfFocus; + gLastFocusedNode->GetParentNode(getter_AddRefs(parentOfFocus)); + if (parentOfFocus != targetNode) { + return NS_OK; + } + // Focus was inside of popup that's being hidden + FireCurrentFocusEvent(); + } else if (eventType.LowerCaseEqualsLiteral("popupshown")) { FireAccessibleFocusEvent(accessible, targetNode); } diff --git a/accessible/src/base/nsRootAccessible.h b/accessible/src/base/nsRootAccessible.h index 871c912f6d90..7de9bcbeadd1 100644 --- a/accessible/src/base/nsRootAccessible.h +++ b/accessible/src/base/nsRootAccessible.h @@ -108,6 +108,7 @@ class nsRootAccessible : public nsDocAccessibleWrap, static void GetTargetNode(nsIDOMEvent *aEvent, nsIDOMNode **aTargetNode); void FireAccessibleFocusEvent(nsIAccessible *focusAccessible, nsIDOMNode *focusNode); void FireDHTMLFocusRelatedEvents(nsIAccessible *aFocusAccessible, PRUint32 aRole); + void FireCurrentFocusEvent(); void GetChromeEventHandler(nsIDOMEventTarget **aChromeTarget); nsCOMPtr mAccService; nsCOMPtr mCaretAccessible;