dom/events/EventStateManager.cpp

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

mercurial