dom/events/EventStateManager.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* vim: set ts=2 sw=2 et tw=80: */
     3 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include "mozilla/Attributes.h"
     8 #include "mozilla/EventDispatcher.h"
     9 #include "mozilla/EventStateManager.h"
    10 #include "mozilla/EventStates.h"
    11 #include "mozilla/IMEStateManager.h"
    12 #include "mozilla/MiscEvents.h"
    13 #include "mozilla/MathAlgorithms.h"
    14 #include "mozilla/MouseEvents.h"
    15 #include "mozilla/TextComposition.h"
    16 #include "mozilla/TextEvents.h"
    17 #include "mozilla/TouchEvents.h"
    18 #include "mozilla/dom/Event.h"
    19 #include "mozilla/dom/TabParent.h"
    20 #include "mozilla/dom/UIEvent.h"
    22 #include "ContentEventHandler.h"
    23 #include "IMEContentObserver.h"
    24 #include "WheelHandlingHelper.h"
    26 #include "nsCOMPtr.h"
    27 #include "nsFocusManager.h"
    28 #include "nsIContent.h"
    29 #include "nsINodeInfo.h"
    30 #include "nsIDocument.h"
    31 #include "nsIFrame.h"
    32 #include "nsIWidget.h"
    33 #include "nsPresContext.h"
    34 #include "nsIPresShell.h"
    35 #include "nsGkAtoms.h"
    36 #include "nsIFormControl.h"
    37 #include "nsIComboboxControlFrame.h"
    38 #include "nsIScrollableFrame.h"
    39 #include "nsIDOMHTMLElement.h"
    40 #include "nsIDOMXULControlElement.h"
    41 #include "nsNameSpaceManager.h"
    42 #include "nsIBaseWindow.h"
    43 #include "nsISelection.h"
    44 #include "nsITextControlElement.h"
    45 #include "nsFrameSelection.h"
    46 #include "nsPIDOMWindow.h"
    47 #include "nsPIWindowRoot.h"
    48 #include "nsIWebNavigation.h"
    49 #include "nsIContentViewer.h"
    50 #include "nsFrameManager.h"
    52 #include "nsIDOMXULElement.h"
    53 #include "nsIDOMKeyEvent.h"
    54 #include "nsIObserverService.h"
    55 #include "nsIDocShell.h"
    56 #include "nsIMarkupDocumentViewer.h"
    57 #include "nsIDOMWheelEvent.h"
    58 #include "nsIDOMDragEvent.h"
    59 #include "nsIDOMUIEvent.h"
    60 #include "nsIMozBrowserFrame.h"
    62 #include "nsSubDocumentFrame.h"
    63 #include "nsLayoutUtils.h"
    64 #include "nsIInterfaceRequestorUtils.h"
    65 #include "nsUnicharUtils.h"
    66 #include "nsContentUtils.h"
    68 #include "imgIContainer.h"
    69 #include "nsIProperties.h"
    70 #include "nsISupportsPrimitives.h"
    72 #include "nsServiceManagerUtils.h"
    73 #include "nsITimer.h"
    74 #include "nsFontMetrics.h"
    75 #include "nsIDOMXULDocument.h"
    76 #include "nsIDragService.h"
    77 #include "nsIDragSession.h"
    78 #include "mozilla/dom/DataTransfer.h"
    79 #include "nsContentAreaDragDrop.h"
    80 #ifdef MOZ_XUL
    81 #include "nsTreeBodyFrame.h"
    82 #endif
    83 #include "nsIController.h"
    84 #include "nsICommandParams.h"
    85 #include "mozilla/Services.h"
    86 #include "mozilla/dom/HTMLLabelElement.h"
    88 #include "mozilla/Preferences.h"
    89 #include "mozilla/LookAndFeel.h"
    90 #include "GeckoProfiler.h"
    91 #include "Units.h"
    93 #ifdef XP_MACOSX
    94 #import <ApplicationServices/ApplicationServices.h>
    95 #endif
    97 namespace mozilla {
    99 using namespace dom;
   101 //#define DEBUG_DOCSHELL_FOCUS
   103 #define NS_USER_INTERACTION_INTERVAL 5000 // ms
   105 static const LayoutDeviceIntPoint kInvalidRefPoint = LayoutDeviceIntPoint(-1,-1);
   107 static uint32_t gMouseOrKeyboardEventCounter = 0;
   108 static nsITimer* gUserInteractionTimer = nullptr;
   109 static nsITimerCallback* gUserInteractionTimerCallback = nullptr;
   111 static inline int32_t
   112 RoundDown(double aDouble)
   113 {
   114   return (aDouble > 0) ? static_cast<int32_t>(floor(aDouble)) :
   115                          static_cast<int32_t>(ceil(aDouble));
   116 }
   118 #ifdef DEBUG_DOCSHELL_FOCUS
   119 static void
   120 PrintDocTree(nsIDocShellTreeItem* aParentItem, int aLevel)
   121 {
   122   for (int32_t i=0;i<aLevel;i++) printf("  ");
   124   int32_t childWebshellCount;
   125   aParentItem->GetChildCount(&childWebshellCount);
   126   nsCOMPtr<nsIDocShell> parentAsDocShell(do_QueryInterface(aParentItem));
   127   int32_t type = aParentItem->ItemType();
   128   nsCOMPtr<nsIPresShell> presShell = parentAsDocShell->GetPresShell();
   129   nsRefPtr<nsPresContext> presContext;
   130   parentAsDocShell->GetPresContext(getter_AddRefs(presContext));
   131   nsCOMPtr<nsIContentViewer> cv;
   132   parentAsDocShell->GetContentViewer(getter_AddRefs(cv));
   133   nsCOMPtr<nsIDOMDocument> domDoc;
   134   if (cv)
   135     cv->GetDOMDocument(getter_AddRefs(domDoc));
   136   nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
   137   nsCOMPtr<nsIDOMWindow> domwin = doc ? doc->GetWindow() : nullptr;
   138   nsIURI* uri = doc ? doc->GetDocumentURI() : nullptr;
   140   printf("DS %p  Type %s  Cnt %d  Doc %p  DW %p  EM %p%c",
   141     static_cast<void*>(parentAsDocShell.get()),
   142     type==nsIDocShellTreeItem::typeChrome?"Chrome":"Content",
   143     childWebshellCount, static_cast<void*>(doc.get()),
   144     static_cast<void*>(domwin.get()),
   145     static_cast<void*>(presContext ? presContext->EventStateManager() : nullptr),
   146     uri ? ' ' : '\n');
   147   if (uri) {
   148     nsAutoCString spec;
   149     uri->GetSpec(spec);
   150     printf("\"%s\"\n", spec.get());
   151   }
   153   if (childWebshellCount > 0) {
   154     for (int32_t i = 0; i < childWebshellCount; i++) {
   155       nsCOMPtr<nsIDocShellTreeItem> child;
   156       aParentItem->GetChildAt(i, getter_AddRefs(child));
   157       PrintDocTree(child, aLevel + 1);
   158     }
   159   }
   160 }
   162 static void
   163 PrintDocTreeAll(nsIDocShellTreeItem* aItem)
   164 {
   165   nsCOMPtr<nsIDocShellTreeItem> item = aItem;
   166   for(;;) {
   167     nsCOMPtr<nsIDocShellTreeItem> parent;
   168     item->GetParent(getter_AddRefs(parent));
   169     if (!parent)
   170       break;
   171     item = parent;
   172   }
   174   PrintDocTree(item, 0);
   175 }
   176 #endif
   178 // mask values for ui.key.chromeAccess and ui.key.contentAccess
   179 #define NS_MODIFIER_SHIFT    1
   180 #define NS_MODIFIER_CONTROL  2
   181 #define NS_MODIFIER_ALT      4
   182 #define NS_MODIFIER_META     8
   183 #define NS_MODIFIER_OS       16
   185 static nsIDocument *
   186 GetDocumentFromWindow(nsIDOMWindow *aWindow)
   187 {
   188   nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(aWindow);
   189   return win ? win->GetExtantDoc() : nullptr;
   190 }
   192 /******************************************************************/
   193 /* mozilla::UITimerCallback                                       */
   194 /******************************************************************/
   196 class UITimerCallback MOZ_FINAL : public nsITimerCallback
   197 {
   198 public:
   199   UITimerCallback() : mPreviousCount(0) {}
   200   NS_DECL_ISUPPORTS
   201   NS_DECL_NSITIMERCALLBACK
   202 private:
   203   uint32_t mPreviousCount;
   204 };
   206 NS_IMPL_ISUPPORTS(UITimerCallback, nsITimerCallback)
   208 // If aTimer is nullptr, this method always sends "user-interaction-inactive"
   209 // notification.
   210 NS_IMETHODIMP
   211 UITimerCallback::Notify(nsITimer* aTimer)
   212 {
   213   nsCOMPtr<nsIObserverService> obs =
   214     mozilla::services::GetObserverService();
   215   if (!obs)
   216     return NS_ERROR_FAILURE;
   217   if ((gMouseOrKeyboardEventCounter == mPreviousCount) || !aTimer) {
   218     gMouseOrKeyboardEventCounter = 0;
   219     obs->NotifyObservers(nullptr, "user-interaction-inactive", nullptr);
   220     if (gUserInteractionTimer) {
   221       gUserInteractionTimer->Cancel();
   222       NS_RELEASE(gUserInteractionTimer);
   223     }
   224   } else {
   225     obs->NotifyObservers(nullptr, "user-interaction-active", nullptr);
   226     EventStateManager::UpdateUserActivityTimer();
   227   }
   228   mPreviousCount = gMouseOrKeyboardEventCounter;
   229   return NS_OK;
   230 }
   232 /******************************************************************/
   233 /* mozilla::OverOutElementsWrapper                                */
   234 /******************************************************************/
   236 OverOutElementsWrapper::OverOutElementsWrapper()
   237   : mLastOverFrame(nullptr)
   238 {
   239 }
   241 OverOutElementsWrapper::~OverOutElementsWrapper()
   242 {
   243 }
   245 NS_IMPL_CYCLE_COLLECTION(OverOutElementsWrapper,
   246                          mLastOverElement,
   247                          mFirstOverEventElement,
   248                          mFirstOutEventElement)
   249 NS_IMPL_CYCLE_COLLECTING_ADDREF(OverOutElementsWrapper)
   250 NS_IMPL_CYCLE_COLLECTING_RELEASE(OverOutElementsWrapper)
   252 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(OverOutElementsWrapper)
   253   NS_INTERFACE_MAP_ENTRY(nsISupports)
   254 NS_INTERFACE_MAP_END
   256 /******************************************************************/
   257 /* mozilla::EventStateManager                                     */
   258 /******************************************************************/
   260 static uint32_t sESMInstanceCount = 0;
   262 int32_t EventStateManager::sUserInputEventDepth = 0;
   263 bool EventStateManager::sNormalLMouseEventInProcess = false;
   264 EventStateManager* EventStateManager::sActiveESM = nullptr;
   265 nsIDocument* EventStateManager::sMouseOverDocument = nullptr;
   266 nsWeakFrame EventStateManager::sLastDragOverFrame = nullptr;
   267 LayoutDeviceIntPoint EventStateManager::sLastRefPoint = kInvalidRefPoint;
   268 nsIntPoint EventStateManager::sLastScreenPoint = nsIntPoint(0, 0);
   269 LayoutDeviceIntPoint EventStateManager::sSynthCenteringPoint = kInvalidRefPoint;
   270 CSSIntPoint EventStateManager::sLastClientPoint = CSSIntPoint(0, 0);
   271 bool EventStateManager::sIsPointerLocked = false;
   272 // Reference to the pointer locked element.
   273 nsWeakPtr EventStateManager::sPointerLockedElement;
   274 // Reference to the document which requested pointer lock.
   275 nsWeakPtr EventStateManager::sPointerLockedDoc;
   276 nsCOMPtr<nsIContent> EventStateManager::sDragOverContent = nullptr;
   277 TimeStamp EventStateManager::sHandlingInputStart;
   279 EventStateManager::WheelPrefs*
   280   EventStateManager::WheelPrefs::sInstance = nullptr;
   281 EventStateManager::DeltaAccumulator*
   282   EventStateManager::DeltaAccumulator::sInstance = nullptr;
   284 EventStateManager::EventStateManager()
   285   : mLockCursor(0)
   286   , mPreLockPoint(0,0)
   287   , mCurrentTarget(nullptr)
   288     // init d&d gesture state machine variables
   289   , mGestureDownPoint(0,0)
   290   , mPresContext(nullptr)
   291   , mLClickCount(0)
   292   , mMClickCount(0)
   293   , mRClickCount(0)
   294   , m_haveShutdown(false)
   295 {
   296   if (sESMInstanceCount == 0) {
   297     gUserInteractionTimerCallback = new UITimerCallback();
   298     if (gUserInteractionTimerCallback)
   299       NS_ADDREF(gUserInteractionTimerCallback);
   300     UpdateUserActivityTimer();
   301   }
   302   ++sESMInstanceCount;
   303 }
   305 nsresult
   306 EventStateManager::UpdateUserActivityTimer()
   307 {
   308   if (!gUserInteractionTimerCallback)
   309     return NS_OK;
   311   if (!gUserInteractionTimer)
   312     CallCreateInstance("@mozilla.org/timer;1", &gUserInteractionTimer);
   314   if (gUserInteractionTimer) {
   315     gUserInteractionTimer->InitWithCallback(gUserInteractionTimerCallback,
   316                                             NS_USER_INTERACTION_INTERVAL,
   317                                             nsITimer::TYPE_ONE_SHOT);
   318   }
   319   return NS_OK;
   320 }
   322 nsresult
   323 EventStateManager::Init()
   324 {
   325   nsCOMPtr<nsIObserverService> observerService =
   326     mozilla::services::GetObserverService();
   327   if (!observerService)
   328     return NS_ERROR_FAILURE;
   330   observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, true);
   332   if (sESMInstanceCount == 1) {
   333     Prefs::Init();
   334   }
   336   return NS_OK;
   337 }
   339 EventStateManager::~EventStateManager()
   340 {
   341   ReleaseCurrentIMEContentObserver();
   343   if (sActiveESM == this) {
   344     sActiveESM = nullptr;
   345   }
   346   if (Prefs::ClickHoldContextMenu())
   347     KillClickHoldTimer();
   349   if (mDocument == sMouseOverDocument)
   350     sMouseOverDocument = nullptr;
   352   --sESMInstanceCount;
   353   if(sESMInstanceCount == 0) {
   354     WheelTransaction::Shutdown();
   355     if (gUserInteractionTimerCallback) {
   356       gUserInteractionTimerCallback->Notify(nullptr);
   357       NS_RELEASE(gUserInteractionTimerCallback);
   358     }
   359     if (gUserInteractionTimer) {
   360       gUserInteractionTimer->Cancel();
   361       NS_RELEASE(gUserInteractionTimer);
   362     }
   363     Prefs::Shutdown();
   364     WheelPrefs::Shutdown();
   365     DeltaAccumulator::Shutdown();
   366   }
   368   if (sDragOverContent && sDragOverContent->OwnerDoc() == mDocument) {
   369     sDragOverContent = nullptr;
   370   }
   372   if (!m_haveShutdown) {
   373     Shutdown();
   375     // Don't remove from Observer service in Shutdown because Shutdown also
   376     // gets called from xpcom shutdown observer.  And we don't want to remove
   377     // from the service in that case.
   379     nsCOMPtr<nsIObserverService> observerService =
   380       mozilla::services::GetObserverService();
   381     if (observerService) {
   382       observerService->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
   383     }
   384   }
   386 }
   388 nsresult
   389 EventStateManager::Shutdown()
   390 {
   391   m_haveShutdown = true;
   392   return NS_OK;
   393 }
   395 NS_IMETHODIMP
   396 EventStateManager::Observe(nsISupports* aSubject,
   397                            const char* aTopic,
   398                            const char16_t *someData)
   399 {
   400   if (!nsCRT::strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
   401     Shutdown();
   402   }
   404   return NS_OK;
   405 }
   407 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(EventStateManager)
   408    NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver)
   409    NS_INTERFACE_MAP_ENTRY(nsIObserver)
   410    NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
   411 NS_INTERFACE_MAP_END
   413 NS_IMPL_CYCLE_COLLECTING_ADDREF(EventStateManager)
   414 NS_IMPL_CYCLE_COLLECTING_RELEASE(EventStateManager)
   416 NS_IMPL_CYCLE_COLLECTION(EventStateManager,
   417                          mCurrentTargetContent,
   418                          mGestureDownContent,
   419                          mGestureDownFrameOwner,
   420                          mLastLeftMouseDownContent,
   421                          mLastLeftMouseDownContentParent,
   422                          mLastMiddleMouseDownContent,
   423                          mLastMiddleMouseDownContentParent,
   424                          mLastRightMouseDownContent,
   425                          mLastRightMouseDownContentParent,
   426                          mActiveContent,
   427                          mHoverContent,
   428                          mURLTargetContent,
   429                          mMouseEnterLeaveHelper,
   430                          mPointersEnterLeaveHelper,
   431                          mDocument,
   432                          mIMEContentObserver,
   433                          mAccessKeys)
   435 void
   436 EventStateManager::ReleaseCurrentIMEContentObserver()
   437 {
   438   if (mIMEContentObserver) {
   439     mIMEContentObserver->DisconnectFromEventStateManager();
   440   }
   441   mIMEContentObserver = nullptr;
   442 }
   444 void
   445 EventStateManager::OnStartToObserveContent(
   446                      IMEContentObserver* aIMEContentObserver)
   447 {
   448   ReleaseCurrentIMEContentObserver();
   449   mIMEContentObserver = aIMEContentObserver;
   450 }
   452 void
   453 EventStateManager::OnStopObservingContent(
   454                      IMEContentObserver* aIMEContentObserver)
   455 {
   456   aIMEContentObserver->DisconnectFromEventStateManager();
   457   NS_ENSURE_TRUE_VOID(mIMEContentObserver == aIMEContentObserver);
   458   mIMEContentObserver = nullptr;
   459 }
   461 nsresult
   462 EventStateManager::PreHandleEvent(nsPresContext* aPresContext,
   463                                   WidgetEvent* aEvent,
   464                                   nsIFrame* aTargetFrame,
   465                                   nsEventStatus* aStatus)
   466 {
   467   NS_ENSURE_ARG_POINTER(aStatus);
   468   NS_ENSURE_ARG(aPresContext);
   469   if (!aEvent) {
   470     NS_ERROR("aEvent is null.  This should never happen.");
   471     return NS_ERROR_NULL_POINTER;
   472   }
   474   mCurrentTarget = aTargetFrame;
   475   mCurrentTargetContent = nullptr;
   477   // Focus events don't necessarily need a frame.
   478   if (NS_EVENT_NEEDS_FRAME(aEvent)) {
   479     NS_ASSERTION(mCurrentTarget, "mCurrentTarget is null.  this should not happen.  see bug #13007");
   480     if (!mCurrentTarget) return NS_ERROR_NULL_POINTER;
   481   }
   482 #ifdef DEBUG
   483   if (aEvent->HasDragEventMessage() && sIsPointerLocked) {
   484     NS_ASSERTION(sIsPointerLocked,
   485       "sIsPointerLocked is true. Drag events should be suppressed when the pointer is locked.");
   486   }
   487 #endif
   488   // Store last known screenPoint and clientPoint so pointer lock
   489   // can use these values as constants.
   490   WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
   491   if (aEvent->mFlags.mIsTrusted &&
   492       ((mouseEvent && mouseEvent->IsReal()) ||
   493        aEvent->eventStructType == NS_WHEEL_EVENT) &&
   494       !sIsPointerLocked) {
   495     sLastScreenPoint =
   496       UIEvent::CalculateScreenPoint(aPresContext, aEvent);
   497     sLastClientPoint =
   498       UIEvent::CalculateClientPoint(aPresContext, aEvent, nullptr);
   499   }
   501   // Do not take account NS_MOUSE_ENTER/EXIT so that loading a page
   502   // when user is not active doesn't change the state to active.
   503   if (aEvent->mFlags.mIsTrusted &&
   504       ((mouseEvent && mouseEvent->IsReal() &&
   505         mouseEvent->message != NS_MOUSE_ENTER &&
   506         mouseEvent->message != NS_MOUSE_EXIT) ||
   507        aEvent->eventStructType == NS_WHEEL_EVENT ||
   508        aEvent->eventStructType == NS_KEY_EVENT)) {
   509     if (gMouseOrKeyboardEventCounter == 0) {
   510       nsCOMPtr<nsIObserverService> obs =
   511         mozilla::services::GetObserverService();
   512       if (obs) {
   513         obs->NotifyObservers(nullptr, "user-interaction-active", nullptr);
   514         UpdateUserActivityTimer();
   515       }
   516     }
   517     ++gMouseOrKeyboardEventCounter;
   518   }
   520   *aStatus = nsEventStatus_eIgnore;
   522   WheelTransaction::OnEvent(aEvent);
   524   switch (aEvent->message) {
   525   case NS_MOUSE_BUTTON_DOWN: {
   526     switch (mouseEvent->button) {
   527     case WidgetMouseEvent::eLeftButton:
   528       BeginTrackingDragGesture(aPresContext, mouseEvent, aTargetFrame);
   529       mLClickCount = mouseEvent->clickCount;
   530       SetClickCount(aPresContext, mouseEvent, aStatus);
   531       sNormalLMouseEventInProcess = true;
   532       break;
   533     case WidgetMouseEvent::eMiddleButton:
   534       mMClickCount = mouseEvent->clickCount;
   535       SetClickCount(aPresContext, mouseEvent, aStatus);
   536       break;
   537     case WidgetMouseEvent::eRightButton:
   538       mRClickCount = mouseEvent->clickCount;
   539       SetClickCount(aPresContext, mouseEvent, aStatus);
   540       break;
   541     }
   542     break;
   543   }
   544   case NS_MOUSE_BUTTON_UP: {
   545     switch (mouseEvent->button) {
   546       case WidgetMouseEvent::eLeftButton:
   547         if (Prefs::ClickHoldContextMenu()) {
   548           KillClickHoldTimer();
   549         }
   550         StopTrackingDragGesture();
   551         sNormalLMouseEventInProcess = false;
   552         // then fall through...
   553       case WidgetMouseEvent::eRightButton:
   554       case WidgetMouseEvent::eMiddleButton:
   555         SetClickCount(aPresContext, mouseEvent, aStatus);
   556         break;
   557     }
   558     break;
   559   }
   560   case NS_POINTER_CANCEL:
   561   {
   562     GenerateMouseEnterExit(mouseEvent);
   563     break;
   564   }
   565   case NS_MOUSE_EXIT:
   566     // If the event is not a top-level window exit, then it's not
   567     // really an exit --- we may have traversed widget boundaries but
   568     // we're still in our toplevel window.
   569     if (mouseEvent->exit != WidgetMouseEvent::eTopLevel) {
   570       // Treat it as a synthetic move so we don't generate spurious
   571       // "exit" or "move" events.  Any necessary "out" or "over" events
   572       // will be generated by GenerateMouseEnterExit
   573       mouseEvent->message = NS_MOUSE_MOVE;
   574       mouseEvent->reason = WidgetMouseEvent::eSynthesized;
   575       // then fall through...
   576     } else {
   577       GenerateMouseEnterExit(mouseEvent);
   578       //This is a window level mouse exit event and should stop here
   579       aEvent->message = 0;
   580       break;
   581     }
   582   case NS_MOUSE_MOVE:
   583   case NS_POINTER_DOWN:
   584   case NS_POINTER_MOVE: {
   585     // on the Mac, GenerateDragGesture() may not return until the drag
   586     // has completed and so |aTargetFrame| may have been deleted (moving
   587     // a bookmark, for example).  If this is the case, however, we know
   588     // that ClearFrameRefs() has been called and it cleared out
   589     // |mCurrentTarget|. As a result, we should pass |mCurrentTarget|
   590     // into UpdateCursor().
   591     GenerateDragGesture(aPresContext, mouseEvent);
   592     UpdateCursor(aPresContext, aEvent, mCurrentTarget, aStatus);
   593     GenerateMouseEnterExit(mouseEvent);
   594     // Flush pending layout changes, so that later mouse move events
   595     // will go to the right nodes.
   596     FlushPendingEvents(aPresContext);
   597     break;
   598   }
   599   case NS_DRAGDROP_GESTURE:
   600     if (Prefs::ClickHoldContextMenu()) {
   601       // an external drag gesture event came in, not generated internally
   602       // by Gecko. Make sure we get rid of the click-hold timer.
   603       KillClickHoldTimer();
   604     }
   605     break;
   606   case NS_DRAGDROP_OVER:
   607     // NS_DRAGDROP_DROP is fired before NS_DRAGDROP_DRAGDROP so send
   608     // the enter/exit events before NS_DRAGDROP_DROP.
   609     GenerateDragDropEnterExit(aPresContext, aEvent->AsDragEvent());
   610     break;
   612   case NS_KEY_PRESS:
   613     {
   614       WidgetKeyboardEvent* keyEvent = aEvent->AsKeyboardEvent();
   616       int32_t modifierMask = 0;
   617       if (keyEvent->IsShift())
   618         modifierMask |= NS_MODIFIER_SHIFT;
   619       if (keyEvent->IsControl())
   620         modifierMask |= NS_MODIFIER_CONTROL;
   621       if (keyEvent->IsAlt())
   622         modifierMask |= NS_MODIFIER_ALT;
   623       if (keyEvent->IsMeta())
   624         modifierMask |= NS_MODIFIER_META;
   625       if (keyEvent->IsOS())
   626         modifierMask |= NS_MODIFIER_OS;
   628       // Prevent keyboard scrolling while an accesskey modifier is in use.
   629       if (modifierMask &&
   630           (modifierMask == Prefs::ChromeAccessModifierMask() ||
   631            modifierMask == Prefs::ContentAccessModifierMask())) {
   632         HandleAccessKey(aPresContext, keyEvent, aStatus, nullptr,
   633                         eAccessKeyProcessingNormal, modifierMask);
   634       }
   635     }
   636     // then fall through...
   637   case NS_KEY_DOWN:
   638   case NS_KEY_UP:
   639     {
   640       nsIContent* content = GetFocusedContent();
   641       if (content)
   642         mCurrentTargetContent = content;
   644       // NOTE: Don't refer TextComposition::IsComposing() since DOM Level 3
   645       //       Events defines that KeyboardEvent.isComposing is true when it's
   646       //       dispatched after compositionstart and compositionend.
   647       //       TextComposition::IsComposing() is false even before
   648       //       compositionend if there is no composing string.
   649       WidgetKeyboardEvent* keyEvent = aEvent->AsKeyboardEvent();
   650       nsRefPtr<TextComposition> composition =
   651         IMEStateManager::GetTextCompositionFor(keyEvent);
   652       keyEvent->mIsComposing = !!composition;
   653     }
   654     break;
   655   case NS_WHEEL_WHEEL:
   656   case NS_WHEEL_START:
   657   case NS_WHEEL_STOP:
   658     {
   659       NS_ASSERTION(aEvent->mFlags.mIsTrusted,
   660                    "Untrusted wheel event shouldn't be here");
   662       nsIContent* content = GetFocusedContent();
   663       if (content) {
   664         mCurrentTargetContent = content;
   665       }
   667       if (aEvent->message != NS_WHEEL_WHEEL) {
   668         break;
   669       }
   671       WidgetWheelEvent* wheelEvent = aEvent->AsWheelEvent();
   672       WheelPrefs::GetInstance()->ApplyUserPrefsToDelta(wheelEvent);
   674       // If we won't dispatch a DOM event for this event, nothing to do anymore.
   675       if (!wheelEvent->IsAllowedToDispatchDOMEvent()) {
   676         break;
   677       }
   679       // Init lineOrPageDelta values for line scroll events for some devices
   680       // on some platforms which might dispatch wheel events which don't have
   681       // lineOrPageDelta values.  And also, if delta values are customized by
   682       // prefs, this recomputes them.
   683       DeltaAccumulator::GetInstance()->
   684         InitLineOrPageDelta(aTargetFrame, this, wheelEvent);
   685     }
   686     break;
   687   case NS_QUERY_SELECTED_TEXT:
   688     DoQuerySelectedText(aEvent->AsQueryContentEvent());
   689     break;
   690   case NS_QUERY_TEXT_CONTENT:
   691     {
   692       if (RemoteQueryContentEvent(aEvent)) {
   693         break;
   694       }
   695       ContentEventHandler handler(mPresContext);
   696       handler.OnQueryTextContent(aEvent->AsQueryContentEvent());
   697     }
   698     break;
   699   case NS_QUERY_CARET_RECT:
   700     {
   701       if (RemoteQueryContentEvent(aEvent)) {
   702         break;
   703       }
   704       ContentEventHandler handler(mPresContext);
   705       handler.OnQueryCaretRect(aEvent->AsQueryContentEvent());
   706     }
   707     break;
   708   case NS_QUERY_TEXT_RECT:
   709     {
   710       if (RemoteQueryContentEvent(aEvent)) {
   711         break;
   712       }
   713       ContentEventHandler handler(mPresContext);
   714       handler.OnQueryTextRect(aEvent->AsQueryContentEvent());
   715     }
   716     break;
   717   case NS_QUERY_EDITOR_RECT:
   718     {
   719       // XXX remote event
   720       ContentEventHandler handler(mPresContext);
   721       handler.OnQueryEditorRect(aEvent->AsQueryContentEvent());
   722     }
   723     break;
   724   case NS_QUERY_CONTENT_STATE:
   725     {
   726       // XXX remote event
   727       ContentEventHandler handler(mPresContext);
   728       handler.OnQueryContentState(aEvent->AsQueryContentEvent());
   729     }
   730     break;
   731   case NS_QUERY_SELECTION_AS_TRANSFERABLE:
   732     {
   733       // XXX remote event
   734       ContentEventHandler handler(mPresContext);
   735       handler.OnQuerySelectionAsTransferable(aEvent->AsQueryContentEvent());
   736     }
   737     break;
   738   case NS_QUERY_CHARACTER_AT_POINT:
   739     {
   740       // XXX remote event
   741       ContentEventHandler handler(mPresContext);
   742       handler.OnQueryCharacterAtPoint(aEvent->AsQueryContentEvent());
   743     }
   744     break;
   745   case NS_QUERY_DOM_WIDGET_HITTEST:
   746     {
   747       // XXX remote event
   748       ContentEventHandler handler(mPresContext);
   749       handler.OnQueryDOMWidgetHittest(aEvent->AsQueryContentEvent());
   750     }
   751     break;
   752   case NS_SELECTION_SET:
   753     {
   754       WidgetSelectionEvent* selectionEvent = aEvent->AsSelectionEvent();
   755       if (IsTargetCrossProcess(selectionEvent)) {
   756         // Will not be handled locally, remote the event
   757         if (GetCrossProcessTarget()->SendSelectionEvent(*selectionEvent)) {
   758           selectionEvent->mSucceeded = true;
   759         }
   760         break;
   761       }
   762       ContentEventHandler handler(mPresContext);
   763       handler.OnSelectionEvent(selectionEvent);
   764     }
   765     break;
   766   case NS_CONTENT_COMMAND_CUT:
   767   case NS_CONTENT_COMMAND_COPY:
   768   case NS_CONTENT_COMMAND_PASTE:
   769   case NS_CONTENT_COMMAND_DELETE:
   770   case NS_CONTENT_COMMAND_UNDO:
   771   case NS_CONTENT_COMMAND_REDO:
   772   case NS_CONTENT_COMMAND_PASTE_TRANSFERABLE:
   773     {
   774       DoContentCommandEvent(aEvent->AsContentCommandEvent());
   775     }
   776     break;
   777   case NS_CONTENT_COMMAND_SCROLL:
   778     {
   779       DoContentCommandScrollEvent(aEvent->AsContentCommandEvent());
   780     }
   781     break;
   782   case NS_TEXT_TEXT:
   783     {
   784       WidgetTextEvent *textEvent = aEvent->AsTextEvent();
   785       if (IsTargetCrossProcess(textEvent)) {
   786         // Will not be handled locally, remote the event
   787         if (GetCrossProcessTarget()->SendTextEvent(*textEvent)) {
   788           // Cancel local dispatching
   789           aEvent->mFlags.mPropagationStopped = true;
   790         }
   791       }
   792     }
   793     break;
   794   case NS_COMPOSITION_START:
   795     if (aEvent->mFlags.mIsTrusted) {
   796       // If the event is trusted event, set the selected text to data of
   797       // composition event.
   798       WidgetCompositionEvent* compositionEvent = aEvent->AsCompositionEvent();
   799       WidgetQueryContentEvent selectedText(true, NS_QUERY_SELECTED_TEXT,
   800                                            compositionEvent->widget);
   801       DoQuerySelectedText(&selectedText);
   802       NS_ASSERTION(selectedText.mSucceeded, "Failed to get selected text");
   803       compositionEvent->data = selectedText.mReply.mString;
   804     }
   805     // through to compositionend handling
   806   case NS_COMPOSITION_UPDATE:
   807   case NS_COMPOSITION_END:
   808     {
   809       WidgetCompositionEvent* compositionEvent = aEvent->AsCompositionEvent();
   810       if (IsTargetCrossProcess(compositionEvent)) {
   811         // Will not be handled locally, remote the event
   812         if (GetCrossProcessTarget()->SendCompositionEvent(*compositionEvent)) {
   813           // Cancel local dispatching
   814           aEvent->mFlags.mPropagationStopped = true;
   815         }
   816       }
   817     }
   818     break;
   819   }
   820   return NS_OK;
   821 }
   823 // static
   824 int32_t
   825 EventStateManager::GetAccessModifierMaskFor(nsISupports* aDocShell)
   826 {
   827   nsCOMPtr<nsIDocShellTreeItem> treeItem(do_QueryInterface(aDocShell));
   828   if (!treeItem)
   829     return -1; // invalid modifier
   831   switch (treeItem->ItemType()) {
   832   case nsIDocShellTreeItem::typeChrome:
   833     return Prefs::ChromeAccessModifierMask();
   835   case nsIDocShellTreeItem::typeContent:
   836     return Prefs::ContentAccessModifierMask();
   838   default:
   839     return -1; // invalid modifier
   840   }
   841 }
   843 static bool
   844 IsAccessKeyTarget(nsIContent* aContent, nsIFrame* aFrame, nsAString& aKey)
   845 {
   846   // Use GetAttr because we want Unicode case=insensitive matching
   847   // XXXbz shouldn't this be case-sensitive, per spec?
   848   nsString contentKey;
   849   if (!aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::accesskey, contentKey) ||
   850       !contentKey.Equals(aKey, nsCaseInsensitiveStringComparator()))
   851     return false;
   853   nsCOMPtr<nsIDOMXULDocument> xulDoc =
   854     do_QueryInterface(aContent->OwnerDoc());
   855   if (!xulDoc && !aContent->IsXUL())
   856     return true;
   858     // For XUL we do visibility checks.
   859   if (!aFrame)
   860     return false;
   862   if (aFrame->IsFocusable())
   863     return true;
   865   if (!aFrame->IsVisibleConsideringAncestors())
   866     return false;
   868   // XUL controls can be activated.
   869   nsCOMPtr<nsIDOMXULControlElement> control(do_QueryInterface(aContent));
   870   if (control)
   871     return true;
   873   if (aContent->IsHTML()) {
   874     nsIAtom* tag = aContent->Tag();
   876     // HTML area, label and legend elements are never focusable, so
   877     // we need to check for them explicitly before giving up.
   878     if (tag == nsGkAtoms::area ||
   879         tag == nsGkAtoms::label ||
   880         tag == nsGkAtoms::legend)
   881       return true;
   883   } else if (aContent->IsXUL()) {
   884     // XUL label elements are never focusable, so we need to check for them
   885     // explicitly before giving up.
   886     if (aContent->Tag() == nsGkAtoms::label)
   887       return true;
   888   }
   890   return false;
   891 }
   893 bool
   894 EventStateManager::ExecuteAccessKey(nsTArray<uint32_t>& aAccessCharCodes,
   895                                     bool aIsTrustedEvent)
   896 {
   897   int32_t count, start = -1;
   898   nsIContent* focusedContent = GetFocusedContent();
   899   if (focusedContent) {
   900     start = mAccessKeys.IndexOf(focusedContent);
   901     if (start == -1 && focusedContent->GetBindingParent())
   902       start = mAccessKeys.IndexOf(focusedContent->GetBindingParent());
   903   }
   904   nsIContent *content;
   905   nsIFrame *frame;
   906   int32_t length = mAccessKeys.Count();
   907   for (uint32_t i = 0; i < aAccessCharCodes.Length(); ++i) {
   908     uint32_t ch = aAccessCharCodes[i];
   909     nsAutoString accessKey;
   910     AppendUCS4ToUTF16(ch, accessKey);
   911     for (count = 1; count <= length; ++count) {
   912       content = mAccessKeys[(start + count) % length];
   913       frame = content->GetPrimaryFrame();
   914       if (IsAccessKeyTarget(content, frame, accessKey)) {
   915         bool shouldActivate = Prefs::KeyCausesActivation();
   916         while (shouldActivate && ++count <= length) {
   917           nsIContent *oc = mAccessKeys[(start + count) % length];
   918           nsIFrame *of = oc->GetPrimaryFrame();
   919           if (IsAccessKeyTarget(oc, of, accessKey))
   920             shouldActivate = false;
   921         }
   922         if (shouldActivate)
   923           content->PerformAccesskey(shouldActivate, aIsTrustedEvent);
   924         else {
   925           nsIFocusManager* fm = nsFocusManager::GetFocusManager();
   926           if (fm) {
   927             nsCOMPtr<nsIDOMElement> element = do_QueryInterface(content);
   928             fm->SetFocus(element, nsIFocusManager::FLAG_BYKEY);
   929           }
   930         }
   931         return true;
   932       }
   933     }
   934   }
   935   return false;
   936 }
   938 bool
   939 EventStateManager::GetAccessKeyLabelPrefix(nsAString& aPrefix)
   940 {
   941   aPrefix.Truncate();
   942   nsAutoString separator, modifierText;
   943   nsContentUtils::GetModifierSeparatorText(separator);
   945   nsCOMPtr<nsISupports> container = mPresContext->GetContainerWeak();
   946   int32_t modifierMask = GetAccessModifierMaskFor(container);
   948   if (modifierMask & NS_MODIFIER_CONTROL) {
   949     nsContentUtils::GetControlText(modifierText);
   950     aPrefix.Append(modifierText + separator);
   951   }
   952   if (modifierMask & NS_MODIFIER_META) {
   953     nsContentUtils::GetMetaText(modifierText);
   954     aPrefix.Append(modifierText + separator);
   955   }
   956   if (modifierMask & NS_MODIFIER_OS) {
   957     nsContentUtils::GetOSText(modifierText);
   958     aPrefix.Append(modifierText + separator);
   959   }
   960   if (modifierMask & NS_MODIFIER_ALT) {
   961     nsContentUtils::GetAltText(modifierText);
   962     aPrefix.Append(modifierText + separator);
   963   }
   964   if (modifierMask & NS_MODIFIER_SHIFT) {
   965     nsContentUtils::GetShiftText(modifierText);
   966     aPrefix.Append(modifierText + separator);
   967   }
   968   return !aPrefix.IsEmpty();
   969 }
   971 void
   972 EventStateManager::HandleAccessKey(nsPresContext* aPresContext,
   973                                    WidgetKeyboardEvent* aEvent,
   974                                    nsEventStatus* aStatus,
   975                                    nsIDocShellTreeItem* aBubbledFrom,
   976                                    ProcessingAccessKeyState aAccessKeyState,
   977                                    int32_t aModifierMask)
   978 {
   979   nsCOMPtr<nsIDocShell> docShell = aPresContext->GetDocShell();
   981   // Alt or other accesskey modifier is down, we may need to do an accesskey
   982   if (mAccessKeys.Count() > 0 &&
   983       aModifierMask == GetAccessModifierMaskFor(docShell)) {
   984     // Someone registered an accesskey.  Find and activate it.
   985     nsAutoTArray<uint32_t, 10> accessCharCodes;
   986     nsContentUtils::GetAccessKeyCandidates(aEvent, accessCharCodes);
   987     if (ExecuteAccessKey(accessCharCodes, aEvent->mFlags.mIsTrusted)) {
   988       *aStatus = nsEventStatus_eConsumeNoDefault;
   989       return;
   990     }
   991   }
   993   // after the local accesskey handling
   994   if (nsEventStatus_eConsumeNoDefault != *aStatus) {
   995     // checking all sub docshells
   997     if (!docShell) {
   998       NS_WARNING("no docShellTreeNode for presContext");
   999       return;
  1002     int32_t childCount;
  1003     docShell->GetChildCount(&childCount);
  1004     for (int32_t counter = 0; counter < childCount; counter++) {
  1005       // Not processing the child which bubbles up the handling
  1006       nsCOMPtr<nsIDocShellTreeItem> subShellItem;
  1007       docShell->GetChildAt(counter, getter_AddRefs(subShellItem));
  1008       if (aAccessKeyState == eAccessKeyProcessingUp &&
  1009           subShellItem == aBubbledFrom)
  1010         continue;
  1012       nsCOMPtr<nsIDocShell> subDS = do_QueryInterface(subShellItem);
  1013       if (subDS && IsShellVisible(subDS)) {
  1014         nsCOMPtr<nsIPresShell> subPS = subDS->GetPresShell();
  1016         // Docshells need not have a presshell (eg. display:none
  1017         // iframes, docshells in transition between documents, etc).
  1018         if (!subPS) {
  1019           // Oh, well.  Just move on to the next child
  1020           continue;
  1023         nsPresContext *subPC = subPS->GetPresContext();
  1025         EventStateManager* esm =
  1026           static_cast<EventStateManager*>(subPC->EventStateManager());
  1028         if (esm)
  1029           esm->HandleAccessKey(subPC, aEvent, aStatus, nullptr,
  1030                                eAccessKeyProcessingDown, aModifierMask);
  1032         if (nsEventStatus_eConsumeNoDefault == *aStatus)
  1033           break;
  1036   }// if end . checking all sub docshell ends here.
  1038   // bubble up the process to the parent docshell if necessary
  1039   if (eAccessKeyProcessingDown != aAccessKeyState && nsEventStatus_eConsumeNoDefault != *aStatus) {
  1040     if (!docShell) {
  1041       NS_WARNING("no docShellTreeItem for presContext");
  1042       return;
  1045     nsCOMPtr<nsIDocShellTreeItem> parentShellItem;
  1046     docShell->GetParent(getter_AddRefs(parentShellItem));
  1047     nsCOMPtr<nsIDocShell> parentDS = do_QueryInterface(parentShellItem);
  1048     if (parentDS) {
  1049       nsCOMPtr<nsIPresShell> parentPS = parentDS->GetPresShell();
  1050       NS_ASSERTION(parentPS, "Our PresShell exists but the parent's does not?");
  1052       nsPresContext *parentPC = parentPS->GetPresContext();
  1053       NS_ASSERTION(parentPC, "PresShell without PresContext");
  1055       EventStateManager* esm =
  1056         static_cast<EventStateManager*>(parentPC->EventStateManager());
  1058       if (esm)
  1059         esm->HandleAccessKey(parentPC, aEvent, aStatus, docShell,
  1060                              eAccessKeyProcessingUp, aModifierMask);
  1062   }// if end. bubble up process
  1063 }// end of HandleAccessKey
  1065 bool
  1066 EventStateManager::DispatchCrossProcessEvent(WidgetEvent* aEvent,
  1067                                              nsFrameLoader* aFrameLoader,
  1068                                              nsEventStatus *aStatus) {
  1069   PBrowserParent* remoteBrowser = aFrameLoader->GetRemoteBrowser();
  1070   TabParent* remote = static_cast<TabParent*>(remoteBrowser);
  1071   if (!remote) {
  1072     return false;
  1075   switch (aEvent->eventStructType) {
  1076   case NS_MOUSE_EVENT: {
  1077     return remote->SendRealMouseEvent(*aEvent->AsMouseEvent());
  1079   case NS_KEY_EVENT: {
  1080     return remote->SendRealKeyEvent(*aEvent->AsKeyboardEvent());
  1082   case NS_WHEEL_EVENT: {
  1083     return remote->SendMouseWheelEvent(*aEvent->AsWheelEvent());
  1085   case NS_TOUCH_EVENT: {
  1086     // Let the child process synthesize a mouse event if needed, and
  1087     // ensure we don't synthesize one in this process.
  1088     *aStatus = nsEventStatus_eConsumeNoDefault;
  1089     return remote->SendRealTouchEvent(*aEvent->AsTouchEvent());
  1091   default: {
  1092     MOZ_CRASH("Attempt to send non-whitelisted event?");
  1097 bool
  1098 EventStateManager::IsRemoteTarget(nsIContent* target) {
  1099   if (!target) {
  1100     return false;
  1103   // <browser/iframe remote=true> from XUL
  1104   if ((target->Tag() == nsGkAtoms::browser ||
  1105        target->Tag() == nsGkAtoms::iframe) &&
  1106       target->IsXUL() &&
  1107       target->AttrValueIs(kNameSpaceID_None, nsGkAtoms::Remote,
  1108                           nsGkAtoms::_true, eIgnoreCase)) {
  1109     return true;
  1112   // <frame/iframe mozbrowser/mozapp>
  1113   nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(target);
  1114   if (browserFrame && browserFrame->GetReallyIsBrowserOrApp()) {
  1115     return !!TabParent::GetFrom(target);
  1118   return false;
  1121 /*static*/ LayoutDeviceIntPoint
  1122 EventStateManager::GetChildProcessOffset(nsFrameLoader* aFrameLoader,
  1123                                          const WidgetEvent& aEvent)
  1125   // The "toplevel widget" in child processes is always at position
  1126   // 0,0.  Map the event coordinates to match that.
  1127   nsIFrame* targetFrame = aFrameLoader->GetPrimaryFrameOfOwningContent();
  1128   if (!targetFrame) {
  1129     return LayoutDeviceIntPoint();
  1131   nsPresContext* presContext = targetFrame->PresContext();
  1133   // Find out how far we're offset from the nearest widget.
  1134   nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(&aEvent,
  1135                                                             targetFrame);
  1136   return LayoutDeviceIntPoint::FromAppUnitsToNearest(pt, presContext->AppUnitsPerDevPixel());
  1139 bool
  1140 CrossProcessSafeEvent(const WidgetEvent& aEvent)
  1142   switch (aEvent.eventStructType) {
  1143   case NS_KEY_EVENT:
  1144   case NS_WHEEL_EVENT:
  1145     return true;
  1146   case NS_MOUSE_EVENT:
  1147     switch (aEvent.message) {
  1148     case NS_MOUSE_BUTTON_DOWN:
  1149     case NS_MOUSE_BUTTON_UP:
  1150     case NS_MOUSE_MOVE:
  1151     case NS_CONTEXTMENU:
  1152       return true;
  1153     default:
  1154       return false;
  1156   case NS_TOUCH_EVENT:
  1157     switch (aEvent.message) {
  1158     case NS_TOUCH_START:
  1159     case NS_TOUCH_MOVE:
  1160     case NS_TOUCH_END:
  1161     case NS_TOUCH_CANCEL:
  1162       return true;
  1163     default:
  1164       return false;
  1166   default:
  1167     return false;
  1171 bool
  1172 EventStateManager::HandleCrossProcessEvent(WidgetEvent* aEvent,
  1173                                            nsIFrame* aTargetFrame,
  1174                                            nsEventStatus *aStatus) {
  1175   if (*aStatus == nsEventStatus_eConsumeNoDefault ||
  1176       aEvent->mFlags.mNoCrossProcessBoundaryForwarding ||
  1177       !CrossProcessSafeEvent(*aEvent)) {
  1178     return false;
  1181   // Collect the remote event targets we're going to forward this
  1182   // event to.
  1183   //
  1184   // NB: the elements of |targets| must be unique, for correctness.
  1185   nsAutoTArray<nsCOMPtr<nsIContent>, 1> targets;
  1186   if (aEvent->eventStructType != NS_TOUCH_EVENT ||
  1187       aEvent->message == NS_TOUCH_START) {
  1188     // If this event only has one target, and it's remote, add it to
  1189     // the array.
  1190     nsIFrame* frame = GetEventTarget();
  1191     nsIContent* target = frame ? frame->GetContent() : nullptr;
  1192     if (IsRemoteTarget(target)) {
  1193       targets.AppendElement(target);
  1195   } else {
  1196     // This is a touch event with possibly multiple touch points.
  1197     // Each touch point may have its own target.  So iterate through
  1198     // all of them and collect the unique set of targets for event
  1199     // forwarding.
  1200     //
  1201     // This loop is similar to the one used in
  1202     // PresShell::DispatchTouchEvent().
  1203     const nsTArray< nsRefPtr<Touch> >& touches =
  1204       aEvent->AsTouchEvent()->touches;
  1205     for (uint32_t i = 0; i < touches.Length(); ++i) {
  1206       Touch* touch = touches[i];
  1207       // NB: the |mChanged| check is an optimization, subprocesses can
  1208       // compute this for themselves.  If the touch hasn't changed, we
  1209       // may be able to avoid forwarding the event entirely (which is
  1210       // not free).
  1211       if (!touch || !touch->mChanged) {
  1212         continue;
  1214       nsCOMPtr<EventTarget> targetPtr = touch->mTarget;
  1215       if (!targetPtr) {
  1216         continue;
  1218       nsCOMPtr<nsIContent> target = do_QueryInterface(targetPtr);
  1219       if (IsRemoteTarget(target) && !targets.Contains(target)) {
  1220         targets.AppendElement(target);
  1225   if (targets.Length() == 0) {
  1226     return false;
  1229   // Look up the frame loader for all the remote targets we found, and
  1230   // then dispatch the event to the remote content they represent.
  1231   bool dispatched = false;
  1232   for (uint32_t i = 0; i < targets.Length(); ++i) {
  1233     nsIContent* target = targets[i];
  1234     nsCOMPtr<nsIFrameLoaderOwner> loaderOwner = do_QueryInterface(target);
  1235     if (!loaderOwner) {
  1236       continue;
  1239     nsRefPtr<nsFrameLoader> frameLoader = loaderOwner->GetFrameLoader();
  1240     if (!frameLoader) {
  1241       continue;
  1244     uint32_t eventMode;
  1245     frameLoader->GetEventMode(&eventMode);
  1246     if (eventMode == nsIFrameLoader::EVENT_MODE_DONT_FORWARD_TO_CHILD) {
  1247       continue;
  1250     dispatched |= DispatchCrossProcessEvent(aEvent, frameLoader, aStatus);
  1252   return dispatched;
  1255 //
  1256 // CreateClickHoldTimer
  1257 //
  1258 // Fire off a timer for determining if the user wants click-hold. This timer
  1259 // is a one-shot that will be cancelled when the user moves enough to fire
  1260 // a drag.
  1261 //
  1262 void
  1263 EventStateManager::CreateClickHoldTimer(nsPresContext* inPresContext,
  1264                                         nsIFrame* inDownFrame,
  1265                                         WidgetGUIEvent* inMouseDownEvent)
  1267   if (!inMouseDownEvent->mFlags.mIsTrusted || IsRemoteTarget(mGestureDownContent))
  1268     return;
  1270   // just to be anal (er, safe)
  1271   if (mClickHoldTimer) {
  1272     mClickHoldTimer->Cancel();
  1273     mClickHoldTimer = nullptr;
  1276   // if content clicked on has a popup, don't even start the timer
  1277   // since we'll end up conflicting and both will show.
  1278   if (mGestureDownContent) {
  1279     // check for the |popup| attribute
  1280     if (nsContentUtils::HasNonEmptyAttr(mGestureDownContent, kNameSpaceID_None,
  1281                                         nsGkAtoms::popup))
  1282       return;
  1284     // check for a <menubutton> like bookmarks
  1285     if (mGestureDownContent->Tag() == nsGkAtoms::menubutton)
  1286       return;
  1289   mClickHoldTimer = do_CreateInstance("@mozilla.org/timer;1");
  1290   if (mClickHoldTimer) {
  1291     int32_t clickHoldDelay =
  1292       Preferences::GetInt("ui.click_hold_context_menus.delay", 500);
  1293     mClickHoldTimer->InitWithFuncCallback(sClickHoldCallback, this,
  1294                                           clickHoldDelay,
  1295                                           nsITimer::TYPE_ONE_SHOT);
  1297 } // CreateClickHoldTimer
  1300 //
  1301 // KillClickHoldTimer
  1302 //
  1303 // Stop the timer that would show the context menu dead in its tracks
  1304 //
  1305 void
  1306 EventStateManager::KillClickHoldTimer()
  1308   if (mClickHoldTimer) {
  1309     mClickHoldTimer->Cancel();
  1310     mClickHoldTimer = nullptr;
  1315 //
  1316 // sClickHoldCallback
  1317 //
  1318 // This fires after the mouse has been down for a certain length of time.
  1319 //
  1320 void
  1321 EventStateManager::sClickHoldCallback(nsITimer* aTimer, void* aESM)
  1323   nsRefPtr<EventStateManager> self = static_cast<EventStateManager*>(aESM);
  1324   if (self) {
  1325     self->FireContextClick();
  1328   // NOTE: |aTimer| and |self->mAutoHideTimer| are invalid after calling ClosePopup();
  1330 } // sAutoHideCallback
  1333 //
  1334 // FireContextClick
  1335 //
  1336 // If we're this far, our timer has fired, which means the mouse has been down
  1337 // for a certain period of time and has not moved enough to generate a dragGesture.
  1338 // We can be certain the user wants a context-click at this stage, so generate
  1339 // a dom event and fire it in.
  1340 //
  1341 // After the event fires, check if PreventDefault() has been set on the event which
  1342 // means that someone either ate the event or put up a context menu. This is our cue
  1343 // to stop tracking the drag gesture. If we always did this, draggable items w/out
  1344 // a context menu wouldn't be draggable after a certain length of time, which is
  1345 // _not_ what we want.
  1346 //
  1347 void
  1348 EventStateManager::FireContextClick()
  1350   if (!mGestureDownContent || !mPresContext) {
  1351     return;
  1354 #ifdef XP_MACOSX
  1355   // Hack to ensure that we don't show a context menu when the user
  1356   // let go of the mouse after a long cpu-hogging operation prevented
  1357   // us from handling any OS events. See bug 117589.
  1358   if (!CGEventSourceButtonState(kCGEventSourceStateCombinedSessionState, kCGMouseButtonLeft))
  1359     return;
  1360 #endif
  1362   nsEventStatus status = nsEventStatus_eIgnore;
  1364   // Dispatch to the DOM. We have to fake out the ESM and tell it that the
  1365   // current target frame is actually where the mouseDown occurred, otherwise it
  1366   // will use the frame the mouse is currently over which may or may not be
  1367   // the same. (Note: saari and I have decided that we don't have to reset |mCurrentTarget|
  1368   // when we're through because no one else is doing anything more with this
  1369   // event and it will get reset on the very next event to the correct frame).
  1370   mCurrentTarget = mPresContext->GetPrimaryFrameFor(mGestureDownContent);
  1371   // make sure the widget sticks around
  1372   nsCOMPtr<nsIWidget> targetWidget;
  1373   if (mCurrentTarget && (targetWidget = mCurrentTarget->GetNearestWidget())) {
  1374     NS_ASSERTION(mPresContext == mCurrentTarget->PresContext(),
  1375                  "a prescontext returned a primary frame that didn't belong to it?");
  1377     // before dispatching, check that we're not on something that
  1378     // doesn't get a context menu
  1379     nsIAtom *tag = mGestureDownContent->Tag();
  1380     bool allowedToDispatch = true;
  1382     if (mGestureDownContent->IsXUL()) {
  1383       if (tag == nsGkAtoms::scrollbar ||
  1384           tag == nsGkAtoms::scrollbarbutton ||
  1385           tag == nsGkAtoms::button)
  1386         allowedToDispatch = false;
  1387       else if (tag == nsGkAtoms::toolbarbutton) {
  1388         // a <toolbarbutton> that has the container attribute set
  1389         // will already have its own dropdown.
  1390         if (nsContentUtils::HasNonEmptyAttr(mGestureDownContent,
  1391                 kNameSpaceID_None, nsGkAtoms::container)) {
  1392           allowedToDispatch = false;
  1393         } else {
  1394           // If the toolbar button has an open menu, don't attempt to open
  1395             // a second menu
  1396           if (mGestureDownContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::open,
  1397                                                nsGkAtoms::_true, eCaseMatters)) {
  1398             allowedToDispatch = false;
  1403     else if (mGestureDownContent->IsHTML()) {
  1404       nsCOMPtr<nsIFormControl> formCtrl(do_QueryInterface(mGestureDownContent));
  1406       if (formCtrl) {
  1407         allowedToDispatch = formCtrl->IsTextControl(false) ||
  1408                             formCtrl->GetType() == NS_FORM_INPUT_FILE;
  1410       else if (tag == nsGkAtoms::applet ||
  1411                tag == nsGkAtoms::embed  ||
  1412                tag == nsGkAtoms::object) {
  1413         allowedToDispatch = false;
  1417     if (allowedToDispatch) {
  1418       // init the event while mCurrentTarget is still good
  1419       WidgetMouseEvent event(true, NS_CONTEXTMENU, targetWidget,
  1420                              WidgetMouseEvent::eReal);
  1421       event.clickCount = 1;
  1422       FillInEventFromGestureDown(&event);
  1424       // stop selection tracking, we're in control now
  1425       if (mCurrentTarget)
  1427         nsRefPtr<nsFrameSelection> frameSel =
  1428           mCurrentTarget->GetFrameSelection();
  1430         if (frameSel && frameSel->GetMouseDownState()) {
  1431           // note that this can cause selection changed events to fire if we're in
  1432           // a text field, which will null out mCurrentTarget
  1433           frameSel->SetMouseDownState(false);
  1437       nsIDocument* doc = mGestureDownContent->GetCurrentDoc();
  1438       AutoHandlingUserInputStatePusher userInpStatePusher(true, &event, doc);
  1440       // dispatch to DOM
  1441       EventDispatcher::Dispatch(mGestureDownContent, mPresContext, &event,
  1442                                 nullptr, &status);
  1444       // We don't need to dispatch to frame handling because no frames
  1445       // watch NS_CONTEXTMENU except for nsMenuFrame and that's only for
  1446       // dismissal. That's just as well since we don't really know
  1447       // which frame to send it to.
  1451   // now check if the event has been handled. If so, stop tracking a drag
  1452   if (status == nsEventStatus_eConsumeNoDefault) {
  1453     StopTrackingDragGesture();
  1456   KillClickHoldTimer();
  1458 } // FireContextClick
  1461 //
  1462 // BeginTrackingDragGesture
  1463 //
  1464 // Record that the mouse has gone down and that we should move to TRACKING state
  1465 // of d&d gesture tracker.
  1466 //
  1467 // We also use this to track click-hold context menus. When the mouse goes down,
  1468 // fire off a short timer. If the timer goes off and we have yet to fire the
  1469 // drag gesture (ie, the mouse hasn't moved a certain distance), then we can
  1470 // assume the user wants a click-hold, so fire a context-click event. We only
  1471 // want to cancel the drag gesture if the context-click event is handled.
  1472 //
  1473 void
  1474 EventStateManager::BeginTrackingDragGesture(nsPresContext* aPresContext,
  1475                                             WidgetMouseEvent* inDownEvent,
  1476                                             nsIFrame* inDownFrame)
  1478   if (!inDownEvent->widget)
  1479     return;
  1481   // Note that |inDownEvent| could be either a mouse down event or a
  1482   // synthesized mouse move event.
  1483   mGestureDownPoint = inDownEvent->refPoint +
  1484     LayoutDeviceIntPoint::FromUntyped(inDownEvent->widget->WidgetToScreenOffset());
  1486   inDownFrame->GetContentForEvent(inDownEvent,
  1487                                   getter_AddRefs(mGestureDownContent));
  1489   mGestureDownFrameOwner = inDownFrame->GetContent();
  1490   mGestureModifiers = inDownEvent->modifiers;
  1491   mGestureDownButtons = inDownEvent->buttons;
  1493   if (Prefs::ClickHoldContextMenu()) {
  1494     // fire off a timer to track click-hold
  1495     CreateClickHoldTimer(aPresContext, inDownFrame, inDownEvent);
  1500 //
  1501 // StopTrackingDragGesture
  1502 //
  1503 // Record that the mouse has gone back up so that we should leave the TRACKING
  1504 // state of d&d gesture tracker and return to the START state.
  1505 //
  1506 void
  1507 EventStateManager::StopTrackingDragGesture()
  1509   mGestureDownContent = nullptr;
  1510   mGestureDownFrameOwner = nullptr;
  1513 void
  1514 EventStateManager::FillInEventFromGestureDown(WidgetMouseEvent* aEvent)
  1516   NS_ASSERTION(aEvent->widget == mCurrentTarget->GetNearestWidget(),
  1517                "Incorrect widget in event");
  1519   // Set the coordinates in the new event to the coordinates of
  1520   // the old event, adjusted for the fact that the widget might be
  1521   // different
  1522   aEvent->refPoint = mGestureDownPoint -
  1523     LayoutDeviceIntPoint::FromUntyped(aEvent->widget->WidgetToScreenOffset());
  1524   aEvent->modifiers = mGestureModifiers;
  1525   aEvent->buttons = mGestureDownButtons;
  1528 //
  1529 // GenerateDragGesture
  1530 //
  1531 // If we're in the TRACKING state of the d&d gesture tracker, check the current position
  1532 // of the mouse in relation to the old one. If we've moved a sufficient amount from
  1533 // the mouse down, then fire off a drag gesture event.
  1534 void
  1535 EventStateManager::GenerateDragGesture(nsPresContext* aPresContext,
  1536                                        WidgetMouseEvent* aEvent)
  1538   NS_ASSERTION(aPresContext, "This shouldn't happen.");
  1539   if (IsTrackingDragGesture()) {
  1540     mCurrentTarget = mGestureDownFrameOwner->GetPrimaryFrame();
  1542     if (!mCurrentTarget) {
  1543       StopTrackingDragGesture();
  1544       return;
  1547     // Check if selection is tracking drag gestures, if so
  1548     // don't interfere!
  1549     if (mCurrentTarget)
  1551       nsRefPtr<nsFrameSelection> frameSel = mCurrentTarget->GetFrameSelection();
  1552       if (frameSel && frameSel->GetMouseDownState()) {
  1553         StopTrackingDragGesture();
  1554         return;
  1558     // If non-native code is capturing the mouse don't start a drag.
  1559     if (nsIPresShell::IsMouseCapturePreventingDrag()) {
  1560       StopTrackingDragGesture();
  1561       return;
  1564     static int32_t pixelThresholdX = 0;
  1565     static int32_t pixelThresholdY = 0;
  1567     if (!pixelThresholdX) {
  1568       pixelThresholdX =
  1569         LookAndFeel::GetInt(LookAndFeel::eIntID_DragThresholdX, 0);
  1570       pixelThresholdY =
  1571         LookAndFeel::GetInt(LookAndFeel::eIntID_DragThresholdY, 0);
  1572       if (!pixelThresholdX)
  1573         pixelThresholdX = 5;
  1574       if (!pixelThresholdY)
  1575         pixelThresholdY = 5;
  1578     // fire drag gesture if mouse has moved enough
  1579     LayoutDeviceIntPoint pt = aEvent->refPoint +
  1580       LayoutDeviceIntPoint::FromUntyped(aEvent->widget->WidgetToScreenOffset());
  1581     if (DeprecatedAbs(pt.x - mGestureDownPoint.x) > pixelThresholdX ||
  1582         DeprecatedAbs(pt.y - mGestureDownPoint.y) > pixelThresholdY) {
  1583       if (Prefs::ClickHoldContextMenu()) {
  1584         // stop the click-hold before we fire off the drag gesture, in case
  1585         // it takes a long time
  1586         KillClickHoldTimer();
  1589       nsCOMPtr<nsISupports> container = aPresContext->GetContainerWeak();
  1590       nsCOMPtr<nsPIDOMWindow> window = do_GetInterface(container);
  1591       if (!window)
  1592         return;
  1594       nsRefPtr<DataTransfer> dataTransfer =
  1595         new DataTransfer(window, NS_DRAGDROP_START, false, -1);
  1597       nsCOMPtr<nsISelection> selection;
  1598       nsCOMPtr<nsIContent> eventContent, targetContent;
  1599       mCurrentTarget->GetContentForEvent(aEvent, getter_AddRefs(eventContent));
  1600       if (eventContent)
  1601         DetermineDragTarget(window, eventContent, dataTransfer,
  1602                             getter_AddRefs(selection), getter_AddRefs(targetContent));
  1604       // Stop tracking the drag gesture now. This should stop us from
  1605       // reentering GenerateDragGesture inside DOM event processing.
  1606       StopTrackingDragGesture();
  1608       if (!targetContent)
  1609         return;
  1611       // Use our targetContent, now that we've determined it, as the
  1612       // parent object of the DataTransfer.
  1613       dataTransfer->SetParentObject(targetContent);
  1615       sLastDragOverFrame = nullptr;
  1616       nsCOMPtr<nsIWidget> widget = mCurrentTarget->GetNearestWidget();
  1618       // get the widget from the target frame
  1619       WidgetDragEvent startEvent(aEvent->mFlags.mIsTrusted,
  1620                                  NS_DRAGDROP_START, widget);
  1621       FillInEventFromGestureDown(&startEvent);
  1623       WidgetDragEvent gestureEvent(aEvent->mFlags.mIsTrusted,
  1624                                    NS_DRAGDROP_GESTURE, widget);
  1625       FillInEventFromGestureDown(&gestureEvent);
  1627       startEvent.dataTransfer = gestureEvent.dataTransfer = dataTransfer;
  1628       startEvent.inputSource = gestureEvent.inputSource = aEvent->inputSource;
  1630       // Dispatch to the DOM. By setting mCurrentTarget we are faking
  1631       // out the ESM and telling it that the current target frame is
  1632       // actually where the mouseDown occurred, otherwise it will use
  1633       // the frame the mouse is currently over which may or may not be
  1634       // the same. (Note: saari and I have decided that we don't have
  1635       // to reset |mCurrentTarget| when we're through because no one
  1636       // else is doing anything more with this event and it will get
  1637       // reset on the very next event to the correct frame).
  1639       // Hold onto old target content through the event and reset after.
  1640       nsCOMPtr<nsIContent> targetBeforeEvent = mCurrentTargetContent;
  1642       // Set the current target to the content for the mouse down
  1643       mCurrentTargetContent = targetContent;
  1645       // Dispatch both the dragstart and draggesture events to the DOM. For
  1646       // elements in an editor, only fire the draggesture event so that the
  1647       // editor code can handle it but content doesn't see a dragstart.
  1648       nsEventStatus status = nsEventStatus_eIgnore;
  1649       EventDispatcher::Dispatch(targetContent, aPresContext, &startEvent,
  1650                                 nullptr, &status);
  1652       WidgetDragEvent* event = &startEvent;
  1653       if (status != nsEventStatus_eConsumeNoDefault) {
  1654         status = nsEventStatus_eIgnore;
  1655         EventDispatcher::Dispatch(targetContent, aPresContext, &gestureEvent,
  1656                                   nullptr, &status);
  1657         event = &gestureEvent;
  1660       nsCOMPtr<nsIObserverService> observerService =
  1661         mozilla::services::GetObserverService();
  1662       // Emit observer event to allow addons to modify the DataTransfer object.
  1663       if (observerService) {
  1664         observerService->NotifyObservers(dataTransfer,
  1665                                          "on-datatransfer-available",
  1666                                          nullptr);
  1669       // now that the dataTransfer has been updated in the dragstart and
  1670       // draggesture events, make it read only so that the data doesn't
  1671       // change during the drag.
  1672       dataTransfer->SetReadOnly();
  1674       if (status != nsEventStatus_eConsumeNoDefault) {
  1675         bool dragStarted = DoDefaultDragStart(aPresContext, event, dataTransfer,
  1676                                               targetContent, selection);
  1677         if (dragStarted) {
  1678           sActiveESM = nullptr;
  1679           aEvent->mFlags.mPropagationStopped = true;
  1683       // Note that frame event handling doesn't care about NS_DRAGDROP_GESTURE,
  1684       // which is just as well since we don't really know which frame to
  1685       // send it to
  1687       // Reset mCurretTargetContent to what it was
  1688       mCurrentTargetContent = targetBeforeEvent;
  1691     // Now flush all pending notifications, for better responsiveness
  1692     // while dragging.
  1693     FlushPendingEvents(aPresContext);
  1695 } // GenerateDragGesture
  1697 void
  1698 EventStateManager::DetermineDragTarget(nsPIDOMWindow* aWindow,
  1699                                        nsIContent* aSelectionTarget,
  1700                                        DataTransfer* aDataTransfer,
  1701                                        nsISelection** aSelection,
  1702                                        nsIContent** aTargetNode)
  1704   *aTargetNode = nullptr;
  1706   // GetDragData determines if a selection, link or image in the content
  1707   // should be dragged, and places the data associated with the drag in the
  1708   // data transfer.
  1709   // mGestureDownContent is the node where the mousedown event for the drag
  1710   // occurred, and aSelectionTarget is the node to use when a selection is used
  1711   bool canDrag;
  1712   nsCOMPtr<nsIContent> dragDataNode;
  1713   bool wasAlt = (mGestureModifiers & MODIFIER_ALT) != 0;
  1714   nsresult rv = nsContentAreaDragDrop::GetDragData(aWindow, mGestureDownContent,
  1715                                                    aSelectionTarget, wasAlt,
  1716                                                    aDataTransfer, &canDrag, aSelection,
  1717                                                    getter_AddRefs(dragDataNode));
  1718   if (NS_FAILED(rv) || !canDrag)
  1719     return;
  1721   // if GetDragData returned a node, use that as the node being dragged.
  1722   // Otherwise, if a selection is being dragged, use the node within the
  1723   // selection that was dragged. Otherwise, just use the mousedown target.
  1724   nsIContent* dragContent = mGestureDownContent;
  1725   if (dragDataNode)
  1726     dragContent = dragDataNode;
  1727   else if (*aSelection)
  1728     dragContent = aSelectionTarget;
  1730   nsIContent* originalDragContent = dragContent;
  1732   // If a selection isn't being dragged, look for an ancestor with the
  1733   // draggable property set. If one is found, use that as the target of the
  1734   // drag instead of the node that was clicked on. If a draggable node wasn't
  1735   // found, just use the clicked node.
  1736   if (!*aSelection) {
  1737     while (dragContent) {
  1738       nsCOMPtr<nsIDOMHTMLElement> htmlElement = do_QueryInterface(dragContent);
  1739       if (htmlElement) {
  1740         bool draggable = false;
  1741         htmlElement->GetDraggable(&draggable);
  1742         if (draggable)
  1743           break;
  1745       else {
  1746         nsCOMPtr<nsIDOMXULElement> xulElement = do_QueryInterface(dragContent);
  1747         if (xulElement) {
  1748           // All XUL elements are draggable, so if a XUL element is
  1749           // encountered, stop looking for draggable nodes and just use the
  1750           // original clicked node instead.
  1751           // XXXndeakin
  1752           // In the future, we will want to improve this so that XUL has a
  1753           // better way to specify whether something is draggable than just
  1754           // on/off.
  1755           dragContent = mGestureDownContent;
  1756           break;
  1758         // otherwise, it's not an HTML or XUL element, so just keep looking
  1760       dragContent = dragContent->GetParent();
  1764   // if no node in the hierarchy was found to drag, but the GetDragData method
  1765   // returned a node, use that returned node. Otherwise, nothing is draggable.
  1766   if (!dragContent && dragDataNode)
  1767     dragContent = dragDataNode;
  1769   if (dragContent) {
  1770     // if an ancestor node was used instead, clear the drag data
  1771     // XXXndeakin rework this a bit. Find a way to just not call GetDragData if we don't need to.
  1772     if (dragContent != originalDragContent)
  1773       aDataTransfer->ClearAll();
  1774     *aTargetNode = dragContent;
  1775     NS_ADDREF(*aTargetNode);
  1779 bool
  1780 EventStateManager::DoDefaultDragStart(nsPresContext* aPresContext,
  1781                                       WidgetDragEvent* aDragEvent,
  1782                                       DataTransfer* aDataTransfer,
  1783                                       nsIContent* aDragTarget,
  1784                                       nsISelection* aSelection)
  1786   nsCOMPtr<nsIDragService> dragService =
  1787     do_GetService("@mozilla.org/widget/dragservice;1");
  1788   if (!dragService)
  1789     return false;
  1791   // Default handling for the draggesture/dragstart event.
  1792   //
  1793   // First, check if a drag session already exists. This means that the drag
  1794   // service was called directly within a draggesture handler. In this case,
  1795   // don't do anything more, as it is assumed that the handler is managing
  1796   // drag and drop manually. Make sure to return true to indicate that a drag
  1797   // began.
  1798   nsCOMPtr<nsIDragSession> dragSession;
  1799   dragService->GetCurrentSession(getter_AddRefs(dragSession));
  1800   if (dragSession)
  1801     return true;
  1803   // No drag session is currently active, so check if a handler added
  1804   // any items to be dragged. If not, there isn't anything to drag.
  1805   uint32_t count = 0;
  1806   if (aDataTransfer)
  1807     aDataTransfer->GetMozItemCount(&count);
  1808   if (!count)
  1809     return false;
  1811   // Get the target being dragged, which may not be the same as the
  1812   // target of the mouse event. If one wasn't set in the
  1813   // aDataTransfer during the event handler, just use the original
  1814   // target instead.
  1815   nsCOMPtr<nsIContent> dragTarget = aDataTransfer->GetDragTarget();
  1816   if (!dragTarget) {
  1817     dragTarget = aDragTarget;
  1818     if (!dragTarget)
  1819       return false;
  1822   // check which drag effect should initially be used. If the effect was not
  1823   // set, just use all actions, otherwise Windows won't allow a drop.
  1824   uint32_t action;
  1825   aDataTransfer->GetEffectAllowedInt(&action);
  1826   if (action == nsIDragService::DRAGDROP_ACTION_UNINITIALIZED)
  1827     action = nsIDragService::DRAGDROP_ACTION_COPY |
  1828              nsIDragService::DRAGDROP_ACTION_MOVE |
  1829              nsIDragService::DRAGDROP_ACTION_LINK;
  1831   // get any custom drag image that was set
  1832   int32_t imageX, imageY;
  1833   Element* dragImage = aDataTransfer->GetDragImage(&imageX, &imageY);
  1835   nsCOMPtr<nsISupportsArray> transArray =
  1836     aDataTransfer->GetTransferables(dragTarget->AsDOMNode());
  1837   if (!transArray)
  1838     return false;
  1840   // XXXndeakin don't really want to create a new drag DOM event
  1841   // here, but we need something to pass to the InvokeDragSession
  1842   // methods.
  1843   nsCOMPtr<nsIDOMEvent> domEvent;
  1844   NS_NewDOMDragEvent(getter_AddRefs(domEvent), dragTarget,
  1845                      aPresContext, aDragEvent);
  1847   nsCOMPtr<nsIDOMDragEvent> domDragEvent = do_QueryInterface(domEvent);
  1848   // if creating a drag event failed, starting a drag session will
  1849   // just fail.
  1851   // Use InvokeDragSessionWithSelection if a selection is being dragged,
  1852   // such that the image can be generated from the selected text. However,
  1853   // use InvokeDragSessionWithImage if a custom image was set or something
  1854   // other than a selection is being dragged.
  1855   if (!dragImage && aSelection) {
  1856     dragService->InvokeDragSessionWithSelection(aSelection, transArray,
  1857                                                 action, domDragEvent,
  1858                                                 aDataTransfer);
  1860   else {
  1861     // if dragging within a XUL tree and no custom drag image was
  1862     // set, the region argument to InvokeDragSessionWithImage needs
  1863     // to be set to the area encompassing the selected rows of the
  1864     // tree to ensure that the drag feedback gets clipped to those
  1865     // rows. For other content, region should be null.
  1866     nsCOMPtr<nsIScriptableRegion> region;
  1867 #ifdef MOZ_XUL
  1868     if (dragTarget && !dragImage) {
  1869       if (dragTarget->NodeInfo()->Equals(nsGkAtoms::treechildren,
  1870                                          kNameSpaceID_XUL)) {
  1871         nsTreeBodyFrame* treeBody =
  1872           do_QueryFrame(dragTarget->GetPrimaryFrame());
  1873         if (treeBody) {
  1874           treeBody->GetSelectionRegion(getter_AddRefs(region));
  1878 #endif
  1880     dragService->InvokeDragSessionWithImage(dragTarget->AsDOMNode(), transArray,
  1881                                             region, action,
  1882                                             dragImage ? dragImage->AsDOMNode() :
  1883                                                         nullptr,
  1884                                             imageX,
  1885                                             imageY, domDragEvent,
  1886                                             aDataTransfer);
  1889   return true;
  1892 nsresult
  1893 EventStateManager::GetMarkupDocumentViewer(nsIMarkupDocumentViewer** aMv)
  1895   *aMv = nullptr;
  1897   nsIFocusManager* fm = nsFocusManager::GetFocusManager();
  1898   if(!fm) return NS_ERROR_FAILURE;
  1900   nsCOMPtr<nsIDOMWindow> focusedWindow;
  1901   fm->GetFocusedWindow(getter_AddRefs(focusedWindow));
  1903   nsCOMPtr<nsPIDOMWindow> ourWindow = do_QueryInterface(focusedWindow);
  1904   if(!ourWindow) return NS_ERROR_FAILURE;
  1906   nsIDOMWindow *rootWindow = ourWindow->GetPrivateRoot();
  1907   if(!rootWindow) return NS_ERROR_FAILURE;
  1909   nsCOMPtr<nsIDOMWindow> contentWindow;
  1910   rootWindow->GetContent(getter_AddRefs(contentWindow));
  1911   if(!contentWindow) return NS_ERROR_FAILURE;
  1913   nsIDocument *doc = GetDocumentFromWindow(contentWindow);
  1914   if(!doc) return NS_ERROR_FAILURE;
  1916   nsIPresShell *presShell = doc->GetShell();
  1917   if(!presShell) return NS_ERROR_FAILURE;
  1918   nsPresContext *presContext = presShell->GetPresContext();
  1919   if(!presContext) return NS_ERROR_FAILURE;
  1921   nsCOMPtr<nsIDocShell> docshell(presContext->GetDocShell());
  1922   if(!docshell) return NS_ERROR_FAILURE;
  1924   nsCOMPtr<nsIContentViewer> cv;
  1925   docshell->GetContentViewer(getter_AddRefs(cv));
  1926   if(!cv) return NS_ERROR_FAILURE;
  1928   nsCOMPtr<nsIMarkupDocumentViewer> mv(do_QueryInterface(cv));
  1929   if(!mv) return NS_ERROR_FAILURE;
  1931   *aMv = mv;
  1932   NS_IF_ADDREF(*aMv);
  1934   return NS_OK;
  1937 nsresult
  1938 EventStateManager::ChangeTextSize(int32_t change)
  1940   nsCOMPtr<nsIMarkupDocumentViewer> mv;
  1941   nsresult rv = GetMarkupDocumentViewer(getter_AddRefs(mv));
  1942   NS_ENSURE_SUCCESS(rv, rv);
  1944   float textzoom;
  1945   float zoomMin = ((float)Preferences::GetInt("zoom.minPercent", 50)) / 100;
  1946   float zoomMax = ((float)Preferences::GetInt("zoom.maxPercent", 300)) / 100;
  1947   mv->GetTextZoom(&textzoom);
  1948   textzoom += ((float)change) / 10;
  1949   if (textzoom < zoomMin)
  1950     textzoom = zoomMin;
  1951   else if (textzoom > zoomMax)
  1952     textzoom = zoomMax;
  1953   mv->SetTextZoom(textzoom);
  1955   return NS_OK;
  1958 nsresult
  1959 EventStateManager::ChangeFullZoom(int32_t change)
  1961   nsCOMPtr<nsIMarkupDocumentViewer> mv;
  1962   nsresult rv = GetMarkupDocumentViewer(getter_AddRefs(mv));
  1963   NS_ENSURE_SUCCESS(rv, rv);
  1965   float fullzoom;
  1966   float zoomMin = ((float)Preferences::GetInt("zoom.minPercent", 50)) / 100;
  1967   float zoomMax = ((float)Preferences::GetInt("zoom.maxPercent", 300)) / 100;
  1968   mv->GetFullZoom(&fullzoom);
  1969   fullzoom += ((float)change) / 10;
  1970   if (fullzoom < zoomMin)
  1971     fullzoom = zoomMin;
  1972   else if (fullzoom > zoomMax)
  1973     fullzoom = zoomMax;
  1974   mv->SetFullZoom(fullzoom);
  1976   return NS_OK;
  1979 void
  1980 EventStateManager::DoScrollHistory(int32_t direction)
  1982   nsCOMPtr<nsISupports> pcContainer(mPresContext->GetContainerWeak());
  1983   if (pcContainer) {
  1984     nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(pcContainer));
  1985     if (webNav) {
  1986       // positive direction to go back one step, nonpositive to go forward
  1987       if (direction > 0)
  1988         webNav->GoBack();
  1989       else
  1990         webNav->GoForward();
  1995 void
  1996 EventStateManager::DoScrollZoom(nsIFrame* aTargetFrame,
  1997                                 int32_t adjustment)
  1999   // Exclude form controls and content in chrome docshells.
  2000   nsIContent *content = aTargetFrame->GetContent();
  2001   if (content &&
  2002       !content->IsNodeOfType(nsINode::eHTML_FORM_CONTROL) &&
  2003       !nsContentUtils::IsInChromeDocshell(content->OwnerDoc()))
  2005       // positive adjustment to decrease zoom, negative to increase
  2006       int32_t change = (adjustment > 0) ? -1 : 1;
  2008       if (Preferences::GetBool("browser.zoom.full") || content->GetCurrentDoc()->IsSyntheticDocument()) {
  2009         ChangeFullZoom(change);
  2010       } else {
  2011         ChangeTextSize(change);
  2016 static nsIFrame*
  2017 GetParentFrameToScroll(nsIFrame* aFrame)
  2019   if (!aFrame)
  2020     return nullptr;
  2022   if (aFrame->StyleDisplay()->mPosition == NS_STYLE_POSITION_FIXED &&
  2023       nsLayoutUtils::IsReallyFixedPos(aFrame))
  2024     return aFrame->PresContext()->GetPresShell()->GetRootScrollFrame();
  2026   return aFrame->GetParent();
  2029 void
  2030 EventStateManager::DispatchLegacyMouseScrollEvents(nsIFrame* aTargetFrame,
  2031                                                    WidgetWheelEvent* aEvent,
  2032                                                    nsEventStatus* aStatus)
  2034   MOZ_ASSERT(aEvent);
  2035   MOZ_ASSERT(aStatus);
  2037   if (!aTargetFrame || *aStatus == nsEventStatus_eConsumeNoDefault) {
  2038     return;
  2041   // Ignore mouse wheel transaction for computing legacy mouse wheel
  2042   // events' delta value.
  2043   nsIScrollableFrame* scrollTarget =
  2044     ComputeScrollTarget(aTargetFrame, aEvent,
  2045                         COMPUTE_LEGACY_MOUSE_SCROLL_EVENT_TARGET);
  2047   nsIFrame* scrollFrame = do_QueryFrame(scrollTarget);
  2048   nsPresContext* pc =
  2049     scrollFrame ? scrollFrame->PresContext() : aTargetFrame->PresContext();
  2051   // DOM event's delta vales are computed from CSS pixels.
  2052   nsSize scrollAmount = GetScrollAmount(pc, aEvent, scrollTarget);
  2053   nsIntSize scrollAmountInCSSPixels(
  2054     nsPresContext::AppUnitsToIntCSSPixels(scrollAmount.width),
  2055     nsPresContext::AppUnitsToIntCSSPixels(scrollAmount.height));
  2057   // XXX We don't deal with fractional amount in legacy event, though the
  2058   //     default action handler (DoScrollText()) deals with it.
  2059   //     If we implemented such strict computation, we would need additional
  2060   //     accumulated delta values. It would made the code more complicated.
  2061   //     And also it would computes different delta values from older version.
  2062   //     It doesn't make sense to implement such code for legacy events and
  2063   //     rare cases.
  2064   int32_t scrollDeltaX, scrollDeltaY, pixelDeltaX, pixelDeltaY;
  2065   switch (aEvent->deltaMode) {
  2066     case nsIDOMWheelEvent::DOM_DELTA_PAGE:
  2067       scrollDeltaX =
  2068         !aEvent->lineOrPageDeltaX ? 0 :
  2069           (aEvent->lineOrPageDeltaX > 0  ? nsIDOMUIEvent::SCROLL_PAGE_DOWN :
  2070                                            nsIDOMUIEvent::SCROLL_PAGE_UP);
  2071       scrollDeltaY =
  2072         !aEvent->lineOrPageDeltaY ? 0 :
  2073           (aEvent->lineOrPageDeltaY > 0  ? nsIDOMUIEvent::SCROLL_PAGE_DOWN :
  2074                                            nsIDOMUIEvent::SCROLL_PAGE_UP);
  2075       pixelDeltaX = RoundDown(aEvent->deltaX * scrollAmountInCSSPixels.width);
  2076       pixelDeltaY = RoundDown(aEvent->deltaY * scrollAmountInCSSPixels.height);
  2077       break;
  2079     case nsIDOMWheelEvent::DOM_DELTA_LINE:
  2080       scrollDeltaX = aEvent->lineOrPageDeltaX;
  2081       scrollDeltaY = aEvent->lineOrPageDeltaY;
  2082       pixelDeltaX = RoundDown(aEvent->deltaX * scrollAmountInCSSPixels.width);
  2083       pixelDeltaY = RoundDown(aEvent->deltaY * scrollAmountInCSSPixels.height);
  2084       break;
  2086     case nsIDOMWheelEvent::DOM_DELTA_PIXEL:
  2087       scrollDeltaX = aEvent->lineOrPageDeltaX;
  2088       scrollDeltaY = aEvent->lineOrPageDeltaY;
  2089       pixelDeltaX = RoundDown(aEvent->deltaX);
  2090       pixelDeltaY = RoundDown(aEvent->deltaY);
  2091       break;
  2093     default:
  2094       MOZ_CRASH("Invalid deltaMode value comes");
  2097   // Send the legacy events in following order:
  2098   // 1. Vertical scroll
  2099   // 2. Vertical pixel scroll (even if #1 isn't consumed)
  2100   // 3. Horizontal scroll (even if #1 and/or #2 are consumed)
  2101   // 4. Horizontal pixel scroll (even if #3 isn't consumed)
  2103   nsWeakFrame targetFrame(aTargetFrame);
  2105   MOZ_ASSERT(*aStatus != nsEventStatus_eConsumeNoDefault &&
  2106              !aEvent->mFlags.mDefaultPrevented,
  2107              "If you make legacy events dispatched for default prevented wheel "
  2108              "event, you need to initialize stateX and stateY");
  2109   EventState stateX, stateY;
  2110   if (scrollDeltaY) {
  2111     SendLineScrollEvent(aTargetFrame, aEvent, stateY,
  2112                         scrollDeltaY, DELTA_DIRECTION_Y);
  2113     if (!targetFrame.IsAlive()) {
  2114       *aStatus = nsEventStatus_eConsumeNoDefault;
  2115       return;
  2119   if (pixelDeltaY) {
  2120     SendPixelScrollEvent(aTargetFrame, aEvent, stateY,
  2121                          pixelDeltaY, DELTA_DIRECTION_Y);
  2122     if (!targetFrame.IsAlive()) {
  2123       *aStatus = nsEventStatus_eConsumeNoDefault;
  2124       return;
  2128   if (scrollDeltaX) {
  2129     SendLineScrollEvent(aTargetFrame, aEvent, stateX,
  2130                         scrollDeltaX, DELTA_DIRECTION_X);
  2131     if (!targetFrame.IsAlive()) {
  2132       *aStatus = nsEventStatus_eConsumeNoDefault;
  2133       return;
  2137   if (pixelDeltaX) {
  2138     SendPixelScrollEvent(aTargetFrame, aEvent, stateX,
  2139                          pixelDeltaX, DELTA_DIRECTION_X);
  2140     if (!targetFrame.IsAlive()) {
  2141       *aStatus = nsEventStatus_eConsumeNoDefault;
  2142       return;
  2146   if (stateY.mDefaultPrevented || stateX.mDefaultPrevented) {
  2147     *aStatus = nsEventStatus_eConsumeNoDefault;
  2148     aEvent->mFlags.mDefaultPrevented = true;
  2149     aEvent->mFlags.mDefaultPreventedByContent |=
  2150       stateY.mDefaultPreventedByContent || stateX.mDefaultPreventedByContent;
  2154 void
  2155 EventStateManager::SendLineScrollEvent(nsIFrame* aTargetFrame,
  2156                                        WidgetWheelEvent* aEvent,
  2157                                        EventState& aState,
  2158                                        int32_t aDelta,
  2159                                        DeltaDirection aDeltaDirection)
  2161   nsCOMPtr<nsIContent> targetContent = aTargetFrame->GetContent();
  2162   if (!targetContent)
  2163     targetContent = GetFocusedContent();
  2164   if (!targetContent)
  2165     return;
  2167   while (targetContent->IsNodeOfType(nsINode::eTEXT)) {
  2168     targetContent = targetContent->GetParent();
  2171   WidgetMouseScrollEvent event(aEvent->mFlags.mIsTrusted, NS_MOUSE_SCROLL,
  2172                                aEvent->widget);
  2173   event.mFlags.mDefaultPrevented = aState.mDefaultPrevented;
  2174   event.mFlags.mDefaultPreventedByContent = aState.mDefaultPreventedByContent;
  2175   event.refPoint = aEvent->refPoint;
  2176   event.widget = aEvent->widget;
  2177   event.time = aEvent->time;
  2178   event.modifiers = aEvent->modifiers;
  2179   event.buttons = aEvent->buttons;
  2180   event.isHorizontal = (aDeltaDirection == DELTA_DIRECTION_X);
  2181   event.delta = aDelta;
  2182   event.inputSource = aEvent->inputSource;
  2184   nsEventStatus status = nsEventStatus_eIgnore;
  2185   EventDispatcher::Dispatch(targetContent, aTargetFrame->PresContext(),
  2186                             &event, nullptr, &status);
  2187   aState.mDefaultPrevented =
  2188     event.mFlags.mDefaultPrevented || status == nsEventStatus_eConsumeNoDefault;
  2189   aState.mDefaultPreventedByContent = event.mFlags.mDefaultPreventedByContent;
  2192 void
  2193 EventStateManager::SendPixelScrollEvent(nsIFrame* aTargetFrame,
  2194                                         WidgetWheelEvent* aEvent,
  2195                                         EventState& aState,
  2196                                         int32_t aPixelDelta,
  2197                                         DeltaDirection aDeltaDirection)
  2199   nsCOMPtr<nsIContent> targetContent = aTargetFrame->GetContent();
  2200   if (!targetContent) {
  2201     targetContent = GetFocusedContent();
  2202     if (!targetContent)
  2203       return;
  2206   while (targetContent->IsNodeOfType(nsINode::eTEXT)) {
  2207     targetContent = targetContent->GetParent();
  2210   WidgetMouseScrollEvent event(aEvent->mFlags.mIsTrusted, NS_MOUSE_PIXEL_SCROLL,
  2211                                aEvent->widget);
  2212   event.mFlags.mDefaultPrevented = aState.mDefaultPrevented;
  2213   event.mFlags.mDefaultPreventedByContent = aState.mDefaultPreventedByContent;
  2214   event.refPoint = aEvent->refPoint;
  2215   event.widget = aEvent->widget;
  2216   event.time = aEvent->time;
  2217   event.modifiers = aEvent->modifiers;
  2218   event.buttons = aEvent->buttons;
  2219   event.isHorizontal = (aDeltaDirection == DELTA_DIRECTION_X);
  2220   event.delta = aPixelDelta;
  2221   event.inputSource = aEvent->inputSource;
  2223   nsEventStatus status = nsEventStatus_eIgnore;
  2224   EventDispatcher::Dispatch(targetContent, aTargetFrame->PresContext(),
  2225                             &event, nullptr, &status);
  2226   aState.mDefaultPrevented =
  2227     event.mFlags.mDefaultPrevented || status == nsEventStatus_eConsumeNoDefault;
  2228   aState.mDefaultPreventedByContent = event.mFlags.mDefaultPreventedByContent;
  2231 nsIScrollableFrame*
  2232 EventStateManager::ComputeScrollTarget(nsIFrame* aTargetFrame,
  2233                                        WidgetWheelEvent* aEvent,
  2234                                        ComputeScrollTargetOptions aOptions)
  2236   return ComputeScrollTarget(aTargetFrame, aEvent->deltaX, aEvent->deltaY,
  2237                              aEvent, aOptions);
  2240 // Overload ComputeScrollTarget method to allow passing "test" dx and dy when looking
  2241 // for which scrollbarowners to activate when two finger down on trackpad
  2242 // and before any actual motion
  2243 nsIScrollableFrame*
  2244 EventStateManager::ComputeScrollTarget(nsIFrame* aTargetFrame,
  2245                                        double aDirectionX,
  2246                                        double aDirectionY,
  2247                                        WidgetWheelEvent* aEvent,
  2248                                        ComputeScrollTargetOptions aOptions)
  2250   if (aOptions & PREFER_MOUSE_WHEEL_TRANSACTION) {
  2251     // If the user recently scrolled with the mousewheel, then they probably
  2252     // want to scroll the same view as before instead of the view under the
  2253     // cursor.  WheelTransaction tracks the frame currently being
  2254     // scrolled with the mousewheel. We consider the transaction ended when the
  2255     // mouse moves more than "mousewheel.transaction.ignoremovedelay"
  2256     // milliseconds after the last scroll operation, or any time the mouse moves
  2257     // out of the frame, or when more than "mousewheel.transaction.timeout"
  2258     // milliseconds have passed after the last operation, even if the mouse
  2259     // hasn't moved.
  2260     nsIFrame* lastScrollFrame = WheelTransaction::GetTargetFrame();
  2261     if (lastScrollFrame) {
  2262       nsIScrollableFrame* frameToScroll =
  2263         lastScrollFrame->GetScrollTargetFrame();
  2264       if (frameToScroll) {
  2265         return frameToScroll;
  2270   // If the event doesn't cause scroll actually, we cannot find scroll target
  2271   // because we check if the event can cause scroll actually on each found
  2272   // scrollable frame.
  2273   if (!aDirectionX && !aDirectionY) {
  2274     return nullptr;
  2277   bool checkIfScrollableX =
  2278     aDirectionX && (aOptions & PREFER_ACTUAL_SCROLLABLE_TARGET_ALONG_X_AXIS);
  2279   bool checkIfScrollableY =
  2280     aDirectionY && (aOptions & PREFER_ACTUAL_SCROLLABLE_TARGET_ALONG_Y_AXIS);
  2282   nsIScrollableFrame* frameToScroll = nullptr;
  2283   nsIFrame* scrollFrame =
  2284     !(aOptions & START_FROM_PARENT) ? aTargetFrame :
  2285                                       GetParentFrameToScroll(aTargetFrame);
  2286   for (; scrollFrame; scrollFrame = GetParentFrameToScroll(scrollFrame)) {
  2287     // Check whether the frame wants to provide us with a scrollable view.
  2288     frameToScroll = scrollFrame->GetScrollTargetFrame();
  2289     if (!frameToScroll) {
  2290       continue;
  2293     // Don't scroll vertically by mouse-wheel on a single-line text control.
  2294     if (checkIfScrollableY) {
  2295       nsIContent* c = scrollFrame->GetContent();
  2296       nsCOMPtr<nsITextControlElement> ctrl =
  2297         do_QueryInterface(c->IsInAnonymousSubtree() ? c->GetBindingParent() : c);
  2298       if (ctrl && ctrl->IsSingleLineTextControl()) {
  2299         continue;
  2303     if (!checkIfScrollableX && !checkIfScrollableY) {
  2304       return frameToScroll;
  2307     ScrollbarStyles ss = frameToScroll->GetScrollbarStyles();
  2308     bool hiddenForV = (NS_STYLE_OVERFLOW_HIDDEN == ss.mVertical);
  2309     bool hiddenForH = (NS_STYLE_OVERFLOW_HIDDEN == ss.mHorizontal);
  2310     if ((hiddenForV && hiddenForH) ||
  2311         (checkIfScrollableY && !checkIfScrollableX && hiddenForV) ||
  2312         (checkIfScrollableX && !checkIfScrollableY && hiddenForH)) {
  2313       continue;
  2316     // For default action, we should climb up the tree if cannot scroll it
  2317     // by the event actually.
  2318     bool canScroll =
  2319       WheelHandlingUtils::CanScrollOn(frameToScroll, aDirectionX, aDirectionY);
  2320     // Comboboxes need special care.
  2321     nsIComboboxControlFrame* comboBox = do_QueryFrame(scrollFrame);
  2322     if (comboBox) {
  2323       if (comboBox->IsDroppedDown()) {
  2324         // Don't propagate to parent when drop down menu is active.
  2325         return canScroll ? frameToScroll : nullptr;
  2327       // Always propagate when not dropped down (even if focused).
  2328       continue;
  2331     if (canScroll) {
  2332       return frameToScroll;
  2336   nsIFrame* newFrame = nsLayoutUtils::GetCrossDocParentFrame(
  2337       aTargetFrame->PresContext()->FrameManager()->GetRootFrame());
  2338   aOptions =
  2339     static_cast<ComputeScrollTargetOptions>(aOptions & ~START_FROM_PARENT);
  2340   return newFrame ? ComputeScrollTarget(newFrame, aEvent, aOptions) : nullptr;
  2343 nsSize
  2344 EventStateManager::GetScrollAmount(nsPresContext* aPresContext,
  2345                                    WidgetWheelEvent* aEvent,
  2346                                    nsIScrollableFrame* aScrollableFrame)
  2348   MOZ_ASSERT(aPresContext);
  2349   MOZ_ASSERT(aEvent);
  2351   bool isPage = (aEvent->deltaMode == nsIDOMWheelEvent::DOM_DELTA_PAGE);
  2352   if (aScrollableFrame) {
  2353     return isPage ? aScrollableFrame->GetPageScrollAmount() :
  2354                     aScrollableFrame->GetLineScrollAmount();
  2357   // If there is no scrollable frame and page scrolling, use view port size.
  2358   if (isPage) {
  2359     return aPresContext->GetVisibleArea().Size();
  2362   // If there is no scrollable frame, we should use root frame's information.
  2363   nsIFrame* rootFrame = aPresContext->PresShell()->GetRootFrame();
  2364   if (!rootFrame) {
  2365     return nsSize(0, 0);
  2367   nsRefPtr<nsFontMetrics> fm;
  2368   nsLayoutUtils::GetFontMetricsForFrame(rootFrame, getter_AddRefs(fm),
  2369     nsLayoutUtils::FontSizeInflationFor(rootFrame));
  2370   NS_ENSURE_TRUE(fm, nsSize(0, 0));
  2371   return nsSize(fm->AveCharWidth(), fm->MaxHeight());
  2374 void
  2375 EventStateManager::DoScrollText(nsIScrollableFrame* aScrollableFrame,
  2376                                 WidgetWheelEvent* aEvent)
  2378   MOZ_ASSERT(aScrollableFrame);
  2379   MOZ_ASSERT(aEvent);
  2381   nsIFrame* scrollFrame = do_QueryFrame(aScrollableFrame);
  2382   MOZ_ASSERT(scrollFrame);
  2383   nsWeakFrame scrollFrameWeak(scrollFrame);
  2385   nsIFrame* lastScrollFrame = WheelTransaction::GetTargetFrame();
  2386   if (!lastScrollFrame) {
  2387     WheelTransaction::BeginTransaction(scrollFrame, aEvent);
  2388   } else if (lastScrollFrame != scrollFrame) {
  2389     WheelTransaction::EndTransaction();
  2390     WheelTransaction::BeginTransaction(scrollFrame, aEvent);
  2391   } else {
  2392     WheelTransaction::UpdateTransaction(aEvent);
  2395   // When the scroll event will not scroll any views, UpdateTransaction
  2396   // fired MozMouseScrollFailed event which is for automated testing.
  2397   // In the event handler, the target frame might be destroyed.  Then,
  2398   // we should not try scrolling anything.
  2399   if (!scrollFrameWeak.IsAlive()) {
  2400     WheelTransaction::EndTransaction();
  2401     return;
  2404   // Default action's actual scroll amount should be computed from device
  2405   // pixels.
  2406   nsPresContext* pc = scrollFrame->PresContext();
  2407   nsSize scrollAmount = GetScrollAmount(pc, aEvent, aScrollableFrame);
  2408   nsIntSize scrollAmountInDevPixels(
  2409     pc->AppUnitsToDevPixels(scrollAmount.width),
  2410     pc->AppUnitsToDevPixels(scrollAmount.height));
  2411   nsIntPoint actualDevPixelScrollAmount =
  2412     DeltaAccumulator::GetInstance()->
  2413       ComputeScrollAmountForDefaultAction(aEvent, scrollAmountInDevPixels);
  2415   // Don't scroll around the axis whose overflow style is hidden.
  2416   ScrollbarStyles overflowStyle = aScrollableFrame->GetScrollbarStyles();
  2417   if (overflowStyle.mHorizontal == NS_STYLE_OVERFLOW_HIDDEN) {
  2418     actualDevPixelScrollAmount.x = 0;
  2420   if (overflowStyle.mVertical == NS_STYLE_OVERFLOW_HIDDEN) {
  2421     actualDevPixelScrollAmount.y = 0;
  2424   nsIAtom* origin = nullptr;
  2425   switch (aEvent->deltaMode) {
  2426     case nsIDOMWheelEvent::DOM_DELTA_LINE:
  2427       origin = nsGkAtoms::mouseWheel;
  2428       break;
  2429     case nsIDOMWheelEvent::DOM_DELTA_PAGE:
  2430       origin = nsGkAtoms::pages;
  2431       break;
  2432     case nsIDOMWheelEvent::DOM_DELTA_PIXEL:
  2433       origin = nsGkAtoms::pixels;
  2434       break;
  2435     default:
  2436       MOZ_CRASH("Invalid deltaMode value comes");
  2439   // We shouldn't scroll more one page at once except when over one page scroll
  2440   // is allowed for the event.
  2441   nsSize pageSize = aScrollableFrame->GetPageScrollAmount();
  2442   nsIntSize devPixelPageSize(pc->AppUnitsToDevPixels(pageSize.width),
  2443                              pc->AppUnitsToDevPixels(pageSize.height));
  2444   if (!WheelPrefs::GetInstance()->IsOverOnePageScrollAllowedX(aEvent) &&
  2445       DeprecatedAbs(actualDevPixelScrollAmount.x) > devPixelPageSize.width) {
  2446     actualDevPixelScrollAmount.x =
  2447       (actualDevPixelScrollAmount.x >= 0) ? devPixelPageSize.width :
  2448                                             -devPixelPageSize.width;
  2451   if (!WheelPrefs::GetInstance()->IsOverOnePageScrollAllowedY(aEvent) &&
  2452       DeprecatedAbs(actualDevPixelScrollAmount.y) > devPixelPageSize.height) {
  2453     actualDevPixelScrollAmount.y =
  2454       (actualDevPixelScrollAmount.y >= 0) ? devPixelPageSize.height :
  2455                                             -devPixelPageSize.height;
  2458   bool isDeltaModePixel =
  2459     (aEvent->deltaMode == nsIDOMWheelEvent::DOM_DELTA_PIXEL);
  2461   nsIScrollableFrame::ScrollMode mode;
  2462   switch (aEvent->scrollType) {
  2463     case WidgetWheelEvent::SCROLL_DEFAULT:
  2464       if (isDeltaModePixel) {
  2465         mode = nsIScrollableFrame::NORMAL;
  2466       } else {
  2467         mode = nsIScrollableFrame::SMOOTH;
  2469       break;
  2470     case WidgetWheelEvent::SCROLL_SYNCHRONOUSLY:
  2471       mode = nsIScrollableFrame::INSTANT;
  2472       break;
  2473     case WidgetWheelEvent::SCROLL_ASYNCHRONOUSELY:
  2474       mode = nsIScrollableFrame::NORMAL;
  2475       break;
  2476     case WidgetWheelEvent::SCROLL_SMOOTHLY:
  2477       mode = nsIScrollableFrame::SMOOTH;
  2478       break;
  2479     default:
  2480       MOZ_CRASH("Invalid scrollType value comes");
  2483   nsIntPoint overflow;
  2484   aScrollableFrame->ScrollBy(actualDevPixelScrollAmount,
  2485                              nsIScrollableFrame::DEVICE_PIXELS,
  2486                              mode, &overflow, origin);
  2488   if (!scrollFrameWeak.IsAlive()) {
  2489     // If the scroll causes changing the layout, we can think that the event
  2490     // has been completely consumed by the content.  Then, users probably don't
  2491     // want additional action.
  2492     aEvent->overflowDeltaX = aEvent->overflowDeltaY = 0;
  2493   } else if (isDeltaModePixel) {
  2494     aEvent->overflowDeltaX = overflow.x;
  2495     aEvent->overflowDeltaY = overflow.y;
  2496   } else {
  2497     aEvent->overflowDeltaX =
  2498       static_cast<double>(overflow.x) / scrollAmountInDevPixels.width;
  2499     aEvent->overflowDeltaY =
  2500       static_cast<double>(overflow.y) / scrollAmountInDevPixels.height;
  2503   // If CSS overflow properties caused not to scroll, the overflowDelta* values
  2504   // should be same as delta* values since they may be used as gesture event by
  2505   // widget.  However, if there is another scrollable element in the ancestor
  2506   // along the axis, probably users don't want the operation to cause
  2507   // additional action such as moving history.  In such case, overflowDelta
  2508   // values should stay zero.
  2509   if (scrollFrameWeak.IsAlive()) {
  2510     if (aEvent->deltaX &&
  2511         overflowStyle.mHorizontal == NS_STYLE_OVERFLOW_HIDDEN &&
  2512         !ComputeScrollTarget(scrollFrame, aEvent,
  2513                              COMPUTE_SCROLLABLE_ANCESTOR_ALONG_X_AXIS)) {
  2514       aEvent->overflowDeltaX = aEvent->deltaX;
  2516     if (aEvent->deltaY &&
  2517         overflowStyle.mVertical == NS_STYLE_OVERFLOW_HIDDEN &&
  2518         !ComputeScrollTarget(scrollFrame, aEvent,
  2519                              COMPUTE_SCROLLABLE_ANCESTOR_ALONG_Y_AXIS)) {
  2520       aEvent->overflowDeltaY = aEvent->deltaY;
  2524   NS_ASSERTION(aEvent->overflowDeltaX == 0 ||
  2525     (aEvent->overflowDeltaX > 0) == (aEvent->deltaX > 0),
  2526     "The sign of overflowDeltaX is different from the scroll direction");
  2527   NS_ASSERTION(aEvent->overflowDeltaY == 0 ||
  2528     (aEvent->overflowDeltaY > 0) == (aEvent->deltaY > 0),
  2529     "The sign of overflowDeltaY is different from the scroll direction");
  2531   WheelPrefs::GetInstance()->CancelApplyingUserPrefsFromOverflowDelta(aEvent);
  2534 void
  2535 EventStateManager::DecideGestureEvent(WidgetGestureNotifyEvent* aEvent,
  2536                                       nsIFrame* targetFrame)
  2539   NS_ASSERTION(aEvent->message == NS_GESTURENOTIFY_EVENT_START,
  2540                "DecideGestureEvent called with a non-gesture event");
  2542   /* Check the ancestor tree to decide if any frame is willing* to receive
  2543    * a MozPixelScroll event. If that's the case, the current touch gesture
  2544    * will be used as a pan gesture; otherwise it will be a regular
  2545    * mousedown/mousemove/click event.
  2547    * *willing: determine if it makes sense to pan the element using scroll events:
  2548    *  - For web content: if there are any visible scrollbars on the touch point
  2549    *  - For XUL: if it's an scrollable element that can currently scroll in some
  2550     *    direction.
  2552    * Note: we'll have to one-off various cases to ensure a good usable behavior
  2553    */
  2554   WidgetGestureNotifyEvent::ePanDirection panDirection =
  2555     WidgetGestureNotifyEvent::ePanNone;
  2556   bool displayPanFeedback = false;
  2557   for (nsIFrame* current = targetFrame; current;
  2558        current = nsLayoutUtils::GetCrossDocParentFrame(current)) {
  2560     nsIAtom* currentFrameType = current->GetType();
  2562     // Scrollbars should always be draggable
  2563     if (currentFrameType == nsGkAtoms::scrollbarFrame) {
  2564       panDirection = WidgetGestureNotifyEvent::ePanNone;
  2565       break;
  2568 #ifdef MOZ_XUL
  2569     // Special check for trees
  2570     nsTreeBodyFrame* treeFrame = do_QueryFrame(current);
  2571     if (treeFrame) {
  2572       if (treeFrame->GetHorizontalOverflow()) {
  2573         panDirection = WidgetGestureNotifyEvent::ePanHorizontal;
  2575       if (treeFrame->GetVerticalOverflow()) {
  2576         panDirection = WidgetGestureNotifyEvent::ePanVertical;
  2578       break;
  2580 #endif
  2582     nsIScrollableFrame* scrollableFrame = do_QueryFrame(current);
  2583     if (scrollableFrame) {
  2584       if (current->IsFrameOfType(nsIFrame::eXULBox)) {
  2585         displayPanFeedback = true;
  2587         nsRect scrollRange = scrollableFrame->GetScrollRange();
  2588         bool canScrollHorizontally = scrollRange.width > 0;
  2590         if (targetFrame->GetType() == nsGkAtoms::menuFrame) {
  2591           // menu frames report horizontal scroll when they have submenus
  2592           // and we don't want that
  2593           canScrollHorizontally = false;
  2594           displayPanFeedback = false;
  2597         // Vertical panning has priority over horizontal panning, so
  2598         // when vertical movement is possible we can just finish the loop.
  2599         if (scrollRange.height > 0) {
  2600           panDirection = WidgetGestureNotifyEvent::ePanVertical;
  2601           break;
  2604         if (canScrollHorizontally) {
  2605           panDirection = WidgetGestureNotifyEvent::ePanHorizontal;
  2606           displayPanFeedback = false;
  2608       } else { //Not a XUL box
  2609         uint32_t scrollbarVisibility = scrollableFrame->GetScrollbarVisibility();
  2611         //Check if we have visible scrollbars
  2612         if (scrollbarVisibility & nsIScrollableFrame::VERTICAL) {
  2613           panDirection = WidgetGestureNotifyEvent::ePanVertical;
  2614           displayPanFeedback = true;
  2615           break;
  2618         if (scrollbarVisibility & nsIScrollableFrame::HORIZONTAL) {
  2619           panDirection = WidgetGestureNotifyEvent::ePanHorizontal;
  2620           displayPanFeedback = true;
  2623     } //scrollableFrame
  2624   } //ancestor chain
  2626   aEvent->displayPanFeedback = displayPanFeedback;
  2627   aEvent->panDirection = panDirection;
  2630 #ifdef XP_MACOSX
  2631 static bool
  2632 NodeAllowsClickThrough(nsINode* aNode)
  2634   while (aNode) {
  2635     if (aNode->IsElement() && aNode->AsElement()->IsXUL()) {
  2636       mozilla::dom::Element* element = aNode->AsElement();
  2637       static nsIContent::AttrValuesArray strings[] =
  2638         {&nsGkAtoms::always, &nsGkAtoms::never, nullptr};
  2639       switch (element->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::clickthrough,
  2640                                        strings, eCaseMatters)) {
  2641         case 0:
  2642           return true;
  2643         case 1:
  2644           return false;
  2647     aNode = nsContentUtils::GetCrossDocParentNode(aNode);
  2649   return true;
  2651 #endif
  2653 nsresult
  2654 EventStateManager::PostHandleEvent(nsPresContext* aPresContext,
  2655                                    WidgetEvent* aEvent,
  2656                                    nsIFrame* aTargetFrame,
  2657                                    nsEventStatus* aStatus)
  2659   NS_ENSURE_ARG(aPresContext);
  2660   NS_ENSURE_ARG_POINTER(aStatus);
  2662   bool dispatchedToContentProcess = HandleCrossProcessEvent(aEvent,
  2663                                                             aTargetFrame,
  2664                                                             aStatus);
  2666   mCurrentTarget = aTargetFrame;
  2667   mCurrentTargetContent = nullptr;
  2669   // Most of the events we handle below require a frame.
  2670   // Add special cases here.
  2671   if (!mCurrentTarget && aEvent->message != NS_MOUSE_BUTTON_UP &&
  2672       aEvent->message != NS_MOUSE_BUTTON_DOWN) {
  2673     return NS_OK;
  2676   //Keep the prescontext alive, we might need it after event dispatch
  2677   nsRefPtr<nsPresContext> presContext = aPresContext;
  2678   nsresult ret = NS_OK;
  2680   switch (aEvent->message) {
  2681   case NS_MOUSE_BUTTON_DOWN:
  2683       WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
  2684       if (mouseEvent->button == WidgetMouseEvent::eLeftButton &&
  2685           !sNormalLMouseEventInProcess) {
  2686         // We got a mouseup event while a mousedown event was being processed.
  2687         // Make sure that the capturing content is cleared.
  2688         nsIPresShell::SetCapturingContent(nullptr, 0);
  2689         break;
  2692       nsCOMPtr<nsIContent> activeContent;
  2693       if (nsEventStatus_eConsumeNoDefault != *aStatus) {
  2694         nsCOMPtr<nsIContent> newFocus;      
  2695         bool suppressBlur = false;
  2696         if (mCurrentTarget) {
  2697           mCurrentTarget->GetContentForEvent(aEvent, getter_AddRefs(newFocus));
  2698           const nsStyleUserInterface* ui = mCurrentTarget->StyleUserInterface();
  2699           activeContent = mCurrentTarget->GetContent();
  2701           // In some cases, we do not want to even blur the current focused
  2702           // element. Those cases are:
  2703           // 1. -moz-user-focus CSS property is set to 'ignore';
  2704           // 2. Element with NS_EVENT_STATE_DISABLED
  2705           //    (aka :disabled pseudo-class for HTML element);
  2706           // 3. XUL control element has the disabled property set to 'true'.
  2707           //
  2708           // We can't use nsIFrame::IsFocusable() because we want to blur when
  2709           // we click on a visibility: none element.
  2710           // We can't use nsIContent::IsFocusable() because we want to blur when
  2711           // we click on a non-focusable element like a <div>.
  2712           // We have to use |aEvent->target| to not make sure we do not check an
  2713           // anonymous node of the targeted element.
  2714           suppressBlur = (ui->mUserFocus == NS_STYLE_USER_FOCUS_IGNORE);
  2716           if (!suppressBlur) {
  2717             nsCOMPtr<Element> element = do_QueryInterface(aEvent->target);
  2718             suppressBlur = element &&
  2719                            element->State().HasState(NS_EVENT_STATE_DISABLED);
  2722           if (!suppressBlur) {
  2723             nsCOMPtr<nsIDOMXULControlElement> xulControl =
  2724               do_QueryInterface(aEvent->target);
  2725             if (xulControl) {
  2726               bool disabled;
  2727               xulControl->GetDisabled(&disabled);
  2728               suppressBlur = disabled;
  2733         if (!suppressBlur) {
  2734           suppressBlur = nsContentUtils::IsUserFocusIgnored(activeContent);
  2737         nsIFrame* currFrame = mCurrentTarget;
  2739         // When a root content which isn't editable but has an editable HTML
  2740         // <body> element is clicked, we should redirect the focus to the
  2741         // the <body> element.  E.g., when an user click bottom of the editor
  2742         // where is outside of the <body> element, the <body> should be focused
  2743         // and the user can edit immediately after that.
  2744         //
  2745         // NOTE: The newFocus isn't editable that also means it's not in
  2746         // designMode.  In designMode, all contents are not focusable.
  2747         if (newFocus && !newFocus->IsEditable()) {
  2748           nsIDocument *doc = newFocus->GetCurrentDoc();
  2749           if (doc && newFocus == doc->GetRootElement()) {
  2750             nsIContent *bodyContent =
  2751               nsLayoutUtils::GetEditableRootContentByContentEditable(doc);
  2752             if (bodyContent) {
  2753               nsIFrame* bodyFrame = bodyContent->GetPrimaryFrame();
  2754               if (bodyFrame) {
  2755                 currFrame = bodyFrame;
  2756                 newFocus = bodyContent;
  2762         // When the mouse is pressed, the default action is to focus the
  2763         // target. Look for the nearest enclosing focusable frame.
  2764         while (currFrame) {
  2765           // If the mousedown happened inside a popup, don't
  2766           // try to set focus on one of its containing elements
  2767           const nsStyleDisplay* display = currFrame->StyleDisplay();
  2768           if (display->mDisplay == NS_STYLE_DISPLAY_POPUP) {
  2769             newFocus = nullptr;
  2770             break;
  2773           int32_t tabIndexUnused;
  2774           if (currFrame->IsFocusable(&tabIndexUnused, true)) {
  2775             newFocus = currFrame->GetContent();
  2776             nsCOMPtr<nsIDOMElement> domElement(do_QueryInterface(newFocus));
  2777             if (domElement)
  2778               break;
  2780           currFrame = currFrame->GetParent();
  2783         nsIFocusManager* fm = nsFocusManager::GetFocusManager();
  2784         if (fm) {
  2785           // if something was found to focus, focus it. Otherwise, if the
  2786           // element that was clicked doesn't have -moz-user-focus: ignore,
  2787           // clear the existing focus. For -moz-user-focus: ignore, the focus
  2788           // is just left as is.
  2789           // Another effect of mouse clicking, handled in nsSelection, is that
  2790           // it should update the caret position to where the mouse was
  2791           // clicked. Because the focus is cleared when clicking on a
  2792           // non-focusable node, the next press of the tab key will cause
  2793           // focus to be shifted from the caret position instead of the root.
  2794           if (newFocus && currFrame) {
  2795             // use the mouse flag and the noscroll flag so that the content
  2796             // doesn't unexpectedly scroll when clicking an element that is
  2797             // only hald visible
  2798             nsCOMPtr<nsIDOMElement> newFocusElement = do_QueryInterface(newFocus);
  2799             fm->SetFocus(newFocusElement, nsIFocusManager::FLAG_BYMOUSE |
  2800                                           nsIFocusManager::FLAG_NOSCROLL);
  2802           else if (!suppressBlur) {
  2803             // clear the focus within the frame and then set it as the
  2804             // focused frame
  2805             EnsureDocument(mPresContext);
  2806             if (mDocument) {
  2807 #ifdef XP_MACOSX
  2808               if (!activeContent || !activeContent->IsXUL())
  2809 #endif
  2810                 fm->ClearFocus(mDocument->GetWindow());
  2811               fm->SetFocusedWindow(mDocument->GetWindow());
  2816         // The rest is left button-specific.
  2817         if (mouseEvent->button != WidgetMouseEvent::eLeftButton) {
  2818           break;
  2821         if (activeContent) {
  2822           // The nearest enclosing element goes into the
  2823           // :active state.  If we fail the QI to DOMElement,
  2824           // then we know we're only a node, and that we need
  2825           // to obtain our parent element and put it into :active
  2826           // instead.
  2827           nsCOMPtr<nsIDOMElement> elt(do_QueryInterface(activeContent));
  2828           if (!elt) {
  2829             nsIContent* par = activeContent->GetParent();
  2830             if (par)
  2831               activeContent = par;
  2835       else {
  2836         // if we're here, the event handler returned false, so stop
  2837         // any of our own processing of a drag. Workaround for bug 43258.
  2838         StopTrackingDragGesture();
  2840         // When the event was cancelled, there is currently a chrome document
  2841         // focused and a mousedown just occurred on a content document, ensure
  2842         // that the window that was clicked is focused.
  2843         EnsureDocument(mPresContext);
  2844         nsIFocusManager* fm = nsFocusManager::GetFocusManager();
  2845         if (mDocument && fm) {
  2846           nsCOMPtr<nsIDOMWindow> currentWindow;
  2847           fm->GetFocusedWindow(getter_AddRefs(currentWindow));
  2848           if (currentWindow && mDocument->GetWindow() &&
  2849               currentWindow != mDocument->GetWindow() &&
  2850               !nsContentUtils::IsChromeDoc(mDocument)) {
  2851             nsCOMPtr<nsIDOMWindow> currentTop;
  2852             nsCOMPtr<nsIDOMWindow> newTop;
  2853             currentWindow->GetTop(getter_AddRefs(currentTop));
  2854             mDocument->GetWindow()->GetTop(getter_AddRefs(newTop));
  2855             nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(currentWindow);
  2856             nsCOMPtr<nsIDocument> currentDoc = win->GetExtantDoc();
  2857             if (nsContentUtils::IsChromeDoc(currentDoc) ||
  2858                 (currentTop && newTop && currentTop != newTop)) {
  2859               fm->SetFocusedWindow(mDocument->GetWindow());
  2864       SetActiveManager(this, activeContent);
  2866     break;
  2867   case NS_POINTER_CANCEL:
  2868   case NS_POINTER_UP: {
  2869     WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent();
  2870     // After UP/Cancel Touch pointers become invalid so we can remove relevant helper from Table
  2871     // Mouse/Pen pointers are valid all the time (not only between down/up)
  2872     if (pointerEvent->inputSource == nsIDOMMouseEvent::MOZ_SOURCE_TOUCH) {
  2873       mPointersEnterLeaveHelper.Remove(pointerEvent->pointerId);
  2875     if (pointerEvent->inputSource != nsIDOMMouseEvent::MOZ_SOURCE_MOUSE) {
  2876       GenerateMouseEnterExit(pointerEvent);
  2878     break;
  2880   case NS_MOUSE_BUTTON_UP:
  2882       ClearGlobalActiveContent(this);
  2883       WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
  2884       if (mouseEvent && mouseEvent->IsReal()) {
  2885         if (!mCurrentTarget) {
  2886           GetEventTarget();
  2888         // Make sure to dispatch the click even if there is no frame for
  2889         // the current target element. This is required for Web compatibility.
  2890         ret = CheckForAndDispatchClick(presContext, mouseEvent, aStatus);
  2893       nsIPresShell *shell = presContext->GetPresShell();
  2894       if (shell) {
  2895         nsRefPtr<nsFrameSelection> frameSelection = shell->FrameSelection();
  2896         frameSelection->SetMouseDownState(false);
  2899     break;
  2900   case NS_WHEEL_STOP:
  2902       MOZ_ASSERT(aEvent->mFlags.mIsTrusted);
  2903       ScrollbarsForWheel::MayInactivate();
  2905     break;
  2906   case NS_WHEEL_WHEEL:
  2907   case NS_WHEEL_START:
  2909       MOZ_ASSERT(aEvent->mFlags.mIsTrusted);
  2911       if (*aStatus == nsEventStatus_eConsumeNoDefault) {
  2912         ScrollbarsForWheel::Inactivate();
  2913         break;
  2916       WidgetWheelEvent* wheelEvent = aEvent->AsWheelEvent();
  2917       switch (WheelPrefs::GetInstance()->ComputeActionFor(wheelEvent)) {
  2918         case WheelPrefs::ACTION_SCROLL: {
  2919           // For scrolling of default action, we should honor the mouse wheel
  2920           // transaction.
  2922           ScrollbarsForWheel::PrepareToScrollText(this, aTargetFrame, wheelEvent);
  2924           if (aEvent->message != NS_WHEEL_WHEEL ||
  2925               (!wheelEvent->deltaX && !wheelEvent->deltaY)) {
  2926             break;
  2929           nsIScrollableFrame* scrollTarget =
  2930             ComputeScrollTarget(aTargetFrame, wheelEvent,
  2931                                 COMPUTE_DEFAULT_ACTION_TARGET);
  2933           ScrollbarsForWheel::SetActiveScrollTarget(scrollTarget);
  2935           nsIFrame* rootScrollFrame = !aTargetFrame ? nullptr :
  2936             aTargetFrame->PresContext()->PresShell()->GetRootScrollFrame();
  2937           nsIScrollableFrame* rootScrollableFrame = nullptr;
  2938           if (rootScrollFrame) {
  2939             rootScrollableFrame = do_QueryFrame(rootScrollFrame);
  2941           if (!scrollTarget || scrollTarget == rootScrollableFrame) {
  2942             wheelEvent->mViewPortIsOverscrolled = true;
  2944           wheelEvent->overflowDeltaX = wheelEvent->deltaX;
  2945           wheelEvent->overflowDeltaY = wheelEvent->deltaY;
  2946           WheelPrefs::GetInstance()->
  2947             CancelApplyingUserPrefsFromOverflowDelta(wheelEvent);
  2948           if (scrollTarget) {
  2949             DoScrollText(scrollTarget, wheelEvent);
  2950           } else {
  2951             WheelTransaction::EndTransaction();
  2952             ScrollbarsForWheel::Inactivate();
  2954           break;
  2956         case WheelPrefs::ACTION_HISTORY: {
  2957           // If this event doesn't cause NS_MOUSE_SCROLL event or the direction
  2958           // is oblique, don't perform history back/forward.
  2959           int32_t intDelta = wheelEvent->GetPreferredIntDelta();
  2960           if (!intDelta) {
  2961             break;
  2963           DoScrollHistory(intDelta);
  2964           break;
  2966         case WheelPrefs::ACTION_ZOOM: {
  2967           // If this event doesn't cause NS_MOUSE_SCROLL event or the direction
  2968           // is oblique, don't perform zoom in/out.
  2969           int32_t intDelta = wheelEvent->GetPreferredIntDelta();
  2970           if (!intDelta) {
  2971             break;
  2973           DoScrollZoom(aTargetFrame, intDelta);
  2974           break;
  2976         case WheelPrefs::ACTION_NONE:
  2977         default:
  2978           // If we don't handle the wheel event, all of the delta values must
  2979           // be overflown delta values.
  2980           wheelEvent->overflowDeltaX = wheelEvent->deltaX;
  2981           wheelEvent->overflowDeltaY = wheelEvent->deltaY;
  2982           WheelPrefs::GetInstance()->
  2983             CancelApplyingUserPrefsFromOverflowDelta(wheelEvent);
  2984           break;
  2986       *aStatus = nsEventStatus_eConsumeNoDefault;
  2988     break;
  2990   case NS_GESTURENOTIFY_EVENT_START:
  2992       if (nsEventStatus_eConsumeNoDefault != *aStatus) {
  2993         DecideGestureEvent(aEvent->AsGestureNotifyEvent(), mCurrentTarget);
  2996     break;
  2998   case NS_DRAGDROP_ENTER:
  2999   case NS_DRAGDROP_OVER:
  3001       NS_ASSERTION(aEvent->eventStructType == NS_DRAG_EVENT, "Expected a drag event");
  3003       nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
  3004       if (!dragSession)
  3005         break;
  3007       // Reset the flag.
  3008       dragSession->SetOnlyChromeDrop(false);
  3009       if (mPresContext) {
  3010         EnsureDocument(mPresContext);
  3012       bool isChromeDoc = nsContentUtils::IsChromeDoc(mDocument);
  3014       // the initial dataTransfer is the one from the dragstart event that
  3015       // was set on the dragSession when the drag began.
  3016       nsCOMPtr<nsIDOMDataTransfer> dataTransfer;
  3017       nsCOMPtr<nsIDOMDataTransfer> initialDataTransfer;
  3018       dragSession->GetDataTransfer(getter_AddRefs(initialDataTransfer));
  3020       WidgetDragEvent *dragEvent = aEvent->AsDragEvent();
  3022       // collect any changes to moz cursor settings stored in the event's
  3023       // data transfer.
  3024       UpdateDragDataTransfer(dragEvent);
  3026       // cancelling a dragenter or dragover event means that a drop should be
  3027       // allowed, so update the dropEffect and the canDrop state to indicate
  3028       // that a drag is allowed. If the event isn't cancelled, a drop won't be
  3029       // allowed. Essentially, to allow a drop somewhere, specify the effects
  3030       // using the effectAllowed and dropEffect properties in a dragenter or
  3031       // dragover event and cancel the event. To not allow a drop somewhere,
  3032       // don't cancel the event or set the effectAllowed or dropEffect to
  3033       // "none". This way, if the event is just ignored, no drop will be
  3034       // allowed.
  3035       uint32_t dropEffect = nsIDragService::DRAGDROP_ACTION_NONE;
  3036       if (nsEventStatus_eConsumeNoDefault == *aStatus) {
  3037         // if the event has a dataTransfer set, use it.
  3038         if (dragEvent->dataTransfer) {
  3039           // get the dataTransfer and the dropEffect that was set on it
  3040           dataTransfer = do_QueryInterface(dragEvent->dataTransfer);
  3041           dataTransfer->GetDropEffectInt(&dropEffect);
  3043         else {
  3044           // if dragEvent->dataTransfer is null, it means that no attempt was
  3045           // made to access the dataTransfer during the event, yet the event
  3046           // was cancelled. Instead, use the initial data transfer available
  3047           // from the drag session. The drop effect would not have been
  3048           // initialized (which is done in DragEvent::GetDataTransfer),
  3049           // so set it from the drag action. We'll still want to filter it
  3050           // based on the effectAllowed below.
  3051           dataTransfer = initialDataTransfer;
  3053           uint32_t action;
  3054           dragSession->GetDragAction(&action);
  3056           // filter the drop effect based on the action. Use UNINITIALIZED as
  3057           // any effect is allowed.
  3058           dropEffect = nsContentUtils::FilterDropEffect(action,
  3059                          nsIDragService::DRAGDROP_ACTION_UNINITIALIZED);
  3062         // At this point, if the dataTransfer is null, it means that the
  3063         // drag was originally started by directly calling the drag service.
  3064         // Just assume that all effects are allowed.
  3065         uint32_t effectAllowed = nsIDragService::DRAGDROP_ACTION_UNINITIALIZED;
  3066         if (dataTransfer)
  3067           dataTransfer->GetEffectAllowedInt(&effectAllowed);
  3069         // set the drag action based on the drop effect and effect allowed.
  3070         // The drop effect field on the drag transfer object specifies the
  3071         // desired current drop effect. However, it cannot be used if the
  3072         // effectAllowed state doesn't include that type of action. If the
  3073         // dropEffect is "none", then the action will be 'none' so a drop will
  3074         // not be allowed.
  3075         uint32_t action = nsIDragService::DRAGDROP_ACTION_NONE;
  3076         if (effectAllowed == nsIDragService::DRAGDROP_ACTION_UNINITIALIZED ||
  3077             dropEffect & effectAllowed)
  3078           action = dropEffect;
  3080         if (action == nsIDragService::DRAGDROP_ACTION_NONE)
  3081           dropEffect = nsIDragService::DRAGDROP_ACTION_NONE;
  3083         // inform the drag session that a drop is allowed on this node.
  3084         dragSession->SetDragAction(action);
  3085         dragSession->SetCanDrop(action != nsIDragService::DRAGDROP_ACTION_NONE);
  3087         // For now, do this only for dragover.
  3088         //XXXsmaug dragenter needs some more work.
  3089         if (aEvent->message == NS_DRAGDROP_OVER && !isChromeDoc) {
  3090           // Someone has called preventDefault(), check whether is was on
  3091           // content or chrome.
  3092           dragSession->SetOnlyChromeDrop(
  3093             !dragEvent->mDefaultPreventedOnContent);
  3095       } else if (aEvent->message == NS_DRAGDROP_OVER && !isChromeDoc) {
  3096         // No one called preventDefault(), so handle drop only in chrome.
  3097         dragSession->SetOnlyChromeDrop(true);
  3100       // now set the drop effect in the initial dataTransfer. This ensures
  3101       // that we can get the desired drop effect in the drop event.
  3102       if (initialDataTransfer)
  3103         initialDataTransfer->SetDropEffectInt(dropEffect);
  3105     break;
  3107   case NS_DRAGDROP_DROP:
  3109       // now fire the dragdrop event, for compatibility with XUL
  3110       if (mCurrentTarget && nsEventStatus_eConsumeNoDefault != *aStatus) {
  3111         nsCOMPtr<nsIContent> targetContent;
  3112         mCurrentTarget->GetContentForEvent(aEvent,
  3113                                            getter_AddRefs(targetContent));
  3115         nsCOMPtr<nsIWidget> widget = mCurrentTarget->GetNearestWidget();
  3116         WidgetDragEvent event(aEvent->mFlags.mIsTrusted,
  3117                               NS_DRAGDROP_DRAGDROP, widget);
  3119         WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
  3120         event.refPoint = mouseEvent->refPoint;
  3121         if (mouseEvent->widget) {
  3122           event.refPoint += LayoutDeviceIntPoint::FromUntyped(mouseEvent->widget->WidgetToScreenOffset());
  3124         event.refPoint -= LayoutDeviceIntPoint::FromUntyped(widget->WidgetToScreenOffset());
  3125         event.modifiers = mouseEvent->modifiers;
  3126         event.buttons = mouseEvent->buttons;
  3127         event.inputSource = mouseEvent->inputSource;
  3129         nsEventStatus status = nsEventStatus_eIgnore;
  3130         nsCOMPtr<nsIPresShell> presShell = mPresContext->GetPresShell();
  3131         if (presShell) {
  3132           presShell->HandleEventWithTarget(&event, mCurrentTarget,
  3133                                            targetContent, &status);
  3136       sLastDragOverFrame = nullptr;
  3137       ClearGlobalActiveContent(this);
  3138       break;
  3140   case NS_DRAGDROP_EXIT:
  3141      // make sure to fire the enter and exit_synth events after the
  3142      // NS_DRAGDROP_EXIT event, otherwise we'll clean up too early
  3143     GenerateDragDropEnterExit(presContext, aEvent->AsDragEvent());
  3144     break;
  3146   case NS_KEY_UP:
  3147     break;
  3149   case NS_KEY_PRESS:
  3150     if (nsEventStatus_eConsumeNoDefault != *aStatus) {
  3151       WidgetKeyboardEvent* keyEvent = aEvent->AsKeyboardEvent();
  3152       //This is to prevent keyboard scrolling while alt modifier in use.
  3153       if (!keyEvent->IsAlt()) {
  3154         switch(keyEvent->keyCode) {
  3155           case NS_VK_TAB:
  3156           case NS_VK_F6:
  3157             // Handling the tab event after it was sent to content is bad,
  3158             // because to the FocusManager the remote-browser looks like one
  3159             // element, so we would just move the focus to the next element
  3160             // in chrome, instead of handling it in content.
  3161             if (dispatchedToContentProcess)
  3162               break;
  3164             EnsureDocument(mPresContext);
  3165             nsIFocusManager* fm = nsFocusManager::GetFocusManager();
  3166             if (fm && mDocument) {
  3167               // Shift focus forward or back depending on shift key
  3168               bool isDocMove =
  3169                 keyEvent->IsControl() || keyEvent->keyCode == NS_VK_F6;
  3170               uint32_t dir = keyEvent->IsShift() ?
  3171                   (isDocMove ? static_cast<uint32_t>(nsIFocusManager::MOVEFOCUS_BACKWARDDOC) :
  3172                                static_cast<uint32_t>(nsIFocusManager::MOVEFOCUS_BACKWARD)) :
  3173                   (isDocMove ? static_cast<uint32_t>(nsIFocusManager::MOVEFOCUS_FORWARDDOC) :
  3174                                static_cast<uint32_t>(nsIFocusManager::MOVEFOCUS_FORWARD));
  3175               nsCOMPtr<nsIDOMElement> result;
  3176               fm->MoveFocus(mDocument->GetWindow(), nullptr, dir,
  3177                             nsIFocusManager::FLAG_BYKEY,
  3178                             getter_AddRefs(result));
  3180             *aStatus = nsEventStatus_eConsumeNoDefault;
  3181             break;
  3185     break;
  3187   case NS_MOUSE_ENTER:
  3188     if (mCurrentTarget) {
  3189       nsCOMPtr<nsIContent> targetContent;
  3190       mCurrentTarget->GetContentForEvent(aEvent, getter_AddRefs(targetContent));
  3191       SetContentState(targetContent, NS_EVENT_STATE_HOVER);
  3193     break;
  3195 #ifdef XP_MACOSX
  3196   case NS_MOUSE_ACTIVATE:
  3197     if (mCurrentTarget) {
  3198       nsCOMPtr<nsIContent> targetContent;
  3199       mCurrentTarget->GetContentForEvent(aEvent, getter_AddRefs(targetContent));
  3200       if (!NodeAllowsClickThrough(targetContent)) {
  3201         *aStatus = nsEventStatus_eConsumeNoDefault;
  3204     break;
  3205 #endif
  3208   //Reset target frame to null to avoid mistargeting after reentrant event
  3209   mCurrentTarget = nullptr;
  3210   mCurrentTargetContent = nullptr;
  3212   return ret;
  3215 bool
  3216 EventStateManager::RemoteQueryContentEvent(WidgetEvent* aEvent)
  3218   WidgetQueryContentEvent* queryEvent = aEvent->AsQueryContentEvent();
  3219   if (!IsTargetCrossProcess(queryEvent)) {
  3220     return false;
  3222   // Will not be handled locally, remote the event
  3223   GetCrossProcessTarget()->HandleQueryContentEvent(*queryEvent);
  3224   return true;
  3227 TabParent*
  3228 EventStateManager::GetCrossProcessTarget()
  3230   return TabParent::GetIMETabParent();
  3233 bool
  3234 EventStateManager::IsTargetCrossProcess(WidgetGUIEvent* aEvent)
  3236   // Check to see if there is a focused, editable content in chrome,
  3237   // in that case, do not forward IME events to content
  3238   nsIContent *focusedContent = GetFocusedContent();
  3239   if (focusedContent && focusedContent->IsEditable())
  3240     return false;
  3241   return TabParent::GetIMETabParent() != nullptr;
  3244 void
  3245 EventStateManager::NotifyDestroyPresContext(nsPresContext* aPresContext)
  3247   IMEStateManager::OnDestroyPresContext(aPresContext);
  3248   if (mHoverContent) {
  3249     // Bug 70855: Presentation is going away, possibly for a reframe.
  3250     // Reset the hover state so that if we're recreating the presentation,
  3251     // we won't have the old hover state still set in the new presentation,
  3252     // as if the new presentation is resized, a new element may be hovered. 
  3253     SetContentState(nullptr, NS_EVENT_STATE_HOVER);
  3255   mPointersEnterLeaveHelper.Clear();
  3258 void
  3259 EventStateManager::SetPresContext(nsPresContext* aPresContext)
  3261   mPresContext = aPresContext;
  3264 void
  3265 EventStateManager::ClearFrameRefs(nsIFrame* aFrame)
  3267   if (aFrame && aFrame == mCurrentTarget) {
  3268     mCurrentTargetContent = aFrame->GetContent();
  3272 void
  3273 EventStateManager::UpdateCursor(nsPresContext* aPresContext,
  3274                                 WidgetEvent* aEvent,
  3275                                 nsIFrame* aTargetFrame,
  3276                                 nsEventStatus* aStatus)
  3278   if (aTargetFrame && IsRemoteTarget(aTargetFrame->GetContent())) {
  3279     return;
  3282   int32_t cursor = NS_STYLE_CURSOR_DEFAULT;
  3283   imgIContainer* container = nullptr;
  3284   bool haveHotspot = false;
  3285   float hotspotX = 0.0f, hotspotY = 0.0f;
  3287   //If cursor is locked just use the locked one
  3288   if (mLockCursor) {
  3289     cursor = mLockCursor;
  3291   //If not locked, look for correct cursor
  3292   else if (aTargetFrame) {
  3293       nsIFrame::Cursor framecursor;
  3294       nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent,
  3295                                                                 aTargetFrame);
  3296       if (NS_FAILED(aTargetFrame->GetCursor(pt, framecursor)))
  3297         return;  // don't update the cursor if we failed to get it from the frame see bug 118877
  3298       cursor = framecursor.mCursor;
  3299       container = framecursor.mContainer;
  3300       haveHotspot = framecursor.mHaveHotspot;
  3301       hotspotX = framecursor.mHotspotX;
  3302       hotspotY = framecursor.mHotspotY;
  3305   if (Preferences::GetBool("ui.use_activity_cursor", false)) {
  3306     // Check whether or not to show the busy cursor
  3307     nsCOMPtr<nsIDocShell> docShell(aPresContext->GetDocShell());
  3308     if (!docShell) return;
  3309     uint32_t busyFlags = nsIDocShell::BUSY_FLAGS_NONE;
  3310     docShell->GetBusyFlags(&busyFlags);
  3312     // Show busy cursor everywhere before page loads
  3313     // and just replace the arrow cursor after page starts loading
  3314     if (busyFlags & nsIDocShell::BUSY_FLAGS_BUSY &&
  3315           (cursor == NS_STYLE_CURSOR_AUTO || cursor == NS_STYLE_CURSOR_DEFAULT))
  3317       cursor = NS_STYLE_CURSOR_SPINNING;
  3318       container = nullptr;
  3322   if (aTargetFrame) {
  3323     SetCursor(cursor, container, haveHotspot, hotspotX, hotspotY,
  3324               aTargetFrame->GetNearestWidget(), false);
  3327   if (mLockCursor || NS_STYLE_CURSOR_AUTO != cursor) {
  3328     *aStatus = nsEventStatus_eConsumeDoDefault;
  3332 nsresult
  3333 EventStateManager::SetCursor(int32_t aCursor, imgIContainer* aContainer,
  3334                              bool aHaveHotspot,
  3335                              float aHotspotX, float aHotspotY,
  3336                              nsIWidget* aWidget, bool aLockCursor)
  3338   EnsureDocument(mPresContext);
  3339   NS_ENSURE_TRUE(mDocument, NS_ERROR_FAILURE);
  3340   sMouseOverDocument = mDocument.get();
  3342   nsCursor c;
  3344   NS_ENSURE_TRUE(aWidget, NS_ERROR_FAILURE);
  3345   if (aLockCursor) {
  3346     if (NS_STYLE_CURSOR_AUTO != aCursor) {
  3347       mLockCursor = aCursor;
  3349     else {
  3350       //If cursor style is set to auto we unlock the cursor again.
  3351       mLockCursor = 0;
  3354   switch (aCursor) {
  3355   default:
  3356   case NS_STYLE_CURSOR_AUTO:
  3357   case NS_STYLE_CURSOR_DEFAULT:
  3358     c = eCursor_standard;
  3359     break;
  3360   case NS_STYLE_CURSOR_POINTER:
  3361     c = eCursor_hyperlink;
  3362     break;
  3363   case NS_STYLE_CURSOR_CROSSHAIR:
  3364     c = eCursor_crosshair;
  3365     break;
  3366   case NS_STYLE_CURSOR_MOVE:
  3367     c = eCursor_move;
  3368     break;
  3369   case NS_STYLE_CURSOR_TEXT:
  3370     c = eCursor_select;
  3371     break;
  3372   case NS_STYLE_CURSOR_WAIT:
  3373     c = eCursor_wait;
  3374     break;
  3375   case NS_STYLE_CURSOR_HELP:
  3376     c = eCursor_help;
  3377     break;
  3378   case NS_STYLE_CURSOR_N_RESIZE:
  3379     c = eCursor_n_resize;
  3380     break;
  3381   case NS_STYLE_CURSOR_S_RESIZE:
  3382     c = eCursor_s_resize;
  3383     break;
  3384   case NS_STYLE_CURSOR_W_RESIZE:
  3385     c = eCursor_w_resize;
  3386     break;
  3387   case NS_STYLE_CURSOR_E_RESIZE:
  3388     c = eCursor_e_resize;
  3389     break;
  3390   case NS_STYLE_CURSOR_NW_RESIZE:
  3391     c = eCursor_nw_resize;
  3392     break;
  3393   case NS_STYLE_CURSOR_SE_RESIZE:
  3394     c = eCursor_se_resize;
  3395     break;
  3396   case NS_STYLE_CURSOR_NE_RESIZE:
  3397     c = eCursor_ne_resize;
  3398     break;
  3399   case NS_STYLE_CURSOR_SW_RESIZE:
  3400     c = eCursor_sw_resize;
  3401     break;
  3402   case NS_STYLE_CURSOR_COPY: // CSS3
  3403     c = eCursor_copy;
  3404     break;
  3405   case NS_STYLE_CURSOR_ALIAS:
  3406     c = eCursor_alias;
  3407     break;
  3408   case NS_STYLE_CURSOR_CONTEXT_MENU:
  3409     c = eCursor_context_menu;
  3410     break;
  3411   case NS_STYLE_CURSOR_CELL:
  3412     c = eCursor_cell;
  3413     break;
  3414   case NS_STYLE_CURSOR_GRAB:
  3415     c = eCursor_grab;
  3416     break;
  3417   case NS_STYLE_CURSOR_GRABBING:
  3418     c = eCursor_grabbing;
  3419     break;
  3420   case NS_STYLE_CURSOR_SPINNING:
  3421     c = eCursor_spinning;
  3422     break;
  3423   case NS_STYLE_CURSOR_ZOOM_IN:
  3424     c = eCursor_zoom_in;
  3425     break;
  3426   case NS_STYLE_CURSOR_ZOOM_OUT:
  3427     c = eCursor_zoom_out;
  3428     break;
  3429   case NS_STYLE_CURSOR_NOT_ALLOWED:
  3430     c = eCursor_not_allowed;
  3431     break;
  3432   case NS_STYLE_CURSOR_COL_RESIZE:
  3433     c = eCursor_col_resize;
  3434     break;
  3435   case NS_STYLE_CURSOR_ROW_RESIZE:
  3436     c = eCursor_row_resize;
  3437     break;
  3438   case NS_STYLE_CURSOR_NO_DROP:
  3439     c = eCursor_no_drop;
  3440     break;
  3441   case NS_STYLE_CURSOR_VERTICAL_TEXT:
  3442     c = eCursor_vertical_text;
  3443     break;
  3444   case NS_STYLE_CURSOR_ALL_SCROLL:
  3445     c = eCursor_all_scroll;
  3446     break;
  3447   case NS_STYLE_CURSOR_NESW_RESIZE:
  3448     c = eCursor_nesw_resize;
  3449     break;
  3450   case NS_STYLE_CURSOR_NWSE_RESIZE:
  3451     c = eCursor_nwse_resize;
  3452     break;
  3453   case NS_STYLE_CURSOR_NS_RESIZE:
  3454     c = eCursor_ns_resize;
  3455     break;
  3456   case NS_STYLE_CURSOR_EW_RESIZE:
  3457     c = eCursor_ew_resize;
  3458     break;
  3459   case NS_STYLE_CURSOR_NONE:
  3460     c = eCursor_none;
  3461     break;
  3464   // First, try the imgIContainer, if non-null
  3465   nsresult rv = NS_ERROR_FAILURE;
  3466   if (aContainer) {
  3467     uint32_t hotspotX, hotspotY;
  3469     // css3-ui says to use the CSS-specified hotspot if present,
  3470     // otherwise use the intrinsic hotspot, otherwise use the top left
  3471     // corner.
  3472     if (aHaveHotspot) {
  3473       int32_t imgWidth, imgHeight;
  3474       aContainer->GetWidth(&imgWidth);
  3475       aContainer->GetHeight(&imgHeight);
  3477       // XXX std::max(NS_lround(x), 0)?
  3478       hotspotX = aHotspotX > 0.0f
  3479                    ? uint32_t(aHotspotX + 0.5f) : uint32_t(0);
  3480       if (hotspotX >= uint32_t(imgWidth))
  3481         hotspotX = imgWidth - 1;
  3482       hotspotY = aHotspotY > 0.0f
  3483                    ? uint32_t(aHotspotY + 0.5f) : uint32_t(0);
  3484       if (hotspotY >= uint32_t(imgHeight))
  3485         hotspotY = imgHeight - 1;
  3486     } else {
  3487       hotspotX = 0;
  3488       hotspotY = 0;
  3489       nsCOMPtr<nsIProperties> props(do_QueryInterface(aContainer));
  3490       if (props) {
  3491         nsCOMPtr<nsISupportsPRUint32> hotspotXWrap, hotspotYWrap;
  3493         props->Get("hotspotX", NS_GET_IID(nsISupportsPRUint32), getter_AddRefs(hotspotXWrap));
  3494         props->Get("hotspotY", NS_GET_IID(nsISupportsPRUint32), getter_AddRefs(hotspotYWrap));
  3496         if (hotspotXWrap)
  3497           hotspotXWrap->GetData(&hotspotX);
  3498         if (hotspotYWrap)
  3499           hotspotYWrap->GetData(&hotspotY);
  3503     rv = aWidget->SetCursor(aContainer, hotspotX, hotspotY);
  3506   if (NS_FAILED(rv))
  3507     aWidget->SetCursor(c);
  3509   return NS_OK;
  3512 class MOZ_STACK_CLASS ESMEventCB : public EventDispatchingCallback
  3514 public:
  3515   ESMEventCB(nsIContent* aTarget) : mTarget(aTarget) {}
  3517   virtual void HandleEvent(EventChainPostVisitor& aVisitor)
  3519     if (aVisitor.mPresContext) {
  3520       nsIFrame* frame = aVisitor.mPresContext->GetPrimaryFrameFor(mTarget);
  3521       if (frame) {
  3522         frame->HandleEvent(aVisitor.mPresContext,
  3523                            aVisitor.mEvent->AsGUIEvent(),
  3524                            &aVisitor.mEventStatus);
  3529   nsCOMPtr<nsIContent> mTarget;
  3530 };
  3532 /*static*/ bool
  3533 EventStateManager::IsHandlingUserInput()
  3535   if (sUserInputEventDepth <= 0) {
  3536     return false;
  3539   TimeDuration timeout = nsContentUtils::HandlingUserInputTimeout();
  3540   return timeout <= TimeDuration(0) ||
  3541          (TimeStamp::Now() - sHandlingInputStart) <= timeout;
  3544 nsIFrame*
  3545 EventStateManager::DispatchMouseOrPointerEvent(WidgetMouseEvent* aMouseEvent,
  3546                                                uint32_t aMessage,
  3547                                                nsIContent* aTargetContent,
  3548                                                nsIContent* aRelatedContent)
  3550   // http://dvcs.w3.org/hg/webevents/raw-file/default/mouse-lock.html#methods
  3551   // "[When the mouse is locked on an element...e]vents that require the concept
  3552   // of a mouse cursor must not be dispatched (for example: mouseover, mouseout).
  3553   if (sIsPointerLocked &&
  3554       (aMessage == NS_MOUSELEAVE ||
  3555        aMessage == NS_MOUSEENTER ||
  3556        aMessage == NS_MOUSE_ENTER_SYNTH ||
  3557        aMessage == NS_MOUSE_EXIT_SYNTH)) {
  3558     mCurrentTargetContent = nullptr;
  3559     nsCOMPtr<Element> pointerLockedElement =
  3560       do_QueryReferent(EventStateManager::sPointerLockedElement);
  3561     if (!pointerLockedElement) {
  3562       NS_WARNING("Should have pointer locked element, but didn't.");
  3563       return nullptr;
  3565     nsCOMPtr<nsIContent> content = do_QueryInterface(pointerLockedElement);
  3566     return mPresContext->GetPrimaryFrameFor(content);
  3569   nsEventStatus status = nsEventStatus_eIgnore;
  3570   nsAutoPtr<WidgetMouseEvent> event;
  3571   WidgetPointerEvent* sourcePointer = aMouseEvent->AsPointerEvent();
  3572   if (sourcePointer) {
  3573     PROFILER_LABEL("Input", "DispatchPointerEvent");
  3574     nsAutoPtr<WidgetPointerEvent> newPointerEvent;
  3575     newPointerEvent =
  3576       new WidgetPointerEvent(aMouseEvent->mFlags.mIsTrusted, aMessage,
  3577                              aMouseEvent->widget);
  3578     newPointerEvent->isPrimary = sourcePointer->isPrimary;
  3579     newPointerEvent->pointerId = sourcePointer->pointerId;
  3580     newPointerEvent->width = sourcePointer->width;
  3581     newPointerEvent->height = sourcePointer->height;
  3582     newPointerEvent->inputSource = sourcePointer->inputSource;
  3583     newPointerEvent->relatedTarget = nsIPresShell::GetPointerCapturingContent(sourcePointer->pointerId)
  3584                                        ? nullptr
  3585                                        : aRelatedContent;
  3586     event = newPointerEvent.forget();
  3587   } else {
  3588     PROFILER_LABEL("Input", "DispatchMouseEvent");
  3589     event =
  3590       new WidgetMouseEvent(aMouseEvent->mFlags.mIsTrusted, aMessage,
  3591                            aMouseEvent->widget, WidgetMouseEvent::eReal);
  3592     event->relatedTarget = aRelatedContent;
  3594   event->refPoint = aMouseEvent->refPoint;
  3595   event->modifiers = aMouseEvent->modifiers;
  3596   event->button = aMouseEvent->button;
  3597   event->buttons = aMouseEvent->buttons;
  3598   event->pressure = aMouseEvent->pressure;
  3599   event->pluginEvent = aMouseEvent->pluginEvent;
  3600   event->inputSource = aMouseEvent->inputSource;
  3602   nsWeakFrame previousTarget = mCurrentTarget;
  3604   mCurrentTargetContent = aTargetContent;
  3606   nsIFrame* targetFrame = nullptr;
  3607   if (aTargetContent) {
  3608     ESMEventCB callback(aTargetContent);
  3609     EventDispatcher::Dispatch(aTargetContent, mPresContext, event, nullptr,
  3610                               &status, &callback);
  3612     // Although the primary frame was checked in event callback, 
  3613     // it may not be the same object after event dispatching and handling.
  3614     // So we need to refetch it.
  3615     if (mPresContext) {
  3616       targetFrame = mPresContext->GetPrimaryFrameFor(aTargetContent);
  3620   mCurrentTargetContent = nullptr;
  3621   mCurrentTarget = previousTarget;
  3623   return targetFrame;
  3626 class EnterLeaveDispatcher
  3628 public:
  3629   EnterLeaveDispatcher(EventStateManager* aESM,
  3630                        nsIContent* aTarget, nsIContent* aRelatedTarget,
  3631                        WidgetMouseEvent* aMouseEvent, uint32_t aType)
  3632     : mESM(aESM)
  3633     , mMouseEvent(aMouseEvent)
  3634     , mType(aType)
  3636     nsPIDOMWindow* win =
  3637       aTarget ? aTarget->OwnerDoc()->GetInnerWindow() : nullptr;
  3638     if (aMouseEvent->AsPointerEvent() ? win && win->HasPointerEnterLeaveEventListeners() :
  3639                                         win && win->HasMouseEnterLeaveEventListeners()) {
  3640       mRelatedTarget = aRelatedTarget ?
  3641         aRelatedTarget->FindFirstNonChromeOnlyAccessContent() : nullptr;
  3642       nsINode* commonParent = nullptr;
  3643       if (aTarget && aRelatedTarget) {
  3644         commonParent =
  3645           nsContentUtils::GetCommonAncestor(aTarget, aRelatedTarget);
  3647       nsIContent* current = aTarget;
  3648       // Note, it is ok if commonParent is null!
  3649       while (current && current != commonParent) {
  3650         if (!current->ChromeOnlyAccess()) {
  3651           mTargets.AppendObject(current);
  3653         // mouseenter/leave is fired only on elements.
  3654         current = current->GetParent();
  3659   ~EnterLeaveDispatcher()
  3661     if (mType == NS_MOUSEENTER ||
  3662         mType == NS_POINTER_ENTER) {
  3663       for (int32_t i = mTargets.Count() - 1; i >= 0; --i) {
  3664         mESM->DispatchMouseOrPointerEvent(mMouseEvent, mType, mTargets[i],
  3665                                           mRelatedTarget);
  3667     } else {
  3668       for (int32_t i = 0; i < mTargets.Count(); ++i) {
  3669         mESM->DispatchMouseOrPointerEvent(mMouseEvent, mType, mTargets[i],
  3670                                           mRelatedTarget);
  3675   EventStateManager* mESM;
  3676   nsCOMArray<nsIContent> mTargets;
  3677   nsCOMPtr<nsIContent> mRelatedTarget;
  3678   WidgetMouseEvent* mMouseEvent;
  3679   uint32_t mType;
  3680 };
  3682 void
  3683 EventStateManager::NotifyMouseOut(WidgetMouseEvent* aMouseEvent,
  3684                                   nsIContent* aMovingInto)
  3686   OverOutElementsWrapper* wrapper = GetWrapperByEventID(aMouseEvent);
  3688   if (!wrapper->mLastOverElement)
  3689     return;
  3690   // Before firing mouseout, check for recursion
  3691   if (wrapper->mLastOverElement == wrapper->mFirstOutEventElement)
  3692     return;
  3694   if (wrapper->mLastOverFrame) {
  3695     // if the frame is associated with a subdocument,
  3696     // tell the subdocument that we're moving out of it
  3697     nsSubDocumentFrame* subdocFrame = do_QueryFrame(wrapper->mLastOverFrame.GetFrame());
  3698     if (subdocFrame) {
  3699       nsCOMPtr<nsIDocShell> docshell;
  3700       subdocFrame->GetDocShell(getter_AddRefs(docshell));
  3701       if (docshell) {
  3702         nsRefPtr<nsPresContext> presContext;
  3703         docshell->GetPresContext(getter_AddRefs(presContext));
  3705         if (presContext) {
  3706           EventStateManager* kidESM = presContext->EventStateManager();
  3707           // Not moving into any element in this subdocument
  3708           kidESM->NotifyMouseOut(aMouseEvent, nullptr);
  3713   // That could have caused DOM events which could wreak havoc. Reverify
  3714   // things and be careful.
  3715   if (!wrapper->mLastOverElement)
  3716     return;
  3718   // Store the first mouseOut event we fire and don't refire mouseOut
  3719   // to that element while the first mouseOut is still ongoing.
  3720   wrapper->mFirstOutEventElement = wrapper->mLastOverElement;
  3722   // Don't touch hover state if aMovingInto is non-null.  Caller will update
  3723   // hover state itself, and we have optimizations for hover switching between
  3724   // two nearby elements both deep in the DOM tree that would be defeated by
  3725   // switching the hover state to null here.
  3726   bool isPointer = aMouseEvent->eventStructType == NS_POINTER_EVENT;
  3727   if (!aMovingInto && !isPointer) {
  3728     // Unset :hover
  3729     SetContentState(nullptr, NS_EVENT_STATE_HOVER);
  3732   EnterLeaveDispatcher leaveDispatcher(this, wrapper->mLastOverElement,
  3733                                        aMovingInto, aMouseEvent,
  3734                                        isPointer ? NS_POINTER_LEAVE :
  3735                                                    NS_MOUSELEAVE);
  3737   // Fire mouseout
  3738   DispatchMouseOrPointerEvent(aMouseEvent, isPointer ? NS_POINTER_OUT : NS_MOUSE_EXIT_SYNTH,
  3739                               wrapper->mLastOverElement, aMovingInto);
  3741   wrapper->mLastOverFrame = nullptr;
  3742   wrapper->mLastOverElement = nullptr;
  3744   // Turn recursion protection back off
  3745   wrapper->mFirstOutEventElement = nullptr;
  3748 void
  3749 EventStateManager::NotifyMouseOver(WidgetMouseEvent* aMouseEvent,
  3750                                    nsIContent* aContent)
  3752   NS_ASSERTION(aContent, "Mouse must be over something");
  3754   OverOutElementsWrapper* wrapper = GetWrapperByEventID(aMouseEvent);
  3756   if (wrapper->mLastOverElement == aContent)
  3757     return;
  3759   // Before firing mouseover, check for recursion
  3760   if (aContent == wrapper->mFirstOverEventElement)
  3761     return;
  3763   // Check to see if we're a subdocument and if so update the parent
  3764   // document's ESM state to indicate that the mouse is over the
  3765   // content associated with our subdocument.
  3766   EnsureDocument(mPresContext);
  3767   nsIDocument *parentDoc = mDocument->GetParentDocument();
  3768   if (parentDoc) {
  3769     nsIContent *docContent = parentDoc->FindContentForSubDocument(mDocument);
  3770     if (docContent) {
  3771       nsIPresShell *parentShell = parentDoc->GetShell();
  3772       if (parentShell) {
  3773         EventStateManager* parentESM =
  3774           parentShell->GetPresContext()->EventStateManager();
  3775         parentESM->NotifyMouseOver(aMouseEvent, docContent);
  3779   // Firing the DOM event in the parent document could cause all kinds
  3780   // of havoc.  Reverify and take care.
  3781   if (wrapper->mLastOverElement == aContent)
  3782     return;
  3784   // Remember mLastOverElement as the related content for the
  3785   // DispatchMouseOrPointerEvent() call below, since NotifyMouseOut() resets it, bug 298477.
  3786   nsCOMPtr<nsIContent> lastOverElement = wrapper->mLastOverElement;
  3788   bool isPointer = aMouseEvent->eventStructType == NS_POINTER_EVENT;
  3789   EnterLeaveDispatcher enterDispatcher(this, aContent, lastOverElement,
  3790                                        aMouseEvent,
  3791                                        isPointer ? NS_POINTER_ENTER :
  3792                                                    NS_MOUSEENTER);
  3794   NotifyMouseOut(aMouseEvent, aContent);
  3796   // Store the first mouseOver event we fire and don't refire mouseOver
  3797   // to that element while the first mouseOver is still ongoing.
  3798   wrapper->mFirstOverEventElement = aContent;
  3800   if (!isPointer) {
  3801     SetContentState(aContent, NS_EVENT_STATE_HOVER);
  3804   // Fire mouseover
  3805   wrapper->mLastOverFrame =
  3806     DispatchMouseOrPointerEvent(aMouseEvent,
  3807                                 isPointer ? NS_POINTER_OVER :
  3808                                             NS_MOUSE_ENTER_SYNTH,
  3809                                 aContent, lastOverElement);
  3810   wrapper->mLastOverElement = aContent;
  3812   // Turn recursion protection back off
  3813   wrapper->mFirstOverEventElement = nullptr;
  3816 // Returns the center point of the window's inner content area.
  3817 // This is in widget coordinates, i.e. relative to the widget's top
  3818 // left corner, not in screen coordinates, the same units that
  3819 // UIEvent::refPoint is in.
  3820 //
  3821 // XXX Hack alert: XXX
  3822 // However, we do the computation in integer CSS pixels, NOT device pix,
  3823 // in order to fudge around the one-pixel error in innerHeight in fullscreen
  3824 // mode (see bug 799523 comment 35, and bug 729011). Using integer CSS pix
  3825 // makes us throw away the fractional error that results, rather than having
  3826 // it manifest as a potential one-device-pix discrepancy.
  3827 static LayoutDeviceIntPoint
  3828 GetWindowInnerRectCenter(nsPIDOMWindow* aWindow,
  3829                          nsIWidget* aWidget,
  3830                          nsPresContext* aContext)
  3832   NS_ENSURE_TRUE(aWindow && aWidget && aContext, LayoutDeviceIntPoint(0, 0));
  3834   float cssInnerX = 0.0;
  3835   aWindow->GetMozInnerScreenX(&cssInnerX);
  3836   int32_t innerX = int32_t(NS_round(cssInnerX));
  3838   float cssInnerY = 0.0;
  3839   aWindow->GetMozInnerScreenY(&cssInnerY);
  3840   int32_t innerY = int32_t(NS_round(cssInnerY));
  3842   int32_t innerWidth = 0;
  3843   aWindow->GetInnerWidth(&innerWidth);
  3845   int32_t innerHeight = 0;
  3846   aWindow->GetInnerHeight(&innerHeight);
  3848   nsIntRect screen;
  3849   aWidget->GetScreenBounds(screen);
  3851   int32_t cssScreenX = aContext->DevPixelsToIntCSSPixels(screen.x);
  3852   int32_t cssScreenY = aContext->DevPixelsToIntCSSPixels(screen.y);
  3854   return LayoutDeviceIntPoint(
  3855     aContext->CSSPixelsToDevPixels(innerX - cssScreenX + innerWidth / 2),
  3856     aContext->CSSPixelsToDevPixels(innerY - cssScreenY + innerHeight / 2));
  3859 void
  3860 EventStateManager::GenerateMouseEnterExit(WidgetMouseEvent* aMouseEvent)
  3862   EnsureDocument(mPresContext);
  3863   if (!mDocument)
  3864     return;
  3866   // Hold onto old target content through the event and reset after.
  3867   nsCOMPtr<nsIContent> targetBeforeEvent = mCurrentTargetContent;
  3869   switch(aMouseEvent->message) {
  3870   case NS_MOUSE_MOVE:
  3872       // Mouse movement is reported on the MouseEvent.movement{X,Y} fields.
  3873       // Movement is calculated in UIEvent::GetMovementPoint() as:
  3874       //   previous_mousemove_refPoint - current_mousemove_refPoint.
  3875       if (sIsPointerLocked && aMouseEvent->widget) {
  3876         // The pointer is locked. If the pointer is not located at the center of
  3877         // the window, dispatch a synthetic mousemove to return the pointer there.
  3878         // Doing this between "real" pointer moves gives the impression that the
  3879         // (locked) pointer can continue moving and won't stop at the screen
  3880         // boundary. We cancel the synthetic event so that we don't end up
  3881         // dispatching the centering move event to content.
  3882         LayoutDeviceIntPoint center =
  3883           GetWindowInnerRectCenter(mDocument->GetWindow(), aMouseEvent->widget,
  3884                                    mPresContext);
  3885         aMouseEvent->lastRefPoint = center;
  3886         if (aMouseEvent->refPoint != center) {
  3887           // Mouse move doesn't finish at the center of the window. Dispatch a
  3888           // synthetic native mouse event to move the pointer back to the center
  3889           // of the window, to faciliate more movement. But first, record that
  3890           // we've dispatched a synthetic mouse movement, so we can cancel it
  3891           // in the other branch here.
  3892           sSynthCenteringPoint = center;
  3893           aMouseEvent->widget->SynthesizeNativeMouseMove(
  3894             LayoutDeviceIntPoint::ToUntyped(center) +
  3895               aMouseEvent->widget->WidgetToScreenOffset());
  3896         } else if (aMouseEvent->refPoint == sSynthCenteringPoint) {
  3897           // This is the "synthetic native" event we dispatched to re-center the
  3898           // pointer. Cancel it so we don't expose the centering move to content.
  3899           aMouseEvent->mFlags.mPropagationStopped = true;
  3900           // Clear sSynthCenteringPoint so we don't cancel other events
  3901           // targeted at the center.
  3902           sSynthCenteringPoint = kInvalidRefPoint;
  3904       } else if (sLastRefPoint == kInvalidRefPoint) {
  3905         // We don't have a valid previous mousemove refPoint. This is either
  3906         // the first move we've encountered, or the mouse has just re-entered
  3907         // the application window. We should report (0,0) movement for this
  3908         // case, so make the current and previous refPoints the same.
  3909         aMouseEvent->lastRefPoint = aMouseEvent->refPoint;
  3910       } else {
  3911         aMouseEvent->lastRefPoint = sLastRefPoint;
  3914       // Update the last known refPoint with the current refPoint.
  3915       sLastRefPoint = aMouseEvent->refPoint;
  3918   case NS_POINTER_MOVE:
  3919   case NS_POINTER_DOWN:
  3921       // Get the target content target (mousemove target == mouseover target)
  3922       nsCOMPtr<nsIContent> targetElement = GetEventTargetContent(aMouseEvent);
  3923       if (!targetElement) {
  3924         // We're always over the document root, even if we're only
  3925         // over dead space in a page (whose frame is not associated with
  3926         // any content) or in print preview dead space
  3927         targetElement = mDocument->GetRootElement();
  3929       if (targetElement) {
  3930         NotifyMouseOver(aMouseEvent, targetElement);
  3933     break;
  3934   case NS_POINTER_UP:
  3936       // Get the target content target (mousemove target == mouseover target)
  3937       nsCOMPtr<nsIContent> targetElement = GetEventTargetContent(aMouseEvent);
  3938       if (!targetElement) {
  3939         // We're always over the document root, even if we're only
  3940         // over dead space in a page (whose frame is not associated with
  3941         // any content) or in print preview dead space
  3942         targetElement = mDocument->GetRootElement();
  3944       if (targetElement) {
  3945         OverOutElementsWrapper* helper = GetWrapperByEventID(aMouseEvent);
  3946         if (helper) {
  3947           helper->mLastOverElement = targetElement;
  3949         NotifyMouseOut(aMouseEvent, nullptr);
  3952     break;
  3953   case NS_POINTER_LEAVE:
  3954   case NS_POINTER_CANCEL:
  3955   case NS_MOUSE_EXIT:
  3957       // This is actually the window mouse exit or pointer leave event. We're not moving
  3958       // into any new element.
  3960       OverOutElementsWrapper* helper = GetWrapperByEventID(aMouseEvent);
  3961       if (helper->mLastOverFrame &&
  3962           nsContentUtils::GetTopLevelWidget(aMouseEvent->widget) !=
  3963           nsContentUtils::GetTopLevelWidget(helper->mLastOverFrame->GetNearestWidget())) {
  3964         // the Mouse/PointerOut event widget doesn't have same top widget with
  3965         // mLastOverFrame, it's a spurious event for mLastOverFrame
  3966         break;
  3969       // Reset sLastRefPoint, so that we'll know not to report any
  3970       // movement the next time we re-enter the window.
  3971       sLastRefPoint = kInvalidRefPoint;
  3973       NotifyMouseOut(aMouseEvent, nullptr);
  3975     break;
  3978   // reset mCurretTargetContent to what it was
  3979   mCurrentTargetContent = targetBeforeEvent;
  3982 OverOutElementsWrapper*
  3983 EventStateManager::GetWrapperByEventID(WidgetMouseEvent* aEvent)
  3985   WidgetPointerEvent* pointer = aEvent->AsPointerEvent();
  3986   if (!pointer) {
  3987     MOZ_ASSERT(aEvent->AsMouseEvent() != nullptr);
  3988     if (!mMouseEnterLeaveHelper) {
  3989       mMouseEnterLeaveHelper = new OverOutElementsWrapper();
  3991     return mMouseEnterLeaveHelper;
  3993   nsRefPtr<OverOutElementsWrapper> helper;
  3994   if (!mPointersEnterLeaveHelper.Get(pointer->pointerId, getter_AddRefs(helper))) {
  3995     helper = new OverOutElementsWrapper();
  3996     mPointersEnterLeaveHelper.Put(pointer->pointerId, helper);
  3999   return helper;
  4002 void
  4003 EventStateManager::SetPointerLock(nsIWidget* aWidget,
  4004                                   nsIContent* aElement)
  4006   // NOTE: aElement will be nullptr when unlocking.
  4007   sIsPointerLocked = !!aElement;
  4009   if (!aWidget) {
  4010     return;
  4013   // Reset mouse wheel transaction
  4014   WheelTransaction::EndTransaction();
  4016   // Deal with DnD events
  4017   nsCOMPtr<nsIDragService> dragService =
  4018     do_GetService("@mozilla.org/widget/dragservice;1");
  4020   if (sIsPointerLocked) {
  4021     // Store the last known ref point so we can reposition the pointer after unlock.
  4022     mPreLockPoint = sLastRefPoint;
  4024     // Fire a synthetic mouse move to ensure event state is updated. We first
  4025     // set the mouse to the center of the window, so that the mouse event
  4026     // doesn't report any movement.
  4027     sLastRefPoint = GetWindowInnerRectCenter(aElement->OwnerDoc()->GetWindow(),
  4028                                              aWidget,
  4029                                              mPresContext);
  4030     aWidget->SynthesizeNativeMouseMove(
  4031       LayoutDeviceIntPoint::ToUntyped(sLastRefPoint) + aWidget->WidgetToScreenOffset());
  4033     // Retarget all events to this element via capture.
  4034     nsIPresShell::SetCapturingContent(aElement, CAPTURE_POINTERLOCK);
  4036     // Suppress DnD
  4037     if (dragService) {
  4038       dragService->Suppress();
  4040   } else {
  4041     // Unlocking, so return pointer to the original position by firing a
  4042     // synthetic mouse event. We first reset sLastRefPoint to its
  4043     // pre-pointerlock position, so that the synthetic mouse event reports
  4044     // no movement.
  4045     sLastRefPoint = mPreLockPoint;
  4046     aWidget->SynthesizeNativeMouseMove(
  4047       LayoutDeviceIntPoint::ToUntyped(mPreLockPoint) + aWidget->WidgetToScreenOffset());
  4049     // Don't retarget events to this element any more.
  4050     nsIPresShell::SetCapturingContent(nullptr, CAPTURE_POINTERLOCK);
  4052     // Unsuppress DnD
  4053     if (dragService) {
  4054       dragService->Unsuppress();
  4059 void
  4060 EventStateManager::GenerateDragDropEnterExit(nsPresContext* aPresContext,
  4061                                              WidgetDragEvent* aDragEvent)
  4063   //Hold onto old target content through the event and reset after.
  4064   nsCOMPtr<nsIContent> targetBeforeEvent = mCurrentTargetContent;
  4066   switch(aDragEvent->message) {
  4067   case NS_DRAGDROP_OVER:
  4069       // when dragging from one frame to another, events are fired in the
  4070       // order: dragexit, dragenter, dragleave
  4071       if (sLastDragOverFrame != mCurrentTarget) {
  4072         //We'll need the content, too, to check if it changed separately from the frames.
  4073         nsCOMPtr<nsIContent> lastContent;
  4074         nsCOMPtr<nsIContent> targetContent;
  4075         mCurrentTarget->GetContentForEvent(aDragEvent,
  4076                                            getter_AddRefs(targetContent));
  4078         if (sLastDragOverFrame) {
  4079           //The frame has changed but the content may not have. Check before dispatching to content
  4080           sLastDragOverFrame->GetContentForEvent(aDragEvent,
  4081                                                  getter_AddRefs(lastContent));
  4083           FireDragEnterOrExit(sLastDragOverFrame->PresContext(),
  4084                               aDragEvent, NS_DRAGDROP_EXIT_SYNTH,
  4085                               targetContent, lastContent, sLastDragOverFrame);
  4088         FireDragEnterOrExit(aPresContext, aDragEvent, NS_DRAGDROP_ENTER,
  4089                             lastContent, targetContent, mCurrentTarget);
  4091         if (sLastDragOverFrame) {
  4092           FireDragEnterOrExit(sLastDragOverFrame->PresContext(),
  4093                               aDragEvent, NS_DRAGDROP_LEAVE_SYNTH,
  4094                               targetContent, lastContent, sLastDragOverFrame);
  4097         sLastDragOverFrame = mCurrentTarget;
  4100     break;
  4102   case NS_DRAGDROP_EXIT:
  4104       //This is actually the window mouse exit event.
  4105       if (sLastDragOverFrame) {
  4106         nsCOMPtr<nsIContent> lastContent;
  4107         sLastDragOverFrame->GetContentForEvent(aDragEvent,
  4108                                                getter_AddRefs(lastContent));
  4110         nsRefPtr<nsPresContext> lastDragOverFramePresContext = sLastDragOverFrame->PresContext();
  4111         FireDragEnterOrExit(lastDragOverFramePresContext,
  4112                             aDragEvent, NS_DRAGDROP_EXIT_SYNTH,
  4113                             nullptr, lastContent, sLastDragOverFrame);
  4114         FireDragEnterOrExit(lastDragOverFramePresContext,
  4115                             aDragEvent, NS_DRAGDROP_LEAVE_SYNTH,
  4116                             nullptr, lastContent, sLastDragOverFrame);
  4118         sLastDragOverFrame = nullptr;
  4121     break;
  4124   //reset mCurretTargetContent to what it was
  4125   mCurrentTargetContent = targetBeforeEvent;
  4127   // Now flush all pending notifications, for better responsiveness.
  4128   FlushPendingEvents(aPresContext);
  4131 void
  4132 EventStateManager::FireDragEnterOrExit(nsPresContext* aPresContext,
  4133                                        WidgetDragEvent* aDragEvent,
  4134                                        uint32_t aMsg,
  4135                                        nsIContent* aRelatedTarget,
  4136                                        nsIContent* aTargetContent,
  4137                                        nsWeakFrame& aTargetFrame)
  4139   nsEventStatus status = nsEventStatus_eIgnore;
  4140   WidgetDragEvent event(aDragEvent->mFlags.mIsTrusted, aMsg,
  4141                         aDragEvent->widget);
  4142   event.refPoint = aDragEvent->refPoint;
  4143   event.modifiers = aDragEvent->modifiers;
  4144   event.buttons = aDragEvent->buttons;
  4145   event.relatedTarget = aRelatedTarget;
  4146   event.inputSource = aDragEvent->inputSource;
  4148   mCurrentTargetContent = aTargetContent;
  4150   if (aTargetContent != aRelatedTarget) {
  4151     //XXX This event should still go somewhere!!
  4152     if (aTargetContent) {
  4153       EventDispatcher::Dispatch(aTargetContent, aPresContext, &event,
  4154                                 nullptr, &status);
  4157     // adjust the drag hover if the dragenter event was cancelled or this is a drag exit
  4158     if (status == nsEventStatus_eConsumeNoDefault || aMsg == NS_DRAGDROP_EXIT)
  4159       SetContentState((aMsg == NS_DRAGDROP_ENTER) ? aTargetContent : nullptr,
  4160                       NS_EVENT_STATE_DRAGOVER);
  4162     // collect any changes to moz cursor settings stored in the event's
  4163     // data transfer.
  4164     if (aMsg == NS_DRAGDROP_LEAVE_SYNTH || aMsg == NS_DRAGDROP_EXIT_SYNTH ||
  4165         aMsg == NS_DRAGDROP_ENTER)
  4166       UpdateDragDataTransfer(&event);
  4169   // Finally dispatch the event to the frame
  4170   if (aTargetFrame)
  4171     aTargetFrame->HandleEvent(aPresContext, &event, &status);
  4174 void
  4175 EventStateManager::UpdateDragDataTransfer(WidgetDragEvent* dragEvent)
  4177   NS_ASSERTION(dragEvent, "drag event is null in UpdateDragDataTransfer!");
  4178   if (!dragEvent->dataTransfer)
  4179     return;
  4181   nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
  4183   if (dragSession) {
  4184     // the initial dataTransfer is the one from the dragstart event that
  4185     // was set on the dragSession when the drag began.
  4186     nsCOMPtr<nsIDOMDataTransfer> initialDataTransfer;
  4187     dragSession->GetDataTransfer(getter_AddRefs(initialDataTransfer));
  4188     if (initialDataTransfer) {
  4189       // retrieve the current moz cursor setting and save it.
  4190       nsAutoString mozCursor;
  4191       dragEvent->dataTransfer->GetMozCursor(mozCursor);
  4192       initialDataTransfer->SetMozCursor(mozCursor);
  4197 nsresult
  4198 EventStateManager::SetClickCount(nsPresContext* aPresContext,
  4199                                  WidgetMouseEvent* aEvent,
  4200                                  nsEventStatus* aStatus)
  4202   nsCOMPtr<nsIContent> mouseContent;
  4203   nsIContent* mouseContentParent = nullptr;
  4204   mCurrentTarget->GetContentForEvent(aEvent, getter_AddRefs(mouseContent));
  4205   if (mouseContent) {
  4206     if (mouseContent->IsNodeOfType(nsINode::eTEXT)) {
  4207       mouseContent = mouseContent->GetParent();
  4209     if (mouseContent && mouseContent->IsRootOfNativeAnonymousSubtree()) {
  4210       mouseContentParent = mouseContent->GetParent();
  4214   switch (aEvent->button) {
  4215   case WidgetMouseEvent::eLeftButton:
  4216     if (aEvent->message == NS_MOUSE_BUTTON_DOWN) {
  4217       mLastLeftMouseDownContent = mouseContent;
  4218       mLastLeftMouseDownContentParent = mouseContentParent;
  4219     } else if (aEvent->message == NS_MOUSE_BUTTON_UP) {
  4220       if (mLastLeftMouseDownContent == mouseContent ||
  4221           mLastLeftMouseDownContentParent == mouseContent ||
  4222           mLastLeftMouseDownContent == mouseContentParent) {
  4223         aEvent->clickCount = mLClickCount;
  4224         mLClickCount = 0;
  4225       } else {
  4226         aEvent->clickCount = 0;
  4228       mLastLeftMouseDownContent = nullptr;
  4229       mLastLeftMouseDownContentParent = nullptr;
  4231     break;
  4233   case WidgetMouseEvent::eMiddleButton:
  4234     if (aEvent->message == NS_MOUSE_BUTTON_DOWN) {
  4235       mLastMiddleMouseDownContent = mouseContent;
  4236       mLastMiddleMouseDownContentParent = mouseContentParent;
  4237     } else if (aEvent->message == NS_MOUSE_BUTTON_UP) {
  4238       if (mLastMiddleMouseDownContent == mouseContent ||
  4239           mLastMiddleMouseDownContentParent == mouseContent ||
  4240           mLastMiddleMouseDownContent == mouseContentParent) {
  4241         aEvent->clickCount = mMClickCount;
  4242         mMClickCount = 0;
  4243       } else {
  4244         aEvent->clickCount = 0;
  4246       mLastMiddleMouseDownContent = nullptr;
  4247       mLastMiddleMouseDownContentParent = nullptr;
  4249     break;
  4251   case WidgetMouseEvent::eRightButton:
  4252     if (aEvent->message == NS_MOUSE_BUTTON_DOWN) {
  4253       mLastRightMouseDownContent = mouseContent;
  4254       mLastRightMouseDownContentParent = mouseContentParent;
  4255     } else if (aEvent->message == NS_MOUSE_BUTTON_UP) {
  4256       if (mLastRightMouseDownContent == mouseContent ||
  4257           mLastRightMouseDownContentParent == mouseContent ||
  4258           mLastRightMouseDownContent == mouseContentParent) {
  4259         aEvent->clickCount = mRClickCount;
  4260         mRClickCount = 0;
  4261       } else {
  4262         aEvent->clickCount = 0;
  4264       mLastRightMouseDownContent = nullptr;
  4265       mLastRightMouseDownContentParent = nullptr;
  4267     break;
  4270   return NS_OK;
  4273 nsresult
  4274 EventStateManager::CheckForAndDispatchClick(nsPresContext* aPresContext,
  4275                                             WidgetMouseEvent* aEvent,
  4276                                             nsEventStatus* aStatus)
  4278   nsresult ret = NS_OK;
  4280   //If mouse is still over same element, clickcount will be > 1.
  4281   //If it has moved it will be zero, so no click.
  4282   if (0 != aEvent->clickCount) {
  4283     //Check that the window isn't disabled before firing a click
  4284     //(see bug 366544).
  4285     if (aEvent->widget && !aEvent->widget->IsEnabled()) {
  4286       return ret;
  4288     //fire click
  4289     bool notDispatchToContents =
  4290      (aEvent->button == WidgetMouseEvent::eMiddleButton ||
  4291       aEvent->button == WidgetMouseEvent::eRightButton);
  4293     WidgetMouseEvent event(aEvent->mFlags.mIsTrusted, NS_MOUSE_CLICK,
  4294                            aEvent->widget, WidgetMouseEvent::eReal);
  4295     event.refPoint = aEvent->refPoint;
  4296     event.clickCount = aEvent->clickCount;
  4297     event.modifiers = aEvent->modifiers;
  4298     event.buttons = aEvent->buttons;
  4299     event.time = aEvent->time;
  4300     event.mFlags.mNoContentDispatch = notDispatchToContents;
  4301     event.button = aEvent->button;
  4302     event.inputSource = aEvent->inputSource;
  4304     nsCOMPtr<nsIPresShell> presShell = mPresContext->GetPresShell();
  4305     if (presShell) {
  4306       nsCOMPtr<nsIContent> mouseContent = GetEventTargetContent(aEvent);
  4307       if (!mouseContent && !mCurrentTarget) {
  4308         return NS_OK;
  4311       // HandleEvent clears out mCurrentTarget which we might need again
  4312       nsWeakFrame currentTarget = mCurrentTarget;
  4313       ret = presShell->HandleEventWithTarget(&event, currentTarget,
  4314                                              mouseContent, aStatus);
  4315       if (NS_SUCCEEDED(ret) && aEvent->clickCount == 2) {
  4316         //fire double click
  4317         WidgetMouseEvent event2(aEvent->mFlags.mIsTrusted, NS_MOUSE_DOUBLECLICK,
  4318                                 aEvent->widget, WidgetMouseEvent::eReal);
  4319         event2.refPoint = aEvent->refPoint;
  4320         event2.clickCount = aEvent->clickCount;
  4321         event2.modifiers = aEvent->modifiers;
  4322         event2.buttons = aEvent->buttons;
  4323         event2.mFlags.mNoContentDispatch = notDispatchToContents;
  4324         event2.button = aEvent->button;
  4325         event2.inputSource = aEvent->inputSource;
  4327         ret = presShell->HandleEventWithTarget(&event2, currentTarget,
  4328                                                mouseContent, aStatus);
  4332   return ret;
  4335 nsIFrame*
  4336 EventStateManager::GetEventTarget()
  4338   nsIPresShell *shell;
  4339   if (mCurrentTarget ||
  4340       !mPresContext ||
  4341       !(shell = mPresContext->GetPresShell())) {
  4342     return mCurrentTarget;
  4345   if (mCurrentTargetContent) {
  4346     mCurrentTarget = mPresContext->GetPrimaryFrameFor(mCurrentTargetContent);
  4347     if (mCurrentTarget) {
  4348       return mCurrentTarget;
  4352   nsIFrame* frame = shell->GetEventTargetFrame();
  4353   return (mCurrentTarget = frame);
  4356 already_AddRefed<nsIContent>
  4357 EventStateManager::GetEventTargetContent(WidgetEvent* aEvent)
  4359   if (aEvent &&
  4360       (aEvent->message == NS_FOCUS_CONTENT ||
  4361        aEvent->message == NS_BLUR_CONTENT)) {
  4362     nsCOMPtr<nsIContent> content = GetFocusedContent();
  4363     return content.forget();
  4366   if (mCurrentTargetContent) {
  4367     nsCOMPtr<nsIContent> content = mCurrentTargetContent;
  4368     return content.forget();
  4371   nsCOMPtr<nsIContent> content;
  4373   nsIPresShell *presShell = mPresContext->GetPresShell();
  4374   if (presShell) {
  4375     content = presShell->GetEventTargetContent(aEvent);
  4378   // Some events here may set mCurrentTarget but not set the corresponding
  4379   // event target in the PresShell.
  4380   if (!content && mCurrentTarget) {
  4381     mCurrentTarget->GetContentForEvent(aEvent, getter_AddRefs(content));
  4384   return content.forget();
  4387 static Element*
  4388 GetLabelTarget(nsIContent* aPossibleLabel)
  4390   mozilla::dom::HTMLLabelElement* label =
  4391     mozilla::dom::HTMLLabelElement::FromContent(aPossibleLabel);
  4392   if (!label)
  4393     return nullptr;
  4395   return label->GetLabeledElement();
  4398 static nsIContent* FindCommonAncestor(nsIContent *aNode1, nsIContent *aNode2)
  4400   // Find closest common ancestor
  4401   if (aNode1 && aNode2) {
  4402     // Find the nearest common ancestor by counting the distance to the
  4403     // root and then walking up again, in pairs.
  4404     int32_t offset = 0;
  4405     nsIContent *anc1 = aNode1;
  4406     for (;;) {
  4407       ++offset;
  4408       nsIContent* parent = anc1->GetParent();
  4409       if (!parent)
  4410         break;
  4411       anc1 = parent;
  4413     nsIContent *anc2 = aNode2;
  4414     for (;;) {
  4415       --offset;
  4416       nsIContent* parent = anc2->GetParent();
  4417       if (!parent)
  4418         break;
  4419       anc2 = parent;
  4421     if (anc1 == anc2) {
  4422       anc1 = aNode1;
  4423       anc2 = aNode2;
  4424       while (offset > 0) {
  4425         anc1 = anc1->GetParent();
  4426         --offset;
  4428       while (offset < 0) {
  4429         anc2 = anc2->GetParent();
  4430         ++offset;
  4432       while (anc1 != anc2) {
  4433         anc1 = anc1->GetParent();
  4434         anc2 = anc2->GetParent();
  4436       return anc1;
  4439   return nullptr;
  4442 static Element*
  4443 GetParentElement(Element* aElement)
  4445   nsIContent* p = aElement->GetParent();
  4446   return (p && p->IsElement()) ? p->AsElement() : nullptr;
  4449 /* static */
  4450 void
  4451 EventStateManager::SetFullScreenState(Element* aElement, bool aIsFullScreen)
  4453   DoStateChange(aElement, NS_EVENT_STATE_FULL_SCREEN, aIsFullScreen);
  4454   Element* ancestor = aElement;
  4455   while ((ancestor = GetParentElement(ancestor))) {
  4456     DoStateChange(ancestor, NS_EVENT_STATE_FULL_SCREEN_ANCESTOR, aIsFullScreen);
  4460 /* static */
  4461 inline void
  4462 EventStateManager::DoStateChange(Element* aElement, EventStates aState,
  4463                                  bool aAddState)
  4465   if (aAddState) {
  4466     aElement->AddStates(aState);
  4467   } else {
  4468     aElement->RemoveStates(aState);
  4472 /* static */
  4473 inline void
  4474 EventStateManager::DoStateChange(nsIContent* aContent, EventStates aState,
  4475                                  bool aStateAdded)
  4477   if (aContent->IsElement()) {
  4478     DoStateChange(aContent->AsElement(), aState, aStateAdded);
  4482 /* static */
  4483 void
  4484 EventStateManager::UpdateAncestorState(nsIContent* aStartNode,
  4485                                        nsIContent* aStopBefore,
  4486                                        EventStates aState,
  4487                                        bool aAddState)
  4489   for (; aStartNode && aStartNode != aStopBefore;
  4490        aStartNode = aStartNode->GetParent()) {
  4491     // We might be starting with a non-element (e.g. a text node) and
  4492     // if someone is doing something weird might be ending with a
  4493     // non-element too (e.g. a document fragment)
  4494     if (!aStartNode->IsElement()) {
  4495       continue;
  4497     Element* element = aStartNode->AsElement();
  4498     DoStateChange(element, aState, aAddState);
  4499     Element* labelTarget = GetLabelTarget(element);
  4500     if (labelTarget) {
  4501       DoStateChange(labelTarget, aState, aAddState);
  4505   if (aAddState) {
  4506     // We might be in a situation where a node was in hover both
  4507     // because it was hovered and because the label for it was
  4508     // hovered, and while we stopped hovering the node the label is
  4509     // still hovered.  Or we might have had two nested labels for the
  4510     // same node, and while one is no longer hovered the other still
  4511     // is.  In that situation, the label that's still hovered will be
  4512     // aStopBefore or some ancestor of it, and the call we just made
  4513     // to UpdateAncestorState with aAddState = false would have
  4514     // removed the hover state from the node.  But the node should
  4515     // still be in hover state.  To handle this situation we need to
  4516     // keep walking up the tree and any time we find a label mark its
  4517     // corresponding node as still in our state.
  4518     for ( ; aStartNode; aStartNode = aStartNode->GetParent()) {
  4519       if (!aStartNode->IsElement()) {
  4520         continue;
  4523       Element* labelTarget = GetLabelTarget(aStartNode->AsElement());
  4524       if (labelTarget && !labelTarget->State().HasState(aState)) {
  4525         DoStateChange(labelTarget, aState, true);
  4531 bool
  4532 EventStateManager::SetContentState(nsIContent* aContent, EventStates aState)
  4534   // We manage 4 states here: ACTIVE, HOVER, DRAGOVER, URLTARGET
  4535   // The input must be exactly one of them.
  4536   NS_PRECONDITION(aState == NS_EVENT_STATE_ACTIVE ||
  4537                   aState == NS_EVENT_STATE_HOVER ||
  4538                   aState == NS_EVENT_STATE_DRAGOVER ||
  4539                   aState == NS_EVENT_STATE_URLTARGET,
  4540                   "Unexpected state");
  4542   nsCOMPtr<nsIContent> notifyContent1;
  4543   nsCOMPtr<nsIContent> notifyContent2;
  4544   bool updateAncestors;
  4546   if (aState == NS_EVENT_STATE_HOVER || aState == NS_EVENT_STATE_ACTIVE) {
  4547     // Hover and active are hierarchical
  4548     updateAncestors = true;
  4550     // check to see that this state is allowed by style. Check dragover too?
  4551     // XXX Is this even what we want?
  4552     if (mCurrentTarget)
  4554       const nsStyleUserInterface* ui = mCurrentTarget->StyleUserInterface();
  4555       if (ui->mUserInput == NS_STYLE_USER_INPUT_NONE)
  4556         return false;
  4559     if (aState == NS_EVENT_STATE_ACTIVE) {
  4560       if (aContent != mActiveContent) {
  4561         notifyContent1 = aContent;
  4562         notifyContent2 = mActiveContent;
  4563         mActiveContent = aContent;
  4565     } else {
  4566       NS_ASSERTION(aState == NS_EVENT_STATE_HOVER, "How did that happen?");
  4567       nsIContent* newHover;
  4569       if (mPresContext->IsDynamic()) {
  4570         newHover = aContent;
  4571       } else {
  4572         NS_ASSERTION(!aContent ||
  4573                      aContent->GetCurrentDoc() == mPresContext->PresShell()->GetDocument(),
  4574                      "Unexpected document");
  4575         nsIFrame *frame = aContent ? aContent->GetPrimaryFrame() : nullptr;
  4576         if (frame && nsLayoutUtils::IsViewportScrollbarFrame(frame)) {
  4577           // The scrollbars of viewport should not ignore the hover state.
  4578           // Because they are *not* the content of the web page.
  4579           newHover = aContent;
  4580         } else {
  4581           // All contents of the web page should ignore the hover state.
  4582           newHover = nullptr;
  4586       if (newHover != mHoverContent) {
  4587         notifyContent1 = newHover;
  4588         notifyContent2 = mHoverContent;
  4589         mHoverContent = newHover;
  4592   } else {
  4593     updateAncestors = false;
  4594     if (aState == NS_EVENT_STATE_DRAGOVER) {
  4595       if (aContent != sDragOverContent) {
  4596         notifyContent1 = aContent;
  4597         notifyContent2 = sDragOverContent;
  4598         sDragOverContent = aContent;
  4600     } else if (aState == NS_EVENT_STATE_URLTARGET) {
  4601       if (aContent != mURLTargetContent) {
  4602         notifyContent1 = aContent;
  4603         notifyContent2 = mURLTargetContent;
  4604         mURLTargetContent = aContent;
  4609   // We need to keep track of which of notifyContent1 and notifyContent2 is
  4610   // getting the state set and which is getting it unset.  If both are
  4611   // non-null, then notifyContent1 is having the state set and notifyContent2
  4612   // is having it unset.  But if one of them is null, we need to keep track of
  4613   // the right thing for notifyContent1 explicitly.
  4614   bool content1StateSet = true;
  4615   if (!notifyContent1) {
  4616     // This is ok because FindCommonAncestor wouldn't find anything
  4617     // anyway if notifyContent1 is null.
  4618     notifyContent1 = notifyContent2;
  4619     notifyContent2 = nullptr;
  4620     content1StateSet = false;
  4623   if (notifyContent1 && mPresContext) {
  4624     EnsureDocument(mPresContext);
  4625     if (mDocument) {
  4626       nsAutoScriptBlocker scriptBlocker;
  4628       if (updateAncestors) {
  4629         nsCOMPtr<nsIContent> commonAncestor =
  4630           FindCommonAncestor(notifyContent1, notifyContent2);
  4631         if (notifyContent2) {
  4632           // It's very important to first notify the state removal and
  4633           // then the state addition, because due to labels it's
  4634           // possible that we're removing state from some element but
  4635           // then adding it again (say because mHoverContent changed
  4636           // from a control to its label).
  4637           UpdateAncestorState(notifyContent2, commonAncestor, aState, false);
  4639         UpdateAncestorState(notifyContent1, commonAncestor, aState,
  4640                             content1StateSet);
  4641       } else {
  4642         if (notifyContent2) {
  4643           DoStateChange(notifyContent2, aState, false);
  4645         DoStateChange(notifyContent1, aState, content1StateSet);
  4650   return true;
  4653 PLDHashOperator
  4654 EventStateManager::ResetLastOverForContent(
  4655                      const uint32_t& aIdx,
  4656                      nsRefPtr<OverOutElementsWrapper>& aElemWrapper,
  4657                      void* aClosure)
  4659   nsIContent* content = static_cast<nsIContent*>(aClosure);
  4660   if (aElemWrapper && aElemWrapper->mLastOverElement &&
  4661       nsContentUtils::ContentIsDescendantOf(aElemWrapper->mLastOverElement, content)) {
  4662     aElemWrapper->mLastOverElement = nullptr;
  4665   return PL_DHASH_NEXT;
  4668 void
  4669 EventStateManager::ContentRemoved(nsIDocument* aDocument, nsIContent* aContent)
  4671   /*
  4672    * Anchor and area elements when focused or hovered might make the UI to show
  4673    * the current link. We want to make sure that the UI gets informed when they
  4674    * are actually removed from the DOM.
  4675    */
  4676   if (aContent->IsHTML() &&
  4677       (aContent->Tag() == nsGkAtoms::a || aContent->Tag() == nsGkAtoms::area) &&
  4678       (aContent->AsElement()->State().HasAtLeastOneOfStates(NS_EVENT_STATE_FOCUS |
  4679                                                             NS_EVENT_STATE_HOVER))) {
  4680     nsGenericHTMLElement* element = static_cast<nsGenericHTMLElement*>(aContent);
  4681     element->LeaveLink(element->GetPresContext());
  4684   IMEStateManager::OnRemoveContent(mPresContext, aContent);
  4686   // inform the focus manager that the content is being removed. If this
  4687   // content is focused, the focus will be removed without firing events.
  4688   nsFocusManager* fm = nsFocusManager::GetFocusManager();
  4689   if (fm)
  4690     fm->ContentRemoved(aDocument, aContent);
  4692   if (mHoverContent &&
  4693       nsContentUtils::ContentIsDescendantOf(mHoverContent, aContent)) {
  4694     // Since hover is hierarchical, set the current hover to the
  4695     // content's parent node.
  4696     SetContentState(aContent->GetParent(), NS_EVENT_STATE_HOVER);
  4699   if (mActiveContent &&
  4700       nsContentUtils::ContentIsDescendantOf(mActiveContent, aContent)) {
  4701     // Active is hierarchical, so set the current active to the
  4702     // content's parent node.
  4703     SetContentState(aContent->GetParent(), NS_EVENT_STATE_ACTIVE);
  4706   if (sDragOverContent &&
  4707       sDragOverContent->OwnerDoc() == aContent->OwnerDoc() &&
  4708       nsContentUtils::ContentIsDescendantOf(sDragOverContent, aContent)) {
  4709     sDragOverContent = nullptr;
  4712   // See bug 292146 for why we want to null this out
  4713   ResetLastOverForContent(0, mMouseEnterLeaveHelper, aContent);
  4714   mPointersEnterLeaveHelper.Enumerate(
  4715     &EventStateManager::ResetLastOverForContent, aContent);
  4718 bool
  4719 EventStateManager::EventStatusOK(WidgetGUIEvent* aEvent)
  4721   return !(aEvent->message == NS_MOUSE_BUTTON_DOWN &&
  4722            aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton &&
  4723            !sNormalLMouseEventInProcess);
  4726 //-------------------------------------------
  4727 // Access Key Registration
  4728 //-------------------------------------------
  4729 void
  4730 EventStateManager::RegisterAccessKey(nsIContent* aContent, uint32_t aKey)
  4732   if (aContent && mAccessKeys.IndexOf(aContent) == -1)
  4733     mAccessKeys.AppendObject(aContent);
  4736 void
  4737 EventStateManager::UnregisterAccessKey(nsIContent* aContent, uint32_t aKey)
  4739   if (aContent)
  4740     mAccessKeys.RemoveObject(aContent);
  4743 uint32_t
  4744 EventStateManager::GetRegisteredAccessKey(nsIContent* aContent)
  4746   MOZ_ASSERT(aContent);
  4748   if (mAccessKeys.IndexOf(aContent) == -1)
  4749     return 0;
  4751   nsAutoString accessKey;
  4752   aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::accesskey, accessKey);
  4753   return accessKey.First();
  4756 void
  4757 EventStateManager::EnsureDocument(nsPresContext* aPresContext)
  4759   if (!mDocument)
  4760     mDocument = aPresContext->Document();
  4763 void
  4764 EventStateManager::FlushPendingEvents(nsPresContext* aPresContext)
  4766   NS_PRECONDITION(nullptr != aPresContext, "nullptr ptr");
  4767   nsIPresShell *shell = aPresContext->GetPresShell();
  4768   if (shell) {
  4769     shell->FlushPendingNotifications(Flush_InterruptibleLayout);
  4773 nsIContent*
  4774 EventStateManager::GetFocusedContent()
  4776   nsIFocusManager* fm = nsFocusManager::GetFocusManager();
  4777   if (!fm || !mDocument)
  4778     return nullptr;
  4780   nsCOMPtr<nsPIDOMWindow> focusedWindow;
  4781   return nsFocusManager::GetFocusedDescendant(mDocument->GetWindow(), false,
  4782                                               getter_AddRefs(focusedWindow));
  4785 //-------------------------------------------------------
  4786 // Return true if the docshell is visible
  4788 bool
  4789 EventStateManager::IsShellVisible(nsIDocShell* aShell)
  4791   NS_ASSERTION(aShell, "docshell is null");
  4793   nsCOMPtr<nsIBaseWindow> basewin = do_QueryInterface(aShell);
  4794   if (!basewin)
  4795     return true;
  4797   bool isVisible = true;
  4798   basewin->GetVisibility(&isVisible);
  4800   // We should be doing some additional checks here so that
  4801   // we don't tab into hidden tabs of tabbrowser.  -bryner
  4803   return isVisible;
  4806 nsresult
  4807 EventStateManager::DoContentCommandEvent(WidgetContentCommandEvent* aEvent)
  4809   EnsureDocument(mPresContext);
  4810   NS_ENSURE_TRUE(mDocument, NS_ERROR_FAILURE);
  4811   nsCOMPtr<nsPIDOMWindow> window(mDocument->GetWindow());
  4812   NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
  4814   nsCOMPtr<nsPIWindowRoot> root = window->GetTopWindowRoot();
  4815   NS_ENSURE_TRUE(root, NS_ERROR_FAILURE);
  4816   const char* cmd;
  4817   switch (aEvent->message) {
  4818     case NS_CONTENT_COMMAND_CUT:
  4819       cmd = "cmd_cut";
  4820       break;
  4821     case NS_CONTENT_COMMAND_COPY:
  4822       cmd = "cmd_copy";
  4823       break;
  4824     case NS_CONTENT_COMMAND_PASTE:
  4825       cmd = "cmd_paste";
  4826       break;
  4827     case NS_CONTENT_COMMAND_DELETE:
  4828       cmd = "cmd_delete";
  4829       break;
  4830     case NS_CONTENT_COMMAND_UNDO:
  4831       cmd = "cmd_undo";
  4832       break;
  4833     case NS_CONTENT_COMMAND_REDO:
  4834       cmd = "cmd_redo";
  4835       break;
  4836     case NS_CONTENT_COMMAND_PASTE_TRANSFERABLE:
  4837       cmd = "cmd_pasteTransferable";
  4838       break;
  4839     default:
  4840       return NS_ERROR_NOT_IMPLEMENTED;
  4842   nsCOMPtr<nsIController> controller;
  4843   nsresult rv = root->GetControllerForCommand(cmd, getter_AddRefs(controller));
  4844   NS_ENSURE_SUCCESS(rv, rv);
  4845   if (!controller) {
  4846     // When GetControllerForCommand succeeded but there is no controller, the
  4847     // command isn't supported.
  4848     aEvent->mIsEnabled = false;
  4849   } else {
  4850     bool canDoIt;
  4851     rv = controller->IsCommandEnabled(cmd, &canDoIt);
  4852     NS_ENSURE_SUCCESS(rv, rv);
  4853     aEvent->mIsEnabled = canDoIt;
  4854     if (canDoIt && !aEvent->mOnlyEnabledCheck) {
  4855       switch (aEvent->message) {
  4856         case NS_CONTENT_COMMAND_PASTE_TRANSFERABLE: {
  4857           nsCOMPtr<nsICommandController> commandController = do_QueryInterface(controller);
  4858           NS_ENSURE_STATE(commandController);
  4860           nsCOMPtr<nsICommandParams> params = do_CreateInstance("@mozilla.org/embedcomp/command-params;1", &rv);
  4861           NS_ENSURE_SUCCESS(rv, rv);
  4863           rv = params->SetISupportsValue("transferable", aEvent->mTransferable);
  4864           NS_ENSURE_SUCCESS(rv, rv);
  4866           rv = commandController->DoCommandWithParams(cmd, params);
  4867           break;
  4870         default:
  4871           rv = controller->DoCommand(cmd);
  4872           break;
  4874       NS_ENSURE_SUCCESS(rv, rv);
  4877   aEvent->mSucceeded = true;
  4878   return NS_OK;
  4881 nsresult
  4882 EventStateManager::DoContentCommandScrollEvent(
  4883                      WidgetContentCommandEvent* aEvent)
  4885   NS_ENSURE_TRUE(mPresContext, NS_ERROR_NOT_AVAILABLE);
  4886   nsIPresShell* ps = mPresContext->GetPresShell();
  4887   NS_ENSURE_TRUE(ps, NS_ERROR_NOT_AVAILABLE);
  4888   NS_ENSURE_TRUE(aEvent->mScroll.mAmount != 0, NS_ERROR_INVALID_ARG);
  4890   nsIScrollableFrame::ScrollUnit scrollUnit;
  4891   switch (aEvent->mScroll.mUnit) {
  4892     case WidgetContentCommandEvent::eCmdScrollUnit_Line:
  4893       scrollUnit = nsIScrollableFrame::LINES;
  4894       break;
  4895     case WidgetContentCommandEvent::eCmdScrollUnit_Page:
  4896       scrollUnit = nsIScrollableFrame::PAGES;
  4897       break;
  4898     case WidgetContentCommandEvent::eCmdScrollUnit_Whole:
  4899       scrollUnit = nsIScrollableFrame::WHOLE;
  4900       break;
  4901     default:
  4902       return NS_ERROR_INVALID_ARG;
  4905   aEvent->mSucceeded = true;
  4907   nsIScrollableFrame* sf =
  4908     ps->GetFrameToScrollAsScrollable(nsIPresShell::eEither);
  4909   aEvent->mIsEnabled = sf ?
  4910     (aEvent->mScroll.mIsHorizontal ?
  4911       WheelHandlingUtils::CanScrollOn(sf, aEvent->mScroll.mAmount, 0) :
  4912       WheelHandlingUtils::CanScrollOn(sf, 0, aEvent->mScroll.mAmount)) : false;
  4914   if (!aEvent->mIsEnabled || aEvent->mOnlyEnabledCheck) {
  4915     return NS_OK;
  4918   nsIntPoint pt(0, 0);
  4919   if (aEvent->mScroll.mIsHorizontal) {
  4920     pt.x = aEvent->mScroll.mAmount;
  4921   } else {
  4922     pt.y = aEvent->mScroll.mAmount;
  4925   // The caller may want synchronous scrolling.
  4926   sf->ScrollBy(pt, scrollUnit, nsIScrollableFrame::INSTANT);
  4927   return NS_OK;
  4930 void
  4931 EventStateManager::DoQuerySelectedText(WidgetQueryContentEvent* aEvent)
  4933   if (RemoteQueryContentEvent(aEvent)) {
  4934     return;
  4936   ContentEventHandler handler(mPresContext);
  4937   handler.OnQuerySelectedText(aEvent);
  4940 void
  4941 EventStateManager::SetActiveManager(EventStateManager* aNewESM,
  4942                                     nsIContent* aContent)
  4944   if (sActiveESM && aNewESM != sActiveESM) {
  4945     sActiveESM->SetContentState(nullptr, NS_EVENT_STATE_ACTIVE);
  4947   sActiveESM = aNewESM;
  4948   if (sActiveESM && aContent) {
  4949     sActiveESM->SetContentState(aContent, NS_EVENT_STATE_ACTIVE);
  4953 void
  4954 EventStateManager::ClearGlobalActiveContent(EventStateManager* aClearer)
  4956   if (aClearer) {
  4957     aClearer->SetContentState(nullptr, NS_EVENT_STATE_ACTIVE);
  4958     if (sDragOverContent) {
  4959       aClearer->SetContentState(nullptr, NS_EVENT_STATE_DRAGOVER);
  4962   if (sActiveESM && aClearer != sActiveESM) {
  4963     sActiveESM->SetContentState(nullptr, NS_EVENT_STATE_ACTIVE);
  4965   sActiveESM = nullptr;
  4968 /******************************************************************/
  4969 /* mozilla::EventStateManager::DeltaAccumulator                   */
  4970 /******************************************************************/
  4972 void
  4973 EventStateManager::DeltaAccumulator::InitLineOrPageDelta(
  4974                                        nsIFrame* aTargetFrame,
  4975                                        EventStateManager* aESM,
  4976                                        WidgetWheelEvent* aEvent)
  4978   MOZ_ASSERT(aESM);
  4979   MOZ_ASSERT(aEvent);
  4981   // Reset if the previous wheel event is too old.
  4982   if (!mLastTime.IsNull()) {
  4983     TimeDuration duration = TimeStamp::Now() - mLastTime;
  4984     if (duration.ToMilliseconds() > WheelTransaction::GetTimeoutTime()) {
  4985       Reset();
  4988   // If we have accumulated delta,  we may need to reset it.
  4989   if (IsInTransaction()) {
  4990     // If wheel event type is changed, reset the values.
  4991     if (mHandlingDeltaMode != aEvent->deltaMode ||
  4992         mHandlingPixelOnlyDevice != aEvent->isPixelOnlyDevice) {
  4993       Reset();
  4994     } else {
  4995       // If the delta direction is changed, we should reset only the
  4996       // accumulated values.
  4997       if (mX && aEvent->deltaX && ((aEvent->deltaX > 0.0) != (mX > 0.0))) {
  4998         mX = mPendingScrollAmountX = 0.0;
  5000       if (mY && aEvent->deltaY && ((aEvent->deltaY > 0.0) != (mY > 0.0))) {
  5001         mY = mPendingScrollAmountY = 0.0;
  5006   mHandlingDeltaMode = aEvent->deltaMode;
  5007   mHandlingPixelOnlyDevice = aEvent->isPixelOnlyDevice;
  5009   // If it's handling neither pixel scroll mode for pixel only device nor
  5010   // delta values multiplied by prefs, we must not modify lineOrPageDelta
  5011   // values.
  5012   if (!(mHandlingDeltaMode == nsIDOMWheelEvent::DOM_DELTA_PIXEL &&
  5013         mHandlingPixelOnlyDevice) &&
  5014       !EventStateManager::WheelPrefs::GetInstance()->
  5015         NeedToComputeLineOrPageDelta(aEvent)) {
  5016     // Set the delta values to mX and mY.  They would be used when above block
  5017     // resets mX/mY/mPendingScrollAmountX/mPendingScrollAmountY if the direction
  5018     // is changed.
  5019     // NOTE: We shouldn't accumulate the delta values, it might could cause
  5020     //       overflow even though it's not a realistic situation.
  5021     if (aEvent->deltaX) {
  5022       mX = aEvent->deltaX;
  5024     if (aEvent->deltaY) {
  5025       mY = aEvent->deltaY;
  5027     mLastTime = TimeStamp::Now();
  5028     return;
  5031   mX += aEvent->deltaX;
  5032   mY += aEvent->deltaY;
  5034   if (mHandlingDeltaMode == nsIDOMWheelEvent::DOM_DELTA_PIXEL) {
  5035     // Records pixel delta values and init lineOrPageDeltaX and
  5036     // lineOrPageDeltaY for wheel events which are caused by pixel only
  5037     // devices.  Ignore mouse wheel transaction for computing this.  The
  5038     // lineOrPageDelta values will be used by dispatching legacy
  5039     // NS_MOUSE_SCROLL_EVENT (DOMMouseScroll) but not be used for scrolling
  5040     // of default action.  The transaction should be used only for the default
  5041     // action.
  5042     nsIScrollableFrame* scrollTarget =
  5043       aESM->ComputeScrollTarget(aTargetFrame, aEvent,
  5044                                 COMPUTE_LEGACY_MOUSE_SCROLL_EVENT_TARGET);
  5045     nsIFrame* frame = do_QueryFrame(scrollTarget);
  5046     nsPresContext* pc =
  5047       frame ? frame->PresContext() : aTargetFrame->PresContext();
  5048     nsSize scrollAmount = aESM->GetScrollAmount(pc, aEvent, scrollTarget);
  5049     nsIntSize scrollAmountInCSSPixels(
  5050       nsPresContext::AppUnitsToIntCSSPixels(scrollAmount.width),
  5051       nsPresContext::AppUnitsToIntCSSPixels(scrollAmount.height));
  5053     aEvent->lineOrPageDeltaX = RoundDown(mX) / scrollAmountInCSSPixels.width;
  5054     aEvent->lineOrPageDeltaY = RoundDown(mY) / scrollAmountInCSSPixels.height;
  5056     mX -= aEvent->lineOrPageDeltaX * scrollAmountInCSSPixels.width;
  5057     mY -= aEvent->lineOrPageDeltaY * scrollAmountInCSSPixels.height;
  5058   } else {
  5059     aEvent->lineOrPageDeltaX = RoundDown(mX);
  5060     aEvent->lineOrPageDeltaY = RoundDown(mY);
  5061     mX -= aEvent->lineOrPageDeltaX;
  5062     mY -= aEvent->lineOrPageDeltaY;
  5065   mLastTime = TimeStamp::Now();
  5068 void
  5069 EventStateManager::DeltaAccumulator::Reset()
  5071   mX = mY = 0.0;
  5072   mPendingScrollAmountX = mPendingScrollAmountY = 0.0;
  5073   mHandlingDeltaMode = UINT32_MAX;
  5074   mHandlingPixelOnlyDevice = false;
  5077 nsIntPoint
  5078 EventStateManager::DeltaAccumulator::ComputeScrollAmountForDefaultAction(
  5079                      WidgetWheelEvent* aEvent,
  5080                      const nsIntSize& aScrollAmountInDevPixels)
  5082   MOZ_ASSERT(aEvent);
  5084   // If the wheel event is line scroll and the delta value is computed from
  5085   // system settings, allow to override the system speed.
  5086   bool allowScrollSpeedOverride =
  5087     (!aEvent->customizedByUserPrefs &&
  5088      aEvent->deltaMode == nsIDOMWheelEvent::DOM_DELTA_LINE);
  5089   DeltaValues acceleratedDelta =
  5090     WheelTransaction::AccelerateWheelDelta(aEvent, allowScrollSpeedOverride);
  5092   nsIntPoint result(0, 0);
  5093   if (aEvent->deltaMode == nsIDOMWheelEvent::DOM_DELTA_PIXEL) {
  5094     mPendingScrollAmountX += acceleratedDelta.deltaX;
  5095     mPendingScrollAmountY += acceleratedDelta.deltaY;
  5096   } else {
  5097     mPendingScrollAmountX +=
  5098       aScrollAmountInDevPixels.width * acceleratedDelta.deltaX;
  5099     mPendingScrollAmountY +=
  5100       aScrollAmountInDevPixels.height * acceleratedDelta.deltaY;
  5102   result.x = RoundDown(mPendingScrollAmountX);
  5103   result.y = RoundDown(mPendingScrollAmountY);
  5104   mPendingScrollAmountX -= result.x;
  5105   mPendingScrollAmountY -= result.y;
  5107   return result;
  5110 /******************************************************************/
  5111 /* mozilla::EventStateManager::WheelPrefs                         */
  5112 /******************************************************************/
  5114 // static
  5115 EventStateManager::WheelPrefs*
  5116 EventStateManager::WheelPrefs::GetInstance()
  5118   if (!sInstance) {
  5119     sInstance = new WheelPrefs();
  5121   return sInstance;
  5124 // static
  5125 void
  5126 EventStateManager::WheelPrefs::Shutdown()
  5128   delete sInstance;
  5129   sInstance = nullptr;
  5132 // static
  5133 void
  5134 EventStateManager::WheelPrefs::OnPrefChanged(const char* aPrefName,
  5135                                              void* aClosure)
  5137   // forget all prefs, it's not problem for performance.
  5138   sInstance->Reset();
  5139   DeltaAccumulator::GetInstance()->Reset();
  5142 EventStateManager::WheelPrefs::WheelPrefs()
  5144   Reset();
  5145   Preferences::RegisterCallback(OnPrefChanged, "mousewheel.", nullptr);
  5148 EventStateManager::WheelPrefs::~WheelPrefs()
  5150   Preferences::UnregisterCallback(OnPrefChanged, "mousewheel.", nullptr);
  5153 void
  5154 EventStateManager::WheelPrefs::Reset()
  5156   memset(mInit, 0, sizeof(mInit));
  5160 EventStateManager::WheelPrefs::Index
  5161 EventStateManager::WheelPrefs::GetIndexFor(WidgetWheelEvent* aEvent)
  5163   if (!aEvent) {
  5164     return INDEX_DEFAULT;
  5167   Modifiers modifiers =
  5168     (aEvent->modifiers & (MODIFIER_ALT |
  5169                           MODIFIER_CONTROL |
  5170                           MODIFIER_META |
  5171                           MODIFIER_SHIFT |
  5172                           MODIFIER_OS));
  5174   switch (modifiers) {
  5175     case MODIFIER_ALT:
  5176       return INDEX_ALT;
  5177     case MODIFIER_CONTROL:
  5178       return INDEX_CONTROL;
  5179     case MODIFIER_META:
  5180       return INDEX_META;
  5181     case MODIFIER_SHIFT:
  5182       return INDEX_SHIFT;
  5183     case MODIFIER_OS:
  5184       return INDEX_OS;
  5185     default:
  5186       // If two or more modifier keys are pressed, we should use default
  5187       // settings.
  5188       return INDEX_DEFAULT;
  5192 void
  5193 EventStateManager::WheelPrefs::GetBasePrefName(
  5194                      EventStateManager::WheelPrefs::Index aIndex,
  5195                      nsACString& aBasePrefName)
  5197   aBasePrefName.AssignLiteral("mousewheel.");
  5198   switch (aIndex) {
  5199     case INDEX_ALT:
  5200       aBasePrefName.AppendLiteral("with_alt.");
  5201       break;
  5202     case INDEX_CONTROL:
  5203       aBasePrefName.AppendLiteral("with_control.");
  5204       break;
  5205     case INDEX_META:
  5206       aBasePrefName.AppendLiteral("with_meta.");
  5207       break;
  5208     case INDEX_SHIFT:
  5209       aBasePrefName.AppendLiteral("with_shift.");
  5210       break;
  5211     case INDEX_OS:
  5212       aBasePrefName.AppendLiteral("with_win.");
  5213       break;
  5214     case INDEX_DEFAULT:
  5215     default:
  5216       aBasePrefName.AppendLiteral("default.");
  5217       break;
  5221 void
  5222 EventStateManager::WheelPrefs::Init(EventStateManager::WheelPrefs::Index aIndex)
  5224   if (mInit[aIndex]) {
  5225     return;
  5227   mInit[aIndex] = true;
  5229   nsAutoCString basePrefName;
  5230   GetBasePrefName(aIndex, basePrefName);
  5232   nsAutoCString prefNameX(basePrefName);
  5233   prefNameX.AppendLiteral("delta_multiplier_x");
  5234   mMultiplierX[aIndex] =
  5235     static_cast<double>(Preferences::GetInt(prefNameX.get(), 100)) / 100;
  5237   nsAutoCString prefNameY(basePrefName);
  5238   prefNameY.AppendLiteral("delta_multiplier_y");
  5239   mMultiplierY[aIndex] =
  5240     static_cast<double>(Preferences::GetInt(prefNameY.get(), 100)) / 100;
  5242   nsAutoCString prefNameZ(basePrefName);
  5243   prefNameZ.AppendLiteral("delta_multiplier_z");
  5244   mMultiplierZ[aIndex] =
  5245     static_cast<double>(Preferences::GetInt(prefNameZ.get(), 100)) / 100;
  5247   nsAutoCString prefNameAction(basePrefName);
  5248   prefNameAction.AppendLiteral("action");
  5249   int32_t action = Preferences::GetInt(prefNameAction.get(), ACTION_SCROLL);
  5250   if (action < int32_t(ACTION_NONE) || action > int32_t(ACTION_LAST)) {
  5251     NS_WARNING("Unsupported action pref value, replaced with 'Scroll'.");
  5252     action = ACTION_SCROLL;
  5254   mActions[aIndex] = static_cast<Action>(action);
  5256   // Compute action values overridden by .override_x pref.
  5257   // At present, override is possible only for the x-direction
  5258   // because this pref is introduced mainly for tilt wheels.
  5259   prefNameAction.AppendLiteral(".override_x");
  5260   int32_t actionOverrideX = Preferences::GetInt(prefNameAction.get(), -1);
  5261   if (actionOverrideX < -1 || actionOverrideX > int32_t(ACTION_LAST)) {
  5262     NS_WARNING("Unsupported action override pref value, didn't override.");
  5263     actionOverrideX = -1;
  5265   mOverriddenActionsX[aIndex] = (actionOverrideX == -1)
  5266                               ? static_cast<Action>(action)
  5267                               : static_cast<Action>(actionOverrideX);
  5270 void
  5271 EventStateManager::WheelPrefs::ApplyUserPrefsToDelta(WidgetWheelEvent* aEvent)
  5273   Index index = GetIndexFor(aEvent);
  5274   Init(index);
  5276   aEvent->deltaX *= mMultiplierX[index];
  5277   aEvent->deltaY *= mMultiplierY[index];
  5278   aEvent->deltaZ *= mMultiplierZ[index];
  5280   // If the multiplier is 1.0 or -1.0, i.e., it doesn't change the absolute
  5281   // value, we should use lineOrPageDelta values which were set by widget.
  5282   // Otherwise, we need to compute them from accumulated delta values.
  5283   if (!NeedToComputeLineOrPageDelta(aEvent)) {
  5284     aEvent->lineOrPageDeltaX *= static_cast<int32_t>(mMultiplierX[index]);
  5285     aEvent->lineOrPageDeltaY *= static_cast<int32_t>(mMultiplierY[index]);
  5286   } else {
  5287     aEvent->lineOrPageDeltaX = 0;
  5288     aEvent->lineOrPageDeltaY = 0;
  5291   aEvent->customizedByUserPrefs =
  5292     ((mMultiplierX[index] != 1.0) || (mMultiplierY[index] != 1.0) ||
  5293      (mMultiplierZ[index] != 1.0));
  5296 void
  5297 EventStateManager::WheelPrefs::CancelApplyingUserPrefsFromOverflowDelta(
  5298                                  WidgetWheelEvent* aEvent)
  5300   Index index = GetIndexFor(aEvent);
  5301   Init(index);
  5303   // XXX If the multiplier pref value is negative, the scroll direction was
  5304   //     changed and caused to scroll different direction.  In such case,
  5305   //     this method reverts the sign of overflowDelta.  Does it make widget
  5306   //     happy?  Although, widget can know the pref applied delta values by
  5307   //     referrencing the deltaX and deltaY of the event.
  5309   if (mMultiplierX[index]) {
  5310     aEvent->overflowDeltaX /= mMultiplierX[index];
  5312   if (mMultiplierY[index]) {
  5313     aEvent->overflowDeltaY /= mMultiplierY[index];
  5317 EventStateManager::WheelPrefs::Action
  5318 EventStateManager::WheelPrefs::ComputeActionFor(WidgetWheelEvent* aEvent)
  5320   Index index = GetIndexFor(aEvent);
  5321   Init(index);
  5323   bool deltaXPreferred =
  5324     (Abs(aEvent->deltaX) > Abs(aEvent->deltaY) &&
  5325      Abs(aEvent->deltaX) > Abs(aEvent->deltaZ));
  5326   Action* actions = deltaXPreferred ? mOverriddenActionsX : mActions;
  5327   if (actions[index] == ACTION_NONE || actions[index] == ACTION_SCROLL) {
  5328     return actions[index];
  5331   // Momentum events shouldn't run special actions.
  5332   if (aEvent->isMomentum) {
  5333     // Use the default action.  Note that user might kill the wheel scrolling.
  5334     Init(INDEX_DEFAULT);
  5335     return (actions[INDEX_DEFAULT] == ACTION_SCROLL) ? ACTION_SCROLL :
  5336                                                        ACTION_NONE;
  5339   return actions[index];
  5342 bool
  5343 EventStateManager::WheelPrefs::NeedToComputeLineOrPageDelta(
  5344                                  WidgetWheelEvent* aEvent)
  5346   Index index = GetIndexFor(aEvent);
  5347   Init(index);
  5349   return (mMultiplierX[index] != 1.0 && mMultiplierX[index] != -1.0) ||
  5350          (mMultiplierY[index] != 1.0 && mMultiplierY[index] != -1.0);
  5353 bool
  5354 EventStateManager::WheelPrefs::IsOverOnePageScrollAllowedX(
  5355                                  WidgetWheelEvent* aEvent)
  5357   Index index = GetIndexFor(aEvent);
  5358   Init(index);
  5359   return Abs(mMultiplierX[index]) >=
  5360            MIN_MULTIPLIER_VALUE_ALLOWING_OVER_ONE_PAGE_SCROLL;
  5363 bool
  5364 EventStateManager::WheelPrefs::IsOverOnePageScrollAllowedY(
  5365                                  WidgetWheelEvent* aEvent)
  5367   Index index = GetIndexFor(aEvent);
  5368   Init(index);
  5369   return Abs(mMultiplierY[index]) >=
  5370            MIN_MULTIPLIER_VALUE_ALLOWING_OVER_ONE_PAGE_SCROLL;
  5373 /******************************************************************/
  5374 /* mozilla::EventStateManager::Prefs                              */
  5375 /******************************************************************/
  5377 bool EventStateManager::Prefs::sKeyCausesActivation = true;
  5378 bool EventStateManager::Prefs::sClickHoldContextMenu = false;
  5379 int32_t EventStateManager::Prefs::sGenericAccessModifierKey = -1;
  5380 int32_t EventStateManager::Prefs::sChromeAccessModifierMask = 0;
  5381 int32_t EventStateManager::Prefs::sContentAccessModifierMask = 0;
  5383 // static
  5384 void
  5385 EventStateManager::Prefs::Init()
  5387   DebugOnly<nsresult> rv =
  5388     Preferences::AddBoolVarCache(&sKeyCausesActivation,
  5389                                  "accessibility.accesskeycausesactivation",
  5390                                  sKeyCausesActivation);
  5391   MOZ_ASSERT(NS_SUCCEEDED(rv),
  5392              "Failed to observe \"accessibility.accesskeycausesactivation\"");
  5393   rv = Preferences::AddBoolVarCache(&sClickHoldContextMenu,
  5394                                     "ui.click_hold_context_menus",
  5395                                     sClickHoldContextMenu);
  5396   MOZ_ASSERT(NS_SUCCEEDED(rv),
  5397              "Failed to observe \"ui.click_hold_context_menus\"");
  5398   rv = Preferences::AddIntVarCache(&sGenericAccessModifierKey,
  5399                                    "ui.key.generalAccessKey",
  5400                                    sGenericAccessModifierKey);
  5401   MOZ_ASSERT(NS_SUCCEEDED(rv),
  5402              "Failed to observe \"ui.key.generalAccessKey\"");
  5403   rv = Preferences::AddIntVarCache(&sChromeAccessModifierMask,
  5404                                    "ui.key.chromeAccess",
  5405                                    sChromeAccessModifierMask);
  5406   MOZ_ASSERT(NS_SUCCEEDED(rv),
  5407              "Failed to observe \"ui.key.chromeAccess\"");
  5408   rv = Preferences::AddIntVarCache(&sContentAccessModifierMask,
  5409                                    "ui.key.contentAccess",
  5410                                    sContentAccessModifierMask);
  5411   MOZ_ASSERT(NS_SUCCEEDED(rv),
  5412              "Failed to observe \"ui.key.contentAccess\"");
  5414   rv = Preferences::RegisterCallback(OnChange, "dom.popup_allowed_events");
  5415   MOZ_ASSERT(NS_SUCCEEDED(rv),
  5416              "Failed to observe \"dom.popup_allowed_events\"");
  5419 // static
  5420 void
  5421 EventStateManager::Prefs::OnChange(const char* aPrefName, void*)
  5423   nsDependentCString prefName(aPrefName);
  5424   if (prefName.EqualsLiteral("dom.popup_allowed_events")) {
  5425     Event::PopupAllowedEventsChanged();
  5429 // static
  5430 void
  5431 EventStateManager::Prefs::Shutdown()
  5433   Preferences::UnregisterCallback(OnChange, "dom.popup_allowed_events");
  5436 // static
  5437 int32_t
  5438 EventStateManager::Prefs::ChromeAccessModifierMask()
  5440   return GetAccessModifierMask(nsIDocShellTreeItem::typeChrome);
  5443 // static
  5444 int32_t
  5445 EventStateManager::Prefs::ContentAccessModifierMask()
  5447   return GetAccessModifierMask(nsIDocShellTreeItem::typeContent);
  5450 // static
  5451 int32_t
  5452 EventStateManager::Prefs::GetAccessModifierMask(int32_t aItemType)
  5454   switch (sGenericAccessModifierKey) {
  5455     case -1:                             break; // use the individual prefs
  5456     case nsIDOMKeyEvent::DOM_VK_SHIFT:   return NS_MODIFIER_SHIFT;
  5457     case nsIDOMKeyEvent::DOM_VK_CONTROL: return NS_MODIFIER_CONTROL;
  5458     case nsIDOMKeyEvent::DOM_VK_ALT:     return NS_MODIFIER_ALT;
  5459     case nsIDOMKeyEvent::DOM_VK_META:    return NS_MODIFIER_META;
  5460     case nsIDOMKeyEvent::DOM_VK_WIN:     return NS_MODIFIER_OS;
  5461     default:                             return 0;
  5464   switch (aItemType) {
  5465     case nsIDocShellTreeItem::typeChrome:
  5466       return sChromeAccessModifierMask;
  5467     case nsIDocShellTreeItem::typeContent:
  5468       return sContentAccessModifierMask;
  5469     default:
  5470       return 0;
  5474 /******************************************************************/
  5475 /* mozilla::AutoHandlingUserInputStatePusher                      */
  5476 /******************************************************************/
  5478 AutoHandlingUserInputStatePusher::AutoHandlingUserInputStatePusher(
  5479                                     bool aIsHandlingUserInput,
  5480                                     WidgetEvent* aEvent,
  5481                                     nsIDocument* aDocument) :
  5482   mIsHandlingUserInput(aIsHandlingUserInput),
  5483   mIsMouseDown(aEvent && aEvent->message == NS_MOUSE_BUTTON_DOWN),
  5484   mResetFMMouseDownState(false)
  5486   if (!aIsHandlingUserInput) {
  5487     return;
  5489   EventStateManager::StartHandlingUserInput();
  5490   if (!mIsMouseDown) {
  5491     return;
  5493   nsIPresShell::SetCapturingContent(nullptr, 0);
  5494   nsIPresShell::AllowMouseCapture(true);
  5495   if (!aDocument || !aEvent->mFlags.mIsTrusted) {
  5496     return;
  5498   nsFocusManager* fm = nsFocusManager::GetFocusManager();
  5499   NS_ENSURE_TRUE_VOID(fm);
  5500   fm->SetMouseButtonDownHandlingDocument(aDocument);
  5501   mResetFMMouseDownState = true;
  5504 AutoHandlingUserInputStatePusher::~AutoHandlingUserInputStatePusher()
  5506   if (!mIsHandlingUserInput) {
  5507     return;
  5509   EventStateManager::StopHandlingUserInput();
  5510   if (!mIsMouseDown) {
  5511     return;
  5513   nsIPresShell::AllowMouseCapture(false);
  5514   if (!mResetFMMouseDownState) {
  5515     return;
  5517   nsFocusManager* fm = nsFocusManager::GetFocusManager();
  5518   NS_ENSURE_TRUE_VOID(fm);
  5519   fm->SetMouseButtonDownHandlingDocument(nullptr);
  5522 } // namespace mozilla

mercurial