1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/base/nsFocusManager.h Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,535 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#ifndef nsFocusManager_h___ 1.10 +#define nsFocusManager_h___ 1.11 + 1.12 +#include "nsCycleCollectionParticipant.h" 1.13 +#include "nsIDocument.h" 1.14 +#include "nsIFocusManager.h" 1.15 +#include "nsIObserver.h" 1.16 +#include "nsIWidget.h" 1.17 +#include "nsWeakReference.h" 1.18 +#include "mozilla/Attributes.h" 1.19 + 1.20 +#define FOCUSMETHOD_MASK 0xF000 1.21 +#define FOCUSMETHODANDRING_MASK 0xF0F000 1.22 + 1.23 +#define FOCUSMANAGER_CONTRACTID "@mozilla.org/focus-manager;1" 1.24 + 1.25 +class nsIContent; 1.26 +class nsIDocShellTreeItem; 1.27 +class nsPIDOMWindow; 1.28 + 1.29 +struct nsDelayedBlurOrFocusEvent; 1.30 + 1.31 +/** 1.32 + * The focus manager keeps track of where the focus is, that is, the node 1.33 + * which receives key events. 1.34 + */ 1.35 + 1.36 +class nsFocusManager MOZ_FINAL : public nsIFocusManager, 1.37 + public nsIObserver, 1.38 + public nsSupportsWeakReference 1.39 +{ 1.40 + typedef mozilla::widget::InputContextAction InputContextAction; 1.41 + 1.42 +public: 1.43 + 1.44 + NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsFocusManager, nsIFocusManager) 1.45 + NS_DECL_CYCLE_COLLECTING_ISUPPORTS 1.46 + NS_DECL_NSIOBSERVER 1.47 + NS_DECL_NSIFOCUSMANAGER 1.48 + 1.49 + // called to initialize and stop the focus manager at startup and shutdown 1.50 + static nsresult Init(); 1.51 + static void Shutdown(); 1.52 + 1.53 + /** 1.54 + * Retrieve the single focus manager. 1.55 + */ 1.56 + static nsFocusManager* GetFocusManager() { return sInstance; } 1.57 + 1.58 + /** 1.59 + * A faster version of nsIFocusManager::GetFocusedElement, returning a 1.60 + * raw nsIContent pointer (instead of having AddRef-ed nsIDOMElement 1.61 + * pointer filled in to an out-parameter). 1.62 + */ 1.63 + nsIContent* GetFocusedContent() { return mFocusedContent; } 1.64 + 1.65 + /** 1.66 + * Return a focused window. Version of nsIFocusManager::GetFocusedWindow. 1.67 + */ 1.68 + nsPIDOMWindow* GetFocusedWindow() const { return mFocusedWindow; } 1.69 + 1.70 + /** 1.71 + * Return an active window. Version of nsIFocusManager::GetActiveWindow. 1.72 + */ 1.73 + nsPIDOMWindow* GetActiveWindow() const { return mActiveWindow; } 1.74 + 1.75 + /** 1.76 + * Called when content has been removed. 1.77 + */ 1.78 + nsresult ContentRemoved(nsIDocument* aDocument, nsIContent* aContent); 1.79 + 1.80 + /** 1.81 + * Called when mouse button down event handling is started and finished. 1.82 + */ 1.83 + void SetMouseButtonDownHandlingDocument(nsIDocument* aDocument) 1.84 + { 1.85 + NS_ASSERTION(!aDocument || !mMouseDownEventHandlingDocument, 1.86 + "Some mouse button down events are nested?"); 1.87 + mMouseDownEventHandlingDocument = aDocument; 1.88 + } 1.89 + 1.90 + /** 1.91 + * Update the caret with current mode (whether in caret browsing mode or not). 1.92 + */ 1.93 + void UpdateCaretForCaretBrowsingMode(); 1.94 + 1.95 + /** 1.96 + * Returns the content node that would be focused if aWindow was in an 1.97 + * active window. This will traverse down the frame hierarchy, starting at 1.98 + * the given window aWindow. Sets aFocusedWindow to the window with the 1.99 + * document containing aFocusedContent. If no element is focused, 1.100 + * aFocusedWindow may be still be set -- this means that the document is 1.101 + * focused but no element within it is focused. 1.102 + * 1.103 + * aWindow and aFocusedWindow must both be non-null. 1.104 + */ 1.105 + static nsIContent* GetFocusedDescendant(nsPIDOMWindow* aWindow, bool aDeep, 1.106 + nsPIDOMWindow** aFocusedWindow); 1.107 + 1.108 + /** 1.109 + * Returns the content node that focus will be redirected to if aContent was 1.110 + * focused. This is used for the special case of certain XUL elements such 1.111 + * as textboxes which redirect focus to an anonymous child. 1.112 + * 1.113 + * aContent must be non-null. 1.114 + * 1.115 + * XXXndeakin this should be removed eventually but I want to do that as 1.116 + * followup work. 1.117 + */ 1.118 + static nsIContent* GetRedirectedFocus(nsIContent* aContent); 1.119 + 1.120 + /** 1.121 + * Returns an InputContextAction cause for aFlags. 1.122 + */ 1.123 + static InputContextAction::Cause GetFocusMoveActionCause(uint32_t aFlags); 1.124 + 1.125 + static bool sMouseFocusesFormControl; 1.126 + 1.127 + static void MarkUncollectableForCCGeneration(uint32_t aGeneration); 1.128 +protected: 1.129 + 1.130 + nsFocusManager(); 1.131 + ~nsFocusManager(); 1.132 + 1.133 + /** 1.134 + * Ensure that the widget associated with the currently focused window is 1.135 + * focused at the widget level. 1.136 + */ 1.137 + void EnsureCurrentWidgetFocused(); 1.138 + 1.139 + /** 1.140 + * Blur whatever is currently focused and focus aNewContent. aFlags is a 1.141 + * bitmask of the flags defined in nsIFocusManager. If aFocusChanged is 1.142 + * true, then the focus has actually shifted and the caret position will be 1.143 + * updated to the new focus, aNewContent will be scrolled into view (unless 1.144 + * a flag disables this) and the focus method for the window will be updated. 1.145 + * If aAdjustWidget is false, don't change the widget focus state. 1.146 + * 1.147 + * All actual focus changes must use this method to do so. (as opposed 1.148 + * to those that update the focus in an inactive window for instance). 1.149 + */ 1.150 + void SetFocusInner(nsIContent* aNewContent, int32_t aFlags, 1.151 + bool aFocusChanged, bool aAdjustWidget); 1.152 + 1.153 + /** 1.154 + * Returns true if aPossibleAncestor is the same as aWindow or an 1.155 + * ancestor of aWindow. 1.156 + */ 1.157 + bool IsSameOrAncestor(nsPIDOMWindow* aPossibleAncestor, 1.158 + nsPIDOMWindow* aWindow); 1.159 + 1.160 + /** 1.161 + * Returns the window that is the lowest common ancestor of both aWindow1 1.162 + * and aWindow2, or null if they share no common ancestor. 1.163 + */ 1.164 + already_AddRefed<nsPIDOMWindow> GetCommonAncestor(nsPIDOMWindow* aWindow1, 1.165 + nsPIDOMWindow* aWindow2); 1.166 + 1.167 + /** 1.168 + * When aNewWindow is focused, adjust the ancestors of aNewWindow so that they 1.169 + * also have their corresponding frames focused. Thus, one can start at 1.170 + * the active top-level window and navigate down the currently focused 1.171 + * elements for each frame in the tree to get to aNewWindow. 1.172 + */ 1.173 + void AdjustWindowFocus(nsPIDOMWindow* aNewWindow, bool aCheckPermission); 1.174 + 1.175 + /** 1.176 + * Returns true if aWindow is visible. 1.177 + */ 1.178 + bool IsWindowVisible(nsPIDOMWindow* aWindow); 1.179 + 1.180 + /** 1.181 + * Returns true if aContent is a root element and not focusable. 1.182 + * I.e., even if aContent is editable root element, this returns true when 1.183 + * the document is in designMode. 1.184 + * 1.185 + * @param aContent must not be null and must be in a document. 1.186 + */ 1.187 + bool IsNonFocusableRoot(nsIContent* aContent); 1.188 + 1.189 + /** 1.190 + * Checks and returns aContent if it may be focused, another content node if 1.191 + * the focus should be retargeted at another node, or null if the node 1.192 + * cannot be focused. aFlags are the flags passed to SetFocus and similar 1.193 + * methods. 1.194 + * 1.195 + * An element is focusable if it is in a document, the document isn't in 1.196 + * print preview mode and the element has an nsIFrame where the 1.197 + * CheckIfFocusable method returns true. For <area> elements, there is no 1.198 + * frame, so only the IsFocusable method on the content node must be 1.199 + * true. 1.200 + */ 1.201 + nsIContent* CheckIfFocusable(nsIContent* aContent, uint32_t aFlags); 1.202 + 1.203 + /** 1.204 + * Blurs the currently focused element. Returns false if another element was 1.205 + * focused as a result. This would mean that the caller should not proceed 1.206 + * with a pending call to Focus. Normally, true would be returned. 1.207 + * 1.208 + * The currently focused element within aWindowToClear will be cleared. 1.209 + * aWindowToClear may be null, which means that no window is cleared. This 1.210 + * will be the case, for example, when lowering a window, as we want to fire 1.211 + * a blur, but not actually change what element would be focused, so that 1.212 + * the same element will be focused again when the window is raised. 1.213 + * 1.214 + * aAncestorWindowToFocus should be set to the common ancestor of the window 1.215 + * that is being blurred and the window that is going to focused, when 1.216 + * switching focus to a sibling window. 1.217 + * 1.218 + * aIsLeavingDocument should be set to true if the document/window is being 1.219 + * blurred as well. Document/window blur events will be fired. It should be 1.220 + * false if an element is the same document is about to be focused. 1.221 + * 1.222 + * If aAdjustWidget is false, don't change the widget focus state. 1.223 + */ 1.224 + bool Blur(nsPIDOMWindow* aWindowToClear, 1.225 + nsPIDOMWindow* aAncestorWindowToFocus, 1.226 + bool aIsLeavingDocument, 1.227 + bool aAdjustWidget); 1.228 + 1.229 + /** 1.230 + * Focus an element in the active window and child frame. 1.231 + * 1.232 + * aWindow is the window containing the element aContent to focus. 1.233 + * 1.234 + * aFlags is the flags passed to the various focus methods in 1.235 + * nsIFocusManager. 1.236 + * 1.237 + * aIsNewDocument should be true if a new document is being focused. 1.238 + * Document/window focus events will be fired. 1.239 + * 1.240 + * aFocusChanged should be true if a new content node is being focused, so 1.241 + * the focused content will be scrolled into view and the caret position 1.242 + * will be updated. If false is passed, then a window is simply being 1.243 + * refocused, for instance, due to a window being raised, or a tab is being 1.244 + * switched to. 1.245 + * 1.246 + * If aFocusChanged is true, then the focus has moved to a new location. 1.247 + * Otherwise, the focus is just being updated because the window was 1.248 + * raised. 1.249 + * 1.250 + * aWindowRaised should be true if the window is being raised. In this case, 1.251 + * command updaters will not be called. 1.252 + * 1.253 + * If aAdjustWidget is false, don't change the widget focus state. 1.254 + */ 1.255 + void Focus(nsPIDOMWindow* aWindow, 1.256 + nsIContent* aContent, 1.257 + uint32_t aFlags, 1.258 + bool aIsNewDocument, 1.259 + bool aFocusChanged, 1.260 + bool aWindowRaised, 1.261 + bool aAdjustWidget); 1.262 + 1.263 + /** 1.264 + * Fires a focus or blur event at aTarget. 1.265 + * 1.266 + * aType should be either NS_FOCUS_CONTENT or NS_BLUR_CONTENT. For blur 1.267 + * events, aFocusMethod should normally be non-zero. 1.268 + * 1.269 + * aWindowRaised should only be true if called from WindowRaised. 1.270 + */ 1.271 + void SendFocusOrBlurEvent(uint32_t aType, 1.272 + nsIPresShell* aPresShell, 1.273 + nsIDocument* aDocument, 1.274 + nsISupports* aTarget, 1.275 + uint32_t aFocusMethod, 1.276 + bool aWindowRaised, 1.277 + bool aIsRefocus = false); 1.278 + 1.279 + /** 1.280 + * Scrolls aContent into view unless the FLAG_NOSCROLL flag is set. 1.281 + */ 1.282 + void ScrollIntoView(nsIPresShell* aPresShell, 1.283 + nsIContent* aContent, 1.284 + uint32_t aFlags); 1.285 + 1.286 + /** 1.287 + * Raises the top-level window aWindow at the widget level. 1.288 + */ 1.289 + void RaiseWindow(nsPIDOMWindow* aWindow); 1.290 + 1.291 + /** 1.292 + * Updates the caret positon and visibility to match the focus. 1.293 + * 1.294 + * aMoveCaretToFocus should be true to move the caret to aContent. 1.295 + * 1.296 + * aUpdateVisibility should be true to update whether the caret is 1.297 + * visible or not. 1.298 + */ 1.299 + void UpdateCaret(bool aMoveCaretToFocus, 1.300 + bool aUpdateVisibility, 1.301 + nsIContent* aContent); 1.302 + 1.303 + /** 1.304 + * Helper method to move the caret to the focused element aContent. 1.305 + */ 1.306 + void MoveCaretToFocus(nsIPresShell* aPresShell, nsIContent* aContent); 1.307 + 1.308 + /** 1.309 + * Makes the caret visible or not, depending on aVisible. 1.310 + */ 1.311 + nsresult SetCaretVisible(nsIPresShell* aPresShell, 1.312 + bool aVisible, 1.313 + nsIContent* aContent); 1.314 + 1.315 + 1.316 + // the remaining functions are used for tab key and document-navigation 1.317 + 1.318 + /** 1.319 + * Retrieves the start and end points of the current selection for 1.320 + * aDocument and stores them in aStartContent and aEndContent. 1.321 + */ 1.322 + nsresult GetSelectionLocation(nsIDocument* aDocument, 1.323 + nsIPresShell* aPresShell, 1.324 + nsIContent **aStartContent, 1.325 + nsIContent **aEndContent); 1.326 + 1.327 + /** 1.328 + * Helper function for MoveFocus which determines the next element 1.329 + * to move the focus to and returns it in aNextContent. 1.330 + * 1.331 + * aWindow is the window to adjust the focus within, and aStart is 1.332 + * the element to start navigation from. For tab key navigation, 1.333 + * this should be the currently focused element. 1.334 + * 1.335 + * aType is the type passed to MoveFocus. If aNoParentTraversal is set, 1.336 + * navigation is not done to parent documents and iteration returns to the 1.337 + * beginning (or end) of the starting document. 1.338 + */ 1.339 + nsresult DetermineElementToMoveFocus(nsPIDOMWindow* aWindow, 1.340 + nsIContent* aStart, 1.341 + int32_t aType, bool aNoParentTraversal, 1.342 + nsIContent** aNextContent); 1.343 + 1.344 + /** 1.345 + * Retrieve the next tabbable element within a document, using focusability 1.346 + * and tabindex to determine the tab order. The element is returned in 1.347 + * aResultContent. 1.348 + * 1.349 + * aRootContent is the root node -- nodes above this will not be examined. 1.350 + * Typically this will be the root node of a document, but could also be 1.351 + * a popup node. 1.352 + * 1.353 + * aOriginalStartContent is the content which was originally the starting 1.354 + * node, in the case of recursive or looping calls. 1.355 + * 1.356 + * aStartContent is the starting point for this call of this method. 1.357 + * If aStartContent doesn't have visual representation, the next content 1.358 + * object, which does have a primary frame, will be used as a start. 1.359 + * If that content object is focusable, the method may return it. 1.360 + * 1.361 + * aForward should be true for forward navigation or false for backward 1.362 + * navigation. 1.363 + * 1.364 + * aCurrentTabIndex is the current tabindex. 1.365 + * 1.366 + * aIgnoreTabIndex to ignore the current tabindex and find the element 1.367 + * irrespective or the tab index. This will be true when a selection is 1.368 + * active, since we just want to focus the next element in tree order 1.369 + * from where the selection is. Similarly, if the starting element isn't 1.370 + * focusable, since it doesn't really have a defined tab index. 1.371 + */ 1.372 + nsresult GetNextTabbableContent(nsIPresShell* aPresShell, 1.373 + nsIContent* aRootContent, 1.374 + nsIContent* aOriginalStartContent, 1.375 + nsIContent* aStartContent, 1.376 + bool aForward, 1.377 + int32_t aCurrentTabIndex, 1.378 + bool aIgnoreTabIndex, 1.379 + nsIContent** aResultContent); 1.380 + 1.381 + /** 1.382 + * Get the next tabbable image map area and returns it. 1.383 + * 1.384 + * aForward should be true for forward navigation or false for backward 1.385 + * navigation. 1.386 + * 1.387 + * aCurrentTabIndex is the current tabindex. 1.388 + * 1.389 + * aImageContent is the image. 1.390 + * 1.391 + * aStartContent is the current image map area. 1.392 + */ 1.393 + nsIContent* GetNextTabbableMapArea(bool aForward, 1.394 + int32_t aCurrentTabIndex, 1.395 + nsIContent* aImageContent, 1.396 + nsIContent* aStartContent); 1.397 + 1.398 + /** 1.399 + * Return the next valid tabindex value after aCurrentTabIndex, if aForward 1.400 + * is true, or the previous tabindex value if aForward is false. aParent is 1.401 + * the node from which to start looking for tab indicies. 1.402 + */ 1.403 + int32_t GetNextTabIndex(nsIContent* aParent, 1.404 + int32_t aCurrentTabIndex, 1.405 + bool aForward); 1.406 + 1.407 + /** 1.408 + * Retrieves and returns the root node from aDocument to be focused. Will 1.409 + * return null if the root node cannot be focused. There are several reasons 1.410 + * for this: 1.411 + * 1.412 + * - if aIsForDocNavigation is true, and aWindow is in an <iframe>. 1.413 + * - if aIsForDocNavigation is false, and aWindow is a chrome shell. 1.414 + * - if aCheckVisibility is true and the aWindow is not visible. 1.415 + * - if aDocument is a frameset document. 1.416 + */ 1.417 + nsIContent* GetRootForFocus(nsPIDOMWindow* aWindow, 1.418 + nsIDocument* aDocument, 1.419 + bool aIsForDocNavigation, 1.420 + bool aCheckVisibility); 1.421 + 1.422 + /** 1.423 + * Get the last docshell child of aItem and return it in aResult. 1.424 + */ 1.425 + void GetLastDocShell(nsIDocShellTreeItem* aItem, 1.426 + nsIDocShellTreeItem** aResult); 1.427 + 1.428 + /** 1.429 + * Get the next docshell child of aItem and return it in aResult. 1.430 + */ 1.431 + void GetNextDocShell(nsIDocShellTreeItem* aItem, 1.432 + nsIDocShellTreeItem** aResult); 1.433 + 1.434 + /** 1.435 + * Get the previous docshell child of aItem and return it in aResult. 1.436 + */ 1.437 + void GetPreviousDocShell(nsIDocShellTreeItem* aItem, 1.438 + nsIDocShellTreeItem** aResult); 1.439 + 1.440 + /** 1.441 + * Determine the first panel with focusable content in document tab order 1.442 + * from the given document. aForward indicates the direction to scan. If 1.443 + * aCurrentPopup is set to a panel, the next or previous popup after 1.444 + * aCurrentPopup after it is used. If aCurrentPopup is null, then the first 1.445 + * or last popup is used. If a panel has no focusable content, it is skipped. 1.446 + * Null is returned if no panel is open or no open panel contains a focusable 1.447 + * element. 1.448 + */ 1.449 + nsIContent* GetNextTabbablePanel(nsIDocument* aDocument, nsIFrame* aCurrentPopup, bool aForward); 1.450 + 1.451 + /** 1.452 + * Get the tabbable next document from aStartContent or, if null, the 1.453 + * currently focused frame if aForward is true, or the previously tabbable 1.454 + * document if aForward is false. If this document is a chrome or frameset 1.455 + * document, returns the first focusable element within this document, 1.456 + * otherwise, returns the root node of the document. 1.457 + * 1.458 + * 1.459 + * Panels with focusable content are also placed in the cycling order, just 1.460 + * after the document containing that panel. 1.461 + * 1.462 + * This method would be used for document navigation, which is typically 1.463 + * invoked by pressing F6. 1.464 + */ 1.465 + nsIContent* GetNextTabbableDocument(nsIContent* aStartContent, bool aForward); 1.466 + 1.467 + /** 1.468 + * Retreives a focusable element within the current selection of aWindow. 1.469 + * Currently, this only detects links. 1.470 + * 1.471 + * This is used when MoveFocus is called with a type of MOVEFOCUS_CARET, 1.472 + * which is used, for example, to focus links as the caret is moved over 1.473 + * them. 1.474 + */ 1.475 + void GetFocusInSelection(nsPIDOMWindow* aWindow, 1.476 + nsIContent* aStartSelection, 1.477 + nsIContent* aEndSelection, 1.478 + nsIContent** aFocusedContent); 1.479 + 1.480 +private: 1.481 + // Notify that the focus state of aContent has changed. Note that 1.482 + // we need to pass in whether the window should show a focus ring 1.483 + // before the SetFocusedNode call on it happened when losing focus 1.484 + // and after the SetFocusedNode call when gaining focus, which is 1.485 + // why that information needs to be an explicit argument instead of 1.486 + // just passing in the window and asking it whether it should show 1.487 + // focus rings: in the losing focus case that information could be 1.488 + // wrong.. 1.489 + static void NotifyFocusStateChange(nsIContent* aContent, 1.490 + bool aWindowShouldShowFocusRing, 1.491 + bool aGettingFocus); 1.492 + 1.493 + void SetFocusedWindowInternal(nsPIDOMWindow* aWindow); 1.494 + 1.495 + // the currently active and front-most top-most window 1.496 + nsCOMPtr<nsPIDOMWindow> mActiveWindow; 1.497 + 1.498 + // the child or top-level window that is currently focused. This window will 1.499 + // either be the same window as mActiveWindow or a descendant of it. 1.500 + // Except during shutdown use SetFocusedWindowInternal to set mFocusedWindow! 1.501 + nsCOMPtr<nsPIDOMWindow> mFocusedWindow; 1.502 + 1.503 + // the currently focused content, which is always inside mFocusedWindow. This 1.504 + // is a cached copy of the mFocusedWindow's current content. This may be null 1.505 + // if no content is focused. 1.506 + nsCOMPtr<nsIContent> mFocusedContent; 1.507 + 1.508 + // these fields store a content node temporarily while it is being focused 1.509 + // or blurred to ensure that a recursive call doesn't refire the same event. 1.510 + // They will always be cleared afterwards. 1.511 + nsCOMPtr<nsIContent> mFirstBlurEvent; 1.512 + nsCOMPtr<nsIContent> mFirstFocusEvent; 1.513 + 1.514 + // keep track of a window while it is being lowered 1.515 + nsCOMPtr<nsPIDOMWindow> mWindowBeingLowered; 1.516 + 1.517 + // synchronized actions cannot be interrupted with events, so queue these up 1.518 + // and fire them later. 1.519 + nsTArray<nsDelayedBlurOrFocusEvent> mDelayedBlurFocusEvents; 1.520 + 1.521 + // A document which is handling a mouse button down event. 1.522 + // When a mouse down event process is finished, ESM sets focus to the target 1.523 + // content. Therefore, while DOM event handlers are handling mouse down 1.524 + // events, the handlers should be able to steal focus from any elements even 1.525 + // if focus is in chrome content. So, if this isn't nullptr and the caller 1.526 + // can access the document node, the caller should succeed in moving focus. 1.527 + nsCOMPtr<nsIDocument> mMouseDownEventHandlingDocument; 1.528 + 1.529 + static bool sTestMode; 1.530 + 1.531 + // the single focus manager 1.532 + static nsFocusManager* sInstance; 1.533 +}; 1.534 + 1.535 +nsresult 1.536 +NS_NewFocusManager(nsIFocusManager** aResult); 1.537 + 1.538 +#endif