1 /* -*- Mode: C++; tab-width: 3; indent-tabs-mode: nil; c-basic-offset: 2 -*-
3 * ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
16 * The Original Code is the Mozilla browser.
18 * The Initial Developer of the Original Code is
19 * Netscape Communications, Inc.
20 * Portions created by the Initial Developer are Copyright (C) 1999
21 * the Initial Developer. All Rights Reserved.
24 * David W. Hyatt <hyatt@netscape.com> (Original Author)
25 * Dan Rosen <dr@netscape.com>
27 * Alternatively, the contents of this file may be used under the terms of
28 * either of the GNU General Public License Version 2 or later (the "GPL"),
29 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
39 * ***** END LICENSE BLOCK ***** */
41 #include "nsIContent.h"
42 #include "nsIControllers.h"
43 #include "nsIDOMDocument.h"
44 #include "nsIDOMHTMLDocument.h"
45 #include "nsIDOMElement.h"
46 #include "nsIDOMNSHTMLInputElement.h"
47 #include "nsIDOMNSHTMLTextAreaElement.h"
48 #include "nsIDOMNSEvent.h"
49 #include "nsIDOMWindowInternal.h"
50 #include "nsIDocument.h"
51 #include "nsPresContext.h"
52 #include "nsIPresShell.h"
53 #include "nsIScriptGlobalObject.h"
54 #include "nsPIDOMWindow.h"
55 #include "nsFocusController.h"
57 #include "nsIDOMEventTarget.h"
58 #include "nsIEventStateManager.h"
59 #include "nsIDocShell.h"
60 #include "nsIBaseWindow.h"
61 #include "nsIWindowWatcher.h"
62 #include "nsIDocShellTreeItem.h"
63 #include "nsIDocShellTreeOwner.h"
64 #include "nsIInterfaceRequestorUtils.h"
65 #include "nsServiceManagerUtils.h"
66 #include "nsGlobalWindow.h"
69 #include "nsIDOMXULDocument.h"
70 #include "nsIDOMXULElement.h"
73 ////////////////////////////////////////////////////////////////////////
75 nsFocusController::nsFocusController(void)
77 mSuppressFocusScroll(PR_FALSE
),
79 mUpdateWindowWatcher(PR_FALSE
),
80 mNeedUpdateCommands(PR_FALSE
)
84 nsFocusController::~nsFocusController(void)
88 NS_IMPL_CYCLE_COLLECTION_CLASS(nsFocusController
)
90 NS_IMPL_CYCLE_COLLECTING_ADDREF_AMBIGUOUS(nsFocusController
, nsIFocusController
)
91 NS_IMPL_CYCLE_COLLECTING_RELEASE_AMBIGUOUS(nsFocusController
,
94 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsFocusController
)
95 NS_INTERFACE_MAP_ENTRY(nsIFocusController
)
96 NS_INTERFACE_MAP_ENTRY(nsIDOMFocusListener
)
97 NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener
)
98 NS_INTERFACE_MAP_ENTRY(nsSupportsWeakReference
)
99 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIFocusController
)
102 NS_IMPL_CYCLE_COLLECTION_UNLINK_0(nsFocusController
)
103 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsFocusController
)
104 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mCurrentElement
)
105 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mPreviousElement
)
106 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mCurrentWindow
)
107 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mPreviousWindow
)
108 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mPopupNode
)
109 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
112 nsFocusController::Create(nsIFocusController
** aResult
)
114 nsFocusController
* controller
= new nsFocusController();
116 return NS_ERROR_OUT_OF_MEMORY
;
118 *aResult
= controller
;
124 ////////////////////////////////////////////////////////////////
125 // nsIFocusController Interface
128 nsFocusController::GetFocusedElement(nsIDOMElement
** aElement
)
130 *aElement
= mCurrentElement
;
131 NS_IF_ADDREF(*aElement
);
136 nsFocusController::GetFocusedWindow(nsIDOMWindowInternal
** aWindow
)
138 *aWindow
= mCurrentWindow
;
139 NS_IF_ADDREF(*aWindow
);
144 nsFocusController::SetFocusedElement(nsIDOMElement
* aElement
)
147 mPreviousElement
= mCurrentElement
;
149 mPreviousElement
= aElement
;
151 mNeedUpdateCommands
= mNeedUpdateCommands
|| mCurrentElement
!= aElement
;
152 mCurrentElement
= aElement
;
154 if (!mSuppressFocus
) {
155 // Need to update focus commands when focus switches from
156 // an element to no element, so don't test mCurrentElement
164 nsFocusController::RewindFocusState()
166 mCurrentElement
= mPreviousElement
;
167 mCurrentWindow
= mPreviousWindow
;
173 nsFocusController::SetFocusedWindow(nsIDOMWindowInternal
* aWindow
)
175 nsCOMPtr
<nsPIDOMWindow
> win
= do_QueryInterface(aWindow
);
178 win
= win
->GetOuterWindow();
181 NS_ASSERTION(!win
|| !win
->IsInnerWindow(),
182 "Uh, inner window can't have focus!");
184 if (win
&& (mCurrentWindow
!= win
)) {
185 nsCOMPtr
<nsIBaseWindow
> basewin
= do_QueryInterface(win
->GetDocShell());
190 if (mCurrentWindow
) {
191 mPreviousWindow
= mCurrentWindow
;
193 mPreviousWindow
= win
;
196 mNeedUpdateCommands
= mNeedUpdateCommands
|| mCurrentWindow
!= win
;
197 mCurrentWindow
= win
;
199 if (mUpdateWindowWatcher
) {
200 NS_ASSERTION(mActive
, "This shouldn't happen");
202 UpdateWWActiveWindow();
203 mUpdateWindowWatcher
= PR_FALSE
;
211 nsFocusController::UpdateCommands()
213 if (!mNeedUpdateCommands
) {
216 nsCOMPtr
<nsIDOMWindowInternal
> window
;
217 nsCOMPtr
<nsIDocument
> doc
;
218 if (mCurrentWindow
) {
219 window
= mCurrentWindow
;
220 nsCOMPtr
<nsIDOMWindow
> domWin(do_QueryInterface(window
));
221 nsCOMPtr
<nsIDOMDocument
> domDoc
;
222 domWin
->GetDocument(getter_AddRefs(domDoc
));
223 doc
= do_QueryInterface(domDoc
);
225 else if (mCurrentElement
) {
226 nsCOMPtr
<nsIDOMDocument
> domDoc
;
227 mCurrentElement
->GetOwnerDocument(getter_AddRefs(domDoc
));
229 doc
= do_QueryInterface(domDoc
);
230 window
= do_QueryInterface(doc
->GetScriptGlobalObject());
234 // If there is no presshell, it's a zombie document which can't handle the command updates
235 if (window
&& doc
&& doc
->GetPrimaryShell()) {
236 // Not a zombie document, so we can handle the command update
237 window
->UpdateCommands(NS_LITERAL_STRING("focus"));
238 mNeedUpdateCommands
= PR_FALSE
;
244 nsFocusController::GetControllers(nsIControllers
** aResult
)
246 // XXX: we should fix this so there's a generic interface that
247 // describes controllers, so this code would have no special
248 // knowledge of what object might have controllers.
249 if (mCurrentElement
) {
252 nsCOMPtr
<nsIDOMXULElement
> xulElement(do_QueryInterface(mCurrentElement
));
254 return xulElement
->GetControllers(aResult
);
257 nsCOMPtr
<nsIDOMNSHTMLTextAreaElement
> htmlTextArea
=
258 do_QueryInterface(mCurrentElement
);
260 return htmlTextArea
->GetControllers(aResult
);
262 nsCOMPtr
<nsIDOMNSHTMLInputElement
> htmlInputElement
=
263 do_QueryInterface(mCurrentElement
);
264 if (htmlInputElement
)
265 return htmlInputElement
->GetControllers(aResult
);
267 nsCOMPtr
<nsIContent
> content
= do_QueryInterface(mCurrentElement
);
268 if (content
&& content
->IsEditable()) {
269 // Move up to the window.
270 nsCOMPtr
<nsIDOMDocument
> domDoc
;
271 mCurrentElement
->GetOwnerDocument(getter_AddRefs(domDoc
));
272 nsCOMPtr
<nsIDOMWindowInternal
> domWindow
=
273 do_QueryInterface(GetWindowFromDocument(domDoc
));
275 return domWindow
->GetControllers(aResult
);
278 else if (mCurrentWindow
) {
279 nsCOMPtr
<nsIDOMWindowInternal
> domWindow
=
280 do_QueryInterface(mCurrentWindow
);
282 return domWindow
->GetControllers(aResult
);
290 nsFocusController::MoveFocus(PRBool aForward
, nsIDOMElement
* aElt
)
292 // Obtain the doc that we'll be shifting focus inside.
293 nsCOMPtr
<nsIDocument
> doc
;
294 nsCOMPtr
<nsIContent
> content
;
296 content
= do_QueryInterface(aElt
);
297 doc
= content
->GetDocument();
300 if (mCurrentElement
) {
301 content
= do_QueryInterface(mCurrentElement
);
302 doc
= content
->GetDocument();
305 else if (mCurrentWindow
) {
306 nsCOMPtr
<nsIDOMDocument
> domDoc
;
307 mCurrentWindow
->GetDocument(getter_AddRefs(domDoc
));
308 doc
= do_QueryInterface(domDoc
);
313 // No way to obtain an event state manager. Give up.
314 return NS_ERROR_FAILURE
;
317 nsIPresShell
*shell
= doc
->GetPrimaryShell();
319 return NS_ERROR_FAILURE
;
321 // Make sure frames have been constructed before shifting focus, bug 273092.
322 shell
->FlushPendingNotifications(Flush_Frames
);
324 // Retrieve the context
325 nsCOMPtr
<nsPresContext
> presContext
= shell
->GetPresContext();
327 // Make this ESM shift the focus per our instructions.
328 return presContext
->EventStateManager()->ShiftFocus(aForward
, content
);
332 // nsIDOMFocusListener
336 nsFocusController::Focus(nsIDOMEvent
* aEvent
)
341 nsCOMPtr
<nsIDOMEventTarget
> t
;
343 nsCOMPtr
<nsIDOMNSEvent
> nsevent(do_QueryInterface(aEvent
));
345 nsevent
->GetOriginalTarget(getter_AddRefs(t
));
348 nsCOMPtr
<nsIDOMElement
> domElement
= do_QueryInterface(t
);
349 if (domElement
&& (domElement
!= mCurrentElement
)) {
350 SetFocusedElement(domElement
);
352 // Also set focus to our innermost window.
353 // XXX Must be done for the Ender case, since ender causes a blur,
354 // but we don't hear the subsequent focus to the Ender window.
355 nsCOMPtr
<nsIDOMDocument
> ownerDoc
;
356 domElement
->GetOwnerDocument(getter_AddRefs(ownerDoc
));
357 nsCOMPtr
<nsIDOMWindowInternal
> domWindow
= GetWindowFromDocument(ownerDoc
);
359 SetFocusedWindow(domWindow
);
362 // We're focusing a window. We only want to do an update commands
363 // if no element is focused.
364 nsCOMPtr
<nsIDOMDocument
> domDoc
= do_QueryInterface(t
);
366 nsCOMPtr
<nsIDOMWindowInternal
> domWindow
= GetWindowFromDocument(domDoc
);
368 SetFocusedWindow(domWindow
);
369 if (mCurrentElement
) {
370 // Make sure this element is in our window. If not, we
371 // should clear this field.
372 nsCOMPtr
<nsIDOMDocument
> ownerDoc
;
373 mCurrentElement
->GetOwnerDocument(getter_AddRefs(ownerDoc
));
374 nsCOMPtr
<nsIDOMDocument
> windowDoc
;
375 mCurrentWindow
->GetDocument(getter_AddRefs(windowDoc
));
376 if (ownerDoc
!= windowDoc
)
377 mCurrentElement
= mPreviousElement
= nsnull
;
380 mPreviousElement
= nsnull
;
382 if (!mCurrentElement
) {
393 nsFocusController::Blur(nsIDOMEvent
* aEvent
)
398 nsCOMPtr
<nsIDOMEventTarget
> t
;
400 nsCOMPtr
<nsIDOMNSEvent
> nsevent(do_QueryInterface(aEvent
));
403 nsevent
->GetOriginalTarget(getter_AddRefs(t
));
406 nsCOMPtr
<nsIDOMElement
> domElement
= do_QueryInterface(t
);
408 SetFocusedElement(nsnull
);
411 nsCOMPtr
<nsIDOMDocument
> domDoc
= do_QueryInterface(t
);
413 nsCOMPtr
<nsIDOMWindowInternal
> domWindow
= GetWindowFromDocument(domDoc
);
415 SetFocusedWindow(nsnull
);
422 nsFocusController::GetWindowFromDocument(nsIDOMDocument
* aDocument
)
424 nsCOMPtr
<nsIDocument
> doc
= do_QueryInterface(aDocument
);
428 return doc
->GetWindow();
432 nsFocusController::GetControllerForCommand(const char * aCommand
,
433 nsIController
** _retval
)
435 NS_ENSURE_ARG_POINTER(_retval
);
438 nsCOMPtr
<nsIControllers
> controllers
;
439 nsCOMPtr
<nsIController
> controller
;
441 GetControllers(getter_AddRefs(controllers
));
443 controllers
->GetControllerForCommand(aCommand
, getter_AddRefs(controller
));
445 controller
.swap(*_retval
);
450 nsCOMPtr
<nsPIDOMWindow
> currentWindow
;
451 if (mCurrentElement
) {
452 // Move up to the window.
453 nsCOMPtr
<nsIDOMDocument
> domDoc
;
454 mCurrentElement
->GetOwnerDocument(getter_AddRefs(domDoc
));
455 currentWindow
= do_QueryInterface(GetWindowFromDocument(domDoc
));
457 else if (mCurrentWindow
) {
458 nsGlobalWindow
*win
=
459 static_cast<nsGlobalWindow
*>
460 (static_cast<nsIDOMWindowInternal
*>(mCurrentWindow
));
461 currentWindow
= win
->GetPrivateParent();
465 while(currentWindow
) {
466 nsCOMPtr
<nsIDOMWindowInternal
> domWindow(do_QueryInterface(currentWindow
));
468 nsCOMPtr
<nsIControllers
> controllers2
;
469 domWindow
->GetControllers(getter_AddRefs(controllers2
));
471 controllers2
->GetControllerForCommand(aCommand
,
472 getter_AddRefs(controller
));
474 controller
.swap(*_retval
);
479 nsGlobalWindow
*win
=
480 static_cast<nsGlobalWindow
*>
481 (static_cast<nsIDOMWindowInternal
*>(currentWindow
));
482 currentWindow
= win
->GetPrivateParent();
489 nsFocusController::GetSuppressFocusScroll(PRBool
* aSuppressFocusScroll
)
491 *aSuppressFocusScroll
= mSuppressFocusScroll
;
496 nsFocusController::SetSuppressFocusScroll(PRBool aSuppressFocusScroll
)
498 mSuppressFocusScroll
= aSuppressFocusScroll
;
503 nsFocusController::GetSuppressFocus(PRBool
* aSuppressFocus
)
505 *aSuppressFocus
= (mSuppressFocus
> 0);
510 nsFocusController::SetSuppressFocus(PRBool aSuppressFocus
, const char* aReason
)
515 //printf("[%p] SuppressFocus incremented to %d. The reason is %s.\n", this, mSuppressFocus, aReason);
518 else if(mSuppressFocus
> 0) {
521 //printf("[%p] SuppressFocus decremented to %d. The reason is %s.\n", this, mSuppressFocus, aReason);
525 // It's ok to unsuppress even if no suppression is active (bug 112294)
528 // we are unsuppressing after activating, so update focus-related commands
529 // we need this to update command, including the case where there is no element
530 // because nsPresShell::UnsuppressPainting may have just now unsuppressed
531 // focus on the currently focused window
532 if (!mSuppressFocus
) {
533 // Always update commands if we have a current element
534 // and mNeedUpdateCommands is true (checked in nsFC::UpdateCommands)
542 nsFocusController::GetActive(PRBool
* aActive
)
549 nsFocusController::SetActive(PRBool aActive
)
553 // We may be activated before we ever have a focused window set.
554 // This happens on window creation, where the FocusController
555 // is activated just prior to setting the focused window.
556 // (see nsEventStateManager::PreHandleEvent/NS_ACTIVATE)
557 // If this is the case, we need to queue a notification of the
558 // WindowWatcher until SetFocusedWindow is called.
561 UpdateWWActiveWindow();
563 mUpdateWindowWatcher
= PR_TRUE
;
565 mUpdateWindowWatcher
= PR_FALSE
;
571 nsFocusController::ResetElementFocus()
573 mCurrentElement
= mPreviousElement
= nsnull
;
578 nsFocusController::UpdateWWActiveWindow()
580 // Inform the window watcher of the new active window.
581 nsCOMPtr
<nsIWindowWatcher
> wwatch
= do_GetService("@mozilla.org/embedcomp/window-watcher;1");
584 // This gets the toplevel DOMWindow
585 nsCOMPtr
<nsIDocShellTreeItem
> docShellAsItem
=
586 do_QueryInterface(mCurrentWindow
->GetDocShell());
587 if (!docShellAsItem
) return;
589 nsCOMPtr
<nsIDocShellTreeItem
> rootItem
;
590 docShellAsItem
->GetRootTreeItem(getter_AddRefs(rootItem
));
591 NS_ASSERTION(rootItem
, "Invalid docshell tree - no root!");
593 nsCOMPtr
<nsIDOMWindow
> domWin
= do_GetInterface(rootItem
);
594 wwatch
->SetActiveWindow(domWin
);
598 nsFocusController::GetPopupNode(nsIDOMNode
** aNode
)
601 printf("dr :: nsFocusController::GetPopupNode\n");
605 NS_IF_ADDREF(*aNode
);
610 nsFocusController::SetPopupNode(nsIDOMNode
* aNode
)
613 printf("dr :: nsFocusController::SetPopupNode\n");