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