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