|
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/. */ |
|
5 |
|
6 #ifndef nsFocusManager_h___ |
|
7 #define nsFocusManager_h___ |
|
8 |
|
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" |
|
16 |
|
17 #define FOCUSMETHOD_MASK 0xF000 |
|
18 #define FOCUSMETHODANDRING_MASK 0xF0F000 |
|
19 |
|
20 #define FOCUSMANAGER_CONTRACTID "@mozilla.org/focus-manager;1" |
|
21 |
|
22 class nsIContent; |
|
23 class nsIDocShellTreeItem; |
|
24 class nsPIDOMWindow; |
|
25 |
|
26 struct nsDelayedBlurOrFocusEvent; |
|
27 |
|
28 /** |
|
29 * The focus manager keeps track of where the focus is, that is, the node |
|
30 * which receives key events. |
|
31 */ |
|
32 |
|
33 class nsFocusManager MOZ_FINAL : public nsIFocusManager, |
|
34 public nsIObserver, |
|
35 public nsSupportsWeakReference |
|
36 { |
|
37 typedef mozilla::widget::InputContextAction InputContextAction; |
|
38 |
|
39 public: |
|
40 |
|
41 NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsFocusManager, nsIFocusManager) |
|
42 NS_DECL_CYCLE_COLLECTING_ISUPPORTS |
|
43 NS_DECL_NSIOBSERVER |
|
44 NS_DECL_NSIFOCUSMANAGER |
|
45 |
|
46 // called to initialize and stop the focus manager at startup and shutdown |
|
47 static nsresult Init(); |
|
48 static void Shutdown(); |
|
49 |
|
50 /** |
|
51 * Retrieve the single focus manager. |
|
52 */ |
|
53 static nsFocusManager* GetFocusManager() { return sInstance; } |
|
54 |
|
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; } |
|
61 |
|
62 /** |
|
63 * Return a focused window. Version of nsIFocusManager::GetFocusedWindow. |
|
64 */ |
|
65 nsPIDOMWindow* GetFocusedWindow() const { return mFocusedWindow; } |
|
66 |
|
67 /** |
|
68 * Return an active window. Version of nsIFocusManager::GetActiveWindow. |
|
69 */ |
|
70 nsPIDOMWindow* GetActiveWindow() const { return mActiveWindow; } |
|
71 |
|
72 /** |
|
73 * Called when content has been removed. |
|
74 */ |
|
75 nsresult ContentRemoved(nsIDocument* aDocument, nsIContent* aContent); |
|
76 |
|
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 } |
|
86 |
|
87 /** |
|
88 * Update the caret with current mode (whether in caret browsing mode or not). |
|
89 */ |
|
90 void UpdateCaretForCaretBrowsingMode(); |
|
91 |
|
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); |
|
104 |
|
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); |
|
116 |
|
117 /** |
|
118 * Returns an InputContextAction cause for aFlags. |
|
119 */ |
|
120 static InputContextAction::Cause GetFocusMoveActionCause(uint32_t aFlags); |
|
121 |
|
122 static bool sMouseFocusesFormControl; |
|
123 |
|
124 static void MarkUncollectableForCCGeneration(uint32_t aGeneration); |
|
125 protected: |
|
126 |
|
127 nsFocusManager(); |
|
128 ~nsFocusManager(); |
|
129 |
|
130 /** |
|
131 * Ensure that the widget associated with the currently focused window is |
|
132 * focused at the widget level. |
|
133 */ |
|
134 void EnsureCurrentWidgetFocused(); |
|
135 |
|
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); |
|
149 |
|
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); |
|
156 |
|
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); |
|
163 |
|
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); |
|
171 |
|
172 /** |
|
173 * Returns true if aWindow is visible. |
|
174 */ |
|
175 bool IsWindowVisible(nsPIDOMWindow* aWindow); |
|
176 |
|
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); |
|
185 |
|
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); |
|
199 |
|
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); |
|
225 |
|
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); |
|
259 |
|
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); |
|
275 |
|
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); |
|
282 |
|
283 /** |
|
284 * Raises the top-level window aWindow at the widget level. |
|
285 */ |
|
286 void RaiseWindow(nsPIDOMWindow* aWindow); |
|
287 |
|
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); |
|
299 |
|
300 /** |
|
301 * Helper method to move the caret to the focused element aContent. |
|
302 */ |
|
303 void MoveCaretToFocus(nsIPresShell* aPresShell, nsIContent* aContent); |
|
304 |
|
305 /** |
|
306 * Makes the caret visible or not, depending on aVisible. |
|
307 */ |
|
308 nsresult SetCaretVisible(nsIPresShell* aPresShell, |
|
309 bool aVisible, |
|
310 nsIContent* aContent); |
|
311 |
|
312 |
|
313 // the remaining functions are used for tab key and document-navigation |
|
314 |
|
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); |
|
323 |
|
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); |
|
340 |
|
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); |
|
377 |
|
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); |
|
394 |
|
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); |
|
403 |
|
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); |
|
418 |
|
419 /** |
|
420 * Get the last docshell child of aItem and return it in aResult. |
|
421 */ |
|
422 void GetLastDocShell(nsIDocShellTreeItem* aItem, |
|
423 nsIDocShellTreeItem** aResult); |
|
424 |
|
425 /** |
|
426 * Get the next docshell child of aItem and return it in aResult. |
|
427 */ |
|
428 void GetNextDocShell(nsIDocShellTreeItem* aItem, |
|
429 nsIDocShellTreeItem** aResult); |
|
430 |
|
431 /** |
|
432 * Get the previous docshell child of aItem and return it in aResult. |
|
433 */ |
|
434 void GetPreviousDocShell(nsIDocShellTreeItem* aItem, |
|
435 nsIDocShellTreeItem** aResult); |
|
436 |
|
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); |
|
447 |
|
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); |
|
463 |
|
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); |
|
476 |
|
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); |
|
489 |
|
490 void SetFocusedWindowInternal(nsPIDOMWindow* aWindow); |
|
491 |
|
492 // the currently active and front-most top-most window |
|
493 nsCOMPtr<nsPIDOMWindow> mActiveWindow; |
|
494 |
|
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; |
|
499 |
|
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; |
|
504 |
|
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; |
|
510 |
|
511 // keep track of a window while it is being lowered |
|
512 nsCOMPtr<nsPIDOMWindow> mWindowBeingLowered; |
|
513 |
|
514 // synchronized actions cannot be interrupted with events, so queue these up |
|
515 // and fire them later. |
|
516 nsTArray<nsDelayedBlurOrFocusEvent> mDelayedBlurFocusEvents; |
|
517 |
|
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; |
|
525 |
|
526 static bool sTestMode; |
|
527 |
|
528 // the single focus manager |
|
529 static nsFocusManager* sInstance; |
|
530 }; |
|
531 |
|
532 nsresult |
|
533 NS_NewFocusManager(nsIFocusManager** aResult); |
|
534 |
|
535 #endif |