dom/events/EventStateManager.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial