Back to index

lightning-sunbird  0.9+nobinonly
nsEventStateManager.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* vim: set ts=2 sw=2 et tw=80: */
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 mozilla.org code.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications Corporation.
00020  * Portions created by the Initial Developer are Copyright (C) 1998
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   Makoto Kato  <m_kato@ga2.so-net.ne.jp>
00025  *   Dean Tessman <dean_tessman@hotmail.com>
00026  *   Mats Palmgren <mats.palmgren@bredband.net>
00027  *   Simon B├╝nzli <zeniko@gmail.com>
00028  *
00029  * Alternatively, the contents of this file may be used under the terms of
00030  * either of the GNU General Public License Version 2 or later (the "GPL"),
00031  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00032  * in which case the provisions of the GPL or the LGPL are applicable instead
00033  * of those above. If you wish to allow use of your version of this file only
00034  * under the terms of either the GPL or the LGPL, and not to allow others to
00035  * use your version of this file under the terms of the MPL, indicate your
00036  * decision by deleting the provisions above and replace them with the notice
00037  * and other provisions required by the GPL or the LGPL. If you do not delete
00038  * the provisions above, a recipient may use your version of this file under
00039  * the terms of any one of the MPL, the GPL or the LGPL.
00040  *
00041  * ***** END LICENSE BLOCK ***** */
00042 
00043 #include "nsCOMPtr.h"
00044 #include "nsEventStateManager.h"
00045 #include "nsEventListenerManager.h"
00046 #include "nsIContent.h"
00047 #include "nsINodeInfo.h"
00048 #include "nsIDocument.h"
00049 #include "nsIFrame.h"
00050 #include "nsIWidget.h"
00051 #include "nsPresContext.h"
00052 #include "nsIPresShell.h"
00053 #include "nsDOMEvent.h"
00054 #include "nsHTMLAtoms.h"
00055 #include "nsIEditorDocShell.h"
00056 #include "nsIFormControl.h"
00057 #include "nsIComboboxControlFrame.h"
00058 #include "nsIDOMHTMLAnchorElement.h"
00059 #include "nsIDOMHTMLInputElement.h"
00060 #include "nsIDOMNSHTMLInputElement.h"
00061 #include "nsIDOMHTMLSelectElement.h"
00062 #include "nsIDOMHTMLTextAreaElement.h"
00063 #include "nsIDOMHTMLAreaElement.h"
00064 #include "nsIDOMHTMLButtonElement.h"
00065 #include "nsIDOMHTMLObjectElement.h"
00066 #include "nsIDOMHTMLImageElement.h"
00067 #include "nsIDOMHTMLMapElement.h"
00068 #include "nsIDOMHTMLBodyElement.h"
00069 #include "nsIDOMXULControlElement.h"
00070 #include "nsImageMapUtils.h"
00071 #include "nsIHTMLDocument.h"
00072 #include "nsINameSpaceManager.h"
00073 #include "nsIBaseWindow.h"
00074 #include "nsIScrollableView.h"
00075 #include "nsISelection.h"
00076 #include "nsIFrameSelection.h"
00077 #include "nsIDeviceContext.h"
00078 #include "nsIScriptGlobalObject.h"
00079 #include "nsIPrivateDOMEvent.h"
00080 #include "nsIDOMWindowInternal.h"
00081 #include "nsPIDOMWindow.h"
00082 #include "nsIDOMEventTarget.h"
00083 #include "nsIEnumerator.h"
00084 #include "nsIDocShellTreeItem.h"
00085 #include "nsIDocShellTreeNode.h"
00086 #include "nsIWebNavigation.h"
00087 #include "nsIContentViewer.h"
00088 #include "nsIPrefBranch2.h"
00089 
00090 #include "nsIServiceManager.h"
00091 #include "nsIScriptSecurityManager.h"
00092 
00093 #include "nsIChromeEventHandler.h"
00094 #include "nsIFocusController.h"
00095 
00096 #include "nsXULAtoms.h"
00097 #include "nsIDOMXULElement.h"
00098 #include "nsIDOMDocument.h"
00099 #include "nsIDOMKeyEvent.h"
00100 #include "nsIObserverService.h"
00101 #include "nsIDocShell.h"
00102 #include "nsIMarkupDocumentViewer.h"
00103 #include "nsIScrollableViewProvider.h"
00104 #include "nsIDOMDocumentRange.h"
00105 #include "nsIDOMDocumentEvent.h"
00106 #include "nsIDOMMouseEvent.h"
00107 #include "nsIDOMEventTarget.h"
00108 #include "nsIDOMDocumentView.h"
00109 #include "nsIDOMAbstractView.h"
00110 #include "nsIDOMNSUIEvent.h"
00111 
00112 #include "nsIDOMRange.h"
00113 #include "nsICaret.h"
00114 #include "nsILookAndFeel.h"
00115 #include "nsWidgetsCID.h"
00116 
00117 #include "nsIFrameFrame.h"
00118 #include "nsIFrameTraversal.h"
00119 #include "nsLayoutAtoms.h"
00120 #include "nsLayoutCID.h"
00121 #include "nsLayoutUtils.h"
00122 #include "nsIInterfaceRequestorUtils.h"
00123 #include "nsUnicharUtils.h"
00124 #include "nsContentUtils.h"
00125 
00126 #include "imgIContainer.h"
00127 #include "nsIProperties.h"
00128 #include "nsISupportsPrimitives.h"
00129 
00130 #if defined (XP_MAC) || defined(XP_MACOSX)
00131 #include <Events.h>
00132 #endif
00133 
00134 #if defined(DEBUG_rods) || defined(DEBUG_bryner)
00135 //#define DEBUG_DOCSHELL_FOCUS
00136 #endif
00137 
00138 static NS_DEFINE_CID(kFrameTraversalCID, NS_FRAMETRAVERSAL_CID);
00139 
00140 
00141 //we will use key binding by default now. this wil lbreak viewer for now
00142 #define NON_KEYBINDING 0
00143 
00144 static NS_DEFINE_CID(kLookAndFeelCID, NS_LOOKANDFEEL_CID);
00145 
00146 nsIContent * gLastFocusedContent = 0; // Strong reference
00147 nsIDocument * gLastFocusedDocument = 0; // Strong reference
00148 nsPresContext* gLastFocusedPresContext = 0; // Weak reference
00149 
00150 enum nsTextfieldSelectModel {
00151   eTextfieldSelect_unset = -1,
00152   eTextfieldSelect_manual = 0,
00153   eTextfieldSelect_auto = 1   // select textfields when focused with keyboard
00154 };
00155 
00156 // Tab focus policy (static, constant across the app):
00157 // Which types of elements are in the tab order?
00158 static PRInt8 sTextfieldSelectModel = eTextfieldSelect_unset;
00159 static PRBool sLeftClickOnly = PR_TRUE;
00160 static PRBool sKeyCausesActivation = PR_TRUE;
00161 static PRUint32 sESMInstanceCount = 0;
00162 static PRInt32 sChromeAccessModifier = 0, sContentAccessModifier = 0;
00163 PRInt32 nsEventStateManager::sUserInputEventDepth = 0;
00164 
00165 enum {
00166  MOUSE_SCROLL_N_LINES,
00167  MOUSE_SCROLL_PAGE,
00168  MOUSE_SCROLL_HISTORY,
00169  MOUSE_SCROLL_TEXTSIZE,
00170  MOUSE_SCROLL_PIXELS
00171 };
00172 
00173 // mask values for ui.key.chromeAccess and ui.key.contentAccess
00174 #define NS_MODIFIER_SHIFT    1
00175 #define NS_MODIFIER_CONTROL  2
00176 #define NS_MODIFIER_ALT      4
00177 #define NS_MODIFIER_META     8
00178 
00179 static nsIScriptGlobalObject *
00180 GetDocumentOuterWindow(nsIDocument *aDocument)
00181 {
00182   if (aDocument) {
00183     nsIScriptGlobalObject *sgo = aDocument->GetScriptGlobalObject();
00184     nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(sgo);
00185 
00186     if (win) {
00187       nsCOMPtr<nsIScriptGlobalObject> outersgo =
00188         do_QueryInterface(win->GetOuterWindow());
00189 
00190       return outersgo;
00191     }
00192 
00193     return sgo;
00194   }
00195 
00196   return nsnull;
00197 }
00198 
00199 static nsIDocument *
00200 GetDocumentFromWindow(nsIDOMWindow *aWindow)
00201 {
00202   nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(aWindow);
00203   nsPIDOMWindow *innerWin;
00204   nsCOMPtr<nsIDocument> doc;
00205 
00206   if (win) {
00207     doc = do_QueryInterface(win->GetExtantDocument());
00208   }
00209 
00210   return doc;
00211 }
00212 
00213 static PRInt32
00214 GetAccessModifierMaskFromPref(PRInt32 aItemType)
00215 {
00216   PRInt32 accessKey = nsContentUtils::GetIntPref("ui.key.generalAccessKey", -1);
00217   switch (accessKey) {
00218     case -1:                             break; // use the individual prefs
00219     case nsIDOMKeyEvent::DOM_VK_SHIFT:   return NS_MODIFIER_SHIFT;
00220     case nsIDOMKeyEvent::DOM_VK_CONTROL: return NS_MODIFIER_CONTROL;
00221     case nsIDOMKeyEvent::DOM_VK_ALT:     return NS_MODIFIER_ALT;
00222     case nsIDOMKeyEvent::DOM_VK_META:    return NS_MODIFIER_META;
00223     default:                             return 0;
00224   }
00225 
00226   switch (aItemType) {
00227   case nsIDocShellTreeItem::typeChrome:
00228     return nsContentUtils::GetIntPref("ui.key.chromeAccess", 0);
00229   case nsIDocShellTreeItem::typeContent:
00230     return nsContentUtils::GetIntPref("ui.key.contentAccess", 0);
00231   default:
00232     return 0;
00233   }
00234 }
00235 
00236 /******************************************************************/
00237 /* nsEventStateManager                                            */
00238 /******************************************************************/
00239 
00240 nsEventStateManager::nsEventStateManager()
00241   : mLockCursor(0),
00242     mCurrentTarget(nsnull),
00243     mLastMouseOverFrame(nsnull),
00244     mLastDragOverFrame(nsnull),
00245     // init d&d gesture state machine variables
00246     mGestureDownPoint(0,0),
00247     mCurrentFocusFrame(nsnull),
00248     mCurrentTabIndex(0),
00249     mLastFocusedWith(eEventFocusedByUnknown),
00250     mPresContext(nsnull),
00251     mLClickCount(0),
00252     mMClickCount(0),
00253     mRClickCount(0),
00254     mConsumeFocusEvents(PR_FALSE),
00255     mNormalLMouseEventInProcess(PR_FALSE),
00256     m_haveShutdown(PR_FALSE),
00257     mClearedFrameRefsDuringEvent(PR_FALSE),
00258     mBrowseWithCaret(PR_FALSE),
00259     mTabbedThroughDocument(PR_FALSE),
00260     mDOMEventLevel(0),
00261     mAccessKeys(nsnull)
00262 {
00263   ++sESMInstanceCount;
00264 }
00265 
00266 NS_IMETHODIMP
00267 nsEventStateManager::Init()
00268 {
00269   nsresult rv;
00270   nsCOMPtr<nsIObserverService> observerService =
00271            do_GetService("@mozilla.org/observer-service;1", &rv);
00272   NS_ENSURE_SUCCESS(rv, rv);
00273 
00274   observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, PR_TRUE);
00275 
00276   nsCOMPtr<nsIPrefBranch2> prefBranch =
00277     do_QueryInterface(nsContentUtils::GetPrefBranch());
00278 
00279   if (prefBranch) {
00280     if (sESMInstanceCount == 1) {
00281       sLeftClickOnly =
00282         nsContentUtils::GetBoolPref("nglayout.events.dispatchLeftClickOnly",
00283                                     sLeftClickOnly);
00284 
00285       sChromeAccessModifier =
00286         GetAccessModifierMaskFromPref(nsIDocShellTreeItem::typeChrome);
00287       sContentAccessModifier =
00288         GetAccessModifierMaskFromPref(nsIDocShellTreeItem::typeContent);
00289 
00290       nsIContent::sTabFocusModelAppliesToXUL =
00291         nsContentUtils::GetBoolPref("accessibility.tabfocus_applies_to_xul",
00292                                     nsIContent::sTabFocusModelAppliesToXUL);
00293     }
00294     prefBranch->AddObserver("accessibility.accesskeycausesactivation", this, PR_TRUE);
00295     prefBranch->AddObserver("accessibility.browsewithcaret", this, PR_TRUE);
00296     prefBranch->AddObserver("accessibility.tabfocus_applies_to_xul", this, PR_TRUE);
00297     prefBranch->AddObserver("nglayout.events.dispatchLeftClickOnly", this, PR_TRUE);
00298     prefBranch->AddObserver("ui.key.generalAccessKey", this, PR_TRUE);
00299     prefBranch->AddObserver("ui.key.chromeAccess", this, PR_TRUE);
00300     prefBranch->AddObserver("ui.key.contentAccess", this, PR_TRUE);
00301 #if 0
00302     prefBranch->AddObserver("mousewheel.withaltkey.action", this, PR_TRUE);
00303     prefBranch->AddObserver("mousewheel.withaltkey.numlines", this, PR_TRUE);
00304     prefBranch->AddObserver("mousewheel.withaltkey.sysnumlines", this, PR_TRUE);
00305     prefBranch->AddObserver("mousewheel.withcontrolkey.action", this, PR_TRUE);
00306     prefBranch->AddObserver("mousewheel.withcontrolkey.numlines", this, PR_TRUE);
00307     prefBranch->AddObserver("mousewheel.withcontrolkey.sysnumlines", this, PR_TRUE);
00308     prefBranch->AddObserver("mousewheel.withnokey.action", this, PR_TRUE);
00309     prefBranch->AddObserver("mousewheel.withnokey.numlines", this, PR_TRUE);
00310     prefBranch->AddObserver("mousewheel.withnokey.sysnumlines", this, PR_TRUE);
00311     prefBranch->AddObserver("mousewheel.withshiftkey.action", this, PR_TRUE);
00312     prefBranch->AddObserver("mousewheel.withshiftkey.numlines", this, PR_TRUE);
00313     prefBranch->AddObserver("mousewheel.withshiftkey.sysnumlines", this, PR_TRUE);
00314 #endif
00315 
00316     prefBranch->AddObserver("dom.popup_allowed_events", this, PR_TRUE);
00317   }
00318 
00319   if (sTextfieldSelectModel == eTextfieldSelect_unset) {
00320     nsCOMPtr<nsILookAndFeel> lookNFeel(do_GetService(kLookAndFeelCID));
00321     PRInt32 selectTextfieldsOnKeyFocus = 0;
00322     lookNFeel->GetMetric(nsILookAndFeel::eMetric_SelectTextfieldsOnKeyFocus,
00323                          selectTextfieldsOnKeyFocus);
00324     sTextfieldSelectModel = selectTextfieldsOnKeyFocus ? eTextfieldSelect_auto:
00325                                                          eTextfieldSelect_manual;
00326   }
00327 
00328   return rv;
00329 }
00330 
00331 nsEventStateManager::~nsEventStateManager()
00332 {
00333 #if CLICK_HOLD_CONTEXT_MENUS
00334   KillClickHoldTimer();
00335 #endif
00336 
00337   --sESMInstanceCount;
00338   if(sESMInstanceCount == 0) {
00339     NS_IF_RELEASE(gLastFocusedContent);
00340     NS_IF_RELEASE(gLastFocusedDocument);
00341   }
00342 
00343   delete mAccessKeys;
00344 
00345   if (!m_haveShutdown) {
00346     Shutdown();
00347 
00348     // Don't remove from Observer service in Shutdown because Shutdown also
00349     // gets called from xpcom shutdown observer.  And we don't want to remove
00350     // from the service in that case.
00351 
00352     nsresult rv;
00353 
00354     nsCOMPtr<nsIObserverService> observerService =
00355              do_GetService("@mozilla.org/observer-service;1", &rv);
00356     if (NS_SUCCEEDED(rv)) {
00357       observerService->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
00358     }
00359   }
00360 
00361 }
00362 
00363 nsresult
00364 nsEventStateManager::Shutdown()
00365 {
00366   nsCOMPtr<nsIPrefBranch2> prefBranch =
00367     do_QueryInterface(nsContentUtils::GetPrefBranch());
00368 
00369   if (prefBranch) {
00370     prefBranch->RemoveObserver("accessibility.accesskeycausesactivation", this);
00371     prefBranch->RemoveObserver("accessibility.browsewithcaret", this);
00372     prefBranch->RemoveObserver("accessibility.tabfocus_applies_to_xul", this);
00373     prefBranch->RemoveObserver("nglayout.events.dispatchLeftClickOnly", this);
00374     prefBranch->RemoveObserver("ui.key.generalAccessKey", this);
00375     prefBranch->RemoveObserver("ui.key.chromeAccess", this);
00376     prefBranch->RemoveObserver("ui.key.contentAccess", this);
00377 #if 0
00378     prefBranch->RemoveObserver("mousewheel.withshiftkey.action", this);
00379     prefBranch->RemoveObserver("mousewheel.withshiftkey.numlines", this);
00380     prefBranch->RemoveObserver("mousewheel.withshiftkey.sysnumlines", this);
00381     prefBranch->RemoveObserver("mousewheel.withcontrolkey.action", this);
00382     prefBranch->RemoveObserver("mousewheel.withcontrolkey.numlines", this);
00383     prefBranch->RemoveObserver("mousewheel.withcontrolkey.sysnumlines", this);
00384     prefBranch->RemoveObserver("mousewheel.withaltkey.action", this);
00385     prefBranch->RemoveObserver("mousewheel.withaltkey.numlines", this);
00386     prefBranch->RemoveObserver("mousewheel.withaltkey.sysnumlines", this);
00387     prefBranch->RemoveObserver("mousewheel.withnokey.action", this);
00388     prefBranch->RemoveObserver("mousewheel.withnokey.numlines", this);
00389     prefBranch->RemoveObserver("mousewheel.withnokey.sysnumlines", this);
00390 #endif
00391 
00392     prefBranch->RemoveObserver("dom.popup_allowed_events", this);
00393   }
00394 
00395   m_haveShutdown = PR_TRUE;
00396   return NS_OK;
00397 }
00398 
00399 NS_IMETHODIMP
00400 nsEventStateManager::Observe(nsISupports *aSubject,
00401                              const char *aTopic,
00402                              const PRUnichar *someData)
00403 {
00404   if (!nsCRT::strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID))
00405     Shutdown();
00406   else if (!nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
00407     if (!someData)
00408       return NS_OK;
00409 
00410     nsDependentString data(someData);
00411     if (data.EqualsLiteral("accessibility.accesskeycausesactivation")) {
00412       sKeyCausesActivation =
00413         nsContentUtils::GetBoolPref("accessibility.accesskeycausesactivation",
00414                                     sKeyCausesActivation);
00415     } else if (data.EqualsLiteral("accessibility.browsewithcaret")) {
00416       ResetBrowseWithCaret();
00417     } else if (data.EqualsLiteral("accessibility.tabfocus_applies_to_xul")) {
00418       nsIContent::sTabFocusModelAppliesToXUL =
00419         nsContentUtils::GetBoolPref("accessibility.tabfocus_applies_to_xul",
00420                                     nsIContent::sTabFocusModelAppliesToXUL);
00421     } else if (data.EqualsLiteral("nglayout.events.dispatchLeftClickOnly")) {
00422       sLeftClickOnly =
00423         nsContentUtils::GetBoolPref("nglayout.events.dispatchLeftClickOnly",
00424                                     sLeftClickOnly);
00425     } else if (data.EqualsLiteral("ui.key.generalAccessKey")) {
00426       sChromeAccessModifier =
00427         GetAccessModifierMaskFromPref(nsIDocShellTreeItem::typeChrome);
00428       sContentAccessModifier =
00429         GetAccessModifierMaskFromPref(nsIDocShellTreeItem::typeContent);
00430     } else if (data.EqualsLiteral("ui.key.chromeAccess")) {
00431       sChromeAccessModifier =
00432         GetAccessModifierMaskFromPref(nsIDocShellTreeItem::typeChrome);
00433     } else if (data.EqualsLiteral("ui.key.contentAccess")) {
00434       sContentAccessModifier =
00435         GetAccessModifierMaskFromPref(nsIDocShellTreeItem::typeContent);
00436 #if 0
00437     } else if (data.EqualsLiteral("mousewheel.withaltkey.action")) {
00438     } else if (data.EqualsLiteral("mousewheel.withaltkey.numlines")) {
00439     } else if (data.EqualsLiteral("mousewheel.withaltkey.sysnumlines")) {
00440     } else if (data.EqualsLiteral("mousewheel.withcontrolkey.action")) {
00441     } else if (data.EqualsLiteral("mousewheel.withcontrolkey.numlines")) {
00442     } else if (data.EqualsLiteral("mousewheel.withcontrolkey.sysnumlines")) {
00443     } else if (data.EqualsLiteral("mousewheel.withshiftkey.action")) {
00444     } else if (data.EqualsLiteral("mousewheel.withshiftkey.numlines")) {
00445     } else if (data.EqualsLiteral("mousewheel.withshiftkey.sysnumlines")) {
00446     } else if (data.EqualsLiteral("mousewheel.withnokey.action")) {
00447     } else if (data.EqualsLiteral("mousewheel.withnokey.numlines")) {
00448     } else if (data.EqualsLiteral("mousewheel.withnokey.sysnumlines")) {
00449 #endif
00450     } else if (data.EqualsLiteral("dom.popup_allowed_events")) {
00451       nsDOMEvent::PopupAllowedEventsChanged();
00452     }
00453   }
00454 
00455   return NS_OK;
00456 }
00457 
00458 
00459 NS_IMPL_ISUPPORTS3(nsEventStateManager, nsIEventStateManager, nsIObserver, nsISupportsWeakReference)
00460 
00461 inline void
00462 SetFrameExternalReference(nsIFrame* aFrame)
00463 {
00464   aFrame->AddStateBits(NS_FRAME_EXTERNAL_REFERENCE);
00465 }
00466 
00467 NS_IMETHODIMP
00468 nsEventStateManager::PreHandleEvent(nsPresContext* aPresContext,
00469                                     nsEvent *aEvent,
00470                                     nsIFrame* aTargetFrame,
00471                                     nsEventStatus* aStatus,
00472                                     nsIView* aView)
00473 {
00474   NS_ENSURE_ARG_POINTER(aStatus);
00475   NS_ENSURE_ARG(aPresContext);
00476 
00477   mCurrentTarget = aTargetFrame;
00478   mCurrentTargetContent = nsnull;
00479 
00480   // Focus events don't necessarily need a frame.
00481   if (NS_EVENT_NEEDS_FRAME(aEvent)) {
00482     NS_ASSERTION(mCurrentTarget, "mCurrentTarget is null.  this should not happen.  see bug #13007");
00483     if (!mCurrentTarget) return NS_ERROR_NULL_POINTER;
00484   }
00485 
00486   if (mCurrentTarget)
00487     SetFrameExternalReference(mCurrentTarget);
00488 
00489   *aStatus = nsEventStatus_eIgnore;
00490 
00491   if (!aEvent) {
00492     NS_ERROR("aEvent is null.  This should never happen.");
00493     return NS_ERROR_NULL_POINTER;
00494   }
00495 
00496   switch (aEvent->message) {
00497   case NS_MOUSE_LEFT_BUTTON_DOWN:
00498 #ifndef XP_OS2
00499     BeginTrackingDragGesture ( aPresContext, (nsMouseEvent*)aEvent, aTargetFrame );
00500 #endif
00501     mLClickCount = ((nsMouseEvent*)aEvent)->clickCount;
00502     SetClickCount(aPresContext, (nsMouseEvent*)aEvent, aStatus);
00503     mNormalLMouseEventInProcess = PR_TRUE;
00504     break;
00505   case NS_MOUSE_MIDDLE_BUTTON_DOWN:
00506     mMClickCount = ((nsMouseEvent*)aEvent)->clickCount;
00507     SetClickCount(aPresContext, (nsMouseEvent*)aEvent, aStatus);
00508     break;
00509   case NS_MOUSE_RIGHT_BUTTON_DOWN:
00510 #ifdef XP_OS2
00511     BeginTrackingDragGesture ( aPresContext, (nsMouseEvent*)aEvent, aTargetFrame );
00512 #endif
00513     mRClickCount = ((nsMouseEvent*)aEvent)->clickCount;
00514     SetClickCount(aPresContext, (nsMouseEvent*)aEvent, aStatus);
00515     break;
00516   case NS_MOUSE_LEFT_BUTTON_UP:
00517 #ifdef CLICK_HOLD_CONTEXT_MENUS
00518     KillClickHoldTimer();
00519 #endif
00520 #ifndef XP_OS2
00521     StopTrackingDragGesture();
00522 #endif
00523     mNormalLMouseEventInProcess = PR_FALSE;
00524   case NS_MOUSE_RIGHT_BUTTON_UP:
00525 #ifdef XP_OS2
00526     StopTrackingDragGesture();
00527 #endif
00528   case NS_MOUSE_MIDDLE_BUTTON_UP:
00529     SetClickCount(aPresContext, (nsMouseEvent*)aEvent, aStatus);
00530     break;
00531   case NS_MOUSE_EXIT:
00532     // If the event coordinate is within the bounds of the view,
00533     // and this is not the top-level window, then it's not really
00534     // an exit --- we may have traversed widget boundaries but
00535     // we're still in our toplevel window.
00536     // On the other hand, if we exit a toplevel window, then
00537     // it's really an exit even if the mouse is still in the
00538     // window bounds --- the mouse probably moved into some
00539     // "on top" window.
00540     {
00541       nsMouseEvent* mouseEvent = NS_STATIC_CAST(nsMouseEvent*, aEvent);
00542       nsCOMPtr<nsIWidget> parentWidget = getter_AddRefs(mouseEvent->widget->GetParent());
00543       if (parentWidget &&
00544         (aView->GetBounds() - aView->GetPosition()).Contains(aEvent->point)) {
00545         // treat it as a move so we don't generate spurious "exit"
00546         // events Any necessary exit events will be generated by
00547         // GenerateMouseEnterExit
00548         aEvent->message = NS_MOUSE_MOVE;
00549         // then fall through...
00550       } else {
00551         GenerateMouseEnterExit((nsGUIEvent*)aEvent);
00552         //This is a window level mouse exit event and should stop here
00553         aEvent->message = 0;
00554         break;
00555       }
00556     }
00557   case NS_MOUSE_MOVE:
00558     // on the Mac, GenerateDragGesture() may not return until the drag
00559     // has completed and so |aTargetFrame| may have been deleted (moving
00560     // a bookmark, for example).  If this is the case, however, we know
00561     // that ClearFrameRefs() has been called and it cleared out
00562     // |mCurrentTarget|. As a result, we should pass |mCurrentTarget|
00563     // into UpdateCursor().
00564     GenerateDragGesture(aPresContext, (nsMouseEvent*)aEvent);
00565     UpdateCursor(aPresContext, aEvent, mCurrentTarget, aStatus);
00566     GenerateMouseEnterExit((nsGUIEvent*)aEvent);
00567     break;
00568 #ifdef CLICK_HOLD_CONTEXT_MENUS
00569   case NS_DRAGDROP_GESTURE:
00570     // an external drag gesture event came in, not generated internally
00571     // by Gecko. Make sure we get rid of the click-hold timer.
00572     KillClickHoldTimer();
00573     break;
00574 #endif
00575   case NS_DRAGDROP_OVER:
00576     GenerateDragDropEnterExit(aPresContext, (nsGUIEvent*)aEvent);
00577     break;
00578   case NS_GOTFOCUS:
00579     {
00580       // This is called when a child widget has received focus.
00581       // We need to take care of sending a blur event for the previously
00582       // focused content and document, then dispatching a focus
00583       // event to the target content, its document, and its window.
00584 
00585       EnsureDocument(aPresContext);
00586 
00587       // If the document didn't change, then the only thing that could have
00588       // changed is the focused content node.  That's handled elsewhere
00589       // (SetContentState and SendFocusBlur).
00590 
00591       if (gLastFocusedDocument == mDocument)
00592         break;
00593 
00594       if (mDocument) {
00595         if (gLastFocusedDocument && gLastFocusedPresContext) {
00596           nsCOMPtr<nsPIDOMWindow> ourWindow =
00597             do_QueryInterface(GetDocumentOuterWindow(gLastFocusedDocument));
00598 
00599           // If the focus controller is already suppressed, it means that we
00600           // are in the middle of an activate sequence. In this case, we do
00601           // _not_ want to fire a blur on the previously focused content, since
00602           // we will be focusing it again later when we receive the NS_ACTIVATE
00603           // event.  See bug 120209.
00604 
00605           // Hold a strong ref to the focus controller, since we need
00606           // it after event dispatch.
00607           nsCOMPtr<nsIFocusController> focusController;
00608           PRBool isAlreadySuppressed = PR_FALSE;
00609 
00610           if (ourWindow) {
00611             focusController = ourWindow->GetRootFocusController();
00612             if (focusController) {
00613               focusController->GetSuppressFocus(&isAlreadySuppressed);
00614               focusController->SetSuppressFocus(PR_TRUE,
00615                                                 "NS_GOTFOCUS ESM Suppression");
00616             }
00617           }
00618 
00619           if (!isAlreadySuppressed) {
00620 
00621             // Fire the blur event on the previously focused document.
00622 
00623             nsEventStatus blurstatus = nsEventStatus_eIgnore;
00624             nsEvent blurevent(PR_TRUE, NS_BLUR_CONTENT);
00625 
00626             gLastFocusedDocument->HandleDOMEvent(gLastFocusedPresContext,
00627                                                  &blurevent,
00628                                                  nsnull, NS_EVENT_FLAG_INIT,
00629                                                  &blurstatus);
00630 
00631             if (!mCurrentFocus && gLastFocusedContent) {
00632               // We also need to blur the previously focused content node here,
00633               // if we don't have a focused content node in this document.
00634               // (SendFocusBlur isn't called in this case).
00635 
00636               nsCOMPtr<nsIContent> blurContent = gLastFocusedContent;
00637               gLastFocusedContent->HandleDOMEvent(gLastFocusedPresContext,
00638                                                   &blurevent, nsnull,
00639                                                   NS_EVENT_FLAG_INIT,
00640                                                   &blurstatus);
00641 
00642               // XXX bryner this isn't quite right -- it can result in
00643               // firing two blur events on the content.
00644 
00645               nsCOMPtr<nsIDocument> doc;
00646               if (gLastFocusedContent) // could have changed in HandleDOMEvent
00647                 doc = gLastFocusedContent->GetDocument();
00648               if (doc) {
00649                 nsIPresShell *shell = doc->GetShellAt(0);
00650                 if (shell) {
00651                   nsCOMPtr<nsPresContext> oldPresContext =
00652                     shell->GetPresContext();
00653 
00654                   nsCOMPtr<nsIEventStateManager> esm;
00655                   esm = oldPresContext->EventStateManager();
00656                   esm->SetFocusedContent(gLastFocusedContent);
00657                   gLastFocusedContent->HandleDOMEvent(oldPresContext,
00658                                                       &blurevent, nsnull,
00659                                                       NS_EVENT_FLAG_INIT,
00660                                                       &blurstatus);
00661                   esm->SetFocusedContent(nsnull);
00662                   NS_IF_RELEASE(gLastFocusedContent);
00663                 }
00664               }
00665             }
00666           }
00667 
00668           if (focusController)
00669             focusController->SetSuppressFocus(PR_FALSE,
00670                                               "NS_GOTFOCUS ESM Suppression");
00671         }
00672 
00673         // Now we should fire the focus event.  We fire it on the document,
00674         // then the content node, then the window.
00675 
00676         nsCOMPtr<nsIScriptGlobalObject> globalObject =
00677           GetDocumentOuterWindow(mDocument);
00678 
00679         if (globalObject) {
00680           // We don't want there to be a focused content node while we're
00681           // dispatching the focus event.
00682 
00683           nsCOMPtr<nsIContent> currentFocus = mCurrentFocus;
00684           // "leak" this reference, but we take it back later
00685           SetFocusedContent(nsnull);
00686 
00687           nsEventStatus status = nsEventStatus_eIgnore;
00688           nsEvent focusevent(PR_TRUE, NS_FOCUS_CONTENT);
00689 
00690           if (gLastFocusedDocument != mDocument) {
00691             mDocument->HandleDOMEvent(aPresContext, &focusevent, nsnull,
00692                                       NS_EVENT_FLAG_INIT, &status);
00693             if (currentFocus && currentFocus != gLastFocusedContent)
00694               currentFocus->HandleDOMEvent(aPresContext, &focusevent, nsnull,
00695                                            NS_EVENT_FLAG_INIT, &status);
00696           }
00697 
00698           globalObject->HandleDOMEvent(aPresContext, &focusevent, nsnull,
00699                                        NS_EVENT_FLAG_INIT, &status);
00700 
00701           SetFocusedContent(currentFocus); // we kept this reference above
00702           NS_IF_RELEASE(gLastFocusedContent);
00703           gLastFocusedContent = mCurrentFocus;
00704           NS_IF_ADDREF(gLastFocusedContent);
00705         }
00706 
00707         // Try to keep the focus controllers and the globals in synch
00708         if (gLastFocusedDocument && gLastFocusedDocument != mDocument) {
00709 
00710           nsIFocusController *lastController = nsnull;
00711           nsCOMPtr<nsPIDOMWindow> lastWindow =
00712             do_QueryInterface(GetDocumentOuterWindow(gLastFocusedDocument));
00713           if (lastWindow)
00714             lastController = lastWindow->GetRootFocusController();
00715 
00716           nsIFocusController *nextController = nsnull;
00717           nsCOMPtr<nsPIDOMWindow> nextWindow =
00718             do_QueryInterface(GetDocumentOuterWindow(mDocument));
00719           if (nextWindow)
00720             nextController = nextWindow->GetRootFocusController();
00721 
00722           if (lastController != nextController && lastController && nextController)
00723             lastController->SetActive(PR_FALSE);
00724         }
00725 
00726         NS_IF_RELEASE(gLastFocusedDocument);
00727         gLastFocusedDocument = mDocument;
00728         gLastFocusedPresContext = aPresContext;
00729         NS_IF_ADDREF(gLastFocusedDocument);
00730       }
00731 
00732       ResetBrowseWithCaret();
00733     }
00734 
00735     break;
00736 
00737   case NS_LOSTFOCUS:
00738     {
00739       // Hide the caret used in "browse with caret mode"
00740       if (mBrowseWithCaret && mPresContext) {
00741         nsIPresShell *presShell = mPresContext->GetPresShell();
00742         if (presShell)
00743            SetContentCaretVisible(presShell, mCurrentFocus, PR_FALSE);
00744       }
00745 
00746       // If focus is going to another mozilla window, we wait for the
00747       // focus event and fire a blur on the old focused content at that time.
00748       // This allows "-moz-user-focus: ignore" to work.
00749 
00750 #if defined(XP_WIN) || defined(XP_OS2)
00751       if (!NS_STATIC_CAST(nsFocusEvent*, aEvent)->isMozWindowTakingFocus) {
00752 
00753         // This situation occurs when focus goes to a non-gecko child window
00754         // in an embedding application.  In this case we do fire a blur
00755         // immediately.
00756 
00757         EnsureDocument(aPresContext);
00758 
00759         // We can get a deactivate on an Ender (editor) widget.  In this
00760         // case, we would like to obtain the DOM Window to start
00761         // with by looking at gLastFocusedContent.
00762         nsCOMPtr<nsIScriptGlobalObject> ourGlobal;
00763         if (gLastFocusedContent) {
00764           nsIDocument* doc = gLastFocusedContent->GetDocument();
00765 
00766           if (doc) {
00767             ourGlobal = GetDocumentOuterWindow(doc);
00768           } else {
00769             ourGlobal = GetDocumentOuterWindow(mDocument);
00770             NS_RELEASE(gLastFocusedContent);
00771           }
00772         }
00773         else {
00774           ourGlobal = GetDocumentOuterWindow(mDocument);
00775         }
00776 
00777         // Now fire blurs.  We fire a blur on the focused document, element,
00778         // and window.
00779 
00780         nsEventStatus status = nsEventStatus_eIgnore;
00781         nsEvent event(PR_TRUE, NS_BLUR_CONTENT);
00782 
00783         if (gLastFocusedDocument && gLastFocusedPresContext) {
00784           if (gLastFocusedContent) {
00785             // Retrieve this content node's pres context. it can be out of sync
00786             // in the Ender widget case.
00787             nsCOMPtr<nsIDocument> doc = gLastFocusedContent->GetDocument();
00788             if (doc) {
00789               nsIPresShell *shell = doc->GetShellAt(0);
00790               if (shell) {
00791                 nsCOMPtr<nsPresContext> oldPresContext =
00792                   shell->GetPresContext();
00793 
00794                 nsCOMPtr<nsIEventStateManager> esm =
00795                   oldPresContext->EventStateManager();
00796                 esm->SetFocusedContent(gLastFocusedContent);
00797                 gLastFocusedContent->HandleDOMEvent(oldPresContext, &event,
00798                                                     nsnull, NS_EVENT_FLAG_INIT,
00799                                                     &status);
00800                 esm->SetFocusedContent(nsnull);
00801                 NS_IF_RELEASE(gLastFocusedContent);
00802               }
00803             }
00804           }
00805 
00806           // fire blur on document and window
00807           if (gLastFocusedDocument) {
00808             // get the window here, in case the event causes
00809             // gLastFocusedDocument to change.
00810 
00811             nsCOMPtr<nsIScriptGlobalObject> globalObject =
00812               GetDocumentOuterWindow(gLastFocusedDocument);
00813 
00814             gLastFocusedDocument->HandleDOMEvent(gLastFocusedPresContext,
00815                                                  &event, nsnull,
00816                                                  NS_EVENT_FLAG_INIT, &status);
00817 
00818             if (globalObject)
00819               globalObject->HandleDOMEvent(gLastFocusedPresContext, &event,
00820                                            nsnull, NS_EVENT_FLAG_INIT,
00821                                            &status);
00822           }
00823 
00824           // Now clear our our global variables
00825           mCurrentTarget = nsnull;
00826           NS_IF_RELEASE(gLastFocusedDocument);
00827           gLastFocusedPresContext = nsnull;
00828         }
00829       }
00830 #endif
00831     }
00832     break;
00833 
00834  case NS_ACTIVATE:
00835     {
00836       // If we have a focus controller, and if it has a focused window and a
00837       // focused element in its focus memory, then restore the focus to those
00838       // objects.
00839 
00840       EnsureDocument(aPresContext);
00841 
00842       nsCOMPtr<nsPIDOMWindow> win =
00843         do_QueryInterface(GetDocumentOuterWindow(mDocument));
00844 
00845       if (!win) {
00846         NS_ERROR("win is null.  this happens [often on xlib builds].  see bug #79213");
00847         return NS_ERROR_NULL_POINTER;
00848       }
00849 
00850       // Hold a strong ref to the focus controller, since we need
00851       // it after event dispatch.
00852       nsCOMPtr<nsIFocusController> focusController =
00853         win->GetRootFocusController();
00854       nsCOMPtr<nsIDOMElement> focusedElement;
00855       nsCOMPtr<nsIDOMWindowInternal> focusedWindow;
00856 
00857       if (focusController) {
00858         // Obtain focus info from the focus controller.
00859         focusController->GetFocusedWindow(getter_AddRefs(focusedWindow));
00860         focusController->GetFocusedElement(getter_AddRefs(focusedElement));
00861 
00862         focusController->SetSuppressFocusScroll(PR_TRUE);
00863         focusController->SetActive(PR_TRUE);
00864       }
00865 
00866       if (!focusedWindow)
00867         focusedWindow = win;
00868 
00869       NS_WARN_IF_FALSE(focusedWindow,"check why focusedWindow is null!!!");
00870 
00871       // Focus the DOM window.
00872       if (focusedWindow) {
00873         focusedWindow->Focus();
00874 
00875         nsCOMPtr<nsIDocument> document = GetDocumentFromWindow(focusedWindow);
00876 
00877         if (document) {
00878           // Use a strong ref to make sure that the shell is alive still
00879           // when calling FrameSelection().
00880           nsCOMPtr<nsIPresShell> shell = document->GetShellAt(0);
00881           NS_ASSERTION(shell, "Focus events should not be getting thru when this is null!");
00882           if (shell) {
00883             if (focusedElement) {
00884               nsCOMPtr<nsIContent> focusContent = do_QueryInterface(focusedElement);
00885               nsCOMPtr<nsPresContext> context = shell->GetPresContext();
00886               focusContent->SetFocus(context);
00887             }
00888 
00889             // disable selection mousedown state on activation
00890             shell->FrameSelection()->SetMouseDownState(PR_FALSE);
00891           }
00892         }
00893       }
00894 
00895       if (focusController) {
00896         // Make sure the focus controller is up-to-date, since restoring
00897         // focus memory may have caused focus to go elsewhere.
00898 
00899         if (gLastFocusedDocument && gLastFocusedDocument == mDocument) {
00900           nsCOMPtr<nsIDOMElement> focusElement = do_QueryInterface(mCurrentFocus);
00901           focusController->SetFocusedElement(focusElement);
00902         }
00903 
00904         PRBool isSuppressed;
00905         focusController->GetSuppressFocus(&isSuppressed);
00906         while (isSuppressed) {
00907           // Unsuppress and let the focus controller listen again.
00908           focusController->SetSuppressFocus(PR_FALSE,
00909                                             "Activation Suppression");
00910 
00911           focusController->GetSuppressFocus(&isSuppressed);
00912         }
00913         focusController->SetSuppressFocusScroll(PR_FALSE);
00914       }
00915     }
00916     break;
00917 
00918  case NS_DEACTIVATE:
00919     {
00920       EnsureDocument(aPresContext);
00921 
00922       nsCOMPtr<nsIScriptGlobalObject> ourGlobal =
00923         GetDocumentOuterWindow(mDocument);
00924 
00925       // Suppress the focus controller for the duration of the
00926       // de-activation.  This will cause it to remember the last
00927       // focused sub-window and sub-element for this top-level
00928       // window.
00929 
00930       nsCOMPtr<nsIFocusController> focusController =
00931         GetFocusControllerForDocument(mDocument);
00932 
00933       if (focusController)
00934         focusController->SetSuppressFocus(PR_TRUE, "Deactivate Suppression");
00935 
00936       // Now fire blurs.  Blur the content, then the document, then the window.
00937 
00938       if (gLastFocusedDocument && gLastFocusedDocument == mDocument) {
00939 
00940         nsEventStatus status = nsEventStatus_eIgnore;
00941         nsEvent event(PR_TRUE, NS_BLUR_CONTENT);
00942 
00943         if (gLastFocusedContent) {
00944           nsIPresShell *shell = gLastFocusedDocument->GetShellAt(0);
00945           if (shell) {
00946             nsCOMPtr<nsPresContext> oldPresContext = shell->GetPresContext();
00947 
00948             nsCOMPtr<nsIDOMElement> focusedElement;
00949             if (focusController)
00950               focusController->GetFocusedElement(getter_AddRefs(focusedElement));
00951 
00952             nsCOMPtr<nsIEventStateManager> esm;
00953             esm = oldPresContext->EventStateManager();
00954             esm->SetFocusedContent(gLastFocusedContent);
00955 
00956             nsCOMPtr<nsIContent> focusedContent = do_QueryInterface(focusedElement);
00957             if (focusedContent) {
00958               // Blur the element.
00959               focusedContent->HandleDOMEvent(oldPresContext, &event, nsnull,
00960                                              NS_EVENT_FLAG_INIT, &status);
00961             }
00962 
00963             esm->SetFocusedContent(nsnull);
00964             NS_IF_RELEASE(gLastFocusedContent);
00965           }
00966         }
00967 
00968         // fire blur on document and window
00969         mDocument->HandleDOMEvent(aPresContext, &event, nsnull,
00970                                   NS_EVENT_FLAG_INIT, &status);
00971 
00972         if (ourGlobal)
00973           ourGlobal->HandleDOMEvent(aPresContext, &event, nsnull,
00974                                     NS_EVENT_FLAG_INIT, &status);
00975 
00976         // Now clear our our global variables
00977         mCurrentTarget = nsnull;
00978         NS_IF_RELEASE(gLastFocusedDocument);
00979         gLastFocusedPresContext = nsnull;
00980       }
00981 
00982       if (focusController) {
00983         focusController->SetActive(PR_FALSE);
00984         focusController->SetSuppressFocus(PR_FALSE, "Deactivate Suppression");
00985       }
00986     }
00987 
00988     break;
00989 
00990   case NS_KEY_PRESS:
00991     {
00992 
00993       nsKeyEvent* keyEvent = (nsKeyEvent*)aEvent;
00994 
00995       PRInt32 modifierMask = 0;
00996       if (keyEvent->isShift)
00997         modifierMask |= NS_MODIFIER_SHIFT;
00998       if (keyEvent->isControl)
00999         modifierMask |= NS_MODIFIER_CONTROL;
01000       if (keyEvent->isAlt)
01001         modifierMask |= NS_MODIFIER_ALT;
01002       if (keyEvent->isMeta)
01003         modifierMask |= NS_MODIFIER_META;
01004 
01005       // Prevent keyboard scrolling while an accesskey modifier is in use.
01006       if (modifierMask && (modifierMask == sChromeAccessModifier ||
01007                            modifierMask == sContentAccessModifier))
01008         HandleAccessKey(aPresContext, keyEvent, aStatus, -1,
01009                         eAccessKeyProcessingNormal, modifierMask);
01010     }
01011   case NS_KEY_DOWN:
01012   case NS_KEY_UP:
01013   case NS_MOUSE_SCROLL:
01014     {
01015       if (mCurrentFocus) {
01016         mCurrentTargetContent = mCurrentFocus;
01017       }
01018     }
01019     break;
01020   }
01021   return NS_OK;
01022 }
01023 
01024 static PRInt32
01025 GetAccessModifierMask(nsISupports* aDocShell)
01026 {
01027   nsCOMPtr<nsIDocShellTreeItem> treeItem(do_QueryInterface(aDocShell));
01028   if (!treeItem)
01029     return -1; // invalid modifier
01030 
01031   PRInt32 itemType;
01032   treeItem->GetItemType(&itemType);
01033   switch (itemType) {
01034 
01035   case nsIDocShellTreeItem::typeChrome:
01036     return sChromeAccessModifier;
01037 
01038   case nsIDocShellTreeItem::typeContent:
01039     return sContentAccessModifier;
01040 
01041   default:
01042     return -1; // invalid modifier
01043   }
01044 }
01045 
01046 // Note: for the in parameter aChildOffset,
01047 // -1 stands for not bubbling from the child docShell
01048 // 0 -- childCount - 1 stands for the child docShell's offset
01049 // which bubbles up the access key handling
01050 void
01051 nsEventStateManager::HandleAccessKey(nsPresContext* aPresContext,
01052                                      nsKeyEvent *aEvent,
01053                                      nsEventStatus* aStatus,
01054                                      PRInt32 aChildOffset,
01055                                      ProcessingAccessKeyState aAccessKeyState,
01056                                      PRInt32 aModifierMask)
01057 {
01058   nsCOMPtr<nsISupports> pcContainer = aPresContext->GetContainer();
01059   NS_ASSERTION(pcContainer, "no container for presContext");
01060   
01061   // Alt or other accesskey modifier is down, we may need to do an accesskey
01062   if (mAccessKeys && aModifierMask == GetAccessModifierMask(pcContainer)) {
01063     // Someone registered an accesskey.  Find and activate it.
01064     PRUint32 accKey = (IS_IN_BMP(aEvent->charCode)) ? 
01065       ToLowerCase((PRUnichar)aEvent->charCode) : aEvent->charCode;
01066 
01067     nsVoidKey key(NS_INT32_TO_PTR(accKey));
01068     if (mAccessKeys->Exists(&key)) {
01069       nsCOMPtr<nsIContent> content = dont_AddRef(NS_STATIC_CAST(nsIContent*, mAccessKeys->Get(&key)));
01070 
01071       // if it's a XUL element...
01072       if (content->IsContentOfType(nsIContent::eXUL)) {
01073         // find out what type of content node this is
01074         if (content->Tag() == nsXULAtoms::label) {
01075           // If anything fails, this will be null ...
01076           nsCOMPtr<nsIDOMElement> element;
01077 
01078           nsAutoString control;
01079           content->GetAttr(kNameSpaceID_None, nsXULAtoms::control, control);
01080           if (!control.IsEmpty()) {
01081             nsCOMPtr<nsIDOMDocument> domDocument =
01082               do_QueryInterface(content->GetDocument());
01083             if (domDocument)
01084               domDocument->GetElementById(control, getter_AddRefs(element));
01085           }
01086           // ... that here we'll either change |content| to the element
01087           // referenced by |element|, or clear it.
01088           content = do_QueryInterface(element);
01089         }
01090 
01091         if (!content)
01092           return;
01093 
01094         nsIFrame* frame = nsnull;
01095         aPresContext->PresShell()->GetPrimaryFrameFor(content, &frame);
01096 
01097         if (frame) {
01098           const nsStyleVisibility* vis = frame->GetStyleVisibility();
01099           PRBool viewShown = frame->AreAncestorViewsVisible();
01100 
01101           // get the XUL element
01102           nsCOMPtr<nsIDOMXULElement> element = do_QueryInterface(content);
01103 
01104           // if collapsed or hidden, we don't get tabbed into.
01105           if (viewShown &&
01106             vis->mVisible != NS_STYLE_VISIBILITY_COLLAPSE &&
01107             vis->mVisible != NS_STYLE_VISIBILITY_HIDDEN &&
01108             element) {
01109 
01110             // find out what type of content node this is
01111             nsIAtom *atom = content->Tag();
01112 
01113             // define behavior for each type of XUL element:
01114             if (atom == nsXULAtoms::textbox || atom == nsXULAtoms::menulist) {
01115               // if it's a text box or menulist, give it focus
01116               element->Focus();
01117             } else if (atom == nsXULAtoms::toolbarbutton) {
01118               // if it's a toolbar button, just click
01119               element->Click();
01120             } else {
01121               // otherwise, focus and click in it
01122               element->Focus();
01123               element->Click();
01124             }
01125           }
01126         }
01127       } else { // otherwise, it must be HTML
01128         // It's hard to say what HTML4 wants us to do in all cases.
01129         // So for now we'll settle for A) Set focus (except for <label>s
01130         // which focus their control in nsHTMLLabelElement::HandleDOMEvent)
01131         if (content->Tag() != nsHTMLAtoms::label || !sKeyCausesActivation) {
01132           ChangeFocusWith(content, eEventFocusedByKey);
01133         }
01134         if (sKeyCausesActivation) {
01135           // B) Click on it if the users prefs indicate to do so.
01136 
01137           // Propagate trusted state to the new event.
01138           nsEventStatus status = nsEventStatus_eIgnore;
01139           nsMouseEvent event(NS_IS_TRUSTED_EVENT(aEvent), NS_MOUSE_LEFT_CLICK,
01140                              nsnull, nsMouseEvent::eReal);
01141 
01142           nsAutoPopupStatePusher popupStatePusher(NS_IS_TRUSTED_EVENT(aEvent) ?
01143                                                   openAllowed : openAbused);
01144 
01145           nsCOMPtr<nsIContent> oldTargetContent = mCurrentTargetContent;
01146           mCurrentTargetContent = content;
01147           PRUint32 flags = NS_EVENT_FLAG_INIT;
01148           content->HandleDOMEvent(mPresContext, &event, nsnull, flags, &status);
01149           event.flags &= ~NS_EVENT_FLAG_STOP_DISPATCH;
01150           if (mCurrentTargetContent && mCurrentTargetContent == content) {
01151             flags |= NS_EVENT_FLAG_SYSTEM_EVENT;
01152             content->HandleDOMEvent(mPresContext, &event, nsnull, flags,
01153                                     &status);
01154           }
01155           mCurrentTargetContent = oldTargetContent;
01156         }
01157 
01158       }
01159 
01160       *aStatus = nsEventStatus_eConsumeNoDefault;
01161     }
01162   }
01163   // after the local accesskey handling
01164   if (nsEventStatus_eConsumeNoDefault != *aStatus) {
01165     // checking all sub docshells
01166 
01167     nsCOMPtr<nsIDocShellTreeNode> docShell(do_QueryInterface(pcContainer));
01168     if (!docShell) {
01169       NS_WARNING("no docShellTreeNode for presContext");
01170       return;
01171     }
01172 
01173     PRInt32 childCount;
01174     docShell->GetChildCount(&childCount);
01175     for (PRInt32 counter = 0; counter < childCount; counter++) {
01176       // Not processing the child which bubbles up the handling
01177       if (aAccessKeyState == eAccessKeyProcessingUp && counter == aChildOffset)
01178         continue;
01179 
01180       nsCOMPtr<nsIDocShellTreeItem> subShellItem;
01181       nsCOMPtr<nsIPresShell> subPS;
01182       nsCOMPtr<nsPresContext> subPC;
01183 
01184       docShell->GetChildAt(counter, getter_AddRefs(subShellItem));
01185       nsCOMPtr<nsIDocShell> subDS = do_QueryInterface(subShellItem);
01186       if (subDS && IsShellVisible(subDS)) {
01187         subDS->GetPresShell(getter_AddRefs(subPS));
01188 
01189         // Docshells need not have a presshell (eg. display:none
01190         // iframes, docshells in transition between documents, etc).
01191         if (!subPS) {
01192           // Oh, well.  Just move on to the next child
01193           continue;
01194         }
01195 
01196         nsPresContext *subPC = subPS->GetPresContext();
01197 
01198         nsEventStateManager* esm =
01199           NS_STATIC_CAST(nsEventStateManager *, subPC->EventStateManager());
01200 
01201         if (esm)
01202           esm->HandleAccessKey(subPC, aEvent, aStatus, -1,
01203                                eAccessKeyProcessingDown, aModifierMask);
01204 
01205         if (nsEventStatus_eConsumeNoDefault == *aStatus)
01206           break;
01207       }
01208     }
01209   }// if end . checking all sub docshell ends here.
01210 
01211   // bubble up the process to the parent docShell if necesary
01212   if (eAccessKeyProcessingDown != aAccessKeyState && nsEventStatus_eConsumeNoDefault != *aStatus) {
01213     nsCOMPtr<nsIDocShellTreeItem> docShell(do_QueryInterface(pcContainer));
01214     if (!docShell) {
01215       NS_WARNING("no docShellTreeNode for presContext");
01216       return;
01217     }
01218 
01219     nsCOMPtr<nsIDocShellTreeItem> parentShellItem;
01220     docShell->GetParent(getter_AddRefs(parentShellItem));
01221     nsCOMPtr<nsIDocShell> parentDS = do_QueryInterface(parentShellItem);
01222     if (parentDS) {
01223       PRInt32 myOffset;
01224       docShell->GetChildOffset(&myOffset);
01225 
01226       nsCOMPtr<nsIPresShell> parentPS;
01227 
01228       parentDS->GetPresShell(getter_AddRefs(parentPS));
01229       NS_ASSERTION(parentPS, "Our PresShell exists but the parent's does not?");
01230 
01231       nsPresContext *parentPC = parentPS->GetPresContext();
01232       NS_ASSERTION(parentPC, "PresShell without PresContext");
01233 
01234       nsEventStateManager* esm =
01235         NS_STATIC_CAST(nsEventStateManager *, parentPC->EventStateManager());
01236 
01237       if (esm)
01238         esm->HandleAccessKey(parentPC, aEvent, aStatus, myOffset,
01239                              eAccessKeyProcessingUp, aModifierMask);
01240     }
01241   }// if end. bubble up process
01242 }// end of HandleAccessKey
01243 
01244 
01245 #ifdef CLICK_HOLD_CONTEXT_MENUS
01246 
01247 
01248 //
01249 // CreateClickHoldTimer
01250 //
01251 // Fire off a timer for determining if the user wants click-hold. This timer
01252 // is a one-shot that will be cancelled when the user moves enough to fire
01253 // a drag.
01254 //
01255 void
01256 nsEventStateManager::CreateClickHoldTimer(nsPresContext* inPresContext,
01257                                           nsIFrame* inDownFrame,
01258                                           nsGUIEvent* inMouseDownEvent)
01259 {
01260   if (!NS_IS_TRUSTED_EVENT(inMouseDownEvent))
01261     return;
01262 
01263   // just to be anal (er, safe)
01264   if (mClickHoldTimer) {
01265     mClickHoldTimer->Cancel();
01266     mClickHoldTimer = nsnull;
01267   }
01268 
01269   // if content clicked on has a popup, don't even start the timer
01270   // since we'll end up conflicting and both will show.
01271   if (mGestureDownContent) {
01272     // check for the |popup| attribute
01273     nsAutoString popup;
01274     mGestureDownContent->GetAttr(kNameSpaceID_None, nsXULAtoms::popup, popup);
01275     if (!popup.IsEmpty())
01276       return;
01277     
01278     // check for a <menubutton> like bookmarks
01279     if (mGestureDownContent->Tag() == nsXULAtoms::menubutton)
01280       return;
01281   }
01282 
01283   mClickHoldTimer = do_CreateInstance("@mozilla.org/timer;1");
01284   if ( mClickHoldTimer )
01285     mClickHoldTimer->InitWithFuncCallback(sClickHoldCallback, this,
01286                                           kClickHoldDelay,
01287                                           nsITimer::TYPE_ONE_SHOT);
01288 } // CreateClickHoldTimer
01289 
01290 
01291 //
01292 // KillClickHoldTimer
01293 //
01294 // Stop the timer that would show the context menu dead in its tracks
01295 //
01296 void
01297 nsEventStateManager::KillClickHoldTimer()
01298 {
01299   if (mClickHoldTimer) {
01300     mClickHoldTimer->Cancel();
01301     mClickHoldTimer = nsnull;
01302   }
01303 }
01304 
01305 
01306 //
01307 // sClickHoldCallback
01308 //
01309 // This fires after the mouse has been down for a certain length of time.
01310 //
01311 void
01312 nsEventStateManager::sClickHoldCallback(nsITimer *aTimer, void* aESM)
01313 {
01314   nsEventStateManager* self = NS_STATIC_CAST(nsEventStateManager*, aESM);
01315   if ( self )
01316     self->FireContextClick();
01317 
01318   // NOTE: |aTimer| and |self->mAutoHideTimer| are invalid after calling ClosePopup();
01319 
01320 } // sAutoHideCallback
01321 
01322 
01323 //
01324 // FireContextClick
01325 //
01326 // If we're this far, our timer has fired, which means the mouse has been down
01327 // for a certain period of time and has not moved enough to generate a dragGesture.
01328 // We can be certain the user wants a context-click at this stage, so generate
01329 // a dom event and fire it in.
01330 //
01331 // After the event fires, check if PreventDefault() has been set on the event which
01332 // means that someone either ate the event or put up a context menu. This is our cue
01333 // to stop tracking the drag gesture. If we always did this, draggable items w/out
01334 // a context menu wouldn't be draggable after a certain length of time, which is
01335 // _not_ what we want.
01336 //
01337 void
01338 nsEventStateManager::FireContextClick()
01339 {
01340   if ( !mGestureDownContent )
01341     return;
01342 
01343 #if defined (XP_MAC) || defined(XP_MACOSX)
01344   // hacky OS call to ensure that we don't show a context menu when the user
01345   // let go of the mouse already, after a long, cpu-hogging operation prevented
01346   // us from handling any OS events. See bug 117589.
01347   if (!::StillDown())
01348     return;
01349 #endif
01350 
01351   nsEventStatus status = nsEventStatus_eIgnore;
01352 
01353   // Dispatch to the DOM. We have to fake out the ESM and tell it that the
01354   // current target frame is actually where the mouseDown occurred, otherwise it
01355   // will use the frame the mouse is currently over which may or may not be
01356   // the same. (Note: saari and I have decided that we don't have to reset |mCurrentTarget|
01357   // when we're through because no one else is doing anything more with this
01358   // event and it will get reset on the very next event to the correct frame).
01359   mCurrentTarget = nsnull;
01360   nsIPresShell *shell = mPresContext->GetPresShell();
01361   if ( shell ) {
01362     shell->GetPrimaryFrameFor(mGestureDownFrameOwner, &mCurrentTarget);
01363 
01364     if ( mCurrentTarget ) {
01365       SetFrameExternalReference(mCurrentTarget);
01366       
01367       NS_ASSERTION(mPresContext == mCurrentTarget->GetPresContext(),
01368                    "a prescontext returned a primary frame that didn't belong to it?");
01369 
01370       // before dispatching, check that we're not on something that
01371       // doesn't get a context menu
01372       nsIAtom *tag = mGestureDownContent->Tag();
01373       PRBool allowedToDispatch = PR_TRUE;
01374 
01375       if (mGestureDownContent->IsContentOfType(nsIContent::eXUL)) {
01376         if (tag == nsXULAtoms::scrollbar ||
01377             tag == nsXULAtoms::scrollbarbutton ||
01378             tag == nsXULAtoms::button)
01379           allowedToDispatch = PR_FALSE;
01380         else if (tag == nsXULAtoms::toolbarbutton) {
01381           // a <toolbarbutton> that has the container attribute set
01382           // will already have its own dropdown.
01383           nsAutoString container;
01384           mGestureDownContent->GetAttr(kNameSpaceID_None, nsXULAtoms::container,
01385                                container);
01386           if (!container.IsEmpty())
01387             allowedToDispatch = PR_FALSE;
01388 
01389           // If the toolbar button has an open menu, don't attempt to open
01390           // a second menu
01391           nsAutoString openAttr;
01392           mGestureDownContent->GetAttr(kNameSpaceID_None, nsXULAtoms::open,
01393                                        openAttr);
01394           if (openAttr.EqualsLiteral("true"))
01395             allowedToDispatch = PR_FALSE;
01396         }
01397       }
01398       else if (mGestureDownContent->IsContentOfType(nsIContent::eHTML)) {
01399         nsCOMPtr<nsIFormControl> formCtrl(do_QueryInterface(mGestureDownContent));
01400 
01401         if (formCtrl) {
01402           // of all form controls, only ones dealing with text are
01403           // allowed to have context menus
01404           PRInt32 type = formCtrl->GetType();
01405 
01406           allowedToDispatch = (type == NS_FORM_INPUT_TEXT ||
01407                                type == NS_FORM_INPUT_PASSWORD ||
01408                                type == NS_FORM_INPUT_FILE ||
01409                                type == NS_FORM_TEXTAREA);
01410         }
01411         else if (tag == nsHTMLAtoms::applet ||
01412                  tag == nsHTMLAtoms::embed  ||
01413                  tag == nsHTMLAtoms::object) {
01414           allowedToDispatch = PR_FALSE;
01415         }
01416       }
01417 
01418       if (allowedToDispatch) {
01419         // make sure the widget sticks around
01420         nsCOMPtr<nsIWidget> targetWidget(mCurrentTarget->GetWindow());
01421         // init the event while mCurrentTarget is still good
01422         nsMouseEvent event(PR_TRUE, NS_CONTEXTMENU,
01423                            targetWidget,
01424                            nsMouseEvent::eReal);
01425         event.clickCount = 1;
01426         FillInEventFromGestureDown(&event);
01427         
01428         // stop selection tracking, we're in control now
01429         nsCOMPtr<nsIFrameSelection> frameSel;
01430         GetSelection(mCurrentTarget, mPresContext, getter_AddRefs(frameSel));
01431         if (frameSel) {
01432           PRBool mouseDownState = PR_TRUE;
01433           frameSel->GetMouseDownState(&mouseDownState);
01434           if (mouseDownState) {
01435             // note that this can cause selection changed events to fire if we're in
01436             // a text field, which will null out mCurrentTarget
01437             frameSel->SetMouseDownState(PR_FALSE);
01438           }
01439         }
01440 
01441         // dispatch to DOM
01442         mGestureDownContent->HandleDOMEvent(mPresContext, &event, nsnull,
01443                                             NS_EVENT_FLAG_INIT, &status);
01444 
01445         // We don't need to dispatch to frame handling because no frames
01446         // watch NS_CONTEXTMENU except for nsMenuFrame and that's only for
01447         // dismissal. That's just as well since we don't really know
01448         // which frame to send it to.
01449       }
01450     }
01451   }
01452 
01453   // now check if the event has been handled. If so, stop tracking a drag
01454   if ( status == nsEventStatus_eConsumeNoDefault ) {
01455     StopTrackingDragGesture();
01456   }
01457 
01458   KillClickHoldTimer();
01459 
01460 } // FireContextClick
01461 
01462 #endif
01463 
01464 
01465 //
01466 // BeginTrackingDragGesture
01467 //
01468 // Record that the mouse has gone down and that we should move to TRACKING state
01469 // of d&d gesture tracker.
01470 //
01471 // We also use this to track click-hold context menus on mac. When the mouse goes down,
01472 // fire off a short timer. If the timer goes off and we have yet to fire the
01473 // drag gesture (ie, the mouse hasn't moved a certain distance), then we can
01474 // assume the user wants a click-hold, so fire a context-click event. We only
01475 // want to cancel the drag gesture if the context-click event is handled.
01476 //
01477 void
01478 nsEventStateManager::BeginTrackingDragGesture(nsPresContext* aPresContext,
01479                                               nsMouseEvent* inDownEvent,
01480                                               nsIFrame* inDownFrame)
01481 {
01482   // Note that |inDownEvent| could be either a mouse down event or a
01483   // synthesized mouse move event.
01484   nsRect screenPt;
01485   inDownEvent->widget->WidgetToScreen(nsRect(inDownEvent->refPoint, nsSize(1, 1)),
01486                                       screenPt);
01487   mGestureDownPoint = screenPt.TopLeft();
01488 
01489   inDownFrame->GetContentForEvent(aPresContext, inDownEvent,
01490                                   getter_AddRefs(mGestureDownContent));
01491 
01492   mGestureDownFrameOwner = inDownFrame->GetContent();
01493   mGestureDownShift = inDownEvent->isShift;
01494   mGestureDownControl = inDownEvent->isControl;
01495   mGestureDownAlt = inDownEvent->isAlt;
01496   mGestureDownMeta = inDownEvent->isMeta;
01497 
01498 #ifdef CLICK_HOLD_CONTEXT_MENUS
01499   // fire off a timer to track click-hold
01500   if (nsContentUtils::GetBoolPref("ui.click_hold_context_menus", PR_TRUE))
01501     CreateClickHoldTimer ( aPresContext, inDownFrame, inDownEvent );
01502 #endif
01503 }
01504 
01505 
01506 //
01507 // StopTrackingDragGesture
01508 //
01509 // Record that the mouse has gone back up so that we should leave the TRACKING
01510 // state of d&d gesture tracker and return to the START state.
01511 //
01512 void
01513 nsEventStateManager::StopTrackingDragGesture()
01514 {
01515   mGestureDownContent = nsnull;
01516   mGestureDownFrameOwner = nsnull;
01517 }
01518 
01519 
01520 //
01521 // GetSelection
01522 //
01523 // Helper routine to get an nsIFrameSelection from the given frame
01524 //
01525 void
01526 nsEventStateManager::GetSelection(nsIFrame* inFrame,
01527                                   nsPresContext* inPresContext,
01528                                   nsIFrameSelection** outSelection)
01529 {
01530   *outSelection = nsnull;
01531 
01532   if (inFrame) {
01533     nsCOMPtr<nsISelectionController> selCon;
01534     nsresult rv = inFrame->GetSelectionController(inPresContext, getter_AddRefs(selCon));
01535 
01536     if (NS_SUCCEEDED(rv) && selCon) {
01537       nsCOMPtr<nsIFrameSelection> frameSel;
01538 
01539       frameSel = do_QueryInterface(selCon);
01540 
01541       if (! frameSel) {
01542         nsIPresShell *shell = inPresContext->GetPresShell();
01543         if (shell)
01544           frameSel = shell->FrameSelection();
01545       }
01546 
01547       *outSelection = frameSel.get();
01548       NS_IF_ADDREF(*outSelection);
01549     }
01550   }
01551 
01552 } // GetSelection
01553 
01554 void
01555 nsEventStateManager::FillInEventFromGestureDown(nsMouseEvent* aEvent)
01556 {
01557   NS_ASSERTION(aEvent->widget == mCurrentTarget->GetWindow(),
01558                "Incorrect widget in event");
01559 
01560   // Set the coordinates in the new event to the coordinates of
01561   // the old event, adjusted for the fact that the widget might be
01562   // different
01563   nsRect tmpRect(0, 0, 1, 1);
01564   aEvent->widget->WidgetToScreen(tmpRect, tmpRect);
01565   aEvent->refPoint = mGestureDownPoint - tmpRect.TopLeft();
01566   
01567   float pixelsToTwips;
01568   pixelsToTwips = mPresContext->DeviceContext()->DevUnitsToTwips();
01569   nsPoint refPointTwips(NSIntPixelsToTwips(aEvent->refPoint.x, pixelsToTwips),
01570                         NSIntPixelsToTwips(aEvent->refPoint.y, pixelsToTwips));
01571 
01572   nsIView* widgetView = mCurrentTarget->GetClosestView();
01573   nsPoint widgetToView;
01574 #ifdef DEBUG
01575   nsIWidget* theWidget =
01576 #endif
01577   widgetView->GetNearestWidget(&widgetToView);
01578   NS_ASSERTION(theWidget == aEvent->widget, "Widget confusion!");
01579   nsPoint widgetViewPoint = refPointTwips + widgetToView;
01580 
01581   nsPoint targetToView;
01582   nsIView* view;
01583   mCurrentTarget->GetOffsetFromView(targetToView, &view);
01584 
01585   aEvent->point = widgetViewPoint + widgetView->GetOffsetTo(view);
01586 
01587   aEvent->isShift = mGestureDownShift;
01588   aEvent->isControl = mGestureDownControl;
01589   aEvent->isAlt = mGestureDownAlt;
01590   aEvent->isMeta = mGestureDownMeta;
01591 }
01592 
01593 //
01594 // GenerateDragGesture
01595 //
01596 // If we're in the TRACKING state of the d&d gesture tracker, check the current position
01597 // of the mouse in relation to the old one. If we've moved a sufficient amount from
01598 // the mouse down, then fire off a drag gesture event.
01599 //
01600 // Note that when the mouse enters a new child window with its own view, the event's
01601 // coordinates will be in relation to the origin of the inner child window, which could
01602 // either be very different from that of the mouse coords of the mouse down and trigger
01603 // a drag too early, or very similar which might not trigger a drag.
01604 //
01605 // Do we need to do anything about this? Let's wait and see.
01606 //
01607 void
01608 nsEventStateManager::GenerateDragGesture(nsPresContext* aPresContext,
01609                                          nsMouseEvent *aEvent)
01610 {
01611   NS_WARN_IF_FALSE(aPresContext, "This shouldn't happen.");
01612   if ( IsTrackingDragGesture() ) {
01613     aPresContext->GetPresShell()->GetPrimaryFrameFor(mGestureDownFrameOwner,
01614                                                      &mCurrentTarget);
01615     if (!mCurrentTarget) {
01616       StopTrackingDragGesture();
01617       return;
01618     }
01619 
01620     SetFrameExternalReference(mCurrentTarget);
01621 
01622     // Check if selection is tracking drag gestures, if so
01623     // don't interfere!
01624     nsCOMPtr<nsIFrameSelection> frameSel;
01625     GetSelection ( mCurrentTarget, aPresContext, getter_AddRefs(frameSel) );
01626     if ( frameSel ) {
01627       PRBool mouseDownState = PR_TRUE;
01628       frameSel->GetMouseDownState(&mouseDownState);
01629       if (mouseDownState) {
01630         StopTrackingDragGesture();
01631         return;
01632       }
01633     }
01634 
01635     static PRInt32 pixelThresholdX = 0;
01636     static PRInt32 pixelThresholdY = 0;
01637 
01638     if (!pixelThresholdX) {
01639       nsILookAndFeel *lf = aPresContext->LookAndFeel();
01640       lf->GetMetric(nsILookAndFeel::eMetric_DragThresholdX, pixelThresholdX);
01641       lf->GetMetric(nsILookAndFeel::eMetric_DragThresholdY, pixelThresholdY);
01642       if (!pixelThresholdX)
01643         pixelThresholdX = 5;
01644       if (!pixelThresholdY)
01645         pixelThresholdY = 5;
01646     }
01647 
01648     // fire drag gesture if mouse has moved enough
01649     nsRect tmpRect;
01650     aEvent->widget->WidgetToScreen(nsRect(aEvent->refPoint, nsSize(1, 1)),
01651                                    tmpRect);
01652     nsPoint pt = tmpRect.TopLeft();
01653     if (PR_ABS(pt.x - mGestureDownPoint.x) > pixelThresholdX ||
01654         PR_ABS(pt.y - mGestureDownPoint.y) > pixelThresholdY) {
01655 #ifdef CLICK_HOLD_CONTEXT_MENUS
01656       // stop the click-hold before we fire off the drag gesture, in case
01657       // it takes a long time
01658       KillClickHoldTimer();
01659 #endif
01660 
01661       nsCOMPtr<nsIContent> targetContent = mGestureDownContent;
01662       // Stop tracking the drag gesture now. This should stop us from
01663       // reentering GenerateDragGesture inside DOM event processing.
01664       StopTrackingDragGesture();
01665 
01666       // get the widget from the target frame
01667       nsEventStatus status = nsEventStatus_eIgnore;
01668       nsMouseEvent event(NS_IS_TRUSTED_EVENT(aEvent), NS_DRAGDROP_GESTURE,
01669                          mCurrentTarget->GetWindow(), nsMouseEvent::eReal);
01670       FillInEventFromGestureDown(&event);
01671 
01672       // Dispatch to the DOM. By setting mCurrentTarget we are faking
01673       // out the ESM and telling it that the current target frame is
01674       // actually where the mouseDown occurred, otherwise it will use
01675       // the frame the mouse is currently over which may or may not be
01676       // the same. (Note: saari and I have decided that we don't have
01677       // to reset |mCurrentTarget| when we're through because no one
01678       // else is doing anything more with this event and it will get
01679       // reset on the very next event to the correct frame).
01680 
01681       // Hold onto old target content through the event and reset after.
01682       nsCOMPtr<nsIContent> targetBeforeEvent = mCurrentTargetContent;
01683 
01684       // Set the current target to the content for the mouse down
01685       mCurrentTargetContent = targetContent;
01686 
01687       // Dispatch to DOM
01688       targetContent->HandleDOMEvent(aPresContext, &event, nsnull,
01689                                     NS_EVENT_FLAG_INIT, &status);
01690 
01691       // Note that frame event handling doesn't care about NS_DRAGDROP_GESTURE,
01692       // which is just as well since we don't really know which frame to
01693       // send it to
01694 
01695       // Reset mCurretTargetContent to what it was
01696       mCurrentTargetContent = targetBeforeEvent;
01697     }
01698 
01699     // Now flush all pending notifications, for better responsiveness
01700     // while dragging.
01701     FlushPendingEvents(aPresContext);
01702   }
01703 } // GenerateDragGesture
01704 
01705 nsresult
01706 nsEventStateManager::ChangeTextSize(PRInt32 change)
01707 {
01708   if(!gLastFocusedDocument) return NS_ERROR_FAILURE;
01709 
01710   nsCOMPtr<nsPIDOMWindow> ourWindow =
01711     do_QueryInterface(GetDocumentOuterWindow(gLastFocusedDocument));
01712   if(!ourWindow) return NS_ERROR_FAILURE;
01713 
01714   nsIDOMWindowInternal *rootWindow = ourWindow->GetPrivateRoot();
01715   if(!rootWindow) return NS_ERROR_FAILURE;
01716 
01717   nsCOMPtr<nsIDOMWindow> contentWindow;
01718   rootWindow->GetContent(getter_AddRefs(contentWindow));
01719   if(!contentWindow) return NS_ERROR_FAILURE;
01720 
01721   nsIDocument *doc = GetDocumentFromWindow(contentWindow);
01722   if(!doc) return NS_ERROR_FAILURE;
01723 
01724   nsIPresShell *presShell = doc->GetShellAt(0);
01725   if(!presShell) return NS_ERROR_FAILURE;
01726   nsPresContext *presContext = presShell->GetPresContext();
01727   if(!presContext) return NS_ERROR_FAILURE;
01728 
01729   nsCOMPtr<nsISupports> pcContainer = presContext->GetContainer();
01730   if(!pcContainer) return NS_ERROR_FAILURE;
01731 
01732   nsCOMPtr<nsIDocShell> docshell(do_QueryInterface(pcContainer));
01733   if(!docshell) return NS_ERROR_FAILURE;
01734 
01735   nsCOMPtr<nsIContentViewer> cv;
01736   docshell->GetContentViewer(getter_AddRefs(cv));
01737   if(!cv) return NS_ERROR_FAILURE;
01738 
01739   nsCOMPtr<nsIMarkupDocumentViewer> mv(do_QueryInterface(cv));
01740   if(!mv) return NS_ERROR_FAILURE;
01741 
01742   float textzoom;
01743   mv->GetTextZoom(&textzoom);
01744   textzoom += ((float)change) / 10;
01745   if (textzoom > 0 && textzoom <= 20)
01746     mv->SetTextZoom(textzoom);
01747 
01748   return NS_OK;
01749 }
01750 
01751 void
01752 nsEventStateManager::DoScrollHistory(PRInt32 direction)
01753 {
01754   nsCOMPtr<nsISupports> pcContainer(mPresContext->GetContainer());
01755   if (pcContainer) {
01756     nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(pcContainer));
01757     if (webNav) {
01758       // positive direction to go back one step, nonpositive to go forward
01759       if (direction > 0)
01760         webNav->GoBack();
01761       else
01762         webNav->GoForward();
01763     }
01764   }
01765 }
01766 
01767 void
01768 nsEventStateManager::DoScrollTextsize(nsIFrame *aTargetFrame,
01769                                       PRInt32 adjustment)
01770 {
01771   // Exclude form controls and XUL content.
01772   nsIContent *content = aTargetFrame->GetContent();
01773   if (content &&
01774       !content->IsContentOfType(nsIContent::eHTML_FORM_CONTROL) &&
01775       !content->IsContentOfType(nsIContent::eXUL))
01776     {
01777       // positive adjustment to increase text size, non-positive to decrease
01778       ChangeTextSize((adjustment > 0) ? 1 : -1);
01779     }
01780 }
01781 
01782 inline PRBool
01783 ShouldScrollRootView(nsPresContext* aPresContext)
01784 {
01785   return (aPresContext->Type() == nsPresContext::eContext_PrintPreview);
01786 }
01787 
01788 nsresult
01789 nsEventStateManager::DoScrollText(nsPresContext* aPresContext,
01790                                   nsIFrame* aTargetFrame,
01791                                   nsInputEvent* aEvent,
01792                                   PRInt32 aNumLines,
01793                                   PRBool aScrollHorizontal,
01794                                   ScrollQuantity aScrollQuantity)
01795 {
01796   nsIScrollableView* scrollView = nsnull;
01797   PRBool scrollRootView = ShouldScrollRootView(aPresContext);
01798 
01799   if (scrollRootView) {
01800     // Get root scroll view
01801     nsIViewManager* vm = aPresContext->GetViewManager();
01802     NS_ENSURE_TRUE(vm, NS_ERROR_FAILURE);
01803     vm->GetRootScrollableView(&scrollView);
01804     if (!scrollView) {
01805       // We don't have root scrollable view in current document.
01806       // Maybe, this is sub frame on Print Preview.
01807       // Let's pass to parent document.
01808       nsIFrame* newFrame = nsnull;
01809       nsCOMPtr<nsPresContext> newPresContext = nsnull;
01810       nsresult rv = GetParentScrollingView(aEvent, aPresContext, newFrame,
01811                                            *getter_AddRefs(newPresContext));
01812       NS_ENSURE_SUCCESS(rv, rv);
01813       NS_ENSURE_TRUE(newFrame && newPresContext, NS_ERROR_FAILURE);
01814       return DoScrollText(newPresContext, newFrame, aEvent, aNumLines,
01815                           aScrollHorizontal, aScrollQuantity);
01816     }
01817     // find target frame that is root scrollable content
01818     nsIFrame* targetFrame = aTargetFrame;
01819     for ( ;targetFrame; targetFrame = targetFrame->GetParent()) {
01820       nsCOMPtr<nsIScrollableViewProvider> svp = do_QueryInterface(targetFrame);
01821       if (svp && scrollView == svp->GetScrollableView())
01822         break;
01823     }
01824     NS_ENSURE_TRUE(targetFrame, NS_ERROR_FAILURE);
01825     aTargetFrame = targetFrame;
01826   }
01827 
01828   nsCOMPtr<nsIContent> targetContent = aTargetFrame->GetContent();
01829   if (!targetContent)
01830     GetFocusedContent(getter_AddRefs(targetContent));
01831   if (!targetContent) return NS_OK;
01832   nsCOMPtr<nsIDOMDocumentEvent> targetDOMDoc(
01833                     do_QueryInterface(targetContent->GetDocument()));
01834   if (!targetDOMDoc) return NS_OK;
01835 
01836   nsCOMPtr<nsIDOMEvent> event;
01837   targetDOMDoc->CreateEvent(NS_LITERAL_STRING("MouseScrollEvents"), getter_AddRefs(event));
01838   if (event) {
01839     nsCOMPtr<nsIDOMMouseEvent> mouseEvent(do_QueryInterface(event));
01840     nsCOMPtr<nsIDOMDocumentView> docView = do_QueryInterface(targetDOMDoc);
01841     if (!docView) return NS_ERROR_FAILURE;
01842     nsCOMPtr<nsIDOMAbstractView> view;
01843     docView->GetDefaultView(getter_AddRefs(view));
01844 
01845     if (aScrollQuantity == eScrollByPage) {
01846       if (aNumLines > 0) {
01847         aNumLines = nsIDOMNSUIEvent::SCROLL_PAGE_DOWN;
01848       } else {
01849         aNumLines = nsIDOMNSUIEvent::SCROLL_PAGE_UP;
01850       }
01851     }
01852 
01853     mouseEvent->InitMouseEvent(NS_LITERAL_STRING("DOMMouseScroll"),
01854                                PR_TRUE, PR_TRUE,
01855                                view, aNumLines,
01856                                aEvent->refPoint.x, aEvent->refPoint.y,
01857                                aEvent->point.x,    aEvent->point.y,
01858                                aEvent->isControl,  aEvent->isAlt,
01859                                aEvent->isShift,    aEvent->isMeta,
01860                                0, nsnull);
01861 
01862     nsCOMPtr<nsIPrivateDOMEvent> privateEvent(do_QueryInterface(mouseEvent));
01863 
01864     if (privateEvent && NS_IS_TRUSTED_EVENT(aEvent)) {
01865       privateEvent->SetTrusted(PR_TRUE);
01866     }
01867 
01868     nsCOMPtr<nsIDOMEventTarget> target(do_QueryInterface(targetContent));
01869     if (target) {
01870       PRBool defaultActionEnabled;
01871       target->DispatchEvent(event, &defaultActionEnabled);
01872       if (!defaultActionEnabled)
01873         return NS_OK;
01874       if (!scrollRootView) {
01875         // Re-resolve |aTargetFrame| in case it was destroyed by the
01876         // DOM event handler above, bug 257998.
01877         // But only if PresShell is still alive, bug 336587.
01878         nsIPresShell* shell = aPresContext->GetPresShell();
01879         aTargetFrame = nsnull;
01880         if (shell) {
01881           shell->GetPrimaryFrameFor(targetContent, &aTargetFrame);
01882         }
01883         if (!aTargetFrame) {
01884           // Without a frame we can't do the normal ancestor search for a view
01885           // to scroll. Don't fall through to the "passToParent" code at the end
01886           // because that will likely scroll the wrong view (in an enclosing
01887           // document).
01888           return NS_OK;
01889         }
01890       }
01891     }
01892   }
01893 
01894   nsIFrame* scrollFrame = aTargetFrame;
01895   PRBool passToParent = !scrollRootView;
01896 
01897   for( ; scrollFrame && passToParent; scrollFrame = scrollFrame->GetParent()) {
01898     // Check whether the frame wants to provide us with a scrollable view.
01899     scrollView = nsnull;
01900     nsCOMPtr<nsIScrollableViewProvider> svp = do_QueryInterface(scrollFrame);
01901     if (svp) {
01902       scrollView = svp->GetScrollableView();
01903     }
01904     if (!scrollView) {
01905       continue;
01906     }
01907 
01908     nsPresContext::ScrollbarStyles ss =
01909       nsLayoutUtils::ScrollbarStylesOfView(scrollView);
01910     if (NS_STYLE_OVERFLOW_HIDDEN ==
01911         (aScrollHorizontal ? ss.mHorizontal : ss.mVertical)) {
01912       continue;
01913     }
01914     
01915     // Check if the scrollable view can be scrolled any further.
01916     nscoord lineHeight;
01917     scrollView->GetLineHeight(&lineHeight);
01918 
01919     if (lineHeight != 0) {
01920       PRBool canScroll;
01921       nsresult rv = scrollView->CanScroll(aScrollHorizontal,
01922                                           (aNumLines > 0), canScroll);
01923       if (NS_SUCCEEDED(rv))
01924         passToParent = !canScroll;
01925 
01926       // Comboboxes need special care.
01927       nsIComboboxControlFrame* comboBox = nsnull;
01928       CallQueryInterface(scrollFrame, &comboBox);
01929       if (comboBox) {
01930         PRBool isDroppedDown = PR_FALSE;
01931         comboBox->IsDroppedDown(&isDroppedDown);
01932         if (isDroppedDown) {
01933           // Don't propagate to parent when drop down menu is active.
01934           if (passToParent) {
01935             passToParent = PR_FALSE;
01936             scrollView = nsnull;
01937           }
01938         } else {
01939           // Always propagate when not dropped down (even if focused).
01940           passToParent = PR_TRUE;
01941         }
01942       }
01943     }
01944   }
01945 
01946   if (!passToParent && scrollView) {
01947     PRInt32 scrollX = 0;
01948     PRInt32 scrollY = aNumLines;
01949 
01950     if (aScrollQuantity == eScrollByPage)
01951       scrollY = (scrollY > 0) ? 1 : -1;
01952       
01953     if (aScrollHorizontal) {
01954       scrollX = scrollY;
01955       scrollY = 0;
01956     }
01957     
01958     if (aScrollQuantity == eScrollByPage)
01959       scrollView->ScrollByPages(scrollX, scrollY);
01960     else if (aScrollQuantity == eScrollByPixel) {
01961       // CallQueryInterface doesn't work beacuse nsIScrollableView does not
01962       // contain QueryInterface.  Casting is fine, because nsScrollPortView
01963       // is the only class that implements the interface, and it's been
01964       // updated to implement nsIScrollableView_MOZILLA_1_8_BRANCH.
01965       nsIScrollableView_MOZILLA_1_8_BRANCH* scrollView_MOZILLA_1_8_BRANCH =
01966        NS_STATIC_CAST(nsIScrollableView_MOZILLA_1_8_BRANCH*, scrollView);
01967       scrollView_MOZILLA_1_8_BRANCH->ScrollByPixels(scrollX, scrollY);
01968     }
01969     else
01970       scrollView->ScrollByLines(scrollX, scrollY);
01971 
01972     ForceViewUpdate(scrollView->View());
01973   }
01974   if (passToParent) {
01975     nsresult rv;
01976     nsIFrame* newFrame = nsnull;
01977     nsCOMPtr<nsPresContext> newPresContext;
01978 
01979     rv = GetParentScrollingView(aEvent, aPresContext, newFrame,
01980                                 *getter_AddRefs(newPresContext));
01981     if (NS_SUCCEEDED(rv) && newFrame)
01982       return DoScrollText(newPresContext, newFrame, aEvent, aNumLines,
01983                           aScrollHorizontal, aScrollQuantity);
01984     else
01985       return NS_ERROR_FAILURE;
01986   }
01987 
01988   return NS_OK;
01989 }
01990 
01991 nsresult
01992 nsEventStateManager::GetParentScrollingView(nsInputEvent *aEvent,
01993                                             nsPresContext* aPresContext,
01994                                             nsIFrame* &targetOuterFrame,
01995                                             nsPresContext* &presCtxOuter)
01996 {
01997   targetOuterFrame = nsnull;
01998 
01999   if (!aEvent) return NS_ERROR_FAILURE;
02000   if (!aPresContext) return NS_ERROR_FAILURE;
02001 
02002   nsIDocument *doc = aPresContext->PresShell()->GetDocument();
02003   NS_ASSERTION(doc, "No document in prescontext!");
02004 
02005   nsIDocument *parentDoc = doc->GetParentDocument();
02006 
02007   if (!parentDoc) {
02008     return NS_OK;
02009   }
02010 
02011   nsIPresShell *pPresShell = nsnull;
02012   for (PRUint32 i = 0; i < parentDoc->GetNumberOfShells(); i++) {
02013     nsIPresShell *tmpPresShell = parentDoc->GetShellAt(i);
02014     NS_ENSURE_TRUE(tmpPresShell, NS_ERROR_FAILURE);
02015     NS_ENSURE_TRUE(tmpPresShell->GetPresContext(), NS_ERROR_FAILURE);
02016     if (tmpPresShell->GetPresContext()->Type() == aPresContext->Type()) {
02017       pPresShell = tmpPresShell;
02018       break;
02019     }
02020   }
02021   NS_ENSURE_TRUE(pPresShell, NS_ERROR_FAILURE);
02022 
02023   /* now find the content node in our parent docshell's document that
02024      corresponds to our docshell */
02025 
02026   nsIContent *frameContent = parentDoc->FindContentForSubDocument(doc);
02027   NS_ENSURE_TRUE(frameContent, NS_ERROR_FAILURE);
02028 
02029   /*
02030     get this content node's frame, and use it as the new event target,
02031     so the event can be processed in the parent docshell.
02032     Note that we don't actually need to translate the event coordinates
02033     because they are not used by DoScrollText().
02034   */
02035 
02036   nsIFrame* frameFrame = nsnull;
02037   pPresShell->GetPrimaryFrameFor(frameContent, &frameFrame);
02038   if (!frameFrame) return NS_ERROR_FAILURE;
02039 
02040   NS_IF_ADDREF(presCtxOuter = pPresShell->GetPresContext());
02041   targetOuterFrame = frameFrame;
02042 
02043   return NS_OK;
02044 }
02045 
02046 NS_IMETHODIMP
02047 nsEventStateManager::PostHandleEvent(nsPresContext* aPresContext,
02048                                      nsEvent *aEvent,
02049                                      nsIFrame* aTargetFrame,
02050                                      nsEventStatus* aStatus,
02051                                      nsIView* aView)
02052 {
02053   NS_ENSURE_ARG(aPresContext);
02054   NS_ENSURE_ARG_POINTER(aStatus);
02055   mCurrentTarget = aTargetFrame;
02056   mCurrentTargetContent = nsnull;
02057   nsresult ret = NS_OK;
02058   //Keep the prescontext alive, we might need it after event dispatch
02059   nsRefPtr<nsPresContext> presContext = aPresContext;
02060 
02061   NS_ASSERTION(mCurrentTarget, "mCurrentTarget is null");
02062   if (!mCurrentTarget) return NS_ERROR_NULL_POINTER;
02063 
02064   SetFrameExternalReference(mCurrentTarget);
02065 
02066   switch (aEvent->message) {
02067   case NS_MOUSE_LEFT_BUTTON_DOWN:
02068   case NS_MOUSE_MIDDLE_BUTTON_DOWN:
02069   case NS_MOUSE_RIGHT_BUTTON_DOWN:
02070     {
02071       if (aEvent->message == NS_MOUSE_LEFT_BUTTON_DOWN && !mNormalLMouseEventInProcess) {
02072         //Our state is out of whack.  We got a mouseup while still processing
02073         //the mousedown.  Kill View-level mouse capture or it'll stay stuck
02074         if (aView) {
02075           nsIViewManager* viewMan = aView->GetViewManager();
02076           if (viewMan) {
02077             nsIView* grabbingView;
02078             viewMan->GetMouseEventGrabber(grabbingView);
02079             if (grabbingView == aView) {
02080               PRBool result;
02081               viewMan->GrabMouseEvents(nsnull, result);
02082             }
02083           }
02084         }
02085         break;
02086       }
02087 
02088       if (mConsumeFocusEvents) {
02089         mConsumeFocusEvents = PR_FALSE;
02090         break;
02091       }
02092 
02093       if (nsEventStatus_eConsumeNoDefault != *aStatus) {
02094         nsCOMPtr<nsIContent> newFocus;
02095         PRBool suppressBlur = PR_FALSE;
02096         if (mCurrentTarget) {
02097           mCurrentTarget->GetContentForEvent(mPresContext, aEvent, getter_AddRefs(newFocus));
02098           const nsStyleUserInterface* ui = mCurrentTarget->GetStyleUserInterface();
02099           suppressBlur = (ui->mUserFocus == NS_STYLE_USER_FOCUS_IGNORE);
02100         }
02101 
02102         nsIFrame* currFrame = mCurrentTarget;
02103         nsIContent* activeContent = nsnull;
02104         if (mCurrentTarget)
02105           activeContent = mCurrentTarget->GetContent();
02106 
02107         // Look for the nearest enclosing focusable frame.
02108         while (currFrame) {
02109           // If the mousedown happened inside a popup, don't
02110           // try to set focus on one of its containing elements
02111           const nsStyleDisplay* display = currFrame->GetStyleDisplay();
02112           if (display->mDisplay == NS_STYLE_DISPLAY_POPUP) {
02113             newFocus = nsnull;
02114             break;
02115           }
02116 
02117           PRInt32 tabIndexUnused;
02118           if (currFrame->IsFocusable(&tabIndexUnused, PR_TRUE)) {
02119             newFocus = currFrame->GetContent();
02120             nsCOMPtr<nsIDOMElement> domElement(do_QueryInterface(newFocus));
02121             if (domElement)
02122               break;
02123           }
02124           currFrame = currFrame->GetParent();
02125         }
02126 
02127         if (newFocus && currFrame)
02128           ChangeFocusWith(newFocus, eEventFocusedByMouse);
02129         else if (!suppressBlur) {
02130           SetContentState(nsnull, NS_EVENT_STATE_FOCUS);
02131         }
02132 
02133         // The rest is left button-specific.
02134         if (aEvent->message != NS_MOUSE_LEFT_BUTTON_DOWN)
02135           break;
02136 
02137         if (activeContent) {
02138           // The nearest enclosing element goes into the
02139           // :active state.  If we fail the QI to DOMElement,
02140           // then we know we're only a node, and that we need
02141           // to obtain our parent element and put it into :active
02142           // instead.
02143           nsCOMPtr<nsIDOMElement> elt(do_QueryInterface(activeContent));
02144           if (!elt) {
02145             nsIContent* par = activeContent->GetParent();
02146             if (par)
02147               activeContent = par;
02148           }
02149           SetContentState(activeContent, NS_EVENT_STATE_ACTIVE);
02150         }
02151       }
02152       else {
02153         // if we're here, the event handler returned false, so stop
02154         // any of our own processing of a drag. Workaround for bug 43258.
02155         StopTrackingDragGesture();
02156       }
02157     }
02158     break;
02159   case NS_MOUSE_LEFT_BUTTON_UP:
02160   case NS_MOUSE_MIDDLE_BUTTON_UP:
02161   case NS_MOUSE_RIGHT_BUTTON_UP:
02162     {
02163       SetContentState(nsnull, NS_EVENT_STATE_ACTIVE);
02164       if (!mCurrentTarget) {
02165         nsIFrame* targ;
02166         GetEventTarget(&targ);
02167         if (!targ) return NS_ERROR_FAILURE;
02168       }
02169       ret = CheckForAndDispatchClick(presContext, (nsMouseEvent*)aEvent, aStatus);
02170       nsIPresShell *shell = presContext->GetPresShell();
02171       if (shell) {
02172         shell->FrameSelection()->SetMouseDownState(PR_FALSE);
02173       }
02174     }
02175     break;
02176   case NS_MOUSE_SCROLL:
02177     if (nsEventStatus_eConsumeNoDefault != *aStatus) {
02178 
02179       // Build the preference keys, based on the event properties.
02180       nsMouseScrollEvent *msEvent = (nsMouseScrollEvent*) aEvent;
02181 
02182       NS_NAMED_LITERAL_CSTRING(prefbase,        "mousewheel");
02183       NS_NAMED_LITERAL_CSTRING(horizscroll,     ".horizscroll");
02184       NS_NAMED_LITERAL_CSTRING(withshift,       ".withshiftkey");
02185       NS_NAMED_LITERAL_CSTRING(withalt,         ".withaltkey");
02186       NS_NAMED_LITERAL_CSTRING(withcontrol,     ".withcontrolkey");
02187       NS_NAMED_LITERAL_CSTRING(withmetakey,     ".withmetakey");
02188       NS_NAMED_LITERAL_CSTRING(withno,          ".withnokey");
02189       NS_NAMED_LITERAL_CSTRING(actionslot,      ".action");
02190       NS_NAMED_LITERAL_CSTRING(numlinesslot,    ".numlines");
02191       NS_NAMED_LITERAL_CSTRING(sysnumlinesslot, ".sysnumlines");
02192 
02193       nsCAutoString baseKey(prefbase);
02194       if (msEvent->scrollFlags & nsMouseScrollEvent::kIsHorizontal) {
02195         baseKey.Append(horizscroll);
02196       }
02197       if (msEvent->isShift) {
02198         baseKey.Append(withshift);
02199       } else if (msEvent->isControl) {
02200         baseKey.Append(withcontrol);
02201       } else if (msEvent->isAlt) {
02202         baseKey.Append(withalt);
02203       } else if (msEvent->isMeta) {
02204         baseKey.Append(withmetakey);
02205       } else {
02206         baseKey.Append(withno);
02207       }
02208 
02209       // Extract the preferences
02210       nsCAutoString actionKey(baseKey);
02211       actionKey.Append(actionslot);
02212 
02213       nsCAutoString sysNumLinesKey(baseKey);
02214       sysNumLinesKey.Append(sysnumlinesslot);
02215 
02216       PRInt32 action = nsContentUtils::GetIntPref(actionKey.get());
02217       PRInt32 numLines = 0;
02218       PRBool useSysNumLines =
02219         nsContentUtils::GetBoolPref(sysNumLinesKey.get());
02220 
02221       if (useSysNumLines) {
02222         numLines = msEvent->delta;
02223         if (msEvent->scrollFlags & nsMouseScrollEvent::kIsFullPage)
02224           action = MOUSE_SCROLL_PAGE;
02225         else if (msEvent->scrollFlags & nsMouseScrollEvent::kIsPixels)
02226           action = MOUSE_SCROLL_PIXELS;
02227       }
02228       else
02229         {
02230           // If the scroll event's delta isn't to our liking, we can
02231           // override it with the "numlines" parameter.  There are two
02232           // things we can do:
02233           //
02234           // (1) Pick a different number.  Instead of scrolling 3
02235           //     lines ("delta" in Gtk2), we would scroll 1 line.
02236           // (2) Swap directions.  Instead of scrolling down, scroll up.
02237           //
02238           // For the first item, the magnitude of the parameter is
02239           // used instead of the magnitude of the delta.  For the
02240           // second item, if the parameter is negative we swap
02241           // directions.
02242 
02243           nsCAutoString numLinesKey(baseKey);
02244           numLinesKey.Append(numlinesslot);
02245 
02246           numLines = nsContentUtils::GetIntPref(numLinesKey.get());
02247 
02248           bool swapDirs = (numLines < 0);
02249           PRInt32 userSize = swapDirs ? -numLines : numLines;
02250 
02251           PRInt32 deltaUp = (msEvent->delta < 0);
02252           if (swapDirs) {
02253             deltaUp = ! deltaUp;
02254           }
02255 
02256           numLines = deltaUp ? -userSize : userSize;
02257         }
02258 
02259       switch (action) {
02260       case MOUSE_SCROLL_N_LINES:
02261         {
02262           DoScrollText(presContext, aTargetFrame, msEvent, numLines,
02263                        (msEvent->scrollFlags & nsMouseScrollEvent::kIsHorizontal),
02264                        eScrollByLine);
02265         }
02266         break;
02267 
02268       case MOUSE_SCROLL_PAGE:
02269         {
02270           DoScrollText(presContext, aTargetFrame, msEvent, numLines,
02271                        (msEvent->scrollFlags & nsMouseScrollEvent::kIsHorizontal),
02272                        eScrollByPage);
02273         }
02274         break;
02275 
02276       case MOUSE_SCROLL_PIXELS:
02277         {
02278           DoScrollText(presContext, aTargetFrame, msEvent, numLines,
02279                        (msEvent->scrollFlags & nsMouseScrollEvent::kIsHorizontal),
02280                        eScrollByPixel);
02281         }
02282         break;
02283 
02284       case MOUSE_SCROLL_HISTORY:
02285         {
02286           DoScrollHistory(numLines);
02287         }
02288         break;
02289 
02290       case MOUSE_SCROLL_TEXTSIZE:
02291         {
02292           DoScrollTextsize(aTargetFrame, numLines);
02293         }
02294         break;
02295 
02296       default:  // Including -1 (do nothing)
02297         break;
02298       }
02299       *aStatus = nsEventStatus_eConsumeNoDefault;
02300 
02301     }
02302 
02303     break;
02304 
02305   case NS_DRAGDROP_DROP:
02306   case NS_DRAGDROP_EXIT:
02307     // clean up after ourselves. make sure we do this _after_ the event, else we'll
02308     // clean up too early!
02309     GenerateDragDropEnterExit(presContext, (nsGUIEvent*)aEvent);
02310     break;
02311 
02312   case NS_KEY_UP:
02313     break;
02314 
02315   case NS_KEY_PRESS:
02316     if (nsEventStatus_eConsumeNoDefault != *aStatus) {
02317       nsKeyEvent* keyEvent = (nsKeyEvent*)aEvent;
02318       //This is to prevent keyboard scrolling while alt modifier in use.
02319       if (!keyEvent->isAlt) {
02320         switch(keyEvent->keyCode) {
02321           case NS_VK_TAB:
02322             if (mConsumeFocusEvents) {
02323               mConsumeFocusEvents = PR_FALSE;
02324               break;
02325             }
02326             if (!((nsInputEvent*)aEvent)->isControl) {
02327               //Shift focus forward or back depending on shift key
02328               ShiftFocus(!((nsInputEvent*)aEvent)->isShift);
02329             } else {
02330               ShiftFocusByDoc(!((nsInputEvent*)aEvent)->isShift);
02331             }
02332             *aStatus = nsEventStatus_eConsumeNoDefault;
02333             break;
02334 
02335           case NS_VK_F6:
02336             if (mConsumeFocusEvents) {
02337               mConsumeFocusEvents = PR_FALSE;
02338               break;
02339             }
02340             //Shift focus forward or back depending on shift key
02341             ShiftFocusByDoc(!((nsInputEvent*)aEvent)->isShift);
02342             *aStatus = nsEventStatus_eConsumeNoDefault;
02343             break;
02344 
02345 //the problem is that viewer does not have xul so we cannot completely eliminate these
02346 #if NON_KEYBINDING
02347           case NS_VK_PAGE_DOWN:
02348           case NS_VK_PAGE_UP:
02349             if (!mCurrentFocus) {
02350               nsIScrollableView* sv = nsLayoutUtils::GetNearestScrollingView(aView, nsLayoutUtils::eVertical);
02351               if (sv) {
02352                 nsKeyEvent * keyEvent = (nsKeyEvent *)aEvent;
02353                 sv->ScrollByPages(0, (keyEvent->keyCode != NS_VK_PAGE_UP) ? 1 : -1);
02354               }
02355             }
02356             break;
02357           case NS_VK_HOME:
02358           case NS_VK_END:
02359             if (!mCurrentFocus) {
02360               nsIScrollableView* sv = nsLayoutUtils::GetNearestScrollingView(aView, nsLayoutUtils::eVertical);
02361               if (sv) {
02362                 nsKeyEvent * keyEvent = (nsKeyEvent *)aEvent;
02363                 sv->ScrollByWhole((keyEvent->keyCode != NS_VK_HOME) ? PR_FALSE : PR_TRUE);
02364               }
02365             }
02366             break;
02367           case NS_VK_DOWN:
02368           case NS_VK_UP:
02369             if (!mCurrentFocus) {
02370               nsIScrollableView* sv = nsLayoutUtils::GetNearestScrollingView(aView, nsLayoutUtils::eVertical);
02371               if (sv) {
02372                 nsKeyEvent * keyEvent = (nsKeyEvent *)aEvent;
02373                 sv->ScrollByLines(0, (keyEvent->keyCode == NS_VK_DOWN) ? 1 : -1);
02374 
02375                 // force the update to happen now, otherwise multiple scrolls can
02376                 // occur before the update is processed. (bug #7354)
02377                 nsIViewManager* vm = aView->GetViewManager();
02378                 if (vm) {
02379                   // I'd use Composite here, but it doesn't always work.
02380                   // vm->Composite();
02381                   vm->ForceUpdate();
02382                 }
02383               }
02384             }
02385             break;
02386           case NS_VK_LEFT:
02387           case NS_VK_RIGHT:
02388             if (!mCurrentFocus) {
02389               nsIScrollableView* sv = nsLayoutUtils::GetNearestScrollingView(aView, nsLayoutUtils::eHorizontal);
02390               if (sv) {
02391                 nsKeyEvent * keyEvent = (nsKeyEvent *)aEvent;
02392                 sv->ScrollByLines((keyEvent->keyCode == NS_VK_RIGHT) ? 1 : -1, 0);
02393 
02394                 // force the update to happen now, otherwise multiple scrolls can
02395                 // occur before the update is processed. (bug #7354)
02396                 nsIViewManager* vm = aView->GetViewManager();
02397                 if (vm) {
02398                   // I'd use Composite here, but it doesn't always work.
02399                   // vm->Composite();
02400                   vm->ForceUpdate();
02401                 }
02402               }
02403             }
02404             break;
02405         case 0: /* check charcode since keycode is 0 */
02406           {
02407           //Spacebar
02408             nsKeyEvent * keyEvent = (nsKeyEvent *)aEvent;
02409             if (keyEvent->charCode == 0x20) {
02410               if (!mCurrentFocus) {
02411                 nsIScrollableView* sv = nsLayoutUtils::GetNearestScrollingView(aView, nsLayoutUtils::eVertical);
02412                 if (sv) {
02413                   sv->ScrollByPages(0, 1);
02414                 }
02415               }
02416             }
02417           }
02418           break;
02419 #endif //NON_KEYBINDING
02420         }
02421       }
02422     }
02423     break;
02424 
02425   case NS_MOUSE_ENTER:
02426     if (mCurrentTarget) {
02427       nsCOMPtr<nsIContent> targetContent;
02428       mCurrentTarget->GetContentForEvent(presContext, aEvent,
02429                                          getter_AddRefs(targetContent));
02430       SetContentState(targetContent, NS_EVENT_STATE_HOVER);
02431     }
02432     break;
02433 
02434   //
02435   // OS custom application event, such as from MS IntelliMouse
02436   //
02437   case NS_APPCOMMAND:
02438     // by default, tell the driver we're not handling the event
02439     ret = PR_FALSE;
02440 
02441     nsAppCommandEvent* appCommandEvent = (nsAppCommandEvent*) aEvent;
02442 
02443     nsCOMPtr<nsISupports> pcContainer;
02444     switch (appCommandEvent->appCommand) {
02445       case NS_APPCOMMAND_BACK:
02446       case NS_APPCOMMAND_FORWARD:
02447       case NS_APPCOMMAND_REFRESH:
02448       case NS_APPCOMMAND_STOP:
02449         // handle these commands using nsIWebNavigation
02450         pcContainer = mPresContext->GetContainer();
02451         if (pcContainer) {
02452           nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(pcContainer));
02453           if (webNav) {
02454             // tell the driver we're handling the event
02455             ret = PR_TRUE;
02456 
02457             switch (appCommandEvent->appCommand) {
02458               case NS_APPCOMMAND_BACK:
02459                 webNav->GoBack();
02460                 break;
02461 
02462               case NS_APPCOMMAND_FORWARD:
02463                 webNav->GoForward();
02464                 break;
02465 
02466               case NS_APPCOMMAND_REFRESH:
02467                 webNav->Reload(nsIWebNavigation::LOAD_FLAGS_BYPASS_CACHE);
02468                 break;
02469 
02470               case NS_APPCOMMAND_STOP:
02471                 webNav->Stop(nsIWebNavigation::STOP_ALL);
02472                 break;
02473 
02474             }  // switch (appCommandEvent->appCommand)
02475           }  // if (webNav)
02476         }  // if (pcContainer)
02477         break;
02478 
02479       // XXX todo: handle these commands
02480       // case NS_APPCOMMAND_SEARCH:
02481       // case NS_APPCOMMAND_FAVORITES:
02482       // case NS_APPCOMMAND_HOME:
02483 
02484         // tell the driver we're handling the event
02485         // ret = PR_TRUE;
02486         // break;
02487 
02488     }  // switch (appCommandEvent->appCommand)
02489 
02490     break;
02491   }
02492 
02493   //Reset target frame to null to avoid mistargeting after reentrant event
02494   mCurrentTarget = nsnull;
02495 
02496   return ret;
02497 }
02498 
02499 NS_IMETHODIMP
02500 nsEventStateManager::SetPresContext(nsPresContext* aPresContext)
02501 {
02502   if (aPresContext == nsnull) {
02503     // A pres context is going away. Make sure we do cleanup.
02504     if (mPresContext == gLastFocusedPresContext) {
02505       gLastFocusedPresContext = nsnull;
02506       NS_IF_RELEASE(gLastFocusedDocument);
02507       NS_IF_RELEASE(gLastFocusedContent);
02508     }
02509   }
02510 
02511   mPresContext = aPresContext;
02512   return NS_OK;
02513 }
02514 
02515 NS_IMETHODIMP
02516 nsEventStateManager::ClearFrameRefs(nsIFrame* aFrame)
02517 {
02518   if (aFrame == mLastMouseOverFrame)
02519     mLastMouseOverFrame = nsnull;
02520   if (aFrame == mLastDragOverFrame)
02521     mLastDragOverFrame = nsnull;
02522   if (aFrame == mCurrentTarget) {
02523     if (aFrame) {
02524       mCurrentTargetContent = aFrame->GetContent();
02525     }
02526     mCurrentTarget = nsnull;
02527   }
02528   if (aFrame == mCurrentFocusFrame)
02529     mCurrentFocusFrame = nsnull;
02530   if (mDOMEventLevel > 0) {
02531     mClearedFrameRefsDuringEvent = PR_TRUE;
02532   }
02533 
02534 
02535   return NS_OK;
02536 }
02537 
02538 void
02539 nsEventStateManager::UpdateCursor(nsPresContext* aPresContext,
02540                                   nsEvent* aEvent, nsIFrame* aTargetFrame,
02541                                   nsEventStatus* aStatus)
02542 {
02543   PRInt32 cursor = NS_STYLE_CURSOR_DEFAULT;
02544   imgIContainer* container = nsnull;
02545   PRBool haveHotspot = PR_FALSE;
02546   float hotspotX = 0.0f, hotspotY = 0.0f;
02547 
02548   //If cursor is locked just use the locked one
02549   if (mLockCursor) {
02550     cursor = mLockCursor;
02551   }
02552   //If not locked, look for correct cursor
02553   else if (aTargetFrame) {
02554       nsIFrame::Cursor framecursor;
02555       if (NS_FAILED(aTargetFrame->GetCursor(aEvent->point, framecursor)))
02556         return;  // don't update the cursor if we failed to get it from the frame see bug 118877
02557       cursor = framecursor.mCursor;
02558       container = framecursor.mContainer;
02559       haveHotspot = framecursor.mHaveHotspot;
02560       hotspotX = framecursor.mHotspotX;
02561       hotspotY = framecursor.mHotspotY;
02562   }
02563 
02564   // Check whether or not to show the busy cursor
02565   nsCOMPtr<nsISupports> pcContainer = aPresContext->GetContainer();
02566   nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(pcContainer));
02567   if (!docShell) return;
02568   PRUint32 busyFlags = nsIDocShell::BUSY_FLAGS_NONE;
02569   docShell->GetBusyFlags(&busyFlags);
02570 
02571   // Show busy cursor everywhere before page loads
02572   // and just replace the arrow cursor after page starts loading
02573   if (busyFlags & nsIDocShell::BUSY_FLAGS_BUSY &&
02574         (cursor == NS_STYLE_CURSOR_AUTO || cursor == NS_STYLE_CURSOR_DEFAULT))
02575   {
02576     cursor = NS_STYLE_CURSOR_SPINNING;
02577     container = nsnull;
02578   }
02579 
02580   if (aTargetFrame) {
02581     SetCursor(cursor, container, haveHotspot, hotspotX, hotspotY,
02582               aTargetFrame->GetWindow(), PR_FALSE);
02583   }
02584 
02585   if (mLockCursor || NS_STYLE_CURSOR_AUTO != cursor) {
02586     *aStatus = nsEventStatus_eConsumeDoDefault;
02587   }
02588 }
02589 
02590 NS_IMETHODIMP
02591 nsEventStateManager::SetCursor(PRInt32 aCursor, imgIContainer* aContainer,
02592                                PRBool aHaveHotspot,
02593                                float aHotspotX, float aHotspotY,
02594                                nsIWidget* aWidget, PRBool aLockCursor)
02595 {
02596   nsCursor c;
02597 
02598   NS_ENSURE_TRUE(aWidget, NS_ERROR_FAILURE);
02599   if (aLockCursor) {
02600     if (NS_STYLE_CURSOR_AUTO != aCursor) {
02601       mLockCursor = aCursor;
02602     }
02603     else {
02604       //If cursor style is set to auto we unlock the cursor again.
02605       mLockCursor = 0;
02606     }
02607   }
02608   switch (aCursor) {
02609   default:
02610   case NS_STYLE_CURSOR_AUTO:
02611   case NS_STYLE_CURSOR_DEFAULT:
02612     c = eCursor_standard;
02613     break;
02614   case NS_STYLE_CURSOR_POINTER:
02615     c = eCursor_hyperlink;
02616     break;
02617   case NS_STYLE_CURSOR_CROSSHAIR:
02618     c = eCursor_crosshair;
02619     break;
02620   case NS_STYLE_CURSOR_MOVE:
02621     c = eCursor_move;
02622     break;
02623   case NS_STYLE_CURSOR_TEXT:
02624     c = eCursor_select;
02625     break;
02626   case NS_STYLE_CURSOR_WAIT:
02627     c = eCursor_wait;
02628     break;
02629   case NS_STYLE_CURSOR_HELP:
02630     c = eCursor_help;
02631     break;
02632   case NS_STYLE_CURSOR_N_RESIZE:
02633     c = eCursor_n_resize;
02634     break;
02635   case NS_STYLE_CURSOR_S_RESIZE:
02636     c = eCursor_s_resize;
02637     break;
02638   case NS_STYLE_CURSOR_W_RESIZE:
02639     c = eCursor_w_resize;
02640     break;
02641   case NS_STYLE_CURSOR_E_RESIZE:
02642     c = eCursor_e_resize;
02643     break;
02644   case NS_STYLE_CURSOR_NW_RESIZE:
02645     c = eCursor_nw_resize;
02646     break;
02647   case NS_STYLE_CURSOR_SE_RESIZE:
02648     c = eCursor_se_resize;
02649     break;
02650   case NS_STYLE_CURSOR_NE_RESIZE:
02651     c = eCursor_ne_resize;
02652     break;
02653   case NS_STYLE_CURSOR_SW_RESIZE:
02654     c = eCursor_sw_resize;
02655     break;
02656   case NS_STYLE_CURSOR_COPY: // CSS3
02657     c = eCursor_copy;
02658     break;
02659   case NS_STYLE_CURSOR_ALIAS:
02660     c = eCursor_alias;
02661     break;
02662   case NS_STYLE_CURSOR_CONTEXT_MENU:
02663     c = eCursor_context_menu;
02664     break;
02665   case NS_STYLE_CURSOR_CELL:
02666     c = eCursor_cell;
02667     break;
02668   case NS_STYLE_CURSOR_GRAB:
02669     c = eCursor_grab;
02670     break;
02671   case NS_STYLE_CURSOR_GRABBING:
02672     c = eCursor_grabbing;
02673     break;
02674   case NS_STYLE_CURSOR_SPINNING:
02675     c = eCursor_spinning;
02676     break;
02677   case NS_STYLE_CURSOR_MOZ_ZOOM_IN:
02678     c = eCursor_zoom_in;
02679     break;
02680   case NS_STYLE_CURSOR_MOZ_ZOOM_OUT:
02681     c = eCursor_zoom_out;
02682     break;
02683   case NS_STYLE_CURSOR_NOT_ALLOWED:
02684     c = eCursor_not_allowed;
02685     break;
02686   case NS_STYLE_CURSOR_COL_RESIZE:
02687     c = eCursor_col_resize;
02688     break;
02689   case NS_STYLE_CURSOR_ROW_RESIZE:
02690     c = eCursor_row_resize;
02691     break;
02692   case NS_STYLE_CURSOR_NO_DROP:
02693     c = eCursor_no_drop;
02694     break;
02695   case NS_STYLE_CURSOR_VERTICAL_TEXT:
02696     c = eCursor_vertical_text;
02697     break;
02698   case NS_STYLE_CURSOR_ALL_SCROLL:
02699     c = eCursor_all_scroll;
02700     break;
02701   case NS_STYLE_CURSOR_NESW_RESIZE:
02702     c = eCursor_nesw_resize;
02703     break;
02704   case NS_STYLE_CURSOR_NWSE_RESIZE:
02705     c = eCursor_nwse_resize;
02706     break;
02707   case NS_STYLE_CURSOR_NS_RESIZE:
02708     c = eCursor_ns_resize;
02709     break;
02710   case NS_STYLE_CURSOR_EW_RESIZE:
02711     c = eCursor_ew_resize;
02712     break;
02713   }
02714 
02715   // First, try the imgIContainer, if non-null
02716   nsresult rv = NS_ERROR_FAILURE;
02717   if (aContainer) {
02718     PRUint32 hotspotX, hotspotY;
02719 
02720     // css3-ui says to use the CSS-specified hotspot if present,
02721     // otherwise use the intrinsic hotspot, otherwise use the top left
02722     // corner.
02723     if (aHaveHotspot) {
02724       PRInt32 imgWidth, imgHeight;
02725       aContainer->GetWidth(&imgWidth);
02726       aContainer->GetHeight(&imgHeight);
02727 
02728       // XXX NSToUintRound?
02729       hotspotX = aHotspotX > 0.0f
02730                    ? PRUint32(aHotspotX + ROUND_CONST_FLOAT) : PRUint32(0);
02731       if (hotspotX >= PRUint32(imgWidth))
02732         hotspotX = imgWidth - 1;
02733       hotspotY = aHotspotY > 0.0f
02734                    ? PRUint32(aHotspotY + ROUND_CONST_FLOAT) : PRUint32(0);
02735       if (hotspotY >= PRUint32(imgHeight))
02736         hotspotY = imgHeight - 1;
02737     } else {
02738       hotspotX = 0;
02739       hotspotY = 0;
02740       nsCOMPtr<nsIProperties> props(do_QueryInterface(aContainer));
02741       if (props) {
02742         nsCOMPtr<nsISupportsPRUint32> hotspotXWrap, hotspotYWrap;
02743 
02744         props->Get("hotspotX", NS_GET_IID(nsISupportsPRUint32), getter_AddRefs(hotspotXWrap));
02745         props->Get("hotspotY", NS_GET_IID(nsISupportsPRUint32), getter_AddRefs(hotspotYWrap));
02746 
02747         if (hotspotXWrap)
02748           hotspotXWrap->GetData(&hotspotX);
02749         if (hotspotYWrap)
02750           hotspotYWrap->GetData(&hotspotY);
02751       }
02752     }
02753 
02754     rv = aWidget->SetCursor(aContainer, hotspotX, hotspotY);
02755   }
02756 
02757   if (NS_FAILED(rv))
02758     aWidget->SetCursor(c);
02759 
02760   return NS_OK;
02761 }
02762 
02763 void
02764 nsEventStateManager::AfterDispatchEvent()
02765 {
02766   mDOMEventLevel--;
02767   if (mClearedFrameRefsDuringEvent && mDOMEventLevel == 0) {
02768     mClearedFrameRefsDuringEvent = PR_FALSE;
02769   }
02770 }
02771 
02772 nsIFrame*
02773 nsEventStateManager::DispatchMouseEvent(nsGUIEvent* aEvent, PRUint32 aMessage,
02774                                         nsIContent* aTargetContent,
02775                                         nsIContent* aRelatedContent)
02776 {
02777   nsEventStatus status = nsEventStatus_eIgnore;
02778   nsMouseEvent event(NS_IS_TRUSTED_EVENT(aEvent), aMessage, aEvent->widget,
02779                      nsMouseEvent::eReal);
02780   event.point = aEvent->point;
02781   event.refPoint = aEvent->refPoint;
02782   event.isShift = ((nsMouseEvent*)aEvent)->isShift;
02783   event.isControl = ((nsMouseEvent*)aEvent)->isControl;
02784   event.isAlt = ((nsMouseEvent*)aEvent)->isAlt;
02785   event.isMeta = ((nsMouseEvent*)aEvent)->isMeta;
02786   event.nativeMsg = ((nsMouseEvent*)aEvent)->nativeMsg;
02787 
02788   mCurrentTargetContent = aTargetContent;
02789   mCurrentRelatedContent = aRelatedContent;
02790 
02791   BeforeDispatchEvent();
02792   nsIFrame* targetFrame = nsnull;
02793   if (aTargetContent) {
02794     aTargetContent->HandleDOMEvent(mPresContext, &event, nsnull,
02795                                    NS_EVENT_FLAG_INIT, &status);
02796 
02797     nsIPresShell *shell = mPresContext->GetPresShell();
02798     if (shell) {
02799       shell->GetPrimaryFrameFor(aTargetContent, &targetFrame);
02800     }
02801   }
02802   if (targetFrame) {
02803     targetFrame->HandleEvent(mPresContext, &event, &status);
02804     SetFrameExternalReference(targetFrame);
02805   }
02806   AfterDispatchEvent();
02807 
02808   mCurrentTargetContent = nsnull;
02809   mCurrentRelatedContent = nsnull;
02810 
02811   return targetFrame;
02812 }
02813 
02814 void
02815 nsEventStateManager::NotifyMouseOut(nsGUIEvent* aEvent, nsIContent* aMovingInto)
02816 {
02817   if (!mLastMouseOverElement)
02818     return;
02819   // Before firing mouseout, check for recursion
02820   if (mLastMouseOverElement == mFirstMouseOutEventElement)
02821     return;
02822 
02823   if (mLastMouseOverFrame) {
02824     // if the frame is associated with a subdocument,
02825     // tell the subdocument that we're moving out of it
02826     nsIFrameFrame* subdocFrame;
02827     CallQueryInterface(mLastMouseOverFrame, &subdocFrame);
02828     if (subdocFrame) {
02829       nsCOMPtr<nsIDocShell> docshell;
02830       subdocFrame->GetDocShell(getter_AddRefs(docshell));
02831       if (docshell) {
02832         nsCOMPtr<nsPresContext> presContext;
02833         docshell->GetPresContext(getter_AddRefs(presContext));
02834         
02835         if (presContext) {
02836           nsEventStateManager* kidESM =
02837             NS_STATIC_CAST(nsEventStateManager*, presContext->EventStateManager());
02838           // Not moving into any element in this subdocument
02839           kidESM->NotifyMouseOut(aEvent, nsnull);
02840         }
02841       }
02842     }
02843   }
02844   // That could have caused DOM events which could wreak havoc. Reverify
02845   // things and be careful.
02846   if (!mLastMouseOverElement)
02847     return;
02848 
02849   // Store the first mouseOut event we fire and don't refire mouseOut
02850   // to that element while the first mouseOut is still ongoing.
02851   mFirstMouseOutEventElement = mLastMouseOverElement;
02852 
02853   // Don't touch hover state if aMovingInto is non-null.  Caller will update
02854   // hover state itself, and we have optimizations for hover switching between
02855   // two nearby elements both deep in the DOM tree that would be defeated by
02856   // switching the hover state to null here.
02857   if (!aMovingInto) {
02858     // Unset :hover
02859     SetContentState(nsnull, NS_EVENT_STATE_HOVER);
02860   }
02861   
02862   // Fire mouseout
02863   DispatchMouseEvent(aEvent, NS_MOUSE_EXIT_SYNTH,
02864                      mLastMouseOverElement, aMovingInto);
02865   
02866   mLastMouseOverFrame = nsnull;
02867   mLastMouseOverElement = nsnull;
02868   
02869   // Turn recursion protection back off
02870   mFirstMouseOutEventElement = nsnull;
02871 }
02872 
02873 void
02874 nsEventStateManager::NotifyMouseOver(nsGUIEvent* aEvent, nsIContent* aContent)
02875 {
02876   NS_ASSERTION(aContent, "Mouse must be over something");
02877 
02878   if (mLastMouseOverElement == aContent)
02879     return;
02880 
02881   // Before firing mouseover, check for recursion
02882   if (aContent == mFirstMouseOverEventElement && mFirstMouseOverEventElement)
02883     return;
02884 
02885   // Check to see if we're a subdocument and if so update the parent
02886   // document's ESM state to indicate that the mouse is over the
02887   // content associated with our subdocument.
02888   EnsureDocument(mPresContext);
02889   nsIDocument *parentDoc = mDocument->GetParentDocument();
02890   if (parentDoc) {
02891     nsIContent *docContent = parentDoc->FindContentForSubDocument(mDocument);
02892     if (docContent) {
02893       nsIPresShell *parentShell = parentDoc->GetShellAt(0);
02894       if (parentShell) {
02895         nsEventStateManager* parentESM =
02896           NS_STATIC_CAST(nsEventStateManager*,
02897                          parentShell->GetPresContext()->EventStateManager());
02898         parentESM->NotifyMouseOver(aEvent, docContent);
02899       }
02900     }
02901   }
02902   // Firing the DOM event in the parent document could cause all kinds
02903   // of havoc.  Reverify and take care.
02904   if (mLastMouseOverElement == aContent)
02905     return;
02906 
02907   // Remember mLastMouseOverElement as the related content for the
02908   // DispatchMouseEvent() call below, since NotifyMouseOut() resets it, bug 298477.
02909   nsCOMPtr<nsIContent> lastMouseOverElement = mLastMouseOverElement;
02910 
02911   NotifyMouseOut(aEvent, aContent);
02912 
02913   // Store the first mouseOver event we fire and don't refire mouseOver
02914   // to that element while the first mouseOver is still ongoing.
02915   mFirstMouseOverEventElement = aContent;
02916   
02917   SetContentState(aContent, NS_EVENT_STATE_HOVER);
02918   
02919   // Fire mouseover
02920   mLastMouseOverFrame = DispatchMouseEvent(aEvent, NS_MOUSE_ENTER_SYNTH,
02921                                            aContent, lastMouseOverElement);
02922   mLastMouseOverElement = aContent;
02923   
02924   // Turn recursion protection back off
02925   mFirstMouseOverEventElement = nsnull;
02926 }
02927 
02928 void
02929 nsEventStateManager::GenerateMouseEnterExit(nsGUIEvent* aEvent)
02930 {
02931   EnsureDocument(mPresContext);
02932   if (!mDocument)
02933     return;
02934 
02935   // Hold onto old target content through the event and reset after.
02936   nsCOMPtr<nsIContent> targetBeforeEvent = mCurrentTargetContent;
02937 
02938   switch(aEvent->message) {
02939   case NS_MOUSE_MOVE:
02940     {
02941       // Get the target content target (mousemove target == mouseover target)
02942       nsCOMPtr<nsIContent> targetElement;
02943       GetEventTargetContent(aEvent, getter_AddRefs(targetElement));
02944       if (!targetElement) {
02945         // We're always over the document root, even if we're only
02946         // over dead space in a page (whose frame is not associated with
02947         // any content) or in print preview dead space
02948         targetElement = mDocument->GetRootContent();
02949       }
02950       NS_ASSERTION(targetElement, "Mouse move must have some target content");
02951       if (targetElement) {
02952         NotifyMouseOver(aEvent, targetElement);
02953       }
02954     }
02955     break;
02956   case NS_MOUSE_EXIT:
02957     {
02958       // This is actually the window mouse exit event. We're not moving
02959       // into any new element.
02960       NotifyMouseOut(aEvent, nsnull);
02961     }
02962     break;
02963   }
02964 
02965   // reset mCurretTargetContent to what it was
02966   mCurrentTargetContent = targetBeforeEvent;
02967 }
02968 
02969 void
02970 nsEventStateManager::GenerateDragDropEnterExit(nsPresContext* aPresContext,
02971                                                nsGUIEvent* aEvent)
02972 {
02973   //Hold onto old target content through the event and reset after.
02974   nsCOMPtr<nsIContent> targetBeforeEvent = mCurrentTargetContent;
02975 
02976   switch(aEvent->message) {
02977   case NS_DRAGDROP_OVER:
02978     {
02979       if (mLastDragOverFrame != mCurrentTarget) {
02980         //We'll need the content, too, to check if it changed separately from the frames.
02981         nsCOMPtr<nsIContent> lastContent;
02982         nsCOMPtr<nsIContent> targetContent;
02983         mCurrentTarget->GetContentForEvent(aPresContext, aEvent, getter_AddRefs(targetContent));
02984 
02985         if ( mLastDragOverFrame ) {
02986           //fire drag exit
02987           nsEventStatus status = nsEventStatus_eIgnore;
02988           nsMouseEvent event(NS_IS_TRUSTED_EVENT(aEvent),
02989                              NS_DRAGDROP_EXIT_SYNTH, aEvent->widget,
02990                              nsMouseEvent::eReal);
02991           event.point = aEvent->point;
02992           event.refPoint = aEvent->refPoint;
02993           event.isShift = ((nsMouseEvent*)aEvent)->isShift;
02994           event.isControl = ((nsMouseEvent*)aEvent)->isControl;
02995           event.isAlt = ((nsMouseEvent*)aEvent)->isAlt;
02996           event.isMeta = ((nsMouseEvent*)aEvent)->isMeta;
02997 
02998           //The frame has change but the content may not have.  Check before dispatching to content
02999           mLastDragOverFrame->GetContentForEvent(aPresContext, aEvent, getter_AddRefs(lastContent));
03000 
03001           mCurrentTargetContent = lastContent;
03002           mCurrentRelatedContent = targetContent;
03003 
03004           if ( lastContent != targetContent ) {
03005             //XXX This event should still go somewhere!!
03006             if (lastContent)
03007               lastContent->HandleDOMEvent(aPresContext, &event, nsnull, NS_EVENT_FLAG_INIT, &status);
03008 
03009             // clear the drag hover
03010             if (status != nsEventStatus_eConsumeNoDefault )
03011               SetContentState(nsnull, NS_EVENT_STATE_DRAGOVER);
03012           }
03013 
03014           // Finally dispatch exit to the frame
03015           if ( mLastDragOverFrame ) {
03016             mLastDragOverFrame->HandleEvent(aPresContext, &event, &status);
03017 
03018           }
03019         }
03020 
03021         //fire drag enter
03022         nsEventStatus status = nsEventStatus_eIgnore;
03023         nsMouseEvent event(NS_IS_TRUSTED_EVENT(aEvent), NS_DRAGDROP_ENTER,
03024                            aEvent->widget, nsMouseEvent::eReal);
03025         event.point = aEvent->point;
03026         event.refPoint = aEvent->refPoint;
03027         event.isShift = ((nsMouseEvent*)aEvent)->isShift;
03028         event.isControl = ((nsMouseEvent*)aEvent)->isControl;
03029         event.isAlt = ((nsMouseEvent*)aEvent)->isAlt;
03030         event.isMeta = ((nsMouseEvent*)aEvent)->isMeta;
03031 
03032         mCurrentTargetContent = targetContent;
03033         mCurrentRelatedContent = lastContent;
03034 
03035         //The frame has change but the content may not have.  Check before dispatching to content
03036         if ( lastContent != targetContent ) {
03037           //XXX This event should still go somewhere!!
03038           if ( targetContent )
03039             targetContent->HandleDOMEvent(aPresContext, &event, nsnull, NS_EVENT_FLAG_INIT, &status);
03040 
03041           // set drag hover on this frame
03042           if ( status != nsEventStatus_eConsumeNoDefault )
03043             SetContentState(targetContent, NS_EVENT_STATE_DRAGOVER);
03044         }
03045 
03046         // Finally dispatch to the frame
03047         if (mCurrentTarget) {
03048           //XXX Get the new frame
03049           mCurrentTarget->HandleEvent(aPresContext, &event, &status);
03050         }
03051 
03052         mLastDragOverFrame = mCurrentTarget;
03053       }
03054     }
03055     break;
03056 
03057   case NS_DRAGDROP_DROP:
03058   case NS_DRAGDROP_EXIT:
03059     {
03060       //This is actually the window mouse exit event.
03061       if ( mLastDragOverFrame ) {
03062 
03063         // fire mouseout
03064         nsEventStatus status = nsEventStatus_eIgnore;
03065         nsMouseEvent event(NS_IS_TRUSTED_EVENT(aEvent), NS_DRAGDROP_EXIT_SYNTH,
03066                            aEvent->widget, nsMouseEvent::eReal);
03067         event.point = aEvent->point;
03068         event.refPoint = aEvent->refPoint;
03069         event.isShift = ((nsMouseEvent*)aEvent)->isShift;
03070         event.isControl = ((nsMouseEvent*)aEvent)->isControl;
03071         event.isAlt = ((nsMouseEvent*)aEvent)->isAlt;
03072         event.isMeta = ((nsMouseEvent*)aEvent)->isMeta;
03073 
03074         // dispatch to content via DOM
03075         nsCOMPtr<nsIContent> lastContent;
03076         mLastDragOverFrame->GetContentForEvent(aPresContext, aEvent, getter_AddRefs(lastContent));
03077 
03078         mCurrentTargetContent = lastContent;
03079         mCurrentRelatedContent = nsnull;
03080 
03081         if ( lastContent ) {
03082           lastContent->HandleDOMEvent(aPresContext, &event, nsnull, NS_EVENT_FLAG_INIT, &status);
03083           if ( status != nsEventStatus_eConsumeNoDefault )
03084             SetContentState(nsnull, NS_EVENT_STATE_DRAGOVER);
03085         }
03086 
03087         // Finally dispatch to the frame
03088         if ( mLastDragOverFrame ) {
03089           //XXX Get the new frame
03090           mLastDragOverFrame->HandleEvent(aPresContext, &event, &status);
03091           mLastDragOverFrame = nsnull;
03092         }
03093      }
03094     }
03095     break;
03096   }
03097 
03098   //reset mCurretTargetContent to what it was
03099   mCurrentTargetContent = targetBeforeEvent;
03100 
03101   // Now flush all pending notifications, for better responsiveness.
03102   FlushPendingEvents(aPresContext);
03103 }
03104 
03105 nsresult
03106 nsEventStateManager::SetClickCount(nsPresContext* aPresContext,
03107                                    nsMouseEvent *aEvent,
03108                                    nsEventStatus* aStatus)
03109 {
03110   nsresult ret = NS_OK;
03111   nsCOMPtr<nsIContent> mouseContent;
03112 
03113   mCurrentTarget->GetContentForEvent(aPresContext, aEvent, getter_AddRefs(mouseContent));
03114 
03115   switch (aEvent->message) {
03116   case NS_MOUSE_LEFT_BUTTON_DOWN:
03117     mLastLeftMouseDownContent = mouseContent;
03118     break;
03119 
03120   case NS_MOUSE_LEFT_BUTTON_UP:
03121     if (mLastLeftMouseDownContent == mouseContent) {
03122       aEvent->clickCount = mLClickCount;
03123       mLClickCount = 0;
03124     }
03125     else {
03126       aEvent->clickCount = 0;
03127     }
03128     mLastLeftMouseDownContent = nsnull;
03129     break;
03130 
03131   case NS_MOUSE_MIDDLE_BUTTON_DOWN:
03132     mLastMiddleMouseDownContent = mouseContent;
03133     break;
03134 
03135   case NS_MOUSE_MIDDLE_BUTTON_UP:
03136     if (mLastMiddleMouseDownContent == mouseContent) {
03137       aEvent->clickCount = mMClickCount;
03138       mMClickCount = 0;
03139     }
03140     else {
03141       aEvent->clickCount = 0;
03142     }
03143     break;
03144 
03145   case NS_MOUSE_RIGHT_BUTTON_DOWN:
03146     mLastRightMouseDownContent = mouseContent;
03147     break;
03148 
03149   case NS_MOUSE_RIGHT_BUTTON_UP:
03150     if (mLastRightMouseDownContent == mouseContent) {
03151       aEvent->clickCount = mRClickCount;
03152       mRClickCount = 0;
03153     }
03154     else {
03155       aEvent->clickCount = 0;
03156     }
03157     break;
03158   }
03159 
03160   return ret;
03161 }
03162 
03163 nsresult
03164 nsEventStateManager::CheckForAndDispatchClick(nsPresContext* aPresContext,
03165                                               nsMouseEvent *aEvent,
03166                                               nsEventStatus* aStatus)
03167 {
03168   nsresult ret = NS_OK;
03169   PRUint32 eventMsg = 0;
03170   PRInt32 flags = NS_EVENT_FLAG_INIT;
03171 
03172   //If mouse is still over same element, clickcount will be > 1.
03173   //If it has moved it will be zero, so no click.
03174   if (0 != aEvent->clickCount) {
03175     //fire click
03176     switch (aEvent->message) {
03177     case NS_MOUSE_LEFT_BUTTON_UP:
03178       eventMsg = NS_MOUSE_LEFT_CLICK;
03179       break;
03180     case NS_MOUSE_MIDDLE_BUTTON_UP:
03181       eventMsg = NS_MOUSE_MIDDLE_CLICK;
03182       flags |= sLeftClickOnly ? NS_EVENT_FLAG_NO_CONTENT_DISPATCH : NS_EVENT_FLAG_NONE;
03183       break;
03184     case NS_MOUSE_RIGHT_BUTTON_UP:
03185       eventMsg = NS_MOUSE_RIGHT_CLICK;
03186       flags |= sLeftClickOnly ? NS_EVENT_FLAG_NO_CONTENT_DISPATCH : NS_EVENT_FLAG_NONE;
03187       break;
03188     }
03189 
03190     nsMouseEvent event(NS_IS_TRUSTED_EVENT(aEvent), eventMsg, aEvent->widget,
03191                        nsMouseEvent::eReal);
03192     event.point = aEvent->point;
03193     event.refPoint = aEvent->refPoint;
03194     event.clickCount = aEvent->clickCount;
03195     event.isShift = aEvent->isShift;
03196     event.isControl = aEvent->isControl;
03197     event.isAlt = aEvent->isAlt;
03198     event.isMeta = aEvent->isMeta;
03199     event.time = aEvent->time;
03200 
03201     nsCOMPtr<nsIPresShell> presShell = mPresContext->GetPresShell();
03202     if (presShell) {
03203       nsCOMPtr<nsIContent> mouseContent;
03204       GetEventTargetContent(aEvent, getter_AddRefs(mouseContent));
03205 
03206       ret = presShell->HandleEventWithTarget(&event, mCurrentTarget, mouseContent, flags, aStatus);
03207       if (NS_SUCCEEDED(ret) && aEvent->clickCount == 2) {
03208         eventMsg = 0;
03209         //fire double click
03210         switch (aEvent->message) {
03211         case NS_MOUSE_LEFT_BUTTON_UP:
03212           eventMsg = NS_MOUSE_LEFT_DOUBLECLICK;
03213           break;
03214         case NS_MOUSE_MIDDLE_BUTTON_UP:
03215           eventMsg = NS_MOUSE_MIDDLE_DOUBLECLICK;
03216           break;
03217         case NS_MOUSE_RIGHT_BUTTON_UP:
03218           eventMsg = NS_MOUSE_RIGHT_DOUBLECLICK;
03219           break;
03220         }
03221 
03222         nsMouseEvent event2(NS_IS_TRUSTED_EVENT(aEvent), eventMsg,
03223                             aEvent->widget, nsMouseEvent::eReal);
03224         event2.point = aEvent->point;
03225         event2.refPoint = aEvent->refPoint;
03226         event2.clickCount = aEvent->clickCount;
03227         event2.isShift = aEvent->isShift;
03228         event2.isControl = aEvent->isControl;
03229         event2.isAlt = aEvent->isAlt;
03230         event2.isMeta = aEvent->isMeta;
03231 
03232         ret = presShell->HandleEventWithTarget(&event2, mCurrentTarget,
03233                                                mouseContent, flags, aStatus);
03234       }
03235     }
03236   }
03237   return ret;
03238 }
03239 
03240 NS_IMETHODIMP
03241 nsEventStateManager::ChangeFocusWith(nsIContent* aFocusContent,
03242                                      EFocusedWithType aFocusedWith)
03243 {
03244   mLastFocusedWith = aFocusedWith;
03245   if (!aFocusContent) {
03246     SetContentState(nsnull, NS_EVENT_STATE_FOCUS);
03247     return NS_OK;
03248   }
03249 
03250   // Get focus controller.
03251   EnsureDocument(mPresContext);
03252   nsCOMPtr<nsIFocusController> focusController = nsnull;
03253   nsCOMPtr<nsPIDOMWindow> window =
03254     do_QueryInterface(GetDocumentOuterWindow(mDocument));
03255   if (window)
03256     focusController = window->GetRootFocusController();
03257 
03258   // If this is called from mouse event, we lock to scroll.
03259   // Because the part of element is always in view. See bug 105894.
03260   PRBool suppressFocusScroll =
03261     focusController && (aFocusedWith == eEventFocusedByMouse);
03262   if (suppressFocusScroll) {
03263     PRBool currentState = PR_FALSE;
03264     focusController->GetSuppressFocusScroll(&currentState);
03265     NS_ASSERTION(!currentState, "locked scroll already!");
03266     focusController->SetSuppressFocusScroll(PR_TRUE);
03267   }
03268 
03269   aFocusContent->SetFocus(mPresContext);
03270   if (aFocusedWith != eEventFocusedByMouse) {
03271     MoveCaretToFocus();
03272     // Select text fields when focused via keyboard (tab or accesskey)
03273     if (sTextfieldSelectModel == eTextfieldSelect_auto &&
03274         mCurrentFocus &&
03275         mCurrentFocus->IsContentOfType(nsIContent::eHTML_FORM_CONTROL)) {
03276       nsCOMPtr<nsIFormControl> formControl(do_QueryInterface(mCurrentFocus));
03277       PRInt32 controlType = formControl->GetType();
03278       if (controlType == NS_FORM_INPUT_TEXT ||
03279           controlType == NS_FORM_INPUT_PASSWORD) {
03280         nsCOMPtr<nsIDOMHTMLInputElement> inputElement =
03281           do_QueryInterface(mCurrentFocus);
03282         if (inputElement) {
03283           inputElement->Select();
03284         }
03285       }
03286     }
03287   }
03288 
03289   // Unlock scroll
03290   if (suppressFocusScroll)
03291     focusController->SetSuppressFocusScroll(PR_FALSE);
03292 
03293   return NS_OK;
03294 }
03295 
03296 //---------------------------------------------------------
03297 // Debug Helpers
03298 #ifdef DEBUG_DOCSHELL_FOCUS
03299 static void
03300 PrintDocTree(nsIDocShellTreeNode * aParentNode, int aLevel)
03301 {
03302   for (PRInt32 i=0;i<aLevel;i++) printf("  ");
03303 
03304   PRInt32 childWebshellCount;
03305   aParentNode->GetChildCount(&childWebshellCount);
03306   nsCOMPtr<nsIDocShell> parentAsDocShell(do_QueryInterface(aParentNode));
03307   nsCOMPtr<nsIDocShellTreeItem> parentAsItem(do_QueryInterface(aParentNode));
03308   PRInt32 type;
03309   parentAsItem->GetItemType(&type);
03310   nsCOMPtr<nsIPresShell> presShell;
03311   parentAsDocShell->GetPresShell(getter_AddRefs(presShell));
03312   nsCOMPtr<nsPresContext> presContext;
03313   parentAsDocShell->GetPresContext(getter_AddRefs(presContext));
03314   nsIDocument *doc = presShell->GetDocument();
03315 
03316   nsCOMPtr<nsIDOMWindowInternal> domwin =
03317     do_QueryInterface(GetDocumentOuterWindow(doc));
03318 
03319   nsCOMPtr<nsIWidget> widget;
03320   nsIViewManager* vm = presShell->GetViewManager();
03321   if (vm) {
03322     vm->GetWidget(getter_AddRefs(widget));
03323   }
03324 
03325   printf("DS %p  Type %s  Cnt %d  Doc %p  DW %p  EM %p\n",
03326     parentAsDocShell.get(),
03327     type==nsIDocShellTreeItem::typeChrome?"Chrome":"Content",
03328     childWebshellCount, doc, domwin.get(),
03329     presContext->EventStateManager());
03330 
03331   if (childWebshellCount > 0) {
03332     for (PRInt32 i=0;i<childWebshellCount;i++) {
03333       nsCOMPtr<nsIDocShellTreeItem> child;
03334       aParentNode->GetChildAt(i, getter_AddRefs(child));
03335       nsCOMPtr<nsIDocShellTreeNode> childAsNode(do_QueryInterface(child));
03336       PrintDocTree(childAsNode, aLevel+1);
03337     }
03338   }
03339 }
03340 #endif // end debug helpers
03341 
03342 NS_IMETHODIMP
03343 nsEventStateManager::ShiftFocus(PRBool aForward, nsIContent* aStart)
03344 {
03345   nsCOMPtr<nsILookAndFeel> lookNFeel(do_GetService(kLookAndFeelCID));
03346   lookNFeel->GetMetric(nsILookAndFeel::eMetric_TabFocusModel,
03347                        nsIContent::sTabFocusModel);
03348 
03349   // We use mTabbedThroughDocument to indicate that we have passed
03350   // the end (or beginning) of the document we started tabbing from,
03351   // without finding anything else to focus.  If we pass the end of
03352   // the same document again (and the flag is set), we know that there
03353   // is no focusable content anywhere in the tree, and should stop.
03354 
03355   mTabbedThroughDocument = PR_FALSE;
03356   return ShiftFocusInternal(aForward, aStart);
03357 }
03358 
03359 nsresult
03360 nsEventStateManager::ShiftFocusInternal(PRBool aForward, nsIContent* aStart)
03361 {
03362 #ifdef DEBUG_DOCSHELL_FOCUS
03363   printf("[%p] ShiftFocusInternal: aForward=%d, aStart=%p, mCurrentFocus=%p\n",
03364          this, aForward, aStart, mCurrentFocus.get());
03365 #endif
03366   NS_ASSERTION(mPresContext, "no pres context");
03367   EnsureDocument(mPresContext);
03368   NS_ASSERTION(mDocument, "no document");
03369 
03370   nsCOMPtr<nsIContent> rootContent = mDocument->GetRootContent();
03371 
03372   nsCOMPtr<nsISupports> pcContainer = mPresContext->GetContainer();
03373   NS_ASSERTION(pcContainer, "no container for presContext");
03374 
03375   nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(pcContainer));
03376   PRBool docHasFocus = PR_FALSE;
03377 
03378   // ignoreTabIndex allows the user to tab to the next link after clicking before it link in the page
03379   // or using find text to get to the link. Without ignoreTabIndex in those cases, pages that
03380   // use tabindex would still enforce that order in those situations.
03381   PRBool ignoreTabIndex = PR_FALSE;
03382 
03383   if (!aStart && !mCurrentFocus) {
03384     // mCurrentFocus is ambiguous for determining whether
03385     // we're in document-focus mode, because it's nulled out
03386     // when the document is blurred, and it's also nulled out
03387     // when the document/canvas has focus.
03388     //
03389     // So, use the docshell focus state to disambiguate.
03390 
03391     docShell->GetHasFocus(&docHasFocus);
03392   }
03393 
03394   nsIFrame* selectionFrame = nsnull;
03395   nsIFrame* curFocusFrame = nsnull;   // This will hold the location we're moving away from
03396 
03397   // If in content, navigate from last cursor position rather than last focus
03398   // If we're in UI, selection location will return null
03399   nsIPresShell *presShell = mPresContext->PresShell();
03400 
03401   // We might use the selection position, rather than mCurrentFocus, as our position to shift focus from
03402   PRInt32 itemType;
03403   nsCOMPtr<nsIDocShellTreeItem> shellItem(do_QueryInterface(docShell));
03404   shellItem->GetItemType(&itemType);
03405 
03406   // Tab from the selection if it exists, but not if we're in chrome or an explicit starting
03407   // point was given.
03408   if (!aStart && itemType != nsIDocShellTreeItem::typeChrome) {
03409     // We're going to tab from the selection position
03410     if (!mCurrentFocus || (mLastFocusedWith != eEventFocusedByMouse && mCurrentFocus->Tag() != nsHTMLAtoms::area)) {
03411       nsCOMPtr<nsIContent> selectionContent, endSelectionContent;  // We won't be using this, need arg for method call
03412       PRUint32 selectionOffset; // We won't be using this either, need arg for method call
03413       GetDocSelectionLocation(getter_AddRefs(selectionContent), getter_AddRefs(endSelectionContent), &selectionFrame, &selectionOffset);
03414       if (selectionContent == rootContent)  // If selection on rootContent, same as null -- we have no selection yet
03415         selectionFrame = nsnull;
03416       // Only use tabindex if selection is synchronized with focus
03417       // That way, if the user clicks in content, or does a find text that lands between focusable elements,
03418       // they can then tab relative to that selection
03419       if (selectionFrame) {
03420         PRBool selectionWithFocus;
03421         MoveFocusToCaret(PR_FALSE, &selectionWithFocus);
03422         ignoreTabIndex = !selectionWithFocus;
03423         // Refresh |selectionFrame| since MoveFocusToCaret() could have
03424         // destroyed it. (bug 308086)
03425         GetDocSelectionLocation(getter_AddRefs(selectionContent),
03426                                 getter_AddRefs(endSelectionContent),
03427                                 &selectionFrame, &selectionOffset);
03428       }
03429     }
03430   }
03431 
03432   nsIContent *startContent = nsnull;
03433 
03434   if (aStart) {
03435     presShell->GetPrimaryFrameFor(aStart, &curFocusFrame);
03436 
03437     // If there is no frame, we can't navigate from this content node, and we
03438     // fall back to navigating from the document root.
03439     if (curFocusFrame)
03440       startContent = aStart;
03441   } else if (selectionFrame) {
03442     // We moved focus to the caret location above, so mCurrentFocus
03443     // reflects the starting content node.
03444     startContent = mCurrentFocus;
03445     curFocusFrame = selectionFrame;
03446   } else if (!docHasFocus) {
03447     startContent = mCurrentFocus;
03448     GetFocusedFrame(&curFocusFrame);
03449   }
03450 
03451   if (aStart) {
03452     if (aStart->HasAttr(kNameSpaceID_None, nsHTMLAtoms::tabindex)) {
03453       aStart->IsFocusable(&mCurrentTabIndex);
03454     } else {
03455       ignoreTabIndex = PR_TRUE; // ignore current tabindex, bug 81481
03456     }
03457   } else if (!mCurrentFocus) {  // Get tabindex ready
03458     if (aForward) {
03459       mCurrentTabIndex = docHasFocus && selectionFrame ? 0 : 1;
03460     } else if (!docHasFocus) {
03461       mCurrentTabIndex = 0;
03462     } else if (selectionFrame) {
03463       mCurrentTabIndex = 1;   // will keep it from wrapping around to end
03464     }
03465   }
03466 
03467   nsCOMPtr<nsIContent> nextFocus;
03468   nsIFrame* nextFocusFrame;
03469   if (aForward || !docHasFocus || selectionFrame)
03470     GetNextTabbableContent(rootContent, startContent, curFocusFrame,
03471                            aForward, ignoreTabIndex || mCurrentTabIndex < 0,
03472                            getter_AddRefs(nextFocus), &nextFocusFrame);
03473 
03474   // Clear out mCurrentTabIndex. It has a garbage value because of GetNextTabbableContent()'s side effects
03475   // It will be set correctly when focus is changed via ChangeFocusWith()
03476   mCurrentTabIndex = 0;
03477 
03478   if (nextFocus) {
03479     // Check to see if the next focused element has a subshell.
03480     // This is the case for an IFRAME or FRAME element.  If it
03481     // does, we send focus into the subshell.
03482 
03483     nsCOMPtr<nsIDocShell> sub_shell;
03484     nsCOMPtr<nsIDocument> doc = nextFocus->GetDocument();
03485 
03486     if (doc) {
03487       nsIDocument *sub_doc = doc->GetSubDocumentFor(nextFocus);
03488 
03489       if (sub_doc) {
03490         nsCOMPtr<nsISupports> container = sub_doc->GetContainer();
03491         sub_shell = do_QueryInterface(container);
03492       }
03493     }
03494 
03495     if (sub_shell) {
03496       // Make sure to scroll before possibly dispatching focus/blur events.
03497       presShell->ScrollFrameIntoView(nextFocusFrame,
03498                                      NS_PRESSHELL_SCROLL_ANYWHERE,
03499                                      NS_PRESSHELL_SCROLL_ANYWHERE);
03500 
03501       SetContentState(nsnull, NS_EVENT_STATE_FOCUS);
03502 
03503       // if we are in the middle of tabbing into
03504       // sub_shell, bail out, to avoid recursion
03505       // see bug #195011 and bug #137191
03506       if (mTabbingFromDocShells.IndexOf(sub_shell) != -1)
03507         return NS_OK;
03508 
03509       TabIntoDocument(sub_shell, aForward);
03510     } else {
03511       // there is no subshell, so just focus nextFocus
03512 #ifdef DEBUG_DOCSHELL_FOCUS
03513       printf("focusing next focusable content: %p\n", nextFocus.get());
03514 #endif
03515       mCurrentTarget = nextFocusFrame;
03516 
03517       //This may be new frame that hasn't been through the ESM so we
03518       //must set its NS_FRAME_EXTERNAL_REFERENCE bit.
03519       if (mCurrentTarget)
03520         SetFrameExternalReference(mCurrentTarget);
03521 
03522       nsCOMPtr<nsIContent> oldFocus(mCurrentFocus);
03523       ChangeFocusWith(nextFocus, eEventFocusedByKey);
03524       if (!mCurrentFocus && oldFocus) {
03525         // ChangeFocusWith failed to move focus to nextFocus because a blur handler
03526         // made it unfocusable. (bug #118685)
03527         // Try again unless it's from the same point, bug 232368.
03528         if (oldFocus != aStart && oldFocus->GetDocument()) {
03529           mCurrentTarget = nsnull;
03530           return ShiftFocusInternal(aForward, oldFocus);
03531         } else {
03532           return NS_OK;
03533         }
03534       } else {
03535         if (mCurrentFocus != nextFocus) {
03536           // A focus or blur handler switched the focus from one of
03537           // its focus/blur/change handlers, don't mess with what the
03538           // page wanted...
03539 
03540           return NS_OK;
03541         }
03542         GetFocusedFrame(&mCurrentTarget);
03543         if (mCurrentTarget)
03544           SetFrameExternalReference(mCurrentTarget);
03545       }
03546 
03547       // It's possible that the act of removing focus from our previously
03548       // focused element caused nextFocus to be removed from the document.
03549       // In this case, we can restart the frame traversal from our previously
03550       // focused content.
03551 
03552       if (oldFocus && doc != nextFocus->GetDocument()) {
03553         mCurrentTarget = nsnull;
03554         return ShiftFocusInternal(aForward, oldFocus);
03555       }
03556 
03557       if (!docHasFocus)
03558         docShell->SetHasFocus(PR_TRUE);
03559     }
03560   } else {
03561 
03562     // If we're going backwards past the first content,
03563     // focus the document.
03564 
03565     PRBool focusDocument;
03566     if (itemType == nsIDocShellTreeItem::typeChrome)
03567       focusDocument = PR_FALSE;
03568     else {
03569       // Check for a frameset document
03570       focusDocument = !(IsFrameSetDoc(docShell));
03571     }
03572 
03573     if (!aForward && !docHasFocus && focusDocument) {
03574 #ifdef DEBUG_DOCSHELL_FOCUS
03575       printf("Focusing document\n");
03576 #endif
03577       SetContentState(nsnull, NS_EVENT_STATE_FOCUS);
03578       docShell->SetHasFocus(PR_TRUE);
03579       docShell->SetCanvasHasFocus(PR_TRUE);
03580       // Next time forward we start at the beginning of the document
03581       // Next time backward we go to URL bar
03582       // We need to move the caret to the document root, so that we don't
03583       // tab from the most recently focused element next time around
03584       SetFocusedContent(rootContent);
03585       MoveCaretToFocus();
03586       SetFocusedContent(nsnull);
03587 
03588     } else {
03589       // If there's nothing left to focus in this document,
03590       // pop out to our parent document, and have it shift focus
03591       // in the same direction starting at the content element
03592       // corresponding to our docshell.
03593       // Guard against infinite recursion (see explanation in ShiftFocus)
03594 
03595       if (mTabbedThroughDocument)
03596         return NS_OK;
03597 
03598       SetFocusedContent(rootContent);
03599       mCurrentTabIndex = 0;
03600       MoveCaretToFocus();
03601       SetFocusedContent(nsnull);
03602 
03603       mTabbedThroughDocument = PR_TRUE;
03604 
03605       nsCOMPtr<nsIDocShellTreeItem> treeItem = do_QueryInterface(pcContainer);
03606       nsCOMPtr<nsIDocShellTreeItem> treeParent;
03607       treeItem->GetParent(getter_AddRefs(treeParent));
03608       if (treeParent) {
03609         nsCOMPtr<nsIDocShell> parentDS = do_QueryInterface(treeParent);
03610         if (parentDS) {
03611           nsCOMPtr<nsIPresShell> parentShell;
03612           parentDS->GetPresShell(getter_AddRefs(parentShell));
03613 
03614           nsIDocument *parent_doc = parentShell->GetDocument();
03615           nsIContent *docContent = parent_doc->FindContentForSubDocument(mDocument);
03616 
03617           nsCOMPtr<nsPresContext> parentPC = parentShell->GetPresContext();
03618           nsIEventStateManager *parentESM = parentPC->EventStateManager();
03619 
03620           SetContentState(nsnull, NS_EVENT_STATE_FOCUS);
03621 
03622 #ifdef DEBUG_DOCSHELL_FOCUS
03623           printf("popping out focus to parent docshell\n");
03624 #endif
03625           parentESM->MoveCaretToFocus();
03626           parentESM->ShiftFocus(aForward, docContent);
03627         }
03628       } else {
03629         PRBool tookFocus = PR_FALSE;
03630         nsCOMPtr<nsIDocShell> subShell = do_QueryInterface(pcContainer);
03631         if (subShell) {
03632           subShell->TabToTreeOwner(aForward, &tookFocus);
03633         }
03634 
03635 #ifdef DEBUG_DOCSHEL_FOCUS
03636         printf("offered focus to tree owner, tookFocus=%d\n",
03637                tookFocus);
03638 #endif
03639 
03640         if (tookFocus) {
03641           SetContentState(nsnull, NS_EVENT_STATE_FOCUS);
03642           docShell->SetHasFocus(PR_FALSE);
03643         } else {
03644           // there is nowhere else to send the focus, so
03645           // refocus ourself.
03646           // Next time forward we start at the beginning of the document
03647 
03648 #ifdef DEBUG_DOCSHELL_FOCUS
03649           printf("wrapping around within this document\n");
03650 #endif
03651 
03652           SetFocusedContent(nsnull);
03653           docShell->SetHasFocus(PR_FALSE);
03654           ShiftFocusInternal(aForward);
03655         }
03656       }
03657     }
03658   }
03659 
03660   return NS_OK;
03661 }
03662 
03663 nsresult
03664 nsEventStateManager::GetNextTabbableContent(nsIContent* aRootContent,
03665                                             nsIContent* aStartContent,
03666                                             nsIFrame* aStartFrame,
03667                                             PRBool forward,
03668                                             PRBool aIgnoreTabIndex,
03669                                             nsIContent** aResultNode,
03670                                             nsIFrame** aResultFrame)
03671 {
03672   *aResultNode = nsnull;
03673   *aResultFrame = nsnull;
03674 
03675   nsresult rv;
03676   nsCOMPtr<nsIFrameTraversal> trav(do_CreateInstance(kFrameTraversalCID, &rv));
03677   NS_ENSURE_SUCCESS(rv, rv);
03678   nsCOMPtr<nsIBidirectionalEnumerator> frameTraversal;
03679 
03680   // --- Get frame to start with ---
03681   if (!aStartFrame) {
03682     // No frame means we need to start with the root content again.
03683     NS_ENSURE_TRUE(mPresContext, NS_ERROR_FAILURE);
03684     nsIPresShell *presShell = mPresContext->GetPresShell();
03685     NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
03686     presShell->GetPrimaryFrameFor(aRootContent, &aStartFrame);
03687     NS_ENSURE_TRUE(aStartFrame, NS_ERROR_FAILURE);
03688     rv = trav->NewFrameTraversal(getter_AddRefs(frameTraversal), FOCUS,
03689                                 mPresContext, aStartFrame);
03690     NS_ENSURE_SUCCESS(rv, rv);
03691     if (!forward) {
03692       rv = frameTraversal->Last();
03693     }
03694   }
03695   else {
03696     rv = trav->NewFrameTraversal(getter_AddRefs(frameTraversal), FOCUS,
03697                                 mPresContext, aStartFrame);
03698     NS_ENSURE_SUCCESS(rv, rv);
03699     if (!aStartContent || aStartContent->Tag() != nsHTMLAtoms::area ||
03700         !aStartContent->IsContentOfType(nsIContent::eHTML)) {
03701       // Need to do special check in case we're in an imagemap which has multiple
03702       // content per frame, so don't skip over the starting frame.
03703       rv = forward ? frameTraversal->Next() : frameTraversal->Prev();
03704     }
03705   }
03706 
03707   // -- Walk frames to find something tabbable matching mCurrentTabIndex --
03708   while (NS_SUCCEEDED(rv)) {
03709     nsISupports* currentItem;
03710     frameTraversal->CurrentItem(&currentItem);
03711     *aResultFrame = (nsIFrame*)currentItem;
03712     if (!*aResultFrame) {
03713       break;
03714     }
03715 
03716     // TabIndex not set defaults to 0 for form elements, anchors and other
03717     // elements that are normally focusable. Tabindex defaults to -1
03718     // for elements that are not normally focusable.
03719     // The returned computed tabindex from IsFocusable() is as follows:
03720     //          < 0 not tabbable at all
03721     //          == 0 in normal tab order (last after positive tabindex'd items)
03722     //          > 0 can be tabbed to in the order specified by this value
03723     PRInt32 tabIndex;
03724     nsIContent* currentContent = (*aResultFrame)->GetContent();
03725     (*aResultFrame)->IsFocusable(&tabIndex);
03726     if (tabIndex >= 0) {
03727       if (currentContent->Tag() == nsHTMLAtoms::img &&
03728           currentContent->HasAttr(kNameSpaceID_None, nsHTMLAtoms::usemap)) {
03729         // Must be an image w/ a map -- it's tabbable but no tabindex is specified
03730         // Special case for image maps: they don't get walked by nsIFrameTraversal
03731         nsIContent *areaContent = GetNextTabbableMapArea(forward, currentContent);
03732         if (areaContent) {
03733           NS_ADDREF(*aResultNode = areaContent);
03734           return NS_OK;
03735         }
03736       }
03737       else if ((aIgnoreTabIndex || mCurrentTabIndex == tabIndex) &&
03738           currentContent != aStartContent) {
03739         NS_ADDREF(*aResultNode = currentContent);
03740         return NS_OK;
03741       }
03742     }
03743     rv = forward ? frameTraversal->Next() : frameTraversal->Prev();
03744   }
03745 
03746   // -- Reached end or beginning of document --
03747 
03748   // If already at lowest priority tab (0), end search completely.
03749   // A bit counterintuitive but true, tabindex order goes 1, 2, ... 32767, 0
03750   if (mCurrentTabIndex == (forward? 0: 1)) {
03751     return NS_OK;
03752   }
03753 
03754   // else continue looking for next highest priority tabindex
03755   mCurrentTabIndex = GetNextTabIndex(aRootContent, forward);
03756   return GetNextTabbableContent(aRootContent, aStartContent, nsnull, forward,
03757                                 aIgnoreTabIndex, aResultNode, aResultFrame);
03758 }
03759 
03760 nsIContent*
03761 nsEventStateManager::GetNextTabbableMapArea(PRBool aForward,
03762                                             nsIContent *aImageContent)
03763 {
03764   nsAutoString useMap;
03765   aImageContent->GetAttr(kNameSpaceID_None, nsHTMLAtoms::usemap, useMap);
03766 
03767   nsCOMPtr<nsIDocument> doc = aImageContent->GetDocument();
03768   if (doc) {
03769     nsCOMPtr<nsIDOMHTMLMapElement> imageMap = nsImageMapUtils::FindImageMap(doc, useMap);
03770     nsCOMPtr<nsIContent> mapContent = do_QueryInterface(imageMap);
03771     PRUint32 count = mapContent->GetChildCount();
03772     // First see if mCurrentFocus is in this map
03773     PRInt32 index = mapContent->IndexOf(mCurrentFocus);
03774     PRInt32 tabIndex;
03775     if (index < 0 || (mCurrentFocus->IsFocusable(&tabIndex) &&
03776                       tabIndex != mCurrentTabIndex)) {
03777       // If mCurrentFocus is in this map we must start iterating past it.
03778       // We skip the case where mCurrentFocus has tabindex == mCurrentTabIndex
03779       // since the next tab ordered element might be before it
03780       // (or after for backwards) in the child list.
03781       index = aForward ? -1 : (PRInt32)count;
03782     }
03783 
03784     // GetChildAt will return nsnull if our index < 0 or index >= count
03785     nsCOMPtr<nsIContent> areaContent;
03786     while ((areaContent = mapContent->GetChildAt(aForward? ++index : --index)) != nsnull) {
03787       if (areaContent->IsFocusable(&tabIndex) && tabIndex == mCurrentTabIndex) {
03788         return areaContent;
03789       }
03790     }
03791   }
03792 
03793   return nsnull;
03794 }
03795 
03796 PRInt32
03797 nsEventStateManager::GetNextTabIndex(nsIContent* aParent, PRBool forward)
03798 {
03799   PRInt32 tabIndex, childTabIndex;
03800   nsIContent *child;
03801 
03802   PRUint32 count = aParent->GetChildCount();
03803 
03804   if (forward) {
03805     tabIndex = 0;
03806     for (PRUint32 index = 0; index < count; index++) {
03807       child = aParent->GetChildAt(index);
03808       childTabIndex = GetNextTabIndex(child, forward);
03809       if (childTabIndex > mCurrentTabIndex && childTabIndex != tabIndex) {
03810         tabIndex = (tabIndex == 0 || childTabIndex < tabIndex) ? childTabIndex : tabIndex;
03811       }
03812 
03813       nsAutoString tabIndexStr;
03814       child->GetAttr(kNameSpaceID_None, nsHTMLAtoms::tabindex, tabIndexStr);
03815       PRInt32 ec, val = tabIndexStr.ToInteger(&ec);
03816       if (NS_SUCCEEDED (ec) && val > mCurrentTabIndex && val != tabIndex) {
03817         tabIndex = (tabIndex == 0 || val < tabIndex) ? val : tabIndex;
03818       }
03819     }
03820   }
03821   else { /* !forward */
03822     tabIndex = 1;
03823     for (PRUint32 index = 0; index < count; index++) {
03824       child = aParent->GetChildAt(index);
03825       childTabIndex = GetNextTabIndex(child, forward);
03826       if ((mCurrentTabIndex == 0 && childTabIndex > tabIndex) ||
03827           (childTabIndex < mCurrentTabIndex && childTabIndex > tabIndex)) {
03828         tabIndex = childTabIndex;
03829       }
03830 
03831       nsAutoString tabIndexStr;
03832       child->GetAttr(kNameSpaceID_None, nsHTMLAtoms::tabindex, tabIndexStr);
03833       PRInt32 ec, val = tabIndexStr.ToInteger(&ec);
03834       if (NS_SUCCEEDED (ec)) {
03835         if ((mCurrentTabIndex == 0 && val > tabIndex) ||
03836             (val < mCurrentTabIndex && val > tabIndex) ) {
03837           tabIndex = val;
03838         }
03839       }
03840     }
03841   }
03842   return tabIndex;
03843 }
03844 
03845 NS_IMETHODIMP
03846 nsEventStateManager::GetEventTarget(nsIFrame **aFrame)
03847 {
03848   if (!mCurrentTarget && mCurrentTargetContent) {
03849     if (mPresContext) {
03850       nsIPresShell *shell = mPresContext->GetPresShell();
03851       if (shell) {
03852         shell->GetPrimaryFrameFor(mCurrentTargetContent, &mCurrentTarget);
03853 
03854         //This may be new frame that hasn't been through the ESM so we
03855         //must set its NS_FRAME_EXTERNAL_REFERENCE bit.
03856         if (mCurrentTarget)
03857           SetFrameExternalReference(mCurrentTarget);
03858       }
03859     }
03860   }
03861 
03862   if (!mCurrentTarget) {
03863     nsIPresShell *presShell = mPresContext->GetPresShell();
03864     if (presShell) {
03865       presShell->GetEventTargetFrame(&mCurrentTarget);
03866 
03867       //This may be new frame that hasn't been through the ESM so we
03868       //must set its NS_FRAME_EXTERNAL_REFERENCE bit.
03869       if (mCurrentTarget)
03870         SetFrameExternalReference(mCurrentTarget);
03871     }
03872   }
03873 
03874   *aFrame = mCurrentTarget;
03875   return NS_OK;
03876 }
03877 
03878 NS_IMETHODIMP
03879 nsEventStateManager::GetEventTargetContent(nsEvent* aEvent,
03880                                            nsIContent** aContent)
03881 {
03882   if (aEvent &&
03883       (aEvent->message == NS_FOCUS_CONTENT ||
03884        aEvent->message == NS_BLUR_CONTENT)) {
03885     *aContent = mCurrentFocus;
03886     NS_IF_ADDREF(*aContent);
03887     return NS_OK;
03888   }
03889 
03890   if (mCurrentTargetContent) {
03891     *aContent = mCurrentTargetContent;
03892     NS_IF_ADDREF(*aContent);
03893     return NS_OK;
03894   }
03895 
03896   *aContent = nsnull;
03897 
03898   nsIPresShell *presShell = mPresContext->GetPresShell();
03899   if (presShell) {
03900     presShell->GetEventTargetContent(aEvent, aContent);
03901   }
03902 
03903   // Some events here may set mCurrentTarget but not set the corresponding
03904   // event target in the PresShell.
03905   if (!*aContent && mCurrentTarget) {
03906     mCurrentTarget->GetContentForEvent(mPresContext, aEvent, aContent);
03907   }
03908 
03909   return NS_OK;
03910 }
03911 
03912 NS_IMETHODIMP
03913 nsEventStateManager::GetEventRelatedContent(nsIContent** aContent)
03914 {
03915   *aContent = mCurrentRelatedContent;
03916   NS_IF_ADDREF(*aContent);
03917   return NS_OK;
03918 }
03919 
03920 NS_IMETHODIMP
03921 nsEventStateManager::GetContentState(nsIContent *aContent, PRInt32& aState)
03922 {
03923   aState = aContent->IntrinsicState();
03924 
03925   // Hierchical active:  Check the ancestor chain of mActiveContent to see
03926   // if we are on it.
03927   for (nsIContent* activeContent = mActiveContent; activeContent;
03928        activeContent = activeContent->GetParent()) {
03929     if (aContent == activeContent) {
03930       aState |= NS_EVENT_STATE_ACTIVE;
03931       break;
03932     }
03933   }
03934   // Hierchical hover:  Check the ancestor chain of mHoverContent to see
03935   // if we are on it.
03936   for (nsIContent* hoverContent = mHoverContent; hoverContent;
03937        hoverContent = hoverContent->GetParent()) {
03938     if (aContent == hoverContent) {
03939       aState |= NS_EVENT_STATE_HOVER;
03940       break;
03941     }
03942   }
03943 
03944   if (aContent == mCurrentFocus) {
03945     aState |= NS_EVENT_STATE_FOCUS;
03946   }
03947   if (aContent == mDragOverContent) {
03948     aState |= NS_EVENT_STATE_DRAGOVER;
03949   }
03950   if (aContent == mURLTargetContent) {
03951     aState |= NS_EVENT_STATE_URLTARGET;
03952   }
03953   return NS_OK;
03954 }
03955 
03956 static nsIContent* FindCommonAncestor(nsIContent *aNode1, nsIContent *aNode2)
03957 {
03958   // Find closest common ancestor
03959   if (aNode1 && aNode2) {
03960     // Find the nearest common ancestor by counting the distance to the
03961     // root and then walking up again, in pairs.
03962     PRInt32 offset = 0;
03963     nsIContent *anc1 = aNode1;
03964     for (;;) {
03965       ++offset;
03966       nsIContent* parent = anc1->GetParent();
03967       if (!parent)
03968         break;
03969       anc1 = parent;
03970     }
03971     nsIContent *anc2 = aNode2;
03972     for (;;) {
03973       --offset;
03974       nsIContent* parent = anc2->GetParent();
03975       if (!parent)
03976         break;
03977       anc2 = parent;
03978     }
03979     if (anc1 == anc2) {
03980       anc1 = aNode1;
03981       anc2 = aNode2;
03982       while (offset > 0) {
03983         anc1 = anc1->GetParent();
03984         --offset;
03985       }
03986       while (offset < 0) {
03987         anc2 = anc2->GetParent();
03988         ++offset;
03989       }
03990       while (anc1 != anc2) {
03991         anc1 = anc1->GetParent();
03992         anc2 = anc2->GetParent();
03993       }
03994       return anc1;
03995     }
03996   }
03997   return nsnull;
03998 }
03999 
04000 NS_IMETHODIMP
04001 nsEventStateManager::SetContentState(nsIContent *aContent, PRInt32 aState)
04002 {
04003   const PRInt32 maxNotify = 5;
04004   // We must initialize this array with memset for the sake of the boneheaded
04005   // OS X compiler.  See bug 134934.
04006   nsIContent  *notifyContent[maxNotify];
04007   memset(notifyContent, 0, sizeof(notifyContent));
04008 
04009   // check to see that this state is allowed by style. Check dragover too?
04010   // XXX This doesn't consider that |aState| is a bitfield.
04011   // XXX Is this even what we want?
04012   if (mCurrentTarget && (aState == NS_EVENT_STATE_ACTIVE || aState == NS_EVENT_STATE_HOVER))
04013   {
04014     const nsStyleUserInterface* ui = mCurrentTarget->GetStyleUserInterface();
04015     if (ui->mUserInput == NS_STYLE_USER_INPUT_NONE)
04016       return NS_OK;
04017   }
04018 
04019   if ((aState & NS_EVENT_STATE_DRAGOVER) && (aContent != mDragOverContent)) {
04020     notifyContent[3] = mDragOverContent; // notify dragover first, since more common case
04021     NS_IF_ADDREF(notifyContent[3]);
04022     mDragOverContent = aContent;
04023   }
04024 
04025   if ((aState & NS_EVENT_STATE_URLTARGET) && (aContent != mURLTargetContent)) {
04026     notifyContent[4] = mURLTargetContent;
04027     NS_IF_ADDREF(notifyContent[4]);
04028     mURLTargetContent = aContent;
04029   }
04030 
04031   nsCOMPtr<nsIContent> commonActiveAncestor, oldActive, newActive;
04032   if ((aState & NS_EVENT_STATE_ACTIVE) && (aContent != mActiveContent)) {
04033     oldActive = mActiveContent;
04034     newActive = aContent;
04035     commonActiveAncestor = FindCommonAncestor(mActiveContent, aContent);
04036     mActiveContent = aContent;
04037   }
04038 
04039   nsCOMPtr<nsIContent> commonHoverAncestor, oldHover, newHover;
04040   if ((aState & NS_EVENT_STATE_HOVER) && (aContent != mHoverContent)) {
04041     oldHover = mHoverContent;
04042     newHover = aContent;
04043     commonHoverAncestor = FindCommonAncestor(mHoverContent, aContent);
04044     mHoverContent = aContent;
04045   }
04046 
04047   if ((aState & NS_EVENT_STATE_FOCUS)) {
04048     EnsureDocument(mPresContext);
04049     if (aContent && (aContent == mCurrentFocus) && gLastFocusedDocument == mDocument) {
04050       // gLastFocusedDocument appears to always be correct, that is why
04051       // I'm not setting it here. This is to catch an edge case.
04052       NS_IF_RELEASE(gLastFocusedContent);
04053       gLastFocusedContent = mCurrentFocus;
04054       NS_IF_ADDREF(gLastFocusedContent);
04055       //If this notification was for focus alone then get rid of aContent
04056       //ref to avoid unnecessary notification.
04057       if (!(aState & ~NS_EVENT_STATE_FOCUS)) {
04058         aContent = nsnull;
04059       }
04060     } else {
04061       // see comments in ShiftFocusInternal on mCurrentFocus overloading
04062       PRBool fcActive = PR_FALSE;
04063       if (mDocument) {
04064         nsIFocusController *fc = GetFocusControllerForDocument(mDocument);
04065         if (fc)
04066           fc->GetActive(&fcActive);
04067       }
04068       notifyContent[2] = gLastFocusedContent;
04069       NS_IF_ADDREF(gLastFocusedContent);
04070       // only raise window if the the focus controller is active
04071       SendFocusBlur(mPresContext, aContent, fcActive);
04072 
04073 #ifdef DEBUG_aleventhal
04074       nsCOMPtr<nsPIDOMWindow> currentWindow =
04075         do_QueryInterface(GetDocumentOuterWindow(mDocument));
04076       if (currentWindow) {
04077         nsIFocusController *fc = currentWindow->GetRootFocusController();
04078         if (fc) {
04079           nsCOMPtr<nsIDOMElement> focusedElement;
04080           fc->GetFocusedElement(getter_AddRefs(focusedElement));
04081           if (!SameCOMIdentity(mCurrentFocus, focusedElement)) {
04082             printf("\n\nFocus out of whack!!!\n\n");
04083           }
04084         }
04085       }
04086 #endif
04087       // If we now have focused content, ensure that the canvas focus ring
04088       // is removed.
04089       if (mDocument) {
04090         nsCOMPtr<nsIDocShell> docShell =
04091           do_QueryInterface(nsCOMPtr<nsISupports>(mDocument->GetContainer()));
04092 
04093         if (docShell && mCurrentFocus)
04094           docShell->SetCanvasHasFocus(PR_FALSE);
04095 
04096 #if !defined(XP_WIN) && !defined(XP_OS2)
04097         // Also make sure that gLastFocusedDocument and gLastFocusedPresContext
04098         // aren't NULL if we have focused content.  Not doing this can mess up
04099         // the NS_GOTFOCUS case in nsEventStateManager::PreHandleEvent().
04100         // gLastFocusedDocument is NULLed by the call to SendFocusBlur() above.
04101         // gLastFocusedPresContext is NULLed by the NS_DEACTIVATE case in
04102         // nsEventStateManager::PreHandleEvent() (which is invoked switching
04103         // from one browser window to another).
04104         // Setting gLastFocusedDocument to mDocument here causes harm on
04105         // Windows -- it sometimes causes the wrong nsIContent to be focused in
04106         // the NS_LOSTFOCUS case of nsEventStateManager::PreHandleEvent() (as a
04107         // result of dispatching an NS_FOCUS_BLUR event to gLastFocusedContent).
04108         // Because the "bad" code in PreHandleEvent() is in an #ifdef block
04109         // that's only compiled on Windows and OS/2, we now only compile this
04110         // block on everything but Windows and OS/2.  This resolves bmo bug
04111         // 406214.
04112         if (!gLastFocusedDocument) {
04113           gLastFocusedDocument = mDocument;
04114           NS_ADDREF(gLastFocusedDocument);
04115         }
04116         if (!gLastFocusedPresContext) {
04117           gLastFocusedPresContext = mPresContext;
04118         }
04119 #endif
04120       }
04121     }
04122   }
04123 
04124   PRInt32 simpleStates = aState & ~(NS_EVENT_STATE_ACTIVE|NS_EVENT_STATE_HOVER);
04125 
04126   if (aContent && simpleStates != 0) {
04127     // notify about new content too
04128     notifyContent[0] = aContent;
04129     NS_ADDREF(aContent);  // everything in notify array has a ref
04130   }
04131 
04132   // remove duplicates
04133   if ((notifyContent[4] == notifyContent[3]) || (notifyContent[4] == notifyContent[2]) || (notifyContent[4] == notifyContent[1])) {
04134     NS_IF_RELEASE(notifyContent[4]);
04135   }
04136   // remove duplicates
04137   if ((notifyContent[3] == notifyContent[2]) || (notifyContent[3] == notifyContent[1])) {
04138     NS_IF_RELEASE(notifyContent[3]);
04139   }
04140   if (notifyContent[2] == notifyContent[1]) {
04141     NS_IF_RELEASE(notifyContent[2]);
04142   }
04143 
04144   // remove notifications for content not in document.
04145   // we may decide this is possible later but right now it has problems.
04146   for  (int i = 0; i < maxNotify; i++) {
04147     if (notifyContent[i] &&
04148         !notifyContent[i]->GetDocument()) {
04149       NS_RELEASE(notifyContent[i]);
04150     }
04151   }
04152 
04153   // compress the notify array to group notifications tighter
04154   nsIContent** from = &(notifyContent[0]);
04155   nsIContent** to   = &(notifyContent[0]);
04156   nsIContent** end  = &(notifyContent[maxNotify]);
04157 
04158   while (from < end) {
04159     if (! *from) {
04160       while (++from < end) {
04161         if (*from) {
04162           *to++ = *from;
04163           *from = nsnull;
04164           break;
04165         }
04166       }
04167     }
04168     else {
04169       if (from == to) {
04170         to++;
04171         from++;
04172       }
04173       else {
04174         *to++ = *from;
04175         *from++ = nsnull;
04176       }
04177     }
04178   }
04179 
04180   if (notifyContent[0] || newHover || oldHover || newActive || oldActive) {
04181     // have at least one to notify about
04182     nsCOMPtr<nsIDocument> doc1, doc2;  // this presumes content can't get/lose state if not connected to doc
04183     if (notifyContent[0]) {
04184       doc1 = notifyContent[0]->GetDocument();
04185       if (notifyContent[1]) {
04186         //For :focus this might be a different doc so check
04187         doc2 = notifyContent[1]->GetDocument();
04188         if (doc1 == doc2) {
04189           doc2 = nsnull;
04190         }
04191       }
04192     }
04193     else {
04194       EnsureDocument(mPresContext);
04195       doc1 = mDocument;
04196     }
04197     if (doc1) {
04198       doc1->BeginUpdate(UPDATE_CONTENT_STATE);
04199 
04200       // Notify all content from newActive to the commonActiveAncestor
04201       while (newActive && newActive != commonActiveAncestor) {
04202         doc1->ContentStatesChanged(newActive, nsnull, NS_EVENT_STATE_ACTIVE);
04203         newActive = newActive->GetParent();
04204       }
04205       // Notify all content from oldActive to the commonActiveAncestor
04206       while (oldActive && oldActive != commonActiveAncestor) {
04207         doc1->ContentStatesChanged(oldActive, nsnull, NS_EVENT_STATE_ACTIVE);
04208         oldActive = oldActive->GetParent();
04209       }
04210 
04211       // Notify all content from newHover to the commonHoverAncestor
04212       while (newHover && newHover != commonHoverAncestor) {
04213         doc1->ContentStatesChanged(newHover, nsnull, NS_EVENT_STATE_HOVER);
04214         newHover = newHover->GetParent();
04215       }
04216       // Notify all content from oldHover to the commonHoverAncestor
04217       while (oldHover && oldHover != commonHoverAncestor) {
04218         doc1->ContentStatesChanged(oldHover, nsnull, NS_EVENT_STATE_HOVER);
04219         oldHover = oldHover->GetParent();
04220       }
04221 
04222       if (notifyContent[0]) {
04223         doc1->ContentStatesChanged(notifyContent[0], notifyContent[1],
04224                                    simpleStates);
04225         if (notifyContent[2]) {
04226           // more that two notifications are needed (should be rare)
04227           // XXX a further optimization here would be to group the
04228           // notification pairs together by parent/child, only needed if
04229           // more than two content changed (ie: if [0] and [2] are
04230           // parent/child, then notify (0,2) (1,3))
04231           doc1->ContentStatesChanged(notifyContent[2], notifyContent[3],
04232                                      simpleStates);
04233           if (notifyContent[4]) {
04234             // more that four notifications are needed (should be rare)
04235             doc1->ContentStatesChanged(notifyContent[4], nsnull,
04236                                        simpleStates);
04237           }
04238         }
04239       }
04240       doc1->EndUpdate(UPDATE_CONTENT_STATE);
04241 
04242       if (doc2) {
04243         doc2->BeginUpdate(UPDATE_CONTENT_STATE);
04244         doc2->ContentStatesChanged(notifyContent[1], notifyContent[2],
04245                                    simpleStates);
04246         if (notifyContent[3]) {
04247           doc1->ContentStatesChanged(notifyContent[3], notifyContent[4],
04248                                      simpleStates);
04249         }
04250         doc2->EndUpdate(UPDATE_CONTENT_STATE);
04251       }
04252     }
04253 
04254     from = &(notifyContent[0]);
04255     while (from < to) {  // release old refs now that we are through
04256       nsIContent* notify = *from++;
04257       NS_RELEASE(notify);
04258     }
04259   }
04260 
04261   return NS_OK;
04262 }
04263 
04264 nsresult
04265 nsEventStateManager::SendFocusBlur(nsPresContext* aPresContext,
04266                                    nsIContent *aContent,
04267                                    PRBool aEnsureWindowHasFocus)
04268 {
04269   // Keep a ref to presShell since dispatching the DOM event may cause
04270   // the document to be destroyed.
04271   nsCOMPtr<nsIPresShell> presShell = aPresContext->PresShell();
04272   if (!presShell)
04273     return NS_OK;
04274 
04275   nsCOMPtr<nsIContent> previousFocus = mCurrentFocus;
04276 
04277   // Make sure previousFocus is in a document.  If it's not, then
04278   // we should never abort firing events based on what happens when we
04279   // send it a blur.
04280 
04281   if (previousFocus && !previousFocus->GetDocument())
04282     previousFocus = nsnull;
04283 
04284   // Track the old focus controller if any focus suppressions is used on it.
04285   nsFocusSuppressor oldFocusSuppressor;
04286   
04287   if (nsnull != gLastFocusedPresContext) {
04288 
04289     nsCOMPtr<nsIContent> focusAfterBlur;
04290 
04291     if (gLastFocusedContent && gLastFocusedContent != mFirstBlurEvent) {
04292 
04293       //Store the first blur event we fire and don't refire blur
04294       //to that element while the first blur is still ongoing.
04295       PRBool clearFirstBlurEvent = PR_FALSE;
04296       if (!mFirstBlurEvent) {
04297         mFirstBlurEvent = gLastFocusedContent;
04298         clearFirstBlurEvent = PR_TRUE;
04299       }
04300 
04301       // Retrieve this content node's pres context. it can be out of sync in
04302       // the Ender widget case.
04303       nsCOMPtr<nsIDocument> doc = gLastFocusedContent->GetDocument();
04304       if (doc) {
04305         // The order of the nsIViewManager and nsIPresShell COM pointers is
04306         // important below.  We want the pres shell to get released before the
04307         // associated view manager on exit from this function.
04308         // See bug 53763.
04309         nsCOMPtr<nsIViewManager> kungFuDeathGrip;
04310         nsIPresShell *shell = doc->GetShellAt(0);
04311         if (shell) {
04312           kungFuDeathGrip = shell->GetViewManager();
04313 
04314           nsCOMPtr<nsPresContext> oldPresContext = shell->GetPresContext();
04315 
04316           //fire blur
04317           nsEventStatus status = nsEventStatus_eIgnore;
04318           nsEvent event(PR_TRUE, NS_BLUR_CONTENT);
04319 
04320           EnsureDocument(presShell);
04321 
04322           // Make sure we're not switching command dispatchers, if so,
04323           // surpress the blurred one
04324           if(gLastFocusedDocument && mDocument) {
04325             nsCOMPtr<nsPIDOMWindow> newWindow =
04326               do_QueryInterface(GetDocumentOuterWindow(mDocument));
04327             if (newWindow) {
04328               nsIFocusController *newFocusController =
04329                 newFocusController = newWindow->GetRootFocusController();
04330               nsCOMPtr<nsPIDOMWindow> oldWindow =
04331                 do_QueryInterface(GetDocumentOuterWindow(gLastFocusedDocument));
04332               if (oldWindow) {
04333                 nsIFocusController *suppressed = oldWindow->GetRootFocusController();
04334                 if (suppressed != newFocusController) {
04335                   oldFocusSuppressor.Suppress(suppressed, "SendFocusBlur Window Switch #1");
04336                 }
04337               }
04338             }
04339           }
04340 
04341           nsCOMPtr<nsIEventStateManager> esm;
04342           esm = oldPresContext->EventStateManager();
04343           esm->SetFocusedContent(gLastFocusedContent);
04344           nsCOMPtr<nsIContent> temp = gLastFocusedContent;
04345           NS_RELEASE(gLastFocusedContent); // nulls out gLastFocusedContent
04346 
04347           nsCxPusher pusher;
04348           if (pusher.Push(temp)) {
04349             temp->HandleDOMEvent(oldPresContext, &event, nsnull,
04350                                  NS_EVENT_FLAG_INIT, &status);
04351             pusher.Pop();
04352           }
04353 
04354           focusAfterBlur = mCurrentFocus;
04355           if (!previousFocus || previousFocus == focusAfterBlur)
04356             esm->SetFocusedContent(nsnull);
04357         }
04358       }
04359 
04360       if (clearFirstBlurEvent) {
04361         mFirstBlurEvent = nsnull;
04362       }
04363 
04364       if (previousFocus && previousFocus != focusAfterBlur) {
04365         // The content node's blur handler focused something else.
04366         // In this case, abort firing any more blur or focus events.
04367         EnsureFocusSynchronization();
04368         return NS_OK;
04369       }
04370     }
04371 
04372     // Go ahead and fire a blur on the window.
04373     nsCOMPtr<nsIScriptGlobalObject> globalObject;
04374 
04375     if(gLastFocusedDocument)
04376       globalObject = GetDocumentOuterWindow(gLastFocusedDocument);
04377 
04378     EnsureDocument(presShell);
04379 
04380     if (gLastFocusedDocument && (gLastFocusedDocument != mDocument) && globalObject) {
04381       nsEventStatus status = nsEventStatus_eIgnore;
04382       nsEvent event(PR_TRUE, NS_BLUR_CONTENT);
04383 
04384       // Make sure we're not switching command dispatchers, if so,
04385       // suppress the blurred one if it isn't already suppressed
04386       if (mDocument && !oldFocusSuppressor.Suppressing()) {
04387         nsCOMPtr<nsPIDOMWindow> newWindow =
04388           do_QueryInterface(GetDocumentOuterWindow(mDocument));
04389 
04390         if (newWindow) {
04391           nsCOMPtr<nsPIDOMWindow> oldWindow =
04392             do_QueryInterface(GetDocumentOuterWindow(gLastFocusedDocument));
04393           nsIFocusController *newFocusController = newWindow->GetRootFocusController();
04394           if (oldWindow) {
04395             nsIFocusController *suppressed = oldWindow->GetRootFocusController();
04396             if (suppressed != newFocusController) {
04397               oldFocusSuppressor.Suppress(suppressed, "SendFocusBlur Window Switch #2");
04398             }
04399           }
04400         }
04401       }
04402 
04403       gLastFocusedPresContext->EventStateManager()->SetFocusedContent(nsnull);
04404       nsCOMPtr<nsIDocument> temp = gLastFocusedDocument;
04405       NS_RELEASE(gLastFocusedDocument);
04406       gLastFocusedDocument = nsnull;
04407 
04408       nsCxPusher pusher;
04409       if (pusher.Push(temp)) {
04410         temp->HandleDOMEvent(gLastFocusedPresContext, &event, nsnull,
04411                              NS_EVENT_FLAG_INIT, &status);
04412         pusher.Pop();
04413       }
04414 
04415       if (previousFocus && mCurrentFocus != previousFocus) {
04416         // The document's blur handler focused something else.
04417         // Abort firing any additional blur or focus events, and make sure
04418         // nsFocusController:mFocusedElement is not nulled out, but agrees
04419         // with our current concept of focus.
04420         EnsureFocusSynchronization();
04421         return NS_OK;
04422       }
04423 
04424       pusher.Push(globalObject);
04425       globalObject->HandleDOMEvent(gLastFocusedPresContext, &event, nsnull, NS_EVENT_FLAG_INIT, &status);
04426 
04427       if (previousFocus && mCurrentFocus != previousFocus) {
04428         // The window's blur handler focused something else.
04429         // Abort firing any additional blur or focus events.
04430         EnsureFocusSynchronization();
04431         return NS_OK;
04432       }
04433     }
04434   }
04435 
04436   if (aContent) {
04437     // Check if the HandleDOMEvent calls above destroyed our frame (bug #118685)
04438     nsIFrame* frame = nsnull;
04439     presShell->GetPrimaryFrameFor(aContent, &frame);
04440     if (!frame) {
04441       aContent = nsnull;
04442     }
04443   }
04444 
04445   NS_IF_RELEASE(gLastFocusedContent);
04446   gLastFocusedContent = aContent;
04447   NS_IF_ADDREF(gLastFocusedContent);
04448   SetFocusedContent(aContent);
04449 
04450   // Moved widget focusing code here, from end of SendFocusBlur
04451   // This fixes the order of accessibility focus events, so that
04452   // the window focus event goes first, and then the focus event for the control
04453   if (aEnsureWindowHasFocus) {
04454     // This raises the window that has both content and scroll bars in it
04455     // instead of the child window just below it that contains only the content
04456     // That way we focus the same window that gets focused by a mouse click
04457     nsIViewManager* vm = presShell->GetViewManager();
04458     if (vm) {
04459       nsCOMPtr<nsIWidget> widget;
04460       vm->GetWidget(getter_AddRefs(widget));
04461       if (widget)
04462         widget->SetFocus(PR_TRUE);
04463     }
04464   }
04465 
04466   if (nsnull != aContent && aContent != mFirstFocusEvent) {
04467 
04468     //Store the first focus event we fire and don't refire focus
04469     //to that element while the first focus is still ongoing.
04470     PRBool clearFirstFocusEvent = PR_FALSE;
04471     if (!mFirstFocusEvent) {
04472       mFirstFocusEvent = aContent;
04473       clearFirstFocusEvent = PR_TRUE;
04474     }
04475 
04476     //fire focus
04477     nsEventStatus status = nsEventStatus_eIgnore;
04478     nsEvent event(PR_TRUE, NS_FOCUS_CONTENT);
04479 
04480     if (nsnull != mPresContext) {
04481       nsCxPusher pusher;
04482       if (pusher.Push(aContent)) {
04483         aContent->HandleDOMEvent(mPresContext, &event, nsnull,
04484                                  NS_EVENT_FLAG_INIT, &status);
04485       }
04486     }
04487 
04488     nsAutoString tabIndex;
04489     aContent->GetAttr(kNameSpaceID_None, nsHTMLAtoms::tabindex, tabIndex);
04490     PRInt32 ec, val = tabIndex.ToInteger(&ec);
04491     if (NS_SUCCEEDED (ec)) {
04492       mCurrentTabIndex = val;
04493     }
04494 
04495     if (clearFirstFocusEvent) {
04496       mFirstFocusEvent = nsnull;
04497     }
04498   } else if (!aContent) {
04499     //fire focus on document even if the content isn't focusable (ie. text)
04500     //see bugzilla bug 93521
04501     nsEventStatus status = nsEventStatus_eIgnore;
04502     nsEvent event(PR_TRUE, NS_FOCUS_CONTENT);
04503 
04504     if (nsnull != mPresContext && mDocument) {
04505       nsCxPusher pusher;
04506       if (pusher.Push(mDocument)) {
04507         mDocument->HandleDOMEvent(mPresContext, &event, nsnull,
04508                                   NS_EVENT_FLAG_INIT, &status);
04509       }
04510     }
04511   }
04512 
04513   if (mBrowseWithCaret)
04514     SetContentCaretVisible(presShell, aContent, PR_TRUE);
04515 
04516   return NS_OK;
04517 }
04518 
04519 NS_IMETHODIMP
04520 nsEventStateManager::GetFocusedContent(nsIContent** aContent)
04521 {
04522   *aContent = mCurrentFocus;
04523   NS_IF_ADDREF(*aContent);
04524   return NS_OK;
04525 }
04526 
04527 void nsEventStateManager::EnsureFocusSynchronization()
04528 {
04529   // Sometimes the focus can get out of whack due to a blur handler
04530   // resetting focus. In addition, we fire onchange from the blur handler
04531   // for some controls, which is another place where focus can be changed.
04532   // XXX Ideally we will eventually store focus in one place instead of
04533   // the focus controller, esm, tabbrowser and some frames, so that it
04534   // cannot get out of sync.
04535   // See Bug 304751, calling FireOnChange() inside
04536   //                 nsComboboxControlFrame::SetFocus() is bad
04537   nsCOMPtr<nsPIDOMWindow> currentWindow =
04538     do_QueryInterface(GetDocumentOuterWindow(mDocument));
04539   if (currentWindow) {
04540     nsIFocusController *fc = currentWindow->GetRootFocusController();
04541     if (fc) {
04542       nsCOMPtr<nsIDOMElement> focusedElement = do_QueryInterface(mCurrentFocus);
04543       fc->SetFocusedElement(focusedElement);
04544     }
04545   }
04546 }
04547 
04548 NS_IMETHODIMP
04549 nsEventStateManager::SetFocusedContent(nsIContent* aContent)
04550 {
04551 
04552   if (aContent &&
04553       (!mPresContext || mPresContext->Type() == nsPresContext::eContext_PrintPreview)) {
04554     return NS_OK;
04555   }
04556 
04557   mCurrentFocus = aContent;
04558   if (mCurrentFocus)
04559     mLastFocus = mCurrentFocus;
04560   mCurrentFocusFrame = nsnull;
04561   return NS_OK;
04562 }
04563 
04564 NS_IMETHODIMP
04565 nsEventStateManager::GetLastFocusedContent(nsIContent** aContent)
04566 {
04567   *aContent = mLastFocus;
04568   NS_IF_ADDREF(*aContent);
04569   return NS_OK;
04570 }
04571 
04572 NS_IMETHODIMP
04573 nsEventStateManager::GetFocusedFrame(nsIFrame** aFrame)
04574 {
04575   if (!mCurrentFocusFrame && mCurrentFocus) {
04576     nsIDocument* doc = mCurrentFocus->GetDocument();
04577     if (doc) {
04578       nsIPresShell *shell = doc->GetShellAt(0);
04579       if (shell) {
04580         shell->GetPrimaryFrameFor(mCurrentFocus, &mCurrentFocusFrame);
04581         if (mCurrentFocusFrame)
04582           SetFrameExternalReference(mCurrentFocusFrame);
04583       }
04584     }
04585   }
04586 
04587   *aFrame = mCurrentFocusFrame;
04588   return NS_OK;
04589 }
04590 
04591 NS_IMETHODIMP
04592 nsEventStateManager::ContentRemoved(nsIContent* aContent)
04593 {
04594   if (mCurrentFocus &&
04595       nsContentUtils::ContentIsDescendantOf(mCurrentFocus, aContent)) {
04596     // Note that we don't use SetContentState() here because
04597     // we don't want to fire a blur.  Blurs should only be fired
04598     // in response to clicks or tabbing.
04599 
04600     SetFocusedContent(nsnull);
04601   }
04602 
04603   if (mLastFocus &&
04604       nsContentUtils::ContentIsDescendantOf(mLastFocus, aContent)) {
04605     mLastFocus = nsnull;
04606   }
04607 
04608   if (mHoverContent &&
04609       nsContentUtils::ContentIsDescendantOf(mHoverContent, aContent)) {
04610     // Since hover is hierarchical, set the current hover to the
04611     // content's parent node.
04612     mHoverContent = aContent->GetParent();
04613   }
04614 
04615   if (mActiveContent &&
04616       nsContentUtils::ContentIsDescendantOf(mActiveContent, aContent)) {
04617     // Active is hierarchical, so set the current active to the
04618     // content's parent node.
04619     mActiveContent = aContent->GetParent();
04620   }
04621 
04622   if (mDragOverContent &&
04623       nsContentUtils::ContentIsDescendantOf(mDragOverContent, aContent)) {
04624     mDragOverContent = nsnull;
04625   }
04626 
04627   if (mLastMouseOverElement &&
04628       nsContentUtils::ContentIsDescendantOf(mLastMouseOverElement, aContent)) {
04629     // See bug 292146 for why we want to null this out
04630     mLastMouseOverElement = nsnull;
04631   }
04632 
04633   return NS_OK;
04634 }
04635 
04636 NS_IMETHODIMP
04637 nsEventStateManager::EventStatusOK(nsGUIEvent* aEvent, PRBool *aOK)
04638 {
04639   *aOK = PR_TRUE;
04640   if (aEvent->message == NS_MOUSE_LEFT_BUTTON_DOWN) {
04641     if (!mNormalLMouseEventInProcess) {
04642       *aOK = PR_FALSE;
04643     }
04644   }
04645   return NS_OK;
04646 }
04647 
04648 //-------------------------------------------
04649 // Access Key Registration
04650 //-------------------------------------------
04651 NS_IMETHODIMP
04652 nsEventStateManager::RegisterAccessKey(nsIContent* aContent, PRUint32 aKey)
04653 {
04654   if (!mAccessKeys) {
04655     mAccessKeys = new nsSupportsHashtable();
04656     if (!mAccessKeys) {
04657       return NS_ERROR_FAILURE;
04658     }
04659   }
04660 
04661   if (aContent) {
04662     PRUint32 accKey = (IS_IN_BMP(aKey)) ? ToLowerCase((PRUnichar)aKey) : aKey;
04663 
04664     nsVoidKey key(NS_INT32_TO_PTR(accKey));
04665 
04666 #ifdef DEBUG_jag
04667     nsCOMPtr<nsIContent> oldContent = dont_AddRef(NS_STATIC_CAST(nsIContent*,  mAccessKeys->Get(&key)));
04668     NS_ASSERTION(!oldContent, "Overwriting accesskey registration");
04669 #endif
04670     mAccessKeys->Put(&key, aContent);
04671   }
04672 
04673   return NS_OK;
04674 }
04675 
04676 NS_IMETHODIMP
04677 nsEventStateManager::UnregisterAccessKey(nsIContent* aContent, PRUint32 aKey)
04678 {
04679   if (!mAccessKeys) {
04680     return NS_ERROR_FAILURE;
04681   }
04682 
04683   if (aContent) {
04684     PRUint32 accKey = (IS_IN_BMP(aKey)) ? ToLowerCase((PRUnichar)aKey) : aKey;
04685 
04686     nsVoidKey key(NS_INT32_TO_PTR(accKey));
04687 
04688     nsCOMPtr<nsIContent> oldContent = dont_AddRef(NS_STATIC_CAST(nsIContent*, mAccessKeys->Get(&key)));
04689 #ifdef DEBUG_jag
04690     NS_ASSERTION(oldContent == aContent, "Trying to unregister wrong content");
04691 #endif
04692     if (oldContent != aContent)
04693       return NS_OK;
04694 
04695     mAccessKeys->Remove(&key);
04696   }
04697   return NS_OK;
04698 }
04699 
04700 void
04701 nsEventStateManager::ForceViewUpdate(nsIView* aView)
04702 {
04703   // force the update to happen now, otherwise multiple scrolls can
04704   // occur before the update is processed. (bug #7354)
04705 
04706   nsIViewManager* vm = aView->GetViewManager();
04707   if (vm) {
04708     // I'd use Composite here, but it doesn't always work.
04709     // vm->Composite();
04710     vm->ForceUpdate();
04711   }
04712 }
04713 
04714 NS_IMETHODIMP
04715 nsEventStateManager::DispatchNewEvent(nsISupports* aTarget,
04716                                       nsIDOMEvent* aEvent,
04717                                       PRBool *aDefaultActionEnabled)
04718 {
04719   nsresult ret = NS_OK;
04720 
04721   nsCOMPtr<nsIPrivateDOMEvent> privEvt(do_QueryInterface(aEvent));
04722   if (privEvt) {
04723     nsEvent * innerEvent;
04724     privEvt->GetInternalNSEvent(&innerEvent);
04725 
04726     NS_ENSURE_TRUE(innerEvent, NS_ERROR_ILLEGAL_VALUE);
04727 
04728     // Make sure this event isn't currently in dispatch.
04729     NS_ENSURE_TRUE(!NS_IS_EVENT_IN_DISPATCH(innerEvent),
04730                    NS_ERROR_ILLEGAL_VALUE);
04731 
04732     // And make sure this event wasn't already dispatched w/o being
04733     // re-initialized in between.
04734     NS_ENSURE_TRUE(!(innerEvent->flags & NS_EVENT_FLAG_STOP_DISPATCH_IMMEDIATELY),
04735                    NS_ERROR_ILLEGAL_VALUE);
04736 
04737     // Mark this event as dispatched now that we're this far along.
04738     NS_MARK_EVENT_DISPATCH_STARTED(innerEvent);
04739 
04740     nsCOMPtr<nsIDOMEventTarget> eventTarget(do_QueryInterface(aTarget));
04741     privEvt->SetTarget(eventTarget);
04742 
04743     PRBool trusted;
04744     nsCOMPtr<nsIDOMNSEvent> nsevent(do_QueryInterface(privEvt));
04745 
04746     nsevent->GetIsTrusted(&trusted);
04747 
04748     if (!trusted) {
04749       //Check security state to determine if dispatcher is trusted
04750       nsIScriptSecurityManager *securityManager =
04751         nsContentUtils::GetSecurityManager();
04752 
04753       PRBool enabled;
04754       nsresult res =
04755         securityManager->IsCapabilityEnabled("UniversalBrowserWrite",
04756                                              &enabled);
04757       privEvt->SetTrusted(NS_SUCCEEDED(res) && enabled);
04758     }
04759 
04760     nsEventStatus status = nsEventStatus_eIgnore;
04761     nsCOMPtr<nsIScriptGlobalObject> target(do_QueryInterface(aTarget));
04762     if (target) {
04763       ret = target->HandleDOMEvent(mPresContext, innerEvent, &aEvent,
04764                                    NS_EVENT_FLAG_INIT, &status);
04765     }
04766     else {
04767       nsCOMPtr<nsIDocument> target(do_QueryInterface(aTarget));
04768       if (target) {
04769         ret = target->HandleDOMEvent(mPresContext, innerEvent, &aEvent,
04770                                      NS_EVENT_FLAG_INIT, &status);
04771       }
04772       else {
04773         nsCOMPtr<nsIContent> target(do_QueryInterface(aTarget));
04774         if (target) {
04775           ret = target->HandleDOMEvent(mPresContext, innerEvent, &aEvent,
04776                                        NS_EVENT_FLAG_INIT, &status);
04777 
04778           // Dispatch to the system event group.  Make sure to clear
04779           // the STOP_DISPATCH flag since this resets for each event
04780           // group per DOM3 Events.
04781 
04782           innerEvent->flags &= ~NS_EVENT_FLAG_STOP_DISPATCH;
04783           ret = target->HandleDOMEvent(mPresContext, innerEvent, &aEvent,
04784                                        NS_EVENT_FLAG_INIT |
04785                                        NS_EVENT_FLAG_SYSTEM_EVENT,
04786                                        &status);
04787         }
04788         else {
04789           nsCOMPtr<nsIChromeEventHandler> target(do_QueryInterface(aTarget));
04790           if (target) {
04791             ret = target->HandleChromeEvent(mPresContext, innerEvent, &aEvent,
04792                                             NS_EVENT_FLAG_INIT, &status);
04793           }
04794         }
04795       }
04796     }
04797 
04798     *aDefaultActionEnabled = status != nsEventStatus_eConsumeNoDefault;
04799   }
04800 
04801   return ret;
04802 }
04803 
04804 void
04805 nsEventStateManager::EnsureDocument(nsPresContext* aPresContext)
04806 {
04807   if (!mDocument)
04808     EnsureDocument(aPresContext->PresShell());
04809 }
04810 
04811 void
04812 nsEventStateManager::EnsureDocument(nsIPresShell* aPresShell)
04813 {
04814   if (!mDocument && aPresShell)
04815     mDocument = aPresShell->GetDocument();
04816 }
04817 
04818 void
04819 nsEventStateManager::FlushPendingEvents(nsPresContext* aPresContext)
04820 {
04821   NS_PRECONDITION(nsnull != aPresContext, "nsnull ptr");
04822   nsIPresShell *shell = aPresContext->GetPresShell();
04823   if (shell) {
04824     shell->FlushPendingNotifications(Flush_Display);
04825   }
04826 }
04827 
04828 nsresult
04829 nsEventStateManager::GetDocSelectionLocation(nsIContent **aStartContent,
04830                                              nsIContent **aEndContent,
04831                                              nsIFrame **aStartFrame,
04832                                              PRUint32* aStartOffset)
04833 {
04834   // In order to return the nsIContent and nsIFrame of the caret's position,
04835   // we need to get a pres shell, and then get the selection from it
04836 
04837   *aStartOffset = 0;
04838   *aStartFrame = nsnull;
04839   *aStartContent = *aEndContent = nsnull;
04840   nsresult rv = NS_ERROR_FAILURE;
04841 
04842   NS_ASSERTION(mPresContext, "mPresContent is null!!");
04843   EnsureDocument(mPresContext);
04844   if (!mDocument)
04845     return rv;
04846   nsIPresShell *shell;
04847   shell = mPresContext->GetPresShell();
04848 
04849   nsIFrameSelection *frameSelection = nsnull;
04850   if (shell)
04851     frameSelection = shell->FrameSelection();
04852 
04853   nsCOMPtr<nsISelection> domSelection;
04854   if (frameSelection)
04855     rv = frameSelection->GetSelection(nsISelectionController::SELECTION_NORMAL,
04856                                       getter_AddRefs(domSelection));
04857 
04858   nsCOMPtr<nsIDOMNode> startNode, endNode;
04859   PRBool isCollapsed = PR_FALSE;
04860   nsCOMPtr<nsIContent> startContent, endContent;
04861   if (domSelection) {
04862     domSelection->GetIsCollapsed(&isCollapsed);
04863     nsCOMPtr<nsIDOMRange> domRange;
04864     rv = domSelection->GetRangeAt(0, getter_AddRefs(domRange));
04865     if (domRange) {
04866       domRange->GetStartContainer(getter_AddRefs(startNode));
04867       domRange->GetEndContainer(getter_AddRefs(endNode));
04868       domRange->GetStartOffset(NS_REINTERPRET_CAST(PRInt32 *, aStartOffset));
04869 
04870       nsIContent *childContent = nsnull;
04871 
04872       startContent = do_QueryInterface(startNode);
04873       if (startContent && startContent->IsContentOfType(nsIContent::eELEMENT)) {
04874         NS_ASSERTION(*aStartOffset >= 0, "Start offset cannot be negative");  
04875         childContent = startContent->GetChildAt(*aStartOffset);
04876         if (childContent) {
04877           startContent = childContent;
04878         }
04879       }
04880 
04881       endContent = do_QueryInterface(endNode);
04882       if (endContent && endContent->IsContentOfType(nsIContent::eELEMENT)) {
04883         PRInt32 endOffset = 0;
04884         domRange->GetEndOffset(&endOffset);
04885         NS_ASSERTION(endOffset >= 0, "End offset cannot be negative");
04886         childContent = endContent->GetChildAt(endOffset);
04887         if (childContent) {
04888           endContent = childContent;
04889         }
04890       }
04891     }
04892   }
04893 
04894   nsIFrame *startFrame = nsnull;
04895   if (startContent) {
04896     rv = shell->GetPrimaryFrameFor(startContent, &startFrame);
04897     if (isCollapsed && NS_SUCCEEDED(rv)) {
04898       // First check to see if we're in a <label>
04899       // We don't want to return the selection in a label, because
04900       // we we should be tabbing relative to what the label 
04901       // points to (the current focus), not relative to the label itself.
04902       nsIContent *parentContent = startContent;
04903       while ((parentContent = parentContent->GetParent()) != nsnull) {
04904         if (parentContent->Tag() == nsHTMLAtoms::label) {
04905           return NS_OK; // Don't return selection location, we're on a label
04906         }
04907       }
04908       // Next check to see if our caret is at the very end of a node
04909       // If so, the caret is actually sitting in front of the next
04910       // logical frame's primary node - so for this case we need to
04911       // change caretContent to that node.
04912 
04913       nsCOMPtr<nsIDOMNode> domNode(do_QueryInterface(startContent));
04914       PRUint16 nodeType;
04915       domNode->GetNodeType(&nodeType);
04916 
04917       if (nodeType == nsIDOMNode::TEXT_NODE) {
04918         nsAutoString nodeValue;
04919         domNode->GetNodeValue(nodeValue);
04920 
04921         PRBool isFormControl =
04922           startContent->IsContentOfType(nsIContent::eHTML_FORM_CONTROL);
04923 
04924         if (nodeValue.Length() == *aStartOffset && !isFormControl &&
04925             startContent != mDocument->GetRootContent()) {
04926           // Yes, indeed we were at the end of the last node
04927           nsCOMPtr<nsIBidirectionalEnumerator> frameTraversal;
04928 
04929           nsCOMPtr<nsIFrameTraversal> trav(do_CreateInstance(kFrameTraversalCID,
04930                                                              &rv));
04931           NS_ENSURE_SUCCESS(rv, rv);
04932 
04933           rv = trav->NewFrameTraversal(getter_AddRefs(frameTraversal), LEAF,
04934                                        mPresContext, startFrame);
04935           NS_ENSURE_SUCCESS(rv, rv);
04936 
04937           nsIFrame *newCaretFrame = nsnull;
04938           nsCOMPtr<nsIContent> newCaretContent = startContent;
04939           PRBool endOfSelectionInStartNode(startContent == endContent);
04940           do {
04941             // Continue getting the next frame until the primary content for the frame
04942             // we are on changes - we don't want to be stuck in the same place
04943             frameTraversal->Next();
04944             nsISupports* currentItem;
04945             frameTraversal->CurrentItem(&currentItem);
04946             if (nsnull == (newCaretFrame = NS_STATIC_CAST(nsIFrame*, currentItem))) {
04947               break;
04948             }
04949             newCaretContent = newCaretFrame->GetContent();            
04950           } while (!newCaretContent || newCaretContent == startContent);
04951 
04952           if (newCaretFrame && newCaretContent) {
04953             // If the caret is exactly at the same position of the new frame,
04954             // then we can use the newCaretFrame and newCaretContent for our position
04955             nsCOMPtr<nsICaret> caret;
04956             shell->GetCaret(getter_AddRefs(caret));
04957             nsRect caretRect;
04958             nsIView *caretView;
04959             caret->GetCaretCoordinates(nsICaret::eClosestViewCoordinates, 
04960                                        domSelection, &caretRect,
04961                                        &isCollapsed, &caretView);
04962             nsPoint framePt;
04963             nsIView *frameClosestView = newCaretFrame->GetClosestView(&framePt);
04964             if (caretView == frameClosestView && caretRect.y == framePt.y &&
04965                 caretRect.x == framePt.x) {
04966               // The caret is at the start of the new element.
04967               startFrame = newCaretFrame;
04968               startContent = newCaretContent;
04969               if (endOfSelectionInStartNode) {
04970                 endContent = newCaretContent; // Ensure end of selection is not before start
04971               }
04972             }
04973           }
04974         }
04975       }
04976     }
04977   }
04978 
04979   *aStartFrame = startFrame;
04980   *aStartContent = startContent;
04981   *aEndContent = endContent;
04982   NS_IF_ADDREF(*aStartContent);
04983   NS_IF_ADDREF(*aEndContent);
04984 
04985   return rv;
04986 }
04987 
04988 void
04989 nsEventStateManager::FocusElementButNotDocument(nsIContent *aContent)
04990 {
04991   // Focus an element in the current document, but don't switch document/window focus!
04992 
04993   if (gLastFocusedDocument == mDocument) {
04994     // If we're already focused in this document,
04995     // use normal focus method
04996     if (mCurrentFocus != aContent) {
04997       if (aContent)
04998         aContent->SetFocus(mPresContext);
04999       else
05000         SetContentState(nsnull, NS_EVENT_STATE_FOCUS);
05001     }
05002     return;
05003   }
05004 
05010   nsIFocusController *focusController =
05011     GetFocusControllerForDocument(mDocument);
05012   if (!focusController)
05013       return;
05014 
05015   // Get previous focus
05016   nsCOMPtr<nsIDOMElement> oldFocusedElement;
05017   focusController->GetFocusedElement(getter_AddRefs(oldFocusedElement));
05018   nsCOMPtr<nsIContent> oldFocusedContent(do_QueryInterface(oldFocusedElement));
05019 
05020   // Temporarily set mCurrentFocus so that esm::GetContentState() tells
05021   // layout system to show focus on this element.
05022   SetFocusedContent(aContent);  // Reset back to null at the end of this method.
05023   mDocument->BeginUpdate(UPDATE_CONTENT_STATE);
05024   mDocument->ContentStatesChanged(oldFocusedContent, aContent,
05025                                   NS_EVENT_STATE_FOCUS);
05026   mDocument->EndUpdate(UPDATE_CONTENT_STATE);
05027 
05028   // Reset mCurrentFocus = nsnull for this doc, so when this document
05029   // does get focus next time via preHandleEvent() NS_GOTFOCUS,
05030   // the old document gets blurred
05031   SetFocusedContent(nsnull);
05032 
05033 }
05034 
05035 NS_IMETHODIMP
05036 nsEventStateManager::MoveFocusToCaret(PRBool aCanFocusDoc,
05037                                       PRBool *aIsSelectionWithFocus)
05038 {
05039   // mBrowseWithCaret equals the pref accessibility.browsewithcaret
05040   // When it's true, the user can arrow around the browser as if it's a
05041   // read only text editor.
05042 
05043   // If the user cursors over a focusable element or gets there with find text, then send focus to it
05044 
05045   *aIsSelectionWithFocus= PR_FALSE;
05046   nsCOMPtr<nsIContent> selectionContent, endSelectionContent;
05047   nsIFrame *selectionFrame;
05048   PRUint32 selectionOffset;
05049   GetDocSelectionLocation(getter_AddRefs(selectionContent), getter_AddRefs(endSelectionContent),
05050     &selectionFrame, &selectionOffset);
05051 
05052   if (!selectionContent)
05053     return NS_ERROR_FAILURE;
05054 
05055   nsCOMPtr<nsIContent> testContent(selectionContent);
05056   nsCOMPtr<nsIContent> nextTestContent(endSelectionContent);
05057 
05058   // We now have the correct start node in selectionContent!
05059   // Search for focusable elements, starting with selectionContent
05060 
05061   // Method #1: Keep going up while we look - an ancestor might be focusable
05062   // We could end the loop earlier, such as when we're no longer
05063   // in the same frame, by comparing getPrimaryFrameFor(selectionContent)
05064   // with a variable holding the starting selectionContent
05065   while (testContent) {
05066     // Keep testing while selectionContent is equal to something,
05067     // eventually we'll run out of ancestors
05068 
05069     if (testContent == mCurrentFocus) {
05070       *aIsSelectionWithFocus = PR_TRUE;
05071       return NS_OK;  // already focused on this node, this whole thing's moot
05072     }
05073 
05074     nsIAtom *tag = testContent->Tag();
05075 
05076     // Add better focusable test here later if necessary ...
05077     if (tag == nsHTMLAtoms::a &&
05078         testContent->IsContentOfType(nsIContent::eHTML)) {
05079       *aIsSelectionWithFocus = PR_TRUE;
05080     }
05081     else {
05082       *aIsSelectionWithFocus = testContent->HasAttr(kNameSpaceID_XLink, nsHTMLAtoms::href);
05083       if (*aIsSelectionWithFocus) {
05084         nsAutoString xlinkType;
05085         testContent->GetAttr(kNameSpaceID_XLink, nsHTMLAtoms::type, xlinkType);
05086         if (!xlinkType.EqualsLiteral("simple")) {
05087           *aIsSelectionWithFocus = PR_FALSE;  // Xlink must be type="simple"
05088         }
05089       }
05090     }
05091 
05092     if (*aIsSelectionWithFocus) {
05093       FocusElementButNotDocument(testContent);
05094       return NS_OK;
05095     }
05096 
05097     // Get the parent
05098     testContent = testContent->GetParent();
05099 
05100     if (!testContent) {
05101       // We run this loop again, checking the ancestor chain of the selection's end point
05102       testContent = nextTestContent;
05103       nextTestContent = nsnull;
05104     }
05105   }
05106 
05107   // We couldn't find an anchor that was an ancestor of the selection start
05108   // Method #2: look for anchor in selection's primary range (depth first search)
05109 
05110   // Turn into nodes so that we can use GetNextSibling() and GetFirstChild()
05111   nsCOMPtr<nsIDOMNode> selectionNode(do_QueryInterface(selectionContent));
05112   nsCOMPtr<nsIDOMNode> endSelectionNode(do_QueryInterface(endSelectionContent));
05113   nsCOMPtr<nsIDOMNode> testNode;
05114 
05115   do {
05116     testContent = do_QueryInterface(selectionNode);
05117 
05118     // We're looking for any focusable item that could be part of the
05119     // main document's selection.
05120     // Right now we only look for elements with the <a> tag.
05121     // Add better focusable test here later if necessary ...
05122     if (testContent) {
05123       if (testContent->Tag() == nsHTMLAtoms::a &&
05124           testContent->IsContentOfType(nsIContent::eHTML)) {
05125         *aIsSelectionWithFocus = PR_TRUE;
05126         FocusElementButNotDocument(testContent);
05127         return NS_OK;
05128       }
05129     }
05130 
05131     selectionNode->GetFirstChild(getter_AddRefs(testNode));
05132     if (testNode) {
05133       selectionNode = testNode;
05134       continue;
05135     }
05136 
05137     if (selectionNode == endSelectionNode)
05138       break;
05139     selectionNode->GetNextSibling(getter_AddRefs(testNode));
05140     if (testNode) {
05141       selectionNode = testNode;
05142       continue;
05143     }
05144 
05145     do {
05146       selectionNode->GetParentNode(getter_AddRefs(testNode));
05147       if (!testNode || testNode == endSelectionNode) {
05148         selectionNode = nsnull;
05149         break;
05150       }
05151       testNode->GetNextSibling(getter_AddRefs(selectionNode));
05152       if (selectionNode)
05153         break;
05154       selectionNode = testNode;
05155     } while (PR_TRUE);
05156   }
05157   while (selectionNode && selectionNode != endSelectionNode);
05158 
05159   if (aCanFocusDoc)
05160     FocusElementButNotDocument(nsnull);
05161 
05162   return NS_OK; // no errors, but caret not inside focusable element other than doc itself
05163 }
05164 
05165 
05166 
05167 NS_IMETHODIMP
05168 nsEventStateManager::MoveCaretToFocus()
05169 {
05170   // If in HTML content and the pref accessibility.browsewithcaret is TRUE,
05171   // then always move the caret to beginning of a new focus
05172 
05173   PRInt32 itemType = nsIDocShellTreeItem::typeChrome;
05174 
05175   if (mPresContext) {
05176     nsCOMPtr<nsISupports> pcContainer = mPresContext->GetContainer();
05177     nsCOMPtr<nsIDocShellTreeItem> treeItem(do_QueryInterface(pcContainer));
05178     if (treeItem)
05179       treeItem->GetItemType(&itemType);
05180     nsCOMPtr<nsIEditorDocShell> editorDocShell(do_QueryInterface(treeItem));
05181     if (editorDocShell) {
05182       PRBool isEditable;
05183       editorDocShell->GetEditable(&isEditable);
05184       if (isEditable) {
05185         return NS_OK;  // Move focus to caret only if browsing, not editing
05186       }
05187     }
05188   }
05189 
05190   if (itemType != nsIDocShellTreeItem::typeChrome) {
05191     nsCOMPtr<nsIContent> selectionContent, endSelectionContent;
05192     nsIFrame *selectionFrame;
05193     PRUint32 selectionOffset;
05194     GetDocSelectionLocation(getter_AddRefs(selectionContent),
05195                             getter_AddRefs(endSelectionContent),
05196                             &selectionFrame, &selectionOffset);
05197 
05198     nsIPresShell *shell = mPresContext->GetPresShell();
05199     if (shell) {
05200       // rangeDoc is a document interface we can create a range with
05201       nsCOMPtr<nsIDOMDocumentRange> rangeDoc(do_QueryInterface(mDocument));
05202 
05203       if (rangeDoc) {
05204         nsCOMPtr<nsISelection> domSelection;
05205         shell->FrameSelection()->
05206           GetSelection(nsISelectionController::SELECTION_NORMAL,
05207                        getter_AddRefs(domSelection));
05208         if (domSelection) {
05209           nsCOMPtr<nsIDOMNode> currentFocusNode(do_QueryInterface(mCurrentFocus));
05210           // First clear the selection
05211           domSelection->RemoveAllRanges();
05212           if (currentFocusNode) {
05213             nsCOMPtr<nsIDOMRange> newRange;
05214             nsresult rv = rangeDoc->CreateRange(getter_AddRefs(newRange));
05215             if (NS_SUCCEEDED(rv)) {
05216               // Set the range to the start of the currently focused node
05217               // Make sure it's collapsed
05218               newRange->SelectNodeContents(currentFocusNode);
05219               nsCOMPtr<nsIDOMNode> firstChild;
05220               currentFocusNode->GetFirstChild(getter_AddRefs(firstChild));
05221               if (!firstChild ||
05222                   mCurrentFocus->IsContentOfType(nsIContent::eHTML_FORM_CONTROL)) {
05223                 // If current focus node is a leaf, set range to before the
05224                 // node by using the parent as a container.
05225                 // This prevents it from appearing as selected.
05226                 newRange->SetStartBefore(currentFocusNode);
05227                 newRange->SetEndBefore(currentFocusNode);
05228               }
05229               domSelection->AddRange(newRange);
05230               domSelection->CollapseToStart();
05231             }
05232           }
05233         }
05234       }
05235     }
05236   }
05237   return NS_OK;
05238 }
05239 
05240 nsresult
05241 nsEventStateManager::SetCaretEnabled(nsIPresShell *aPresShell, PRBool aEnabled)
05242 {
05243   nsCOMPtr<nsICaret> caret;
05244   aPresShell->GetCaret(getter_AddRefs(caret));
05245 
05246   nsCOMPtr<nsISelectionController> selCon(do_QueryInterface(aPresShell));
05247   if (!selCon || !caret)
05248     return NS_ERROR_FAILURE;
05249 
05250   selCon->SetCaretEnabled(aEnabled);
05251   caret->SetCaretVisible(aEnabled);
05252 
05253   return NS_OK;
05254 }
05255 
05256 nsresult
05257 nsEventStateManager::SetContentCaretVisible(nsIPresShell* aPresShell,
05258                                             nsIContent *aFocusedContent,
05259                                             PRBool aVisible)
05260 {
05261   // When browsing with caret, make sure caret is visible after new focus
05262   nsCOMPtr<nsICaret> caret;
05263   aPresShell->GetCaret(getter_AddRefs(caret));
05264 
05265   nsCOMPtr<nsIFrameSelection> frameSelection;
05266   if (aFocusedContent) {
05267     nsIFrame *focusFrame = nsnull;
05268     aPresShell->GetPrimaryFrameFor(aFocusedContent, &focusFrame);
05269 
05270     GetSelection(focusFrame, mPresContext, getter_AddRefs(frameSelection));
05271   }
05272 
05273   nsIFrameSelection *docFrameSelection = aPresShell->FrameSelection();
05274 
05275   if (docFrameSelection && caret &&
05276      (frameSelection == docFrameSelection || !aFocusedContent)) {
05277     nsCOMPtr<nsISelection> domSelection;
05278     docFrameSelection->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(domSelection));
05279     if (domSelection) {
05280       // First, tell the caret which selection to use
05281       caret->SetCaretDOMSelection(domSelection);
05282 
05283       // In content, we need to set the caret
05284       // the only other case is edit fields, where they have a different frame selection from the doc's
05285       // in that case they'll take care of making the caret visible themselves
05286 
05287       // Then make sure it's visible
05288       return SetCaretEnabled(aPresShell, aVisible);
05289     }
05290   }
05291 
05292   return NS_OK;
05293 }
05294 
05295 
05296 PRBool
05297 nsEventStateManager::GetBrowseWithCaret()
05298 {
05299   return mBrowseWithCaret;
05300 }
05301 
05302 void
05303 nsEventStateManager::ResetBrowseWithCaret()
05304 {
05305   // This is called when browse with caret changes on the fly
05306   // or when a document gets focused
05307 
05308   if (!mPresContext)
05309     return;
05310 
05311   nsCOMPtr<nsISupports> pcContainer = mPresContext->GetContainer();
05312   PRInt32 itemType;
05313   nsCOMPtr<nsIDocShellTreeItem> shellItem(do_QueryInterface(pcContainer));
05314   if (!shellItem)
05315     return;
05316 
05317   shellItem->GetItemType(&itemType);
05318 
05319   if (itemType == nsIDocShellTreeItem::typeChrome)
05320     return;  // Never browse with caret in chrome
05321 
05322   nsCOMPtr<nsIEditorDocShell> editorDocShell(do_QueryInterface(shellItem));
05323   if (editorDocShell) {
05324     PRBool isEditable;
05325     editorDocShell->GetEditable(&isEditable);
05326     if (isEditable) {
05327       return;  // Reset caret visibility only if browsing, not editing
05328     }
05329   }
05330 
05331   PRPackedBool browseWithCaret =
05332     nsContentUtils::GetBoolPref("accessibility.browsewithcaret");
05333 
05334   mBrowseWithCaret = browseWithCaret;
05335 
05336   nsIPresShell *presShell = mPresContext->GetPresShell();
05337 
05338   // Make caret visible or not, depending on what's appropriate
05339   if (presShell) {
05340     SetContentCaretVisible(presShell, mCurrentFocus,
05341                            browseWithCaret &&
05342                            (!gLastFocusedDocument ||
05343                             gLastFocusedDocument == mDocument));
05344   }
05345 }
05346 
05347 //--------------------------------------------------------------------------------
05348 //-- DocShell Focus Traversal Methods
05349 //--------------------------------------------------------------------------------
05350 
05351 //----------------------------------------
05352 // Returns PR_TRUE if this doc contains a frameset
05353 PRBool
05354 nsEventStateManager::IsFrameSetDoc(nsIDocShell* aDocShell)
05355 {
05356   NS_ASSERTION(aDocShell, "docshell is null");
05357   PRBool isFrameSet = PR_FALSE;
05358 
05359   // a frameset element will always be the immediate child
05360   // of the root content (the HTML tag)
05361   nsCOMPtr<nsIPresShell> presShell;
05362   aDocShell->GetPresShell(getter_AddRefs(presShell));
05363   if (presShell) {
05364     nsIDocument *doc = presShell->GetDocument();
05365     nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(doc);
05366     if (htmlDoc) {
05367       nsIContent *rootContent = doc->GetRootContent();
05368       if (rootContent) {
05369         PRUint32 childCount = rootContent->GetChildCount();
05370         for (PRUint32 i = 0; i < childCount; ++i) {
05371           nsIContent *childContent = rootContent->GetChildAt(i);
05372 
05373           nsINodeInfo *ni = childContent->GetNodeInfo();
05374 
05375           if (childContent->IsContentOfType(nsIContent::eHTML) &&
05376               ni->Equals(nsHTMLAtoms::frameset)) {
05377             isFrameSet = PR_TRUE;
05378             break;
05379           }
05380         }
05381       }
05382     }
05383   }
05384 
05385   return isFrameSet;
05386 }
05387 
05388 //----------------------------------------
05389 // Returns PR_TRUE if this doc is an IFRAME
05390 
05391 PRBool
05392 nsEventStateManager::IsIFrameDoc(nsIDocShell* aDocShell)
05393 {
05394   NS_ASSERTION(aDocShell, "docshell is null");
05395 
05396   nsCOMPtr<nsPIDOMWindow> domWindow = do_GetInterface(aDocShell);
05397   if (!domWindow) {
05398     NS_ERROR("We're a child of a docshell without a window?");
05399     return PR_FALSE;
05400   }
05401 
05402   nsCOMPtr<nsIContent> docContent =
05403     do_QueryInterface(domWindow->GetFrameElementInternal());
05404 
05405   if (!docContent) {
05406     return PR_FALSE;
05407   }
05408 
05409   return docContent->Tag() == nsHTMLAtoms::iframe;
05410 }
05411 
05412 //-------------------------------------------------------
05413 // Return PR_TRUE if the docshell is visible
05414 
05415 PRBool
05416 nsEventStateManager::IsShellVisible(nsIDocShell* aShell)
05417 {
05418   NS_ASSERTION(aShell, "docshell is null");
05419 
05420   nsCOMPtr<nsIBaseWindow> basewin = do_QueryInterface(aShell);
05421   if (!basewin)
05422     return PR_TRUE;
05423 
05424   PRBool isVisible = PR_TRUE;
05425   basewin->GetVisibility(&isVisible);
05426 
05427   // We should be doing some additional checks here so that
05428   // we don't tab into hidden tabs of tabbrowser.  -bryner
05429 
05430   return isVisible;
05431 }
05432 
05433 //------------------------------------------------
05434 // This method should be called when tab or F6/ctrl-tab
05435 // traversal wants to focus a new document.  It will focus
05436 // the docshell, traverse into the document if this type
05437 // of document does not get document focus (i.e. framsets
05438 // and chrome), and update the canvas focus state on the docshell.
05439 
05440 void
05441 nsEventStateManager::TabIntoDocument(nsIDocShell* aDocShell,
05442                                      PRBool aForward)
05443 {
05444   NS_ASSERTION(aDocShell, "null docshell");
05445   nsCOMPtr<nsIDOMWindowInternal> domwin = do_GetInterface(aDocShell);
05446   if (domwin)
05447     domwin->Focus();
05448 
05449   PRInt32 itemType;
05450   nsCOMPtr<nsIDocShellTreeItem> treeItem = do_QueryInterface(aDocShell);
05451   treeItem->GetItemType(&itemType);
05452 
05453   nsCOMPtr<nsPresContext> presContext;
05454   aDocShell->GetPresContext(getter_AddRefs(presContext));
05455   PRBool focusDocument;
05456   if (presContext &&
05457       presContext->Type() == nsPresContext::eContext_PrintPreview) {
05458     // Don't focus any content in print preview mode, bug 244128.
05459     focusDocument = PR_TRUE;
05460   } else {
05461     if (!aForward || (itemType == nsIDocShellTreeItem::typeChrome))
05462       focusDocument = PR_FALSE;
05463     else {
05464       // Check for a frameset document
05465       focusDocument = !(IsFrameSetDoc(aDocShell));
05466     }
05467   }
05468 
05469   if (focusDocument) {
05470     // make sure we're in view
05471     aDocShell->SetCanvasHasFocus(PR_TRUE);
05472   }
05473   else {
05474     aDocShell->SetHasFocus(PR_FALSE);
05475 
05476     if (presContext) {
05477       nsIEventStateManager *docESM = presContext->EventStateManager();
05478 
05479       // we are about to shift focus to aDocShell
05480       // keep track of the document, so we don't try to go back into it.
05481       mTabbingFromDocShells.AppendObject(aDocShell);
05482 
05483       // clear out any existing focus state
05484       docESM->SetContentState(nsnull, NS_EVENT_STATE_FOCUS);
05485       // now focus the first (or last) focusable content
05486       docESM->ShiftFocus(aForward, nsnull);
05487 
05488       // remove the document from the list
05489       mTabbingFromDocShells.RemoveObject(aDocShell);
05490     }
05491   }
05492 }
05493 
05494 void
05495 nsEventStateManager::GetLastChildDocShell(nsIDocShellTreeItem* aItem,
05496                                           nsIDocShellTreeItem** aResult)
05497 {
05498   NS_ASSERTION(aItem, "null docshell");
05499   NS_ASSERTION(aResult, "null out pointer");
05500 
05501   nsCOMPtr<nsIDocShellTreeItem> curItem = do_QueryInterface(aItem);
05502   while (1) {
05503     nsCOMPtr<nsIDocShellTreeNode> curNode = do_QueryInterface(curItem);
05504     PRInt32 childCount = 0;
05505     curNode->GetChildCount(&childCount);
05506     if (!childCount) {
05507       *aResult = curItem;
05508       NS_ADDREF(*aResult);
05509       return;
05510     }
05511 
05512     curNode->GetChildAt(childCount - 1, getter_AddRefs(curItem));
05513   }
05514 }
05515 
05516 void
05517 nsEventStateManager::GetNextDocShell(nsIDocShellTreeNode* aNode,
05518                                      nsIDocShellTreeItem** aResult)
05519 {
05520   NS_ASSERTION(aNode, "null docshell");
05521   NS_ASSERTION(aResult, "null out pointer");
05522   PRInt32 numChildren = 0;
05523 
05524   *aResult = nsnull;
05525 
05526   aNode->GetChildCount(&numChildren);
05527   if (numChildren) {
05528     aNode->GetChildAt(0, aResult);
05529     if (*aResult)
05530       return;
05531   }
05532 
05533   nsCOMPtr<nsIDocShellTreeNode> curNode = aNode;
05534   while (curNode) {
05535     nsCOMPtr<nsIDocShellTreeItem> curItem = do_QueryInterface(curNode);
05536     nsCOMPtr<nsIDocShellTreeItem> parentItem;
05537     curItem->GetParent(getter_AddRefs(parentItem));
05538     if (!parentItem) {
05539       *aResult = nsnull;
05540       return;
05541     }
05542 
05543     PRInt32 childOffset = 0;
05544     curItem->GetChildOffset(&childOffset);
05545     nsCOMPtr<nsIDocShellTreeNode> parentNode = do_QueryInterface(parentItem);
05546     numChildren = 0;
05547     parentNode->GetChildCount(&numChildren);
05548     if (childOffset+1 < numChildren) {
05549       parentNode->GetChildAt(childOffset+1, aResult);
05550       if (*aResult)
05551         return;
05552     }
05553 
05554     curNode = do_QueryInterface(parentItem);
05555   }
05556 }
05557 
05558 void
05559 nsEventStateManager::GetPrevDocShell(nsIDocShellTreeNode* aNode,
05560                                      nsIDocShellTreeItem** aResult)
05561 {
05562   NS_ASSERTION(aNode, "null docshell");
05563   NS_ASSERTION(aResult, "null out pointer");
05564 
05565   nsCOMPtr<nsIDocShellTreeNode> curNode = aNode;
05566   nsCOMPtr<nsIDocShellTreeItem> curItem = do_QueryInterface(curNode);
05567   nsCOMPtr<nsIDocShellTreeItem> parentItem;
05568 
05569   curItem->GetParent(getter_AddRefs(parentItem));
05570   if (!parentItem) {
05571     *aResult = nsnull;
05572     return;
05573   }
05574 
05575   PRInt32 childOffset = 0;
05576   curItem->GetChildOffset(&childOffset);
05577   if (childOffset) {
05578     nsCOMPtr<nsIDocShellTreeNode> parentNode = do_QueryInterface(parentItem);
05579     parentNode->GetChildAt(childOffset - 1, getter_AddRefs(curItem));
05580 
05581     // get the last child recursively of this node
05582     while (1) {
05583       PRInt32 childCount = 0;
05584       curNode = do_QueryInterface(curItem);
05585       curNode->GetChildCount(&childCount);
05586       if (!childCount)
05587         break;
05588 
05589       curNode->GetChildAt(childCount - 1, getter_AddRefs(curItem));
05590     }
05591 
05592     *aResult = curItem;
05593     NS_ADDREF(*aResult);
05594     return;
05595   }
05596 
05597   *aResult = parentItem;
05598   NS_ADDREF(*aResult);
05599   return;
05600 }
05601 
05602 //-------------------------------------------------
05603 // Traversal by document/DocShell only
05604 // this does not include any content inside the doc
05605 // or IFrames
05606 void
05607 nsEventStateManager::ShiftFocusByDoc(PRBool aForward)
05608 {
05609   // Note that we use the docshell tree here instead of iteratively calling
05610   // ShiftFocus.  The docshell tree should be kept in depth-first frame tree
05611   // order, the same as we use for tabbing, so the effect should be the same,
05612   // but this is much faster.
05613 
05614   NS_ASSERTION(mPresContext, "no prescontext");
05615 
05616   nsCOMPtr<nsISupports> pcContainer = mPresContext->GetContainer();
05617   nsCOMPtr<nsIDocShellTreeNode> curNode = do_QueryInterface(pcContainer);
05618 
05619   // perform a depth first search (preorder) of the docshell tree
05620   // looking for an HTML Frame or a chrome document
05621 
05622   nsCOMPtr<nsIDocShellTreeItem> nextItem;
05623   nsCOMPtr<nsIDocShell> nextShell;
05624   do {
05625     if (aForward) {
05626       GetNextDocShell(curNode, getter_AddRefs(nextItem));
05627       if (!nextItem) {
05628         nsCOMPtr<nsIDocShellTreeItem> curItem = do_QueryInterface(pcContainer);
05629         // wrap around to the beginning, which is the top of the tree
05630         curItem->GetRootTreeItem(getter_AddRefs(nextItem));
05631       }
05632     }
05633     else {
05634       GetPrevDocShell(curNode, getter_AddRefs(nextItem));
05635       if (!nextItem) {
05636         nsCOMPtr<nsIDocShellTreeItem> curItem = do_QueryInterface(pcContainer);
05637         // wrap around to the end, which is the last node in the tree
05638         nsCOMPtr<nsIDocShellTreeItem> rootItem;
05639         curItem->GetRootTreeItem(getter_AddRefs(rootItem));
05640         GetLastChildDocShell(rootItem, getter_AddRefs(nextItem));
05641       }
05642     }
05643 
05644     curNode = do_QueryInterface(nextItem);
05645     nextShell = do_QueryInterface(nextItem);
05646   } while (IsFrameSetDoc(nextShell) || IsIFrameDoc(nextShell) || !IsShellVisible(nextShell));
05647 
05648   if (nextShell) {
05649     // NOTE: always tab forward into the document, this ensures that we
05650     // focus the document itself, not its last focusable content.
05651     // chrome documents will get their first focusable content focused.
05652     SetContentState(nsnull, NS_EVENT_STATE_FOCUS);
05653     TabIntoDocument(nextShell, PR_TRUE);
05654   }
05655 }
05656 
05657 // Get the FocusController given an nsIDocument
05658 nsIFocusController*
05659 nsEventStateManager::GetFocusControllerForDocument(nsIDocument* aDocument)
05660 {
05661   nsCOMPtr<nsISupports> container = aDocument->GetContainer();
05662   nsCOMPtr<nsPIDOMWindow> windowPrivate = do_GetInterface(container);
05663 
05664   return windowPrivate ? windowPrivate->GetRootFocusController() : nsnull;
05665 }