1 /* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 * The contents of this file are subject to the Mozilla Public License Version
5 * 1.1 (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 * http://www.mozilla.org/MPL/
9 * Software distributed under the License is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 * for the specific language governing rights and limitations under the
14 * The Original Code is mozilla.org code.
16 * The Initial Developer of the Original Code is
18 * Portions created by the Initial Developer are Copyright (C) 2011
19 * the Initial Developer. All Rights Reserved.
22 * Alexander Surkov <surkov.alexander@gmail.com> (original author)
24 * Alternatively, the contents of this file may be used under the terms of
25 * either the GNU General Public License Version 2 or later (the "GPL"), or
26 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
36 * ***** END LICENSE BLOCK ***** */
38 #include "FocusManager.h"
40 #include "nsAccessibilityService.h"
41 #include "nsAccUtils.h"
42 #include "nsRootAccessible.h"
44 #include "nsFocusManager.h"
46 namespace dom = mozilla::dom;
47 using namespace mozilla::a11y;
49 FocusManager::FocusManager()
53 FocusManager::~FocusManager()
58 FocusManager::FocusedAccessible() const
63 nsINode* focusedNode = FocusedDOMNode();
65 return GetAccService()->GetAccessibleOrContainer(focusedNode, nsnull);
71 FocusManager::IsFocused(const nsAccessible* aAccessible) const
74 return mActiveItem == aAccessible;
76 nsINode* focusedNode = FocusedDOMNode();
78 // XXX: Before getting an accessible for node having a DOM focus make sure
79 // they belong to the same document because it can trigger unwanted document
80 // accessible creation for temporary about:blank document. Without this
81 // peculiarity we would end up with plain implementation based on
82 // FocusedAccessible() method call. Make sure this issue is fixed in
84 if (focusedNode->OwnerDoc() == aAccessible->GetNode()->OwnerDoc()) {
86 GetAccService()->GetAccessibleOrContainer(focusedNode, nsnull);
93 FocusManager::IsFocusWithin(const nsAccessible* aContainer) const
95 nsAccessible* child = FocusedAccessible();
97 if (child == aContainer)
100 child = child->Parent();
105 FocusManager::FocusDisposition
106 FocusManager::IsInOrContainsFocus(const nsAccessible* aAccessible) const
108 nsAccessible* focus = FocusedAccessible();
113 if (focus == aAccessible)
116 // If contains the focus.
117 nsAccessible* child = focus->Parent();
119 if (child == aAccessible)
120 return eContainsFocus;
122 child = child->Parent();
125 // If contained by focus.
126 child = aAccessible->Parent();
129 return eContainedByFocus;
131 child = child->Parent();
138 FocusManager::NotifyOfDOMFocus(nsISupports* aTarget)
140 A11YDEBUG_FOCUS_NOTIFICATION_SUPPORTSTARGET("DOM focus", "DOM focus target",
143 mActiveItem = nsnull;
145 nsCOMPtr<nsINode> targetNode(do_QueryInterface(aTarget));
147 nsDocAccessible* document =
148 GetAccService()->GetDocAccessible(targetNode->OwnerDoc());
150 // Set selection listener for focused element.
151 if (targetNode->IsElement()) {
152 nsRootAccessible* root = document->RootAccessible();
153 nsCaretAccessible* caretAcc = root->GetCaretAccessible();
154 caretAcc->SetControlSelectionListener(targetNode->AsElement());
157 document->HandleNotification<FocusManager, nsINode>
158 (this, &FocusManager::ProcessDOMFocus, targetNode);
164 FocusManager::NotifyOfDOMBlur(nsISupports* aTarget)
166 A11YDEBUG_FOCUS_NOTIFICATION_SUPPORTSTARGET("DOM blur", "DOM blur target",
169 mActiveItem = nsnull;
171 // If DOM document stays focused then fire accessible focus event to process
172 // the case when no element within this DOM document will be focused.
173 nsCOMPtr<nsINode> targetNode(do_QueryInterface(aTarget));
174 if (targetNode && targetNode->OwnerDoc() == FocusedDOMDocument()) {
175 nsIDocument* DOMDoc = targetNode->OwnerDoc();
176 nsDocAccessible* document =
177 GetAccService()->GetDocAccessible(DOMDoc);
179 document->HandleNotification<FocusManager, nsINode>
180 (this, &FocusManager::ProcessDOMFocus, DOMDoc);
186 FocusManager::ActiveItemChanged(nsAccessible* aItem, bool aCheckIfActive)
188 A11YDEBUG_FOCUS_NOTIFICATION_ACCTARGET("active item changed",
189 "Active item", aItem)
191 // Nothing changed, happens for XUL trees and HTML selects.
192 if (aItem && aItem == mActiveItem)
195 mActiveItem = nsnull;
197 if (aItem && aCheckIfActive) {
198 nsAccessible* widget = aItem->ContainerWidget();
199 A11YDEBUG_FOCUS_LOG_WIDGET("Active item widget", widget)
200 if (!widget || !widget->IsActiveWidget() || !widget->AreItemsOperable())
205 // If active item is changed then fire accessible focus event on it, otherwise
206 // if there's no an active item then fire focus event to accessible having
208 nsAccessible* target = FocusedAccessible();
210 DispatchFocusEvent(target->GetDocAccessible(), target);
214 FocusManager::ForceFocusEvent()
216 nsINode* focusedNode = FocusedDOMNode();
218 nsDocAccessible* document =
219 GetAccService()->GetDocAccessible(focusedNode->OwnerDoc());
221 document->HandleNotification<FocusManager, nsINode>
222 (this, &FocusManager::ProcessDOMFocus, focusedNode);
228 FocusManager::DispatchFocusEvent(nsDocAccessible* aDocument,
229 nsAccessible* aTarget)
231 NS_PRECONDITION(aDocument, "No document for focused accessible!");
233 nsRefPtr<AccEvent> event =
234 new AccEvent(nsIAccessibleEvent::EVENT_FOCUS, aTarget,
235 eAutoDetect, AccEvent::eCoalesceOfSameType);
236 aDocument->FireDelayedAccessibleEvent(event);
238 A11YDEBUG_FOCUS_LOG_ACCTARGET("Focus notification", aTarget)
243 FocusManager::ProcessDOMFocus(nsINode* aTarget)
245 A11YDEBUG_FOCUS_NOTIFICATION_DOMTARGET("Process DOM focus",
246 "Notification target", aTarget)
248 nsDocAccessible* document =
249 GetAccService()->GetDocAccessible(aTarget->OwnerDoc());
251 nsAccessible* target = document->GetAccessibleOrContainer(aTarget);
253 // Check if still focused. Otherwise we can end up with storing the active
254 // item for control that isn't focused anymore.
255 nsAccessible* DOMFocus =
256 GetAccService()->GetAccessibleOrContainer(FocusedDOMNode(), nsnull);
257 if (target != DOMFocus)
260 nsAccessible* activeItem = target->CurrentItem();
262 mActiveItem = activeItem;
266 DispatchFocusEvent(document, target);
271 FocusManager::ProcessFocusEvent(AccEvent* aEvent)
273 NS_PRECONDITION(aEvent->GetEventType() == nsIAccessibleEvent::EVENT_FOCUS,
274 "Focus event is expected!");
276 EIsFromUserInput fromUserInputFlag = aEvent->IsFromUserInput() ?
277 eFromUserInput : eNoUserInput;
279 // Emit focus event if event target is the active item. Otherwise then check
280 // if it's still focused and then update active item and emit focus event.
281 nsAccessible* target = aEvent->GetAccessible();
282 if (target != mActiveItem) {
283 // Check if still focused. Otherwise we can end up with storing the active
284 // item for control that isn't focused anymore.
285 nsAccessible* DOMFocus =
286 GetAccService()->GetAccessibleOrContainer(FocusedDOMNode(), nsnull);
287 if (target != DOMFocus)
290 nsAccessible* activeItem = target->CurrentItem();
292 mActiveItem = activeItem;
297 // Fire menu start/end events for ARIA menus.
298 if (target->ARIARole() == nsIAccessibleRole::ROLE_MENUITEM) {
299 // The focus was moved into menu.
300 nsAccessible* ARIAMenubar =
301 nsAccUtils::GetAncestorWithRole(target, nsIAccessibleRole::ROLE_MENUBAR);
303 if (ARIAMenubar != mActiveARIAMenubar) {
304 // Leaving ARIA menu. Fire menu_end event on current menubar.
305 if (mActiveARIAMenubar) {
306 nsRefPtr<AccEvent> menuEndEvent =
307 new AccEvent(nsIAccessibleEvent::EVENT_MENU_END, mActiveARIAMenubar,
309 nsEventShell::FireEvent(menuEndEvent);
312 mActiveARIAMenubar = ARIAMenubar;
314 // Entering ARIA menu. Fire menu_start event.
315 if (mActiveARIAMenubar) {
316 nsRefPtr<AccEvent> menuStartEvent =
317 new AccEvent(nsIAccessibleEvent::EVENT_MENU_START,
318 mActiveARIAMenubar, fromUserInputFlag);
319 nsEventShell::FireEvent(menuStartEvent);
322 } else if (mActiveARIAMenubar) {
323 // Focus left a menu. Fire menu_end event.
324 nsRefPtr<AccEvent> menuEndEvent =
325 new AccEvent(nsIAccessibleEvent::EVENT_MENU_END, mActiveARIAMenubar,
327 nsEventShell::FireEvent(menuEndEvent);
329 mActiveARIAMenubar = nsnull;
332 A11YDEBUG_FOCUS_NOTIFICATION_ACCTARGET("FIRE FOCUS EVENT", "Focus target",
335 nsRefPtr<AccEvent> focusEvent =
336 new AccEvent(nsIAccessibleEvent::EVENT_FOCUS, target, fromUserInputFlag);
337 nsEventShell::FireEvent(focusEvent);
339 // Fire scrolling_start event when the document receives the focus if it has
340 // an anchor jump. If an accessible within the document receive the focus
341 // then null out the anchor jump because it no longer applies.
342 nsDocAccessible* targetDocument = target->GetDocAccessible();
343 nsAccessible* anchorJump = targetDocument->AnchorJump();
345 if (target == targetDocument) {
346 // XXX: bug 625699, note in some cases the node could go away before we
347 // we receive focus event, for example if the node is removed from DOM.
348 nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_SCROLLING_START,
349 anchorJump, fromUserInputFlag);
351 targetDocument->SetAnchorJump(nsnull);
356 FocusManager::FocusedDOMElm() const
358 nsFocusManager* DOMFocusManager = nsFocusManager::GetFocusManager();
359 return DOMFocusManager->GetFocusedContent();
363 FocusManager::FocusedDOMDocument() const
365 nsFocusManager* DOMFocusManager = nsFocusManager::GetFocusManager();
367 nsCOMPtr<nsIDOMWindow> focusedWnd;
368 DOMFocusManager->GetFocusedWindow(getter_AddRefs(focusedWnd));
370 nsCOMPtr<nsIDOMDocument> DOMDoc;
371 focusedWnd->GetDocument(getter_AddRefs(DOMDoc));
372 nsCOMPtr<nsIDocument> DOMDocNode(do_QueryInterface(DOMDoc));