summaryrefslogtreecommitdiffstats
path: root/dom/base/nsFocusManager.h
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--dom/base/nsFocusManager.h1027
1 files changed, 1027 insertions, 0 deletions
diff --git a/dom/base/nsFocusManager.h b/dom/base/nsFocusManager.h
new file mode 100644
index 0000000000..4dde93c6ea
--- /dev/null
+++ b/dom/base/nsFocusManager.h
@@ -0,0 +1,1027 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsFocusManager_h___
+#define nsFocusManager_h___
+
+#include "nsCycleCollectionParticipant.h"
+#include "nsIContent.h"
+#include "mozilla/dom/Document.h"
+#include "nsIFocusManager.h"
+#include "nsIObserver.h"
+#include "nsWeakReference.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/StaticPtr.h"
+
+#define FOCUSMANAGER_CONTRACTID "@mozilla.org/focus-manager;1"
+
+class nsIContent;
+class nsPIDOMWindowOuter;
+
+namespace mozilla {
+class PresShell;
+namespace dom {
+class Element;
+struct FocusOptions;
+class BrowserParent;
+class ContentChild;
+class ContentParent;
+} // namespace dom
+} // namespace mozilla
+
+struct nsDelayedBlurOrFocusEvent;
+
+/**
+ * The focus manager keeps track of where the focus is, that is, the node
+ * which receives key events.
+ */
+
+class nsFocusManager final : public nsIFocusManager,
+ public nsIObserver,
+ public nsSupportsWeakReference {
+ using InputContextAction = mozilla::widget::InputContextAction;
+ using Document = mozilla::dom::Document;
+ friend class mozilla::dom::ContentChild;
+ friend class mozilla::dom::ContentParent;
+
+ public:
+ NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsFocusManager, nsIFocusManager)
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+ NS_DECL_NSIFOCUSMANAGER
+
+ // called to initialize and stop the focus manager at startup and shutdown
+ static nsresult Init();
+ static void Shutdown();
+
+ // Simple helper to call SetFocusedWindow on the instance.
+ //
+ // This raises the window and switches to the tab as needed.
+ MOZ_CAN_RUN_SCRIPT static void FocusWindow(
+ nsPIDOMWindowOuter* aWindow, mozilla::dom::CallerType aCallerType);
+
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY static void PrefChanged(const char* aPref,
+ void* aSelf);
+ MOZ_CAN_RUN_SCRIPT void PrefChanged(const char* aPref);
+
+ /**
+ * Retrieve the single focus manager.
+ */
+ static nsFocusManager* GetFocusManager() { return sInstance; }
+
+ /**
+ * A faster version of nsIFocusManager::GetFocusedElement, returning a
+ * raw Element pointer (instead of having AddRef-ed Element
+ * pointer filled in to an out-parameter).
+ */
+ mozilla::dom::Element* GetFocusedElement() { return mFocusedElement; }
+ static mozilla::dom::Element* GetFocusedElementStatic() {
+ return sInstance ? sInstance->GetFocusedElement() : nullptr;
+ }
+
+ /**
+ * Returns true if aContent currently has focus.
+ */
+ bool IsFocused(nsIContent* aContent);
+
+ /**
+ * Returns true if test mode is enabled.
+ */
+ bool IsTestMode();
+
+ /**
+ * Return a focused window. Version of nsIFocusManager::GetFocusedWindow.
+ */
+ nsPIDOMWindowOuter* GetFocusedWindow() const { return mFocusedWindow; }
+
+ /**
+ * In the chrome process, retrieves the BrowsingContext corresponding
+ * to GetFocusedWindow(). In a content process, retrieves the
+ * focused BrowsingContext, which may not belong to this process.
+ */
+ mozilla::dom::BrowsingContext* GetFocusedBrowsingContext() const {
+ if (XRE_IsParentProcess()) {
+ if (mFocusedWindow) {
+ return mFocusedWindow->GetBrowsingContext();
+ }
+ return nullptr;
+ }
+ return mFocusedBrowsingContextInContent;
+ }
+
+ /**
+ * Returns whether the given browsing context is in the active window.
+ */
+ bool IsInActiveWindow(mozilla::dom::BrowsingContext*) const;
+
+ /**
+ * Return an active window. Version of nsIFocusManager::GetActiveWindow.
+ */
+ nsPIDOMWindowOuter* GetActiveWindow() const { return mActiveWindow; }
+
+ /**
+ * In the chrome process, retrieves the BrowsingContext corresponding
+ * to GetActiveWindow(). In a content process, retrieves the
+ * BrowsingContext of the top-level Web content in the active tab if
+ * in the same process as the caller or nullptr otherwise.
+ */
+ mozilla::dom::BrowsingContext* GetActiveBrowsingContext() const {
+ if (XRE_IsParentProcess()) {
+ if (mActiveWindow) {
+ return mActiveWindow->GetBrowsingContext();
+ }
+ return nullptr;
+ }
+ return mActiveBrowsingContextInContent;
+ }
+
+ /**
+ * Called when content has been removed.
+ */
+ MOZ_CAN_RUN_SCRIPT nsresult ContentRemoved(Document* aDocument,
+ nsIContent* aContent);
+
+ void NeedsFlushBeforeEventHandling(mozilla::dom::Element* aElement) {
+ if (mFocusedElement == aElement) {
+ mEventHandlingNeedsFlush = true;
+ }
+ }
+
+ bool CanSkipFocus(nsIContent* aContent);
+
+ MOZ_CAN_RUN_SCRIPT void FlushBeforeEventHandlingIfNeeded(
+ nsIContent* aContent) {
+ if (mEventHandlingNeedsFlush) {
+ nsCOMPtr<Document> doc = aContent->GetComposedDoc();
+ if (doc) {
+ mEventHandlingNeedsFlush = false;
+ doc->FlushPendingNotifications(mozilla::FlushType::Layout);
+ }
+ }
+ }
+
+ /**
+ * Update the caret with current mode (whether in caret browsing mode or not).
+ */
+ MOZ_CAN_RUN_SCRIPT void UpdateCaretForCaretBrowsingMode();
+
+ /** @see nsIFocusManager.getLastFocusMethod() */
+ uint32_t GetLastFocusMethod(nsPIDOMWindowOuter*) const;
+
+ /**
+ * Returns the content node that would be focused if aWindow was in an
+ * active window. This will traverse down the frame hierarchy, starting at
+ * the given window aWindow. Sets aFocusedWindow to the window with the
+ * document containing aFocusedContent. If no element is focused,
+ * aFocusedWindow may be still be set -- this means that the document is
+ * focused but no element within it is focused.
+ *
+ * aWindow, aFocusIsOutOfProcess, aFocusedWindow must all be non-null.
+ */
+ enum SearchRange {
+ // Return focused content in aWindow. So, aFocusedWindow is always aWindow.
+ eOnlyCurrentWindow,
+ // Return focused content in aWindow or one of all sub windows.
+ eIncludeAllDescendants,
+ // Return focused content in aWindow or one of visible sub windows.
+ eIncludeVisibleDescendants,
+ };
+ static mozilla::dom::Element* GetFocusedDescendant(
+ nsPIDOMWindowOuter* aWindow, SearchRange aSearchRange,
+ nsPIDOMWindowOuter** aFocusedWindow);
+
+ /**
+ * Helper function for MoveFocus which determines the next element
+ * to move the focus to and returns it in aNextContent.
+ *
+ * aWindow is the window to adjust the focus within, and aStart is
+ * the element to start navigation from. For tab key navigation,
+ * this should be the currently focused element.
+ *
+ * aType is the type passed to MoveFocus. If aNoParentTraversal is set,
+ * navigation is not done to parent documents and iteration returns to the
+ * beginning (or end) of the starting document.
+ *
+ * aNavigateByKey to move focus by keyboard as a side effect of computing the
+ * next target.
+ */
+ MOZ_CAN_RUN_SCRIPT nsresult DetermineElementToMoveFocus(
+ nsPIDOMWindowOuter* aWindow, nsIContent* aStart, int32_t aType,
+ bool aNoParentTraversal, bool aNavigateByKey, nsIContent** aNextContent);
+
+ /**
+ * Setter for focusedWindow with CallerType
+ */
+ MOZ_CAN_RUN_SCRIPT nsresult SetFocusedWindowWithCallerType(
+ mozIDOMWindowProxy* aWindowToFocus, mozilla::dom::CallerType aCallerType);
+
+ /**
+ * Given an element, which must be the focused element, activate the remote
+ * frame it embeds, if any.
+ */
+ void ActivateRemoteFrameIfNeeded(mozilla::dom::Element&, uint64_t aActionId);
+
+ /**
+ * Raises the top-level window aWindow at the widget level.
+ */
+ MOZ_CAN_RUN_SCRIPT void RaiseWindow(nsPIDOMWindowOuter* aWindow,
+ mozilla::dom::CallerType aCallerType,
+ uint64_t aActionId);
+
+ /**
+ * Called when a window has been raised.
+ */
+ MOZ_CAN_RUN_SCRIPT void WindowRaised(mozIDOMWindowProxy* aWindow,
+ uint64_t aActionId);
+
+ /**
+ * Called when a window has been lowered.
+ */
+ MOZ_CAN_RUN_SCRIPT void WindowLowered(mozIDOMWindowProxy* aWindow,
+ uint64_t aActionId);
+
+ /**
+ * Called when a new document in a window is shown.
+ *
+ * If aNeedsFocus is true, then focus events are expected to be fired on the
+ * window if this window is in the focused window chain.
+ */
+ MOZ_CAN_RUN_SCRIPT void WindowShown(mozIDOMWindowProxy* aWindow,
+ bool aNeedsFocus);
+
+ /**
+ * Called when a document in a window has been hidden or otherwise can no
+ * longer accept focus.
+ */
+ MOZ_CAN_RUN_SCRIPT void WindowHidden(mozIDOMWindowProxy* aWindow,
+ uint64_t aActionId);
+
+ /**
+ * Fire any events that have been delayed due to synchronized actions.
+ */
+ MOZ_CAN_RUN_SCRIPT void FireDelayedEvents(Document* aDocument);
+
+ void WasNuked(nsPIDOMWindowOuter* aWindow);
+
+ static uint32_t ProgrammaticFocusFlags(
+ const mozilla::dom::FocusOptions& aOptions);
+
+ /**
+ * Returns an InputContextAction cause for aFlags.
+ */
+ static InputContextAction::Cause GetFocusMoveActionCause(uint32_t aFlags);
+
+ /**
+ * Notify of re-focus to same element.
+ *
+ * aElement is focused element.
+ */
+ MOZ_CAN_RUN_SCRIPT void NotifyOfReFocus(mozilla::dom::Element& aElement);
+
+ static void MarkUncollectableForCCGeneration(uint32_t aGeneration);
+
+ struct BlurredElementInfo {
+ const mozilla::OwningNonNull<mozilla::dom::Element> mElement;
+
+ explicit BlurredElementInfo(mozilla::dom::Element&);
+ ~BlurredElementInfo();
+ };
+
+ protected:
+ nsFocusManager();
+ ~nsFocusManager();
+
+ /**
+ * Ensure that the widget associated with the currently focused window is
+ * focused at the widget level.
+ */
+ void EnsureCurrentWidgetFocused(mozilla::dom::CallerType aCallerType);
+
+ /**
+ * Activate or deactivate the window and send the activate/deactivate events.
+ */
+ void ActivateOrDeactivate(nsPIDOMWindowOuter* aWindow, bool aActive);
+
+ /**
+ * Blur whatever is currently focused and focus aNewContent. aFlags is a
+ * bitmask of the flags defined in nsIFocusManager. If aFocusChanged is
+ * true, then the focus has actually shifted and the caret position will be
+ * updated to the new focus, aNewContent will be scrolled into view (unless
+ * a flag disables this) and the focus method for the window will be updated.
+ * If aAdjustWidget is false, don't change the widget focus state.
+ *
+ * All actual focus changes must use this method to do so. (as opposed
+ * to those that update the focus in an inactive window for instance).
+ *
+ * Returns Nothing() if we end up not trying to focus the element,
+ * otherwise returns the generated action id.
+ */
+ MOZ_CAN_RUN_SCRIPT mozilla::Maybe<uint64_t> SetFocusInner(
+ mozilla::dom::Element* aNewContent, int32_t aFlags, bool aFocusChanged,
+ bool aAdjustWidget);
+
+ /**
+ * Returns true if aPossibleAncestor is the same as aWindow or an
+ * ancestor of aWindow.
+ */
+ bool IsSameOrAncestor(nsPIDOMWindowOuter* aPossibleAncestor,
+ nsPIDOMWindowOuter* aWindow) const;
+ bool IsSameOrAncestor(nsPIDOMWindowOuter* aPossibleAncestor,
+ mozilla::dom::BrowsingContext* aContext) const;
+ bool IsSameOrAncestor(mozilla::dom::BrowsingContext* aPossibleAncestor,
+ nsPIDOMWindowOuter* aWindow) const;
+
+ public:
+ bool IsSameOrAncestor(mozilla::dom::BrowsingContext* aPossibleAncestor,
+ mozilla::dom::BrowsingContext* aContext) const;
+
+ protected:
+ /**
+ * Returns the window that is the lowest common ancestor of both aWindow
+ * and aContext, or null if they share no common ancestor.
+ */
+ mozilla::dom::BrowsingContext* GetCommonAncestor(
+ nsPIDOMWindowOuter* aWindow, mozilla::dom::BrowsingContext* aContext);
+
+ /**
+ * When aBrowsingContext is focused, adjust the ancestors of aBrowsingContext
+ * so that they also have their corresponding frames focused. Thus, one can
+ * start at the active top-level window and navigate down the currently
+ * focused elements for each frame in the tree to get to aBrowsingContext.
+ */
+ MOZ_CAN_RUN_SCRIPT bool AdjustInProcessWindowFocus(
+ mozilla::dom::BrowsingContext* aBrowsingContext, bool aCheckPermission,
+ bool aIsVisible, uint64_t aActionId);
+ MOZ_CAN_RUN_SCRIPT void AdjustWindowFocus(
+ mozilla::dom::BrowsingContext* aBrowsingContext, bool aCheckPermission,
+ bool aIsVisible, uint64_t aActionId);
+
+ /**
+ * Returns true if aWindow is visible.
+ */
+ bool IsWindowVisible(nsPIDOMWindowOuter* aWindow);
+
+ /**
+ * Returns true if aContent is a root element and not focusable.
+ * I.e., even if aContent is editable root element, this returns true when
+ * the document is in designMode.
+ *
+ * @param aContent must not be null and must be in a document.
+ */
+ bool IsNonFocusableRoot(nsIContent* aContent);
+
+ /**
+ * First flushes the pending notifications to ensure the PresShell and frames
+ * are updated.
+ * Checks and returns aElement if it may be focused, another element node if
+ * the focus should be retargeted at another node, or null if the node
+ * cannot be focused. aFlags are the flags passed to SetFocus and similar
+ * methods.
+ *
+ * An element is focusable if it is in a document, the document isn't in
+ * print preview mode and the element has an nsIFrame where the
+ * IsFocusable method returns true. For <area> elements, there is no
+ * frame, so only the IsFocusable method on the content node must be
+ * true.
+ */
+ MOZ_CAN_RUN_SCRIPT mozilla::dom::Element* FlushAndCheckIfFocusable(
+ mozilla::dom::Element* aElement, uint32_t aFlags);
+
+ /**
+ * Blurs the currently focused element. Returns false if another element was
+ * focused as a result. This would mean that the caller should not proceed
+ * with a pending call to Focus. Normally, true would be returned.
+ *
+ * The currently focused element within aBrowsingContextToClear will be
+ * cleared. aBrowsingContextToClear may be null, which means that no window is
+ * cleared. This will be the case, for example, when lowering a window, as we
+ * want to fire a blur, but not actually change what element would be focused,
+ * so that the same element will be focused again when the window is raised.
+ *
+ * aAncestorBrowsingContextToFocus should be set to the common ancestor of the
+ * window that is being blurred and the window that is going to focused, when
+ * switching focus to a sibling window.
+ *
+ * aIsLeavingDocument should be set to true if the document/window is being
+ * blurred as well. Document/window blur events will be fired. It should be
+ * false if an element is the same document is about to be focused.
+ *
+ * If aAdjustWidget is false, don't change the widget focus state.
+ */
+ MOZ_CAN_RUN_SCRIPT bool Blur(
+ mozilla::dom::BrowsingContext* aBrowsingContextToClear,
+ mozilla::dom::BrowsingContext* aAncestorBrowsingContextToFocus,
+ bool aIsLeavingDocument, bool aAdjustWidget, bool aRemainActive,
+ uint64_t aActionId, mozilla::dom::Element* aElementToFocus = nullptr);
+ MOZ_CAN_RUN_SCRIPT void BlurFromOtherProcess(
+ mozilla::dom::BrowsingContext* aFocusedBrowsingContext,
+ mozilla::dom::BrowsingContext* aBrowsingContextToClear,
+ mozilla::dom::BrowsingContext* aAncestorBrowsingContextToFocus,
+ bool aIsLeavingDocument, bool aAdjustWidget, uint64_t aActionId);
+ MOZ_CAN_RUN_SCRIPT bool BlurImpl(
+ mozilla::dom::BrowsingContext* aBrowsingContextToClear,
+ mozilla::dom::BrowsingContext* aAncestorBrowsingContextToFocus,
+ bool aIsLeavingDocument, bool aAdjustWidget, bool aRemainActive,
+ mozilla::dom::Element* aElementToFocus, uint64_t aActionId);
+
+ /**
+ * Focus an element in the active window and child frame.
+ *
+ * aWindow is the window containing the element aContent to focus.
+ *
+ * aFlags is the flags passed to the various focus methods in
+ * nsIFocusManager.
+ *
+ * aIsNewDocument should be true if a new document is being focused.
+ * Document/window focus events will be fired.
+ *
+ * aFocusChanged should be true if a new content node is being focused, so
+ * the focused content will be scrolled into view and the caret position
+ * will be updated. If false is passed, then a window is simply being
+ * refocused, for instance, due to a window being raised, or a tab is being
+ * switched to.
+ *
+ * If aFocusChanged is true, then the focus has moved to a new location.
+ * Otherwise, the focus is just being updated because the window was
+ * raised.
+ *
+ * aWindowRaised should be true if the window is being raised. In this case,
+ * command updaters will not be called.
+ *
+ * If aAdjustWidget is false, don't change the widget focus state.
+ */
+ MOZ_CAN_RUN_SCRIPT void Focus(
+ nsPIDOMWindowOuter* aWindow, mozilla::dom::Element* aContent,
+ uint32_t aFlags, bool aIsNewDocument, bool aFocusChanged,
+ bool aWindowRaised, bool aAdjustWidget, uint64_t aActionId,
+ const mozilla::Maybe<BlurredElementInfo>& = mozilla::Nothing());
+
+ /**
+ * Send a focus or blur event at aTarget. It may be added to the delayed
+ * event queue if the document is suppressing events.
+ *
+ * aEventMessage should be either eFocus or eBlur.
+ *
+ * aWindowRaised should only be true if called from WindowRaised.
+ */
+ MOZ_CAN_RUN_SCRIPT void SendFocusOrBlurEvent(
+ mozilla::EventMessage aEventMessage, mozilla::PresShell* aPresShell,
+ Document* aDocument, nsISupports* aTarget, bool aWindowRaised,
+ bool aIsRefocus = false,
+ mozilla::dom::EventTarget* aRelatedTarget = nullptr);
+ /**
+ * Fire a focus or blur event at aTarget.
+ *
+ * aEventMessage should be either eFocus or eBlur.
+ * For blur events, aFocusMethod should normally be non-zero.
+ *
+ * aWindowRaised should only be true if called from WindowRaised.
+ */
+ MOZ_CAN_RUN_SCRIPT void FireFocusOrBlurEvent(
+ mozilla::EventMessage aEventMessage, mozilla::PresShell* aPresShell,
+ nsISupports* aTarget, bool aWindowRaised, bool aIsRefocus = false,
+ mozilla::dom::EventTarget* aRelatedTarget = nullptr);
+
+ /**
+ * Fire a focusin or focusout event
+ *
+ * aEventMessage should be either eFocusIn or eFocusOut.
+ *
+ * aTarget is the content the event will fire on (the object that gained
+ * focus for focusin, the object blurred for focusout).
+ *
+ * aCurrentFocusedWindow is the window focused before the focus/blur event
+ * was fired.
+ *
+ * aCurrentFocusedContent is the content focused before the focus/blur event
+ * was fired.
+ *
+ * aRelatedTarget is the content related to the event (the object
+ * losing focus for focusin, the object getting focus for focusout).
+ */
+ MOZ_CAN_RUN_SCRIPT void FireFocusInOrOutEvent(
+ mozilla::EventMessage aEventMessage, mozilla::PresShell* aPresShell,
+ nsISupports* aTarget, nsPIDOMWindowOuter* aCurrentFocusedWindow,
+ nsIContent* aCurrentFocusedContent,
+ mozilla::dom::EventTarget* aRelatedTarget = nullptr);
+
+ /**
+ * Scrolls aContent into view unless the FLAG_NOSCROLL flag is set.
+ */
+ MOZ_CAN_RUN_SCRIPT
+ void ScrollIntoView(mozilla::PresShell* aPresShell, nsIContent* aContent,
+ uint32_t aFlags);
+
+ /**
+ * Updates the caret positon and visibility to match the focus.
+ *
+ * aMoveCaretToFocus should be true to move the caret to aContent.
+ *
+ * aUpdateVisibility should be true to update whether the caret is
+ * visible or not.
+ */
+ MOZ_CAN_RUN_SCRIPT void UpdateCaret(bool aMoveCaretToFocus,
+ bool aUpdateVisibility,
+ nsIContent* aContent);
+
+ /**
+ * Helper method to move the caret to the focused element aContent.
+ */
+ MOZ_CAN_RUN_SCRIPT void MoveCaretToFocus(mozilla::PresShell* aPresShell,
+ nsIContent* aContent);
+
+ /**
+ * Makes the caret visible or not, depending on aVisible.
+ */
+ nsresult SetCaretVisible(mozilla::PresShell* aPresShell, bool aVisible,
+ nsIContent* aContent);
+
+ // the remaining functions are used for tab key and document-navigation
+
+ /**
+ * Retrieves the start and end points of the current selection for
+ * aDocument and stores them in aStartContent and aEndContent.
+ */
+ nsresult GetSelectionLocation(Document* aDocument,
+ mozilla::PresShell* aPresShell,
+ nsIContent** aStartContent,
+ nsIContent** aEndContent);
+
+ /**
+ * Retrieve the next tabbable element in scope owned by aOwner, using
+ * focusability and tabindex to determine the tab order.
+ *
+ * aOwner is the owner of scope to search in.
+ *
+ * aStartContent is the starting point for this call of this method.
+ *
+ * aOriginalStartContent is the initial starting point for sequential
+ * navigation.
+ *
+ * aForward should be true for forward navigation or false for backward
+ * navigation.
+ *
+ * aCurrentTabIndex is the current tabindex.
+ *
+ * aIgnoreTabIndex to ignore the current tabindex and find the element
+ * irrespective or the tab index.
+ *
+ * aForDocumentNavigation informs whether we're navigating only through
+ * documents.
+ *
+ * aSkipOwner to skip owner while searching. The flag is set when caller is
+ * |GetNextTabbableContent| in order to let caller handle owner.
+ *
+ * NOTE:
+ * Consider the method searches downwards in flattened subtree
+ * rooted at aOwner.
+ */
+ MOZ_CAN_RUN_SCRIPT nsIContent* GetNextTabbableContentInScope(
+ nsIContent* aOwner, nsIContent* aStartContent,
+ nsIContent* aOriginalStartContent, bool aForward,
+ int32_t aCurrentTabIndex, bool aIgnoreTabIndex,
+ bool aForDocumentNavigation, bool aNavigateByKey, bool aSkipOwner);
+
+ /**
+ * Retrieve the next tabbable element in scope including aStartContent
+ * and the scope's ancestor scopes, using focusability and tabindex to
+ * determine the tab order.
+ *
+ * aStartOwner is the scope owner of the aStartContent.
+ *
+ * aStartContent an in/out paremeter. It as input is the starting point
+ * for this call of this method; as output it is the shadow host in
+ * light DOM if the next tabbable element is not found in shadow DOM,
+ * in order to continue searching in light DOM.
+ *
+ * aOriginalStartContent is the initial starting point for sequential
+ * navigation.
+ *
+ * aForward should be true for forward navigation or false for backward
+ * navigation.
+ *
+ * aCurrentTabIndex returns tab index of shadow host in light DOM if the
+ * next tabbable element is not found in shadow DOM, in order to continue
+ * searching in light DOM.
+ *
+ * aIgnoreTabIndex to ignore the current tabindex and find the element
+ * irrespective or the tab index.
+ *
+ * aForDocumentNavigation informs whether we're navigating only through
+ * documents.
+ *
+ * aNavigateByKey to move focus by keyboard as a side effect of computing the
+ * next target.
+ *
+ * NOTE:
+ * Consider the method searches upwards in all shadow host- or slot-rooted
+ * flattened subtrees that contains aStartContent as non-root, except
+ * the flattened subtree rooted at shadow host in light DOM.
+ */
+ MOZ_CAN_RUN_SCRIPT nsIContent* GetNextTabbableContentInAncestorScopes(
+ nsIContent* aStartOwner, nsCOMPtr<nsIContent>& aStartContent /* inout */,
+ nsIContent* aOriginalStartContent, bool aForward,
+ int32_t* aCurrentTabIndex, bool* aIgnoreTabIndex,
+ bool aForDocumentNavigation, bool aNavigateByKey);
+
+ /**
+ * Retrieve the next tabbable element within a document, using focusability
+ * and tabindex to determine the tab order. The element is returned in
+ * aResultContent.
+ *
+ * aRootContent is the root node -- nodes above this will not be examined.
+ * Typically this will be the root node of a document, but could also be
+ * a popup node.
+ *
+ * aOriginalStartContent is the content which was originally the starting
+ * node, in the case of recursive or looping calls.
+ *
+ * aStartContent is the starting point for this call of this method.
+ * If aStartContent doesn't have visual representation, the next content
+ * object, which does have a primary frame, will be used as a start.
+ * If that content object is focusable, the method may return it.
+ *
+ * aForward should be true for forward navigation or false for backward
+ * navigation.
+ *
+ * aCurrentTabIndex is the current tabindex.
+ *
+ * aIgnoreTabIndex to ignore the current tabindex and find the element
+ * irrespective or the tab index. This will be true when a selection is
+ * active, since we just want to focus the next element in tree order
+ * from where the selection is. Similarly, if the starting element isn't
+ * focusable, since it doesn't really have a defined tab index.
+ *
+ * aNavigateByKey to move focus by keyboard as a side effect of computing the
+ * next target.
+ */
+ MOZ_CAN_RUN_SCRIPT nsresult GetNextTabbableContent(
+ mozilla::PresShell* aPresShell, nsIContent* aRootContent,
+ nsIContent* aOriginalStartContent, nsIContent* aStartContent,
+ bool aForward, int32_t aCurrentTabIndex, bool aIgnoreTabIndex,
+ bool aForDocumentNavigation, bool aNavigateByKey,
+ nsIContent** aResultContent);
+
+ /**
+ * Get the next tabbable image map area and returns it.
+ *
+ * aForward should be true for forward navigation or false for backward
+ * navigation.
+ *
+ * aCurrentTabIndex is the current tabindex.
+ *
+ * aImageContent is the image.
+ *
+ * aStartContent is the current image map area.
+ */
+ nsIContent* GetNextTabbableMapArea(bool aForward, int32_t aCurrentTabIndex,
+ mozilla::dom::Element* aImageContent,
+ nsIContent* aStartContent);
+
+ /**
+ * Return the next valid tabindex value after aCurrentTabIndex, if aForward
+ * is true, or the previous tabindex value if aForward is false. aParent is
+ * the node from which to start looking for tab indicies.
+ */
+ int32_t GetNextTabIndex(nsIContent* aParent, int32_t aCurrentTabIndex,
+ bool aForward);
+
+ /**
+ * Focus the first focusable content within the document with a root node of
+ * aRootContent. For content documents, this will be aRootContent itself, but
+ * for chrome documents, this will locate the next focusable content.
+ */
+ MOZ_CAN_RUN_SCRIPT nsresult FocusFirst(mozilla::dom::Element* aRootContent,
+ nsIContent** aNextContent);
+
+ /**
+ * Retrieves and returns the root node from aDocument to be focused. Will
+ * return null if the root node cannot be focused. There are several reasons
+ * for this:
+ *
+ * - if aForDocumentNavigation is false and aWindow is a chrome shell.
+ * - if aCheckVisibility is true and the aWindow is not visible.
+ * - if aDocument is a frameset document.
+ */
+ mozilla::dom::Element* GetRootForFocus(nsPIDOMWindowOuter* aWindow,
+ Document* aDocument,
+ bool aForDocumentNavigation,
+ bool aCheckVisibility);
+
+ /**
+ * Retrieves and returns the root node as with GetRootForFocus but only if
+ * aContent is a frame with a valid child document.
+ */
+ mozilla::dom::Element* GetRootForChildDocument(nsIContent* aContent);
+
+ /**
+ * Retreives a focusable element within the current selection of aWindow.
+ * Currently, this only detects links.
+ *
+ * This is used when MoveFocus is called with a type of MOVEFOCUS_CARET,
+ * which is used, for example, to focus links as the caret is moved over
+ * them.
+ */
+ void GetFocusInSelection(nsPIDOMWindowOuter* aWindow,
+ nsIContent* aStartSelection,
+ nsIContent* aEndSelection,
+ nsIContent** aFocusedContent);
+
+ private:
+ // Notify that the focus state of aElement has changed. Note that we need to
+ // pass in whether the window should show a focus ring before the
+ // SetFocusedNode call on it happened when losing focus and after the
+ // SetFocusedNode call when gaining focus, which is why that information needs
+ // to be an explicit argument instead of just passing in the window and asking
+ // it whether it should show focus rings: in the losing focus case that
+ // information could be wrong.
+ //
+ // aShouldShowFocusRing is only relevant if aGettingFocus is true.
+ static void NotifyFocusStateChange(mozilla::dom::Element* aElement,
+ mozilla::dom::Element* aElementToFocus,
+ int32_t aFlags, bool aGettingFocus,
+ bool aShouldShowFocusRing);
+
+ void SetFocusedWindowInternal(nsPIDOMWindowOuter* aWindow, uint64_t aActionId,
+ bool aSyncBrowsingContext = true);
+
+ MOZ_CAN_RUN_SCRIPT bool TryDocumentNavigation(nsIContent* aCurrentContent,
+ bool* aCheckSubDocument,
+ nsIContent** aResultContent);
+
+ MOZ_CAN_RUN_SCRIPT bool TryToMoveFocusToSubDocument(
+ nsIContent* aCurrentContent, nsIContent* aOriginalStartContent,
+ bool aForward, bool aForDocumentNavigation, bool aNavigateByKey,
+ nsIContent** aResultContent);
+
+ // Sets the focused BrowsingContext and, if appropriate, syncs it to
+ // other processes.
+ void SetFocusedBrowsingContext(mozilla::dom::BrowsingContext* aContext,
+ uint64_t aActionId);
+
+ // Content-only
+ // Called when receiving an IPC message about another process setting
+ // the focused BrowsingContext.
+ void SetFocusedBrowsingContextFromOtherProcess(
+ mozilla::dom::BrowsingContext* aContext, uint64_t aActionId);
+
+ // Chrome-only
+ // When returning true, sets the chrome process notion of what
+ // BrowsingContext is focused in content. When returning false,
+ // ignores the attempt to set as out-of-sequence.
+ bool SetFocusedBrowsingContextInChrome(
+ mozilla::dom::BrowsingContext* aContext, uint64_t aActionId);
+
+ void InsertNewFocusActionId(uint64_t aActionId);
+
+ bool ProcessPendingActiveBrowsingContextActionId(uint64_t aActionId,
+ bool aSettingToNonNull);
+
+ bool ProcessPendingFocusedBrowsingContextActionId(uint64_t aActionId);
+
+ public:
+ // Chrome-only
+ // Gets the chrome process notion of what BrowsingContext is focused
+ // in content.
+ mozilla::dom::BrowsingContext* GetFocusedBrowsingContextInChrome();
+
+ // Chrome-only
+ // Notifies the focus manager that BrowsingContext::Detach was called
+ // on a BrowsingContext so that pointers to it can be forgotten.
+ void BrowsingContextDetached(mozilla::dom::BrowsingContext* aContext);
+
+ private:
+ // Content-only
+ // Sets the BrowsingContext corresponding to top-level Web content
+ // in the frontmost tab if focus is in Web content.
+ void SetActiveBrowsingContextInContent(
+ mozilla::dom::BrowsingContext* aContext, uint64_t aActionId);
+
+ // Content-only
+ // Receives notification of another process setting the top-level Web
+ // content as being in the frontmost tab with focus in Web content.
+ void SetActiveBrowsingContextFromOtherProcess(
+ mozilla::dom::BrowsingContext* aContext, uint64_t aActionId);
+
+ // Content-only
+ // Receives notification that another process determined that focus
+ // moved to chrome so a particular BrowsingContext is no longer the
+ // "active" one.
+ void UnsetActiveBrowsingContextFromOtherProcess(
+ mozilla::dom::BrowsingContext* aContext, uint64_t aActionId);
+
+ // Content-only
+ // Receives a notification from parent that this content process's
+ // attempt to set the active browsing context was late and the
+ // prevailing browsing context is instead the second argument of
+ // this method call. This should be ignored if the first argument
+ // doesn't match the latest action id associated with setting the
+ // active browsing context in this process, because in that case,
+ // this revision is late.
+ void ReviseActiveBrowsingContext(uint64_t aOldActionId,
+ mozilla::dom::BrowsingContext* aContext,
+ uint64_t aNewActionId);
+
+ // Receives a notification from parent that this content process's
+ // attempt to set the focused browsing context was late and the
+ // prevailing browsing context is instead the second argument of
+ // this method call. This should be ignored if the first argument
+ // doesn't match the latest action id associated with setting the
+ // active browsing context in this process, because in that case,
+ // this revision is late.
+ void ReviseFocusedBrowsingContext(uint64_t aOldActionId,
+ mozilla::dom::BrowsingContext* aContext,
+ uint64_t aNewActionId);
+
+ // Chrome-only
+ // Sets the chrome process notion of what content believes to be
+ // the top-level BrowsingContext in the frontmost tab when focus
+ // is in Web content.
+ // Returns true if set and false if ignored.
+ bool SetActiveBrowsingContextInChrome(mozilla::dom::BrowsingContext* aContext,
+ uint64_t aActionId);
+
+ public:
+ // Chrome-only
+ // Gets the chrome process notion of what content believes to be
+ // the top-level BrowsingContext in the frontmost tab when focus
+ // is in Web content.
+ mozilla::dom::BrowsingContext* GetActiveBrowsingContextInChrome();
+
+ uint64_t GetActionIdForActiveBrowsingContextInChrome() const;
+
+ uint64_t GetActionIdForFocusedBrowsingContextInChrome() const;
+
+ static uint64_t GenerateFocusActionId();
+
+ // This function works very similar to
+ // https://html.spec.whatwg.org/#get-the-focusable-area
+ static mozilla::dom::Element* GetTheFocusableArea(
+ mozilla::dom::Element* aTarget, uint32_t aFlags);
+
+ private:
+ // In the chrome process, the currently active and front-most top-most
+ // window. Not supposed to be used in a meaningful way in content
+ // processes. For legacy reasons, this exists as a separate field
+ // instead of being derived from mFocusedWindow when needed, because
+ // the defined relation that mActiveWindow is supposed to be the same
+ // as or ancestor of mFocusedWindow is temporarily broken when a
+ // window is being raised or lowered.
+ nsCOMPtr<nsPIDOMWindowOuter> mActiveWindow;
+
+ // In a content process, the BrowsingContext corresponding to top-level
+ // Web content in the active tab or nullptr if focus is not in a
+ // BrowsingContextGroup that this process participates in. Synced
+ // across processes in a BrowsingContextGroup. This field exists
+ // separately from mFocusedBrowsingContextInContent instead of being being
+ // derived from it, because for legacy reasons the relation
+ // mFocusedBrowsingContextInContent->Top() == mActiveBrowsingContextInContent
+ // is temporarily broken when a window is being raised or lowered.
+ // Not supposed to be used in a meaningful way in the chrome process.
+ RefPtr<mozilla::dom::BrowsingContext> mActiveBrowsingContextInContent;
+
+ // If this content process set mActiveBrowsingContextInContent, this
+ // field holds the corresponding actionId so that
+ // mActiveBrowsingContextInContent can be revised of the parent rejects
+ // the update. This field is used for accepting revisions only if nothing
+ // else has updated mActiveBrowsingContextInContent before the revision
+ // arrives.
+ uint64_t mActionIdForActiveBrowsingContextInContent;
+
+ uint64_t mActionIdForActiveBrowsingContextInChrome;
+
+ // If this content process set mFocusedBrowsingContextInContent, this
+ // field holds the corresponding actionId so that
+ // mFocusedBrowsingContextInContent can be revised of the parent rejects
+ // the update. This field is used for accepting revisions only if nothing
+ // else has updated mFocusedBrowsingContextInContent before the revision
+ // arrives.
+ uint64_t mActionIdForFocusedBrowsingContextInContent;
+
+ uint64_t mActionIdForFocusedBrowsingContextInChrome;
+
+ // Whether or not mActiveBrowsingContextInContent was set from another process
+ // or from this process.
+ bool mActiveBrowsingContextInContentSetFromOtherProcess;
+
+ // This is the chrome process notion of content's
+ // mActiveBrowsingContextInContent. Avoiding field reuse for different
+ // semantics in different process types to make it easier to catch bugs.
+ RefPtr<mozilla::dom::BrowsingContext> mActiveBrowsingContextInChrome;
+
+ // the child or top-level window that is currently focused. In the chrome
+ // process, when a window isn't being raised or lowered, this window will
+ // either be the same window as mActiveWindow or a descendant of it.
+ // Except during shutdown use SetFocusedWindowInternal to set mFocusedWindow!
+ nsCOMPtr<nsPIDOMWindowOuter> mFocusedWindow;
+
+ // The focused BrowsingContext if this is a chrome process and focus is
+ // in chrome or if this is a content process and focus is in Web content
+ // in this BrowsingContextGroup. nullptr otherwise.
+ // Except during shutdown, must be set via SetFocusedWindowInternal which
+ // calls SetFocusedBrowsingContext or if the value is coming in via IPC
+ // via SetFocusedBrowsingContextFromOtherProcess.
+ RefPtr<mozilla::dom::BrowsingContext> mFocusedBrowsingContextInContent;
+
+ // This is the chrome process notion of content's
+ // mFocusedBrowsingContextInContent. Avoiding field reuse for different
+ // semantics in different process types to make it easier to catch bugs.
+ RefPtr<mozilla::dom::BrowsingContext> mFocusedBrowsingContextInChrome;
+
+ // the currently focused content if in-process or the XUL browser in which
+ // Web content focus resides. Always inside mFocusedWindow. When a window
+ // isn't being raised or lowered, this is a cached copy of the
+ // mFocusedWindow's current content. This may be null if no content is
+ // focused.
+ RefPtr<mozilla::dom::Element> mFocusedElement;
+
+ // these fields store a content node temporarily while it is being focused
+ // or blurred to ensure that a recursive call doesn't refire the same event.
+ // They will always be cleared afterwards.
+ RefPtr<mozilla::dom::Element> mFirstBlurEvent;
+ RefPtr<mozilla::dom::Element> mFirstFocusEvent;
+
+ // keep track of a window while it is being lowered
+ nsCOMPtr<nsPIDOMWindowOuter> mWindowBeingLowered;
+
+ // synchronized actions cannot be interrupted with events, so queue these up
+ // and fire them later.
+ nsTArray<nsDelayedBlurOrFocusEvent> mDelayedBlurFocusEvents;
+
+ // Array of focus action ids for which we haven't seen an active browsing
+ // context set yet. As set is allowed to overwrite an unset. Therefore,
+ // an unset removes earlier ids but not the matching id. A set removes
+ // earlier ids and the matching id.
+ //
+ // Conceptually, active browsing context shouldn't have to exist as a
+ // field, because it should be possible to always derive it from the
+ // focused browsing context. Unfortunately, for legacy reasons, this
+ // is not the case while a window is being raised or lowered.
+ //
+ // Conceptually, it should be possible for the parent to manage the
+ // active browsing context. Unfortunately, for legacy reasons, the
+ // code for setting the active browsing context needs to reside in
+ // the content process to retain the existing and test-passing code
+ // flow.
+ //
+ // This, obviously, raises the issue of content processes racing to
+ // set the active browsing context. In particular, there is a pattern
+ // that the parent initiates actions that cause multiple content
+ // processes to mutate the active browsing context at almost the
+ // same time. When two native browser windows change order, the
+ // lowering isn't distinguished from the case of lowering the
+ // entire app. For this reason, the owner of the previous active
+ // browsing context tries to unset it and at almost the same time
+ // the another content process sets a new active browsing context.
+ // If the IPC messages for these unset and set actions were to
+ // arrive in the wrong order, this could get in the wrong state.
+ //
+ // To address this issue, the parent manages an authortative order
+ // of attempts to (un)set the active browsing context using the
+ // array mPendingActiveBrowsingContextActions.
+ //
+ // A process reserves a slot in the order by calling
+ // GenerateFocusActionId(). Per one call to GenerateFocusActionId(),
+ // there may be at most one action to set the active browsing context
+ // to a new value. There may be logically prior attempts to unset it
+ // (i.e. set it to nullptr). That is, if there are both attempts to
+ // unset and set the active browsing context with the same action id,
+ // the attempt to set to a non-null value wins.
+ //
+ // The completion of an action from reserting the slot in the order
+ // and actually performing the setting of the active browsing context
+ // may span multiple processes and IPC messages.
+ //
+ // The at-most-once property is not asserted, because the process
+ // claiming the position in the order and the process setting the
+ // active browsing context with that actionId may be different, and
+ // the act of using an actionId to set the active browsing context
+ // is used to delete stale items from the array to avoid excessive
+ // growth of the array.
+ nsTArray<uint64_t> mPendingActiveBrowsingContextActions;
+
+ // Like mPendingActiveBrowsingContextActions but for the focused
+ // browsing context.
+ nsTArray<uint64_t> mPendingFocusedBrowsingContextActions;
+
+ // If set to true, layout of the document of the event target should be
+ // flushed before handling focus depending events.
+ bool mEventHandlingNeedsFlush;
+
+ static bool sTestMode;
+
+ // Process-specific counter for maintaining the prosess-specific
+ // uniqueness of actionIds.
+ static uint64_t sFocusActionCounter;
+
+ // the single focus manager
+ static mozilla::StaticRefPtr<nsFocusManager> sInstance;
+};
+
+nsresult NS_NewFocusManager(nsIFocusManager** aResult);
+
+#endif