LCOV - code coverage report
Current view: top level - accessible/base - FocusManager.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 190 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 16 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* This Source Code Form is subject to the terms of the Mozilla Public
       2             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       3             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       4             : 
       5             : #include "FocusManager.h"
       6             : 
       7             : #include "Accessible-inl.h"
       8             : #include "AccIterator.h"
       9             : #include "DocAccessible-inl.h"
      10             : #include "nsAccessibilityService.h"
      11             : #include "nsAccUtils.h"
      12             : #include "nsEventShell.h"
      13             : #include "Role.h"
      14             : 
      15             : #include "nsFocusManager.h"
      16             : #include "mozilla/EventStateManager.h"
      17             : #include "mozilla/dom/Element.h"
      18             : #include "mozilla/dom/TabParent.h"
      19             : 
      20             : namespace mozilla {
      21             : namespace a11y {
      22             : 
      23           0 : FocusManager::FocusManager()
      24             : {
      25           0 : }
      26             : 
      27           0 : FocusManager::~FocusManager()
      28             : {
      29           0 : }
      30             : 
      31             : Accessible*
      32           0 : FocusManager::FocusedAccessible() const
      33             : {
      34           0 :   if (mActiveItem)
      35           0 :     return mActiveItem;
      36             : 
      37           0 :   nsINode* focusedNode = FocusedDOMNode();
      38           0 :   if (focusedNode) {
      39             :     DocAccessible* doc =
      40           0 :       GetAccService()->GetDocAccessible(focusedNode->OwnerDoc());
      41           0 :     return doc ? doc->GetAccessibleEvenIfNotInMapOrContainer(focusedNode) : nullptr;
      42             :   }
      43             : 
      44           0 :   return nullptr;
      45             : }
      46             : 
      47             : bool
      48           0 : FocusManager::IsFocused(const Accessible* aAccessible) const
      49             : {
      50           0 :   if (mActiveItem)
      51           0 :     return mActiveItem == aAccessible;
      52             : 
      53           0 :   nsINode* focusedNode = FocusedDOMNode();
      54           0 :   if (focusedNode) {
      55             :     // XXX: Before getting an accessible for node having a DOM focus make sure
      56             :     // they belong to the same document because it can trigger unwanted document
      57             :     // accessible creation for temporary about:blank document. Without this
      58             :     // peculiarity we would end up with plain implementation based on
      59             :     // FocusedAccessible() method call. Make sure this issue is fixed in
      60             :     // bug 638465.
      61           0 :     if (focusedNode->OwnerDoc() == aAccessible->GetNode()->OwnerDoc()) {
      62             :       DocAccessible* doc =
      63           0 :         GetAccService()->GetDocAccessible(focusedNode->OwnerDoc());
      64             :       return aAccessible ==
      65           0 :         (doc ? doc->GetAccessibleEvenIfNotInMapOrContainer(focusedNode) : nullptr);
      66             :     }
      67             :   }
      68           0 :   return false;
      69             : }
      70             : 
      71             : bool
      72           0 : FocusManager::IsFocusWithin(const Accessible* aContainer) const
      73             : {
      74           0 :   Accessible* child = FocusedAccessible();
      75           0 :   while (child) {
      76           0 :     if (child == aContainer)
      77           0 :       return true;
      78             : 
      79           0 :     child = child->Parent();
      80             :   }
      81           0 :   return false;
      82             : }
      83             : 
      84             : FocusManager::FocusDisposition
      85           0 : FocusManager::IsInOrContainsFocus(const Accessible* aAccessible) const
      86             : {
      87           0 :   Accessible* focus = FocusedAccessible();
      88           0 :   if (!focus)
      89           0 :     return eNone;
      90             : 
      91             :   // If focused.
      92           0 :   if (focus == aAccessible)
      93           0 :     return eFocused;
      94             : 
      95             :   // If contains the focus.
      96           0 :   Accessible* child = focus->Parent();
      97           0 :   while (child) {
      98           0 :     if (child == aAccessible)
      99           0 :       return eContainsFocus;
     100             : 
     101           0 :     child = child->Parent();
     102             :   }
     103             : 
     104             :   // If contained by focus.
     105           0 :   child = aAccessible->Parent();
     106           0 :   while (child) {
     107           0 :     if (child == focus)
     108           0 :       return eContainedByFocus;
     109             : 
     110           0 :     child = child->Parent();
     111             :   }
     112             : 
     113           0 :   return eNone;
     114             : }
     115             : 
     116             : void
     117           0 : FocusManager::NotifyOfDOMFocus(nsISupports* aTarget)
     118             : {
     119             : #ifdef A11Y_LOG
     120           0 :   if (logging::IsEnabled(logging::eFocus))
     121           0 :     logging::FocusNotificationTarget("DOM focus", "Target", aTarget);
     122             : #endif
     123             : 
     124           0 :   mActiveItem = nullptr;
     125             : 
     126           0 :   nsCOMPtr<nsINode> targetNode(do_QueryInterface(aTarget));
     127           0 :   if (targetNode) {
     128             :     DocAccessible* document =
     129           0 :       GetAccService()->GetDocAccessible(targetNode->OwnerDoc());
     130           0 :     if (document) {
     131             :       // Set selection listener for focused element.
     132           0 :       if (targetNode->IsElement())
     133           0 :         SelectionMgr()->SetControlSelectionListener(targetNode->AsElement());
     134             : 
     135             :       document->HandleNotification<FocusManager, nsINode>
     136           0 :         (this, &FocusManager::ProcessDOMFocus, targetNode);
     137             :     }
     138             :   }
     139           0 : }
     140             : 
     141             : void
     142           0 : FocusManager::NotifyOfDOMBlur(nsISupports* aTarget)
     143             : {
     144             : #ifdef A11Y_LOG
     145           0 :   if (logging::IsEnabled(logging::eFocus))
     146           0 :     logging::FocusNotificationTarget("DOM blur", "Target", aTarget);
     147             : #endif
     148             : 
     149           0 :   mActiveItem = nullptr;
     150             : 
     151             :   // If DOM document stays focused then fire accessible focus event to process
     152             :   // the case when no element within this DOM document will be focused.
     153           0 :   nsCOMPtr<nsINode> targetNode(do_QueryInterface(aTarget));
     154           0 :   if (targetNode && targetNode->OwnerDoc() == FocusedDOMDocument()) {
     155           0 :     nsIDocument* DOMDoc = targetNode->OwnerDoc();
     156             :     DocAccessible* document =
     157           0 :       GetAccService()->GetDocAccessible(DOMDoc);
     158           0 :     if (document) {
     159             :       // Clear selection listener for previously focused element.
     160           0 :       if (targetNode->IsElement())
     161           0 :         SelectionMgr()->ClearControlSelectionListener();
     162             : 
     163             :       document->HandleNotification<FocusManager, nsINode>
     164           0 :         (this, &FocusManager::ProcessDOMFocus, DOMDoc);
     165             :     }
     166             :   }
     167           0 : }
     168             : 
     169             : void
     170           0 : FocusManager::ActiveItemChanged(Accessible* aItem, bool aCheckIfActive)
     171             : {
     172             : #ifdef A11Y_LOG
     173           0 :   if (logging::IsEnabled(logging::eFocus))
     174           0 :     logging::FocusNotificationTarget("active item changed", "Item", aItem);
     175             : #endif
     176             : 
     177             :   // Nothing changed, happens for XUL trees and HTML selects.
     178           0 :   if (aItem && aItem == mActiveItem)
     179           0 :     return;
     180             : 
     181           0 :   mActiveItem = nullptr;
     182             : 
     183           0 :   if (aItem && aCheckIfActive) {
     184           0 :     Accessible* widget = aItem->ContainerWidget();
     185             : #ifdef A11Y_LOG
     186           0 :     if (logging::IsEnabled(logging::eFocus))
     187           0 :       logging::ActiveWidget(widget);
     188             : #endif
     189           0 :     if (!widget || !widget->IsActiveWidget() || !widget->AreItemsOperable())
     190           0 :       return;
     191             :   }
     192           0 :   mActiveItem = aItem;
     193             : 
     194             :   // If mActiveItem is null we may need to shift a11y focus back to a tab
     195             :   // document. For example, when combobox popup is closed, then
     196             :   // the focus should be moved back to the combobox.
     197           0 :   if (!mActiveItem && XRE_IsParentProcess()) {
     198           0 :     nsFocusManager* domfm = nsFocusManager::GetFocusManager();
     199           0 :     if (domfm) {
     200           0 :       nsIContent* focusedElm = domfm->GetFocusedContent();
     201           0 :       if (EventStateManager::IsRemoteTarget(focusedElm)) {
     202           0 :         dom::TabParent* tab = dom::TabParent::GetFrom(focusedElm);
     203           0 :         if (tab) {
     204           0 :           a11y::DocAccessibleParent* dap = tab->GetTopLevelDocAccessible();
     205           0 :           if (dap) {
     206           0 :             Unused << dap->SendRestoreFocus();
     207             :           }
     208             :         }
     209             :       }
     210             :     }
     211             :   }
     212             : 
     213             :   // If active item is changed then fire accessible focus event on it, otherwise
     214             :   // if there's no an active item then fire focus event to accessible having
     215             :   // DOM focus.
     216           0 :   Accessible* target = FocusedAccessible();
     217           0 :   if (target) {
     218           0 :     DispatchFocusEvent(target->Document(), target);
     219             :   }
     220             : }
     221             : 
     222             : void
     223           0 : FocusManager::ForceFocusEvent()
     224             : {
     225           0 :   nsINode* focusedNode = FocusedDOMNode();
     226           0 :   if (focusedNode) {
     227             :     DocAccessible* document =
     228           0 :       GetAccService()->GetDocAccessible(focusedNode->OwnerDoc());
     229           0 :     if (document) {
     230             :       document->HandleNotification<FocusManager, nsINode>
     231           0 :         (this, &FocusManager::ProcessDOMFocus, focusedNode);
     232             :     }
     233             :   }
     234           0 : }
     235             : 
     236             : void
     237           0 : FocusManager::DispatchFocusEvent(DocAccessible* aDocument,
     238             :                                  Accessible* aTarget)
     239             : {
     240           0 :   NS_PRECONDITION(aDocument, "No document for focused accessible!");
     241           0 :   if (aDocument) {
     242             :     RefPtr<AccEvent> event =
     243             :       new AccEvent(nsIAccessibleEvent::EVENT_FOCUS, aTarget,
     244           0 :                    eAutoDetect, AccEvent::eCoalesceOfSameType);
     245           0 :     aDocument->FireDelayedEvent(event);
     246             : 
     247             : #ifdef A11Y_LOG
     248           0 :     if (logging::IsEnabled(logging::eFocus))
     249           0 :       logging::FocusDispatched(aTarget);
     250             : #endif
     251             :   }
     252           0 : }
     253             : 
     254             : void
     255           0 : FocusManager::ProcessDOMFocus(nsINode* aTarget)
     256             : {
     257             : #ifdef A11Y_LOG
     258           0 :   if (logging::IsEnabled(logging::eFocus))
     259           0 :     logging::FocusNotificationTarget("process DOM focus", "Target", aTarget);
     260             : #endif
     261             : 
     262             :   DocAccessible* document =
     263           0 :     GetAccService()->GetDocAccessible(aTarget->OwnerDoc());
     264           0 :   if (!document)
     265           0 :     return;
     266             : 
     267           0 :   Accessible* target = document->GetAccessibleEvenIfNotInMapOrContainer(aTarget);
     268           0 :   if (target) {
     269             :     // Check if still focused. Otherwise we can end up with storing the active
     270             :     // item for control that isn't focused anymore.
     271           0 :     nsINode* focusedNode = FocusedDOMNode();
     272           0 :     if (!focusedNode)
     273           0 :       return;
     274             : 
     275             :     Accessible* DOMFocus =
     276           0 :       document->GetAccessibleEvenIfNotInMapOrContainer(focusedNode);
     277           0 :     if (target != DOMFocus)
     278           0 :       return;
     279             : 
     280           0 :     Accessible* activeItem = target->CurrentItem();
     281           0 :     if (activeItem) {
     282           0 :       mActiveItem = activeItem;
     283           0 :       target = activeItem;
     284             :     }
     285             : 
     286           0 :     DispatchFocusEvent(document, target);
     287             :   }
     288             : }
     289             : 
     290             : void
     291           0 : FocusManager::ProcessFocusEvent(AccEvent* aEvent)
     292             : {
     293           0 :   NS_PRECONDITION(aEvent->GetEventType() == nsIAccessibleEvent::EVENT_FOCUS,
     294             :                   "Focus event is expected!");
     295             : 
     296             :   // Emit focus event if event target is the active item. Otherwise then check
     297             :   // if it's still focused and then update active item and emit focus event.
     298           0 :   Accessible* target = aEvent->GetAccessible();
     299           0 :   if (target != mActiveItem) {
     300             : 
     301             :     // Check if still focused. Otherwise we can end up with storing the active
     302             :     // item for control that isn't focused anymore.
     303           0 :     DocAccessible* document = aEvent->Document();
     304           0 :     nsINode* focusedNode = FocusedDOMNode();
     305           0 :     if (!focusedNode)
     306           0 :       return;
     307             : 
     308             :     Accessible* DOMFocus =
     309           0 :       document->GetAccessibleEvenIfNotInMapOrContainer(focusedNode);
     310           0 :     if (target != DOMFocus)
     311           0 :       return;
     312             : 
     313           0 :     Accessible* activeItem = target->CurrentItem();
     314           0 :     if (activeItem) {
     315           0 :       mActiveItem = activeItem;
     316           0 :       target = activeItem;
     317             :     }
     318             :   }
     319             : 
     320             :   // Fire menu start/end events for ARIA menus.
     321           0 :   if (target->IsARIARole(nsGkAtoms::menuitem)) {
     322             :     // The focus was moved into menu.
     323           0 :     Accessible* ARIAMenubar = nullptr;
     324           0 :     for (Accessible* parent = target->Parent(); parent; parent = parent->Parent()) {
     325           0 :       if (parent->IsARIARole(nsGkAtoms::menubar)) {
     326           0 :         ARIAMenubar = parent;
     327           0 :         break;
     328             :       }
     329             : 
     330             :       // Go up in the parent chain of the menu hierarchy.
     331           0 :       if (!parent->IsARIARole(nsGkAtoms::menuitem) &&
     332           0 :           !parent->IsARIARole(nsGkAtoms::menu)) {
     333           0 :         break;
     334             :       }
     335             :     }
     336             : 
     337           0 :     if (ARIAMenubar != mActiveARIAMenubar) {
     338             :       // Leaving ARIA menu. Fire menu_end event on current menubar.
     339           0 :       if (mActiveARIAMenubar) {
     340             :         RefPtr<AccEvent> menuEndEvent =
     341             :           new AccEvent(nsIAccessibleEvent::EVENT_MENU_END, mActiveARIAMenubar,
     342           0 :                        aEvent->FromUserInput());
     343           0 :         nsEventShell::FireEvent(menuEndEvent);
     344             :       }
     345             : 
     346           0 :       mActiveARIAMenubar = ARIAMenubar;
     347             : 
     348             :       // Entering ARIA menu. Fire menu_start event.
     349           0 :       if (mActiveARIAMenubar) {
     350             :         RefPtr<AccEvent> menuStartEvent =
     351             :           new AccEvent(nsIAccessibleEvent::EVENT_MENU_START,
     352           0 :                        mActiveARIAMenubar, aEvent->FromUserInput());
     353           0 :         nsEventShell::FireEvent(menuStartEvent);
     354             :       }
     355             :     }
     356           0 :   } else if (mActiveARIAMenubar) {
     357             :     // Focus left a menu. Fire menu_end event.
     358             :     RefPtr<AccEvent> menuEndEvent =
     359             :       new AccEvent(nsIAccessibleEvent::EVENT_MENU_END, mActiveARIAMenubar,
     360           0 :                    aEvent->FromUserInput());
     361           0 :     nsEventShell::FireEvent(menuEndEvent);
     362             : 
     363           0 :     mActiveARIAMenubar = nullptr;
     364             :   }
     365             : 
     366             : #ifdef A11Y_LOG
     367           0 :   if (logging::IsEnabled(logging::eFocus))
     368           0 :     logging::FocusNotificationTarget("fire focus event", "Target", target);
     369             : #endif
     370             : 
     371             :   // Reset cached caret value. The cache will be updated upon processing the
     372             :   // next caret move event. This ensures that we will return the correct caret
     373             :   // offset before the caret move event is handled.
     374           0 :   SelectionMgr()->ResetCaretOffset();
     375             : 
     376             :   RefPtr<AccEvent> focusEvent =
     377           0 :     new AccEvent(nsIAccessibleEvent::EVENT_FOCUS, target, aEvent->FromUserInput());
     378           0 :   nsEventShell::FireEvent(focusEvent);
     379             : 
     380             :   // Fire scrolling_start event when the document receives the focus if it has
     381             :   // an anchor jump. If an accessible within the document receive the focus
     382             :   // then null out the anchor jump because it no longer applies.
     383           0 :   DocAccessible* targetDocument = target->Document();
     384           0 :   Accessible* anchorJump = targetDocument->AnchorJump();
     385           0 :   if (anchorJump) {
     386           0 :     if (target == targetDocument) {
     387             :       // XXX: bug 625699, note in some cases the node could go away before we
     388             :       // we receive focus event, for example if the node is removed from DOM.
     389           0 :       nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_SCROLLING_START,
     390           0 :                               anchorJump, aEvent->FromUserInput());
     391             :     }
     392           0 :     targetDocument->SetAnchorJump(nullptr);
     393             :   }
     394             : }
     395             : 
     396             : nsINode*
     397           0 : FocusManager::FocusedDOMNode() const
     398             : {
     399           0 :   nsFocusManager* DOMFocusManager = nsFocusManager::GetFocusManager();
     400           0 :   nsIContent* focusedElm = DOMFocusManager->GetFocusedContent();
     401             : 
     402             :   // No focus on remote target elements like xul:browser having DOM focus and
     403             :   // residing in chrome process because it means an element in content process
     404             :   // keeps the focus.
     405           0 :   if (focusedElm) {
     406           0 :     if (EventStateManager::IsRemoteTarget(focusedElm)) {
     407           0 :       return nullptr;
     408             :     }
     409           0 :     return focusedElm;
     410             :   }
     411             : 
     412             :   // Otherwise the focus can be on DOM document.
     413           0 :   nsPIDOMWindowOuter* focusedWnd = DOMFocusManager->GetFocusedWindow();
     414           0 :   return focusedWnd ? focusedWnd->GetExtantDoc() : nullptr;
     415             : }
     416             : 
     417             : nsIDocument*
     418           0 : FocusManager::FocusedDOMDocument() const
     419             : {
     420           0 :   nsINode* focusedNode = FocusedDOMNode();
     421           0 :   return focusedNode ? focusedNode->OwnerDoc() : nullptr;
     422             : }
     423             : 
     424             : } // namespace a11y
     425             : } // namespace mozilla

Generated by: LCOV version 1.13