dom/base/nsFocusManager.h

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #ifndef nsFocusManager_h___
     7 #define nsFocusManager_h___
     9 #include "nsCycleCollectionParticipant.h"
    10 #include "nsIDocument.h"
    11 #include "nsIFocusManager.h"
    12 #include "nsIObserver.h"
    13 #include "nsIWidget.h"
    14 #include "nsWeakReference.h"
    15 #include "mozilla/Attributes.h"
    17 #define FOCUSMETHOD_MASK 0xF000
    18 #define FOCUSMETHODANDRING_MASK 0xF0F000
    20 #define FOCUSMANAGER_CONTRACTID "@mozilla.org/focus-manager;1"
    22 class nsIContent;
    23 class nsIDocShellTreeItem;
    24 class nsPIDOMWindow;
    26 struct nsDelayedBlurOrFocusEvent;
    28 /**
    29  * The focus manager keeps track of where the focus is, that is, the node
    30  * which receives key events.
    31  */
    33 class nsFocusManager MOZ_FINAL : public nsIFocusManager,
    34                                  public nsIObserver,
    35                                  public nsSupportsWeakReference
    36 {
    37   typedef mozilla::widget::InputContextAction InputContextAction;
    39 public:
    41   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsFocusManager, nsIFocusManager)
    42   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
    43   NS_DECL_NSIOBSERVER
    44   NS_DECL_NSIFOCUSMANAGER
    46   // called to initialize and stop the focus manager at startup and shutdown
    47   static nsresult Init();
    48   static void Shutdown();
    50   /**
    51    * Retrieve the single focus manager.
    52    */
    53   static nsFocusManager* GetFocusManager() { return sInstance; }
    55   /**
    56    * A faster version of nsIFocusManager::GetFocusedElement, returning a
    57    * raw nsIContent pointer (instead of having AddRef-ed nsIDOMElement
    58    * pointer filled in to an out-parameter).
    59    */
    60   nsIContent* GetFocusedContent() { return mFocusedContent; }
    62   /**
    63    * Return a focused window. Version of nsIFocusManager::GetFocusedWindow.
    64    */
    65   nsPIDOMWindow* GetFocusedWindow() const { return mFocusedWindow; }
    67   /**
    68    * Return an active window. Version of nsIFocusManager::GetActiveWindow.
    69    */
    70   nsPIDOMWindow* GetActiveWindow() const { return mActiveWindow; }
    72   /**
    73    * Called when content has been removed.
    74    */
    75   nsresult ContentRemoved(nsIDocument* aDocument, nsIContent* aContent);
    77   /**
    78    * Called when mouse button down event handling is started and finished.
    79    */
    80   void SetMouseButtonDownHandlingDocument(nsIDocument* aDocument)
    81   {
    82     NS_ASSERTION(!aDocument || !mMouseDownEventHandlingDocument,
    83                  "Some mouse button down events are nested?");
    84     mMouseDownEventHandlingDocument = aDocument;
    85   }
    87   /**
    88    * Update the caret with current mode (whether in caret browsing mode or not).
    89    */
    90   void UpdateCaretForCaretBrowsingMode();
    92   /**
    93    * Returns the content node that would be focused if aWindow was in an
    94    * active window. This will traverse down the frame hierarchy, starting at
    95    * the given window aWindow. Sets aFocusedWindow to the window with the
    96    * document containing aFocusedContent. If no element is focused,
    97    * aFocusedWindow may be still be set -- this means that the document is
    98    * focused but no element within it is focused.
    99    *
   100    * aWindow and aFocusedWindow must both be non-null.
   101    */
   102   static nsIContent* GetFocusedDescendant(nsPIDOMWindow* aWindow, bool aDeep,
   103                                           nsPIDOMWindow** aFocusedWindow);
   105   /**
   106    * Returns the content node that focus will be redirected to if aContent was
   107    * focused. This is used for the special case of certain XUL elements such
   108    * as textboxes which redirect focus to an anonymous child.
   109    *
   110    * aContent must be non-null.
   111    *
   112    * XXXndeakin this should be removed eventually but I want to do that as
   113    * followup work.
   114    */
   115   static nsIContent* GetRedirectedFocus(nsIContent* aContent);
   117   /**
   118    * Returns an InputContextAction cause for aFlags.
   119    */
   120   static InputContextAction::Cause GetFocusMoveActionCause(uint32_t aFlags);
   122   static bool sMouseFocusesFormControl;
   124   static void MarkUncollectableForCCGeneration(uint32_t aGeneration);
   125 protected:
   127   nsFocusManager();
   128   ~nsFocusManager();
   130   /**
   131    * Ensure that the widget associated with the currently focused window is
   132    * focused at the widget level.
   133    */
   134   void EnsureCurrentWidgetFocused();
   136   /**
   137    * Blur whatever is currently focused and focus aNewContent. aFlags is a
   138    * bitmask of the flags defined in nsIFocusManager. If aFocusChanged is
   139    * true, then the focus has actually shifted and the caret position will be
   140    * updated to the new focus, aNewContent will be scrolled into view (unless
   141    * a flag disables this) and the focus method for the window will be updated.
   142    * If aAdjustWidget is false, don't change the widget focus state.
   143    *
   144    * All actual focus changes must use this method to do so. (as opposed
   145    * to those that update the focus in an inactive window for instance).
   146    */
   147   void SetFocusInner(nsIContent* aNewContent, int32_t aFlags,
   148                      bool aFocusChanged, bool aAdjustWidget);
   150   /**
   151    * Returns true if aPossibleAncestor is the same as aWindow or an
   152    * ancestor of aWindow.
   153    */
   154   bool IsSameOrAncestor(nsPIDOMWindow* aPossibleAncestor,
   155                           nsPIDOMWindow* aWindow);
   157   /**
   158    * Returns the window that is the lowest common ancestor of both aWindow1
   159    * and aWindow2, or null if they share no common ancestor.
   160    */
   161   already_AddRefed<nsPIDOMWindow> GetCommonAncestor(nsPIDOMWindow* aWindow1,
   162                                                     nsPIDOMWindow* aWindow2);
   164   /**
   165    * When aNewWindow is focused, adjust the ancestors of aNewWindow so that they
   166    * also have their corresponding frames focused. Thus, one can start at
   167    * the active top-level window and navigate down the currently focused
   168    * elements for each frame in the tree to get to aNewWindow.
   169    */
   170   void AdjustWindowFocus(nsPIDOMWindow* aNewWindow, bool aCheckPermission);
   172   /**
   173    * Returns true if aWindow is visible.
   174    */
   175   bool IsWindowVisible(nsPIDOMWindow* aWindow);
   177   /**
   178    * Returns true if aContent is a root element and not focusable.
   179    * I.e., even if aContent is editable root element, this returns true when
   180    * the document is in designMode.
   181    *
   182    * @param aContent must not be null and must be in a document.
   183    */
   184   bool IsNonFocusableRoot(nsIContent* aContent);
   186   /**
   187    * Checks and returns aContent if it may be focused, another content node if
   188    * the focus should be retargeted at another node, or null if the node
   189    * cannot be focused. aFlags are the flags passed to SetFocus and similar
   190    * methods.
   191    *
   192    * An element is focusable if it is in a document, the document isn't in
   193    * print preview mode and the element has an nsIFrame where the
   194    * CheckIfFocusable method returns true. For <area> elements, there is no
   195    * frame, so only the IsFocusable method on the content node must be
   196    * true.
   197    */
   198   nsIContent* CheckIfFocusable(nsIContent* aContent, uint32_t aFlags);
   200   /**
   201    * Blurs the currently focused element. Returns false if another element was
   202    * focused as a result. This would mean that the caller should not proceed
   203    * with a pending call to Focus. Normally, true would be returned.
   204    *
   205    * The currently focused element within aWindowToClear will be cleared.
   206    * aWindowToClear may be null, which means that no window is cleared. This
   207    * will be the case, for example, when lowering a window, as we want to fire
   208    * a blur, but not actually change what element would be focused, so that
   209    * the same element will be focused again when the window is raised.
   210    *
   211    * aAncestorWindowToFocus should be set to the common ancestor of the window
   212    * that is being blurred and the window that is going to focused, when
   213    * switching focus to a sibling window.
   214    *
   215    * aIsLeavingDocument should be set to true if the document/window is being
   216    * blurred as well. Document/window blur events will be fired. It should be
   217    * false if an element is the same document is about to be focused.
   218    *
   219    * If aAdjustWidget is false, don't change the widget focus state.
   220    */
   221   bool Blur(nsPIDOMWindow* aWindowToClear,
   222               nsPIDOMWindow* aAncestorWindowToFocus,
   223               bool aIsLeavingDocument,
   224               bool aAdjustWidget);
   226   /**
   227    * Focus an element in the active window and child frame.
   228    *
   229    * aWindow is the window containing the element aContent to focus.
   230    *
   231    * aFlags is the flags passed to the various focus methods in
   232    * nsIFocusManager.
   233    *
   234    * aIsNewDocument should be true if a new document is being focused.
   235    * Document/window focus events will be fired.
   236    *
   237    * aFocusChanged should be true if a new content node is being focused, so
   238    * the focused content will be scrolled into view and the caret position
   239    * will be updated. If false is passed, then a window is simply being
   240    * refocused, for instance, due to a window being raised, or a tab is being
   241    * switched to.
   242    *
   243    * If aFocusChanged is true, then the focus has moved to a new location.
   244    * Otherwise, the focus is just being updated because the window was
   245    * raised.
   246    *
   247    * aWindowRaised should be true if the window is being raised. In this case,
   248    * command updaters will not be called.
   249    *
   250    * If aAdjustWidget is false, don't change the widget focus state.
   251    */
   252   void Focus(nsPIDOMWindow* aWindow,
   253              nsIContent* aContent,
   254              uint32_t aFlags,
   255              bool aIsNewDocument,
   256              bool aFocusChanged,
   257              bool aWindowRaised,
   258              bool aAdjustWidget);
   260   /**
   261    * Fires a focus or blur event at aTarget.
   262    *
   263    * aType should be either NS_FOCUS_CONTENT or NS_BLUR_CONTENT. For blur
   264    * events, aFocusMethod should normally be non-zero.
   265    *
   266    * aWindowRaised should only be true if called from WindowRaised.
   267    */
   268   void SendFocusOrBlurEvent(uint32_t aType,
   269                             nsIPresShell* aPresShell,
   270                             nsIDocument* aDocument,
   271                             nsISupports* aTarget,
   272                             uint32_t aFocusMethod,
   273                             bool aWindowRaised,
   274                             bool aIsRefocus = false);
   276   /**
   277    * Scrolls aContent into view unless the FLAG_NOSCROLL flag is set.
   278    */
   279   void ScrollIntoView(nsIPresShell* aPresShell,
   280                       nsIContent* aContent,
   281                       uint32_t aFlags);
   283   /**
   284    * Raises the top-level window aWindow at the widget level.
   285    */
   286   void RaiseWindow(nsPIDOMWindow* aWindow);
   288   /**
   289    * Updates the caret positon and visibility to match the focus.
   290    *
   291    * aMoveCaretToFocus should be true to move the caret to aContent.
   292    *
   293    * aUpdateVisibility should be true to update whether the caret is
   294    * visible or not.
   295    */
   296   void UpdateCaret(bool aMoveCaretToFocus,
   297                    bool aUpdateVisibility,
   298                    nsIContent* aContent);
   300   /**
   301    * Helper method to move the caret to the focused element aContent.
   302    */
   303   void MoveCaretToFocus(nsIPresShell* aPresShell, nsIContent* aContent);
   305   /**
   306    * Makes the caret visible or not, depending on aVisible.
   307    */
   308   nsresult SetCaretVisible(nsIPresShell* aPresShell,
   309                            bool aVisible,
   310                            nsIContent* aContent);
   313   // the remaining functions are used for tab key and document-navigation
   315   /**
   316    * Retrieves the start and end points of the current selection for
   317    * aDocument and stores them in aStartContent and aEndContent.
   318    */
   319   nsresult GetSelectionLocation(nsIDocument* aDocument,
   320                                 nsIPresShell* aPresShell,
   321                                 nsIContent **aStartContent,
   322                                 nsIContent **aEndContent);
   324   /**
   325    * Helper function for MoveFocus which determines the next element
   326    * to move the focus to and returns it in aNextContent.
   327    *
   328    * aWindow is the window to adjust the focus within, and aStart is
   329    * the element to start navigation from. For tab key navigation,
   330    * this should be the currently focused element.
   331    *
   332    * aType is the type passed to MoveFocus. If aNoParentTraversal is set,
   333    * navigation is not done to parent documents and iteration returns to the
   334    * beginning (or end) of the starting document.
   335    */
   336   nsresult DetermineElementToMoveFocus(nsPIDOMWindow* aWindow,
   337                                        nsIContent* aStart,
   338                                        int32_t aType, bool aNoParentTraversal,
   339                                        nsIContent** aNextContent);
   341   /**
   342    * Retrieve the next tabbable element within a document, using focusability
   343    * and tabindex to determine the tab order. The element is returned in
   344    * aResultContent.
   345    *
   346    * aRootContent is the root node -- nodes above this will not be examined.
   347    * Typically this will be the root node of a document, but could also be
   348    * a popup node.
   349    *
   350    * aOriginalStartContent is the content which was originally the starting
   351    * node, in the case of recursive or looping calls.
   352    *
   353    * aStartContent is the starting point for this call of this method.
   354    * If aStartContent doesn't have visual representation, the next content
   355    * object, which does have a primary frame, will be used as a start.
   356    * If that content object is focusable, the method may return it.
   357    *
   358    * aForward should be true for forward navigation or false for backward
   359    * navigation.
   360    *
   361    * aCurrentTabIndex is the current tabindex.
   362    *
   363    * aIgnoreTabIndex to ignore the current tabindex and find the element
   364    * irrespective or the tab index. This will be true when a selection is
   365    * active, since we just want to focus the next element in tree order
   366    * from where the selection is. Similarly, if the starting element isn't
   367    * focusable, since it doesn't really have a defined tab index.
   368    */
   369   nsresult GetNextTabbableContent(nsIPresShell* aPresShell,
   370                                   nsIContent* aRootContent,
   371                                   nsIContent* aOriginalStartContent,
   372                                   nsIContent* aStartContent,
   373                                   bool aForward,
   374                                   int32_t aCurrentTabIndex,
   375                                   bool aIgnoreTabIndex,
   376                                   nsIContent** aResultContent);
   378   /**
   379    * Get the next tabbable image map area and returns it.
   380    *
   381    * aForward should be true for forward navigation or false for backward
   382    * navigation.
   383    *
   384    * aCurrentTabIndex is the current tabindex.
   385    *
   386    * aImageContent is the image.
   387    *
   388    * aStartContent is the current image map area.
   389    */
   390   nsIContent* GetNextTabbableMapArea(bool aForward,
   391                                      int32_t aCurrentTabIndex,
   392                                      nsIContent* aImageContent,
   393                                      nsIContent* aStartContent);
   395   /**
   396    * Return the next valid tabindex value after aCurrentTabIndex, if aForward
   397    * is true, or the previous tabindex value if aForward is false. aParent is
   398    * the node from which to start looking for tab indicies.
   399    */
   400   int32_t GetNextTabIndex(nsIContent* aParent,
   401                           int32_t aCurrentTabIndex,
   402                           bool aForward);
   404   /**
   405    * Retrieves and returns the root node from aDocument to be focused. Will
   406    * return null if the root node cannot be focused. There are several reasons
   407    * for this:
   408    *
   409    * - if aIsForDocNavigation is true, and aWindow is in an <iframe>.
   410    * - if aIsForDocNavigation is false, and aWindow is a chrome shell.
   411    * - if aCheckVisibility is true and the aWindow is not visible.
   412    * - if aDocument is a frameset document.
   413    */
   414   nsIContent* GetRootForFocus(nsPIDOMWindow* aWindow,
   415                               nsIDocument* aDocument,
   416                               bool aIsForDocNavigation,
   417                               bool aCheckVisibility);
   419   /**
   420    * Get the last docshell child of aItem and return it in aResult.
   421    */
   422   void GetLastDocShell(nsIDocShellTreeItem* aItem,
   423                        nsIDocShellTreeItem** aResult);
   425   /**
   426    * Get the next docshell child of aItem and return it in aResult.
   427    */
   428   void GetNextDocShell(nsIDocShellTreeItem* aItem,
   429                        nsIDocShellTreeItem** aResult);
   431   /**
   432    * Get the previous docshell child of aItem and return it in aResult.
   433    */
   434   void GetPreviousDocShell(nsIDocShellTreeItem* aItem,
   435                            nsIDocShellTreeItem** aResult);
   437   /**
   438    * Determine the first panel with focusable content in document tab order
   439    * from the given document. aForward indicates the direction to scan. If
   440    * aCurrentPopup is set to a panel, the next or previous popup after
   441    * aCurrentPopup after it is used. If aCurrentPopup is null, then the first
   442    * or last popup is used. If a panel has no focusable content, it is skipped.
   443    * Null is returned if no panel is open or no open panel contains a focusable
   444    * element.
   445    */
   446   nsIContent* GetNextTabbablePanel(nsIDocument* aDocument, nsIFrame* aCurrentPopup, bool aForward);
   448   /**
   449    * Get the tabbable next document from aStartContent or, if null, the
   450    * currently focused frame if aForward is true, or the previously tabbable
   451    * document if aForward is false. If this document is a chrome or frameset
   452    * document, returns the first focusable element within this document,
   453    * otherwise, returns the root node of the document.
   454    *
   455    *
   456    * Panels with focusable content are also placed in the cycling order, just
   457    * after the document containing that panel.
   458    *
   459    * This method would be used for document navigation, which is typically
   460    * invoked by pressing F6.
   461    */
   462   nsIContent* GetNextTabbableDocument(nsIContent* aStartContent, bool aForward);
   464   /**
   465    * Retreives a focusable element within the current selection of aWindow.
   466    * Currently, this only detects links.
   467    *  
   468    * This is used when MoveFocus is called with a type of MOVEFOCUS_CARET,
   469    * which is used, for example, to focus links as the caret is moved over
   470    * them.
   471    */
   472   void GetFocusInSelection(nsPIDOMWindow* aWindow,
   473                            nsIContent* aStartSelection,
   474                            nsIContent* aEndSelection,
   475                            nsIContent** aFocusedContent);
   477 private:
   478   // Notify that the focus state of aContent has changed.  Note that
   479   // we need to pass in whether the window should show a focus ring
   480   // before the SetFocusedNode call on it happened when losing focus
   481   // and after the SetFocusedNode call when gaining focus, which is
   482   // why that information needs to be an explicit argument instead of
   483   // just passing in the window and asking it whether it should show
   484   // focus rings: in the losing focus case that information could be
   485   // wrong..
   486   static void NotifyFocusStateChange(nsIContent* aContent,
   487                                      bool aWindowShouldShowFocusRing,
   488                                      bool aGettingFocus);
   490   void SetFocusedWindowInternal(nsPIDOMWindow* aWindow);
   492   // the currently active and front-most top-most window
   493   nsCOMPtr<nsPIDOMWindow> mActiveWindow;
   495   // the child or top-level window that is currently focused. This window will
   496   // either be the same window as mActiveWindow or a descendant of it.
   497   // Except during shutdown use SetFocusedWindowInternal to set mFocusedWindow!
   498   nsCOMPtr<nsPIDOMWindow> mFocusedWindow;
   500   // the currently focused content, which is always inside mFocusedWindow. This
   501   // is a cached copy of the mFocusedWindow's current content. This may be null
   502   // if no content is focused.
   503   nsCOMPtr<nsIContent> mFocusedContent;
   505   // these fields store a content node temporarily while it is being focused
   506   // or blurred to ensure that a recursive call doesn't refire the same event.
   507   // They will always be cleared afterwards.
   508   nsCOMPtr<nsIContent> mFirstBlurEvent;
   509   nsCOMPtr<nsIContent> mFirstFocusEvent;
   511   // keep track of a window while it is being lowered
   512   nsCOMPtr<nsPIDOMWindow> mWindowBeingLowered;
   514   // synchronized actions cannot be interrupted with events, so queue these up
   515   // and fire them later.
   516   nsTArray<nsDelayedBlurOrFocusEvent> mDelayedBlurFocusEvents;
   518   // A document which is handling a mouse button down event.
   519   // When a mouse down event process is finished, ESM sets focus to the target
   520   // content.  Therefore, while DOM event handlers are handling mouse down
   521   // events, the handlers should be able to steal focus from any elements even
   522   // if focus is in chrome content.  So, if this isn't nullptr and the caller
   523   // can access the document node, the caller should succeed in moving focus.
   524   nsCOMPtr<nsIDocument> mMouseDownEventHandlingDocument;
   526   static bool sTestMode;
   528   // the single focus manager
   529   static nsFocusManager* sInstance;
   530 };
   532 nsresult
   533 NS_NewFocusManager(nsIFocusManager** aResult);
   535 #endif

mercurial