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
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/
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
15 * The Original Code is mozila.org code.
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.
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.
35 * ***** END LICENSE BLOCK ***** */
37 #include "nsFocusManager.h"
39 #include "nsIInterfaceRequestor.h"
40 #include "nsIInterfaceRequestorUtils.h"
41 #include "nsIServiceManager.h"
42 #include "nsIEnumerator.h"
43 #include "nsTPtrArray.h"
44 #include "nsGkAtoms.h"
45 #include "nsIPrefBranch2.h"
46 #include "nsContentUtils.h"
47 #include "nsIDocument.h"
48 #include "nsIDOMWindow.h"
49 #include "nsPIDOMWindow.h"
50 #include "nsIDOMElement.h"
51 #include "nsIDOMXULElement.h"
52 #include "nsIDOMNSHTMLFrameElement.h"
53 #include "nsIDOMHTMLInputElement.h"
54 #include "nsIDOMHTMLMapElement.h"
55 #include "nsIDOMHTMLLegendElement.h"
56 #include "nsIDOMDocumentRange.h"
57 #include "nsIDOMRange.h"
58 #include "nsIHTMLDocument.h"
59 #include "nsIFormControlFrame.h"
60 #include "nsGenericHTMLElement.h"
61 #include "nsIDocShell.h"
62 #include "nsIEditorDocShell.h"
63 #include "nsIDocShellTreeItem.h"
64 #include "nsIDocShellTreeOwner.h"
65 #include "nsLayoutUtils.h"
66 #include "nsIPresShell.h"
67 #include "nsIContentViewer.h"
68 #include "nsFrameTraversal.h"
69 #include "nsObjectFrame.h"
70 #include "nsEventDispatcher.h"
71 #include "nsIEventStateManager.h"
72 #include "nsIMEStateManager.h"
73 #include "nsIWebNavigation.h"
75 #include "nsWidgetsCID.h"
76 #include "nsILookAndFeel.h"
77 #include "nsIWidget.h"
78 #include "nsIBaseWindow.h"
79 #include "nsIViewManager.h"
80 #include "nsFrameSelection.h"
81 #include "nsXULPopupManager.h"
82 #include "nsImageMapUtils.h"
83 #include "nsTreeWalker.h"
84 #include "nsIDOMNodeFilter.h"
85 #include "nsIScriptObjectPrincipal.h"
86 #include "nsIPrincipal.h"
87 #include "mozilla/dom/Element.h"
90 #include "nsIDOMXULTextboxElement.h"
91 #include "nsIDOMXULMenuListElement.h"
94 using namespace mozilla::dom
;
96 //#define DEBUG_FOCUS 1
97 //#define DEBUG_FOCUS_NAVIGATION 1
98 #define PRINTTAGF(format, content) \
100 nsAutoString tag(NS_LITERAL_STRING("(none)")); \
102 content->Tag()->ToString(tag); \
103 printf(format, NS_ConvertUTF16toUTF8(tag).get()); \
106 struct nsDelayedBlurOrFocusEvent
108 nsDelayedBlurOrFocusEvent(PRUint32 aType
,
109 nsIPresShell
* aPresShell
,
110 nsIDocument
* aDocument
,
111 nsPIDOMEventTarget
* aTarget
)
113 mPresShell(aPresShell
),
114 mDocument(aDocument
),
117 nsDelayedBlurOrFocusEvent(const nsDelayedBlurOrFocusEvent
& aOther
)
118 : mType(aOther
.mType
),
119 mPresShell(aOther
.mPresShell
),
120 mDocument(aOther
.mDocument
),
121 mTarget(aOther
.mTarget
) { }
124 nsCOMPtr
<nsIPresShell
> mPresShell
;
125 nsCOMPtr
<nsIDocument
> mDocument
;
126 nsCOMPtr
<nsPIDOMEventTarget
> mTarget
;
129 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsFocusManager
)
130 NS_INTERFACE_MAP_ENTRY(nsIFocusManager
)
131 NS_INTERFACE_MAP_ENTRY(nsIObserver
)
132 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference
)
133 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIFocusManager
)
136 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFocusManager
)
137 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFocusManager
)
139 NS_IMPL_CYCLE_COLLECTION_CLASS(nsFocusManager
)
140 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsFocusManager
)
141 NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mActiveWindow
)
142 NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mFocusedWindow
)
143 NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mFocusedContent
)
144 NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mFirstBlurEvent
)
145 NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mFirstFocusEvent
)
146 NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mWindowBeingLowered
)
147 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
148 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsFocusManager
)
149 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mActiveWindow
)
150 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mFocusedWindow
)
151 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mFocusedContent
)
152 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mFirstBlurEvent
)
153 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mFirstFocusEvent
)
154 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mWindowBeingLowered
)
155 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
157 static NS_DEFINE_CID(kLookAndFeelCID
, NS_LOOKANDFEEL_CID
);
159 nsFocusManager
* nsFocusManager::sInstance
= nsnull
;
160 PRBool
nsFocusManager::sMouseFocusesFormControl
= PR_FALSE
;
162 nsFocusManager::nsFocusManager()
165 nsFocusManager::~nsFocusManager()
167 nsIPrefBranch2
* prefBranch
= nsContentUtils::GetPrefBranch();
170 prefBranch
->RemoveObserver("accessibility.browsewithcaret", this);
171 prefBranch
->RemoveObserver("accessibility.tabfocus_applies_to_xul", this);
172 prefBranch
->RemoveObserver("accessibility.mouse_focuses_formcontrol", this);
178 nsFocusManager::Init()
180 nsFocusManager
* fm
= new nsFocusManager();
181 NS_ENSURE_TRUE(fm
, NS_ERROR_OUT_OF_MEMORY
);
185 nsIContent::sTabFocusModelAppliesToXUL
=
186 nsContentUtils::GetBoolPref("accessibility.tabfocus_applies_to_xul",
187 nsIContent::sTabFocusModelAppliesToXUL
);
189 sMouseFocusesFormControl
=
190 nsContentUtils::GetBoolPref("accessibility.mouse_focuses_formcontrol", PR_FALSE
);
192 nsIPrefBranch2
* prefBranch
= nsContentUtils::GetPrefBranch();
193 prefBranch
->AddObserver("accessibility.browsewithcaret", fm
, PR_TRUE
);
194 prefBranch
->AddObserver("accessibility.tabfocus_applies_to_xul", fm
, PR_TRUE
);
195 prefBranch
->AddObserver("accessibility.mouse_focuses_formcontrol", fm
, PR_TRUE
);
202 nsFocusManager::Shutdown()
204 NS_IF_RELEASE(sInstance
);
208 nsFocusManager::Observe(nsISupports
*aSubject
,
210 const PRUnichar
*aData
)
212 nsDependentString
data(aData
);
213 if (!nsCRT::strcmp(aTopic
, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID
)) {
214 if (data
.EqualsLiteral("accessibility.browsewithcaret")) {
215 UpdateCaret(PR_FALSE
, PR_TRUE
, mFocusedContent
);
217 else if (data
.EqualsLiteral("accessibility.tabfocus_applies_to_xul")) {
218 nsIContent::sTabFocusModelAppliesToXUL
=
219 nsContentUtils::GetBoolPref("accessibility.tabfocus_applies_to_xul",
220 nsIContent::sTabFocusModelAppliesToXUL
);
222 else if (data
.EqualsLiteral("accessibility.mouse_focuses_formcontrol")) {
223 sMouseFocusesFormControl
=
224 nsContentUtils::GetBoolPref("accessibility.mouse_focuses_formcontrol", PR_FALSE
);
231 // given a frame content node, retrieve the nsIDOMWindow displayed in it
232 static nsPIDOMWindow
*
233 GetContentWindow(nsIContent
* aContent
)
235 nsIDocument
* doc
= aContent
->GetCurrentDoc();
237 nsIDocument
* subdoc
= doc
->GetSubDocumentFor(aContent
);
239 return subdoc
->GetWindow();
245 // get the current window for the given content node
246 static nsPIDOMWindow
*
247 GetCurrentWindow(nsIContent
* aContent
)
249 nsIDocument
*doc
= aContent
->GetCurrentDoc();
250 return doc
? doc
->GetWindow() : nsnull
;
255 nsFocusManager::GetFocusedDescendant(nsPIDOMWindow
* aWindow
, PRBool aDeep
,
256 nsPIDOMWindow
** aFocusedWindow
)
258 NS_ENSURE_TRUE(aWindow
, nsnull
);
260 *aFocusedWindow
= nsnull
;
262 nsIContent
* currentContent
= nsnull
;
263 nsPIDOMWindow
* window
= aWindow
->GetOuterWindow();
265 *aFocusedWindow
= window
;
266 currentContent
= window
->GetFocusedNode();
267 if (!currentContent
|| !aDeep
)
270 window
= GetContentWindow(currentContent
);
273 NS_IF_ADDREF(*aFocusedWindow
);
275 return currentContent
;
280 nsFocusManager::GetRedirectedFocus(nsIContent
* aContent
)
283 if (aContent
->IsXUL()) {
284 nsCOMPtr
<nsIDOMNode
> inputField
;
286 nsCOMPtr
<nsIDOMXULTextBoxElement
> textbox
= do_QueryInterface(aContent
);
288 textbox
->GetInputField(getter_AddRefs(inputField
));
291 nsCOMPtr
<nsIDOMXULMenuListElement
> menulist
= do_QueryInterface(aContent
);
293 menulist
->GetInputField(getter_AddRefs(inputField
));
295 else if (aContent
->Tag() == nsGkAtoms::scale
) {
296 nsCOMPtr
<nsIDocument
> doc
= aContent
->GetCurrentDoc();
300 nsINodeList
* children
= doc
->BindingManager()->GetXBLChildNodesFor(aContent
);
302 nsIContent
* child
= children
->GetNodeAt(0);
303 if (child
&& child
->Tag() == nsGkAtoms::slider
)
310 nsCOMPtr
<nsIContent
> retval
= do_QueryInterface(inputField
);
320 nsFocusManager::GetActiveWindow(nsIDOMWindow
** aWindow
)
322 NS_IF_ADDREF(*aWindow
= mActiveWindow
);
327 nsFocusManager::SetActiveWindow(nsIDOMWindow
* aWindow
)
329 // only top-level windows can be made active
330 nsCOMPtr
<nsPIDOMWindow
> piWindow
= do_QueryInterface(aWindow
);
331 NS_ASSERTION(!piWindow
|| piWindow
->IsOuterWindow(), "outer window expected");
333 NS_ENSURE_TRUE(piWindow
&& (piWindow
== piWindow
->GetPrivateRoot()),
334 NS_ERROR_INVALID_ARG
);
336 RaiseWindow(piWindow
);
341 nsFocusManager::GetFocusedWindow(nsIDOMWindow
** aFocusedWindow
)
343 NS_IF_ADDREF(*aFocusedWindow
= mFocusedWindow
);
347 NS_IMETHODIMP
nsFocusManager::SetFocusedWindow(nsIDOMWindow
* aWindowToFocus
)
350 printf("<<SetFocusedWindow begin>>\n");
353 nsCOMPtr
<nsPIDOMWindow
> windowToFocus(do_QueryInterface(aWindowToFocus
));
354 NS_ENSURE_TRUE(windowToFocus
, NS_ERROR_FAILURE
);
356 windowToFocus
= windowToFocus
->GetOuterWindow();
358 nsCOMPtr
<nsIContent
> frameContent
=
359 do_QueryInterface(windowToFocus
->GetFrameElementInternal());
361 // pass false so that the caret does not get updated and scrolling does
363 SetFocusInner(frameContent
, 0, PR_FALSE
);
366 // this is a top-level window. If the window has a child frame focused,
367 // clear the focus. Otherwise, focus should already be in this frame, or
368 // already cleared. This ensures that focus will be in this frame and not
370 nsIContent
* content
= windowToFocus
->GetFocusedNode();
372 nsCOMPtr
<nsIDOMWindow
> childWindow
= GetContentWindow(content
);
374 ClearFocus(windowToFocus
);
378 nsCOMPtr
<nsPIDOMWindow
> rootWindow
= windowToFocus
->GetPrivateRoot();
380 RaiseWindow(rootWindow
);
383 printf("<<SetFocusedWindow end>>\n");
390 nsFocusManager::GetFocusedElement(nsIDOMElement
** aFocusedElement
)
393 CallQueryInterface(mFocusedContent
, aFocusedElement
);
395 *aFocusedElement
= nsnull
;
400 nsFocusManager::GetLastFocusMethod(nsIDOMWindow
* aWindow
, PRUint32
* aLastFocusMethod
)
402 // the focus method is stored on the inner window
403 nsCOMPtr
<nsPIDOMWindow
> window(do_QueryInterface(aWindow
));
405 window
= window
->GetCurrentInnerWindow();
407 window
= mFocusedWindow
;
409 *aLastFocusMethod
= window
? window
->GetFocusMethod() : 0;
411 NS_ASSERTION((*aLastFocusMethod
& FOCUSMETHOD_MASK
) == *aLastFocusMethod
,
412 "invalid focus method");
417 nsFocusManager::SetFocus(nsIDOMElement
* aElement
, PRUint32 aFlags
)
420 printf("<<SetFocus>>\n");
423 nsCOMPtr
<nsIContent
> newFocus
= do_QueryInterface(aElement
);
424 NS_ENSURE_ARG(newFocus
);
426 SetFocusInner(newFocus
, aFlags
, PR_TRUE
);
432 nsFocusManager::MoveFocus(nsIDOMWindow
* aWindow
, nsIDOMElement
* aStartElement
,
433 PRUint32 aType
, PRUint32 aFlags
, nsIDOMElement
** aElement
)
438 printf("<<MoveFocus Type: %d Flags: %x>>\n<<", aType
, aFlags
);
440 nsCOMPtr
<nsPIDOMWindow
> focusedWindow
= mFocusedWindow
;
442 nsCOMPtr
<nsIDocument
> doc
= do_QueryInterface(focusedWindow
->GetExtantDocument());
445 doc
->GetDocumentURI()->GetSpec(spec
);
446 printf(" [%p] Focused Window: %s", mFocusedWindow
.get(), spec
.get());
449 PRINTTAGF(">> $[[%s]]\n", mFocusedContent
);
452 // use FLAG_BYMOVEFOCUS when switching focus with MoveFocus unless one of
453 // the other focus methods is already set, or we're just moving to the root
454 // or caret position.
455 if (aType
!= MOVEFOCUS_ROOT
&& aType
!= MOVEFOCUS_CARET
&&
456 (aFlags
& FOCUSMETHOD_MASK
) == 0) {
457 aFlags
|= FLAG_BYMOVEFOCUS
;
460 nsCOMPtr
<nsPIDOMWindow
> window
;
461 nsCOMPtr
<nsIContent
> startContent
;
463 startContent
= do_QueryInterface(aStartElement
);
464 NS_ENSURE_TRUE(startContent
, NS_ERROR_INVALID_ARG
);
466 window
= GetCurrentWindow(startContent
);
469 window
= aWindow
? do_QueryInterface(aWindow
) : mFocusedWindow
;
470 NS_ENSURE_TRUE(window
, NS_ERROR_FAILURE
);
471 window
= window
->GetOuterWindow();
474 NS_ENSURE_TRUE(window
, NS_ERROR_FAILURE
);
476 nsCOMPtr
<nsIContent
> newFocus
;
477 nsresult rv
= DetermineElementToMoveFocus(window
, startContent
, aType
,
478 getter_AddRefs(newFocus
));
479 NS_ENSURE_SUCCESS(rv
, rv
);
481 #ifdef DEBUG_FOCUS_NAVIGATION
482 PRINTTAGF("-> Element to be focused: %s\n", newFocus
);
486 // for caret movement, pass false for the aFocusChanged argument,
487 // otherwise the caret will end up moving to the focus position. This
488 // would be a problem because the caret would move to the beginning of the
489 // focused link making it impossible to navigate the caret over a link.
490 SetFocusInner(newFocus
, aFlags
, aType
!= MOVEFOCUS_CARET
);
491 CallQueryInterface(newFocus
, aElement
);
493 else if (aType
== MOVEFOCUS_ROOT
|| aType
== MOVEFOCUS_CARET
) {
494 // no content was found, so clear the focus for these two types.
499 printf("<<MoveFocus end>>\n");
506 nsFocusManager::ClearFocus(nsIDOMWindow
* aWindow
)
509 printf("<<ClearFocus begin>>\n");
512 // if the window to clear is the focused window or an ancestor of the
513 // focused window, then blur the existing focused content. Otherwise, the
514 // focus is somewhere else so just update the current node.
515 nsCOMPtr
<nsPIDOMWindow
> window(do_QueryInterface(aWindow
));
516 NS_ENSURE_TRUE(window
, NS_ERROR_INVALID_ARG
);
518 window
= window
->GetOuterWindow();
519 NS_ENSURE_TRUE(window
, NS_ERROR_INVALID_ARG
);
521 if (IsSameOrAncestor(window
, mFocusedWindow
)) {
522 PRBool isAncestor
= (window
!= mFocusedWindow
);
523 if (Blur(window
, nsnull
, isAncestor
)) {
524 // if we are clearing the focus on an ancestor of the focused window,
525 // the ancestor will become the new focused window, so focus it
527 Focus(window
, nsnull
, 0, PR_TRUE
, PR_FALSE
, PR_FALSE
);
531 window
->SetFocusedNode(nsnull
);
535 printf("<<ClearFocus end>>\n");
542 nsFocusManager::GetFocusedElementForWindow(nsIDOMWindow
* aWindow
,
544 nsIDOMWindow
** aFocusedWindow
,
545 nsIDOMElement
** aElement
)
549 *aFocusedWindow
= nsnull
;
551 nsCOMPtr
<nsPIDOMWindow
> window(do_QueryInterface(aWindow
));
552 NS_ENSURE_TRUE(window
, NS_ERROR_INVALID_ARG
);
554 window
= window
->GetOuterWindow();
555 NS_ENSURE_TRUE(window
, NS_ERROR_INVALID_ARG
);
557 nsCOMPtr
<nsPIDOMWindow
> focusedWindow
;
558 nsCOMPtr
<nsIContent
> focusedContent
=
559 GetFocusedDescendant(window
, aDeep
, getter_AddRefs(focusedWindow
));
561 CallQueryInterface(focusedContent
, aElement
);
564 NS_IF_ADDREF(*aFocusedWindow
= focusedWindow
);
570 nsFocusManager::MoveCaretToFocus(nsIDOMWindow
* aWindow
)
572 PRInt32 itemType
= nsIDocShellTreeItem::typeChrome
;
574 nsCOMPtr
<nsIWebNavigation
> webnav
= do_GetInterface(aWindow
);
575 nsCOMPtr
<nsIDocShellTreeItem
> dsti
= do_QueryInterface(webnav
);
577 dsti
->GetItemType(&itemType
);
578 if (itemType
!= nsIDocShellTreeItem::typeChrome
) {
579 // don't move the caret for editable documents
580 nsCOMPtr
<nsIEditorDocShell
> editorDocShell(do_QueryInterface(dsti
));
581 if (editorDocShell
) {
583 editorDocShell
->GetEditable(&isEditable
);
588 nsCOMPtr
<nsIDocShell
> docShell
= do_QueryInterface(dsti
);
589 NS_ENSURE_TRUE(docShell
, NS_ERROR_FAILURE
);
591 nsCOMPtr
<nsIPresShell
> presShell
;
592 docShell
->GetPresShell(getter_AddRefs(presShell
));
593 NS_ENSURE_TRUE(presShell
, NS_ERROR_FAILURE
);
595 nsCOMPtr
<nsPIDOMWindow
> window(do_QueryInterface(aWindow
));
596 nsCOMPtr
<nsIContent
> content
= window
->GetFocusedNode();
598 MoveCaretToFocus(presShell
, content
);
606 nsFocusManager::WindowRaised(nsIDOMWindow
* aWindow
)
608 nsCOMPtr
<nsPIDOMWindow
> window
= do_QueryInterface(aWindow
);
609 NS_ENSURE_TRUE(window
&& window
->IsOuterWindow(), NS_ERROR_INVALID_ARG
);
612 printf("Window %p Raised [Currently: %p %p] <<", aWindow
, mActiveWindow
.get(), mFocusedWindow
.get());
614 nsCOMPtr
<nsIDocument
> doc
= do_QueryInterface(window
->GetExtantDocument());
616 doc
->GetDocumentURI()->GetSpec(spec
);
617 printf("[%p] Raised Window: %s", aWindow
, spec
.get());
620 doc
= do_QueryInterface(mActiveWindow
->GetExtantDocument());
622 doc
->GetDocumentURI()->GetSpec(spec
);
623 printf(" [%p] Active Window: %s", mActiveWindow
.get(), spec
.get());
629 if (mActiveWindow
== window
) {
630 // The window is already active, so there is no need to focus anything,
631 // but make sure that the right widget is focused. This is a special case
632 // for Windows because when restoring a minimized window, a second
633 // activation will occur and the top-level widget could be focused instead
634 // of the child we want. We solve this by calling SetFocus to ensure that
635 // what the focus manager thinks should be the current widget is actually
637 EnsureCurrentWidgetFocused();
641 // lower the existing window, if any. This shouldn't happen usually.
643 WindowLowered(mActiveWindow
);
645 nsCOMPtr
<nsIWebNavigation
> webnav(do_GetInterface(aWindow
));
646 nsCOMPtr
<nsIDocShellTreeItem
> docShellAsItem(do_QueryInterface(webnav
));
647 // If there's no docShellAsItem, this window must have been closed,
648 // in that case there is no tree owner.
649 NS_ENSURE_TRUE(docShellAsItem
, NS_OK
);
651 // set this as the active window
652 mActiveWindow
= window
;
654 // ensure that the window is enabled and visible
655 nsCOMPtr
<nsIDocShellTreeOwner
> treeOwner
;
656 docShellAsItem
->GetTreeOwner(getter_AddRefs(treeOwner
));
657 nsCOMPtr
<nsIBaseWindow
> baseWindow
= do_QueryInterface(treeOwner
);
659 PRBool isEnabled
= PR_TRUE
;
660 if (NS_SUCCEEDED(baseWindow
->GetEnabled(&isEnabled
)) && !isEnabled
) {
661 return NS_ERROR_FAILURE
;
664 baseWindow
->SetVisibility(PR_TRUE
);
667 // inform the DOM window that it has activated, so that the active attribute
668 // is updated on the window
669 window
->ActivateOrDeactivate(PR_TRUE
);
671 // send activate event
672 nsCOMPtr
<nsIDocument
> document
= do_QueryInterface(window
->GetExtantDocument());
673 nsContentUtils::DispatchTrustedEvent(document
,
675 NS_LITERAL_STRING("activate"),
676 PR_TRUE
, PR_TRUE
, nsnull
);
678 // retrieve the last focused element within the window that was raised
679 nsCOMPtr
<nsPIDOMWindow
> currentWindow
;
680 nsCOMPtr
<nsIContent
> currentFocus
=
681 GetFocusedDescendant(window
, PR_TRUE
, getter_AddRefs(currentWindow
));
683 NS_ASSERTION(currentWindow
, "window raised with no window current");
687 nsCOMPtr
<nsIDocShell
> currentDocShell
= currentWindow
->GetDocShell();
689 nsCOMPtr
<nsIPresShell
> presShell
;
690 currentDocShell
->GetPresShell(getter_AddRefs(presShell
));
692 // disable selection mousedown state on activation
693 // XXXndeakin P3 not sure if this is necessary, but it doesn't hurt
694 nsCOMPtr
<nsFrameSelection
> frameSelection
= presShell
->FrameSelection();
695 frameSelection
->SetMouseDownState(PR_FALSE
);
698 Focus(currentWindow
, currentFocus
, 0, PR_TRUE
, PR_FALSE
, PR_TRUE
);
704 nsFocusManager::WindowLowered(nsIDOMWindow
* aWindow
)
706 nsCOMPtr
<nsPIDOMWindow
> window
= do_QueryInterface(aWindow
);
707 NS_ENSURE_TRUE(window
&& window
->IsOuterWindow(), NS_ERROR_INVALID_ARG
);
710 printf("Window %p Lowered [Currently: %p %p] <<", aWindow
, mActiveWindow
.get(), mFocusedWindow
.get());
712 nsCOMPtr
<nsIDocument
> doc
= do_QueryInterface(window
->GetExtantDocument());
714 doc
->GetDocumentURI()->GetSpec(spec
);
715 printf("[%p] Lowered Window: %s", aWindow
, spec
.get());
718 doc
= do_QueryInterface(mActiveWindow
->GetExtantDocument());
720 doc
->GetDocumentURI()->GetSpec(spec
);
721 printf(" [%p] Active Window: %s", mActiveWindow
.get(), spec
.get());
727 if (mActiveWindow
!= window
)
730 // clear the mouse capture as the active window has changed
731 nsIPresShell::SetCapturingContent(nsnull
, 0);
733 // inform the DOM window that it has deactivated, so that the active
734 // attribute is updated on the window
735 window
->ActivateOrDeactivate(PR_FALSE
);
737 // send deactivate event
738 nsCOMPtr
<nsIDocument
> document
= do_QueryInterface(window
->GetExtantDocument());
739 nsContentUtils::DispatchTrustedEvent(document
,
741 NS_LITERAL_STRING("deactivate"),
742 PR_TRUE
, PR_TRUE
, nsnull
);
744 // keep track of the window being lowered, so that attempts to raise the
745 // window can be prevented until we return. Otherwise, focus can get into
747 mWindowBeingLowered
= mActiveWindow
;
748 mActiveWindow
= nsnull
;
751 Blur(nsnull
, nsnull
, PR_TRUE
);
753 mWindowBeingLowered
= nsnull
;
759 nsFocusManager::ContentRemoved(nsIDocument
* aDocument
, nsIContent
* aContent
)
761 NS_ENSURE_ARG(aDocument
);
762 NS_ENSURE_ARG(aContent
);
764 nsPIDOMWindow
*window
= aDocument
->GetWindow();
768 // if the content is currently focused in the window, or is an ancestor
769 // of the currently focused element, reset the focus within that window.
770 nsIContent
* content
= window
->GetFocusedNode();
771 if (content
&& nsContentUtils::ContentIsDescendantOf(content
, aContent
)) {
772 window
->SetFocusedNode(nsnull
);
774 nsCOMPtr
<nsIDocShell
> docShell
= window
->GetDocShell();
776 nsCOMPtr
<nsIPresShell
> presShell
;
777 docShell
->GetPresShell(getter_AddRefs(presShell
));
778 nsIMEStateManager::OnRemoveContent(presShell
->GetPresContext(), content
);
781 // if this window is currently focused, clear the global focused
782 // element as well, but don't fire any events.
783 if (window
== mFocusedWindow
) {
784 mFocusedContent
= nsnull
;
787 // Check if the node that was focused is an iframe or similar by looking
788 // if it has a subdocument. This would indicate that this focused iframe
789 // and its descendants will be going away. We will need to move the
790 // focus somewhere else, so just clear the focus in the toplevel window
791 // so that no element is focused.
792 nsIDocument
* subdoc
= aDocument
->GetSubDocumentFor(content
);
794 nsCOMPtr
<nsISupports
> container
= subdoc
->GetContainer();
795 nsCOMPtr
<nsPIDOMWindow
> childWindow
= do_GetInterface(container
);
796 if (childWindow
&& IsSameOrAncestor(childWindow
, mFocusedWindow
)) {
797 ClearFocus(mActiveWindow
);
807 nsFocusManager::WindowShown(nsIDOMWindow
* aWindow
, PRBool aNeedsFocus
)
809 nsCOMPtr
<nsPIDOMWindow
> window
= do_QueryInterface(aWindow
);
810 NS_ENSURE_TRUE(window
, NS_ERROR_INVALID_ARG
);
812 window
= window
->GetOuterWindow();
815 printf("Window %p Shown [Currently: %p %p] <<", window
.get(), mActiveWindow
.get(), mFocusedWindow
.get());
817 nsCOMPtr
<nsIDocument
> doc
= do_QueryInterface(window
->GetExtantDocument());
819 doc
->GetDocumentURI()->GetSpec(spec
);
820 printf("Shown Window: %s", spec
.get());
823 if (mFocusedWindow
) {
824 doc
= do_QueryInterface(mFocusedWindow
->GetExtantDocument());
826 doc
->GetDocumentURI()->GetSpec(spec
);
827 printf(" Focused Window: %s", spec
.get());
833 if (mFocusedWindow
!= window
)
837 nsCOMPtr
<nsPIDOMWindow
> currentWindow
;
838 nsCOMPtr
<nsIContent
> currentFocus
=
839 GetFocusedDescendant(window
, PR_TRUE
, getter_AddRefs(currentWindow
));
841 Focus(currentWindow
, currentFocus
, 0, PR_TRUE
, PR_FALSE
, PR_FALSE
);
844 // Sometimes, an element in a window can be focused before the window is
845 // visible, which would mean that the widget may not be properly focused.
846 // When the window becomes visible, make sure the right widget is focused.
847 EnsureCurrentWidgetFocused();
854 nsFocusManager::WindowHidden(nsIDOMWindow
* aWindow
)
856 // if there is no window or it is not the same or an ancestor of the
857 // currently focused window, just return, as the current focus will not
860 nsCOMPtr
<nsPIDOMWindow
> window
= do_QueryInterface(aWindow
);
861 NS_ENSURE_TRUE(window
, NS_ERROR_INVALID_ARG
);
863 window
= window
->GetOuterWindow();
866 printf("Window %p Hidden [Currently: %p %p] <<", window
.get(), mActiveWindow
.get(), mFocusedWindow
.get());
868 nsCOMPtr
<nsIDocument
> doc
= do_QueryInterface(window
->GetExtantDocument());
870 doc
->GetDocumentURI()->GetSpec(spec
);
871 printf("Hide Window: %s", spec
.get());
874 if (mFocusedWindow
) {
875 doc
= do_QueryInterface(mFocusedWindow
->GetExtantDocument());
877 doc
->GetDocumentURI()->GetSpec(spec
);
878 printf(" Focused Window: %s", spec
.get());
883 doc
= do_QueryInterface(mActiveWindow
->GetExtantDocument());
885 doc
->GetDocumentURI()->GetSpec(spec
);
886 printf(" Active Window: %s", spec
.get());
892 if (!IsSameOrAncestor(window
, mFocusedWindow
))
895 // at this point, we know that the window being hidden is either the focused
896 // window, or an ancestor of the focused window. Either way, the focus is no
897 // longer valid, so it needs to be updated.
899 nsCOMPtr
<nsIDocShell
> focusedDocShell
= mFocusedWindow
->GetDocShell();
900 nsCOMPtr
<nsIPresShell
> presShell
;
901 focusedDocShell
->GetPresShell(getter_AddRefs(presShell
));
903 presShell
->GetPresContext()->EventStateManager()->
904 SetContentState(mFocusedContent
, NS_EVENT_STATE_FOCUS
);
907 mFocusedContent
= nsnull
;
909 nsIMEStateManager::OnTextStateBlur(nsnull
, nsnull
);
911 nsIMEStateManager::OnChangeFocus(presShell
->GetPresContext(), nsnull
);
912 SetCaretVisible(presShell
, PR_FALSE
, nsnull
);
915 // if the docshell being hidden is being destroyed, then we want to move
916 // focus somewhere else. Call ClearFocus on the toplevel window, which
917 // will have the effect of clearing the focus and moving the focused window
918 // to the toplevel window. But if the window isn't being destroyed, we are
919 // likely just loading a new document in it, so we want to maintain the
920 // focused window so that the new document gets properly focused.
921 PRBool beingDestroyed
;
922 nsCOMPtr
<nsIDocShell
> docShellBeingHidden
= window
->GetDocShell();
923 docShellBeingHidden
->IsBeingDestroyed(&beingDestroyed
);
924 if (beingDestroyed
) {
925 // There is usually no need to do anything if a toplevel window is going
926 // away, as we assume that WindowLowered will be called. However, this may
927 // not happen if nsIAppStartup::eForceQuit is used to quit, and can cause
928 // a leak. So if the active window is being destroyed, call WindowLowered
930 NS_ASSERTION(mFocusedWindow
->IsOuterWindow(), "outer window expected");
931 if (mActiveWindow
== mFocusedWindow
|| mActiveWindow
== window
)
932 WindowLowered(mActiveWindow
);
934 ClearFocus(mActiveWindow
);
938 // if the window being hidden is an ancestor of the focused window, adjust
939 // the focused window so that it points to the one being hidden. This
940 // ensures that the focused window isn't in a chain of frames that doesn't
942 if (window
!= mFocusedWindow
) {
943 nsCOMPtr
<nsIWebNavigation
> webnav(do_GetInterface(mFocusedWindow
));
944 nsCOMPtr
<nsIDocShellTreeItem
> dsti
= do_QueryInterface(webnav
);
946 nsCOMPtr
<nsIDocShellTreeItem
> parentDsti
;
947 dsti
->GetParent(getter_AddRefs(parentDsti
));
948 nsCOMPtr
<nsPIDOMWindow
> parentWindow
= do_GetInterface(parentDsti
);
950 parentWindow
->SetFocusedNode(nsnull
);
953 mFocusedWindow
= window
;
960 nsFocusManager::FireDelayedEvents(nsIDocument
* aDocument
)
962 NS_ENSURE_ARG(aDocument
);
964 // fire any delayed focus and blur events in the same order that they were added
965 for (PRUint32 i
= 0; i
< mDelayedBlurFocusEvents
.Length(); i
++)
967 if (mDelayedBlurFocusEvents
[i
].mDocument
== aDocument
&&
968 !aDocument
->EventHandlingSuppressed()) {
969 PRUint32 type
= mDelayedBlurFocusEvents
[i
].mType
;
970 nsCOMPtr
<nsPIDOMEventTarget
> target
= mDelayedBlurFocusEvents
[i
].mTarget
;
971 nsCOMPtr
<nsIPresShell
> presShell
= mDelayedBlurFocusEvents
[i
].mPresShell
;
972 mDelayedBlurFocusEvents
.RemoveElementAt(i
);
973 SendFocusOrBlurEvent(type
, presShell
, aDocument
, target
, 0, PR_FALSE
);
983 nsFocusManager::EnsureCurrentWidgetFocused()
988 // get the main child widget for the focused window and ensure that the
989 // platform knows that this widget is focused.
990 nsCOMPtr
<nsIDocShell
> docShell
= mFocusedWindow
->GetDocShell();
992 nsCOMPtr
<nsIPresShell
> presShell
;
993 docShell
->GetPresShell(getter_AddRefs(presShell
));
995 nsIViewManager
* vm
= presShell
->GetViewManager();
997 nsCOMPtr
<nsIWidget
> widget
;
998 vm
->GetRootWidget(getter_AddRefs(widget
));
1000 widget
->SetFocus(PR_FALSE
);
1007 nsFocusManager::SetFocusInner(nsIContent
* aNewContent
, PRInt32 aFlags
,
1008 PRBool aFocusChanged
)
1010 // if the element is not focusable, just return and leave the focus as is
1011 nsCOMPtr
<nsIContent
> contentToFocus
= CheckIfFocusable(aNewContent
, aFlags
);
1012 if (!contentToFocus
)
1015 // check if the element to focus is a frame (iframe) containing a child
1016 // document. Frames are never directly focused; instead focusing a frame
1017 // means focus what is inside the frame. To do this, the descendant content
1018 // within the frame is retrieved and that will be focused instead.
1019 nsCOMPtr
<nsPIDOMWindow
> newWindow
;
1020 nsCOMPtr
<nsPIDOMWindow
> subWindow
= GetContentWindow(contentToFocus
);
1022 contentToFocus
= GetFocusedDescendant(subWindow
, PR_TRUE
, getter_AddRefs(newWindow
));
1023 // since a window is being refocused, clear aFocusChanged so that the
1024 // caret position isn't updated.
1025 aFocusChanged
= PR_FALSE
;
1028 // unless it was set above, retrieve the window for the element to focus
1030 newWindow
= GetCurrentWindow(contentToFocus
);
1032 // if the element is already focused, just return. Note that this happens
1033 // after the frame check above so that we compare the element that will be
1034 // focused rather than the frame it is in.
1035 if (!newWindow
|| (newWindow
== mFocusedWindow
&& contentToFocus
== mFocusedContent
))
1038 // don't allow focus to be placed in docshells or descendants of docshells
1039 // that are being destroyed. Also, ensure that the page hasn't been
1040 // unloaded. The prevents content from being refocused during an unload event.
1041 nsCOMPtr
<nsIDocShell
> newDocShell
= newWindow
->GetDocShell();
1042 nsCOMPtr
<nsIDocShell
> docShell
= newDocShell
;
1045 docShell
->GetIsInUnload(&inUnload
);
1049 PRBool beingDestroyed
;
1050 docShell
->IsBeingDestroyed(&beingDestroyed
);
1054 nsCOMPtr
<nsIDocShellTreeItem
> dsti
= do_QueryInterface(docShell
);
1055 nsCOMPtr
<nsIDocShellTreeItem
> parentDsti
;
1056 dsti
->GetParent(getter_AddRefs(parentDsti
));
1057 docShell
= do_QueryInterface(parentDsti
);
1060 // if the new element is in the same window as the currently focused element
1061 PRBool isElementInFocusedWindow
= (mFocusedWindow
== newWindow
);
1063 if (!isElementInFocusedWindow
&& mFocusedWindow
&& newWindow
&&
1064 nsContentUtils::IsHandlingKeyBoardEvent()) {
1065 nsCOMPtr
<nsIScriptObjectPrincipal
> focused
=
1066 do_QueryInterface(mFocusedWindow
);
1067 nsCOMPtr
<nsIScriptObjectPrincipal
> newFocus
=
1068 do_QueryInterface(newWindow
);
1069 nsIPrincipal
* focusedPrincipal
= focused
->GetPrincipal();
1070 nsIPrincipal
* newPrincipal
= newFocus
->GetPrincipal();
1071 if (!focusedPrincipal
|| !newPrincipal
) {
1074 PRBool subsumes
= PR_FALSE
;
1075 focusedPrincipal
->Subsumes(newPrincipal
, &subsumes
);
1076 if (!subsumes
&& !nsContentUtils::IsCallerTrustedForWrite()) {
1077 NS_WARNING("Not allowed to focus the new window!");
1082 // to check if the new element is in the active window, compare the
1083 // new root docshell for the new element with the active window's docshell.
1084 PRBool isElementInActiveWindow
= PR_FALSE
;
1086 nsCOMPtr
<nsIWebNavigation
> webnav
= do_GetInterface(newWindow
);
1087 nsCOMPtr
<nsIDocShellTreeItem
> dsti
= do_QueryInterface(webnav
);
1088 nsCOMPtr
<nsPIDOMWindow
> newRootWindow
;
1090 nsCOMPtr
<nsIDocShellTreeItem
> root
;
1091 dsti
->GetRootTreeItem(getter_AddRefs(root
));
1092 newRootWindow
= do_GetInterface(root
);
1094 isElementInActiveWindow
= (mActiveWindow
&& newRootWindow
== mActiveWindow
);
1098 PRINTTAGF("Shift Focus: %s", contentToFocus
);
1099 printf(" Flags: %x Current Window: %p New Window: %p Current Element: %p",
1100 aFlags
, mFocusedWindow
.get(), newWindow
.get(), mFocusedContent
.get());
1101 printf(" In Active Window: %d In Focused Window: %d\n",
1102 isElementInActiveWindow
, isElementInFocusedWindow
);
1105 // if the FLAG_NOSWITCHFRAME flag is used, only allow the focus to be
1106 // shifted away from the current element if the new shell to focus is
1107 // the same or an ancestor shell of the currently focused shell.
1108 PRBool allowFrameSwitch
= !(aFlags
& FLAG_NOSWITCHFRAME
) ||
1109 IsSameOrAncestor(newWindow
, mFocusedWindow
);
1111 // if the element is in the active window, frame switching is allowed and
1112 // the content is in a visible window, fire blur and focus events.
1113 PRBool sendFocusEvent
=
1114 isElementInActiveWindow
&& allowFrameSwitch
&& IsWindowVisible(newWindow
);
1116 // When the following conditions are true:
1117 // * an element has focus
1118 // * isn't called by trusted event (i.e., called by untrusted event or by js)
1119 // * the focus is moved to another document's element
1120 // we need to check the permission.
1121 if (sendFocusEvent
&& mFocusedContent
&&
1122 mFocusedContent
->GetOwnerDoc() != aNewContent
->GetOwnerDoc()) {
1123 // If the caller cannot access the current focused node, the caller should
1124 // not be able to steal focus from it. E.g., When the current focused node
1125 // is in chrome, any web contents should not be able to steal the focus.
1126 nsCOMPtr
<nsIDOMNode
> domNode(do_QueryInterface(mFocusedContent
));
1127 sendFocusEvent
= nsContentUtils::CanCallerAccess(domNode
);
1130 if (sendFocusEvent
) {
1131 // return if blurring fails or the focus changes during the blur
1132 if (mFocusedWindow
) {
1133 // if the focus is being moved to another element in the same document,
1134 // or to a descendant, pass the existing window to Blur so that the
1135 // current node in the existing window is cleared. If moving to a
1136 // window elsewhere, we want to maintain the current node in the
1137 // window but still blur it.
1138 PRBool currentIsSameOrAncestor
= IsSameOrAncestor(mFocusedWindow
, newWindow
);
1139 // find the common ancestor of the currently focused window and the new
1140 // window. The ancestor will need to have its currently focused node
1141 // cleared once the document has been blurred. Otherwise, we'll be in a
1142 // state where a document is blurred yet the chain of windows above it
1143 // still points to that document.
1144 // For instance, in the following frame tree:
1148 // D is focused and we want to focus C. Once D has been blurred, we need
1149 // to clear out the focus in A, otherwise A would still maintain that B
1150 // was focused, and B that D was focused.
1151 nsCOMPtr
<nsPIDOMWindow
> commonAncestor
;
1152 if (!isElementInFocusedWindow
)
1153 commonAncestor
= GetCommonAncestor(newWindow
, mFocusedWindow
);
1155 if (!Blur(currentIsSameOrAncestor
? mFocusedWindow
.get() : nsnull
,
1156 commonAncestor
, !isElementInFocusedWindow
))
1160 Focus(newWindow
, contentToFocus
, aFlags
, !isElementInFocusedWindow
,
1161 aFocusChanged
, PR_FALSE
);
1164 // otherwise, for inactive windows and when the caller cannot steal the
1165 // focus, update the node in the window, and raise the window if desired.
1166 if (allowFrameSwitch
)
1167 AdjustWindowFocus(newWindow
, PR_TRUE
);
1169 // set the focus node and method as needed
1170 PRUint32 focusMethod
= aFocusChanged
? aFlags
& FOCUSMETHODANDRING_MASK
:
1171 newWindow
->GetFocusMethod() | (aFlags
& FLAG_SHOWRING
);
1172 newWindow
->SetFocusedNode(contentToFocus
, focusMethod
);
1173 if (aFocusChanged
) {
1174 nsCOMPtr
<nsIDocShell
> docShell
= newWindow
->GetDocShell();
1176 nsCOMPtr
<nsIPresShell
> presShell
;
1177 docShell
->GetPresShell(getter_AddRefs(presShell
));
1179 ScrollIntoView(presShell
, contentToFocus
, aFlags
);
1182 // update the commands even when inactive so that the attributes for that
1183 // window are up to date.
1184 if (allowFrameSwitch
)
1185 newWindow
->UpdateCommands(NS_LITERAL_STRING("focus"));
1187 if (aFlags
& FLAG_RAISE
)
1188 RaiseWindow(newRootWindow
);
1193 nsFocusManager::IsSameOrAncestor(nsPIDOMWindow
* aPossibleAncestor
,
1194 nsPIDOMWindow
* aWindow
)
1196 nsCOMPtr
<nsIWebNavigation
> awebnav(do_GetInterface(aPossibleAncestor
));
1197 nsCOMPtr
<nsIDocShellTreeItem
> ancestordsti
= do_QueryInterface(awebnav
);
1199 nsCOMPtr
<nsIWebNavigation
> fwebnav(do_GetInterface(aWindow
));
1200 nsCOMPtr
<nsIDocShellTreeItem
> dsti
= do_QueryInterface(fwebnav
);
1202 if (dsti
== ancestordsti
)
1204 nsCOMPtr
<nsIDocShellTreeItem
> parentDsti
;
1205 dsti
->GetParent(getter_AddRefs(parentDsti
));
1206 dsti
.swap(parentDsti
);
1212 already_AddRefed
<nsPIDOMWindow
>
1213 nsFocusManager::GetCommonAncestor(nsPIDOMWindow
* aWindow1
,
1214 nsPIDOMWindow
* aWindow2
)
1216 nsCOMPtr
<nsIWebNavigation
> webnav(do_GetInterface(aWindow1
));
1217 nsCOMPtr
<nsIDocShellTreeItem
> dsti1
= do_QueryInterface(webnav
);
1218 NS_ENSURE_TRUE(dsti1
, nsnull
);
1220 webnav
= do_GetInterface(aWindow2
);
1221 nsCOMPtr
<nsIDocShellTreeItem
> dsti2
= do_QueryInterface(webnav
);
1222 NS_ENSURE_TRUE(dsti2
, nsnull
);
1224 nsAutoTPtrArray
<nsIDocShellTreeItem
, 30> parents1
, parents2
;
1226 parents1
.AppendElement(dsti1
);
1227 nsCOMPtr
<nsIDocShellTreeItem
> parentDsti1
;
1228 dsti1
->GetParent(getter_AddRefs(parentDsti1
));
1229 dsti1
.swap(parentDsti1
);
1232 parents2
.AppendElement(dsti2
);
1233 nsCOMPtr
<nsIDocShellTreeItem
> parentDsti2
;
1234 dsti2
->GetParent(getter_AddRefs(parentDsti2
));
1235 dsti2
.swap(parentDsti2
);
1238 PRUint32 pos1
= parents1
.Length();
1239 PRUint32 pos2
= parents2
.Length();
1240 nsIDocShellTreeItem
* parent
= nsnull
;
1242 for (len
= NS_MIN(pos1
, pos2
); len
> 0; --len
) {
1243 nsIDocShellTreeItem
* child1
= parents1
.ElementAt(--pos1
);
1244 nsIDocShellTreeItem
* child2
= parents2
.ElementAt(--pos2
);
1245 if (child1
!= child2
) {
1251 nsCOMPtr
<nsPIDOMWindow
> window
= do_GetInterface(parent
);
1252 return window
.forget();
1256 nsFocusManager::AdjustWindowFocus(nsPIDOMWindow
* aWindow
,
1257 PRBool aCheckPermission
)
1259 PRBool isVisible
= IsWindowVisible(aWindow
);
1261 nsCOMPtr
<nsPIDOMWindow
> window(aWindow
);
1263 // get the containing <iframe> or equivalent element so that it can be
1265 nsCOMPtr
<nsIContent
> frameContent
=
1266 do_QueryInterface(window
->GetFrameElementInternal());
1268 nsCOMPtr
<nsIWebNavigation
> webnav(do_GetInterface(window
));
1269 nsCOMPtr
<nsIDocShellTreeItem
> dsti
= do_QueryInterface(webnav
);
1272 nsCOMPtr
<nsIDocShellTreeItem
> parentDsti
;
1273 dsti
->GetParent(getter_AddRefs(parentDsti
));
1275 window
= do_GetInterface(parentDsti
);
1277 // if the parent window is visible but aWindow was not, then we have
1278 // likely moved up and out from a hidden tab to the browser window, or a
1279 // similar such arrangement. Stop adjusting the current nodes.
1280 if (IsWindowVisible(window
) != isVisible
)
1283 // When aCheckPermission is true, we should check whether the caller can
1284 // access the window or not. If it cannot access, we should stop the
1286 if (aCheckPermission
&& !nsContentUtils::CanCallerAccess(window
))
1289 window
->SetFocusedNode(frameContent
);
1295 nsFocusManager::IsWindowVisible(nsPIDOMWindow
* aWindow
)
1300 nsCOMPtr
<nsIDocShell
> docShell
= aWindow
->GetDocShell();
1301 nsCOMPtr
<nsIBaseWindow
> baseWin(do_QueryInterface(docShell
));
1305 PRBool visible
= PR_FALSE
;
1306 baseWin
->GetVisibility(&visible
);
1311 nsFocusManager::IsNonFocusableRoot(nsIContent
* aContent
)
1313 NS_PRECONDITION(aContent
, "aContent must not be NULL");
1314 NS_PRECONDITION(aContent
->IsInDoc(), "aContent must be in a document");
1316 // If aContent is in designMode, the root element is not focusable.
1317 // NOTE: in designMode, most elements are not focusable, just the document is
1319 // Also, if aContent is not editable but it isn't in designMode, it's not
1321 nsIDocument
* doc
= aContent
->GetCurrentDoc();
1322 NS_ASSERTION(doc
, "aContent must have current document");
1323 return aContent
== doc
->GetRootElement() &&
1324 (doc
->HasFlag(NODE_IS_EDITABLE
) || !aContent
->IsEditable());
1328 nsFocusManager::CheckIfFocusable(nsIContent
* aContent
, PRUint32 aFlags
)
1333 // this is a special case for some XUL elements where an anonymous child is
1334 // actually focusable and not the element itself.
1335 nsIContent
* redirectedFocus
= GetRedirectedFocus(aContent
);
1336 if (redirectedFocus
)
1337 return CheckIfFocusable(redirectedFocus
, aFlags
);
1339 nsCOMPtr
<nsIDocument
> doc
= aContent
->GetCurrentDoc();
1340 // can't focus elements that are not in documents
1344 // Make sure that our frames are up to date
1346 doc
->FlushPendingNotifications(Flush_Frames
);
1348 nsIPresShell
*shell
= doc
->GetShell();
1352 // the root content can always be focused
1353 if (aContent
== doc
->GetRootElement())
1356 // cannot focus content in print preview mode. Only the root can be focused.
1357 nsPresContext
* presContext
= shell
->GetPresContext();
1358 if (presContext
&& presContext
->Type() == nsPresContext::eContext_PrintPreview
)
1361 nsIFrame
* frame
= aContent
->GetPrimaryFrame();
1365 if (aContent
->Tag() == nsGkAtoms::area
&& aContent
->IsHTML()) {
1366 // HTML areas do not have their own frame, and the img frame we get from
1367 // GetPrimaryFrame() is not relevant as to whether it is focusable or
1368 // not, so we have to do all the relevant checks manually for them.
1369 return frame
->AreAncestorViewsVisible() &&
1370 frame
->GetStyleVisibility()->IsVisible() &&
1371 aContent
->IsFocusable() ? aContent
: nsnull
;
1374 // if this is a child frame content node, check if it is visible and
1375 // call the content node's IsFocusable method instead of the frame's
1376 // IsFocusable method. This skips checking the style system and ensures that
1377 // offscreen browsers can still be focused.
1378 nsIDocument
* subdoc
= doc
->GetSubDocumentFor(aContent
);
1379 if (subdoc
&& IsWindowVisible(subdoc
->GetWindow())) {
1380 const nsStyleUserInterface
* ui
= frame
->GetStyleUserInterface();
1381 PRInt32 tabIndex
= (ui
->mUserFocus
== NS_STYLE_USER_FOCUS_IGNORE
||
1382 ui
->mUserFocus
== NS_STYLE_USER_FOCUS_NONE
) ? -1 : 0;
1383 return aContent
->IsFocusable(&tabIndex
, aFlags
& FLAG_BYMOUSE
) ? aContent
: nsnull
;
1386 return frame
->IsFocusable(nsnull
, aFlags
& FLAG_BYMOUSE
) ? aContent
: nsnull
;
1390 nsFocusManager::Blur(nsPIDOMWindow
* aWindowToClear
,
1391 nsPIDOMWindow
* aAncestorWindowToFocus
,
1392 PRBool aIsLeavingDocument
)
1394 // hold a reference to the focused content, which may be null
1395 nsCOMPtr
<nsIContent
> content
= mFocusedContent
;
1397 if (!content
->IsInDoc()) {
1398 mFocusedContent
= nsnull
;
1401 if (content
== mFirstBlurEvent
)
1405 // hold a reference to the focused window
1406 nsCOMPtr
<nsPIDOMWindow
> window
= mFocusedWindow
;
1408 mFocusedContent
= nsnull
;
1412 nsCOMPtr
<nsIDocShell
> docShell
= window
->GetDocShell();
1414 mFocusedContent
= nsnull
;
1418 // Keep a ref to presShell since dispatching the DOM event may cause
1419 // the document to be destroyed.
1420 nsCOMPtr
<nsIPresShell
> presShell
;
1421 docShell
->GetPresShell(getter_AddRefs(presShell
));
1423 mFocusedContent
= nsnull
;
1427 PRBool clearFirstBlurEvent
= PR_FALSE
;
1428 if (!mFirstBlurEvent
) {
1429 mFirstBlurEvent
= content
;
1430 clearFirstBlurEvent
= PR_TRUE
;
1433 // if there is still an active window, adjust the IME state.
1434 // This has to happen before the focus is cleared below, otherwise, the IME
1435 // compositionend event won't get fired at the element being blurred.
1436 nsIMEStateManager::OnTextStateBlur(nsnull
, nsnull
);
1438 nsIMEStateManager::OnChangeFocus(presShell
->GetPresContext(), nsnull
);
1440 // now adjust the actual focus, by clearing the fields in the focus manager
1441 // and in the window.
1442 mFocusedContent
= nsnull
;
1444 aWindowToClear
->SetFocusedNode(nsnull
);
1447 PRINTTAGF("**Element %s has been blurred\n", content
);
1450 // Don't fire blur event on the root content which isn't editable.
1451 PRBool sendBlurEvent
=
1452 content
&& content
->IsInDoc() && !IsNonFocusableRoot(content
);
1454 if (sendBlurEvent
) {
1455 // unusual to pass a content node to SetContentState on a blur,
1456 // but we are just calling it to get the ContentStatesChanged notifications
1457 presShell
->GetPresContext()->EventStateManager()->
1458 SetContentState(content
, NS_EVENT_STATE_FOCUS
);
1461 // if an object/plug-in is being blurred, move the system focus to the
1462 // parent window, otherwise events will still get fired at the plugin.
1463 // But don't do this if we are blurring due to the window being lowered,
1464 // otherwise, the parent window can get raised again.
1465 if (mActiveWindow
) {
1466 nsIFrame
* contentFrame
= content
->GetPrimaryFrame();
1467 nsIObjectFrame
* objectFrame
= do_QueryFrame(contentFrame
);
1469 // note that the presshell's widget is being retrieved here, not the one
1470 // for the object frame.
1471 nsIViewManager
* vm
= presShell
->GetViewManager();
1473 nsCOMPtr
<nsIWidget
> widget
;
1474 vm
->GetRootWidget(getter_AddRefs(widget
));
1476 widget
->SetFocus(PR_FALSE
);
1482 PRBool result
= PR_TRUE
;
1483 if (sendBlurEvent
) {
1484 // if there is an active window, update commands. If there isn't an active
1485 // window, then this was a blur caused by the active window being lowered,
1486 // so there is no need to update the commands
1488 window
->UpdateCommands(NS_LITERAL_STRING("focus"));
1490 SendFocusOrBlurEvent(NS_BLUR_CONTENT
, presShell
,
1491 content
->GetCurrentDoc(), content
, 1, PR_FALSE
);
1494 // if we are leaving the document or the window was lowered, make the caret
1496 if (aIsLeavingDocument
|| !mActiveWindow
)
1497 SetCaretVisible(presShell
, PR_FALSE
, nsnull
);
1499 // at this point, it is expected that this window will be still be
1500 // focused, but the focused content will be null, as it was cleared before
1501 // the event. If this isn't the case, then something else was focused during
1502 // the blur event above and we should just return. However, if
1503 // aIsLeavingDocument is set, a new document is desired, so make sure to
1504 // blur the document and window.
1505 if (mFocusedWindow
!= window
||
1506 (mFocusedContent
!= nsnull
&& !aIsLeavingDocument
)) {
1509 else if (aIsLeavingDocument
) {
1510 window
->TakeFocus(PR_FALSE
, 0);
1512 // clear the focus so that the ancestor frame hierarchy is in the correct
1513 // state. Pass true because aAncestorWindowToFocus is thought to be
1514 // focused at this point.
1515 if (aAncestorWindowToFocus
)
1516 aAncestorWindowToFocus
->SetFocusedNode(nsnull
, 0, PR_TRUE
);
1518 mFocusedWindow
= nsnull
;
1519 mFocusedContent
= nsnull
;
1521 // pass 1 for the focus method when calling SendFocusOrBlurEvent just so
1522 // that the check is made for suppressed documents. Check to ensure that
1523 // the document isn't null in case someone closed it during the blur above
1524 nsCOMPtr
<nsIDocument
> doc
= do_QueryInterface(window
->GetExtantDocument());
1526 SendFocusOrBlurEvent(NS_BLUR_CONTENT
, presShell
, doc
, doc
, 1, PR_FALSE
);
1527 if (mFocusedWindow
== nsnull
)
1528 SendFocusOrBlurEvent(NS_BLUR_CONTENT
, presShell
, doc
, window
, 1, PR_FALSE
);
1530 // check if a different window was focused
1531 result
= (mFocusedWindow
== nsnull
&& mActiveWindow
);
1533 else if (mActiveWindow
) {
1534 // Otherwise, the blur of the element without blurring the document
1535 // occurred normally. Call UpdateCaret to redisplay the caret at the right
1536 // location within the document. This is needed to ensure that the caret
1537 // used for caret browsing is made visible again when an input field is
1539 UpdateCaret(PR_FALSE
, PR_TRUE
, nsnull
);
1542 if (clearFirstBlurEvent
)
1543 mFirstBlurEvent
= nsnull
;
1549 nsFocusManager::Focus(nsPIDOMWindow
* aWindow
,
1550 nsIContent
* aContent
,
1552 PRBool aIsNewDocument
,
1553 PRBool aFocusChanged
,
1554 PRBool aWindowRaised
)
1559 if (aContent
&& (aContent
== mFirstFocusEvent
|| aContent
== mFirstBlurEvent
))
1562 // Keep a reference to the presShell since dispatching the DOM event may
1563 // cause the document to be destroyed.
1564 nsCOMPtr
<nsIDocShell
> docShell
= aWindow
->GetDocShell();
1568 nsCOMPtr
<nsIPresShell
> presShell
;
1569 docShell
->GetPresShell(getter_AddRefs(presShell
));
1573 // If the focus actually changed, set the focus method (mouse, keyboard, etc).
1574 // Otherwise, just get the current focus method and use that. This ensures
1575 // that the method is set during the document and window focus events.
1576 PRUint32 focusMethod
= aFocusChanged
? aFlags
& FOCUSMETHODANDRING_MASK
:
1577 aWindow
->GetFocusMethod() | (aFlags
& FLAG_SHOWRING
);
1579 if (!IsWindowVisible(aWindow
)) {
1580 // if the window isn't visible, for instance because it is a hidden tab,
1581 // update the current focus and scroll it into view but don't do anything else
1582 if (CheckIfFocusable(aContent
, aFlags
)) {
1583 aWindow
->SetFocusedNode(aContent
, focusMethod
);
1585 ScrollIntoView(presShell
, aContent
, aFlags
);
1590 PRBool clearFirstFocusEvent
= PR_FALSE
;
1591 if (!mFirstFocusEvent
) {
1592 mFirstFocusEvent
= aContent
;
1593 clearFirstFocusEvent
= PR_TRUE
;
1597 PRINTTAGF("**Element %s has been focused", aContent
);
1598 nsCOMPtr
<nsIDocument
> docm
= do_QueryInterface(aWindow
->GetExtantDocument());
1600 PRINTTAGF(" from %s", docm
->GetRootElement());
1601 printf(" [Newdoc: %d FocusChanged: %d Raised: %d Flags: %x]\n",
1602 aIsNewDocument
, aFocusChanged
, aWindowRaised
, aFlags
);
1605 if (aIsNewDocument
) {
1606 // if this is a new document, update the parent chain of frames so that
1607 // focus can be traversed from the top level down to the newly focused
1609 AdjustWindowFocus(aWindow
, PR_FALSE
);
1611 // Update the window touch registration to reflect the state of
1612 // the new document that got focus
1613 aWindow
->UpdateTouchState();
1616 // indicate that the window has taken focus.
1617 if (aWindow
->TakeFocus(PR_TRUE
, focusMethod
))
1618 aIsNewDocument
= PR_TRUE
;
1620 mFocusedWindow
= aWindow
;
1622 // update the system focus.
1623 nsIViewManager
* vm
= presShell
->GetViewManager();
1625 nsCOMPtr
<nsIWidget
> widget
;
1626 vm
->GetRootWidget(getter_AddRefs(widget
));
1628 widget
->SetFocus(PR_FALSE
);
1631 // if switching to a new document, first fire the focus event on the
1632 // document and then the window.
1633 if (aIsNewDocument
) {
1634 nsCOMPtr
<nsIDocument
> doc
= do_QueryInterface(aWindow
->GetExtantDocument());
1636 SendFocusOrBlurEvent(NS_FOCUS_CONTENT
, presShell
, doc
,
1637 doc
, aFlags
& FOCUSMETHOD_MASK
, aWindowRaised
);
1638 if (mFocusedWindow
== aWindow
&& mFocusedContent
== nsnull
)
1639 SendFocusOrBlurEvent(NS_FOCUS_CONTENT
, presShell
, doc
,
1640 aWindow
, aFlags
& FOCUSMETHOD_MASK
, aWindowRaised
);
1643 // check to ensure that the element is still focusable, and that nothing
1644 // else was focused during the events above.
1645 if (CheckIfFocusable(aContent
, aFlags
) &&
1646 mFocusedWindow
== aWindow
&& mFocusedContent
== nsnull
) {
1647 mFocusedContent
= aContent
;
1648 aWindow
->SetFocusedNode(aContent
, focusMethod
);
1650 PRBool sendFocusEvent
=
1651 aContent
&& aContent
->IsInDoc() && !IsNonFocusableRoot(aContent
);
1652 if (sendFocusEvent
) {
1653 // if the focused element changed, scroll it into view
1655 ScrollIntoView(presShell
, aContent
, aFlags
);
1657 // inform the EventStateManager so that content state change notifications
1659 nsPresContext
* presContext
= presShell
->GetPresContext();
1660 presContext
->EventStateManager()->
1661 SetContentState(aContent
, NS_EVENT_STATE_FOCUS
);
1663 // if this is an object/plug-in, focus the plugin's widget. Note that we might
1664 // no longer be in the same document, due to the events we fired above when
1666 if (presShell
->GetDocument() == aContent
->GetDocument()) {
1667 nsIFrame
* contentFrame
= aContent
->GetPrimaryFrame();
1668 nsIObjectFrame
* objectFrame
= do_QueryFrame(contentFrame
);
1670 nsIWidget
* widget
= objectFrame
->GetWidget();
1672 widget
->SetFocus(PR_FALSE
);
1676 nsIMEStateManager::OnChangeFocus(presContext
, aContent
);
1678 // as long as this focus wasn't because a window was raised, update the
1680 // XXXndeakin P2 someone could adjust the focus during the update
1682 aWindow
->UpdateCommands(NS_LITERAL_STRING("focus"));
1684 SendFocusOrBlurEvent(NS_FOCUS_CONTENT
, presShell
, aContent
->GetCurrentDoc(),
1685 aContent
, aFlags
& FOCUSMETHOD_MASK
, aWindowRaised
);
1687 nsIMEStateManager::OnTextStateFocus(presContext
, aContent
);
1689 nsPresContext
* presContext
= presShell
->GetPresContext();
1690 nsIMEStateManager::OnTextStateBlur(presContext
, nsnull
);
1691 nsIMEStateManager::OnChangeFocus(presContext
, nsnull
);
1692 if (!aWindowRaised
) {
1693 aWindow
->UpdateCommands(NS_LITERAL_STRING("focus"));
1698 nsPresContext
* presContext
= presShell
->GetPresContext();
1699 nsIMEStateManager::OnTextStateBlur(presContext
, nsnull
);
1700 nsIMEStateManager::OnChangeFocus(presContext
, nsnull
);
1703 aWindow
->UpdateCommands(NS_LITERAL_STRING("focus"));
1706 // update the caret visibility and position to match the newly focused
1707 // element. However, don't update the position if this was a focus due to a
1708 // mouse click as the selection code would already have moved the caret as
1709 // needed. If this is a different document than was focused before, also
1710 // update the caret's visibility. If this is the same document, the caret
1711 // visibility should be the same as before so there is no need to update it.
1712 if (mFocusedContent
== aContent
)
1713 UpdateCaret(aFocusChanged
&& !(aFlags
& FLAG_BYMOUSE
), aIsNewDocument
,
1716 if (clearFirstFocusEvent
)
1717 mFirstFocusEvent
= nsnull
;
1720 class FocusBlurEvent
: public nsRunnable
1723 FocusBlurEvent(nsISupports
* aTarget
, PRUint32 aType
,
1724 nsPresContext
* aContext
, PRBool aWindowRaised
)
1725 : mTarget(aTarget
), mType(aType
), mContext(aContext
),
1726 mWindowRaised(aWindowRaised
) {}
1730 nsFocusEvent
event(PR_TRUE
, mType
);
1731 event
.flags
|= NS_EVENT_FLAG_CANT_BUBBLE
;
1732 event
.fromRaise
= mWindowRaised
;
1733 return nsEventDispatcher::Dispatch(mTarget
, mContext
, &event
);
1736 nsCOMPtr
<nsISupports
> mTarget
;
1738 nsRefPtr
<nsPresContext
> mContext
;
1739 PRBool mWindowRaised
;
1743 nsFocusManager::SendFocusOrBlurEvent(PRUint32 aType
,
1744 nsIPresShell
* aPresShell
,
1745 nsIDocument
* aDocument
,
1746 nsISupports
* aTarget
,
1747 PRUint32 aFocusMethod
,
1748 PRBool aWindowRaised
)
1750 NS_ASSERTION(aType
== NS_FOCUS_CONTENT
|| aType
== NS_BLUR_CONTENT
,
1751 "Wrong event type for SendFocusOrBlurEvent");
1753 nsCOMPtr
<nsPIDOMEventTarget
> eventTarget
= do_QueryInterface(aTarget
);
1755 // for focus events, if this event was from a mouse or key and event
1756 // handling on the document is suppressed, queue the event and fire it
1757 // later. For blur events, a non-zero value would be set for aFocusMethod.
1758 if (aFocusMethod
&& aDocument
&& aDocument
->EventHandlingSuppressed()) {
1759 // aFlags is always 0 when aWindowRaised is true so this won't be called
1760 // on a window raise.
1761 NS_ASSERTION(!aWindowRaised
, "aWindowRaised should not be set");
1763 for (PRUint32 i
= mDelayedBlurFocusEvents
.Length(); i
> 0; --i
) {
1764 // if this event was already queued, remove it and append it to the end
1765 if (mDelayedBlurFocusEvents
[i
- 1].mType
== aType
&&
1766 mDelayedBlurFocusEvents
[i
- 1].mPresShell
== aPresShell
&&
1767 mDelayedBlurFocusEvents
[i
- 1].mDocument
== aDocument
&&
1768 mDelayedBlurFocusEvents
[i
- 1].mTarget
== eventTarget
) {
1769 mDelayedBlurFocusEvents
.RemoveElementAt(i
- 1);
1773 mDelayedBlurFocusEvents
.AppendElement(
1774 nsDelayedBlurOrFocusEvent(aType
, aPresShell
, aDocument
, eventTarget
));
1778 nsContentUtils::AddScriptRunner(
1779 new FocusBlurEvent(aTarget
, aType
, aPresShell
->GetPresContext(),
1784 nsFocusManager::ScrollIntoView(nsIPresShell
* aPresShell
,
1785 nsIContent
* aContent
,
1788 // if the noscroll flag isn't set, scroll the newly focused element into view
1789 if (!(aFlags
& FLAG_NOSCROLL
))
1790 aPresShell
->ScrollContentIntoView(aContent
,
1791 NS_PRESSHELL_SCROLL_IF_NOT_VISIBLE
,
1792 NS_PRESSHELL_SCROLL_IF_NOT_VISIBLE
);
1797 nsFocusManager::RaiseWindow(nsPIDOMWindow
* aWindow
)
1799 // don't raise windows that are already raised or are in the process of
1801 if (!aWindow
|| aWindow
== mActiveWindow
|| aWindow
== mWindowBeingLowered
)
1804 #if defined(XP_WIN) || defined(XP_OS2)
1805 // Windows would rather we focus the child widget, otherwise, the toplevel
1806 // widget will always end up being focused. Fortunately, focusing the child
1807 // widget will also have the effect of raising the window this widget is in.
1808 // But on other platforms, we can just focus the toplevel widget to raise
1810 nsCOMPtr
<nsPIDOMWindow
> childWindow
;
1811 GetFocusedDescendant(aWindow
, PR_TRUE
, getter_AddRefs(childWindow
));
1813 childWindow
= aWindow
;
1815 nsCOMPtr
<nsIDocShell
> docShell
= aWindow
->GetDocShell();
1819 nsCOMPtr
<nsIPresShell
> presShell
;
1820 docShell
->GetPresShell(getter_AddRefs(presShell
));
1824 nsIViewManager
* vm
= presShell
->GetViewManager();
1826 nsCOMPtr
<nsIWidget
> widget
;
1827 vm
->GetRootWidget(getter_AddRefs(widget
));
1829 widget
->SetFocus(PR_TRUE
);
1832 nsCOMPtr
<nsIWebNavigation
> webnav
= do_GetInterface(aWindow
);
1833 nsCOMPtr
<nsIBaseWindow
> treeOwnerAsWin
= do_QueryInterface(webnav
);
1834 if (treeOwnerAsWin
) {
1835 nsCOMPtr
<nsIWidget
> widget
;
1836 treeOwnerAsWin
->GetMainWidget(getter_AddRefs(widget
));
1838 widget
->SetFocus(PR_TRUE
);
1844 nsFocusManager::UpdateCaret(PRBool aMoveCaretToFocus
,
1845 PRBool aUpdateVisibility
,
1846 nsIContent
* aContent
)
1849 printf("Update Caret: %d %d\n", aMoveCaretToFocus
, aUpdateVisibility
);
1852 if (!mFocusedWindow
)
1855 // this is called when a document is focused or when the caretbrowsing
1856 // preference is changed
1857 nsCOMPtr
<nsIDocShell
> focusedDocShell
= mFocusedWindow
->GetDocShell();
1858 nsCOMPtr
<nsIDocShellTreeItem
> dsti
= do_QueryInterface(focusedDocShell
);
1863 dsti
->GetItemType(&itemType
);
1864 if (itemType
== nsIDocShellTreeItem::typeChrome
)
1865 return; // Never browse with caret in chrome
1867 PRPackedBool browseWithCaret
=
1868 nsContentUtils::GetBoolPref("accessibility.browsewithcaret");
1870 nsCOMPtr
<nsIPresShell
> presShell
;
1871 focusedDocShell
->GetPresShell(getter_AddRefs(presShell
));
1875 // If this is an editable document which isn't contentEditable, or a
1876 // contentEditable document and the node to focus is contentEditable,
1877 // return, so that we don't mess with caret visibility.
1878 PRBool isEditable
= PR_FALSE
;
1879 nsCOMPtr
<nsIEditorDocShell
> editorDocShell(do_QueryInterface(dsti
));
1880 if (editorDocShell
) {
1881 editorDocShell
->GetEditable(&isEditable
);
1884 nsCOMPtr
<nsIHTMLDocument
> doc
=
1885 do_QueryInterface(presShell
->GetDocument());
1887 PRBool isContentEditableDoc
=
1888 doc
&& doc
->GetEditingState() == nsIHTMLDocument::eContentEditable
;
1890 PRBool isFocusEditable
=
1891 aContent
&& aContent
->HasFlag(NODE_IS_EDITABLE
);
1892 if (!isContentEditableDoc
|| isFocusEditable
)
1897 if (!isEditable
&& aMoveCaretToFocus
)
1898 MoveCaretToFocus(presShell
, aContent
);
1900 if (!aUpdateVisibility
)
1903 // XXXndeakin this doesn't seem right. It should be checking for this only
1904 // on the nearest ancestor frame which is a chrome frame. But this is
1905 // what the existing code does, so just leave it for now.
1906 if (!browseWithCaret
) {
1907 nsCOMPtr
<nsIContent
> docContent
=
1908 do_QueryInterface(mFocusedWindow
->GetFrameElementInternal());
1910 browseWithCaret
= docContent
->AttrValueIs(kNameSpaceID_None
,
1911 nsGkAtoms::showcaret
,
1912 NS_LITERAL_STRING("true"),
1916 SetCaretVisible(presShell
, browseWithCaret
, aContent
);
1920 nsFocusManager::MoveCaretToFocus(nsIPresShell
* aPresShell
, nsIContent
* aContent
)
1922 // rangeDoc is a document interface we can create a range with
1923 nsCOMPtr
<nsIDOMDocumentRange
> rangeDoc(do_QueryInterface(aPresShell
->GetDocument()));
1925 nsCOMPtr
<nsFrameSelection
> frameSelection
= aPresShell
->FrameSelection();
1926 nsCOMPtr
<nsISelection
> domSelection
= frameSelection
->
1927 GetSelection(nsISelectionController::SELECTION_NORMAL
);
1929 nsCOMPtr
<nsIDOMNode
> currentFocusNode(do_QueryInterface(aContent
));
1930 // First clear the selection. This way, if there is no currently focused
1931 // content, the selection will just be cleared.
1932 domSelection
->RemoveAllRanges();
1933 if (currentFocusNode
) {
1934 nsCOMPtr
<nsIDOMRange
> newRange
;
1935 nsresult rv
= rangeDoc
->CreateRange(getter_AddRefs(newRange
));
1936 if (NS_SUCCEEDED(rv
)) {
1937 // Set the range to the start of the currently focused node
1938 // Make sure it's collapsed
1939 newRange
->SelectNodeContents(currentFocusNode
);
1940 nsCOMPtr
<nsIDOMNode
> firstChild
;
1941 currentFocusNode
->GetFirstChild(getter_AddRefs(firstChild
));
1943 aContent
->IsNodeOfType(nsINode::eHTML_FORM_CONTROL
)) {
1944 // If current focus node is a leaf, set range to before the
1945 // node by using the parent as a container.
1946 // This prevents it from appearing as selected.
1947 newRange
->SetStartBefore(currentFocusNode
);
1948 newRange
->SetEndBefore(currentFocusNode
);
1950 domSelection
->AddRange(newRange
);
1951 domSelection
->CollapseToStart();
1959 nsFocusManager::SetCaretVisible(nsIPresShell
* aPresShell
,
1961 nsIContent
* aContent
)
1963 // When browsing with caret, make sure caret is visible after new focus
1964 // Return early if there is no caret. This can happen for the testcase
1965 // for bug 308025 where a window is closed in a blur handler.
1966 nsRefPtr
<nsCaret
> caret
= aPresShell
->GetCaret();
1970 PRBool caretVisible
= PR_FALSE
;
1971 caret
->GetCaretVisible(&caretVisible
);
1972 if (!aVisible
&& !caretVisible
)
1975 nsCOMPtr
<nsFrameSelection
> frameSelection
;
1977 NS_ASSERTION(aContent
->GetDocument() == aPresShell
->GetDocument(),
1979 nsIFrame
*focusFrame
= aContent
->GetPrimaryFrame();
1981 frameSelection
= focusFrame
->GetFrameSelection();
1984 nsCOMPtr
<nsFrameSelection
> docFrameSelection
= aPresShell
->FrameSelection();
1986 if (docFrameSelection
&& caret
&&
1987 (frameSelection
== docFrameSelection
|| !aContent
)) {
1988 nsISelection
* domSelection
= docFrameSelection
->
1989 GetSelection(nsISelectionController::SELECTION_NORMAL
);
1991 // First, tell the caret which selection to use
1992 caret
->SetCaretDOMSelection(domSelection
);
1994 // In content, we need to set the caret. The only special case is edit
1995 // fields, which have a different frame selection from the document.
1996 // They will take care of making the caret visible themselves.
1998 nsCOMPtr
<nsISelectionController
> selCon(do_QueryInterface(aPresShell
));
2000 return NS_ERROR_FAILURE
;
2002 selCon
->SetCaretEnabled(aVisible
);
2003 caret
->SetCaretVisible(aVisible
);
2011 nsFocusManager::GetSelectionLocation(nsIDocument
* aDocument
,
2012 nsIPresShell
* aPresShell
,
2013 nsIContent
**aStartContent
,
2014 nsIContent
**aEndContent
)
2016 *aStartContent
= *aEndContent
= nsnull
;
2017 nsresult rv
= NS_ERROR_FAILURE
;
2019 nsPresContext
* presContext
= aPresShell
->GetPresContext();
2020 NS_ASSERTION(presContext
, "mPresContent is null!!");
2022 nsCOMPtr
<nsFrameSelection
> frameSelection
;
2023 frameSelection
= aPresShell
->FrameSelection();
2025 nsCOMPtr
<nsISelection
> domSelection
;
2026 if (frameSelection
) {
2027 domSelection
= frameSelection
->
2028 GetSelection(nsISelectionController::SELECTION_NORMAL
);
2031 nsCOMPtr
<nsIDOMNode
> startNode
, endNode
;
2032 PRBool isCollapsed
= PR_FALSE
;
2033 nsCOMPtr
<nsIContent
> startContent
, endContent
;
2034 PRInt32 startOffset
= 0;
2036 domSelection
->GetIsCollapsed(&isCollapsed
);
2037 nsCOMPtr
<nsIDOMRange
> domRange
;
2038 rv
= domSelection
->GetRangeAt(0, getter_AddRefs(domRange
));
2040 domRange
->GetStartContainer(getter_AddRefs(startNode
));
2041 domRange
->GetEndContainer(getter_AddRefs(endNode
));
2042 domRange
->GetStartOffset(&startOffset
);
2044 nsIContent
*childContent
= nsnull
;
2046 startContent
= do_QueryInterface(startNode
);
2047 if (startContent
&& startContent
->IsElement()) {
2048 NS_ASSERTION(startOffset
>= 0, "Start offset cannot be negative");
2049 childContent
= startContent
->GetChildAt(startOffset
);
2051 startContent
= childContent
;
2055 endContent
= do_QueryInterface(endNode
);
2056 if (endContent
&& endContent
->IsElement()) {
2057 PRInt32 endOffset
= 0;
2058 domRange
->GetEndOffset(&endOffset
);
2059 NS_ASSERTION(endOffset
>= 0, "End offset cannot be negative");
2060 childContent
= endContent
->GetChildAt(endOffset
);
2062 endContent
= childContent
;
2068 rv
= NS_ERROR_INVALID_ARG
;
2071 nsIFrame
*startFrame
= nsnull
;
2073 startFrame
= startContent
->GetPrimaryFrame();
2075 // Next check to see if our caret is at the very end of a node
2076 // If so, the caret is actually sitting in front of the next
2077 // logical frame's primary node - so for this case we need to
2078 // change caretContent to that node.
2080 nsCOMPtr
<nsIDOMNode
> domNode(do_QueryInterface(startContent
));
2082 domNode
->GetNodeType(&nodeType
);
2084 if (nodeType
== nsIDOMNode::TEXT_NODE
) {
2085 nsAutoString nodeValue
;
2086 domNode
->GetNodeValue(nodeValue
);
2088 PRBool isFormControl
=
2089 startContent
->IsNodeOfType(nsINode::eHTML_FORM_CONTROL
);
2091 if (nodeValue
.Length() == (PRUint32
)startOffset
&& !isFormControl
&&
2092 startContent
!= aDocument
->GetRootElement()) {
2093 // Yes, indeed we were at the end of the last node
2094 nsCOMPtr
<nsIFrameEnumerator
> frameTraversal
;
2095 nsresult rv
= NS_NewFrameTraversal(getter_AddRefs(frameTraversal
),
2096 presContext
, startFrame
,
2098 PR_FALSE
, // aVisual
2099 PR_FALSE
, // aLockInScrollView
2100 PR_TRUE
// aFollowOOFs
2102 NS_ENSURE_SUCCESS(rv
, rv
);
2104 nsIFrame
*newCaretFrame
= nsnull
;
2105 nsCOMPtr
<nsIContent
> newCaretContent
= startContent
;
2106 PRBool
endOfSelectionInStartNode(startContent
== endContent
);
2108 // Continue getting the next frame until the primary content for the frame
2109 // we are on changes - we don't want to be stuck in the same place
2110 frameTraversal
->Next();
2111 newCaretFrame
= static_cast<nsIFrame
*>(frameTraversal
->CurrentItem());
2112 if (nsnull
== newCaretFrame
)
2114 newCaretContent
= newCaretFrame
->GetContent();
2115 } while (!newCaretContent
|| newCaretContent
== startContent
);
2117 if (newCaretFrame
&& newCaretContent
) {
2118 // If the caret is exactly at the same position of the new frame,
2119 // then we can use the newCaretFrame and newCaretContent for our position
2120 nsRefPtr
<nsCaret
> caret
= aPresShell
->GetCaret();
2122 nsIFrame
*frame
= caret
->GetGeometry(domSelection
, &caretRect
);
2124 nsPoint caretWidgetOffset
;
2125 nsIWidget
*widget
= frame
->GetNearestWidget(caretWidgetOffset
);
2126 caretRect
.MoveBy(caretWidgetOffset
);
2127 nsPoint newCaretOffset
;
2128 nsIWidget
*newCaretWidget
= newCaretFrame
->GetNearestWidget(newCaretOffset
);
2129 if (widget
== newCaretWidget
&& caretRect
.y
== newCaretOffset
.y
&&
2130 caretRect
.x
== newCaretOffset
.x
) {
2131 // The caret is at the start of the new element.
2132 startFrame
= newCaretFrame
;
2133 startContent
= newCaretContent
;
2134 if (endOfSelectionInStartNode
) {
2135 endContent
= newCaretContent
; // Ensure end of selection is not before start
2145 *aStartContent
= startContent
;
2146 *aEndContent
= endContent
;
2147 NS_IF_ADDREF(*aStartContent
);
2148 NS_IF_ADDREF(*aEndContent
);
2154 nsFocusManager::DetermineElementToMoveFocus(nsPIDOMWindow
* aWindow
,
2155 nsIContent
* aStartContent
,
2157 nsIContent
** aNextContent
)
2159 *aNextContent
= nsnull
;
2161 nsCOMPtr
<nsIDocShell
> docShell
= aWindow
->GetDocShell();
2165 nsCOMPtr
<nsIContent
> startContent
= aStartContent
;
2166 if (!startContent
&& aType
!= MOVEFOCUS_CARET
)
2167 startContent
= aWindow
->GetFocusedNode();
2169 nsCOMPtr
<nsIDocument
> doc
;
2171 doc
= startContent
->GetCurrentDoc();
2173 doc
= do_QueryInterface(aWindow
->GetExtantDocument());
2177 nsCOMPtr
<nsILookAndFeel
> lookNFeel(do_GetService(kLookAndFeelCID
));
2178 lookNFeel
->GetMetric(nsILookAndFeel::eMetric_TabFocusModel
,
2179 nsIContent::sTabFocusModel
);
2181 if (aType
== MOVEFOCUS_ROOT
) {
2182 NS_IF_ADDREF(*aNextContent
= GetRootForFocus(aWindow
, doc
, PR_FALSE
, PR_FALSE
));
2185 if (aType
== MOVEFOCUS_FORWARDDOC
) {
2186 NS_IF_ADDREF(*aNextContent
= GetNextTabbableDocument(PR_TRUE
));
2189 if (aType
== MOVEFOCUS_BACKWARDDOC
) {
2190 NS_IF_ADDREF(*aNextContent
= GetNextTabbableDocument(PR_FALSE
));
2194 nsIContent
* rootContent
= doc
->GetRootElement();
2195 NS_ENSURE_TRUE(rootContent
, NS_OK
);
2197 nsIPresShell
*presShell
= doc
->GetShell();
2198 NS_ENSURE_TRUE(presShell
, NS_OK
);
2200 if (aType
== MOVEFOCUS_FIRST
) {
2202 startContent
= rootContent
;
2203 return GetNextTabbableContent(presShell
, startContent
,
2204 nsnull
, startContent
,
2205 PR_TRUE
, 1, PR_FALSE
, aNextContent
);
2207 if (aType
== MOVEFOCUS_LAST
) {
2209 startContent
= rootContent
;
2210 return GetNextTabbableContent(presShell
, startContent
,
2211 nsnull
, startContent
,
2212 PR_FALSE
, 0, PR_FALSE
, aNextContent
);
2215 PRBool forward
= (aType
== MOVEFOCUS_FORWARD
|| aType
== MOVEFOCUS_CARET
);
2216 PRBool doNavigation
= PR_TRUE
;
2217 PRBool ignoreTabIndex
= PR_FALSE
;
2218 // when a popup is open, we want to ensure that tab navigation occurs only
2219 // within the most recently opened panel. If a popup is open, its frame will
2220 // be stored in popupFrame.
2221 nsIFrame
* popupFrame
= nsnull
;
2223 PRInt32 tabIndex
= forward
? 1 : 0;
2225 nsIFrame
* frame
= startContent
->GetPrimaryFrame();
2226 if (startContent
->Tag() == nsGkAtoms::area
&&
2227 startContent
->IsHTML())
2228 startContent
->IsFocusable(&tabIndex
);
2230 frame
->IsFocusable(&tabIndex
, 0);
2232 startContent
->IsFocusable(&tabIndex
);
2234 // if the current element isn't tabbable, ignore the tabindex and just
2235 // look for the next element. The root content won't have a tabindex
2236 // so just treat this as the beginning of the tab order.
2239 if (startContent
!= rootContent
)
2240 ignoreTabIndex
= PR_TRUE
;
2243 // check if the focus is currently inside a popup. Elements such as the
2244 // autocomplete widget use the noautofocus attribute to allow the focus to
2245 // remain outside the popup when it is opened.
2247 popupFrame
= nsLayoutUtils::GetClosestFrameOfType(frame
,
2248 nsGkAtoms::menuPopupFrame
);
2252 // Don't navigate outside of a popup, so pretend that the
2253 // root content is the popup itself
2254 rootContent
= popupFrame
->GetContent();
2255 NS_ASSERTION(rootContent
, "Popup frame doesn't have a content node");
2257 else if (!forward
) {
2258 // If focus moves backward and when current focused node is root
2259 // content or <body> element which is editable by contenteditable
2260 // attribute, focus should move to its parent document.
2261 if (startContent
== rootContent
) {
2262 doNavigation
= PR_FALSE
;
2264 nsIDocument
* doc
= startContent
->GetCurrentDoc();
2266 nsLayoutUtils::GetEditableRootContentByContentEditable(doc
)) {
2267 doNavigation
= PR_FALSE
;
2274 // if there is no focus, yet a panel is open, focus the first item in
2276 nsXULPopupManager
* pm
= nsXULPopupManager::GetInstance();
2278 popupFrame
= pm
->GetTopPopup(ePopupTypePanel
);
2281 rootContent
= popupFrame
->GetContent();
2282 NS_ASSERTION(rootContent
, "Popup frame doesn't have a content node");
2283 startContent
= rootContent
;
2286 // Otherwise, for content shells, start from the location of the caret.
2288 nsCOMPtr
<nsIDocShellTreeItem
> shellItem
= do_QueryInterface(docShell
);
2289 shellItem
->GetItemType(&itemType
);
2290 if (itemType
!= nsIDocShellTreeItem::typeChrome
) {
2291 nsCOMPtr
<nsIContent
> endSelectionContent
;
2292 GetSelectionLocation(doc
, presShell
,
2293 getter_AddRefs(startContent
),
2294 getter_AddRefs(endSelectionContent
));
2295 // If the selection is on the rootContent, then there is no selection
2296 if (startContent
== rootContent
) {
2297 startContent
= nsnull
;
2299 else if (startContent
&& startContent
->HasFlag(NODE_IS_EDITABLE
)) {
2300 // Don't start from the selection if the selection is in a
2301 // contentEditable region.
2302 nsCOMPtr
<nsIHTMLDocument
> htmlDoc
= do_QueryInterface(doc
);
2304 htmlDoc
->GetEditingState() == nsIHTMLDocument::eContentEditable
)
2305 startContent
= nsnull
;
2308 if (aType
== MOVEFOCUS_CARET
) {
2309 // GetFocusInSelection finds a focusable link near the caret.
2310 // If there is no start content though, don't do this to avoid
2311 // focusing something unexpected.
2313 GetFocusInSelection(aWindow
, startContent
,
2314 endSelectionContent
, aNextContent
);
2320 // when starting from a selection, we always want to find the next or
2321 // previous element in the document. So the tabindex on elements
2322 // should be ignored.
2323 ignoreTabIndex
= PR_TRUE
;
2327 if (!startContent
) {
2328 // otherwise, just use the root content as the starting point
2329 startContent
= rootContent
;
2330 NS_ENSURE_TRUE(startContent
, NS_OK
);
2335 NS_ASSERTION(startContent
, "starting content not set");
2337 // keep a reference to the starting content. If we find that again, it means
2338 // we've iterated around completely and we don't want to adjust the focus.
2339 // The skipOriginalContentCheck will be set to true only for the first time
2340 // GetNextTabbableContent is called. This ensures that we don't break out
2341 // when nothing is focused to start with. Specifically,
2342 // GetNextTabbableContent first checks the root content -- which happens to
2343 // be the same as the start content -- when nothing is focused and tabbing
2344 // forward. Without skipOriginalContentCheck set to true, we'd end up
2345 // returning right away and focusing nothing. Luckily, GetNextTabbableContent
2346 // will never wrap around on its own, and can only return the original
2347 // content when it is called a second time or later.
2348 PRBool skipOriginalContentCheck
= PR_TRUE
;
2349 nsIContent
* originalStartContent
= startContent
;
2351 #ifdef DEBUG_FOCUS_NAVIGATION
2352 PRINTTAGF("Focus Navigation Start Content %s\n", startContent
);
2353 printf("[Tabindex: %d Ignore: %d]", tabIndex
, ignoreTabIndex
);
2358 nsCOMPtr
<nsIContent
> nextFocus
;
2359 nsresult rv
= GetNextTabbableContent(presShell
, rootContent
,
2360 skipOriginalContentCheck
? nsnull
: originalStartContent
,
2361 startContent
, forward
,
2362 tabIndex
, ignoreTabIndex
,
2363 getter_AddRefs(nextFocus
));
2364 NS_ENSURE_SUCCESS(rv
, rv
);
2366 // found a content node to focus.
2368 #ifdef DEBUG_FOCUS_NAVIGATION
2369 PRINTTAGF("Next Content: %s\n", nextFocus
);
2371 // as long as the found node was not the same as the starting node,
2372 // set it as the return value.
2373 if (nextFocus
!= originalStartContent
)
2374 NS_ADDREF(*aNextContent
= nextFocus
);
2379 // in a popup, so start again from the beginning of the popup. However,
2380 // if we already started at the beginning, then there isn't anything to
2381 // focus, so just return
2382 if (startContent
!= rootContent
) {
2383 startContent
= rootContent
;
2384 tabIndex
= forward
? 1 : 0;
2391 doNavigation
= PR_TRUE
;
2392 skipOriginalContentCheck
= PR_FALSE
;
2393 ignoreTabIndex
= PR_FALSE
;
2395 // reached the beginning or end of the document. Traverse up to the parent
2396 // document and try again.
2397 nsCOMPtr
<nsIDocShellTreeItem
> dsti
= do_QueryInterface(docShell
);
2399 nsCOMPtr
<nsIDocShellTreeItem
> docShellParent
;
2400 dsti
->GetParent(getter_AddRefs(docShellParent
));
2401 if (docShellParent
) {
2402 // move up to the parent shell and try again from there.
2404 // first, get the frame element this window is inside.
2405 nsCOMPtr
<nsPIDOMWindow
> piWindow
= do_GetInterface(docShell
);
2406 NS_ENSURE_TRUE(piWindow
, NS_ERROR_FAILURE
);
2408 // Next, retrieve the parent docshell, document and presshell.
2409 docShell
= do_QueryInterface(docShellParent
);
2410 NS_ENSURE_TRUE(docShell
, NS_ERROR_FAILURE
);
2412 nsCOMPtr
<nsPIDOMWindow
> piParentWindow
= do_GetInterface(docShellParent
);
2413 NS_ENSURE_TRUE(piParentWindow
, NS_ERROR_FAILURE
);
2414 doc
= do_QueryInterface(piParentWindow
->GetExtantDocument());
2415 NS_ENSURE_TRUE(doc
, NS_ERROR_FAILURE
);
2417 presShell
= doc
->GetShell();
2419 rootContent
= doc
->GetRootElement();
2420 startContent
= do_QueryInterface(piWindow
->GetFrameElementInternal());
2422 nsIFrame
* frame
= startContent
->GetPrimaryFrame();
2426 frame
->IsFocusable(&tabIndex
, 0);
2429 ignoreTabIndex
= PR_TRUE
;
2432 // if the frame is inside a popup, make sure to scan only within the
2433 // popup. This handles the situation of tabbing amongst elements
2434 // inside an iframe which is itself inside a popup. Otherwise,
2435 // navigation would move outside the popup when tabbing outside the
2437 popupFrame
= nsLayoutUtils::GetClosestFrameOfType(frame
,
2438 nsGkAtoms::menuPopupFrame
);
2440 rootContent
= popupFrame
->GetContent();
2441 NS_ASSERTION(rootContent
, "Popup frame doesn't have a content node");
2445 startContent
= rootContent
;
2446 tabIndex
= forward
? 1 : 0;
2450 // no parent, so call the tree owner. This will tell the embedder that
2451 // it should take the focus.
2453 docShell
->TabToTreeOwner(forward
, &tookFocus
);
2454 // if the tree owner, took the focus, blur the current content
2456 nsCOMPtr
<nsPIDOMWindow
> window
= do_GetInterface(docShell
);
2457 if (window
->GetFocusedNode() == mFocusedContent
)
2458 Blur(mFocusedWindow
, nsnull
, PR_TRUE
);
2460 window
->SetFocusedNode(nsnull
);
2464 // reset the tab index and start again from the beginning or end
2465 startContent
= rootContent
;
2466 tabIndex
= forward
? 1 : 0;
2469 // wrapped all the way around and didn't find anything to move the focus
2470 // to, so just break out
2471 if (startContent
== originalStartContent
)
2479 nsFocusManager::GetNextTabbableContent(nsIPresShell
* aPresShell
,
2480 nsIContent
* aRootContent
,
2481 nsIContent
* aOriginalStartContent
,
2482 nsIContent
* aStartContent
,
2484 PRInt32 aCurrentTabIndex
,
2485 PRBool aIgnoreTabIndex
,
2486 nsIContent
** aResultContent
)
2488 *aResultContent
= nsnull
;
2490 nsCOMPtr
<nsIContent
> startContent
= aStartContent
;
2494 #ifdef DEBUG_FOCUS_NAVIGATION
2495 PRINTTAGF("GetNextTabbable: %s", aStartContent
);
2496 printf(" tabindex: %d\n", aCurrentTabIndex
);
2499 nsPresContext
* presContext
= aPresShell
->GetPresContext();
2501 PRBool getNextFrame
= PR_TRUE
;
2502 nsCOMPtr
<nsIContent
> iterStartContent
= aStartContent
;
2504 nsIFrame
* startFrame
= iterStartContent
->GetPrimaryFrame();
2505 // if there is no frame, look for another content node that has a frame
2507 // if the root content doesn't have a frame, just return
2508 if (iterStartContent
== aRootContent
)
2511 // look for the next or previous content node in tree order
2512 nsTreeWalker
walker(aRootContent
, nsIDOMNodeFilter::SHOW_ALL
, nsnull
, PR_TRUE
);
2513 nsCOMPtr
<nsIDOMNode
> nextNode
= do_QueryInterface(iterStartContent
);
2514 walker
.SetCurrentNode(nextNode
);
2515 if (NS_SUCCEEDED(aForward
? walker
.NextNode(getter_AddRefs(nextNode
)) :
2516 walker
.PreviousNode(getter_AddRefs(nextNode
)))) {
2517 iterStartContent
= do_QueryInterface(nextNode
);
2518 // we've already skipped over the initial focused content, so we
2519 // don't want to traverse frames.
2520 getNextFrame
= PR_FALSE
;
2521 if (iterStartContent
)
2525 // otherwise, as a last attempt, just look at the root content
2526 iterStartContent
= aRootContent
;
2530 nsCOMPtr
<nsIFrameEnumerator
> frameTraversal
;
2531 nsresult rv
= NS_NewFrameTraversal(getter_AddRefs(frameTraversal
),
2532 presContext
, startFrame
,
2534 PR_FALSE
, // aVisual
2535 PR_FALSE
, // aLockInScrollView
2536 PR_TRUE
// aFollowOOFs
2538 NS_ENSURE_SUCCESS(rv
, rv
);
2540 if (iterStartContent
== aRootContent
) {
2542 frameTraversal
->Last();
2543 } else if (aRootContent
->IsFocusable()) {
2544 frameTraversal
->Next();
2547 else if (getNextFrame
&&
2548 (!iterStartContent
|| iterStartContent
->Tag() != nsGkAtoms::area
||
2549 !iterStartContent
->IsHTML())) {
2550 // Need to do special check in case we're in an imagemap which has multiple
2551 // content nodes per frame, so don't skip over the starting frame.
2553 frameTraversal
->Next();
2555 frameTraversal
->Prev();
2558 // Walk frames to find something tabbable matching mCurrentTabIndex
2559 nsIFrame
* frame
= static_cast<nsIFrame
*>(frameTraversal
->CurrentItem());
2561 // TabIndex not set defaults to 0 for form elements, anchors and other
2562 // elements that are normally focusable. Tabindex defaults to -1
2563 // for elements that are not normally focusable.
2564 // The returned computed tabindex from IsFocusable() is as follows:
2565 // < 0 not tabbable at all
2566 // == 0 in normal tab order (last after positive tabindexed items)
2567 // > 0 can be tabbed to in the order specified by this value
2570 frame
->IsFocusable(&tabIndex
, 0);
2572 #ifdef DEBUG_FOCUS_NAVIGATION
2573 if (frame
->GetContent()) {
2574 PRINTTAGF("Next Tabbable %s:", frame
->GetContent());
2575 printf(" with tabindex: %d expected: %d\n", tabIndex
, aCurrentTabIndex
);
2579 nsIContent
* currentContent
= frame
->GetContent();
2580 if (tabIndex
>= 0) {
2581 NS_ASSERTION(currentContent
, "IsFocusable set a tabindex for a frame with no content");
2582 if (currentContent
->Tag() == nsGkAtoms::img
&&
2583 currentContent
->HasAttr(kNameSpaceID_None
, nsGkAtoms::usemap
)) {
2584 // This is an image with a map. Image map areas are not traversed by
2585 // nsIFrameTraversal so look for the next or previous area element.
2586 nsIContent
*areaContent
=
2587 GetNextTabbableMapArea(aForward
, aCurrentTabIndex
,
2588 currentContent
, iterStartContent
);
2590 NS_ADDREF(*aResultContent
= areaContent
);
2594 else if (aIgnoreTabIndex
|| aCurrentTabIndex
== tabIndex
) {
2595 // break out if we've wrapped around to the start again.
2596 if (aOriginalStartContent
&& currentContent
== aOriginalStartContent
) {
2597 NS_ADDREF(*aResultContent
= currentContent
);
2601 // found a node with a matching tab index. Check if it is a child
2602 // frame. If so, navigate into the child frame instead.
2603 nsIDocument
* doc
= currentContent
->GetCurrentDoc();
2604 NS_ASSERTION(doc
, "content not in document");
2605 nsIDocument
* subdoc
= doc
->GetSubDocumentFor(currentContent
);
2607 if (!subdoc
->EventHandlingSuppressed()) {
2609 // when tabbing forward into a frame, return the root
2610 // frame so that the canvas becomes focused.
2611 nsCOMPtr
<nsPIDOMWindow
> subframe
= subdoc
->GetWindow();
2613 // If the subframe body is editable by contenteditable,
2614 // we should set the editor's root element rather than the
2615 // actual root element. Otherwise, we should set the focus
2616 // to the root content.
2618 nsLayoutUtils::GetEditableRootContentByContentEditable(subdoc
);
2619 if (!*aResultContent
||
2620 !((*aResultContent
)->GetPrimaryFrame())) {
2622 GetRootForFocus(subframe
, subdoc
, PR_FALSE
, PR_TRUE
);
2624 if (*aResultContent
) {
2625 NS_ADDREF(*aResultContent
);
2630 Element
* rootElement
= subdoc
->GetRootElement();
2631 nsIPresShell
* subShell
= subdoc
->GetShell();
2632 if (rootElement
&& subShell
) {
2633 rv
= GetNextTabbableContent(subShell
, rootElement
,
2634 aOriginalStartContent
, rootElement
,
2635 aForward
, (aForward
? 1 : 0),
2636 PR_FALSE
, aResultContent
);
2637 NS_ENSURE_SUCCESS(rv
, rv
);
2638 if (*aResultContent
)
2643 // otherwise, use this as the next content node to tab to, unless
2644 // this was the element we started on. This would happen for
2645 // instance on an element with child frames, where frame navigation
2646 // could return the original element again. In that case, just skip
2647 // it. Also, if the next content node is the root content, then
2648 // return it. This latter case would happen only if someone made a
2650 // Also, when going backwards, check to ensure that the focus
2651 // wouldn't be redirected. Otherwise, for example, when an input in
2652 // a textbox is focused, the enclosing textbox would be found and
2653 // the same inner input would be returned again.
2654 else if (currentContent
== aRootContent
||
2655 (currentContent
!= startContent
&&
2656 (aForward
|| !GetRedirectedFocus(currentContent
)))) {
2657 NS_ADDREF(*aResultContent
= currentContent
);
2662 else if (aOriginalStartContent
&& currentContent
== aOriginalStartContent
) {
2663 // not focusable, so return if we have wrapped around to the original
2664 // content. This is necessary in case the original starting content was
2666 NS_ADDREF(*aResultContent
= currentContent
);
2670 // Move to the next or previous frame, but ignore continuation frames
2671 // since only the first frame should be involved in focusability.
2672 // Otherwise, a loop will occur in the following example:
2673 // <span tabindex="1">...<a/><a/>...</span>
2674 // where the text wraps onto multiple lines. Tabbing from the second
2675 // link can find one of the span's continuation frames between the link
2676 // and the end of the span, and the span would end up getting focused
2680 frameTraversal
->Next();
2682 frameTraversal
->Prev();
2683 frame
= static_cast<nsIFrame
*>(frameTraversal
->CurrentItem());
2684 } while (frame
&& frame
->GetPrevContinuation());
2687 // If already at lowest priority tab (0), end search completely.
2688 // A bit counterintuitive but true, tabindex order goes 1, 2, ... 32767, 0
2689 if (aCurrentTabIndex
== (aForward
? 0 : 1)) {
2690 // if going backwards, the canvas should be focused once the beginning
2691 // has been reached.
2693 nsCOMPtr
<nsPIDOMWindow
> window
= GetCurrentWindow(aRootContent
);
2694 NS_ENSURE_TRUE(window
, NS_ERROR_FAILURE
);
2695 NS_IF_ADDREF(*aResultContent
=
2696 GetRootForFocus(window
, aRootContent
->GetCurrentDoc(), PR_FALSE
, PR_TRUE
));
2701 // continue looking for next highest priority tabindex
2702 aCurrentTabIndex
= GetNextTabIndex(aRootContent
, aCurrentTabIndex
, aForward
);
2703 startContent
= iterStartContent
= aRootContent
;
2710 nsFocusManager::GetNextTabbableMapArea(PRBool aForward
,
2711 PRInt32 aCurrentTabIndex
,
2712 nsIContent
* aImageContent
,
2713 nsIContent
* aStartContent
)
2715 nsAutoString useMap
;
2716 aImageContent
->GetAttr(kNameSpaceID_None
, nsGkAtoms::usemap
, useMap
);
2718 nsCOMPtr
<nsIDocument
> doc
= aImageContent
->GetDocument();
2720 nsCOMPtr
<nsIDOMHTMLMapElement
> imageMap
= nsImageMapUtils::FindImageMap(doc
, useMap
);
2723 nsCOMPtr
<nsIContent
> mapContent
= do_QueryInterface(imageMap
);
2724 PRUint32 count
= mapContent
->GetChildCount();
2725 // First see if the the start content is in this map
2727 PRInt32 index
= mapContent
->IndexOf(aStartContent
);
2729 if (index
< 0 || (aStartContent
->IsFocusable(&tabIndex
) &&
2730 tabIndex
!= aCurrentTabIndex
)) {
2731 // If aStartContent is in this map we must start iterating past it.
2732 // We skip the case where aStartContent has tabindex == aStartContent
2733 // since the next tab ordered element might be before it
2734 // (or after for backwards) in the child list.
2735 index
= aForward
? -1 : (PRInt32
)count
;
2738 // GetChildAt will return nsnull if our index < 0 or index >= count
2739 nsCOMPtr
<nsIContent
> areaContent
;
2740 while ((areaContent
= mapContent
->GetChildAt(aForward
? ++index
: --index
)) != nsnull
) {
2741 if (areaContent
->IsFocusable(&tabIndex
) && tabIndex
== aCurrentTabIndex
) {
2751 nsFocusManager::GetNextTabIndex(nsIContent
* aParent
,
2752 PRInt32 aCurrentTabIndex
,
2755 PRInt32 tabIndex
, childTabIndex
;
2758 PRUint32 count
= aParent
->GetChildCount();
2762 for (PRUint32 index
= 0; index
< count
; index
++) {
2763 child
= aParent
->GetChildAt(index
);
2764 childTabIndex
= GetNextTabIndex(child
, aCurrentTabIndex
, aForward
);
2765 if (childTabIndex
> aCurrentTabIndex
&& childTabIndex
!= tabIndex
) {
2766 tabIndex
= (tabIndex
== 0 || childTabIndex
< tabIndex
) ? childTabIndex
: tabIndex
;
2769 nsAutoString tabIndexStr
;
2770 child
->GetAttr(kNameSpaceID_None
, nsGkAtoms::tabindex
, tabIndexStr
);
2771 PRInt32 ec
, val
= tabIndexStr
.ToInteger(&ec
);
2772 if (NS_SUCCEEDED (ec
) && val
> aCurrentTabIndex
&& val
!= tabIndex
) {
2773 tabIndex
= (tabIndex
== 0 || val
< tabIndex
) ? val
: tabIndex
;
2777 else { /* !aForward */
2779 for (PRUint32 index
= 0; index
< count
; index
++) {
2780 child
= aParent
->GetChildAt(index
);
2781 childTabIndex
= GetNextTabIndex(child
, aCurrentTabIndex
, aForward
);
2782 if ((aCurrentTabIndex
== 0 && childTabIndex
> tabIndex
) ||
2783 (childTabIndex
< aCurrentTabIndex
&& childTabIndex
> tabIndex
)) {
2784 tabIndex
= childTabIndex
;
2787 nsAutoString tabIndexStr
;
2788 child
->GetAttr(kNameSpaceID_None
, nsGkAtoms::tabindex
, tabIndexStr
);
2789 PRInt32 ec
, val
= tabIndexStr
.ToInteger(&ec
);
2790 if (NS_SUCCEEDED (ec
)) {
2791 if ((aCurrentTabIndex
== 0 && val
> tabIndex
) ||
2792 (val
< aCurrentTabIndex
&& val
> tabIndex
) ) {
2803 nsFocusManager::GetRootForFocus(nsPIDOMWindow
* aWindow
,
2804 nsIDocument
* aDocument
,
2805 PRBool aIsForDocNavigation
,
2806 PRBool aCheckVisibility
)
2808 // the root element's canvas may be focused as long as the document is in a
2809 // a non-chrome shell and does not contain a frameset.
2810 if (aIsForDocNavigation
) {
2811 nsCOMPtr
<nsIContent
> docContent
=
2812 do_QueryInterface(aWindow
->GetFrameElementInternal());
2813 if (docContent
&& docContent
->Tag() == nsGkAtoms::iframe
)
2818 nsCOMPtr
<nsIDocShellTreeItem
> shellItem
= do_QueryInterface(aWindow
->GetDocShell());
2819 shellItem
->GetItemType(&itemType
);
2821 if (itemType
== nsIDocShellTreeItem::typeChrome
)
2825 if (aCheckVisibility
&& !IsWindowVisible(aWindow
))
2828 Element
*rootElement
= aDocument
->GetRootElement();
2830 if (aCheckVisibility
&& !rootElement
->GetPrimaryFrame()) {
2834 // Finally, check if this is a frameset
2835 nsCOMPtr
<nsIHTMLDocument
> htmlDoc
= do_QueryInterface(aDocument
);
2837 PRUint32 childCount
= rootElement
->GetChildCount();
2838 for (PRUint32 i
= 0; i
< childCount
; ++i
) {
2839 nsIContent
*childContent
= rootElement
->GetChildAt(i
);
2840 nsINodeInfo
*ni
= childContent
->NodeInfo();
2841 if (childContent
->IsHTML() &&
2842 ni
->Equals(nsGkAtoms::frameset
))
2852 nsFocusManager::GetLastDocShell(nsIDocShellTreeItem
* aItem
,
2853 nsIDocShellTreeItem
** aResult
)
2857 nsCOMPtr
<nsIDocShellTreeItem
> curItem
= aItem
;
2859 PRInt32 childCount
= 0;
2860 curItem
->GetChildCount(&childCount
);
2863 NS_ADDREF(*aResult
);
2868 curItem
->GetChildAt(childCount
- 1, getter_AddRefs(curItem
));
2873 nsFocusManager::GetNextDocShell(nsIDocShellTreeItem
* aItem
,
2874 nsIDocShellTreeItem
** aResult
)
2878 PRInt32 childCount
= 0;
2879 aItem
->GetChildCount(&childCount
);
2881 aItem
->GetChildAt(0, aResult
);
2886 nsCOMPtr
<nsIDocShellTreeItem
> curItem
= aItem
;
2888 nsCOMPtr
<nsIDocShellTreeItem
> parentItem
;
2889 curItem
->GetParent(getter_AddRefs(parentItem
));
2893 // Note that we avoid using GetChildOffset() here because docshell
2894 // child offsets can't be trusted to be correct. bug 162283.
2895 nsCOMPtr
<nsIDocShellTreeItem
> iterItem
;
2897 parentItem
->GetChildCount(&childCount
);
2898 for (PRInt32 index
= 0; index
< childCount
; ++index
) {
2899 parentItem
->GetChildAt(index
, getter_AddRefs(iterItem
));
2900 if (iterItem
== curItem
) {
2902 if (index
< childCount
) {
2903 parentItem
->GetChildAt(index
, aResult
);
2911 curItem
= parentItem
;
2916 nsFocusManager::GetPreviousDocShell(nsIDocShellTreeItem
* aItem
,
2917 nsIDocShellTreeItem
** aResult
)
2921 nsCOMPtr
<nsIDocShellTreeItem
> parentItem
;
2922 aItem
->GetParent(getter_AddRefs(parentItem
));
2926 // Note that we avoid using GetChildOffset() here because docshell
2927 // child offsets can't be trusted to be correct. bug 162283.
2928 PRInt32 childCount
= 0;
2929 parentItem
->GetChildCount(&childCount
);
2930 nsCOMPtr
<nsIDocShellTreeItem
> prevItem
, iterItem
;
2931 for (PRInt32 index
= 0; index
< childCount
; ++index
) {
2932 parentItem
->GetChildAt(index
, getter_AddRefs(iterItem
));
2933 if (iterItem
== aItem
)
2935 prevItem
= iterItem
;
2939 GetLastDocShell(prevItem
, aResult
);
2941 NS_ADDREF(*aResult
= parentItem
);
2945 nsFocusManager::GetNextTabbableDocument(PRBool aForward
)
2947 nsCOMPtr
<nsIDocShellTreeItem
> startItem
;
2948 if (mFocusedWindow
) {
2949 startItem
= do_QueryInterface(mFocusedWindow
->GetDocShell());
2952 nsCOMPtr
<nsIWebNavigation
> webnav
= do_GetInterface(mActiveWindow
);
2953 startItem
= do_QueryInterface(webnav
);
2958 // perform a depth first search (preorder) of the docshell tree
2959 // looking for an HTML Frame or a chrome document
2960 nsIContent
* content
= nsnull
;
2961 nsCOMPtr
<nsIDocShellTreeItem
> curItem
= startItem
;
2962 nsCOMPtr
<nsIDocShellTreeItem
> nextItem
;
2965 GetNextDocShell(curItem
, getter_AddRefs(nextItem
));
2967 // wrap around to the beginning, which is the top of the tree
2968 startItem
->GetRootTreeItem(getter_AddRefs(nextItem
));
2972 GetPreviousDocShell(curItem
, getter_AddRefs(nextItem
));
2974 // wrap around to the end, which is the last item in the tree
2975 nsCOMPtr
<nsIDocShellTreeItem
> rootItem
;
2976 startItem
->GetRootTreeItem(getter_AddRefs(rootItem
));
2977 GetLastDocShell(rootItem
, getter_AddRefs(nextItem
));
2982 nsCOMPtr
<nsPIDOMWindow
> nextFrame
= do_GetInterface(nextItem
);
2986 nsCOMPtr
<nsIDocument
> doc
= do_QueryInterface(nextFrame
->GetExtantDocument());
2987 if (doc
&& !doc
->EventHandlingSuppressed()) {
2988 content
= GetRootForFocus(nextFrame
, doc
, PR_TRUE
, PR_TRUE
);
2989 if (content
&& !GetRootForFocus(nextFrame
, doc
, PR_FALSE
, PR_FALSE
)) {
2990 // if the found content is in a chrome shell or a frameset, navigate
2991 // forward one tabbable item so that the first item is focused. Note
2992 // that we always go forward and not back here.
2993 nsCOMPtr
<nsIContent
> nextFocus
;
2994 Element
* rootElement
= doc
->GetRootElement();
2995 nsIPresShell
* presShell
= doc
->GetShell();
2997 nsresult rv
= GetNextTabbableContent(presShell
, rootElement
,
2998 nsnull
, rootElement
,
2999 PR_TRUE
, 1, PR_FALSE
,
3000 getter_AddRefs(nextFocus
));
3001 return NS_SUCCEEDED(rv
) ? nextFocus
.get() : nsnull
;
3011 nsFocusManager::GetFocusInSelection(nsPIDOMWindow
* aWindow
,
3012 nsIContent
* aStartSelection
,
3013 nsIContent
* aEndSelection
,
3014 nsIContent
** aFocusedContent
)
3016 *aFocusedContent
= nsnull
;
3018 nsCOMPtr
<nsIContent
> testContent
= aStartSelection
;
3019 nsCOMPtr
<nsIContent
> nextTestContent
= aEndSelection
;
3021 nsCOMPtr
<nsIContent
> currentFocus
= aWindow
->GetFocusedNode();
3023 // We now have the correct start node in selectionContent!
3024 // Search for focusable elements, starting with selectionContent
3026 // Method #1: Keep going up while we look - an ancestor might be focusable
3027 // We could end the loop earlier, such as when we're no longer
3028 // in the same frame, by comparing selectionContent->GetPrimaryFrame()
3029 // with a variable holding the starting selectionContent
3030 while (testContent
) {
3031 // Keep testing while selectionContent is equal to something,
3032 // eventually we'll run out of ancestors
3034 nsCOMPtr
<nsIURI
> uri
;
3035 if (testContent
== currentFocus
||
3036 testContent
->IsLink(getter_AddRefs(uri
))) {
3037 NS_ADDREF(*aFocusedContent
= testContent
);
3042 testContent
= testContent
->GetParent();
3045 // We run this loop again, checking the ancestor chain of the selection's end point
3046 testContent
= nextTestContent
;
3047 nextTestContent
= nsnull
;
3051 // We couldn't find an anchor that was an ancestor of the selection start
3052 // Method #2: look for anchor in selection's primary range (depth first search)
3054 // Turn into nodes so that we can use GetNextSibling() and GetFirstChild()
3055 nsCOMPtr
<nsIDOMNode
> selectionNode(do_QueryInterface(aStartSelection
));
3056 nsCOMPtr
<nsIDOMNode
> endSelectionNode(do_QueryInterface(aEndSelection
));
3057 nsCOMPtr
<nsIDOMNode
> testNode
;
3060 testContent
= do_QueryInterface(selectionNode
);
3062 // We're looking for any focusable link that could be part of the
3063 // main document's selection.
3064 nsCOMPtr
<nsIURI
> uri
;
3065 if (testContent
== currentFocus
||
3066 testContent
->IsLink(getter_AddRefs(uri
))) {
3067 NS_ADDREF(*aFocusedContent
= testContent
);
3071 selectionNode
->GetFirstChild(getter_AddRefs(testNode
));
3073 selectionNode
= testNode
;
3077 if (selectionNode
== endSelectionNode
)
3079 selectionNode
->GetNextSibling(getter_AddRefs(testNode
));
3081 selectionNode
= testNode
;
3086 selectionNode
->GetParentNode(getter_AddRefs(testNode
));
3087 if (!testNode
|| testNode
== endSelectionNode
) {
3088 selectionNode
= nsnull
;
3091 testNode
->GetNextSibling(getter_AddRefs(selectionNode
));
3094 selectionNode
= testNode
;
3097 while (selectionNode
&& selectionNode
!= endSelectionNode
);
3101 NS_NewFocusManager(nsIFocusManager
** aResult
)
3103 NS_IF_ADDREF(*aResult
= nsFocusManager::GetFocusManager());