Back to index

lightning-sunbird  0.9+nobinonly
nsFocusController.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 3; indent-tabs-mode: nil; c-basic-offset: 2 -*-
00002  *
00003  * ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is the Mozilla browser.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications, Inc.
00020  * Portions created by the Initial Developer are Copyright (C) 1999
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   David W. Hyatt <hyatt@netscape.com> (Original Author)
00025  *   Dan Rosen <dr@netscape.com>
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either of the GNU General Public License Version 2 or later (the "GPL"),
00029  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 
00041 #include "nsIContent.h"
00042 #include "nsIControllers.h"
00043 #include "nsIDOMDocument.h"
00044 #include "nsIDOMHTMLDocument.h"
00045 #include "nsIDOMElement.h"
00046 #include "nsIDOMNSHTMLInputElement.h"
00047 #include "nsIDOMNSHTMLTextAreaElement.h"
00048 #include "nsIDOMNSEvent.h"
00049 #include "nsIDOMWindowInternal.h"
00050 #include "nsIDocument.h"
00051 #include "nsPresContext.h"
00052 #include "nsIPresShell.h"
00053 #include "nsIScriptGlobalObject.h"
00054 #include "nsPIDOMWindow.h"
00055 #include "nsFocusController.h"
00056 #include "prlog.h"
00057 #include "nsIDOMEventTarget.h"
00058 #include "nsIEventStateManager.h"
00059 #include "nsIDocShell.h"
00060 #include "nsIBaseWindow.h"
00061 #include "nsIWindowWatcher.h"
00062 #include "nsIDocShellTreeItem.h"
00063 #include "nsIDocShellTreeOwner.h"
00064 #include "nsIInterfaceRequestorUtils.h"
00065 #include "nsServiceManagerUtils.h"
00066 #include "nsGlobalWindow.h"
00067 
00068 #ifdef MOZ_XUL
00069 #include "nsIDOMXULDocument.h"
00070 #include "nsIDOMXULElement.h"
00071 #endif
00072 
00074 
00075 nsFocusController::nsFocusController(void)
00076 : mSuppressFocus(0), 
00077   mSuppressFocusScroll(PR_FALSE), 
00078   mActive(PR_FALSE),
00079   mUpdateWindowWatcher(PR_FALSE),
00080   mNeedUpdateCommands(PR_FALSE)
00081 {
00082 }
00083 
00084 nsFocusController::~nsFocusController(void)
00085 {
00086 }
00087 
00088 NS_IMPL_ISUPPORTS5(nsFocusController, nsIFocusController,
00089                    nsIFocusController_MOZILLA_1_8_BRANCH, nsIDOMFocusListener,
00090                    nsIDOMEventListener, nsSupportsWeakReference)
00091 
00092 NS_IMETHODIMP
00093 nsFocusController::Create(nsIFocusController** aResult)
00094 {
00095   nsFocusController* controller = new nsFocusController();
00096   if (!controller)
00097     return NS_ERROR_OUT_OF_MEMORY;
00098 
00099   *aResult = controller;
00100   NS_ADDREF(*aResult);
00101   return NS_OK;
00102 }
00103 
00104 
00106 // nsIFocusController Interface
00107 
00108 NS_IMETHODIMP
00109 nsFocusController::GetFocusedElement(nsIDOMElement** aElement)
00110 {
00111   *aElement = mCurrentElement;
00112   NS_IF_ADDREF(*aElement);
00113   return NS_OK;
00114 }
00115 
00116 NS_IMETHODIMP
00117 nsFocusController::GetFocusedWindow(nsIDOMWindowInternal** aWindow)
00118 {
00119   *aWindow = mCurrentWindow;
00120   NS_IF_ADDREF(*aWindow);
00121   return NS_OK;
00122 }
00123 
00124 NS_IMETHODIMP
00125 nsFocusController::SetFocusedElement(nsIDOMElement* aElement)
00126 {
00127   if (mCurrentElement) 
00128     mPreviousElement = mCurrentElement;
00129   else if (aElement) 
00130     mPreviousElement = aElement;
00131 
00132   mNeedUpdateCommands = mNeedUpdateCommands || mCurrentElement != aElement;
00133   mCurrentElement = aElement;
00134 
00135   if (!mSuppressFocus) {
00136     // Need to update focus commands when focus switches from
00137     // an element to no element, so don't test mCurrentElement
00138     // before updating.
00139     UpdateCommands();
00140   }
00141   return NS_OK;
00142 }
00143 
00144 NS_IMETHODIMP
00145 nsFocusController::RewindFocusState()
00146 {
00147   mCurrentElement = mPreviousElement;
00148   mCurrentWindow = mPreviousWindow;
00149 
00150   return NS_OK;
00151 }
00152 
00153 NS_IMETHODIMP
00154 nsFocusController::SetFocusedWindow(nsIDOMWindowInternal* aWindow)
00155 {
00156   nsCOMPtr<nsPIDOMWindow> pwin = do_QueryInterface(aWindow);
00157 
00158   if (pwin) {
00159     pwin = pwin->GetOuterWindow();
00160   }
00161 
00162   NS_ASSERTION(!pwin || !pwin->IsInnerWindow(),
00163                "Uh, inner window can't have focus!");
00164 
00165   nsCOMPtr<nsIDOMWindowInternal> win = do_QueryInterface(pwin);
00166 
00167   if (win && (mCurrentWindow != win)) {
00168     nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(win);
00169     if (sgo) {
00170       nsCOMPtr<nsIBaseWindow> basewin = do_QueryInterface(sgo->GetDocShell());
00171       if (basewin)
00172         basewin->SetFocus();
00173     }
00174   }
00175 
00176   if (mCurrentWindow) {
00177     mPreviousWindow = mCurrentWindow;
00178   } else if (win) {
00179     mPreviousWindow = win;
00180   }
00181 
00182   mNeedUpdateCommands = mNeedUpdateCommands || mCurrentWindow != win;
00183   mCurrentWindow = win;
00184 
00185   if (mUpdateWindowWatcher) {
00186     NS_ASSERTION(mActive, "This shouldn't happen");
00187     if (mCurrentWindow)
00188       UpdateWWActiveWindow();
00189     mUpdateWindowWatcher = PR_FALSE;
00190   }
00191 
00192   return NS_OK;
00193 }
00194 
00195 
00196 void
00197 nsFocusController::UpdateCommands()
00198 {
00199   if (!mNeedUpdateCommands) {
00200     return;
00201   }
00202   nsCOMPtr<nsIDOMWindowInternal> window;
00203   nsCOMPtr<nsIDocument> doc;
00204   if (mCurrentWindow) {
00205     window = mCurrentWindow;
00206     nsCOMPtr<nsIDOMWindow> domWin(do_QueryInterface(window));
00207     nsCOMPtr<nsIDOMDocument> domDoc;
00208     domWin->GetDocument(getter_AddRefs(domDoc));
00209     doc = do_QueryInterface(domDoc);
00210   }
00211   else if (mCurrentElement) {
00212     nsCOMPtr<nsIDOMDocument> domDoc;
00213     mCurrentElement->GetOwnerDocument(getter_AddRefs(domDoc));
00214     if (domDoc) {
00215       doc = do_QueryInterface(domDoc);
00216       window = do_QueryInterface(doc->GetScriptGlobalObject());
00217     }
00218   }
00219 
00220   // If there is no presshell, it's a zombie document which can't handle the command updates
00221   if (window && doc && doc->GetNumberOfShells()) {
00222     // Not a zombie document, so we can handle the command update
00223     window->UpdateCommands(NS_LITERAL_STRING("focus"));
00224     mNeedUpdateCommands = PR_FALSE;
00225   }
00226 }
00227 
00228   
00229 NS_IMETHODIMP
00230 nsFocusController::GetControllers(nsIControllers** aResult)
00231 {
00232   // XXX: we should fix this so there's a generic interface that
00233   // describes controllers, so this code would have no special
00234   // knowledge of what object might have controllers.
00235   if (mCurrentElement) {
00236 
00237 #ifdef MOZ_XUL
00238     nsCOMPtr<nsIDOMXULElement> xulElement(do_QueryInterface(mCurrentElement));
00239     if (xulElement)
00240       return xulElement->GetControllers(aResult);
00241 #endif
00242 
00243     nsCOMPtr<nsIDOMNSHTMLTextAreaElement> htmlTextArea =
00244       do_QueryInterface(mCurrentElement);
00245     if (htmlTextArea)
00246       return htmlTextArea->GetControllers(aResult);
00247 
00248     nsCOMPtr<nsIDOMNSHTMLInputElement> htmlInputElement =
00249       do_QueryInterface(mCurrentElement);
00250     if (htmlInputElement)
00251       return htmlInputElement->GetControllers(aResult);
00252   }
00253   else if (mCurrentWindow) {
00254     nsCOMPtr<nsIDOMWindowInternal> domWindow =
00255       do_QueryInterface(mCurrentWindow);
00256     if (domWindow)
00257       return domWindow->GetControllers(aResult);
00258   }
00259 
00260   *aResult = nsnull;
00261   return NS_OK;
00262 }
00263 
00264 NS_IMETHODIMP
00265 nsFocusController::MoveFocus(PRBool aForward, nsIDOMElement* aElt)
00266 {
00267   // Obtain the doc that we'll be shifting focus inside.
00268   nsCOMPtr<nsIDocument> doc;
00269   nsCOMPtr<nsIContent> content;
00270   if (aElt) {
00271     content = do_QueryInterface(aElt);
00272     doc = content->GetDocument();
00273   }
00274   else {
00275     if (mCurrentElement) {
00276       content = do_QueryInterface(mCurrentElement);
00277       doc = content->GetDocument();
00278       content = nsnull;
00279     }
00280     else if (mCurrentWindow) {
00281       nsCOMPtr<nsIDOMDocument> domDoc;
00282       mCurrentWindow->GetDocument(getter_AddRefs(domDoc));
00283       doc = do_QueryInterface(domDoc);
00284     }
00285   }
00286 
00287   if (!doc)
00288     // No way to obtain an event state manager.  Give up.
00289     return NS_OK;
00290 
00291 
00292   // Obtain a presentation context
00293   PRInt32 count = doc->GetNumberOfShells();
00294   if (count == 0)
00295     return NS_OK;
00296 
00297   nsIPresShell *shell = doc->GetShellAt(0);
00298   if (!shell)
00299     return NS_OK;
00300 
00301   // Make sure frames have been constructed before shifting focus, bug 273092.
00302   shell->FlushPendingNotifications(Flush_Frames);
00303 
00304   // Retrieve the context
00305   nsCOMPtr<nsPresContext> presContext = shell->GetPresContext();
00306 
00307   // Make this ESM shift the focus per our instructions.
00308   presContext->EventStateManager()->ShiftFocus(aForward, content);
00309 
00310   return NS_OK;
00311 }
00312 
00314 // nsIDOMFocusListener
00316 
00317 nsresult 
00318 nsFocusController::Focus(nsIDOMEvent* aEvent)
00319 {
00320   if (mSuppressFocus)
00321     return NS_OK;
00322 
00323   nsCOMPtr<nsIDOMEventTarget> t;
00324 
00325   nsCOMPtr<nsIDOMNSEvent> nsevent(do_QueryInterface(aEvent));
00326   if (nsevent) {
00327     nsevent->GetOriginalTarget(getter_AddRefs(t));
00328   }
00329 
00330   nsCOMPtr<nsIDOMElement> domElement = do_QueryInterface(t);
00331   if (domElement && (domElement != mCurrentElement)) {
00332     SetFocusedElement(domElement);
00333 
00334     // Also set focus to our innermost window.
00335     // XXX Must be done for the Ender case, since ender causes a blur,
00336     // but we don't hear the subsequent focus to the Ender window.
00337     nsCOMPtr<nsIDOMDocument> ownerDoc;
00338     domElement->GetOwnerDocument(getter_AddRefs(ownerDoc));
00339     nsCOMPtr<nsIDOMWindowInternal> domWindow = GetWindowFromDocument(ownerDoc);
00340     if (domWindow)
00341       SetFocusedWindow(domWindow);
00342   }
00343   else {
00344     // We're focusing a window.  We only want to do an update commands
00345     // if no element is focused.
00346     nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(t);
00347     if (domDoc) {
00348       nsCOMPtr<nsIDOMWindowInternal> domWindow = GetWindowFromDocument(domDoc);
00349       if (domWindow) {
00350         SetFocusedWindow(domWindow);
00351         if (mCurrentElement) {
00352           // Make sure this element is in our window. If not, we
00353           // should clear this field.
00354           nsCOMPtr<nsIDOMDocument> ownerDoc;
00355           mCurrentElement->GetOwnerDocument(getter_AddRefs(ownerDoc));
00356           nsCOMPtr<nsIDOMDocument> windowDoc;
00357           mCurrentWindow->GetDocument(getter_AddRefs(windowDoc));
00358           if (ownerDoc != windowDoc)
00359             mCurrentElement = mPreviousElement = nsnull;
00360         }
00361         else
00362           mPreviousElement = nsnull;
00363 
00364         if (!mCurrentElement) {
00365           UpdateCommands();
00366         }
00367       }
00368     }
00369   }
00370 
00371   return NS_OK;
00372 }
00373 
00374 nsresult 
00375 nsFocusController::Blur(nsIDOMEvent* aEvent)
00376 {
00377   if (mSuppressFocus)
00378     return NS_OK;
00379 
00380   nsCOMPtr<nsIDOMEventTarget> t;
00381 
00382   nsCOMPtr<nsIDOMNSEvent> nsevent(do_QueryInterface(aEvent));
00383 
00384   if (nsevent) {
00385     nsevent->GetOriginalTarget(getter_AddRefs(t));
00386   }
00387 
00388   nsCOMPtr<nsIDOMElement> domElement = do_QueryInterface(t);
00389   if (domElement) {
00390     SetFocusedElement(nsnull);
00391   }
00392   
00393   nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(t);
00394   if (domDoc) {
00395     nsCOMPtr<nsIDOMWindowInternal> domWindow = GetWindowFromDocument(domDoc);
00396     if (domWindow)
00397       SetFocusedWindow(nsnull);
00398   }
00399 
00400   return NS_OK;
00401 }
00402 
00403 nsPIDOMWindow *
00404 nsFocusController::GetWindowFromDocument(nsIDOMDocument* aDocument)
00405 {
00406   nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDocument);
00407   if (!doc)
00408     return NS_OK;
00409 
00410   nsCOMPtr<nsPIDOMWindow> win =
00411     do_QueryInterface(doc->GetScriptGlobalObject());
00412 
00413   if (win && win->IsInnerWindow()) {
00414     return win->GetOuterWindow();
00415   }
00416 
00417   return win;
00418 }
00419 
00420 NS_IMETHODIMP
00421 nsFocusController::GetControllerForCommand(const char * aCommand,
00422                                            nsIController** _retval)
00423 {
00424   NS_ENSURE_ARG_POINTER(_retval);  
00425   *_retval = nsnull;
00426 
00427   nsCOMPtr<nsIControllers> controllers;
00428   nsCOMPtr<nsIController> controller;
00429 
00430   GetControllers(getter_AddRefs(controllers));
00431   if(controllers) {
00432     controllers->GetControllerForCommand(aCommand, getter_AddRefs(controller));
00433     if(controller) {
00434       controller.swap(*_retval);
00435       return NS_OK;
00436     }
00437   }
00438   
00439   nsCOMPtr<nsPIDOMWindow> currentWindow;
00440   if (mCurrentElement) {
00441     // Move up to the window.
00442     nsCOMPtr<nsIDOMDocument> domDoc;
00443     mCurrentElement->GetOwnerDocument(getter_AddRefs(domDoc));
00444     currentWindow = do_QueryInterface(GetWindowFromDocument(domDoc));
00445   }
00446   else if (mCurrentWindow) {
00447     nsGlobalWindow *win =
00448       NS_STATIC_CAST(nsGlobalWindow *,
00449                      NS_STATIC_CAST(nsIDOMWindowInternal *, mCurrentWindow));
00450     currentWindow = win->GetPrivateParent();
00451   }
00452   else return NS_OK;
00453 
00454   while(currentWindow) {
00455     nsCOMPtr<nsIDOMWindowInternal> domWindow(do_QueryInterface(currentWindow));
00456 
00457     nsCOMPtr<nsIControllers> controllers2;
00458     domWindow->GetControllers(getter_AddRefs(controllers2));
00459     if(controllers2) {
00460       controllers2->GetControllerForCommand(aCommand,
00461                                             getter_AddRefs(controller));
00462       if(controller) {
00463         controller.swap(*_retval);
00464         return NS_OK;
00465       }
00466     }
00467 
00468     nsGlobalWindow *win =
00469       NS_STATIC_CAST(nsGlobalWindow *,
00470                      NS_STATIC_CAST(nsIDOMWindowInternal *, currentWindow));
00471     currentWindow = win->GetPrivateParent();
00472   }
00473   
00474   return NS_OK;
00475 }
00476 
00477 NS_IMETHODIMP
00478 nsFocusController::GetSuppressFocusScroll(PRBool* aSuppressFocusScroll)
00479 {
00480   *aSuppressFocusScroll = mSuppressFocusScroll;
00481   return NS_OK;
00482 }
00483 
00484 NS_IMETHODIMP
00485 nsFocusController::SetSuppressFocusScroll(PRBool aSuppressFocusScroll)
00486 {
00487   mSuppressFocusScroll = aSuppressFocusScroll;
00488   return NS_OK;
00489 }
00490 
00491 NS_IMETHODIMP
00492 nsFocusController::GetSuppressFocus(PRBool* aSuppressFocus)
00493 {
00494   *aSuppressFocus = (mSuppressFocus > 0);
00495   return NS_OK;
00496 }
00497 
00498 NS_IMETHODIMP
00499 nsFocusController::SetSuppressFocus(PRBool aSuppressFocus, const char* aReason)
00500 {
00501   if(aSuppressFocus) {
00502     ++mSuppressFocus;
00503     //#ifdef DEBUG_hyatt
00504     //printf("[%p] SuppressFocus incremented to %d. The reason is %s.\n", this, mSuppressFocus, aReason);
00505     //#endif
00506   }
00507   else if(mSuppressFocus > 0) {
00508     --mSuppressFocus;
00509     //#ifdef DEBUG_hyatt
00510     //printf("[%p] SuppressFocus decremented to %d. The reason is %s.\n", this, mSuppressFocus, aReason);
00511     //#endif
00512   }
00513   else 
00514     NS_ASSERTION(PR_FALSE, "Attempt to decrement focus controller's suppression when no suppression active!\n");
00515 
00516   // we are unsuppressing after activating, so update focus-related commands
00517   // we need this to update command, including the case where there is no element
00518   // because nsPresShell::UnsuppressPainting may have just now unsuppressed
00519   // focus on the currently focused window
00520   if (!mSuppressFocus) {
00521     // Always update commands if we have a current element
00522     mNeedUpdateCommands = mNeedUpdateCommands || mCurrentElement;
00523     UpdateCommands();
00524   }
00525   
00526   return NS_OK;
00527 }
00528 
00529 NS_IMETHODIMP
00530 nsFocusController::GetActive(PRBool* aActive)
00531 {
00532   *aActive = mActive;
00533   return NS_OK;
00534 }
00535 
00536 NS_IMETHODIMP
00537 nsFocusController::SetActive(PRBool aActive)
00538 {
00539   mActive = aActive;
00540 
00541   // We may be activated before we ever have a focused window set.
00542   // This happens on window creation, where the FocusController
00543   // is activated just prior to setting the focused window.
00544   // (see nsEventStateManager::PreHandleEvent/NS_ACTIVATE)
00545   // If this is the case, we need to queue a notification of the
00546   // WindowWatcher until SetFocusedWindow is called.
00547   if (mActive) {
00548     if (mCurrentWindow)
00549       UpdateWWActiveWindow();
00550     else
00551       mUpdateWindowWatcher = PR_TRUE;
00552   }
00553 
00554   return NS_OK;
00555 }
00556 
00557 NS_IMETHODIMP
00558 nsFocusController::ResetElementFocus()
00559 {
00560   mCurrentElement = mPreviousElement = nsnull;
00561   return NS_OK;
00562 }
00563 
00564 void
00565 nsFocusController::UpdateWWActiveWindow()
00566 {
00567   // Inform the window watcher of the new active window.
00568   nsCOMPtr<nsIWindowWatcher> wwatch = do_GetService("@mozilla.org/embedcomp/window-watcher;1");
00569   if (!wwatch) return;
00570 
00571   // This gets the toplevel DOMWindow
00572   nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(mCurrentWindow);
00573 
00574   nsCOMPtr<nsIDocShellTreeItem> docShellAsItem =
00575     do_QueryInterface(sgo->GetDocShell());
00576   if (!docShellAsItem) return;
00577 
00578   nsCOMPtr<nsIDocShellTreeItem> rootItem;
00579   docShellAsItem->GetRootTreeItem(getter_AddRefs(rootItem));
00580   NS_ASSERTION(rootItem, "Invalid docshell tree - no root!");
00581 
00582   nsCOMPtr<nsIDOMWindow> domWin = do_GetInterface(rootItem);
00583   wwatch->SetActiveWindow(domWin);
00584 }
00585 
00586 NS_IMETHODIMP
00587 nsFocusController::GetPopupNode(nsIDOMNode** aNode)
00588 {
00589 #ifdef DEBUG_dr
00590   printf("dr :: nsFocusController::GetPopupNode\n");
00591 #endif
00592 
00593   *aNode = mPopupNode;
00594   NS_IF_ADDREF(*aNode);
00595   return NS_OK;
00596 }
00597 
00598 NS_IMETHODIMP
00599 nsFocusController::SetPopupNode(nsIDOMNode* aNode)
00600 {
00601 #ifdef DEBUG_dr
00602   printf("dr :: nsFocusController::SetPopupNode\n");
00603 #endif
00604 
00605   mPopupNode = aNode;
00606   return NS_OK;
00607 }
00608 
00609 NS_IMETHODIMP
00610 nsFocusController::GetPopupEvent(nsIDOMEvent** aEvent)
00611 {
00612   *aEvent = mPopupEvent;
00613   NS_IF_ADDREF(*aEvent);
00614   return NS_OK;
00615 }
00616 
00617 NS_IMETHODIMP
00618 nsFocusController::SetPopupEvent(nsIDOMEvent* aEvent)
00619 {
00620   mPopupEvent = aEvent;
00621   return NS_OK;
00622 }