1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "EventStateManager.h"
9 #include "mozilla/AsyncEventDispatcher.h"
10 #include "mozilla/Attributes.h"
11 #include "mozilla/EditorBase.h"
12 #include "mozilla/EventDispatcher.h"
13 #include "mozilla/EventForwards.h"
14 #include "mozilla/Hal.h"
15 #include "mozilla/HTMLEditor.h"
16 #include "mozilla/IMEStateManager.h"
17 #include "mozilla/Likely.h"
18 #include "mozilla/MiscEvents.h"
19 #include "mozilla/MathAlgorithms.h"
20 #include "mozilla/MouseEvents.h"
21 #include "mozilla/PointerLockManager.h"
22 #include "mozilla/PresShell.h"
23 #include "mozilla/ScopeExit.h"
24 #include "mozilla/ScrollTypes.h"
25 #include "mozilla/TextComposition.h"
26 #include "mozilla/TextControlElement.h"
27 #include "mozilla/TextEditor.h"
28 #include "mozilla/TextEvents.h"
29 #include "mozilla/TouchEvents.h"
30 #include "mozilla/Telemetry.h"
31 #include "mozilla/UniquePtr.h"
32 #include "mozilla/dom/BrowserBridgeChild.h"
33 #include "mozilla/dom/BrowsingContext.h"
34 #include "mozilla/dom/CanonicalBrowsingContext.h"
35 #include "mozilla/dom/ContentChild.h"
36 #include "mozilla/dom/DOMIntersectionObserver.h"
37 #include "mozilla/dom/DragEvent.h"
38 #include "mozilla/dom/Event.h"
39 #include "mozilla/dom/FrameLoaderBinding.h"
40 #include "mozilla/dom/HTMLLabelElement.h"
41 #include "mozilla/dom/HTMLInputElement.h"
42 #include "mozilla/dom/MouseEventBinding.h"
43 #include "mozilla/dom/BrowserChild.h"
44 #include "mozilla/dom/PointerEventHandler.h"
45 #include "mozilla/dom/UIEvent.h"
46 #include "mozilla/dom/UIEventBinding.h"
47 #include "mozilla/dom/UserActivation.h"
48 #include "mozilla/dom/WheelEventBinding.h"
49 #include "mozilla/glean/GleanMetrics.h"
50 #include "mozilla/StaticPrefs_accessibility.h"
51 #include "mozilla/StaticPrefs_browser.h"
52 #include "mozilla/StaticPrefs_dom.h"
53 #include "mozilla/StaticPrefs_layout.h"
54 #include "mozilla/StaticPrefs_mousewheel.h"
55 #include "mozilla/StaticPrefs_ui.h"
56 #include "mozilla/StaticPrefs_zoom.h"
58 #include "ContentEventHandler.h"
59 #include "IMEContentObserver.h"
60 #include "WheelHandlingHelper.h"
61 #include "RemoteDragStartData.h"
63 #include "nsCommandParams.h"
65 #include "nsCopySupport.h"
66 #include "nsFocusManager.h"
67 #include "nsGenericHTMLElement.h"
68 #include "nsIClipboard.h"
69 #include "nsIContent.h"
70 #include "nsIContentInlines.h"
71 #include "mozilla/dom/Document.h"
72 #include "nsICookieJarSettings.h"
74 #include "nsFrameLoaderOwner.h"
75 #include "nsIWidget.h"
76 #include "nsLiteralString.h"
77 #include "nsPresContext.h"
79 #include "nsGkAtoms.h"
80 #include "nsIFormControl.h"
81 #include "nsComboboxControlFrame.h"
82 #include "nsIScrollableFrame.h"
83 #include "nsIDOMXULControlElement.h"
84 #include "nsNameSpaceManager.h"
85 #include "nsIBaseWindow.h"
86 #include "nsFrameSelection.h"
87 #include "nsPIDOMWindow.h"
88 #include "nsPIWindowRoot.h"
89 #include "nsIWebNavigation.h"
90 #include "nsIDocumentViewer.h"
91 #include "nsFrameManager.h"
92 #include "nsIBrowserChild.h"
93 #include "nsMenuPopupFrame.h"
95 #include "nsIObserverService.h"
96 #include "nsIDocShell.h"
98 #include "nsSubDocumentFrame.h"
99 #include "nsLayoutUtils.h"
100 #include "nsIInterfaceRequestorUtils.h"
101 #include "nsUnicharUtils.h"
102 #include "nsContentUtils.h"
104 #include "imgIContainer.h"
105 #include "nsIProperties.h"
106 #include "nsISupportsPrimitives.h"
108 #include "nsServiceManagerUtils.h"
109 #include "nsITimer.h"
110 #include "nsFontMetrics.h"
111 #include "nsIDragService.h"
112 #include "nsIDragSession.h"
113 #include "mozilla/dom/DataTransfer.h"
114 #include "nsContentAreaDragDrop.h"
115 #include "nsTreeBodyFrame.h"
116 #include "nsIController.h"
117 #include "mozilla/Services.h"
118 #include "mozilla/dom/ContentParent.h"
119 #include "mozilla/dom/Record.h"
120 #include "mozilla/dom/Selection.h"
122 #include "mozilla/Preferences.h"
123 #include "mozilla/LookAndFeel.h"
124 #include "mozilla/ProfilerLabels.h"
128 # import <ApplicationServices/ApplicationServices.h>
135 static const LayoutDeviceIntPoint kInvalidRefPoint
=
136 LayoutDeviceIntPoint(-1, -1);
138 static uint32_t gMouseOrKeyboardEventCounter
= 0;
139 static nsITimer
* gUserInteractionTimer
= nullptr;
140 static nsITimerCallback
* gUserInteractionTimerCallback
= nullptr;
142 static const double kCursorLoadingTimeout
= 1000; // ms
143 static AutoWeakFrame gLastCursorSourceFrame
;
144 static TimeStamp gLastCursorUpdateTime
;
145 static TimeStamp gTypingStartTime
;
146 static TimeStamp gTypingEndTime
;
147 static int32_t gTypingInteractionKeyPresses
= 0;
148 static dom::InteractionData gTypingInteraction
= {};
150 static inline int32_t RoundDown(double aDouble
) {
151 return (aDouble
> 0) ? static_cast<int32_t>(floor(aDouble
))
152 : static_cast<int32_t>(ceil(aDouble
));
155 static bool IsSelectingLink(nsIFrame
* aTargetFrame
) {
159 const nsFrameSelection
* frameSel
= aTargetFrame
->GetConstFrameSelection();
160 if (!frameSel
|| !frameSel
->GetDragState()) {
164 if (!nsContentUtils::GetClosestLinkInFlatTree(aTargetFrame
->GetContent())) {
170 static UniquePtr
<WidgetMouseEvent
> CreateMouseOrPointerWidgetEvent(
171 WidgetMouseEvent
* aMouseEvent
, EventMessage aMessage
,
172 EventTarget
* aRelatedTarget
);
175 * Returns the common ancestor for mouseup purpose, given the
176 * current mouseup target and the previous mousedown target.
178 static nsINode
* GetCommonAncestorForMouseUp(
179 nsINode
* aCurrentMouseUpTarget
, nsINode
* aLastMouseDownTarget
,
180 Maybe
<FormControlType
>& aLastMouseDownInputControlType
) {
181 if (!aCurrentMouseUpTarget
|| !aLastMouseDownTarget
) {
185 if (aCurrentMouseUpTarget
== aLastMouseDownTarget
) {
186 return aCurrentMouseUpTarget
;
189 // Build the chain of parents
190 AutoTArray
<nsINode
*, 30> parents1
;
192 parents1
.AppendElement(aCurrentMouseUpTarget
);
193 aCurrentMouseUpTarget
= aCurrentMouseUpTarget
->GetFlattenedTreeParentNode();
194 } while (aCurrentMouseUpTarget
);
196 AutoTArray
<nsINode
*, 30> parents2
;
198 parents2
.AppendElement(aLastMouseDownTarget
);
199 if (aLastMouseDownTarget
== parents1
.LastElement()) {
202 aLastMouseDownTarget
= aLastMouseDownTarget
->GetFlattenedTreeParentNode();
203 } while (aLastMouseDownTarget
);
205 // Find where the parent chain differs
206 uint32_t pos1
= parents1
.Length();
207 uint32_t pos2
= parents2
.Length();
208 nsINode
* parent
= nullptr;
209 for (uint32_t len
= std::min(pos1
, pos2
); len
> 0; --len
) {
210 nsINode
* child1
= parents1
.ElementAt(--pos1
);
211 nsINode
* child2
= parents2
.ElementAt(--pos2
);
212 if (child1
!= child2
) {
216 // If the input control type is different between mouseup and mousedown,
217 // this is not a valid click.
218 if (HTMLInputElement
* input
= HTMLInputElement::FromNodeOrNull(child1
)) {
219 if (aLastMouseDownInputControlType
.isSome() &&
220 aLastMouseDownInputControlType
.ref() != input
->ControlType()) {
230 LazyLogModule
sMouseBoundaryLog("MouseBoundaryEvents");
231 LazyLogModule
sPointerBoundaryLog("PointerBoundaryEvents");
233 /******************************************************************/
234 /* mozilla::UITimerCallback */
235 /******************************************************************/
237 class UITimerCallback final
: public nsITimerCallback
, public nsINamed
{
239 UITimerCallback() : mPreviousCount(0) {}
241 NS_DECL_NSITIMERCALLBACK
244 ~UITimerCallback() = default;
245 uint32_t mPreviousCount
;
248 NS_IMPL_ISUPPORTS(UITimerCallback
, nsITimerCallback
, nsINamed
)
250 // If aTimer is nullptr, this method always sends "user-interaction-inactive"
253 UITimerCallback::Notify(nsITimer
* aTimer
) {
254 nsCOMPtr
<nsIObserverService
> obs
= mozilla::services::GetObserverService();
255 if (!obs
) return NS_ERROR_FAILURE
;
256 if ((gMouseOrKeyboardEventCounter
== mPreviousCount
) || !aTimer
) {
257 gMouseOrKeyboardEventCounter
= 0;
258 obs
->NotifyObservers(nullptr, "user-interaction-inactive", nullptr);
259 if (gUserInteractionTimer
) {
260 gUserInteractionTimer
->Cancel();
261 NS_RELEASE(gUserInteractionTimer
);
264 obs
->NotifyObservers(nullptr, "user-interaction-active", nullptr);
265 EventStateManager::UpdateUserActivityTimer();
267 if (XRE_IsParentProcess()) {
268 hal::BatteryInformation batteryInfo
;
269 hal::GetCurrentBatteryInformation(&batteryInfo
);
270 glean::power_battery::percentage_when_user_active
.AccumulateSingleSample(
271 uint64_t(batteryInfo
.level() * 100));
274 mPreviousCount
= gMouseOrKeyboardEventCounter
;
279 UITimerCallback::GetName(nsACString
& aName
) {
280 aName
.AssignLiteral("UITimerCallback_timer");
284 /******************************************************************/
285 /* mozilla::OverOutElementsWrapper */
286 /******************************************************************/
288 NS_IMPL_CYCLE_COLLECTION(OverOutElementsWrapper
, mDeepestEnterEventTarget
,
289 mDispatchingOverEventTarget
,
290 mDispatchingOutOrDeepestLeaveEventTarget
)
291 NS_IMPL_CYCLE_COLLECTING_ADDREF(OverOutElementsWrapper
)
292 NS_IMPL_CYCLE_COLLECTING_RELEASE(OverOutElementsWrapper
)
294 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(OverOutElementsWrapper
)
295 NS_INTERFACE_MAP_ENTRY(nsISupports
)
298 void OverOutElementsWrapper::ContentRemoved(nsIContent
& aContent
) {
299 if (!mDeepestEnterEventTarget
) {
303 if (!nsContentUtils::ContentIsFlattenedTreeDescendantOf(
304 mDeepestEnterEventTarget
, &aContent
)) {
308 LogModule
* const logModule
= mType
== BoundaryEventType::Mouse
310 : sPointerBoundaryLog
;
313 dom_events_mouse_pointer_boundary_keep_enter_targets_after_over_target_removed()) {
314 MOZ_LOG(logModule
, LogLevel::Info
,
315 ("The last \"over\" event target (%p) is removed",
316 mDeepestEnterEventTarget
.get()));
317 mDeepestEnterEventTarget
= nullptr;
321 if (mDispatchingOverEventTarget
&&
322 (mDeepestEnterEventTarget
== mDispatchingOverEventTarget
||
323 nsContentUtils::ContentIsFlattenedTreeDescendantOf(
324 mDispatchingOverEventTarget
, &aContent
))) {
325 if (mDispatchingOverEventTarget
==
326 mDispatchingOutOrDeepestLeaveEventTarget
) {
327 MOZ_LOG(logModule
, LogLevel::Info
,
328 ("The dispatching \"%s\" event target (%p) is removed",
329 mDeepestEnterEventTargetIsOverEventTarget
? "out" : "leave",
330 mDispatchingOutOrDeepestLeaveEventTarget
.get()));
331 mDispatchingOutOrDeepestLeaveEventTarget
= nullptr;
333 MOZ_LOG(logModule
, LogLevel::Info
,
334 ("The dispatching \"over\" event target (%p) is removed",
335 mDispatchingOverEventTarget
.get()));
336 mDispatchingOverEventTarget
= nullptr;
338 if (mDispatchingOutOrDeepestLeaveEventTarget
&&
339 (mDeepestEnterEventTarget
== mDispatchingOutOrDeepestLeaveEventTarget
||
340 nsContentUtils::ContentIsFlattenedTreeDescendantOf(
341 mDispatchingOutOrDeepestLeaveEventTarget
, &aContent
))) {
342 MOZ_LOG(logModule
, LogLevel::Info
,
343 ("The dispatching \"%s\" event target (%p) is removed",
344 mDeepestEnterEventTargetIsOverEventTarget
? "out" : "leave",
345 mDispatchingOutOrDeepestLeaveEventTarget
.get()));
346 mDispatchingOutOrDeepestLeaveEventTarget
= nullptr;
348 MOZ_LOG(logModule
, LogLevel::Info
,
349 ("The last \"%s\" event target (%p) is removed and now the last "
350 "deepest enter target becomes %s(%p)",
351 mDeepestEnterEventTargetIsOverEventTarget
? "over" : "enter",
352 mDeepestEnterEventTarget
.get(),
353 aContent
.GetFlattenedTreeParent()
354 ? ToString(*aContent
.GetFlattenedTreeParent()).c_str()
356 aContent
.GetFlattenedTreeParent()));
357 mDeepestEnterEventTarget
= aContent
.GetFlattenedTreeParent();
358 mDeepestEnterEventTargetIsOverEventTarget
= false;
361 void OverOutElementsWrapper::DidDispatchOverAndEnterEvent(
362 nsIContent
* aOriginalOverTargetInComposedDoc
) {
363 mDispatchingOverEventTarget
= nullptr;
365 // Pointer Events define that once the `pointerover` event target is removed
366 // from the tree, `pointerout` should not be fired on that and the closest
367 // connected ancestor at the target removal should be kept as the deepest
368 // `pointerleave` target. Therefore, we don't need the special handling for
369 // `pointerout` event target if the last `pointerover` target is temporarily
370 // removed from the tree.
371 if (mType
== OverOutElementsWrapper::BoundaryEventType::Pointer
) {
375 // Assume that the caller checks whether aOriginalOverTarget is in the
376 // original document. If we don't enable the strict mouse/pointer event
377 // boundary event dispatching by the pref (see below),
378 // mDeepestEnterEventTarget is set to nullptr when the last "over" target is
379 // removed. Therefore, we cannot check whether aOriginalOverTarget is in the
380 // original document here.
381 if (!aOriginalOverTargetInComposedDoc
) {
384 MOZ_ASSERT_IF(mDeepestEnterEventTarget
,
385 mDeepestEnterEventTarget
->GetComposedDoc() ==
386 aOriginalOverTargetInComposedDoc
->GetComposedDoc());
387 // If the "mouseover" event target is removed temporarily while we're
388 // dispatching "mouseover" and "mouseenter" events and the target gets back
389 // under the deepest enter event target, we should restore the "mouseover"
392 dom_events_mouse_pointer_boundary_keep_enter_targets_after_over_target_removed() &&
393 !mDeepestEnterEventTarget
) ||
394 (!mDeepestEnterEventTargetIsOverEventTarget
&& mDeepestEnterEventTarget
&&
395 nsContentUtils::ContentIsFlattenedTreeDescendantOf(
396 aOriginalOverTargetInComposedDoc
, mDeepestEnterEventTarget
))) {
397 mDeepestEnterEventTarget
= aOriginalOverTargetInComposedDoc
;
398 mDeepestEnterEventTargetIsOverEventTarget
= true;
399 LogModule
* const logModule
= mType
== BoundaryEventType::Mouse
401 : sPointerBoundaryLog
;
402 MOZ_LOG(logModule
, LogLevel::Info
,
403 ("The \"over\" event target (%p) is restored",
404 mDeepestEnterEventTarget
.get()));
408 /******************************************************************/
409 /* mozilla::EventStateManager */
410 /******************************************************************/
412 static uint32_t sESMInstanceCount
= 0;
414 bool EventStateManager::sNormalLMouseEventInProcess
= false;
415 int16_t EventStateManager::sCurrentMouseBtn
= MouseButton::eNotPressed
;
416 EventStateManager
* EventStateManager::sActiveESM
= nullptr;
417 EventStateManager
* EventStateManager::sCursorSettingManager
= nullptr;
418 AutoWeakFrame
EventStateManager::sLastDragOverFrame
= nullptr;
419 LayoutDeviceIntPoint
EventStateManager::sPreLockScreenPoint
=
420 LayoutDeviceIntPoint(0, 0);
421 LayoutDeviceIntPoint
EventStateManager::sLastRefPoint
= kInvalidRefPoint
;
422 CSSIntPoint
EventStateManager::sLastScreenPoint
= CSSIntPoint(0, 0);
423 LayoutDeviceIntPoint
EventStateManager::sSynthCenteringPoint
= kInvalidRefPoint
;
424 CSSIntPoint
EventStateManager::sLastClientPoint
= CSSIntPoint(0, 0);
425 nsCOMPtr
<nsIContent
> EventStateManager::sDragOverContent
= nullptr;
427 EventStateManager::WheelPrefs
* EventStateManager::WheelPrefs::sInstance
=
429 EventStateManager::DeltaAccumulator
*
430 EventStateManager::DeltaAccumulator::sInstance
= nullptr;
432 constexpr const StyleCursorKind kInvalidCursorKind
=
433 static_cast<StyleCursorKind
>(255);
435 EventStateManager::EventStateManager()
436 : mLockCursor(kInvalidCursorKind
),
437 mCurrentTarget(nullptr),
438 // init d&d gesture state machine variables
439 mGestureDownPoint(0, 0),
440 mGestureModifiers(0),
441 mGestureDownButtons(0),
442 mPresContext(nullptr),
443 mShouldAlwaysUseLineDeltas(false),
444 mShouldAlwaysUseLineDeltasInitialized(false),
445 mGestureDownInTextControl(false),
447 m_haveShutdown(false) {
448 if (sESMInstanceCount
== 0) {
449 gUserInteractionTimerCallback
= new UITimerCallback();
450 if (gUserInteractionTimerCallback
) NS_ADDREF(gUserInteractionTimerCallback
);
451 UpdateUserActivityTimer();
456 nsresult
EventStateManager::UpdateUserActivityTimer() {
457 if (!gUserInteractionTimerCallback
) return NS_OK
;
459 if (!gUserInteractionTimer
) {
460 gUserInteractionTimer
= NS_NewTimer().take();
463 if (gUserInteractionTimer
) {
464 gUserInteractionTimer
->InitWithCallback(
465 gUserInteractionTimerCallback
,
466 StaticPrefs::dom_events_user_interaction_interval(),
467 nsITimer::TYPE_ONE_SHOT
);
472 nsresult
EventStateManager::Init() {
473 nsCOMPtr
<nsIObserverService
> observerService
=
474 mozilla::services::GetObserverService();
475 if (!observerService
) return NS_ERROR_FAILURE
;
477 observerService
->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID
, true);
482 bool EventStateManager::ShouldAlwaysUseLineDeltas() {
483 if (MOZ_UNLIKELY(!mShouldAlwaysUseLineDeltasInitialized
)) {
484 mShouldAlwaysUseLineDeltasInitialized
= true;
485 mShouldAlwaysUseLineDeltas
=
486 !StaticPrefs::dom_event_wheel_deltaMode_lines_disabled();
487 if (!mShouldAlwaysUseLineDeltas
&& mDocument
) {
488 if (nsIPrincipal
* principal
=
489 mDocument
->GetPrincipalForPrefBasedHacks()) {
490 mShouldAlwaysUseLineDeltas
= principal
->IsURIInPrefList(
491 "dom.event.wheel-deltaMode-lines.always-enabled");
495 return mShouldAlwaysUseLineDeltas
;
498 EventStateManager::~EventStateManager() {
499 ReleaseCurrentIMEContentObserver();
501 if (sActiveESM
== this) {
502 sActiveESM
= nullptr;
505 if (StaticPrefs::ui_click_hold_context_menus()) {
506 KillClickHoldTimer();
509 if (sCursorSettingManager
== this) {
510 sCursorSettingManager
= nullptr;
514 if (sESMInstanceCount
== 0) {
515 WheelTransaction::Shutdown();
516 if (gUserInteractionTimerCallback
) {
517 gUserInteractionTimerCallback
->Notify(nullptr);
518 NS_RELEASE(gUserInteractionTimerCallback
);
520 if (gUserInteractionTimer
) {
521 gUserInteractionTimer
->Cancel();
522 NS_RELEASE(gUserInteractionTimer
);
524 WheelPrefs::Shutdown();
525 DeltaAccumulator::Shutdown();
528 if (sDragOverContent
&& sDragOverContent
->OwnerDoc() == mDocument
) {
529 sDragOverContent
= nullptr;
532 if (!m_haveShutdown
) {
535 // Don't remove from Observer service in Shutdown because Shutdown also
536 // gets called from xpcom shutdown observer. And we don't want to remove
537 // from the service in that case.
539 nsCOMPtr
<nsIObserverService
> observerService
=
540 mozilla::services::GetObserverService();
541 if (observerService
) {
542 observerService
->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID
);
547 nsresult
EventStateManager::Shutdown() {
548 m_haveShutdown
= true;
553 EventStateManager::Observe(nsISupports
* aSubject
, const char* aTopic
,
554 const char16_t
* someData
) {
555 if (!nsCRT::strcmp(aTopic
, NS_XPCOM_SHUTDOWN_OBSERVER_ID
)) {
562 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(EventStateManager
)
563 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIObserver
)
564 NS_INTERFACE_MAP_ENTRY(nsIObserver
)
565 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference
)
568 NS_IMPL_CYCLE_COLLECTING_ADDREF(EventStateManager
)
569 NS_IMPL_CYCLE_COLLECTING_RELEASE(EventStateManager
)
571 NS_IMPL_CYCLE_COLLECTION_WEAK(EventStateManager
, mCurrentTargetContent
,
572 mGestureDownContent
, mGestureDownFrameOwner
,
573 mLastLeftMouseDownInfo
.mLastMouseDownContent
,
574 mLastMiddleMouseDownInfo
.mLastMouseDownContent
,
575 mLastRightMouseDownInfo
.mLastMouseDownContent
,
576 mActiveContent
, mHoverContent
, mURLTargetContent
,
577 mPopoverPointerDownTarget
, mMouseEnterLeaveHelper
,
578 mPointersEnterLeaveHelper
, mDocument
,
579 mIMEContentObserver
, mAccessKeys
)
581 void EventStateManager::ReleaseCurrentIMEContentObserver() {
582 if (mIMEContentObserver
) {
583 mIMEContentObserver
->DisconnectFromEventStateManager();
585 mIMEContentObserver
= nullptr;
588 void EventStateManager::OnStartToObserveContent(
589 IMEContentObserver
* aIMEContentObserver
) {
590 if (mIMEContentObserver
== aIMEContentObserver
) {
593 ReleaseCurrentIMEContentObserver();
594 mIMEContentObserver
= aIMEContentObserver
;
597 void EventStateManager::OnStopObservingContent(
598 IMEContentObserver
* aIMEContentObserver
) {
599 aIMEContentObserver
->DisconnectFromEventStateManager();
600 NS_ENSURE_TRUE_VOID(mIMEContentObserver
== aIMEContentObserver
);
601 mIMEContentObserver
= nullptr;
604 void EventStateManager::TryToFlushPendingNotificationsToIME() {
605 if (mIMEContentObserver
) {
606 mIMEContentObserver
->TryToFlushPendingNotifications(true);
610 static bool IsMessageMouseUserActivity(EventMessage aMessage
) {
611 return aMessage
== eMouseMove
|| aMessage
== eMouseUp
||
612 aMessage
== eMouseDown
|| aMessage
== eMouseAuxClick
||
613 aMessage
== eMouseDoubleClick
|| aMessage
== eMouseClick
||
614 aMessage
== eMouseActivate
|| aMessage
== eMouseLongTap
;
617 static bool IsMessageGamepadUserActivity(EventMessage aMessage
) {
618 return aMessage
== eGamepadButtonDown
|| aMessage
== eGamepadButtonUp
||
619 aMessage
== eGamepadAxisMove
;
623 bool EventStateManager::IsKeyboardEventUserActivity(WidgetEvent
* aEvent
) {
624 // We ignore things that shouldn't cause popups, but also things that look
625 // like shortcut presses. In some obscure cases these may actually be
626 // website input, but any meaningful website will have other input anyway,
627 // and we can't very well tell whether shortcut input was supposed to be
628 // directed at chrome or the document.
630 WidgetKeyboardEvent
* keyEvent
= aEvent
->AsKeyboardEvent();
631 // Access keys should be treated as page interaction.
632 if (keyEvent
->ModifiersMatchWithAccessKey(AccessKeyType::eContent
)) {
635 if (!keyEvent
->CanTreatAsUserInput() || keyEvent
->IsControl() ||
636 keyEvent
->IsMeta() || keyEvent
->IsAlt()) {
639 // Deal with function keys:
640 switch (keyEvent
->mKeyNameIndex
) {
641 case KEY_NAME_INDEX_F1
:
642 case KEY_NAME_INDEX_F2
:
643 case KEY_NAME_INDEX_F3
:
644 case KEY_NAME_INDEX_F4
:
645 case KEY_NAME_INDEX_F5
:
646 case KEY_NAME_INDEX_F6
:
647 case KEY_NAME_INDEX_F7
:
648 case KEY_NAME_INDEX_F8
:
649 case KEY_NAME_INDEX_F9
:
650 case KEY_NAME_INDEX_F10
:
651 case KEY_NAME_INDEX_F11
:
652 case KEY_NAME_INDEX_F12
:
653 case KEY_NAME_INDEX_F13
:
654 case KEY_NAME_INDEX_F14
:
655 case KEY_NAME_INDEX_F15
:
656 case KEY_NAME_INDEX_F16
:
657 case KEY_NAME_INDEX_F17
:
658 case KEY_NAME_INDEX_F18
:
659 case KEY_NAME_INDEX_F19
:
660 case KEY_NAME_INDEX_F20
:
661 case KEY_NAME_INDEX_F21
:
662 case KEY_NAME_INDEX_F22
:
663 case KEY_NAME_INDEX_F23
:
664 case KEY_NAME_INDEX_F24
:
671 static void OnTypingInteractionEnded() {
672 // We don't consider a single keystroke to be typing.
673 if (gTypingInteractionKeyPresses
> 1) {
674 gTypingInteraction
.mInteractionCount
+= gTypingInteractionKeyPresses
;
675 gTypingInteraction
.mInteractionTimeInMilliseconds
+= static_cast<uint32_t>(
676 std::ceil((gTypingEndTime
- gTypingStartTime
).ToMilliseconds()));
679 gTypingInteractionKeyPresses
= 0;
680 gTypingStartTime
= TimeStamp();
681 gTypingEndTime
= TimeStamp();
684 static void HandleKeyUpInteraction(WidgetKeyboardEvent
* aKeyEvent
) {
685 if (EventStateManager::IsKeyboardEventUserActivity(aKeyEvent
)) {
686 TimeStamp now
= TimeStamp::Now();
687 if (gTypingEndTime
.IsNull()) {
688 gTypingEndTime
= now
;
690 TimeDuration delay
= now
- gTypingEndTime
;
691 // Has it been too long since the last keystroke to be considered typing?
692 if (gTypingInteractionKeyPresses
> 0 &&
694 TimeDuration::FromMilliseconds(
695 StaticPrefs::browser_places_interactions_typing_timeout_ms())) {
696 OnTypingInteractionEnded();
698 gTypingInteractionKeyPresses
++;
699 if (gTypingStartTime
.IsNull()) {
700 gTypingStartTime
= now
;
702 gTypingEndTime
= now
;
706 nsresult
EventStateManager::PreHandleEvent(nsPresContext
* aPresContext
,
708 nsIFrame
* aTargetFrame
,
709 nsIContent
* aTargetContent
,
710 nsEventStatus
* aStatus
,
711 nsIContent
* aOverrideClickTarget
) {
712 NS_ENSURE_ARG_POINTER(aStatus
);
713 NS_ENSURE_ARG(aPresContext
);
715 NS_ERROR("aEvent is null. This should never happen.");
716 return NS_ERROR_NULL_POINTER
;
719 NS_WARNING_ASSERTION(
720 !aTargetFrame
|| !aTargetFrame
->GetContent() ||
721 aTargetFrame
->GetContent() == aTargetContent
||
722 aTargetFrame
->GetContent()->GetFlattenedTreeParent() ==
724 aTargetFrame
->IsGeneratedContentFrame(),
725 "aTargetFrame should be related with aTargetContent");
727 if (aTargetFrame
&& aTargetFrame
->IsGeneratedContentFrame()) {
728 nsCOMPtr
<nsIContent
> targetContent
;
729 aTargetFrame
->GetContentForEvent(aEvent
, getter_AddRefs(targetContent
));
730 MOZ_ASSERT(aTargetContent
== targetContent
,
731 "Unexpected target for generated content frame!");
735 mCurrentTarget
= aTargetFrame
;
736 mCurrentTargetContent
= nullptr;
738 // Do not take account eMouseEnterIntoWidget/ExitFromWidget so that loading
739 // a page when user is not active doesn't change the state to active.
740 WidgetMouseEvent
* mouseEvent
= aEvent
->AsMouseEvent();
741 if (aEvent
->IsTrusted() &&
742 ((mouseEvent
&& mouseEvent
->IsReal() &&
743 IsMessageMouseUserActivity(mouseEvent
->mMessage
)) ||
744 aEvent
->mClass
== eWheelEventClass
||
745 aEvent
->mClass
== ePointerEventClass
||
746 aEvent
->mClass
== eTouchEventClass
||
747 aEvent
->mClass
== eKeyboardEventClass
||
748 (aEvent
->mClass
== eDragEventClass
&& aEvent
->mMessage
== eDrop
) ||
749 IsMessageGamepadUserActivity(aEvent
->mMessage
))) {
750 if (gMouseOrKeyboardEventCounter
== 0) {
751 nsCOMPtr
<nsIObserverService
> obs
=
752 mozilla::services::GetObserverService();
754 obs
->NotifyObservers(nullptr, "user-interaction-active", nullptr);
755 UpdateUserActivityTimer();
758 ++gMouseOrKeyboardEventCounter
;
760 nsCOMPtr
<nsINode
> node
= aTargetContent
;
762 ((aEvent
->mMessage
== eKeyUp
&& IsKeyboardEventUserActivity(aEvent
)) ||
763 aEvent
->mMessage
== eMouseUp
|| aEvent
->mMessage
== eWheel
||
764 aEvent
->mMessage
== eTouchEnd
|| aEvent
->mMessage
== ePointerUp
||
765 aEvent
->mMessage
== eDrop
)) {
766 Document
* doc
= node
->OwnerDoc();
768 doc
->SetUserHasInteracted();
769 doc
= nsContentUtils::IsChildOfSameType(doc
)
770 ? doc
->GetInProcessParentDocument()
776 WheelTransaction::OnEvent(aEvent
);
778 // Focus events don't necessarily need a frame.
779 if (!mCurrentTarget
&& !aTargetContent
) {
780 NS_ERROR("mCurrentTarget and aTargetContent are null");
781 return NS_ERROR_NULL_POINTER
;
784 if (aEvent
->HasDragEventMessage() && PointerLockManager::IsLocked()) {
785 NS_ASSERTION(PointerLockManager::IsLocked(),
786 "Pointer is locked. Drag events should be suppressed when "
787 "the pointer is locked.");
790 // Store last known screenPoint and clientPoint so pointer lock
791 // can use these values as constants.
792 if (aEvent
->IsTrusted() &&
793 ((mouseEvent
&& mouseEvent
->IsReal()) ||
794 aEvent
->mClass
== eWheelEventClass
) &&
795 !PointerLockManager::IsLocked()) {
796 // XXX Probably doesn't matter much, but storing these in CSS pixels instead
797 // of device pixels means behavior can be a bit odd if you zoom while
800 Event::GetScreenCoords(aPresContext
, aEvent
, aEvent
->mRefPoint
)
802 sLastClientPoint
= Event::GetClientCoords(
803 aPresContext
, aEvent
, aEvent
->mRefPoint
, CSSIntPoint(0, 0));
806 *aStatus
= nsEventStatus_eIgnore
;
808 if (aEvent
->mClass
== eQueryContentEventClass
) {
809 HandleQueryContentEvent(aEvent
->AsQueryContentEvent());
813 WidgetTouchEvent
* touchEvent
= aEvent
->AsTouchEvent();
814 if (touchEvent
&& mInTouchDrag
) {
815 if (touchEvent
->mMessage
== eTouchMove
) {
816 GenerateDragGesture(aPresContext
, touchEvent
);
818 mInTouchDrag
= false;
819 StopTrackingDragGesture(true);
823 switch (aEvent
->mMessage
) {
825 if (PointerLockManager::IsLocked()) {
826 return NS_ERROR_DOM_INVALID_STATE_ERR
;
829 case eMouseTouchDrag
:
831 BeginTrackingDragGesture(aPresContext
, mouseEvent
, aTargetFrame
);
834 switch (mouseEvent
->mButton
) {
835 case MouseButton::ePrimary
:
836 BeginTrackingDragGesture(aPresContext
, mouseEvent
, aTargetFrame
);
837 mLastLeftMouseDownInfo
.mClickCount
= mouseEvent
->mClickCount
;
838 SetClickCount(mouseEvent
, aStatus
);
839 sNormalLMouseEventInProcess
= true;
841 case MouseButton::eMiddle
:
842 mLastMiddleMouseDownInfo
.mClickCount
= mouseEvent
->mClickCount
;
843 SetClickCount(mouseEvent
, aStatus
);
845 case MouseButton::eSecondary
:
846 mLastRightMouseDownInfo
.mClickCount
= mouseEvent
->mClickCount
;
847 SetClickCount(mouseEvent
, aStatus
);
850 NotifyTargetUserActivation(aEvent
, aTargetContent
);
854 switch (mouseEvent
->mButton
) {
855 case MouseButton::ePrimary
:
856 if (StaticPrefs::ui_click_hold_context_menus()) {
857 KillClickHoldTimer();
859 mInTouchDrag
= false;
860 StopTrackingDragGesture(true);
861 sNormalLMouseEventInProcess
= false;
862 // then fall through...
864 case MouseButton::eSecondary
:
865 case MouseButton::eMiddle
:
866 RefPtr
<EventStateManager
> esm
=
867 ESMFromContentOrThis(aOverrideClickTarget
);
868 esm
->SetClickCount(mouseEvent
, aStatus
, aOverrideClickTarget
);
873 case eMouseEnterIntoWidget
:
874 PointerEventHandler::UpdateActivePointerState(mouseEvent
, aTargetContent
);
875 // In some cases on e10s eMouseEnterIntoWidget
876 // event was sent twice into child process of content.
877 // (From specific widget code (sending is not permanent) and
878 // from ESM::DispatchMouseOrPointerEvent (sending is permanent)).
879 // IsCrossProcessForwardingStopped() helps to suppress sending accidental
880 // event from widget code.
881 aEvent
->StopCrossProcessForwarding();
883 case eMouseExitFromWidget
:
884 // If this is a remote frame, we receive eMouseExitFromWidget from the
885 // parent the mouse exits our content. Since the parent may update the
886 // cursor while the mouse is outside our frame, and since PuppetWidget
887 // caches the current cursor internally, re-entering our content (say from
888 // over a window edge) wont update the cursor if the cached value and the
889 // current cursor match. So when the mouse exits a remote frame, clear the
890 // cached widget cursor so a proper update will occur when the mouse
892 if (XRE_IsContentProcess()) {
893 ClearCachedWidgetCursor(mCurrentTarget
);
896 // IsCrossProcessForwardingStopped() helps to suppress double event
897 // sending into process of content. For more information see comment
898 // above, at eMouseEnterIntoWidget case.
899 aEvent
->StopCrossProcessForwarding();
901 // If the event is not a top-level window or puppet widget exit, then it's
902 // not really an exit --- we may have traversed widget boundaries but
903 // we're still in our toplevel window or puppet widget.
904 if (mouseEvent
->mExitFrom
.value() !=
905 WidgetMouseEvent::ePlatformTopLevel
&&
906 mouseEvent
->mExitFrom
.value() != WidgetMouseEvent::ePuppet
) {
907 // Treat it as a synthetic move so we don't generate spurious
908 // "exit" or "move" events. Any necessary "out" or "over" events
909 // will be generated by GenerateMouseEnterExit
910 mouseEvent
->mMessage
= eMouseMove
;
911 mouseEvent
->mReason
= WidgetMouseEvent::eSynthesized
;
912 // then fall through...
914 MOZ_ASSERT_IF(XRE_IsParentProcess(),
915 mouseEvent
->mExitFrom
.value() ==
916 WidgetMouseEvent::ePlatformTopLevel
);
917 MOZ_ASSERT_IF(XRE_IsContentProcess(), mouseEvent
->mExitFrom
.value() ==
918 WidgetMouseEvent::ePuppet
);
919 // We should synthetize corresponding pointer events
920 GeneratePointerEnterExit(ePointerLeave
, mouseEvent
);
921 GenerateMouseEnterExit(mouseEvent
);
922 // This is really an exit and should stop here
923 aEvent
->mMessage
= eVoidEvent
;
929 if (aEvent
->mMessage
== ePointerDown
) {
930 PointerEventHandler::UpdateActivePointerState(mouseEvent
,
932 PointerEventHandler::ImplicitlyCapturePointer(aTargetFrame
, aEvent
);
933 if (mouseEvent
->mInputSource
!= MouseEvent_Binding::MOZ_SOURCE_TOUCH
) {
934 NotifyTargetUserActivation(aEvent
, aTargetContent
);
937 LightDismissOpenPopovers(aEvent
, aTargetContent
);
942 PointerEventHandler::IsDragAndDropEnabled(*mouseEvent
)) {
943 GenerateDragGesture(aPresContext
, mouseEvent
);
945 // on the Mac, GenerateDragGesture() may not return until the drag
946 // has completed and so |aTargetFrame| may have been deleted (moving
947 // a bookmark, for example). If this is the case, however, we know
948 // that ClearFrameRefs() has been called and it cleared out
949 // |mCurrentTarget|. As a result, we should pass |mCurrentTarget|
950 // into UpdateCursor().
951 UpdateCursor(aPresContext
, mouseEvent
, mCurrentTarget
, aStatus
);
953 UpdateLastRefPointOfMouseEvent(mouseEvent
);
954 if (PointerLockManager::IsLocked()) {
955 ResetPointerToWindowCenterWhilePointerLocked(mouseEvent
);
957 UpdateLastPointerPosition(mouseEvent
);
959 GenerateMouseEnterExit(mouseEvent
);
960 // Flush pending layout changes, so that later mouse move events
961 // will go to the right nodes.
962 FlushLayout(aPresContext
);
966 LightDismissOpenPopovers(aEvent
, aTargetContent
);
968 case ePointerGotCapture
:
969 GenerateMouseEnterExit(mouseEvent
);
972 if (StaticPrefs::ui_click_hold_context_menus()) {
973 // an external drag gesture event came in, not generated internally
974 // by Gecko. Make sure we get rid of the click-hold timer.
975 KillClickHoldTimer();
979 WidgetDragEvent
* dragEvent
= aEvent
->AsDragEvent();
980 MOZ_ASSERT(dragEvent
);
981 if (dragEvent
->mFlags
.mIsSynthesizedForTests
) {
982 dragEvent
->InitDropEffectForTests();
984 // Send the enter/exit events before eDrop.
985 GenerateDragDropEnterExit(aPresContext
, dragEvent
);
989 if (aEvent
->mFlags
.mIsSynthesizedForTests
) {
990 MOZ_ASSERT(aEvent
->AsDragEvent());
991 aEvent
->AsDragEvent()->InitDropEffectForTests();
996 WidgetKeyboardEvent
* keyEvent
= aEvent
->AsKeyboardEvent();
997 if (keyEvent
->ModifiersMatchWithAccessKey(AccessKeyType::eChrome
) ||
998 keyEvent
->ModifiersMatchWithAccessKey(AccessKeyType::eContent
)) {
999 // If the eKeyPress event will be sent to a remote process, this
1000 // process needs to wait reply from the remote process for checking if
1001 // preceding eKeyDown event is consumed. If preceding eKeyDown event
1002 // is consumed in the remote process, BrowserChild won't send the event
1003 // back to this process. So, only when this process receives a reply
1004 // eKeyPress event in BrowserParent, we should handle accesskey in this
1006 if (IsTopLevelRemoteTarget(GetFocusedElement())) {
1007 // However, if there is no accesskey target for the key combination,
1008 // we don't need to wait reply from the remote process. Otherwise,
1009 // Mark the event as waiting reply from remote process and stop
1010 // propagation in this process.
1011 if (CheckIfEventMatchesAccessKey(keyEvent
, aPresContext
)) {
1012 keyEvent
->StopPropagation();
1013 keyEvent
->MarkAsWaitingReplyFromRemoteProcess();
1016 // If the event target is in this process, we can handle accesskey now
1017 // since if preceding eKeyDown event was consumed, eKeyPress event
1018 // won't be dispatched by widget. So, coming eKeyPress event means
1019 // that the preceding eKeyDown event wasn't consumed in this case.
1021 AutoTArray
<uint32_t, 10> accessCharCodes
;
1022 keyEvent
->GetAccessKeyCandidates(accessCharCodes
);
1024 if (HandleAccessKey(keyEvent
, aPresContext
, accessCharCodes
)) {
1025 *aStatus
= nsEventStatus_eConsumeNoDefault
;
1030 // then fall through...
1033 if (aEvent
->mMessage
== eKeyDown
) {
1034 NotifyTargetUserActivation(aEvent
, aTargetContent
);
1038 Element
* element
= GetFocusedElement();
1040 mCurrentTargetContent
= element
;
1043 // NOTE: Don't refer TextComposition::IsComposing() since UI Events
1044 // defines that KeyboardEvent.isComposing is true when it's
1045 // dispatched after compositionstart and compositionend.
1046 // TextComposition::IsComposing() is false even before
1047 // compositionend if there is no composing string.
1048 // And also don't expose other document's composition state.
1049 // A native IME context is typically shared by multiple documents.
1050 // So, don't use GetTextCompositionFor(nsIWidget*) here.
1051 RefPtr
<TextComposition
> composition
=
1052 IMEStateManager::GetTextCompositionFor(aPresContext
);
1053 aEvent
->AsKeyboardEvent()->mIsComposing
= !!composition
;
1055 // Widget may need to perform default action for specific keyboard
1056 // event if it's not consumed. In this case, widget has already marked
1057 // the event as "waiting reply from remote process". However, we need
1058 // to reset it if the target (focused content) isn't in a remote process
1059 // because PresShell needs to check if it's marked as so before
1060 // dispatching events into the DOM tree.
1061 if (aEvent
->IsWaitingReplyFromRemoteProcess() &&
1062 !aEvent
->PropagationStopped() && !IsTopLevelRemoteTarget(element
)) {
1063 aEvent
->ResetWaitingReplyFromRemoteProcessState();
1067 case eWheelOperationStart
:
1068 case eWheelOperationEnd
: {
1069 NS_ASSERTION(aEvent
->IsTrusted(),
1070 "Untrusted wheel event shouldn't be here");
1071 using DeltaModeCheckingState
= WidgetWheelEvent::DeltaModeCheckingState
;
1073 if (Element
* element
= GetFocusedElement()) {
1074 mCurrentTargetContent
= element
;
1077 if (aEvent
->mMessage
!= eWheel
) {
1081 WidgetWheelEvent
* wheelEvent
= aEvent
->AsWheelEvent();
1082 WheelPrefs::GetInstance()->ApplyUserPrefsToDelta(wheelEvent
);
1084 // If we won't dispatch a DOM event for this event, nothing to do anymore.
1085 if (!wheelEvent
->IsAllowedToDispatchDOMEvent()) {
1089 if (StaticPrefs::dom_event_wheel_deltaMode_lines_always_disabled()) {
1090 wheelEvent
->mDeltaModeCheckingState
= DeltaModeCheckingState::Unchecked
;
1091 } else if (ShouldAlwaysUseLineDeltas()) {
1092 wheelEvent
->mDeltaModeCheckingState
= DeltaModeCheckingState::Checked
;
1094 wheelEvent
->mDeltaModeCheckingState
= DeltaModeCheckingState::Unknown
;
1097 // Init lineOrPageDelta values for line scroll events for some devices
1098 // on some platforms which might dispatch wheel events which don't
1099 // have lineOrPageDelta values. And also, if delta values are
1100 // customized by prefs, this recomputes them.
1101 DeltaAccumulator::GetInstance()->InitLineOrPageDelta(aTargetFrame
, this,
1104 case eSetSelection
: {
1105 RefPtr
<Element
> focuedElement
= GetFocusedElement();
1106 IMEStateManager::HandleSelectionEvent(aPresContext
, focuedElement
,
1107 aEvent
->AsSelectionEvent());
1110 case eContentCommandCut
:
1111 case eContentCommandCopy
:
1112 case eContentCommandPaste
:
1113 case eContentCommandDelete
:
1114 case eContentCommandUndo
:
1115 case eContentCommandRedo
:
1116 case eContentCommandPasteTransferable
:
1117 case eContentCommandLookUpDictionary
:
1118 DoContentCommandEvent(aEvent
->AsContentCommandEvent());
1120 case eContentCommandInsertText
:
1121 DoContentCommandInsertTextEvent(aEvent
->AsContentCommandEvent());
1123 case eContentCommandScroll
:
1124 DoContentCommandScrollEvent(aEvent
->AsContentCommandEvent());
1126 case eCompositionStart
:
1127 if (aEvent
->IsTrusted()) {
1128 // If the event is trusted event, set the selected text to data of
1129 // composition event.
1130 WidgetCompositionEvent
* compositionEvent
= aEvent
->AsCompositionEvent();
1131 WidgetQueryContentEvent
querySelectedTextEvent(
1132 true, eQuerySelectedText
, compositionEvent
->mWidget
);
1133 HandleQueryContentEvent(&querySelectedTextEvent
);
1134 if (querySelectedTextEvent
.FoundSelection()) {
1135 compositionEvent
->mData
= querySelectedTextEvent
.mReply
->DataRef();
1137 NS_ASSERTION(querySelectedTextEvent
.Succeeded(),
1138 "Failed to get selected text");
1142 SetGestureDownPoint(aEvent
->AsTouchEvent());
1145 NotifyTargetUserActivation(aEvent
, aTargetContent
);
1153 // Returns true if this event is likely an user activation for a link or
1154 // a link-like button, where modifier keys are likely be used for controlling
1155 // where the link is opened.
1157 // The modifiers associated with the user activation is used for controlling
1158 // where the `window.open` is opened into.
1159 static bool CanReflectModifiersToUserActivation(WidgetInputEvent
* aEvent
) {
1160 MOZ_ASSERT(aEvent
->mMessage
== eKeyDown
|| aEvent
->mMessage
== eMouseDown
||
1161 aEvent
->mMessage
== ePointerDown
|| aEvent
->mMessage
== eTouchEnd
);
1163 WidgetKeyboardEvent
* keyEvent
= aEvent
->AsKeyboardEvent();
1165 return keyEvent
->CanReflectModifiersToUserActivation();
1171 void EventStateManager::NotifyTargetUserActivation(WidgetEvent
* aEvent
,
1172 nsIContent
* aTargetContent
) {
1173 if (!aEvent
->IsTrusted()) {
1177 WidgetMouseEvent
* mouseEvent
= aEvent
->AsMouseEvent();
1178 if (mouseEvent
&& !mouseEvent
->IsReal()) {
1182 nsCOMPtr
<nsINode
> node
= aTargetContent
;
1187 Document
* doc
= node
->OwnerDoc();
1192 // Don't gesture activate for key events for keys which are likely
1193 // to be interaction with the browser, OS.
1194 WidgetKeyboardEvent
* keyEvent
= aEvent
->AsKeyboardEvent();
1195 if (keyEvent
&& !keyEvent
->CanUserGestureActivateTarget()) {
1199 // Touch gestures that end outside the drag target were touches that turned
1200 // into scroll/pan/swipe actions. We don't want to gesture activate on such
1201 // actions, we want to only gesture activate on touches that are taps.
1202 // That is, touches that end in roughly the same place that they started.
1203 if (aEvent
->mMessage
== eTouchEnd
&& aEvent
->AsTouchEvent() &&
1204 IsEventOutsideDragThreshold(aEvent
->AsTouchEvent())) {
1208 // Do not treat the click on scrollbar as a user interaction with the web
1210 if (StaticPrefs::dom_user_activation_ignore_scrollbars() &&
1211 (aEvent
->mMessage
== eMouseDown
|| aEvent
->mMessage
== ePointerDown
) &&
1212 aTargetContent
->IsInNativeAnonymousSubtree()) {
1213 nsIContent
* current
= aTargetContent
;
1215 nsIContent
* root
= current
->GetClosestNativeAnonymousSubtreeRoot();
1219 if (root
->IsXULElement(nsGkAtoms::scrollbar
)) {
1222 current
= root
->GetParent();
1226 MOZ_ASSERT(aEvent
->mMessage
== eKeyDown
|| aEvent
->mMessage
== eMouseDown
||
1227 aEvent
->mMessage
== ePointerDown
|| aEvent
->mMessage
== eTouchEnd
);
1228 UserActivation::Modifiers modifiers
;
1229 if (WidgetInputEvent
* inputEvent
= aEvent
->AsInputEvent()) {
1230 if (CanReflectModifiersToUserActivation(inputEvent
)) {
1231 if (inputEvent
->IsShift()) {
1232 modifiers
.SetShift();
1234 if (inputEvent
->IsMeta()) {
1235 modifiers
.SetMeta();
1237 if (inputEvent
->IsControl()) {
1238 modifiers
.SetControl();
1240 if (inputEvent
->IsAlt()) {
1245 doc
->NotifyUserGestureActivation(modifiers
);
1248 // https://html.spec.whatwg.org/multipage/popover.html#popover-light-dismiss
1249 void EventStateManager::LightDismissOpenPopovers(WidgetEvent
* aEvent
,
1250 nsIContent
* aTargetContent
) {
1251 MOZ_ASSERT(aEvent
->mMessage
== ePointerDown
|| aEvent
->mMessage
== ePointerUp
,
1252 "Light dismiss must be called for pointer up/down only");
1254 if (!StaticPrefs::dom_element_popover_enabled() || !aEvent
->IsTrusted() ||
1259 Element
* topmostPopover
= aTargetContent
->OwnerDoc()->GetTopmostAutoPopover();
1260 if (!topmostPopover
) {
1264 // Pointerdown: set document's popover pointerdown target to the result of
1265 // running topmost clicked popover given target.
1266 if (aEvent
->mMessage
== ePointerDown
) {
1267 mPopoverPointerDownTarget
= aTargetContent
->GetTopmostClickedPopover();
1271 // Pointerup: hide open popovers.
1272 RefPtr
<nsINode
> ancestor
= aTargetContent
->GetTopmostClickedPopover();
1273 bool sameTarget
= mPopoverPointerDownTarget
== ancestor
;
1274 mPopoverPointerDownTarget
= nullptr;
1280 ancestor
= aTargetContent
->OwnerDoc();
1282 RefPtr
<Document
> doc(ancestor
->OwnerDoc());
1283 doc
->HideAllPopoversUntil(*ancestor
, false, true);
1286 already_AddRefed
<EventStateManager
> EventStateManager::ESMFromContentOrThis(
1287 nsIContent
* aContent
) {
1289 PresShell
* presShell
= aContent
->OwnerDoc()->GetPresShell();
1291 nsPresContext
* prescontext
= presShell
->GetPresContext();
1293 RefPtr
<EventStateManager
> esm
= prescontext
->EventStateManager();
1295 return esm
.forget();
1301 RefPtr
<EventStateManager
> esm
= this;
1302 return esm
.forget();
1305 EventStateManager::LastMouseDownInfo
& EventStateManager::GetLastMouseDownInfo(
1308 case MouseButton::ePrimary
:
1309 return mLastLeftMouseDownInfo
;
1310 case MouseButton::eMiddle
:
1311 return mLastMiddleMouseDownInfo
;
1312 case MouseButton::eSecondary
:
1313 return mLastRightMouseDownInfo
;
1315 MOZ_ASSERT_UNREACHABLE("This button shouldn't use this method");
1316 return mLastLeftMouseDownInfo
;
1320 void EventStateManager::HandleQueryContentEvent(
1321 WidgetQueryContentEvent
* aEvent
) {
1322 switch (aEvent
->mMessage
) {
1323 case eQuerySelectedText
:
1324 case eQueryTextContent
:
1325 case eQueryCaretRect
:
1326 case eQueryTextRect
:
1327 case eQueryEditorRect
:
1328 if (!IsTargetCrossProcess(aEvent
)) {
1331 // Will not be handled locally, remote the event
1332 GetCrossProcessTarget()->HandleQueryContentEvent(*aEvent
);
1334 // Following events have not been supported in e10s mode yet.
1335 case eQueryContentState
:
1336 case eQuerySelectionAsTransferable
:
1337 case eQueryCharacterAtPoint
:
1338 case eQueryDOMWidgetHittest
:
1339 case eQueryTextRectArray
:
1345 // If there is an IMEContentObserver, we need to handle QueryContentEvent
1347 if (mIMEContentObserver
) {
1348 RefPtr
<IMEContentObserver
> contentObserver
= mIMEContentObserver
;
1349 contentObserver
->HandleQueryContentEvent(aEvent
);
1353 ContentEventHandler
handler(mPresContext
);
1354 handler
.HandleQueryContentEvent(aEvent
);
1357 static AccessKeyType
GetAccessKeyTypeFor(nsISupports
* aDocShell
) {
1358 nsCOMPtr
<nsIDocShellTreeItem
> treeItem(do_QueryInterface(aDocShell
));
1360 return AccessKeyType::eNone
;
1363 switch (treeItem
->ItemType()) {
1364 case nsIDocShellTreeItem::typeChrome
:
1365 return AccessKeyType::eChrome
;
1366 case nsIDocShellTreeItem::typeContent
:
1367 return AccessKeyType::eContent
;
1369 return AccessKeyType::eNone
;
1373 static bool IsAccessKeyTarget(Element
* aElement
, nsAString
& aKey
) {
1374 // Use GetAttr because we want Unicode case=insensitive matching
1375 // XXXbz shouldn't this be case-sensitive, per spec?
1376 nsString contentKey
;
1377 if (!aElement
|| !aElement
->GetAttr(nsGkAtoms::accesskey
, contentKey
) ||
1378 !contentKey
.Equals(aKey
, nsCaseInsensitiveStringComparator
)) {
1382 if (!aElement
->IsXULElement()) {
1386 // For XUL we do visibility checks.
1387 nsIFrame
* frame
= aElement
->GetPrimaryFrame();
1392 if (frame
->IsFocusable()) {
1396 if (!frame
->IsVisibleConsideringAncestors()) {
1400 // XUL controls can be activated.
1401 nsCOMPtr
<nsIDOMXULControlElement
> control
= aElement
->AsXULControl();
1406 // XUL label elements are never focusable, so we need to check for them
1407 // explicitly before giving up.
1408 if (aElement
->IsXULElement(nsGkAtoms::label
)) {
1415 bool EventStateManager::CheckIfEventMatchesAccessKey(
1416 WidgetKeyboardEvent
* aEvent
, nsPresContext
* aPresContext
) {
1417 AutoTArray
<uint32_t, 10> accessCharCodes
;
1418 aEvent
->GetAccessKeyCandidates(accessCharCodes
);
1419 return WalkESMTreeToHandleAccessKey(aEvent
, aPresContext
, accessCharCodes
,
1420 nullptr, eAccessKeyProcessingNormal
,
1424 bool EventStateManager::LookForAccessKeyAndExecute(
1425 nsTArray
<uint32_t>& aAccessCharCodes
, bool aIsTrustedEvent
, bool aIsRepeat
,
1427 int32_t count
, start
= -1;
1428 if (Element
* focusedElement
= GetFocusedElement()) {
1429 start
= mAccessKeys
.IndexOf(focusedElement
);
1430 if (start
== -1 && focusedElement
->IsInNativeAnonymousSubtree()) {
1431 start
= mAccessKeys
.IndexOf(Element::FromNodeOrNull(
1432 focusedElement
->GetClosestNativeAnonymousSubtreeRootParentOrHost()));
1435 RefPtr
<Element
> element
;
1436 int32_t length
= mAccessKeys
.Count();
1437 for (uint32_t i
= 0; i
< aAccessCharCodes
.Length(); ++i
) {
1438 uint32_t ch
= aAccessCharCodes
[i
];
1439 nsAutoString accessKey
;
1440 AppendUCS4ToUTF16(ch
, accessKey
);
1441 for (count
= 1; count
<= length
; ++count
) {
1442 // mAccessKeys always stores Element instances.
1443 MOZ_DIAGNOSTIC_ASSERT(length
== mAccessKeys
.Count());
1444 element
= mAccessKeys
[(start
+ count
) % length
];
1445 if (IsAccessKeyTarget(element
, accessKey
)) {
1449 Document
* doc
= element
->OwnerDoc();
1450 const bool shouldActivate
= [&] {
1451 if (!StaticPrefs::accessibility_accesskeycausesactivation()) {
1454 if (aIsRepeat
&& nsContentUtils::IsChromeDoc(doc
)) {
1458 // XXXedgar, Bug 1700646, maybe we could use other data structure to
1459 // make searching target with same accesskey easier, and current setup
1460 // could not ensure we cycle the target with tree order.
1462 while (++j
< length
) {
1463 Element
* el
= mAccessKeys
[(start
+ count
+ j
) % length
];
1464 if (IsAccessKeyTarget(el
, accessKey
)) {
1471 // TODO(bug 1641171): This shouldn't be needed if we considered the
1472 // accesskey combination properly.
1473 if (aIsTrustedEvent
) {
1474 doc
->NotifyUserGestureActivation();
1478 element
->PerformAccesskey(shouldActivate
, aIsTrustedEvent
);
1479 if (result
.isOk()) {
1480 if (result
.unwrap() && aIsTrustedEvent
) {
1481 // If this is a child process, inform the parent that we want the
1482 // focus, but pass false since we don't want to change the window
1484 nsIDocShell
* docShell
= mPresContext
->GetDocShell();
1485 nsCOMPtr
<nsIBrowserChild
> child
=
1486 docShell
? docShell
->GetBrowserChild() : nullptr;
1488 child
->SendRequestFocus(false, CallerType::System
);
1500 void EventStateManager::GetAccessKeyLabelPrefix(Element
* aElement
,
1501 nsAString
& aPrefix
) {
1503 nsAutoString separator
, modifierText
;
1504 nsContentUtils::GetModifierSeparatorText(separator
);
1506 AccessKeyType accessKeyType
=
1507 GetAccessKeyTypeFor(aElement
->OwnerDoc()->GetDocShell());
1508 if (accessKeyType
== AccessKeyType::eNone
) {
1511 Modifiers modifiers
= WidgetKeyboardEvent::AccessKeyModifiers(accessKeyType
);
1512 if (modifiers
== MODIFIER_NONE
) {
1516 if (modifiers
& MODIFIER_CONTROL
) {
1517 nsContentUtils::GetControlText(modifierText
);
1518 aPrefix
.Append(modifierText
+ separator
);
1520 if (modifiers
& MODIFIER_META
) {
1521 nsContentUtils::GetCommandOrWinText(modifierText
);
1522 aPrefix
.Append(modifierText
+ separator
);
1524 if (modifiers
& MODIFIER_ALT
) {
1525 nsContentUtils::GetAltText(modifierText
);
1526 aPrefix
.Append(modifierText
+ separator
);
1528 if (modifiers
& MODIFIER_SHIFT
) {
1529 nsContentUtils::GetShiftText(modifierText
);
1530 aPrefix
.Append(modifierText
+ separator
);
1534 struct MOZ_STACK_CLASS AccessKeyInfo
{
1535 WidgetKeyboardEvent
* event
;
1536 nsTArray
<uint32_t>& charCodes
;
1538 AccessKeyInfo(WidgetKeyboardEvent
* aEvent
, nsTArray
<uint32_t>& aCharCodes
)
1539 : event(aEvent
), charCodes(aCharCodes
) {}
1542 bool EventStateManager::WalkESMTreeToHandleAccessKey(
1543 WidgetKeyboardEvent
* aEvent
, nsPresContext
* aPresContext
,
1544 nsTArray
<uint32_t>& aAccessCharCodes
, nsIDocShellTreeItem
* aBubbledFrom
,
1545 ProcessingAccessKeyState aAccessKeyState
, bool aExecute
) {
1546 EnsureDocument(mPresContext
);
1547 nsCOMPtr
<nsIDocShell
> docShell
= aPresContext
->GetDocShell();
1548 if (NS_WARN_IF(!docShell
) || NS_WARN_IF(!mDocument
)) {
1551 AccessKeyType accessKeyType
= GetAccessKeyTypeFor(docShell
);
1552 if (accessKeyType
== AccessKeyType::eNone
) {
1555 // Alt or other accesskey modifier is down, we may need to do an accesskey.
1556 if (mAccessKeys
.Count() > 0 &&
1557 aEvent
->ModifiersMatchWithAccessKey(accessKeyType
)) {
1558 // Someone registered an accesskey. Find and activate it.
1559 if (LookForAccessKeyAndExecute(aAccessCharCodes
, aEvent
->IsTrusted(),
1560 aEvent
->mIsRepeat
, aExecute
)) {
1566 docShell
->GetInProcessChildCount(&childCount
);
1567 for (int32_t counter
= 0; counter
< childCount
; counter
++) {
1568 // Not processing the child which bubbles up the handling
1569 nsCOMPtr
<nsIDocShellTreeItem
> subShellItem
;
1570 docShell
->GetInProcessChildAt(counter
, getter_AddRefs(subShellItem
));
1571 if (aAccessKeyState
== eAccessKeyProcessingUp
&&
1572 subShellItem
== aBubbledFrom
) {
1576 nsCOMPtr
<nsIDocShell
> subDS
= do_QueryInterface(subShellItem
);
1577 if (subDS
&& IsShellVisible(subDS
)) {
1578 // Guarantee subPresShell lifetime while we're handling access key
1579 // since somebody may assume that it won't be deleted before the
1580 // corresponding nsPresContext and EventStateManager.
1581 RefPtr
<PresShell
> subPresShell
= subDS
->GetPresShell();
1583 // Docshells need not have a presshell (eg. display:none
1584 // iframes, docshells in transition between documents, etc).
1585 if (!subPresShell
) {
1586 // Oh, well. Just move on to the next child
1590 RefPtr
<nsPresContext
> subPresContext
= subPresShell
->GetPresContext();
1592 RefPtr
<EventStateManager
> esm
=
1593 static_cast<EventStateManager
*>(subPresContext
->EventStateManager());
1595 if (esm
&& esm
->WalkESMTreeToHandleAccessKey(
1596 aEvent
, subPresContext
, aAccessCharCodes
, nullptr,
1597 eAccessKeyProcessingDown
, aExecute
)) {
1601 } // if end . checking all sub docshell ends here.
1603 // bubble up the process to the parent docshell if necessary
1604 if (eAccessKeyProcessingDown
!= aAccessKeyState
) {
1605 nsCOMPtr
<nsIDocShellTreeItem
> parentShellItem
;
1606 docShell
->GetInProcessParent(getter_AddRefs(parentShellItem
));
1607 nsCOMPtr
<nsIDocShell
> parentDS
= do_QueryInterface(parentShellItem
);
1609 // Guarantee parentPresShell lifetime while we're handling access key
1610 // since somebody may assume that it won't be deleted before the
1611 // corresponding nsPresContext and EventStateManager.
1612 RefPtr
<PresShell
> parentPresShell
= parentDS
->GetPresShell();
1613 NS_ASSERTION(parentPresShell
,
1614 "Our PresShell exists but the parent's does not?");
1616 RefPtr
<nsPresContext
> parentPresContext
=
1617 parentPresShell
->GetPresContext();
1618 NS_ASSERTION(parentPresContext
, "PresShell without PresContext");
1620 RefPtr
<EventStateManager
> esm
= static_cast<EventStateManager
*>(
1621 parentPresContext
->EventStateManager());
1622 if (esm
&& esm
->WalkESMTreeToHandleAccessKey(
1623 aEvent
, parentPresContext
, aAccessCharCodes
, docShell
,
1624 eAccessKeyProcessingDown
, aExecute
)) {
1628 } // if end. bubble up process
1630 // If the content access key modifier is pressed, try remote children
1632 aEvent
->ModifiersMatchWithAccessKey(AccessKeyType::eContent
) &&
1633 mDocument
&& mDocument
->GetWindow()) {
1634 // If the focus is currently on a node with a BrowserParent, the key event
1635 // should've gotten forwarded to the child process and HandleAccessKey
1636 // called from there.
1637 if (BrowserParent::GetFrom(GetFocusedElement())) {
1638 // If access key may be only in remote contents, this method won't handle
1639 // access key synchronously. In this case, only reply event should reach
1641 MOZ_ASSERT(aEvent
->IsHandledInRemoteProcess() ||
1642 !aEvent
->IsWaitingReplyFromRemoteProcess());
1644 // If focus is somewhere else, then we need to check the remote children.
1645 // However, if the event has already been handled in a remote process,
1646 // then, focus is moved from the remote process after posting the event.
1647 // In such case, we shouldn't retry to handle access keys in remote
1649 else if (!aEvent
->IsHandledInRemoteProcess()) {
1650 AccessKeyInfo
accessKeyInfo(aEvent
, aAccessCharCodes
);
1651 nsContentUtils::CallOnAllRemoteChildren(
1652 mDocument
->GetWindow(),
1653 [&accessKeyInfo
](BrowserParent
* aBrowserParent
) -> CallState
{
1654 // Only forward accesskeys for the active tab.
1655 if (aBrowserParent
->GetDocShellIsActive()) {
1656 // Even if there is no target for the accesskey in this process,
1657 // the event may match with a content accesskey. If so, the
1658 // keyboard event should be handled with reply event for
1659 // preventing double action. (e.g., Alt+Shift+F on Windows may
1660 // focus a content in remote and open "File" menu.)
1661 accessKeyInfo
.event
->StopPropagation();
1662 accessKeyInfo
.event
->MarkAsWaitingReplyFromRemoteProcess();
1663 aBrowserParent
->HandleAccessKey(*accessKeyInfo
.event
,
1664 accessKeyInfo
.charCodes
);
1665 return CallState::Stop
;
1668 return CallState::Continue
;
1674 } // end of HandleAccessKey
1676 static BrowserParent
* GetBrowserParentAncestor(BrowserParent
* aBrowserParent
) {
1677 MOZ_ASSERT(aBrowserParent
);
1679 BrowserBridgeParent
* bbp
= aBrowserParent
->GetBrowserBridgeParent();
1684 return bbp
->Manager();
1687 static void DispatchCrossProcessMouseExitEvents(WidgetMouseEvent
* aMouseEvent
,
1688 BrowserParent
* aRemoteTarget
,
1689 BrowserParent
* aStopAncestor
,
1690 bool aIsReallyExit
) {
1691 MOZ_ASSERT(aMouseEvent
);
1692 MOZ_ASSERT(aRemoteTarget
);
1693 MOZ_ASSERT(aRemoteTarget
!= aStopAncestor
);
1694 MOZ_ASSERT_IF(aStopAncestor
, nsContentUtils::GetCommonBrowserParentAncestor(
1695 aRemoteTarget
, aStopAncestor
));
1697 while (aRemoteTarget
!= aStopAncestor
) {
1698 UniquePtr
<WidgetMouseEvent
> mouseExitEvent
=
1699 CreateMouseOrPointerWidgetEvent(aMouseEvent
, eMouseExitFromWidget
,
1700 aMouseEvent
->mRelatedTarget
);
1701 mouseExitEvent
->mExitFrom
=
1702 Some(aIsReallyExit
? WidgetMouseEvent::ePuppet
1703 : WidgetMouseEvent::ePuppetParentToPuppetChild
);
1704 aRemoteTarget
->SendRealMouseEvent(*mouseExitEvent
);
1706 aRemoteTarget
= GetBrowserParentAncestor(aRemoteTarget
);
1710 void EventStateManager::DispatchCrossProcessEvent(WidgetEvent
* aEvent
,
1711 BrowserParent
* aRemoteTarget
,
1712 nsEventStatus
* aStatus
) {
1714 MOZ_ASSERT(aRemoteTarget
);
1715 MOZ_ASSERT(aStatus
);
1717 BrowserParent
* remote
= aRemoteTarget
;
1719 WidgetMouseEvent
* mouseEvent
= aEvent
->AsMouseEvent();
1720 bool isContextMenuKey
= mouseEvent
&& mouseEvent
->IsContextMenuKeyEvent();
1721 if (aEvent
->mClass
== eKeyboardEventClass
|| isContextMenuKey
) {
1722 // APZ attaches a LayersId to hit-testable events, for keyboard events,
1724 BrowserParent
* preciseRemote
= BrowserParent::GetFocused();
1725 if (preciseRemote
) {
1726 remote
= preciseRemote
;
1728 // else there is a race between layout and focus tracking,
1729 // so fall back to delivering the event to the topmost child process.
1730 } else if (aEvent
->mLayersId
.IsValid()) {
1731 BrowserParent
* preciseRemote
=
1732 BrowserParent::GetBrowserParentFromLayersId(aEvent
->mLayersId
);
1733 if (preciseRemote
) {
1734 remote
= preciseRemote
;
1736 // else there is a race between APZ and the LayersId to BrowserParent
1737 // mapping, so fall back to delivering the event to the topmost child
1741 switch (aEvent
->mClass
) {
1742 case eMouseEventClass
: {
1743 BrowserParent
* oldRemote
= BrowserParent::GetLastMouseRemoteTarget();
1745 // If this is a eMouseExitFromWidget event, need to redirect the event to
1746 // the last remote and and notify all its ancestors about the exit, if
1748 if (mouseEvent
->mMessage
== eMouseExitFromWidget
) {
1749 MOZ_ASSERT(mouseEvent
->mExitFrom
.value() == WidgetMouseEvent::ePuppet
);
1750 MOZ_ASSERT(mouseEvent
->mReason
== WidgetMouseEvent::eReal
);
1751 MOZ_ASSERT(!mouseEvent
->mLayersId
.IsValid());
1752 MOZ_ASSERT(remote
->GetBrowserHost());
1754 if (oldRemote
&& oldRemote
!= remote
) {
1755 Unused
<< NS_WARN_IF(nsContentUtils::GetCommonBrowserParentAncestor(
1756 remote
, oldRemote
) != remote
);
1760 DispatchCrossProcessMouseExitEvents(mouseEvent
, remote
, nullptr, true);
1764 if (BrowserParent
* pointerLockedRemote
=
1765 PointerLockManager::GetLockedRemoteTarget()) {
1766 remote
= pointerLockedRemote
;
1767 } else if (BrowserParent
* pointerCapturedRemote
=
1768 PointerEventHandler::GetPointerCapturingRemoteTarget(
1769 mouseEvent
->pointerId
)) {
1770 remote
= pointerCapturedRemote
;
1771 } else if (BrowserParent
* capturingRemote
=
1772 PresShell::GetCapturingRemoteTarget()) {
1773 remote
= capturingRemote
;
1776 // If a mouse is over a remote target A, and then moves to
1777 // remote target B, we'd deliver the event directly to remote target B
1778 // after the moving, A would never get notified that the mouse left.
1779 // So we generate a exit event to notify A after the move.
1780 // XXXedgar, if the synthesized mouse events could deliver to the correct
1781 // process directly (see
1782 // https://bugzilla.mozilla.org/show_bug.cgi?id=1549355), we probably
1783 // don't need to check mReason then.
1784 if (mouseEvent
->mReason
== WidgetMouseEvent::eReal
&&
1785 remote
!= oldRemote
) {
1786 MOZ_ASSERT(mouseEvent
->mMessage
!= eMouseExitFromWidget
);
1788 BrowserParent
* commonAncestor
=
1789 nsContentUtils::GetCommonBrowserParentAncestor(remote
, oldRemote
);
1790 if (commonAncestor
== oldRemote
) {
1791 // Mouse moves to the inner OOP frame, it is not a really exit.
1792 DispatchCrossProcessMouseExitEvents(
1793 mouseEvent
, GetBrowserParentAncestor(remote
),
1794 GetBrowserParentAncestor(commonAncestor
), false);
1795 } else if (commonAncestor
== remote
) {
1796 // Mouse moves to the outer OOP frame, it is a really exit.
1797 DispatchCrossProcessMouseExitEvents(mouseEvent
, oldRemote
,
1798 commonAncestor
, true);
1800 // Mouse moves to OOP frame in other subtree, it is a really exit,
1801 // need to notify all its ancestors before common ancestor about the
1803 DispatchCrossProcessMouseExitEvents(mouseEvent
, oldRemote
,
1804 commonAncestor
, true);
1805 if (commonAncestor
) {
1806 UniquePtr
<WidgetMouseEvent
> mouseExitEvent
=
1807 CreateMouseOrPointerWidgetEvent(mouseEvent
,
1808 eMouseExitFromWidget
,
1809 mouseEvent
->mRelatedTarget
);
1810 mouseExitEvent
->mExitFrom
=
1811 Some(WidgetMouseEvent::ePuppetParentToPuppetChild
);
1812 commonAncestor
->SendRealMouseEvent(*mouseExitEvent
);
1817 if (mouseEvent
->mMessage
!= eMouseExitFromWidget
&&
1818 mouseEvent
->mMessage
!= eMouseEnterIntoWidget
) {
1819 // This is to make cursor would be updated correctly.
1820 remote
->MouseEnterIntoWidget();
1824 remote
->SendRealMouseEvent(*mouseEvent
);
1827 case eKeyboardEventClass
: {
1828 auto* keyboardEvent
= aEvent
->AsKeyboardEvent();
1829 if (aEvent
->mMessage
== eKeyUp
) {
1830 HandleKeyUpInteraction(keyboardEvent
);
1832 remote
->SendRealKeyEvent(*keyboardEvent
);
1835 case eWheelEventClass
: {
1836 if (BrowserParent
* pointerLockedRemote
=
1837 PointerLockManager::GetLockedRemoteTarget()) {
1838 remote
= pointerLockedRemote
;
1840 remote
->SendMouseWheelEvent(*aEvent
->AsWheelEvent());
1843 case eTouchEventClass
: {
1844 // Let the child process synthesize a mouse event if needed, and
1845 // ensure we don't synthesize one in this process.
1846 *aStatus
= nsEventStatus_eConsumeNoDefault
;
1847 remote
->SendRealTouchEvent(*aEvent
->AsTouchEvent());
1850 case eDragEventClass
: {
1851 RefPtr
<BrowserParent
> browserParent
= remote
;
1852 browserParent
->Manager()->MaybeInvokeDragSession(browserParent
,
1855 nsCOMPtr
<nsIDragSession
> dragSession
= nsContentUtils::GetDragSession();
1856 uint32_t dropEffect
= nsIDragService::DRAGDROP_ACTION_NONE
;
1857 uint32_t action
= nsIDragService::DRAGDROP_ACTION_NONE
;
1858 nsCOMPtr
<nsIPrincipal
> principal
;
1859 nsCOMPtr
<nsIContentSecurityPolicy
> csp
;
1862 dragSession
->DragEventDispatchedToChildProcess();
1863 dragSession
->GetDragAction(&action
);
1864 dragSession
->GetTriggeringPrincipal(getter_AddRefs(principal
));
1865 dragSession
->GetCsp(getter_AddRefs(csp
));
1866 RefPtr
<DataTransfer
> initialDataTransfer
=
1867 dragSession
->GetDataTransfer();
1868 if (initialDataTransfer
) {
1869 dropEffect
= initialDataTransfer
->DropEffectInt();
1873 browserParent
->SendRealDragEvent(*aEvent
->AsDragEvent(), action
,
1874 dropEffect
, principal
, csp
);
1878 MOZ_CRASH("Attempt to send non-whitelisted event?");
1883 bool EventStateManager::IsRemoteTarget(nsIContent
* target
) {
1884 return BrowserParent::GetFrom(target
) || BrowserBridgeChild::GetFrom(target
);
1887 bool EventStateManager::IsTopLevelRemoteTarget(nsIContent
* target
) {
1888 return !!BrowserParent::GetFrom(target
);
1891 bool EventStateManager::HandleCrossProcessEvent(WidgetEvent
* aEvent
,
1892 nsEventStatus
* aStatus
) {
1893 if (!aEvent
->CanBeSentToRemoteProcess()) {
1897 MOZ_ASSERT(!aEvent
->HasBeenPostedToRemoteProcess(),
1898 "Why do we need to post same event to remote processes again?");
1900 // Collect the remote event targets we're going to forward this
1903 // NB: the elements of |remoteTargets| must be unique, for correctness.
1904 AutoTArray
<RefPtr
<BrowserParent
>, 1> remoteTargets
;
1905 if (aEvent
->mClass
!= eTouchEventClass
|| aEvent
->mMessage
== eTouchStart
) {
1906 // If this event only has one target, and it's remote, add it to
1908 nsIFrame
* frame
= aEvent
->mMessage
== eDragExit
1909 ? sLastDragOverFrame
.GetFrame()
1911 nsIContent
* target
= frame
? frame
->GetContent() : nullptr;
1912 if (BrowserParent
* remoteTarget
= BrowserParent::GetFrom(target
)) {
1913 remoteTargets
.AppendElement(remoteTarget
);
1916 // This is a touch event with possibly multiple touch points.
1917 // Each touch point may have its own target. So iterate through
1918 // all of them and collect the unique set of targets for event
1921 // This loop is similar to the one used in
1922 // PresShell::DispatchTouchEvent().
1923 const WidgetTouchEvent::TouchArray
& touches
=
1924 aEvent
->AsTouchEvent()->mTouches
;
1925 for (uint32_t i
= 0; i
< touches
.Length(); ++i
) {
1926 Touch
* touch
= touches
[i
];
1927 // NB: the |mChanged| check is an optimization, subprocesses can
1928 // compute this for themselves. If the touch hasn't changed, we
1929 // may be able to avoid forwarding the event entirely (which is
1931 if (!touch
|| !touch
->mChanged
) {
1934 nsCOMPtr
<EventTarget
> targetPtr
= touch
->mTarget
;
1938 nsCOMPtr
<nsIContent
> target
= do_QueryInterface(targetPtr
);
1939 BrowserParent
* remoteTarget
= BrowserParent::GetFrom(target
);
1940 if (remoteTarget
&& !remoteTargets
.Contains(remoteTarget
)) {
1941 remoteTargets
.AppendElement(remoteTarget
);
1946 if (remoteTargets
.Length() == 0) {
1950 // Dispatch the event to the remote target.
1951 for (uint32_t i
= 0; i
< remoteTargets
.Length(); ++i
) {
1952 DispatchCrossProcessEvent(aEvent
, remoteTargets
[i
], aStatus
);
1954 return aEvent
->HasBeenPostedToRemoteProcess();
1958 // CreateClickHoldTimer
1960 // Fire off a timer for determining if the user wants click-hold. This timer
1961 // is a one-shot that will be cancelled when the user moves enough to fire
1964 void EventStateManager::CreateClickHoldTimer(nsPresContext
* inPresContext
,
1965 nsIFrame
* inDownFrame
,
1966 WidgetGUIEvent
* inMouseDownEvent
) {
1967 if (!inMouseDownEvent
->IsTrusted() ||
1968 IsTopLevelRemoteTarget(mGestureDownContent
) ||
1969 PointerLockManager::IsLocked()) {
1973 // just to be anal (er, safe)
1974 if (mClickHoldTimer
) {
1975 mClickHoldTimer
->Cancel();
1976 mClickHoldTimer
= nullptr;
1979 // if content clicked on has a popup, don't even start the timer
1980 // since we'll end up conflicting and both will show.
1981 if (mGestureDownContent
&&
1982 nsContentUtils::HasNonEmptyAttr(mGestureDownContent
, kNameSpaceID_None
,
1983 nsGkAtoms::popup
)) {
1987 int32_t clickHoldDelay
= StaticPrefs::ui_click_hold_context_menus_delay();
1988 NS_NewTimerWithFuncCallback(
1989 getter_AddRefs(mClickHoldTimer
), sClickHoldCallback
, this, clickHoldDelay
,
1990 nsITimer::TYPE_ONE_SHOT
, "EventStateManager::CreateClickHoldTimer");
1991 } // CreateClickHoldTimer
1994 // KillClickHoldTimer
1996 // Stop the timer that would show the context menu dead in its tracks
1998 void EventStateManager::KillClickHoldTimer() {
1999 if (mClickHoldTimer
) {
2000 mClickHoldTimer
->Cancel();
2001 mClickHoldTimer
= nullptr;
2006 // sClickHoldCallback
2008 // This fires after the mouse has been down for a certain length of time.
2010 void EventStateManager::sClickHoldCallback(nsITimer
* aTimer
, void* aESM
) {
2011 RefPtr
<EventStateManager
> self
= static_cast<EventStateManager
*>(aESM
);
2013 self
->FireContextClick();
2016 // NOTE: |aTimer| and |self->mAutoHideTimer| are invalid after calling
2019 } // sAutoHideCallback
2024 // If we're this far, our timer has fired, which means the mouse has been down
2025 // for a certain period of time and has not moved enough to generate a
2026 // dragGesture. We can be certain the user wants a context-click at this stage,
2027 // so generate a dom event and fire it in.
2029 // After the event fires, check if PreventDefault() has been set on the event
2030 // which means that someone either ate the event or put up a context menu. This
2031 // is our cue to stop tracking the drag gesture. If we always did this,
2032 // draggable items w/out a context menu wouldn't be draggable after a certain
2033 // length of time, which is _not_ what we want.
2035 void EventStateManager::FireContextClick() {
2036 if (!mGestureDownContent
|| !mPresContext
|| PointerLockManager::IsLocked()) {
2041 // Hack to ensure that we don't show a context menu when the user
2042 // let go of the mouse after a long cpu-hogging operation prevented
2043 // us from handling any OS events. See bug 117589.
2044 if (!CGEventSourceButtonState(kCGEventSourceStateCombinedSessionState
,
2045 kCGMouseButtonLeft
))
2049 nsEventStatus status
= nsEventStatus_eIgnore
;
2051 // Dispatch to the DOM. We have to fake out the ESM and tell it that the
2052 // current target frame is actually where the mouseDown occurred, otherwise it
2053 // will use the frame the mouse is currently over which may or may not be
2054 // the same. (Note: saari and I have decided that we don't have to reset
2055 // |mCurrentTarget| when we're through because no one else is doing anything
2056 // more with this event and it will get reset on the very next event to the
2058 mCurrentTarget
= mPresContext
->GetPrimaryFrameFor(mGestureDownContent
);
2059 // make sure the widget sticks around
2060 nsCOMPtr
<nsIWidget
> targetWidget
;
2061 if (mCurrentTarget
&& (targetWidget
= mCurrentTarget
->GetNearestWidget())) {
2063 mPresContext
== mCurrentTarget
->PresContext(),
2064 "a prescontext returned a primary frame that didn't belong to it?");
2066 // before dispatching, check that we're not on something that
2067 // doesn't get a context menu
2068 bool allowedToDispatch
= true;
2070 if (mGestureDownContent
->IsAnyOfXULElements(nsGkAtoms::scrollbar
,
2071 nsGkAtoms::scrollbarbutton
,
2072 nsGkAtoms::button
)) {
2073 allowedToDispatch
= false;
2074 } else if (mGestureDownContent
->IsXULElement(nsGkAtoms::toolbarbutton
)) {
2075 // a <toolbarbutton> that has the container attribute set
2076 // will already have its own dropdown.
2077 if (nsContentUtils::HasNonEmptyAttr(
2078 mGestureDownContent
, kNameSpaceID_None
, nsGkAtoms::container
)) {
2079 allowedToDispatch
= false;
2081 // If the toolbar button has an open menu, don't attempt to open
2083 if (mGestureDownContent
->IsElement() &&
2084 mGestureDownContent
->AsElement()->AttrValueIs(
2085 kNameSpaceID_None
, nsGkAtoms::open
, nsGkAtoms::_true
,
2087 allowedToDispatch
= false;
2090 } else if (mGestureDownContent
->IsHTMLElement()) {
2091 nsCOMPtr
<nsIFormControl
> formCtrl(do_QueryInterface(mGestureDownContent
));
2095 formCtrl
->IsTextControl(/*aExcludePassword*/ false) ||
2096 formCtrl
->ControlType() == FormControlType::InputFile
;
2097 } else if (mGestureDownContent
->IsAnyOfHTMLElements(
2098 nsGkAtoms::embed
, nsGkAtoms::object
, nsGkAtoms::label
)) {
2099 allowedToDispatch
= false;
2103 if (allowedToDispatch
) {
2104 // init the event while mCurrentTarget is still good
2105 WidgetMouseEvent
event(true, eContextMenu
, targetWidget
,
2106 WidgetMouseEvent::eReal
);
2107 event
.mClickCount
= 1;
2108 FillInEventFromGestureDown(&event
);
2110 // stop selection tracking, we're in control now
2111 if (mCurrentTarget
) {
2112 RefPtr
<nsFrameSelection
> frameSel
= mCurrentTarget
->GetFrameSelection();
2114 if (frameSel
&& frameSel
->GetDragState()) {
2115 // note that this can cause selection changed events to fire if we're
2116 // in a text field, which will null out mCurrentTarget
2117 frameSel
->SetDragState(false);
2121 AutoHandlingUserInputStatePusher
userInpStatePusher(true, &event
);
2124 RefPtr
<nsIContent
> gestureDownContent
= mGestureDownContent
;
2125 RefPtr
<nsPresContext
> presContext
= mPresContext
;
2126 EventDispatcher::Dispatch(gestureDownContent
, presContext
, &event
,
2129 // We don't need to dispatch to frame handling because no frames
2130 // watch eContextMenu except for nsMenuFrame and that's only for
2131 // dismissal. That's just as well since we don't really know
2132 // which frame to send it to.
2136 // now check if the event has been handled. If so, stop tracking a drag
2137 if (status
== nsEventStatus_eConsumeNoDefault
) {
2138 StopTrackingDragGesture(true);
2141 KillClickHoldTimer();
2143 } // FireContextClick
2146 // BeginTrackingDragGesture
2148 // Record that the mouse has gone down and that we should move to TRACKING state
2149 // of d&d gesture tracker.
2151 // We also use this to track click-hold context menus. When the mouse goes down,
2152 // fire off a short timer. If the timer goes off and we have yet to fire the
2153 // drag gesture (ie, the mouse hasn't moved a certain distance), then we can
2154 // assume the user wants a click-hold, so fire a context-click event. We only
2155 // want to cancel the drag gesture if the context-click event is handled.
2157 void EventStateManager::BeginTrackingDragGesture(nsPresContext
* aPresContext
,
2158 WidgetMouseEvent
* inDownEvent
,
2159 nsIFrame
* inDownFrame
) {
2160 if (!inDownEvent
->mWidget
) {
2164 // Note that |inDownEvent| could be either a mouse down event or a
2165 // synthesized mouse move event.
2166 SetGestureDownPoint(inDownEvent
);
2169 inDownFrame
->GetContentForEvent(inDownEvent
,
2170 getter_AddRefs(mGestureDownContent
));
2172 mGestureDownFrameOwner
= inDownFrame
->GetContent();
2173 if (!mGestureDownFrameOwner
) {
2174 mGestureDownFrameOwner
= mGestureDownContent
;
2177 mGestureModifiers
= inDownEvent
->mModifiers
;
2178 mGestureDownButtons
= inDownEvent
->mButtons
;
2180 if (inDownEvent
->mMessage
!= eMouseTouchDrag
&&
2181 StaticPrefs::ui_click_hold_context_menus()) {
2182 // fire off a timer to track click-hold
2183 CreateClickHoldTimer(aPresContext
, inDownFrame
, inDownEvent
);
2187 void EventStateManager::SetGestureDownPoint(WidgetGUIEvent
* aEvent
) {
2189 GetEventRefPoint(aEvent
) + aEvent
->mWidget
->WidgetToScreenOffset();
2192 LayoutDeviceIntPoint
EventStateManager::GetEventRefPoint(
2193 WidgetEvent
* aEvent
) const {
2194 auto touchEvent
= aEvent
->AsTouchEvent();
2195 return (touchEvent
&& !touchEvent
->mTouches
.IsEmpty())
2196 ? aEvent
->AsTouchEvent()->mTouches
[0]->mRefPoint
2197 : aEvent
->mRefPoint
;
2200 void EventStateManager::BeginTrackingRemoteDragGesture(
2201 nsIContent
* aContent
, RemoteDragStartData
* aDragStartData
) {
2202 mGestureDownContent
= aContent
;
2203 mGestureDownFrameOwner
= aContent
;
2204 mGestureDownInTextControl
=
2205 aContent
&& aContent
->IsInNativeAnonymousSubtree() &&
2206 TextControlElement::FromNodeOrNull(
2207 aContent
->GetClosestNativeAnonymousSubtreeRootParentOrHost());
2208 mGestureDownDragStartData
= aDragStartData
;
2212 // StopTrackingDragGesture
2214 // Record that the mouse has gone back up so that we should leave the TRACKING
2215 // state of d&d gesture tracker and return to the START state.
2217 void EventStateManager::StopTrackingDragGesture(bool aClearInChildProcesses
) {
2218 mGestureDownContent
= nullptr;
2219 mGestureDownFrameOwner
= nullptr;
2220 mGestureDownInTextControl
= false;
2221 mGestureDownDragStartData
= nullptr;
2223 // If a content process starts a drag but the mouse is released before the
2224 // parent starts the actual drag, the content process will think a drag is
2225 // still happening. Inform any child processes with active drags that the drag
2226 // should be stopped.
2227 if (aClearInChildProcesses
) {
2228 nsCOMPtr
<nsIDragService
> dragService
=
2229 do_GetService("@mozilla.org/widget/dragservice;1");
2231 nsCOMPtr
<nsIDragSession
> dragSession
;
2232 dragService
->GetCurrentSession(getter_AddRefs(dragSession
));
2234 // Only notify if there isn't a drag session active.
2235 dragService
->RemoveAllChildProcesses();
2241 void EventStateManager::FillInEventFromGestureDown(WidgetMouseEvent
* aEvent
) {
2242 NS_ASSERTION(aEvent
->mWidget
== mCurrentTarget
->GetNearestWidget(),
2243 "Incorrect widget in event");
2245 // Set the coordinates in the new event to the coordinates of
2246 // the old event, adjusted for the fact that the widget might be
2249 mGestureDownPoint
- aEvent
->mWidget
->WidgetToScreenOffset();
2250 aEvent
->mModifiers
= mGestureModifiers
;
2251 aEvent
->mButtons
= mGestureDownButtons
;
2254 void EventStateManager::MaybeFirePointerCancel(WidgetInputEvent
* aEvent
) {
2255 RefPtr
<PresShell
> presShell
= mPresContext
->GetPresShell();
2256 AutoWeakFrame targetFrame
= mCurrentTarget
;
2258 if (!presShell
|| !targetFrame
) {
2262 nsCOMPtr
<nsIContent
> content
;
2263 targetFrame
->GetContentForEvent(aEvent
, getter_AddRefs(content
));
2268 nsEventStatus status
= nsEventStatus_eIgnore
;
2270 if (WidgetMouseEvent
* aMouseEvent
= aEvent
->AsMouseEvent()) {
2271 WidgetPointerEvent
event(*aMouseEvent
);
2272 PointerEventHandler::InitPointerEventFromMouse(&event
, aMouseEvent
,
2275 event
.convertToPointer
= false;
2276 presShell
->HandleEventWithTarget(&event
, targetFrame
, content
, &status
);
2277 } else if (WidgetTouchEvent
* aTouchEvent
= aEvent
->AsTouchEvent()) {
2278 WidgetPointerEvent
event(aTouchEvent
->IsTrusted(), ePointerCancel
,
2279 aTouchEvent
->mWidget
);
2281 PointerEventHandler::InitPointerEventFromTouch(
2282 event
, *aTouchEvent
, *aTouchEvent
->mTouches
[0], true);
2284 event
.convertToPointer
= false;
2285 presShell
->HandleEventWithTarget(&event
, targetFrame
, content
, &status
);
2290 // HandleEventWithTarget clears out mCurrentTarget, which may be used in the
2291 // caller GenerateDragGesture. We have to restore mCurrentTarget.
2292 mCurrentTarget
= targetFrame
;
2295 bool EventStateManager::IsEventOutsideDragThreshold(
2296 WidgetInputEvent
* aEvent
) const {
2297 static int32_t sPixelThresholdX
= 0;
2298 static int32_t sPixelThresholdY
= 0;
2300 if (!sPixelThresholdX
) {
2302 LookAndFeel::GetInt(LookAndFeel::IntID::DragThresholdX
, 0);
2304 LookAndFeel::GetInt(LookAndFeel::IntID::DragThresholdY
, 0);
2305 if (sPixelThresholdX
<= 0) {
2306 sPixelThresholdX
= 5;
2308 if (sPixelThresholdY
<= 0) {
2309 sPixelThresholdY
= 5;
2313 LayoutDeviceIntPoint pt
=
2314 aEvent
->mWidget
->WidgetToScreenOffset() + GetEventRefPoint(aEvent
);
2315 LayoutDeviceIntPoint distance
= pt
- mGestureDownPoint
;
2316 return Abs(distance
.x
) > sPixelThresholdX
||
2317 Abs(distance
.y
) > sPixelThresholdY
;
2321 // GenerateDragGesture
2323 // If we're in the TRACKING state of the d&d gesture tracker, check the current
2324 // position of the mouse in relation to the old one. If we've moved a sufficient
2325 // amount from the mouse down, then fire off a drag gesture event.
2326 void EventStateManager::GenerateDragGesture(nsPresContext
* aPresContext
,
2327 WidgetInputEvent
* aEvent
) {
2328 NS_ASSERTION(aPresContext
, "This shouldn't happen.");
2329 if (!IsTrackingDragGesture()) {
2333 AutoWeakFrame targetFrameBefore
= mCurrentTarget
;
2334 auto autoRestore
= MakeScopeExit([&] { mCurrentTarget
= targetFrameBefore
; });
2335 mCurrentTarget
= mGestureDownFrameOwner
->GetPrimaryFrame();
2337 if (!mCurrentTarget
|| !mCurrentTarget
->GetNearestWidget()) {
2338 StopTrackingDragGesture(true);
2342 // Check if selection is tracking drag gestures, if so
2344 if (mCurrentTarget
) {
2345 RefPtr
<nsFrameSelection
> frameSel
= mCurrentTarget
->GetFrameSelection();
2346 if (frameSel
&& frameSel
->GetDragState()) {
2347 StopTrackingDragGesture(true);
2352 // If non-native code is capturing the mouse don't start a drag.
2353 if (PresShell::IsMouseCapturePreventingDrag()) {
2354 StopTrackingDragGesture(true);
2358 if (!IsEventOutsideDragThreshold(aEvent
)) {
2359 // To keep the old behavior, flush layout even if we don't start dnd.
2360 FlushLayout(aPresContext
);
2364 if (StaticPrefs::ui_click_hold_context_menus()) {
2365 // stop the click-hold before we fire off the drag gesture, in case
2366 // it takes a long time
2367 KillClickHoldTimer();
2370 nsCOMPtr
<nsIDocShell
> docshell
= aPresContext
->GetDocShell();
2375 nsCOMPtr
<nsPIDOMWindowOuter
> window
= docshell
->GetWindow();
2376 if (!window
) return;
2378 RefPtr
<DataTransfer
> dataTransfer
=
2379 new DataTransfer(window
, eDragStart
, false, -1);
2380 auto protectDataTransfer
= MakeScopeExit([&] {
2382 dataTransfer
->Disconnect();
2386 RefPtr
<Selection
> selection
;
2387 RefPtr
<RemoteDragStartData
> remoteDragStartData
;
2388 nsCOMPtr
<nsIContent
> eventContent
, targetContent
;
2389 nsCOMPtr
<nsIPrincipal
> principal
;
2390 nsCOMPtr
<nsIContentSecurityPolicy
> csp
;
2391 nsCOMPtr
<nsICookieJarSettings
> cookieJarSettings
;
2392 bool allowEmptyDataTransfer
= false;
2393 mCurrentTarget
->GetContentForEvent(aEvent
, getter_AddRefs(eventContent
));
2395 // If the content is a text node in a password field, we shouldn't
2396 // allow to drag its raw text. Note that we've supported drag from
2397 // password fields but dragging data was masked text. So, it doesn't
2398 // make sense anyway.
2399 if (eventContent
->IsText() && eventContent
->HasFlag(NS_MAYBE_MASKED
)) {
2400 // However, it makes sense to allow to drag selected password text
2401 // when copying selected password is allowed because users may want
2402 // to use drag and drop rather than copy and paste when web apps
2403 // request to input password twice for conforming new password but
2404 // they used password generator.
2405 TextEditor
* textEditor
=
2406 nsContentUtils::GetTextEditorFromAnonymousNodeWithoutCreation(
2408 if (!textEditor
|| !textEditor
->IsCopyToClipboardAllowed()) {
2409 StopTrackingDragGesture(true);
2413 DetermineDragTargetAndDefaultData(
2414 window
, eventContent
, dataTransfer
, &allowEmptyDataTransfer
,
2415 getter_AddRefs(selection
), getter_AddRefs(remoteDragStartData
),
2416 getter_AddRefs(targetContent
), getter_AddRefs(principal
),
2417 getter_AddRefs(csp
), getter_AddRefs(cookieJarSettings
));
2420 // Stop tracking the drag gesture now. This should stop us from
2421 // reentering GenerateDragGesture inside DOM event processing.
2422 // Pass false to avoid clearing the child process state since a real
2423 // drag should be starting.
2424 StopTrackingDragGesture(false);
2426 if (!targetContent
) return;
2428 // Use our targetContent, now that we've determined it, as the
2429 // parent object of the DataTransfer.
2430 nsCOMPtr
<nsIContent
> parentContent
=
2431 targetContent
->FindFirstNonChromeOnlyAccessContent();
2432 dataTransfer
->SetParentObject(parentContent
);
2434 sLastDragOverFrame
= nullptr;
2435 nsCOMPtr
<nsIWidget
> widget
= mCurrentTarget
->GetNearestWidget();
2437 // get the widget from the target frame
2438 WidgetDragEvent
startEvent(aEvent
->IsTrusted(), eDragStart
, widget
);
2439 startEvent
.mFlags
.mIsSynthesizedForTests
=
2440 aEvent
->mFlags
.mIsSynthesizedForTests
;
2441 FillInEventFromGestureDown(&startEvent
);
2443 startEvent
.mDataTransfer
= dataTransfer
;
2444 if (aEvent
->AsMouseEvent()) {
2445 startEvent
.mInputSource
= aEvent
->AsMouseEvent()->mInputSource
;
2446 } else if (aEvent
->AsTouchEvent()) {
2447 startEvent
.mInputSource
= MouseEvent_Binding::MOZ_SOURCE_TOUCH
;
2452 // Dispatch to the DOM. By setting mCurrentTarget we are faking
2453 // out the ESM and telling it that the current target frame is
2454 // actually where the mouseDown occurred, otherwise it will use
2455 // the frame the mouse is currently over which may or may not be
2458 // Hold onto old target content through the event and reset after.
2459 nsCOMPtr
<nsIContent
> targetBeforeEvent
= mCurrentTargetContent
;
2461 // Set the current target to the content for the mouse down
2462 mCurrentTargetContent
= targetContent
;
2464 // Dispatch the dragstart event to the DOM.
2465 nsEventStatus status
= nsEventStatus_eIgnore
;
2466 EventDispatcher::Dispatch(targetContent
, aPresContext
, &startEvent
, nullptr,
2469 WidgetDragEvent
* event
= &startEvent
;
2471 nsCOMPtr
<nsIObserverService
> observerService
=
2472 mozilla::services::GetObserverService();
2473 // Emit observer event to allow addons to modify the DataTransfer
2475 if (observerService
) {
2476 observerService
->NotifyObservers(dataTransfer
, "on-datatransfer-available",
2480 if (status
!= nsEventStatus_eConsumeNoDefault
) {
2481 bool dragStarted
= DoDefaultDragStart(aPresContext
, event
, dataTransfer
,
2482 allowEmptyDataTransfer
, targetContent
,
2483 selection
, remoteDragStartData
,
2484 principal
, csp
, cookieJarSettings
);
2486 sActiveESM
= nullptr;
2487 MaybeFirePointerCancel(aEvent
);
2488 aEvent
->StopPropagation();
2492 // Reset mCurretTargetContent to what it was
2493 mCurrentTargetContent
= targetBeforeEvent
;
2495 // Now flush all pending notifications, for better responsiveness
2497 FlushLayout(aPresContext
);
2498 } // GenerateDragGesture
2500 void EventStateManager::DetermineDragTargetAndDefaultData(
2501 nsPIDOMWindowOuter
* aWindow
, nsIContent
* aSelectionTarget
,
2502 DataTransfer
* aDataTransfer
, bool* aAllowEmptyDataTransfer
,
2503 Selection
** aSelection
, RemoteDragStartData
** aRemoteDragStartData
,
2504 nsIContent
** aTargetNode
, nsIPrincipal
** aPrincipal
,
2505 nsIContentSecurityPolicy
** aCsp
,
2506 nsICookieJarSettings
** aCookieJarSettings
) {
2507 *aTargetNode
= nullptr;
2508 *aAllowEmptyDataTransfer
= false;
2509 nsCOMPtr
<nsIContent
> dragDataNode
;
2511 nsIContent
* editingElement
= aSelectionTarget
->IsEditable()
2512 ? aSelectionTarget
->GetEditingHost()
2515 // In chrome, only allow dragging inside editable areas.
2516 bool isChromeContext
= !aWindow
->GetBrowsingContext()->IsContent();
2517 if (isChromeContext
&& !editingElement
) {
2518 if (mGestureDownDragStartData
) {
2519 // A child process started a drag so use any data it assigned for the dnd
2521 mGestureDownDragStartData
->AddInitialDnDDataTo(aDataTransfer
, aPrincipal
,
2522 aCsp
, aCookieJarSettings
);
2523 mGestureDownDragStartData
.forget(aRemoteDragStartData
);
2524 *aAllowEmptyDataTransfer
= true;
2527 mGestureDownDragStartData
= nullptr;
2529 // GetDragData determines if a selection, link or image in the content
2530 // should be dragged, and places the data associated with the drag in the
2532 // mGestureDownContent is the node where the mousedown event for the drag
2533 // occurred, and aSelectionTarget is the node to use when a selection is
2536 bool wasAlt
= (mGestureModifiers
& MODIFIER_ALT
) != 0;
2537 nsresult rv
= nsContentAreaDragDrop::GetDragData(
2538 aWindow
, mGestureDownContent
, aSelectionTarget
, wasAlt
, aDataTransfer
,
2539 &canDrag
, aSelection
, getter_AddRefs(dragDataNode
), aCsp
,
2540 aCookieJarSettings
);
2541 if (NS_FAILED(rv
) || !canDrag
) {
2546 // if GetDragData returned a node, use that as the node being dragged.
2547 // Otherwise, if a selection is being dragged, use the node within the
2548 // selection that was dragged. Otherwise, just use the mousedown target.
2549 nsIContent
* dragContent
= mGestureDownContent
;
2551 dragContent
= dragDataNode
;
2552 else if (*aSelection
)
2553 dragContent
= aSelectionTarget
;
2555 nsIContent
* originalDragContent
= dragContent
;
2557 // If a selection isn't being dragged, look for an ancestor with the
2558 // draggable property set. If one is found, use that as the target of the
2559 // drag instead of the node that was clicked on. If a draggable node wasn't
2560 // found, just use the clicked node.
2562 while (dragContent
) {
2563 if (auto htmlElement
= nsGenericHTMLElement::FromNode(dragContent
)) {
2564 if (htmlElement
->Draggable()) {
2565 // We let draggable elements to trigger dnd even if there is no data
2566 // in the DataTransfer.
2567 *aAllowEmptyDataTransfer
= true;
2571 if (dragContent
->IsXULElement()) {
2572 // All XUL elements are draggable, so if a XUL element is
2573 // encountered, stop looking for draggable nodes and just use the
2574 // original clicked node instead.
2576 // In the future, we will want to improve this so that XUL has a
2577 // better way to specify whether something is draggable than just
2579 dragContent
= mGestureDownContent
;
2582 // otherwise, it's not an HTML or XUL element, so just keep looking
2584 dragContent
= dragContent
->GetFlattenedTreeParent();
2588 // if no node in the hierarchy was found to drag, but the GetDragData method
2589 // returned a node, use that returned node. Otherwise, nothing is draggable.
2590 if (!dragContent
&& dragDataNode
) dragContent
= dragDataNode
;
2593 // if an ancestor node was used instead, clear the drag data
2594 // XXXndeakin rework this a bit. Find a way to just not call GetDragData if
2595 // we don't need to.
2596 if (dragContent
!= originalDragContent
) aDataTransfer
->ClearAll();
2597 *aTargetNode
= dragContent
;
2598 NS_ADDREF(*aTargetNode
);
2602 bool EventStateManager::DoDefaultDragStart(
2603 nsPresContext
* aPresContext
, WidgetDragEvent
* aDragEvent
,
2604 DataTransfer
* aDataTransfer
, bool aAllowEmptyDataTransfer
,
2605 nsIContent
* aDragTarget
, Selection
* aSelection
,
2606 RemoteDragStartData
* aDragStartData
, nsIPrincipal
* aPrincipal
,
2607 nsIContentSecurityPolicy
* aCsp
, nsICookieJarSettings
* aCookieJarSettings
) {
2608 nsCOMPtr
<nsIDragService
> dragService
=
2609 do_GetService("@mozilla.org/widget/dragservice;1");
2610 if (!dragService
) return false;
2612 // Default handling for the dragstart event.
2614 // First, check if a drag session already exists. This means that the drag
2615 // service was called directly within a draggesture handler. In this case,
2616 // don't do anything more, as it is assumed that the handler is managing
2617 // drag and drop manually. Make sure to return true to indicate that a drag
2618 // began. However, if we're handling drag session for synthesized events,
2619 // we need to initialize some information of the session. Therefore, we
2620 // need to keep going for synthesized case.
2621 nsCOMPtr
<nsIDragSession
> dragSession
;
2622 dragService
->GetCurrentSession(getter_AddRefs(dragSession
));
2623 if (dragSession
&& !dragSession
->IsSynthesizedForTests()) {
2627 // No drag session is currently active, so check if a handler added
2628 // any items to be dragged. If not, there isn't anything to drag.
2630 if (aDataTransfer
) {
2631 count
= aDataTransfer
->MozItemCount();
2633 if (!aAllowEmptyDataTransfer
&& !count
) {
2637 // Get the target being dragged, which may not be the same as the
2638 // target of the mouse event. If one wasn't set in the
2639 // aDataTransfer during the event handler, just use the original
2641 nsCOMPtr
<nsIContent
> dragTarget
= aDataTransfer
->GetDragTarget();
2643 dragTarget
= aDragTarget
;
2649 // check which drag effect should initially be used. If the effect was not
2650 // set, just use all actions, otherwise Windows won't allow a drop.
2651 uint32_t action
= aDataTransfer
->EffectAllowedInt();
2652 if (action
== nsIDragService::DRAGDROP_ACTION_UNINITIALIZED
) {
2653 action
= nsIDragService::DRAGDROP_ACTION_COPY
|
2654 nsIDragService::DRAGDROP_ACTION_MOVE
|
2655 nsIDragService::DRAGDROP_ACTION_LINK
;
2658 // get any custom drag image that was set
2659 int32_t imageX
, imageY
;
2660 RefPtr
<Element
> dragImage
= aDataTransfer
->GetDragImage(&imageX
, &imageY
);
2662 nsCOMPtr
<nsIArray
> transArray
= aDataTransfer
->GetTransferables(dragTarget
);
2667 RefPtr
<DataTransfer
> dataTransfer
;
2669 // After this function returns, the DataTransfer will be cleared so it
2670 // appears empty to content. We need to pass a DataTransfer into the Drag
2671 // Session, so we need to make a copy.
2672 aDataTransfer
->Clone(aDragTarget
, eDrop
, aDataTransfer
->MozUserCancelled(),
2673 false, getter_AddRefs(dataTransfer
));
2675 // Copy over the drop effect, as Clone doesn't copy it for us.
2676 dataTransfer
->SetDropEffectInt(aDataTransfer
->DropEffectInt());
2678 MOZ_ASSERT(dragSession
->IsSynthesizedForTests());
2679 MOZ_ASSERT(aDragEvent
->mFlags
.mIsSynthesizedForTests
);
2680 // If we're initializing synthesized drag session, we should use given
2681 // DataTransfer as is because it'll be used with following drag events
2682 // in any tests, therefore it should be set to nsIDragSession.dataTransfer
2683 // because it and DragEvent.dataTransfer should be same instance.
2684 dataTransfer
= aDataTransfer
;
2687 // XXXndeakin don't really want to create a new drag DOM event
2688 // here, but we need something to pass to the InvokeDragSession
2690 RefPtr
<DragEvent
> event
=
2691 NS_NewDOMDragEvent(dragTarget
, aPresContext
, aDragEvent
);
2693 // Use InvokeDragSessionWithSelection if a selection is being dragged,
2694 // such that the image can be generated from the selected text. However,
2695 // use InvokeDragSessionWithImage if a custom image was set or something
2696 // other than a selection is being dragged.
2697 if (!dragImage
&& aSelection
) {
2698 dragService
->InvokeDragSessionWithSelection(aSelection
, aPrincipal
, aCsp
,
2699 aCookieJarSettings
, transArray
,
2700 action
, event
, dataTransfer
);
2701 } else if (aDragStartData
) {
2702 MOZ_ASSERT(XRE_IsParentProcess());
2703 dragService
->InvokeDragSessionWithRemoteImage(
2704 dragTarget
, aPrincipal
, aCsp
, aCookieJarSettings
, transArray
, action
,
2705 aDragStartData
, event
, dataTransfer
);
2707 dragService
->InvokeDragSessionWithImage(
2708 dragTarget
, aPrincipal
, aCsp
, aCookieJarSettings
, transArray
, action
,
2709 dragImage
, imageX
, imageY
, event
, dataTransfer
);
2715 void EventStateManager::ChangeZoom(bool aIncrease
) {
2716 // Send the zoom change to the top level browser so it will be handled by the
2717 // front end in the same way as other zoom actions.
2718 nsIDocShell
* docShell
= mDocument
->GetDocShell();
2723 BrowsingContext
* bc
= docShell
->GetBrowsingContext();
2728 if (XRE_IsParentProcess()) {
2729 bc
->Canonical()->DispatchWheelZoomChange(aIncrease
);
2730 } else if (BrowserChild
* child
= BrowserChild::GetFrom(docShell
)) {
2731 child
->SendWheelZoomChange(aIncrease
);
2735 void EventStateManager::DoScrollHistory(int32_t direction
) {
2736 nsCOMPtr
<nsISupports
> pcContainer(mPresContext
->GetContainerWeak());
2738 nsCOMPtr
<nsIWebNavigation
> webNav(do_QueryInterface(pcContainer
));
2740 // positive direction to go back one step, nonpositive to go forward
2741 // This is doing user-initiated history traversal, hence we want
2742 // to require that history entries we navigate to have user interaction.
2744 webNav
->GoBack(StaticPrefs::browser_navigation_requireUserInteraction(),
2748 StaticPrefs::browser_navigation_requireUserInteraction(), true);
2753 void EventStateManager::DoScrollZoom(nsIFrame
* aTargetFrame
,
2754 int32_t adjustment
) {
2755 // Exclude content in chrome docshells.
2756 nsIContent
* content
= aTargetFrame
->GetContent();
2757 if (content
&& !nsContentUtils::IsInChromeDocshell(content
->OwnerDoc())) {
2758 // Positive adjustment to decrease zoom, negative to increase
2759 const bool increase
= adjustment
<= 0;
2760 EnsureDocument(mPresContext
);
2761 ChangeZoom(increase
);
2765 static nsIFrame
* GetParentFrameToScroll(nsIFrame
* aFrame
) {
2766 if (!aFrame
) return nullptr;
2768 if (aFrame
->StyleDisplay()->mPosition
== StylePositionProperty::Fixed
&&
2769 nsLayoutUtils::IsReallyFixedPos(aFrame
))
2770 return aFrame
->PresShell()->GetRootScrollFrame();
2772 return aFrame
->GetParent();
2775 void EventStateManager::DispatchLegacyMouseScrollEvents(
2776 nsIFrame
* aTargetFrame
, WidgetWheelEvent
* aEvent
, nsEventStatus
* aStatus
) {
2778 MOZ_ASSERT(aStatus
);
2780 if (!aTargetFrame
|| *aStatus
== nsEventStatus_eConsumeNoDefault
) {
2784 // Ignore mouse wheel transaction for computing legacy mouse wheel
2785 // events' delta value.
2786 // DOM event's delta vales are computed from CSS pixels.
2787 auto scrollAmountInCSSPixels
=
2788 CSSIntSize::FromAppUnitsRounded(aEvent
->mScrollAmount
);
2790 // XXX We don't deal with fractional amount in legacy event, though the
2791 // default action handler (DoScrollText()) deals with it.
2792 // If we implemented such strict computation, we would need additional
2793 // accumulated delta values. It would made the code more complicated.
2794 // And also it would computes different delta values from older version.
2795 // It doesn't make sense to implement such code for legacy events and
2797 int32_t scrollDeltaX
, scrollDeltaY
, pixelDeltaX
, pixelDeltaY
;
2798 switch (aEvent
->mDeltaMode
) {
2799 case WheelEvent_Binding::DOM_DELTA_PAGE
:
2800 scrollDeltaX
= !aEvent
->mLineOrPageDeltaX
2802 : (aEvent
->mLineOrPageDeltaX
> 0
2803 ? UIEvent_Binding::SCROLL_PAGE_DOWN
2804 : UIEvent_Binding::SCROLL_PAGE_UP
);
2805 scrollDeltaY
= !aEvent
->mLineOrPageDeltaY
2807 : (aEvent
->mLineOrPageDeltaY
> 0
2808 ? UIEvent_Binding::SCROLL_PAGE_DOWN
2809 : UIEvent_Binding::SCROLL_PAGE_UP
);
2810 pixelDeltaX
= RoundDown(aEvent
->mDeltaX
* scrollAmountInCSSPixels
.width
);
2811 pixelDeltaY
= RoundDown(aEvent
->mDeltaY
* scrollAmountInCSSPixels
.height
);
2814 case WheelEvent_Binding::DOM_DELTA_LINE
:
2815 scrollDeltaX
= aEvent
->mLineOrPageDeltaX
;
2816 scrollDeltaY
= aEvent
->mLineOrPageDeltaY
;
2817 pixelDeltaX
= RoundDown(aEvent
->mDeltaX
* scrollAmountInCSSPixels
.width
);
2818 pixelDeltaY
= RoundDown(aEvent
->mDeltaY
* scrollAmountInCSSPixels
.height
);
2821 case WheelEvent_Binding::DOM_DELTA_PIXEL
:
2822 scrollDeltaX
= aEvent
->mLineOrPageDeltaX
;
2823 scrollDeltaY
= aEvent
->mLineOrPageDeltaY
;
2824 pixelDeltaX
= RoundDown(aEvent
->mDeltaX
);
2825 pixelDeltaY
= RoundDown(aEvent
->mDeltaY
);
2829 MOZ_CRASH("Invalid deltaMode value comes");
2832 // Send the legacy events in following order:
2833 // 1. Vertical scroll
2834 // 2. Vertical pixel scroll (even if #1 isn't consumed)
2835 // 3. Horizontal scroll (even if #1 and/or #2 are consumed)
2836 // 4. Horizontal pixel scroll (even if #3 isn't consumed)
2838 AutoWeakFrame
targetFrame(aTargetFrame
);
2840 MOZ_ASSERT(*aStatus
!= nsEventStatus_eConsumeNoDefault
&&
2841 !aEvent
->DefaultPrevented(),
2842 "If you make legacy events dispatched for default prevented wheel "
2843 "event, you need to initialize stateX and stateY");
2844 EventState stateX
, stateY
;
2846 SendLineScrollEvent(aTargetFrame
, aEvent
, stateY
, scrollDeltaY
,
2848 if (!targetFrame
.IsAlive()) {
2849 *aStatus
= nsEventStatus_eConsumeNoDefault
;
2855 SendPixelScrollEvent(aTargetFrame
, aEvent
, stateY
, pixelDeltaY
,
2857 if (!targetFrame
.IsAlive()) {
2858 *aStatus
= nsEventStatus_eConsumeNoDefault
;
2864 SendLineScrollEvent(aTargetFrame
, aEvent
, stateX
, scrollDeltaX
,
2866 if (!targetFrame
.IsAlive()) {
2867 *aStatus
= nsEventStatus_eConsumeNoDefault
;
2873 SendPixelScrollEvent(aTargetFrame
, aEvent
, stateX
, pixelDeltaX
,
2875 if (!targetFrame
.IsAlive()) {
2876 *aStatus
= nsEventStatus_eConsumeNoDefault
;
2881 if (stateY
.mDefaultPrevented
) {
2882 *aStatus
= nsEventStatus_eConsumeNoDefault
;
2883 aEvent
->PreventDefault(!stateY
.mDefaultPreventedByContent
);
2886 if (stateX
.mDefaultPrevented
) {
2887 *aStatus
= nsEventStatus_eConsumeNoDefault
;
2888 aEvent
->PreventDefault(!stateX
.mDefaultPreventedByContent
);
2892 void EventStateManager::SendLineScrollEvent(nsIFrame
* aTargetFrame
,
2893 WidgetWheelEvent
* aEvent
,
2894 EventState
& aState
, int32_t aDelta
,
2895 DeltaDirection aDeltaDirection
) {
2896 nsCOMPtr
<nsIContent
> targetContent
= aTargetFrame
->GetContent();
2897 if (!targetContent
) {
2898 targetContent
= GetFocusedElement();
2899 if (!targetContent
) {
2904 while (targetContent
->IsText()) {
2905 targetContent
= targetContent
->GetFlattenedTreeParent();
2908 WidgetMouseScrollEvent
event(aEvent
->IsTrusted(),
2909 eLegacyMouseLineOrPageScroll
, aEvent
->mWidget
);
2910 event
.mFlags
.mDefaultPrevented
= aState
.mDefaultPrevented
;
2911 event
.mFlags
.mDefaultPreventedByContent
= aState
.mDefaultPreventedByContent
;
2912 event
.mRefPoint
= aEvent
->mRefPoint
;
2913 event
.mTimeStamp
= aEvent
->mTimeStamp
;
2914 event
.mModifiers
= aEvent
->mModifiers
;
2915 event
.mButtons
= aEvent
->mButtons
;
2916 event
.mIsHorizontal
= (aDeltaDirection
== DELTA_DIRECTION_X
);
2917 event
.mDelta
= aDelta
;
2918 event
.mInputSource
= aEvent
->mInputSource
;
2920 RefPtr
<nsPresContext
> presContext
= aTargetFrame
->PresContext();
2921 nsEventStatus status
= nsEventStatus_eIgnore
;
2922 EventDispatcher::Dispatch(targetContent
, presContext
, &event
, nullptr,
2924 aState
.mDefaultPrevented
=
2925 event
.DefaultPrevented() || status
== nsEventStatus_eConsumeNoDefault
;
2926 aState
.mDefaultPreventedByContent
= event
.DefaultPreventedByContent();
2929 void EventStateManager::SendPixelScrollEvent(nsIFrame
* aTargetFrame
,
2930 WidgetWheelEvent
* aEvent
,
2932 int32_t aPixelDelta
,
2933 DeltaDirection aDeltaDirection
) {
2934 nsCOMPtr
<nsIContent
> targetContent
= aTargetFrame
->GetContent();
2935 if (!targetContent
) {
2936 targetContent
= GetFocusedElement();
2937 if (!targetContent
) {
2942 while (targetContent
->IsText()) {
2943 targetContent
= targetContent
->GetFlattenedTreeParent();
2946 WidgetMouseScrollEvent
event(aEvent
->IsTrusted(), eLegacyMousePixelScroll
,
2948 event
.mFlags
.mDefaultPrevented
= aState
.mDefaultPrevented
;
2949 event
.mFlags
.mDefaultPreventedByContent
= aState
.mDefaultPreventedByContent
;
2950 event
.mRefPoint
= aEvent
->mRefPoint
;
2951 event
.mTimeStamp
= aEvent
->mTimeStamp
;
2952 event
.mModifiers
= aEvent
->mModifiers
;
2953 event
.mButtons
= aEvent
->mButtons
;
2954 event
.mIsHorizontal
= (aDeltaDirection
== DELTA_DIRECTION_X
);
2955 event
.mDelta
= aPixelDelta
;
2956 event
.mInputSource
= aEvent
->mInputSource
;
2958 RefPtr
<nsPresContext
> presContext
= aTargetFrame
->PresContext();
2959 nsEventStatus status
= nsEventStatus_eIgnore
;
2960 EventDispatcher::Dispatch(targetContent
, presContext
, &event
, nullptr,
2962 aState
.mDefaultPrevented
=
2963 event
.DefaultPrevented() || status
== nsEventStatus_eConsumeNoDefault
;
2964 aState
.mDefaultPreventedByContent
= event
.DefaultPreventedByContent();
2967 nsIFrame
* EventStateManager::ComputeScrollTargetAndMayAdjustWheelEvent(
2968 nsIFrame
* aTargetFrame
, WidgetWheelEvent
* aEvent
,
2969 ComputeScrollTargetOptions aOptions
) {
2970 return ComputeScrollTargetAndMayAdjustWheelEvent(
2971 aTargetFrame
, aEvent
->mDeltaX
, aEvent
->mDeltaY
, aEvent
, aOptions
);
2974 // Overload ComputeScrollTargetAndMayAdjustWheelEvent method to allow passing
2975 // "test" dx and dy when looking for which scrollbarmediators to activate when
2976 // two finger down on trackpad and before any actual motion
2977 nsIFrame
* EventStateManager::ComputeScrollTargetAndMayAdjustWheelEvent(
2978 nsIFrame
* aTargetFrame
, double aDirectionX
, double aDirectionY
,
2979 WidgetWheelEvent
* aEvent
, ComputeScrollTargetOptions aOptions
) {
2980 bool isAutoDir
= false;
2981 bool honoursRoot
= false;
2982 if (MAY_BE_ADJUSTED_BY_AUTO_DIR
& aOptions
) {
2983 // If the scroll is respected as auto-dir, aDirection* should always be
2984 // equivalent to the event's delta vlaues(Currently, there are only one case
2985 // where aDirection*s have different values from the widget wheel event's
2986 // original delta values and the only case isn't auto-dir, see
2987 // ScrollbarsForWheel::TemporarilyActivateAllPossibleScrollTargets).
2988 MOZ_ASSERT(aDirectionX
== aEvent
->mDeltaX
&&
2989 aDirectionY
== aEvent
->mDeltaY
);
2991 WheelDeltaAdjustmentStrategy strategy
=
2992 GetWheelDeltaAdjustmentStrategy(*aEvent
);
2994 case WheelDeltaAdjustmentStrategy::eAutoDir
:
2996 honoursRoot
= false;
2998 case WheelDeltaAdjustmentStrategy::eAutoDirWithRootHonour
:
3007 if (aOptions
& PREFER_MOUSE_WHEEL_TRANSACTION
) {
3008 // If the user recently scrolled with the mousewheel, then they probably
3009 // want to scroll the same view as before instead of the view under the
3010 // cursor. WheelTransaction tracks the frame currently being
3011 // scrolled with the mousewheel. We consider the transaction ended when the
3012 // mouse moves more than "mousewheel.transaction.ignoremovedelay"
3013 // milliseconds after the last scroll operation, or any time the mouse moves
3014 // out of the frame, or when more than "mousewheel.transaction.timeout"
3015 // milliseconds have passed after the last operation, even if the mouse
3017 nsIFrame
* lastScrollFrame
= WheelTransaction::GetScrollTargetFrame();
3018 if (lastScrollFrame
) {
3019 nsIScrollableFrame
* scrollableFrame
=
3020 lastScrollFrame
->GetScrollTargetFrame();
3021 if (scrollableFrame
) {
3022 nsIFrame
* frameToScroll
= do_QueryFrame(scrollableFrame
);
3023 MOZ_ASSERT(frameToScroll
);
3025 ESMAutoDirWheelDeltaAdjuster
adjuster(*aEvent
, *lastScrollFrame
,
3027 // Note that calling this function will not always cause the delta to
3028 // be adjusted, it only adjusts the delta when it should, because
3029 // Adjust() internally calls ShouldBeAdjusted() before making
3033 return frameToScroll
;
3038 // If the event doesn't cause scroll actually, we cannot find scroll target
3039 // because we check if the event can cause scroll actually on each found
3040 // scrollable frame.
3041 if (!aDirectionX
&& !aDirectionY
) {
3045 bool checkIfScrollableX
;
3046 bool checkIfScrollableY
;
3048 // Always check the frame's scrollability in both the two directions for an
3049 // auto-dir scroll. That is, for an auto-dir scroll,
3050 // PREFER_ACTUAL_SCROLLABLE_TARGET_ALONG_X_AXIS and
3051 // PREFER_ACTUAL_SCROLLABLE_TARGET_ALONG_Y_AXIS should be ignored.
3052 checkIfScrollableX
= true;
3053 checkIfScrollableY
= true;
3055 checkIfScrollableX
=
3057 (aOptions
& PREFER_ACTUAL_SCROLLABLE_TARGET_ALONG_X_AXIS
);
3058 checkIfScrollableY
=
3060 (aOptions
& PREFER_ACTUAL_SCROLLABLE_TARGET_ALONG_Y_AXIS
);
3063 nsIFrame
* scrollFrame
= !(aOptions
& START_FROM_PARENT
)
3065 : GetParentFrameToScroll(aTargetFrame
);
3066 for (; scrollFrame
; scrollFrame
= GetParentFrameToScroll(scrollFrame
)) {
3067 // Check whether the frame wants to provide us with a scrollable view.
3068 nsIScrollableFrame
* scrollableFrame
= scrollFrame
->GetScrollTargetFrame();
3069 if (!scrollableFrame
) {
3070 nsMenuPopupFrame
* menuPopupFrame
= do_QueryFrame(scrollFrame
);
3071 if (menuPopupFrame
) {
3077 nsIFrame
* frameToScroll
= do_QueryFrame(scrollableFrame
);
3078 MOZ_ASSERT(frameToScroll
);
3080 if (!checkIfScrollableX
&& !checkIfScrollableY
) {
3081 return frameToScroll
;
3084 // If the frame disregards the direction the user is trying to scroll, then
3085 // it should just bubbles the scroll event up to its parental scroll frame
3087 Maybe
<layers::ScrollDirection
> disregardedDirection
=
3088 WheelHandlingUtils::GetDisregardedWheelScrollDirection(scrollFrame
);
3089 if (disregardedDirection
) {
3090 switch (disregardedDirection
.ref()) {
3091 case layers::ScrollDirection::eHorizontal
:
3092 if (checkIfScrollableX
) {
3096 case layers::ScrollDirection::eVertical
:
3097 if (checkIfScrollableY
) {
3104 layers::ScrollDirections directions
=
3105 scrollableFrame
->GetAvailableScrollingDirectionsForUserInputEvents();
3106 if ((!(directions
.contains(layers::ScrollDirection::eVertical
)) &&
3107 !(directions
.contains(layers::ScrollDirection::eHorizontal
))) ||
3108 (checkIfScrollableY
&& !checkIfScrollableX
&&
3109 !(directions
.contains(layers::ScrollDirection::eVertical
))) ||
3110 (checkIfScrollableX
&& !checkIfScrollableY
&&
3111 !(directions
.contains(layers::ScrollDirection::eHorizontal
)))) {
3115 // Computes whether the currently checked frame is scrollable by this wheel
3117 bool canScroll
= false;
3119 ESMAutoDirWheelDeltaAdjuster
adjuster(*aEvent
, *scrollFrame
, honoursRoot
);
3120 if (adjuster
.ShouldBeAdjusted()) {
3123 } else if (WheelHandlingUtils::CanScrollOn(scrollableFrame
, aDirectionX
,
3127 } else if (WheelHandlingUtils::CanScrollOn(scrollableFrame
, aDirectionX
,
3133 return frameToScroll
;
3136 // Where we are at is the block ending in a for loop.
3137 // The current frame has been checked to be unscrollable by this wheel
3138 // event, continue the loop to check its parent, if any.
3141 nsIFrame
* newFrame
= nsLayoutUtils::GetCrossDocParentFrameInProcess(
3142 aTargetFrame
->PresShell()->GetRootFrame());
3144 static_cast<ComputeScrollTargetOptions
>(aOptions
& ~START_FROM_PARENT
);
3148 return ComputeScrollTargetAndMayAdjustWheelEvent(newFrame
, aEvent
, aOptions
);
3151 nsSize
EventStateManager::GetScrollAmount(
3152 nsPresContext
* aPresContext
, WidgetWheelEvent
* aEvent
,
3153 nsIScrollableFrame
* aScrollableFrame
) {
3154 MOZ_ASSERT(aPresContext
);
3157 const bool isPage
= aEvent
->mDeltaMode
== WheelEvent_Binding::DOM_DELTA_PAGE
;
3158 if (!aScrollableFrame
) {
3159 // If there is no scrollable frame, we should use root, see below.
3161 aPresContext
->PresShell()->GetRootScrollFrameAsScrollable();
3164 if (aScrollableFrame
) {
3165 return isPage
? aScrollableFrame
->GetPageScrollAmount()
3166 : aScrollableFrame
->GetLineScrollAmount();
3169 // If there is no scrollable frame and page scrolling, use viewport size.
3171 return aPresContext
->GetVisibleArea().Size();
3174 // Otherwise use root frame's font metrics.
3176 // FIXME(emilio): Should this use the root element's style frame? The root
3177 // frame will always have the initial font. Then again it should never matter
3178 // for content, we should always have a root scrollable frame in html
3180 nsIFrame
* rootFrame
= aPresContext
->PresShell()->GetRootFrame();
3182 return nsSize(0, 0);
3184 RefPtr
<nsFontMetrics
> fm
=
3185 nsLayoutUtils::GetInflatedFontMetricsForFrame(rootFrame
);
3186 NS_ENSURE_TRUE(fm
, nsSize(0, 0));
3187 return nsSize(fm
->AveCharWidth(), fm
->MaxHeight());
3190 void EventStateManager::DoScrollText(nsIScrollableFrame
* aScrollableFrame
,
3191 WidgetWheelEvent
* aEvent
) {
3192 MOZ_ASSERT(aScrollableFrame
);
3195 nsIFrame
* scrollFrame
= do_QueryFrame(aScrollableFrame
);
3196 MOZ_ASSERT(scrollFrame
);
3198 AutoWeakFrame
scrollFrameWeak(scrollFrame
);
3199 AutoWeakFrame
eventFrameWeak(mCurrentTarget
);
3200 if (!WheelTransaction::WillHandleDefaultAction(aEvent
, scrollFrameWeak
,
3205 // Default action's actual scroll amount should be computed from device
3207 nsPresContext
* pc
= scrollFrame
->PresContext();
3208 nsSize scrollAmount
= GetScrollAmount(pc
, aEvent
, aScrollableFrame
);
3209 nsIntSize
scrollAmountInDevPixels(
3210 pc
->AppUnitsToDevPixels(scrollAmount
.width
),
3211 pc
->AppUnitsToDevPixels(scrollAmount
.height
));
3212 nsIntPoint actualDevPixelScrollAmount
=
3213 DeltaAccumulator::GetInstance()->ComputeScrollAmountForDefaultAction(
3214 aEvent
, scrollAmountInDevPixels
);
3216 // Don't scroll around the axis whose overflow style is hidden.
3217 ScrollStyles overflowStyle
= aScrollableFrame
->GetScrollStyles();
3218 if (overflowStyle
.mHorizontal
== StyleOverflow::Hidden
) {
3219 actualDevPixelScrollAmount
.x
= 0;
3221 if (overflowStyle
.mVertical
== StyleOverflow::Hidden
) {
3222 actualDevPixelScrollAmount
.y
= 0;
3225 ScrollSnapFlags snapFlags
= ScrollSnapFlags::Disabled
;
3226 mozilla::ScrollOrigin origin
= mozilla::ScrollOrigin::NotSpecified
;
3227 switch (aEvent
->mDeltaMode
) {
3228 case WheelEvent_Binding::DOM_DELTA_LINE
:
3229 origin
= mozilla::ScrollOrigin::MouseWheel
;
3230 snapFlags
= ScrollSnapFlags::IntendedDirection
;
3232 case WheelEvent_Binding::DOM_DELTA_PAGE
:
3233 origin
= mozilla::ScrollOrigin::Pages
;
3234 snapFlags
= ScrollSnapFlags::IntendedDirection
|
3235 ScrollSnapFlags::IntendedEndPosition
;
3237 case WheelEvent_Binding::DOM_DELTA_PIXEL
:
3238 origin
= mozilla::ScrollOrigin::Pixels
;
3241 MOZ_CRASH("Invalid deltaMode value comes");
3244 // We shouldn't scroll more one page at once except when over one page scroll
3245 // is allowed for the event.
3246 nsSize pageSize
= aScrollableFrame
->GetPageScrollAmount();
3247 nsIntSize
devPixelPageSize(pc
->AppUnitsToDevPixels(pageSize
.width
),
3248 pc
->AppUnitsToDevPixels(pageSize
.height
));
3249 if (!WheelPrefs::GetInstance()->IsOverOnePageScrollAllowedX(aEvent
) &&
3250 DeprecatedAbs(actualDevPixelScrollAmount
.x
.value
) >
3251 devPixelPageSize
.width
) {
3252 actualDevPixelScrollAmount
.x
= (actualDevPixelScrollAmount
.x
>= 0)
3253 ? devPixelPageSize
.width
3254 : -devPixelPageSize
.width
;
3257 if (!WheelPrefs::GetInstance()->IsOverOnePageScrollAllowedY(aEvent
) &&
3258 DeprecatedAbs(actualDevPixelScrollAmount
.y
.value
) >
3259 devPixelPageSize
.height
) {
3260 actualDevPixelScrollAmount
.y
= (actualDevPixelScrollAmount
.y
>= 0)
3261 ? devPixelPageSize
.height
3262 : -devPixelPageSize
.height
;
3265 bool isDeltaModePixel
=
3266 (aEvent
->mDeltaMode
== WheelEvent_Binding::DOM_DELTA_PIXEL
);
3269 switch (aEvent
->mScrollType
) {
3270 case WidgetWheelEvent::SCROLL_DEFAULT
:
3271 if (isDeltaModePixel
) {
3272 mode
= ScrollMode::Normal
;
3273 } else if (aEvent
->mFlags
.mHandledByAPZ
) {
3274 mode
= ScrollMode::SmoothMsd
;
3276 mode
= ScrollMode::Smooth
;
3279 case WidgetWheelEvent::SCROLL_SYNCHRONOUSLY
:
3280 mode
= ScrollMode::Instant
;
3282 case WidgetWheelEvent::SCROLL_ASYNCHRONOUSLY
:
3283 mode
= ScrollMode::Normal
;
3285 case WidgetWheelEvent::SCROLL_SMOOTHLY
:
3286 mode
= ScrollMode::Smooth
;
3289 MOZ_CRASH("Invalid mScrollType value comes");
3292 nsIScrollableFrame::ScrollMomentum momentum
=
3293 aEvent
->mIsMomentum
? nsIScrollableFrame::SYNTHESIZED_MOMENTUM_EVENT
3294 : nsIScrollableFrame::NOT_MOMENTUM
;
3296 nsIntPoint overflow
;
3297 aScrollableFrame
->ScrollBy(actualDevPixelScrollAmount
,
3298 ScrollUnit::DEVICE_PIXELS
, mode
, &overflow
, origin
,
3299 momentum
, snapFlags
);
3301 if (!scrollFrameWeak
.IsAlive()) {
3302 // If the scroll causes changing the layout, we can think that the event
3303 // has been completely consumed by the content. Then, users probably don't
3304 // want additional action.
3305 aEvent
->mOverflowDeltaX
= aEvent
->mOverflowDeltaY
= 0;
3306 } else if (isDeltaModePixel
) {
3307 aEvent
->mOverflowDeltaX
= overflow
.x
;
3308 aEvent
->mOverflowDeltaY
= overflow
.y
;
3310 aEvent
->mOverflowDeltaX
=
3311 static_cast<double>(overflow
.x
) / scrollAmountInDevPixels
.width
;
3312 aEvent
->mOverflowDeltaY
=
3313 static_cast<double>(overflow
.y
) / scrollAmountInDevPixels
.height
;
3316 // If CSS overflow properties caused not to scroll, the overflowDelta* values
3317 // should be same as delta* values since they may be used as gesture event by
3318 // widget. However, if there is another scrollable element in the ancestor
3319 // along the axis, probably users don't want the operation to cause
3320 // additional action such as moving history. In such case, overflowDelta
3321 // values should stay zero.
3322 if (scrollFrameWeak
.IsAlive()) {
3323 if (aEvent
->mDeltaX
&& overflowStyle
.mHorizontal
== StyleOverflow::Hidden
&&
3324 !ComputeScrollTargetAndMayAdjustWheelEvent(
3325 scrollFrame
, aEvent
,
3326 COMPUTE_SCROLLABLE_ANCESTOR_ALONG_X_AXIS_WITH_AUTO_DIR
)) {
3327 aEvent
->mOverflowDeltaX
= aEvent
->mDeltaX
;
3329 if (aEvent
->mDeltaY
&& overflowStyle
.mVertical
== StyleOverflow::Hidden
&&
3330 !ComputeScrollTargetAndMayAdjustWheelEvent(
3331 scrollFrame
, aEvent
,
3332 COMPUTE_SCROLLABLE_ANCESTOR_ALONG_Y_AXIS_WITH_AUTO_DIR
)) {
3333 aEvent
->mOverflowDeltaY
= aEvent
->mDeltaY
;
3338 aEvent
->mOverflowDeltaX
== 0 ||
3339 (aEvent
->mOverflowDeltaX
> 0) == (aEvent
->mDeltaX
> 0),
3340 "The sign of mOverflowDeltaX is different from the scroll direction");
3342 aEvent
->mOverflowDeltaY
== 0 ||
3343 (aEvent
->mOverflowDeltaY
> 0) == (aEvent
->mDeltaY
> 0),
3344 "The sign of mOverflowDeltaY is different from the scroll direction");
3346 WheelPrefs::GetInstance()->CancelApplyingUserPrefsFromOverflowDelta(aEvent
);
3349 void EventStateManager::DecideGestureEvent(WidgetGestureNotifyEvent
* aEvent
,
3350 nsIFrame
* targetFrame
) {
3351 NS_ASSERTION(aEvent
->mMessage
== eGestureNotify
,
3352 "DecideGestureEvent called with a non-gesture event");
3354 /* Check the ancestor tree to decide if any frame is willing* to receive
3355 * a MozPixelScroll event. If that's the case, the current touch gesture
3356 * will be used as a pan gesture; otherwise it will be a regular
3357 * mousedown/mousemove/click event.
3359 * *willing: determine if it makes sense to pan the element using scroll
3361 * - For web content: if there are any visible scrollbars on the touch point
3362 * - For XUL: if it's an scrollable element that can currently scroll in some
3365 * Note: we'll have to one-off various cases to ensure a good usable behavior
3367 WidgetGestureNotifyEvent::PanDirection panDirection
=
3368 WidgetGestureNotifyEvent::ePanNone
;
3369 bool displayPanFeedback
= false;
3370 for (nsIFrame
* current
= targetFrame
; current
;
3371 current
= nsLayoutUtils::GetCrossDocParentFrame(current
)) {
3372 // e10s - mark remote content as pannable. This is a work around since
3373 // we don't have access to remote frame scroll info here. Apz data may
3374 // assist is solving this.
3375 if (current
&& IsTopLevelRemoteTarget(current
->GetContent())) {
3376 panDirection
= WidgetGestureNotifyEvent::ePanBoth
;
3377 // We don't know when we reach bounds, so just disable feedback for now.
3378 displayPanFeedback
= false;
3382 LayoutFrameType currentFrameType
= current
->Type();
3384 // Scrollbars should always be draggable
3385 if (currentFrameType
== LayoutFrameType::Scrollbar
) {
3386 panDirection
= WidgetGestureNotifyEvent::ePanNone
;
3390 // Special check for trees
3391 if (nsTreeBodyFrame
* treeFrame
= do_QueryFrame(current
)) {
3392 if (treeFrame
->GetHorizontalOverflow()) {
3393 panDirection
= WidgetGestureNotifyEvent::ePanHorizontal
;
3395 if (treeFrame
->GetVerticalOverflow()) {
3396 panDirection
= WidgetGestureNotifyEvent::ePanVertical
;
3401 if (nsIScrollableFrame
* scrollableFrame
= do_QueryFrame(current
)) {
3402 layers::ScrollDirections scrollbarVisibility
=
3403 scrollableFrame
->GetScrollbarVisibility();
3405 // Check if we have visible scrollbars
3406 if (scrollbarVisibility
.contains(layers::ScrollDirection::eVertical
)) {
3407 panDirection
= WidgetGestureNotifyEvent::ePanVertical
;
3408 displayPanFeedback
= true;
3412 if (scrollbarVisibility
.contains(layers::ScrollDirection::eHorizontal
)) {
3413 panDirection
= WidgetGestureNotifyEvent::ePanHorizontal
;
3414 displayPanFeedback
= true;
3416 } // scrollableFrame
3418 aEvent
->mDisplayPanFeedback
= displayPanFeedback
;
3419 aEvent
->mPanDirection
= panDirection
;
3423 static nsINode
* GetCrossDocParentNode(nsINode
* aChild
) {
3424 MOZ_ASSERT(aChild
, "The child is null!");
3425 MOZ_ASSERT(XRE_IsParentProcess());
3427 nsINode
* parent
= aChild
->GetParentNode();
3428 if (parent
&& parent
->IsContent() && aChild
->IsContent()) {
3429 parent
= aChild
->AsContent()->GetFlattenedTreeParent();
3432 if (parent
|| !aChild
->IsDocument()) {
3436 return aChild
->AsDocument()->GetEmbedderElement();
3439 static bool NodeAllowsClickThrough(nsINode
* aNode
) {
3441 if (aNode
->IsAnyOfXULElements(nsGkAtoms::browser
, nsGkAtoms::tree
)) {
3444 if (aNode
->IsAnyOfXULElements(nsGkAtoms::scrollbar
, nsGkAtoms::resizer
)) {
3447 aNode
= GetCrossDocParentNode(aNode
);
3453 void EventStateManager::PostHandleKeyboardEvent(
3454 WidgetKeyboardEvent
* aKeyboardEvent
, nsIFrame
* aTargetFrame
,
3455 nsEventStatus
& aStatus
) {
3456 if (aStatus
== nsEventStatus_eConsumeNoDefault
) {
3460 RefPtr
<nsPresContext
> presContext
= mPresContext
;
3462 if (!aKeyboardEvent
->HasBeenPostedToRemoteProcess()) {
3463 if (aKeyboardEvent
->IsWaitingReplyFromRemoteProcess()) {
3464 RefPtr
<BrowserParent
> remote
=
3465 aTargetFrame
? BrowserParent::GetFrom(aTargetFrame
->GetContent())
3468 // remote is null-checked above in order to let pre-existing event
3469 // targeting code's chrome vs. content decision override in case of
3470 // disagreement in order not to disrupt non-Fission e10s mode in case
3471 // there are still bugs in the Fission-mode code. That is, if remote
3472 // is nullptr, the pre-existing event targeting code has deemed this
3473 // event to belong to chrome rather than content.
3474 BrowserParent
* preciseRemote
= BrowserParent::GetFocused();
3475 if (preciseRemote
) {
3476 remote
= preciseRemote
;
3478 // else there was a race between layout and focus tracking
3480 if (remote
&& !remote
->IsReadyToHandleInputEvents()) {
3481 // We need to dispatch the event to the browser element again if we were
3482 // waiting for the key reply but the event wasn't sent to the content
3483 // process due to the remote browser wasn't ready.
3484 WidgetKeyboardEvent
keyEvent(*aKeyboardEvent
);
3485 aKeyboardEvent
->MarkAsHandledInRemoteProcess();
3486 RefPtr
<Element
> ownerElement
= remote
->GetOwnerElement();
3487 EventDispatcher::Dispatch(ownerElement
, presContext
, &keyEvent
);
3488 if (keyEvent
.DefaultPrevented()) {
3489 aKeyboardEvent
->PreventDefault(!keyEvent
.DefaultPreventedByContent());
3490 aStatus
= nsEventStatus_eConsumeNoDefault
;
3495 // The widget expects a reply for every keyboard event. If the event wasn't
3496 // dispatched to a content process (non-e10s or no content process
3497 // running), we need to short-circuit here. Otherwise, we need to wait for
3498 // the content process to handle the event.
3499 if (aKeyboardEvent
->mWidget
) {
3500 aKeyboardEvent
->mWidget
->PostHandleKeyEvent(aKeyboardEvent
);
3502 if (aKeyboardEvent
->DefaultPrevented()) {
3503 aStatus
= nsEventStatus_eConsumeNoDefault
;
3508 // XXX Currently, our automated tests don't support mKeyNameIndex.
3509 // Therefore, we still need to handle this with keyCode.
3510 switch (aKeyboardEvent
->mKeyCode
) {
3513 // This is to prevent keyboard scrolling while alt modifier in use.
3514 if (!aKeyboardEvent
->IsAlt()) {
3515 aStatus
= nsEventStatus_eConsumeNoDefault
;
3517 // Handling the tab event after it was sent to content is bad,
3518 // because to the FocusManager the remote-browser looks like one
3519 // element, so we would just move the focus to the next element
3520 // in chrome, instead of handling it in content.
3521 if (aKeyboardEvent
->HasBeenPostedToRemoteProcess()) {
3525 EnsureDocument(presContext
);
3526 nsFocusManager
* fm
= nsFocusManager::GetFocusManager();
3527 if (fm
&& mDocument
) {
3528 // Shift focus forward or back depending on shift key
3529 bool isDocMove
= aKeyboardEvent
->IsControl() ||
3530 aKeyboardEvent
->mKeyCode
== NS_VK_F6
;
3532 aKeyboardEvent
->IsShift()
3533 ? (isDocMove
? static_cast<uint32_t>(
3534 nsIFocusManager::MOVEFOCUS_BACKWARDDOC
)
3535 : static_cast<uint32_t>(
3536 nsIFocusManager::MOVEFOCUS_BACKWARD
))
3537 : (isDocMove
? static_cast<uint32_t>(
3538 nsIFocusManager::MOVEFOCUS_FORWARDDOC
)
3539 : static_cast<uint32_t>(
3540 nsIFocusManager::MOVEFOCUS_FORWARD
));
3541 RefPtr
<Element
> result
;
3542 fm
->MoveFocus(mDocument
->GetWindow(), nullptr, dir
,
3543 nsIFocusManager::FLAG_BYKEY
, getter_AddRefs(result
));
3548 // We handle keys with no specific keycode value below.
3554 switch (aKeyboardEvent
->mKeyNameIndex
) {
3555 case KEY_NAME_INDEX_ZoomIn
:
3556 case KEY_NAME_INDEX_ZoomOut
:
3557 ChangeZoom(aKeyboardEvent
->mKeyNameIndex
== KEY_NAME_INDEX_ZoomIn
);
3558 aStatus
= nsEventStatus_eConsumeNoDefault
;
3565 static bool NeedsActiveContentChange(const WidgetMouseEvent
* aMouseEvent
) {
3566 // If the mouse event is a synthesized mouse event due to a touch, do
3567 // not set/clear the activation state. Element activation is handled by APZ.
3568 return !aMouseEvent
||
3569 aMouseEvent
->mInputSource
!= MouseEvent_Binding::MOZ_SOURCE_TOUCH
;
3572 nsresult
EventStateManager::PostHandleEvent(nsPresContext
* aPresContext
,
3573 WidgetEvent
* aEvent
,
3574 nsIFrame
* aTargetFrame
,
3575 nsEventStatus
* aStatus
,
3576 nsIContent
* aOverrideClickTarget
) {
3577 NS_ENSURE_ARG(aPresContext
);
3578 NS_ENSURE_ARG_POINTER(aStatus
);
3580 mCurrentTarget
= aTargetFrame
;
3581 mCurrentTargetContent
= nullptr;
3583 HandleCrossProcessEvent(aEvent
, aStatus
);
3584 // NOTE: the above call may have destroyed aTargetFrame, please use
3585 // mCurrentTarget henceforth. This is to avoid using it accidentally:
3586 aTargetFrame
= nullptr;
3588 // Most of the events we handle below require a frame.
3589 // Add special cases here.
3590 if (!mCurrentTarget
&& aEvent
->mMessage
!= eMouseUp
&&
3591 aEvent
->mMessage
!= eMouseDown
&& aEvent
->mMessage
!= eDragEnter
&&
3592 aEvent
->mMessage
!= eDragOver
&& aEvent
->mMessage
!= ePointerUp
&&
3593 aEvent
->mMessage
!= ePointerCancel
) {
3597 // Keep the prescontext alive, we might need it after event dispatch
3598 RefPtr
<nsPresContext
> presContext
= aPresContext
;
3599 nsresult ret
= NS_OK
;
3601 switch (aEvent
->mMessage
) {
3603 WidgetMouseEvent
* mouseEvent
= aEvent
->AsMouseEvent();
3604 if (mouseEvent
->mButton
== MouseButton::ePrimary
&&
3605 !sNormalLMouseEventInProcess
) {
3606 // We got a mouseup event while a mousedown event was being processed.
3607 // Make sure that the capturing content is cleared.
3608 PresShell::ReleaseCapturingContent();
3612 // For remote content, capture the event in the parent process at the
3613 // <xul:browser remote> element. This will ensure that subsequent
3614 // mousemove/mouseup events will continue to be dispatched to this element
3615 // and therefore forwarded to the child.
3616 if (aEvent
->HasBeenPostedToRemoteProcess() &&
3617 !PresShell::GetCapturingContent()) {
3618 if (nsIContent
* content
=
3619 mCurrentTarget
? mCurrentTarget
->GetContent() : nullptr) {
3620 PresShell::SetCapturingContent(content
, CaptureFlags::None
, aEvent
);
3622 PresShell::ReleaseCapturingContent();
3626 // If MouseEvent::PreventClickEvent() was called by chrome script,
3627 // we need to forget the clicking content and click count for the
3628 // following eMouseUp event.
3629 if (mouseEvent
->mClickEventPrevented
) {
3630 RefPtr
<EventStateManager
> esm
=
3631 ESMFromContentOrThis(aOverrideClickTarget
);
3632 switch (mouseEvent
->mButton
) {
3633 case MouseButton::ePrimary
:
3634 case MouseButton::eSecondary
:
3635 case MouseButton::eMiddle
: {
3636 LastMouseDownInfo
& mouseDownInfo
=
3637 GetLastMouseDownInfo(mouseEvent
->mButton
);
3638 mouseDownInfo
.mLastMouseDownContent
= nullptr;
3639 mouseDownInfo
.mClickCount
= 0;
3640 mouseDownInfo
.mLastMouseDownInputControlType
= Nothing();
3649 nsCOMPtr
<nsIContent
> activeContent
;
3650 // When content calls PreventDefault on pointerdown, we also call
3651 // PreventDefault on the subsequent mouse events to suppress default
3652 // behaviors. Normally, aStatus should be nsEventStatus_eConsumeNoDefault
3653 // when the event is DefaultPrevented but it's reset to
3654 // nsEventStatus_eIgnore in EventStateManager::PreHandleEvent. So we also
3655 // check if the event is DefaultPrevented.
3656 if (nsEventStatus_eConsumeNoDefault
!= *aStatus
&&
3657 !aEvent
->DefaultPrevented()) {
3658 nsCOMPtr
<nsIContent
> newFocus
;
3659 bool suppressBlur
= false;
3660 if (mCurrentTarget
) {
3661 mCurrentTarget
->GetContentForEvent(aEvent
, getter_AddRefs(newFocus
));
3662 activeContent
= mCurrentTarget
->GetContent();
3664 // In some cases, we do not want to even blur the current focused
3665 // element. Those cases are:
3666 // 1. -moz-user-focus CSS property is set to 'ignore';
3667 // 2. XUL control element has the disabled property set to 'true'.
3669 // We can't use nsIFrame::IsFocusable() because we want to blur when
3670 // we click on a visibility: none element.
3671 // We can't use nsIContent::IsFocusable() because we want to blur when
3672 // we click on a non-focusable element like a <div>.
3673 // We have to use |aEvent->mTarget| to not make sure we do not check
3674 // an anonymous node of the targeted element.
3676 mCurrentTarget
->StyleUI()->UserFocus() == StyleUserFocus::Ignore
;
3678 if (!suppressBlur
) {
3679 if (Element
* element
=
3680 Element::FromEventTargetOrNull(aEvent
->mTarget
)) {
3681 if (nsCOMPtr
<nsIDOMXULControlElement
> xulControl
=
3682 element
->AsXULControl()) {
3683 bool disabled
= false;
3684 xulControl
->GetDisabled(&disabled
);
3685 suppressBlur
= disabled
;
3691 // When a root content which isn't editable but has an editable HTML
3692 // <body> element is clicked, we should redirect the focus to the
3693 // the <body> element. E.g., when an user click bottom of the editor
3694 // where is outside of the <body> element, the <body> should be focused
3695 // and the user can edit immediately after that.
3697 // NOTE: The newFocus isn't editable that also means it's not in
3698 // designMode. In designMode, all contents are not focusable.
3699 if (newFocus
&& !newFocus
->IsEditable()) {
3700 Document
* doc
= newFocus
->GetComposedDoc();
3701 if (doc
&& newFocus
== doc
->GetRootElement()) {
3702 nsIContent
* bodyContent
=
3703 nsLayoutUtils::GetEditableRootContentByContentEditable(doc
);
3704 if (bodyContent
&& bodyContent
->GetPrimaryFrame()) {
3705 newFocus
= bodyContent
;
3710 // When the mouse is pressed, the default action is to focus the
3711 // target. Look for the nearest enclosing focusable frame.
3713 // TODO: Probably this should be moved to Element::PostHandleEvent.
3714 for (; newFocus
; newFocus
= newFocus
->GetFlattenedTreeParent()) {
3715 if (!newFocus
->IsElement()) {
3719 nsIFrame
* frame
= newFocus
->GetPrimaryFrame();
3724 // If the mousedown happened inside a popup, don't try to set focus on
3725 // one of its containing elements
3726 if (frame
->IsMenuPopupFrame()) {
3731 if (frame
->IsFocusable(/* aWithMouse = */ true)) {
3735 if (ShadowRoot
* root
= newFocus
->GetShadowRoot()) {
3736 if (root
->DelegatesFocus()) {
3737 if (Element
* firstFocusable
=
3738 root
->GetFocusDelegate(/* aWithMouse */ true)) {
3739 newFocus
= firstFocusable
;
3746 MOZ_ASSERT_IF(newFocus
, newFocus
->IsElement());
3748 if (RefPtr
<nsFocusManager
> fm
= nsFocusManager::GetFocusManager()) {
3749 // if something was found to focus, focus it. Otherwise, if the
3750 // element that was clicked doesn't have -moz-user-focus: ignore,
3751 // clear the existing focus. For -moz-user-focus: ignore, the focus
3752 // is just left as is.
3753 // Another effect of mouse clicking, handled in Selection, is that
3754 // it should update the caret position to where the mouse was
3755 // clicked. Because the focus is cleared when clicking on a
3756 // non-focusable node, the next press of the tab key will cause
3757 // focus to be shifted from the caret position instead of the root.
3759 // use the mouse flag and the noscroll flag so that the content
3760 // doesn't unexpectedly scroll when clicking an element that is
3761 // only half visible
3763 nsIFocusManager::FLAG_BYMOUSE
| nsIFocusManager::FLAG_NOSCROLL
;
3764 // If this was a touch-generated event, pass that information:
3765 if (mouseEvent
->mInputSource
==
3766 MouseEvent_Binding::MOZ_SOURCE_TOUCH
) {
3767 flags
|= nsIFocusManager::FLAG_BYTOUCH
;
3769 fm
->SetFocus(MOZ_KnownLive(newFocus
->AsElement()), flags
);
3770 } else if (!suppressBlur
) {
3771 // clear the focus within the frame and then set it as the
3773 EnsureDocument(mPresContext
);
3775 nsCOMPtr
<nsPIDOMWindowOuter
> outerWindow
= mDocument
->GetWindow();
3777 if (!activeContent
|| !activeContent
->IsXULElement())
3779 fm
->ClearFocus(outerWindow
);
3780 // Prevent switch frame if we're already not in the foreground tab
3781 // and we're in a content process.
3782 // TODO: If we were inactive frame in this tab, and now in
3783 // background tab, we shouldn't make the tab foreground, but
3784 // we should set focus to clicked document in the background
3785 // tab. However, nsFocusManager does not have proper method
3786 // for doing this. Therefore, we should skip setting focus
3787 // to clicked document for now.
3788 if (XRE_IsParentProcess() || IsInActiveTab(mDocument
)) {
3789 fm
->SetFocusedWindow(outerWindow
);
3795 // The rest is left button-specific.
3796 if (mouseEvent
->mButton
!= MouseButton::ePrimary
) {
3800 // The nearest enclosing element goes into the :active state. If we're
3801 // not an element (so we're text or something) we need to obtain
3802 // our parent element and put it into :active instead.
3803 if (activeContent
&& !activeContent
->IsElement()) {
3804 if (nsIContent
* par
= activeContent
->GetFlattenedTreeParent()) {
3805 activeContent
= par
;
3809 // if we're here, the event handler returned false, so stop
3810 // any of our own processing of a drag. Workaround for bug 43258.
3811 StopTrackingDragGesture(true);
3813 // XXX Why do we always set this is active? Active window may be changed
3814 // by a mousedown event listener.
3815 if (NeedsActiveContentChange(mouseEvent
)) {
3816 SetActiveManager(this, activeContent
);
3819 case ePointerCancel
:
3821 WidgetPointerEvent
* pointerEvent
= aEvent
->AsPointerEvent();
3822 MOZ_ASSERT(pointerEvent
);
3823 // Implicitly releasing capture for given pointer. ePointerLostCapture
3824 // should be send after ePointerUp or ePointerCancel.
3825 PointerEventHandler::ImplicitlyReleasePointerCapture(pointerEvent
);
3826 PointerEventHandler::UpdateActivePointerState(pointerEvent
);
3828 if (pointerEvent
->mMessage
== ePointerCancel
||
3829 pointerEvent
->mInputSource
== MouseEvent_Binding::MOZ_SOURCE_TOUCH
) {
3830 // After pointercancel, pointer becomes invalid so we can remove
3831 // relevant helper from table. Regarding pointerup with non-hoverable
3832 // device, the pointer also becomes invalid. Hoverable (mouse/pen)
3833 // pointers are valid all the time (not only between down/up).
3834 GenerateMouseEnterExit(pointerEvent
);
3835 mPointersEnterLeaveHelper
.Remove(pointerEvent
->pointerId
);
3840 // We can unconditionally stop capturing because
3841 // we should never be capturing when the mouse button is up
3842 PresShell::ReleaseCapturingContent();
3844 WidgetMouseEvent
* mouseUpEvent
= aEvent
->AsMouseEvent();
3845 if (NeedsActiveContentChange(mouseUpEvent
)) {
3846 ClearGlobalActiveContent(this);
3848 if (mouseUpEvent
&& EventCausesClickEvents(*mouseUpEvent
)) {
3849 // Make sure to dispatch the click even if there is no frame for
3850 // the current target element. This is required for Web compatibility.
3851 RefPtr
<EventStateManager
> esm
=
3852 ESMFromContentOrThis(aOverrideClickTarget
);
3854 esm
->PostHandleMouseUp(mouseUpEvent
, aStatus
, aOverrideClickTarget
);
3857 if (PresShell
* presShell
= presContext
->GetPresShell()) {
3858 RefPtr
<nsFrameSelection
> frameSelection
= presShell
->FrameSelection();
3859 frameSelection
->SetDragState(false);
3862 case eWheelOperationEnd
: {
3863 MOZ_ASSERT(aEvent
->IsTrusted());
3864 ScrollbarsForWheel::MayInactivate();
3865 WidgetWheelEvent
* wheelEvent
= aEvent
->AsWheelEvent();
3866 nsIScrollableFrame
* scrollTarget
=
3867 do_QueryFrame(ComputeScrollTargetAndMayAdjustWheelEvent(
3868 mCurrentTarget
, wheelEvent
,
3869 COMPUTE_DEFAULT_ACTION_TARGET_WITH_AUTO_DIR
));
3870 // If the wheel event was handled by APZ, APZ will perform the scroll
3872 if (scrollTarget
&& !WheelTransaction::HandledByApz()) {
3873 scrollTarget
->ScrollSnap();
3877 case eWheelOperationStart
: {
3878 MOZ_ASSERT(aEvent
->IsTrusted());
3880 if (*aStatus
== nsEventStatus_eConsumeNoDefault
) {
3881 ScrollbarsForWheel::Inactivate();
3885 WidgetWheelEvent
* wheelEvent
= aEvent
->AsWheelEvent();
3886 MOZ_ASSERT(wheelEvent
);
3888 // When APZ is enabled, the actual scroll animation might be handled by
3890 WheelPrefs::Action action
=
3891 wheelEvent
->mFlags
.mHandledByAPZ
3892 ? WheelPrefs::ACTION_NONE
3893 : WheelPrefs::GetInstance()->ComputeActionFor(wheelEvent
);
3895 WheelDeltaAdjustmentStrategy strategy
=
3896 GetWheelDeltaAdjustmentStrategy(*wheelEvent
);
3897 // Adjust the delta values of the wheel event if the current default
3898 // action is to horizontalize scrolling. I.e., deltaY values are set to
3899 // deltaX and deltaY and deltaZ values are set to 0.
3900 // If horizontalized, the delta values will be restored and its overflow
3901 // deltaX will become 0 when the WheelDeltaHorizontalizer instance is
3903 WheelDeltaHorizontalizer
horizontalizer(*wheelEvent
);
3904 if (WheelDeltaAdjustmentStrategy::eHorizontalize
== strategy
) {
3905 horizontalizer
.Horizontalize();
3908 // Since ComputeScrollTargetAndMayAdjustWheelEvent() may adjust the delta
3909 // if the event is auto-dir. So we use |ESMAutoDirWheelDeltaRestorer|
3911 // An instance of |ESMAutoDirWheelDeltaRestorer| is used to monitor
3912 // auto-dir adjustment which may happen during its lifetime. If the delta
3913 // values is adjusted during its lifetime, the instance will restore the
3914 // adjusted delta when it's being destrcuted.
3915 ESMAutoDirWheelDeltaRestorer
restorer(*wheelEvent
);
3916 nsIFrame
* frameToScroll
= ComputeScrollTargetAndMayAdjustWheelEvent(
3917 mCurrentTarget
, wheelEvent
,
3918 COMPUTE_DEFAULT_ACTION_TARGET_WITH_AUTO_DIR
);
3921 case WheelPrefs::ACTION_SCROLL
:
3922 case WheelPrefs::ACTION_HORIZONTALIZED_SCROLL
: {
3923 // For scrolling of default action, we should honor the mouse wheel
3926 ScrollbarsForWheel::PrepareToScrollText(this, mCurrentTarget
,
3929 if (aEvent
->mMessage
!= eWheel
||
3930 (!wheelEvent
->mDeltaX
&& !wheelEvent
->mDeltaY
)) {
3934 nsIScrollableFrame
* scrollTarget
= do_QueryFrame(frameToScroll
);
3935 ScrollbarsForWheel::SetActiveScrollTarget(scrollTarget
);
3937 nsIFrame
* rootScrollFrame
=
3940 : mCurrentTarget
->PresShell()->GetRootScrollFrame();
3941 nsIScrollableFrame
* rootScrollableFrame
= nullptr;
3942 if (rootScrollFrame
) {
3943 rootScrollableFrame
= do_QueryFrame(rootScrollFrame
);
3945 if (!scrollTarget
|| scrollTarget
== rootScrollableFrame
) {
3946 wheelEvent
->mViewPortIsOverscrolled
= true;
3948 wheelEvent
->mOverflowDeltaX
= wheelEvent
->mDeltaX
;
3949 wheelEvent
->mOverflowDeltaY
= wheelEvent
->mDeltaY
;
3950 WheelPrefs::GetInstance()->CancelApplyingUserPrefsFromOverflowDelta(
3953 DoScrollText(scrollTarget
, wheelEvent
);
3955 WheelTransaction::EndTransaction();
3956 ScrollbarsForWheel::Inactivate();
3960 case WheelPrefs::ACTION_HISTORY
: {
3961 // If this event doesn't cause eLegacyMouseLineOrPageScroll event or
3962 // the direction is oblique, don't perform history back/forward.
3963 int32_t intDelta
= wheelEvent
->GetPreferredIntDelta();
3967 DoScrollHistory(intDelta
);
3970 case WheelPrefs::ACTION_ZOOM
: {
3971 // If this event doesn't cause eLegacyMouseLineOrPageScroll event or
3972 // the direction is oblique, don't perform zoom in/out.
3973 int32_t intDelta
= wheelEvent
->GetPreferredIntDelta();
3977 DoScrollZoom(mCurrentTarget
, intDelta
);
3980 case WheelPrefs::ACTION_NONE
:
3982 bool allDeltaOverflown
= false;
3983 if (StaticPrefs::dom_event_wheel_event_groups_enabled() &&
3984 (wheelEvent
->mDeltaX
!= 0.0 || wheelEvent
->mDeltaY
!= 0.0)) {
3985 if (frameToScroll
) {
3986 WheelTransaction::WillHandleDefaultAction(
3987 wheelEvent
, frameToScroll
, mCurrentTarget
);
3989 WheelTransaction::EndTransaction();
3992 if (wheelEvent
->mFlags
.mHandledByAPZ
) {
3993 if (wheelEvent
->mCanTriggerSwipe
) {
3994 // For events that can trigger swipes, APZ needs to know whether
3995 // scrolling is possible in the requested direction. It does this
3996 // by looking at the scroll overflow values on mCanTriggerSwipe
3997 // events after they have been processed.
3998 allDeltaOverflown
= !ComputeScrollTarget(
3999 mCurrentTarget
, wheelEvent
, COMPUTE_DEFAULT_ACTION_TARGET
);
4002 // The event was processed neither by APZ nor by us, so all of the
4003 // delta values must be overflown delta values.
4004 allDeltaOverflown
= true;
4007 if (!allDeltaOverflown
) {
4010 wheelEvent
->mOverflowDeltaX
= wheelEvent
->mDeltaX
;
4011 wheelEvent
->mOverflowDeltaY
= wheelEvent
->mDeltaY
;
4012 WheelPrefs::GetInstance()->CancelApplyingUserPrefsFromOverflowDelta(
4014 wheelEvent
->mViewPortIsOverscrolled
= true;
4017 *aStatus
= nsEventStatus_eConsumeNoDefault
;
4020 case eGestureNotify
: {
4021 if (nsEventStatus_eConsumeNoDefault
!= *aStatus
) {
4022 DecideGestureEvent(aEvent
->AsGestureNotifyEvent(), mCurrentTarget
);
4028 NS_ASSERTION(aEvent
->mClass
== eDragEventClass
, "Expected a drag event");
4030 // Check if the drag is occurring inside a scrollable area. If so, scroll
4031 // the area when the mouse is near the edges.
4032 if (mCurrentTarget
&& aEvent
->mMessage
== eDragOver
) {
4033 nsIFrame
* checkFrame
= mCurrentTarget
;
4034 while (checkFrame
) {
4035 nsIScrollableFrame
* scrollFrame
= do_QueryFrame(checkFrame
);
4036 // Break out so only the innermost scrollframe is scrolled.
4037 if (scrollFrame
&& scrollFrame
->DragScroll(aEvent
)) {
4040 checkFrame
= checkFrame
->GetParent();
4044 nsCOMPtr
<nsIDragSession
> dragSession
= nsContentUtils::GetDragSession();
4045 if (!dragSession
) break;
4048 dragSession
->SetOnlyChromeDrop(false);
4050 EnsureDocument(mPresContext
);
4052 bool isChromeDoc
= nsContentUtils::IsChromeDoc(mDocument
);
4054 // the initial dataTransfer is the one from the dragstart event that
4055 // was set on the dragSession when the drag began.
4056 RefPtr
<DataTransfer
> dataTransfer
;
4057 RefPtr
<DataTransfer
> initialDataTransfer
= dragSession
->GetDataTransfer();
4059 WidgetDragEvent
* dragEvent
= aEvent
->AsDragEvent();
4061 // collect any changes to moz cursor settings stored in the event's
4063 UpdateDragDataTransfer(dragEvent
);
4065 // cancelling a dragenter or dragover event means that a drop should be
4066 // allowed, so update the dropEffect and the canDrop state to indicate
4067 // that a drag is allowed. If the event isn't cancelled, a drop won't be
4068 // allowed. Essentially, to allow a drop somewhere, specify the effects
4069 // using the effectAllowed and dropEffect properties in a dragenter or
4070 // dragover event and cancel the event. To not allow a drop somewhere,
4071 // don't cancel the event or set the effectAllowed or dropEffect to
4072 // "none". This way, if the event is just ignored, no drop will be
4074 uint32_t dropEffect
= nsIDragService::DRAGDROP_ACTION_NONE
;
4075 uint32_t action
= nsIDragService::DRAGDROP_ACTION_NONE
;
4076 if (nsEventStatus_eConsumeNoDefault
== *aStatus
) {
4077 // If the event has initialized its mDataTransfer, use it.
4078 // Or the event has not been initialized its mDataTransfer, but
4079 // it's set before dispatch because of synthesized, but without
4080 // testing session (e.g., emulating drag from another app), use it
4081 // coming from outside.
4082 // XXX Perhaps, for the latter case, we need new API because we don't
4083 // have a chance to initialize allowed effects of the session.
4084 if (dragEvent
->mDataTransfer
) {
4085 // get the dataTransfer and the dropEffect that was set on it
4086 dataTransfer
= dragEvent
->mDataTransfer
;
4087 dropEffect
= dataTransfer
->DropEffectInt();
4089 // if dragEvent->mDataTransfer is null, it means that no attempt was
4090 // made to access the dataTransfer during the event, yet the event
4091 // was cancelled. Instead, use the initial data transfer available
4092 // from the drag session. The drop effect would not have been
4093 // initialized (which is done in DragEvent::GetDataTransfer),
4094 // so set it from the drag action. We'll still want to filter it
4095 // based on the effectAllowed below.
4096 dataTransfer
= initialDataTransfer
;
4098 dragSession
->GetDragAction(&action
);
4100 // filter the drop effect based on the action. Use UNINITIALIZED as
4101 // any effect is allowed.
4102 dropEffect
= nsContentUtils::FilterDropEffect(
4103 action
, nsIDragService::DRAGDROP_ACTION_UNINITIALIZED
);
4106 // At this point, if the dataTransfer is null, it means that the
4107 // drag was originally started by directly calling the drag service.
4108 // Just assume that all effects are allowed.
4109 uint32_t effectAllowed
= nsIDragService::DRAGDROP_ACTION_UNINITIALIZED
;
4111 effectAllowed
= dataTransfer
->EffectAllowedInt();
4114 // set the drag action based on the drop effect and effect allowed.
4115 // The drop effect field on the drag transfer object specifies the
4116 // desired current drop effect. However, it cannot be used if the
4117 // effectAllowed state doesn't include that type of action. If the
4118 // dropEffect is "none", then the action will be 'none' so a drop will
4120 if (effectAllowed
== nsIDragService::DRAGDROP_ACTION_UNINITIALIZED
||
4121 dropEffect
& effectAllowed
)
4122 action
= dropEffect
;
4124 if (action
== nsIDragService::DRAGDROP_ACTION_NONE
)
4125 dropEffect
= nsIDragService::DRAGDROP_ACTION_NONE
;
4127 // inform the drag session that a drop is allowed on this node.
4128 dragSession
->SetDragAction(action
);
4129 dragSession
->SetCanDrop(action
!= nsIDragService::DRAGDROP_ACTION_NONE
);
4131 // For now, do this only for dragover.
4132 // XXXsmaug dragenter needs some more work.
4133 if (aEvent
->mMessage
== eDragOver
&& !isChromeDoc
) {
4134 // Someone has called preventDefault(), check whether is was on
4135 // content or chrome.
4136 dragSession
->SetOnlyChromeDrop(
4137 !dragEvent
->mDefaultPreventedOnContent
);
4139 } else if (aEvent
->mMessage
== eDragOver
&& !isChromeDoc
) {
4140 // No one called preventDefault(), so handle drop only in chrome.
4141 dragSession
->SetOnlyChromeDrop(true);
4143 if (ContentChild
* child
= ContentChild::GetSingleton()) {
4144 child
->SendUpdateDropEffect(action
, dropEffect
);
4146 if (aEvent
->HasBeenPostedToRemoteProcess()) {
4147 dragSession
->SetCanDrop(true);
4148 } else if (initialDataTransfer
) {
4149 // Now set the drop effect in the initial dataTransfer. This ensures
4150 // that we can get the desired drop effect in the drop event. For events
4151 // dispatched to content, the content process will take care of setting
4153 initialDataTransfer
->SetDropEffectInt(dropEffect
);
4158 if (aEvent
->mFlags
.mIsSynthesizedForTests
) {
4159 if (nsCOMPtr
<nsIDragSession
> dragSession
=
4160 nsContentUtils::GetDragSession()) {
4161 MOZ_ASSERT(dragSession
->IsSynthesizedForTests());
4162 RefPtr
<WindowContext
> sourceWC
;
4163 DebugOnly
<nsresult
> rvIgnored
=
4164 dragSession
->GetSourceWindowContext(getter_AddRefs(sourceWC
));
4165 NS_WARNING_ASSERTION(
4166 NS_SUCCEEDED(rvIgnored
),
4167 "nsIDragSession::GetSourceDocument() failed, but ignored");
4168 // If the drag source hasn't been initialized, i.e., dragstart was
4169 // consumed by the test, the test needs to dispatch "dragend" event
4170 // instead of the drag session. Therefore, it does not make sense
4171 // to set drag end point in such case (you hit assersion if you do
4174 CSSIntPoint dropPointInScreen
=
4175 Event::GetScreenCoords(aPresContext
, aEvent
, aEvent
->mRefPoint
)
4177 dragSession
->SetDragEndPointForTests(dropPointInScreen
.x
,
4178 dropPointInScreen
.y
);
4182 sLastDragOverFrame
= nullptr;
4183 ClearGlobalActiveContent(this);
4187 // make sure to fire the enter and exit_synth events after the
4188 // eDragExit event, otherwise we'll clean up too early
4189 GenerateDragDropEnterExit(presContext
, aEvent
->AsDragEvent());
4190 if (ContentChild
* child
= ContentChild::GetSingleton()) {
4191 // SendUpdateDropEffect to prevent nsIDragService from waiting for
4192 // response of forwarded dragexit event.
4193 child
->SendUpdateDropEffect(nsIDragService::DRAGDROP_ACTION_NONE
,
4194 nsIDragService::DRAGDROP_ACTION_NONE
);
4199 // If space key is released, we need to inactivate the element which was
4200 // activated by preceding space key down.
4201 // XXX Currently, we don't store the reason of activation. Therefore,
4202 // this may cancel what is activated by a mousedown, but it must not
4203 // cause actual problem in web apps in the wild since it must be
4204 // rare case that users release space key during a mouse click/drag.
4205 if (aEvent
->AsKeyboardEvent()->ShouldWorkAsSpaceKey()) {
4206 ClearGlobalActiveContent(this);
4211 WidgetKeyboardEvent
* keyEvent
= aEvent
->AsKeyboardEvent();
4212 PostHandleKeyboardEvent(keyEvent
, mCurrentTarget
, *aStatus
);
4215 case eMouseEnterIntoWidget
:
4216 if (mCurrentTarget
) {
4217 nsCOMPtr
<nsIContent
> targetContent
;
4218 mCurrentTarget
->GetContentForEvent(aEvent
,
4219 getter_AddRefs(targetContent
));
4220 SetContentState(targetContent
, ElementState::HOVER
);
4224 case eMouseExitFromWidget
:
4225 PointerEventHandler::UpdateActivePointerState(aEvent
->AsMouseEvent());
4229 case eMouseActivate
:
4230 if (mCurrentTarget
) {
4231 nsCOMPtr
<nsIContent
> targetContent
;
4232 mCurrentTarget
->GetContentForEvent(aEvent
,
4233 getter_AddRefs(targetContent
));
4234 if (!NodeAllowsClickThrough(targetContent
)) {
4235 *aStatus
= nsEventStatus_eConsumeNoDefault
;
4245 // Reset target frame to null to avoid mistargeting after reentrant event
4246 mCurrentTarget
= nullptr;
4247 mCurrentTargetContent
= nullptr;
4252 BrowserParent
* EventStateManager::GetCrossProcessTarget() {
4253 return IMEStateManager::GetActiveBrowserParent();
4256 bool EventStateManager::IsTargetCrossProcess(WidgetGUIEvent
* aEvent
) {
4257 // Check to see if there is a focused, editable content in chrome,
4258 // in that case, do not forward IME events to content
4259 Element
* focusedElement
= GetFocusedElement();
4260 if (focusedElement
&& focusedElement
->IsEditable()) {
4263 return IMEStateManager::GetActiveBrowserParent() != nullptr;
4266 void EventStateManager::NotifyDestroyPresContext(nsPresContext
* aPresContext
) {
4267 RefPtr
<nsPresContext
> presContext
= aPresContext
;
4269 IMEStateManager::OnDestroyPresContext(*presContext
);
4272 // Bug 70855: Presentation is going away, possibly for a reframe.
4273 // Reset the hover state so that if we're recreating the presentation,
4274 // we won't have the old hover state still set in the new presentation,
4275 // as if the new presentation is resized, a new element may be hovered.
4278 mPointersEnterLeaveHelper
.Clear();
4279 PointerEventHandler::NotifyDestroyPresContext(presContext
);
4282 void EventStateManager::ResetHoverState() {
4283 if (mHoverContent
) {
4284 SetContentState(nullptr, ElementState::HOVER
);
4288 void EventStateManager::SetPresContext(nsPresContext
* aPresContext
) {
4289 mPresContext
= aPresContext
;
4292 void EventStateManager::ClearFrameRefs(nsIFrame
* aFrame
) {
4293 if (aFrame
&& aFrame
== mCurrentTarget
) {
4294 mCurrentTargetContent
= aFrame
->GetContent();
4298 struct CursorImage
{
4299 gfx::IntPoint mHotspot
;
4300 nsCOMPtr
<imgIContainer
> mContainer
;
4301 ImageResolution mResolution
;
4302 bool mEarlierCursorLoading
= false;
4305 // Given the event that we're processing, and the computed cursor and hotspot,
4306 // determine whether the custom CSS cursor should be blocked (that is, not
4309 // We will not honor it all of the following are true:
4311 // * the size of the custom cursor is bigger than layout.cursor.block.max-size.
4312 // * the bounds of the cursor would end up outside of the viewport of the
4313 // top-level content document.
4315 // This is done in order to prevent hijacking the cursor, see bug 1445844 and
4317 static bool ShouldBlockCustomCursor(nsPresContext
* aPresContext
,
4318 WidgetEvent
* aEvent
,
4319 const CursorImage
& aCursor
) {
4322 aCursor
.mContainer
->GetWidth(&width
);
4323 aCursor
.mContainer
->GetHeight(&height
);
4324 aCursor
.mResolution
.ApplyTo(width
, height
);
4326 int32_t maxSize
= StaticPrefs::layout_cursor_block_max_size();
4328 if (width
<= maxSize
&& height
<= maxSize
) {
4332 auto input
= DOMIntersectionObserver::ComputeInput(*aPresContext
->Document(),
4335 if (!input
.mRootFrame
) {
4339 nsPoint point
= nsLayoutUtils::GetEventCoordinatesRelativeTo(
4340 aEvent
, RelativeTo
{input
.mRootFrame
});
4342 // The cursor size won't be affected by our full zoom in the parent process,
4343 // so undo that before checking the rect.
4344 float zoom
= aPresContext
->GetFullZoom();
4346 // Also adjust for accessibility cursor scaling factor.
4347 zoom
/= LookAndFeel::GetFloat(LookAndFeel::FloatID::CursorScale
, 1.0f
);
4349 nsSize
size(CSSPixel::ToAppUnits(width
/ zoom
),
4350 CSSPixel::ToAppUnits(height
/ zoom
));
4352 CSSPixel::ToAppUnits(ViewAs
<CSSPixel
>(aCursor
.mHotspot
.x
/ zoom
)),
4353 CSSPixel::ToAppUnits(ViewAs
<CSSPixel
>(aCursor
.mHotspot
.y
/ zoom
)));
4355 const nsRect
cursorRect(point
- hotspot
, size
);
4356 auto output
= DOMIntersectionObserver::Intersect(input
, cursorRect
);
4357 return !output
.mIntersectionRect
||
4358 !(*output
.mIntersectionRect
== cursorRect
);
4361 static gfx::IntPoint
ComputeHotspot(imgIContainer
* aContainer
,
4362 const Maybe
<gfx::Point
>& aHotspot
) {
4363 MOZ_ASSERT(aContainer
);
4365 // css3-ui says to use the CSS-specified hotspot if present,
4366 // otherwise use the intrinsic hotspot, otherwise use the top left
4369 int32_t imgWidth
, imgHeight
;
4370 aContainer
->GetWidth(&imgWidth
);
4371 aContainer
->GetHeight(&imgHeight
);
4372 auto hotspot
= gfx::IntPoint::Round(*aHotspot
);
4373 return {std::max(std::min(hotspot
.x
.value
, imgWidth
- 1), 0),
4374 std::max(std::min(hotspot
.y
.value
, imgHeight
- 1), 0)};
4377 gfx::IntPoint hotspot
;
4378 aContainer
->GetHotspotX(&hotspot
.x
.value
);
4379 aContainer
->GetHotspotY(&hotspot
.y
.value
);
4383 static CursorImage
ComputeCustomCursor(nsPresContext
* aPresContext
,
4384 WidgetEvent
* aEvent
,
4385 const nsIFrame
& aFrame
,
4386 const nsIFrame::Cursor
& aCursor
) {
4387 if (aCursor
.mAllowCustomCursor
== nsIFrame::AllowCustomCursorImage::No
) {
4390 const ComputedStyle
& style
=
4391 aCursor
.mStyle
? *aCursor
.mStyle
: *aFrame
.Style();
4393 // If we are falling back because any cursor before us is loading, let the
4395 bool loading
= false;
4396 for (const auto& image
: style
.StyleUI()->Cursor().images
.AsSpan()) {
4397 MOZ_ASSERT(image
.image
.IsImageRequestType(),
4398 "Cursor image should only parse url() types");
4400 imgRequestProxy
* req
= image
.image
.GetImageRequest();
4401 if (!req
|| NS_FAILED(req
->GetImageStatus(&status
))) {
4404 if (!(status
& imgIRequest::STATUS_LOAD_COMPLETE
)) {
4408 if (status
& imgIRequest::STATUS_ERROR
) {
4411 nsCOMPtr
<imgIContainer
> container
;
4412 req
->GetImage(getter_AddRefs(container
));
4416 StyleImageOrientation orientation
=
4417 aFrame
.StyleVisibility()->UsedImageOrientation(req
);
4418 container
= nsLayoutUtils::OrientImage(container
, orientation
);
4419 Maybe
<gfx::Point
> specifiedHotspot
=
4420 image
.has_hotspot
? Some(gfx::Point
{image
.hotspot_x
, image
.hotspot_y
})
4422 gfx::IntPoint hotspot
= ComputeHotspot(container
, specifiedHotspot
);
4423 CursorImage result
{hotspot
, std::move(container
),
4424 image
.image
.GetResolution(style
), loading
};
4425 if (ShouldBlockCustomCursor(aPresContext
, aEvent
, result
)) {
4428 // This is the one we want!
4431 return {{}, nullptr, {}, loading
};
4434 void EventStateManager::UpdateCursor(nsPresContext
* aPresContext
,
4435 WidgetMouseEvent
* aEvent
,
4436 nsIFrame
* aTargetFrame
,
4437 nsEventStatus
* aStatus
) {
4438 if (aTargetFrame
&& IsRemoteTarget(aTargetFrame
->GetContent())) {
4442 auto cursor
= StyleCursorKind::Default
;
4443 nsCOMPtr
<imgIContainer
> container
;
4444 ImageResolution resolution
;
4445 Maybe
<gfx::IntPoint
> hotspot
;
4447 if (mHidingCursorWhileTyping
&& aEvent
->IsReal()) {
4448 // Any non-synthetic mouse event makes us show the cursor again.
4449 mHidingCursorWhileTyping
= false;
4452 if (mHidingCursorWhileTyping
) {
4453 cursor
= StyleCursorKind::None
;
4454 } else if (mLockCursor
!= kInvalidCursorKind
) {
4455 // If cursor is locked just use the locked one
4456 cursor
= mLockCursor
;
4457 } else if (aTargetFrame
) {
4458 // If not locked, look for correct cursor
4459 nsPoint pt
= nsLayoutUtils::GetEventCoordinatesRelativeTo(
4460 aEvent
, RelativeTo
{aTargetFrame
});
4461 const nsIFrame::Cursor framecursor
= aTargetFrame
->GetCursor(pt
);
4462 const CursorImage customCursor
=
4463 ComputeCustomCursor(aPresContext
, aEvent
, *aTargetFrame
, framecursor
);
4465 // If the current cursor is from the same frame, and it is now
4466 // loading some new image for the cursor, we should wait for a
4467 // while rather than taking its fallback cursor directly.
4468 if (customCursor
.mEarlierCursorLoading
&&
4469 gLastCursorSourceFrame
== aTargetFrame
&&
4470 TimeStamp::NowLoRes() - gLastCursorUpdateTime
<
4471 TimeDuration::FromMilliseconds(kCursorLoadingTimeout
)) {
4474 cursor
= framecursor
.mCursor
;
4475 container
= std::move(customCursor
.mContainer
);
4476 resolution
= customCursor
.mResolution
;
4477 hotspot
= Some(customCursor
.mHotspot
);
4481 if (cursor
== StyleCursorKind::Pointer
&& IsSelectingLink(aTargetFrame
)) {
4482 cursor
= aTargetFrame
->GetWritingMode().IsVertical()
4483 ? StyleCursorKind::VerticalText
4484 : StyleCursorKind::Text
;
4486 SetCursor(cursor
, container
, resolution
, hotspot
,
4487 aTargetFrame
->GetNearestWidget(), false);
4488 gLastCursorSourceFrame
= aTargetFrame
;
4489 gLastCursorUpdateTime
= TimeStamp::NowLoRes();
4492 if (mLockCursor
!= kInvalidCursorKind
|| StyleCursorKind::Auto
!= cursor
) {
4493 *aStatus
= nsEventStatus_eConsumeDoDefault
;
4497 void EventStateManager::ClearCachedWidgetCursor(nsIFrame
* aTargetFrame
) {
4498 if (!aTargetFrame
) {
4501 nsIWidget
* aWidget
= aTargetFrame
->GetNearestWidget();
4505 aWidget
->ClearCachedCursor();
4508 void EventStateManager::StartHidingCursorWhileTyping(nsIWidget
* aWidget
) {
4509 if (mHidingCursorWhileTyping
|| sCursorSettingManager
!= this) {
4512 mHidingCursorWhileTyping
= true;
4513 SetCursor(StyleCursorKind::None
, nullptr, {}, {}, aWidget
, false);
4516 nsresult
EventStateManager::SetCursor(StyleCursorKind aCursor
,
4517 imgIContainer
* aContainer
,
4518 const ImageResolution
& aResolution
,
4519 const Maybe
<gfx::IntPoint
>& aHotspot
,
4520 nsIWidget
* aWidget
, bool aLockCursor
) {
4521 EnsureDocument(mPresContext
);
4522 NS_ENSURE_TRUE(mDocument
, NS_ERROR_FAILURE
);
4523 sCursorSettingManager
= this;
4525 NS_ENSURE_TRUE(aWidget
, NS_ERROR_FAILURE
);
4527 if (StyleCursorKind::Auto
!= aCursor
) {
4528 mLockCursor
= aCursor
;
4530 // If cursor style is set to auto we unlock the cursor again.
4531 mLockCursor
= kInvalidCursorKind
;
4536 case StyleCursorKind::Auto
:
4537 case StyleCursorKind::Default
:
4538 c
= eCursor_standard
;
4540 case StyleCursorKind::Pointer
:
4541 c
= eCursor_hyperlink
;
4543 case StyleCursorKind::Crosshair
:
4544 c
= eCursor_crosshair
;
4546 case StyleCursorKind::Move
:
4549 case StyleCursorKind::Text
:
4552 case StyleCursorKind::Wait
:
4555 case StyleCursorKind::Help
:
4558 case StyleCursorKind::NResize
:
4559 c
= eCursor_n_resize
;
4561 case StyleCursorKind::SResize
:
4562 c
= eCursor_s_resize
;
4564 case StyleCursorKind::WResize
:
4565 c
= eCursor_w_resize
;
4567 case StyleCursorKind::EResize
:
4568 c
= eCursor_e_resize
;
4570 case StyleCursorKind::NwResize
:
4571 c
= eCursor_nw_resize
;
4573 case StyleCursorKind::SeResize
:
4574 c
= eCursor_se_resize
;
4576 case StyleCursorKind::NeResize
:
4577 c
= eCursor_ne_resize
;
4579 case StyleCursorKind::SwResize
:
4580 c
= eCursor_sw_resize
;
4582 case StyleCursorKind::Copy
: // CSS3
4585 case StyleCursorKind::Alias
:
4588 case StyleCursorKind::ContextMenu
:
4589 c
= eCursor_context_menu
;
4591 case StyleCursorKind::Cell
:
4594 case StyleCursorKind::Grab
:
4597 case StyleCursorKind::Grabbing
:
4598 c
= eCursor_grabbing
;
4600 case StyleCursorKind::Progress
:
4601 c
= eCursor_spinning
;
4603 case StyleCursorKind::ZoomIn
:
4604 c
= eCursor_zoom_in
;
4606 case StyleCursorKind::ZoomOut
:
4607 c
= eCursor_zoom_out
;
4609 case StyleCursorKind::NotAllowed
:
4610 c
= eCursor_not_allowed
;
4612 case StyleCursorKind::ColResize
:
4613 c
= eCursor_col_resize
;
4615 case StyleCursorKind::RowResize
:
4616 c
= eCursor_row_resize
;
4618 case StyleCursorKind::NoDrop
:
4619 c
= eCursor_no_drop
;
4621 case StyleCursorKind::VerticalText
:
4622 c
= eCursor_vertical_text
;
4624 case StyleCursorKind::AllScroll
:
4625 c
= eCursor_all_scroll
;
4627 case StyleCursorKind::NeswResize
:
4628 c
= eCursor_nesw_resize
;
4630 case StyleCursorKind::NwseResize
:
4631 c
= eCursor_nwse_resize
;
4633 case StyleCursorKind::NsResize
:
4634 c
= eCursor_ns_resize
;
4636 case StyleCursorKind::EwResize
:
4637 c
= eCursor_ew_resize
;
4639 case StyleCursorKind::None
:
4643 MOZ_ASSERT_UNREACHABLE("Unknown cursor kind");
4644 c
= eCursor_standard
;
4648 uint32_t x
= aHotspot
? aHotspot
->x
.value
: 0;
4649 uint32_t y
= aHotspot
? aHotspot
->y
.value
: 0;
4650 aWidget
->SetCursor(nsIWidget::Cursor
{c
, aContainer
, x
, y
, aResolution
});
4654 class MOZ_STACK_CLASS ESMEventCB
: public EventDispatchingCallback
{
4656 explicit ESMEventCB(nsIContent
* aTarget
) : mTarget(aTarget
) {}
4659 void HandleEvent(EventChainPostVisitor
& aVisitor
) override
{
4660 if (aVisitor
.mPresContext
) {
4661 nsIFrame
* frame
= aVisitor
.mPresContext
->GetPrimaryFrameFor(mTarget
);
4663 frame
->HandleEvent(aVisitor
.mPresContext
, aVisitor
.mEvent
->AsGUIEvent(),
4664 &aVisitor
.mEventStatus
);
4669 nsCOMPtr
<nsIContent
> mTarget
;
4672 static UniquePtr
<WidgetMouseEvent
> CreateMouseOrPointerWidgetEvent(
4673 WidgetMouseEvent
* aMouseEvent
, EventMessage aMessage
,
4674 EventTarget
* aRelatedTarget
) {
4675 // This method does not support creating a mouse/pointer button change event
4676 // because of no data about the changing state.
4677 MOZ_ASSERT(aMessage
!= eMouseDown
);
4678 MOZ_ASSERT(aMessage
!= eMouseUp
);
4679 MOZ_ASSERT(aMessage
!= ePointerDown
);
4680 MOZ_ASSERT(aMessage
!= ePointerUp
);
4681 // This method is currently designed to create the following events.
4682 MOZ_ASSERT(aMessage
== eMouseOver
|| aMessage
== eMouseEnter
||
4683 aMessage
== eMouseOut
|| aMessage
== eMouseLeave
||
4684 aMessage
== ePointerOver
|| aMessage
== ePointerEnter
||
4685 aMessage
== ePointerOut
|| aMessage
== ePointerLeave
||
4686 aMessage
== eMouseEnterIntoWidget
||
4687 aMessage
== eMouseExitFromWidget
);
4689 WidgetPointerEvent
* sourcePointer
= aMouseEvent
->AsPointerEvent();
4690 UniquePtr
<WidgetMouseEvent
> newEvent
;
4691 if (sourcePointer
) {
4692 AUTO_PROFILER_LABEL("CreateMouseOrPointerWidgetEvent", OTHER
);
4694 WidgetPointerEvent
* newPointerEvent
= new WidgetPointerEvent(
4695 aMouseEvent
->IsTrusted(), aMessage
, aMouseEvent
->mWidget
);
4696 newPointerEvent
->mIsPrimary
= sourcePointer
->mIsPrimary
;
4697 newPointerEvent
->mWidth
= sourcePointer
->mWidth
;
4698 newPointerEvent
->mHeight
= sourcePointer
->mHeight
;
4699 newPointerEvent
->mInputSource
= sourcePointer
->mInputSource
;
4701 newEvent
= WrapUnique(newPointerEvent
);
4703 newEvent
= MakeUnique
<WidgetMouseEvent
>(aMouseEvent
->IsTrusted(), aMessage
,
4704 aMouseEvent
->mWidget
,
4705 WidgetMouseEvent::eReal
);
4708 // Inherit whether the event is synthesized by the test API or not.
4709 // Then, when the event is synthesized by a test API and handled in a remote
4710 // process, it won't be ignored. See PresShell::HandleEvent().
4711 newEvent
->mFlags
.mIsSynthesizedForTests
=
4712 aMouseEvent
->mFlags
.mIsSynthesizedForTests
;
4714 newEvent
->mRelatedTarget
= aRelatedTarget
;
4715 newEvent
->mRefPoint
= aMouseEvent
->mRefPoint
;
4716 newEvent
->mModifiers
= aMouseEvent
->mModifiers
;
4717 if (!aMouseEvent
->mFlags
.mDispatchedAtLeastOnce
&&
4718 aMouseEvent
->InputSourceSupportsHover()) {
4719 // If we synthesize a pointer event or a mouse event from another event
4720 // which changes a button state whose input soucre supports hover state and
4721 // the source event has not been dispatched yet, we should set to the button
4722 // state of the synthesizing event to previous one.
4723 // Note that we don't need to do this if the input source does not support
4724 // hover state because a WPT check the behavior (see below) and the other
4725 // browsers pass the test even though this is inconsistent behavior.
4727 sourcePointer
? MouseButton::eNotPressed
: MouseButton::ePrimary
;
4728 if (aMouseEvent
->IsPressingButton()) {
4729 // If the source event has not been dispatched into the DOM yet, we
4730 // need to remove the flag which is being pressed.
4731 newEvent
->mButtons
= static_cast<decltype(WidgetMouseEvent::mButtons
)>(
4732 aMouseEvent
->mButtons
&
4733 ~MouseButtonsFlagToChange(
4734 static_cast<MouseButton
>(aMouseEvent
->mButton
)));
4735 } else if (aMouseEvent
->IsReleasingButton()) {
4736 // If the source event has not been dispatched into the DOM yet, we
4737 // need to add the flag which is being released.
4738 newEvent
->mButtons
= static_cast<decltype(WidgetMouseEvent::mButtons
)>(
4739 aMouseEvent
->mButtons
|
4740 MouseButtonsFlagToChange(
4741 static_cast<MouseButton
>(aMouseEvent
->mButton
)));
4743 // The source event does not change the buttons state so that we can
4744 // set mButtons value as-is.
4745 newEvent
->mButtons
= aMouseEvent
->mButtons
;
4747 // Adjust pressure if it does not matches with mButtons.
4748 // FIXME: We may use wrong pressure value if the source event has not been
4749 // dispatched into the DOM yet. However, fixing this requires to store the
4750 // last pressure value somewhere.
4751 if (newEvent
->mButtons
&& aMouseEvent
->mPressure
== 0) {
4752 newEvent
->mPressure
= 0.5f
;
4753 } else if (!newEvent
->mButtons
&& aMouseEvent
->mPressure
!= 0) {
4754 newEvent
->mPressure
= 0;
4756 newEvent
->mPressure
= aMouseEvent
->mPressure
;
4759 // If the event has already been dispatched into the tree, web apps has
4760 // already handled the button state change, so the button state of the
4761 // source event has already synced.
4762 // If the input source does not have hover state, we don't need to modify
4763 // the state because the other browsers behave so and tested by
4764 // pointerevent_attributes_nohover_pointers.html even though this is
4765 // different expectation from
4766 // pointerevent_attributes_hoverable_pointers.html, but the other browsers
4767 // pass both of them.
4768 newEvent
->mButton
= aMouseEvent
->mButton
;
4769 newEvent
->mButtons
= aMouseEvent
->mButtons
;
4770 newEvent
->mPressure
= aMouseEvent
->mPressure
;
4773 newEvent
->mInputSource
= aMouseEvent
->mInputSource
;
4774 newEvent
->pointerId
= aMouseEvent
->pointerId
;
4779 nsIFrame
* EventStateManager::DispatchMouseOrPointerEvent(
4780 WidgetMouseEvent
* aMouseEvent
, EventMessage aMessage
,
4781 nsIContent
* aTargetContent
, nsIContent
* aRelatedContent
) {
4782 // http://dvcs.w3.org/hg/webevents/raw-file/default/mouse-lock.html#methods
4783 // "[When the mouse is locked on an element...e]vents that require the concept
4784 // of a mouse cursor must not be dispatched (for example: mouseover,
4786 if (PointerLockManager::IsLocked() &&
4787 (aMessage
== eMouseLeave
|| aMessage
== eMouseEnter
||
4788 aMessage
== eMouseOver
|| aMessage
== eMouseOut
)) {
4789 mCurrentTargetContent
= nullptr;
4790 nsCOMPtr
<Element
> pointerLockedElement
=
4791 PointerLockManager::GetLockedElement();
4792 if (!pointerLockedElement
) {
4793 NS_WARNING("Should have pointer locked element, but didn't.");
4796 return mPresContext
->GetPrimaryFrameFor(pointerLockedElement
);
4799 mCurrentTargetContent
= nullptr;
4801 if (!aTargetContent
) {
4805 nsCOMPtr
<nsIContent
> targetContent
= aTargetContent
;
4806 nsCOMPtr
<nsIContent
> relatedContent
= aRelatedContent
;
4808 UniquePtr
<WidgetMouseEvent
> dispatchEvent
=
4809 CreateMouseOrPointerWidgetEvent(aMouseEvent
, aMessage
, relatedContent
);
4811 AutoWeakFrame previousTarget
= mCurrentTarget
;
4812 mCurrentTargetContent
= targetContent
;
4814 nsIFrame
* targetFrame
= nullptr;
4816 nsEventStatus status
= nsEventStatus_eIgnore
;
4817 ESMEventCB
callback(targetContent
);
4818 RefPtr
<nsPresContext
> presContext
= mPresContext
;
4819 EventDispatcher::Dispatch(targetContent
, presContext
, dispatchEvent
.get(),
4820 nullptr, &status
, &callback
);
4823 // Although the primary frame was checked in event callback, it may not be
4824 // the same object after event dispatch and handling, so refetch it.
4825 targetFrame
= mPresContext
->GetPrimaryFrameFor(targetContent
);
4827 // If we are entering/leaving remote content, dispatch a mouse enter/exit
4828 // event to the remote frame.
4829 if (IsTopLevelRemoteTarget(targetContent
)) {
4830 if (aMessage
== eMouseOut
) {
4831 // For remote content, send a puppet widget mouse exit event.
4832 UniquePtr
<WidgetMouseEvent
> remoteEvent
=
4833 CreateMouseOrPointerWidgetEvent(aMouseEvent
, eMouseExitFromWidget
,
4835 remoteEvent
->mExitFrom
= Some(WidgetMouseEvent::ePuppet
);
4837 // mCurrentTarget is set to the new target, so we must reset it to the
4838 // old target and then dispatch a cross-process event. (mCurrentTarget
4839 // will be set back below.) HandleCrossProcessEvent will query for the
4840 // proper target via GetEventTarget which will return mCurrentTarget.
4841 mCurrentTarget
= targetFrame
;
4842 HandleCrossProcessEvent(remoteEvent
.get(), &status
);
4843 } else if (aMessage
== eMouseOver
) {
4844 UniquePtr
<WidgetMouseEvent
> remoteEvent
=
4845 CreateMouseOrPointerWidgetEvent(aMouseEvent
, eMouseEnterIntoWidget
,
4847 HandleCrossProcessEvent(remoteEvent
.get(), &status
);
4852 mCurrentTargetContent
= nullptr;
4853 mCurrentTarget
= previousTarget
;
4858 static nsIContent
* FindCommonAncestor(nsIContent
* aNode1
, nsIContent
* aNode2
) {
4859 if (!aNode1
|| !aNode2
) {
4862 return nsContentUtils::GetCommonFlattenedTreeAncestor(aNode1
, aNode2
);
4865 class EnterLeaveDispatcher
{
4867 EnterLeaveDispatcher(EventStateManager
* aESM
, nsIContent
* aTarget
,
4868 nsIContent
* aRelatedTarget
,
4869 WidgetMouseEvent
* aMouseEvent
,
4870 EventMessage aEventMessage
)
4871 : mESM(aESM
), mMouseEvent(aMouseEvent
), mEventMessage(aEventMessage
) {
4872 nsPIDOMWindowInner
* win
=
4873 aTarget
? aTarget
->OwnerDoc()->GetInnerWindow() : nullptr;
4874 if (aMouseEvent
->AsPointerEvent()
4875 ? win
&& win
->HasPointerEnterLeaveEventListeners()
4876 : win
&& win
->HasMouseEnterLeaveEventListeners()) {
4878 aRelatedTarget
? aRelatedTarget
->FindFirstNonChromeOnlyAccessContent()
4880 nsINode
* commonParent
= FindCommonAncestor(aTarget
, aRelatedTarget
);
4881 nsIContent
* current
= aTarget
;
4882 // Note, it is ok if commonParent is null!
4883 while (current
&& current
!= commonParent
) {
4884 if (!current
->ChromeOnlyAccess()) {
4885 mTargets
.AppendObject(current
);
4887 // mouseenter/leave is fired only on elements.
4888 current
= current
->GetFlattenedTreeParent();
4893 // TODO: Convert this to MOZ_CAN_RUN_SCRIPT (bug 1415230)
4894 MOZ_CAN_RUN_SCRIPT_BOUNDARY
void Dispatch() {
4895 if (mEventMessage
== eMouseEnter
|| mEventMessage
== ePointerEnter
) {
4896 for (int32_t i
= mTargets
.Count() - 1; i
>= 0; --i
) {
4897 mESM
->DispatchMouseOrPointerEvent(mMouseEvent
, mEventMessage
,
4898 MOZ_KnownLive(mTargets
[i
]),
4902 for (int32_t i
= 0; i
< mTargets
.Count(); ++i
) {
4903 mESM
->DispatchMouseOrPointerEvent(mMouseEvent
, mEventMessage
,
4904 MOZ_KnownLive(mTargets
[i
]),
4910 // Nothing overwrites anything after constructor. Please remove MOZ_KnownLive
4911 // and MOZ_KNOWN_LIVE if anything marked as such becomes mutable.
4912 const RefPtr
<EventStateManager
> mESM
;
4913 nsCOMArray
<nsIContent
> mTargets
;
4914 MOZ_KNOWN_LIVE nsCOMPtr
<nsIContent
> mRelatedTarget
;
4915 WidgetMouseEvent
* mMouseEvent
;
4916 EventMessage mEventMessage
;
4919 void EventStateManager::NotifyMouseOut(WidgetMouseEvent
* aMouseEvent
,
4920 nsIContent
* aMovingInto
) {
4921 const bool isPointer
= aMouseEvent
->mClass
== ePointerEventClass
;
4922 LogModule
* const logModule
=
4923 isPointer
? sPointerBoundaryLog
: sMouseBoundaryLog
;
4925 RefPtr
<OverOutElementsWrapper
> wrapper
= GetWrapperByEventID(aMouseEvent
);
4927 // If there is no deepest "leave" event target, that means the last "over"
4928 // target has already been removed from the tree. Therefore, checking only
4929 // the "leave" event target is enough.
4930 if (!wrapper
|| !wrapper
->GetDeepestLeaveEventTarget()) {
4933 // Before firing "out" and/or "leave" events, check for recursion
4934 if (wrapper
->IsDispatchingOutEventOnLastOverEventTarget()) {
4938 MOZ_LOG(logModule
, LogLevel::Info
,
4939 ("NotifyMouseOut: the source event is %s (IsReal()=%s)",
4940 ToChar(aMouseEvent
->mMessage
),
4941 aMouseEvent
->IsReal() ? "true" : "false"));
4943 // XXX If a content node is a container of remove content, it should be
4944 // replaced with them and its children should not be visible. Therefore,
4945 // if the deepest "enter" target is not the last "over" target, i.e., the
4946 // last "over" target has been removed from the DOM tree, it means that the
4947 // child/descendant was not replaced by remote content. So,
4948 // wrapper->GetOutEventTaget() may be enough here.
4949 if (RefPtr
<nsFrameLoaderOwner
> flo
=
4950 do_QueryObject(wrapper
->GetDeepestLeaveEventTarget())) {
4951 if (BrowsingContext
* bc
= flo
->GetExtantBrowsingContext()) {
4952 if (nsIDocShell
* docshell
= bc
->GetDocShell()) {
4953 if (RefPtr
<nsPresContext
> presContext
= docshell
->GetPresContext()) {
4954 EventStateManager
* kidESM
= presContext
->EventStateManager();
4955 // Not moving into any element in this subdocument
4956 MOZ_LOG(logModule
, LogLevel::Info
,
4957 ("Notifying child EventStateManager (%p) of \"out\" "
4960 kidESM
->NotifyMouseOut(aMouseEvent
, nullptr);
4965 // That could have caused DOM events which could wreak havoc. Reverify
4966 // things and be careful.
4967 if (!wrapper
->GetDeepestLeaveEventTarget()) {
4971 wrapper
->WillDispatchOutAndOrLeaveEvent();
4973 // Don't touch hover state if aMovingInto is non-null. Caller will update
4974 // hover state itself, and we have optimizations for hover switching between
4975 // two nearby elements both deep in the DOM tree that would be defeated by
4976 // switching the hover state to null here.
4977 if (!aMovingInto
&& !isPointer
) {
4979 SetContentState(nullptr, ElementState::HOVER
);
4982 EnterLeaveDispatcher
leaveDispatcher(
4983 this, wrapper
->GetDeepestLeaveEventTarget(), aMovingInto
, aMouseEvent
,
4984 isPointer
? ePointerLeave
: eMouseLeave
);
4986 // "out" events hould be fired only when the deepest "leave" event target
4987 // is the last "over" event target.
4988 if (nsCOMPtr
<nsIContent
> outEventTarget
= wrapper
->GetOutEventTarget()) {
4989 MOZ_LOG(logModule
, LogLevel::Info
,
4990 ("Dispatching %s event to %s (%p)",
4991 isPointer
? "ePointerOut" : "eMouseOut",
4992 outEventTarget
? ToString(*outEventTarget
).c_str() : "nullptr",
4993 outEventTarget
.get()));
4994 DispatchMouseOrPointerEvent(aMouseEvent
,
4995 isPointer
? ePointerOut
: eMouseOut
,
4996 outEventTarget
, aMovingInto
);
4999 MOZ_LOG(logModule
, LogLevel::Info
,
5000 ("Dispatching %s event to %s (%p) and its ancestors",
5001 isPointer
? "ePointerLeave" : "eMouseLeave",
5002 wrapper
->GetDeepestLeaveEventTarget()
5003 ? ToString(*wrapper
->GetDeepestLeaveEventTarget()).c_str()
5005 wrapper
->GetDeepestLeaveEventTarget()));
5006 leaveDispatcher
.Dispatch();
5008 MOZ_LOG(logModule
, LogLevel::Info
,
5009 ("Dispatched \"out\" and/or \"leave\" events"));
5010 wrapper
->DidDispatchOutAndOrLeaveEvent();
5013 void EventStateManager::RecomputeMouseEnterStateForRemoteFrame(
5014 Element
& aElement
) {
5015 if (!mMouseEnterLeaveHelper
||
5016 mMouseEnterLeaveHelper
->GetDeepestLeaveEventTarget() != &aElement
) {
5020 if (BrowserParent
* remote
= BrowserParent::GetFrom(&aElement
)) {
5021 remote
->MouseEnterIntoWidget();
5025 void EventStateManager::NotifyMouseOver(WidgetMouseEvent
* aMouseEvent
,
5026 nsIContent
* aContent
) {
5027 NS_ASSERTION(aContent
, "Mouse must be over something");
5029 const bool isPointer
= aMouseEvent
->mClass
== ePointerEventClass
;
5030 LogModule
* const logModule
=
5031 isPointer
? sPointerBoundaryLog
: sMouseBoundaryLog
;
5033 RefPtr
<OverOutElementsWrapper
> wrapper
= GetWrapperByEventID(aMouseEvent
);
5035 // If we have next "out" event target and it's the new "over" target, we don't
5036 // need to dispatch "out" nor "enter" event.
5037 if (!wrapper
|| aContent
== wrapper
->GetOutEventTarget()) {
5041 // Before firing "over" and "enter" events, check for recursion
5042 if (wrapper
->IsDispatchingOverEventOn(aContent
)) {
5046 MOZ_LOG(logModule
, LogLevel::Info
,
5047 ("NotifyMouseOver: the source event is %s (IsReal()=%s)",
5048 ToChar(aMouseEvent
->mMessage
),
5049 aMouseEvent
->IsReal() ? "true" : "false"));
5051 // Check to see if we're a subdocument and if so update the parent
5052 // document's ESM state to indicate that the mouse is over the
5053 // content associated with our subdocument.
5054 EnsureDocument(mPresContext
);
5055 if (Document
* parentDoc
= mDocument
->GetInProcessParentDocument()) {
5056 if (nsCOMPtr
<nsIContent
> docContent
= mDocument
->GetEmbedderElement()) {
5057 if (PresShell
* parentPresShell
= parentDoc
->GetPresShell()) {
5058 RefPtr
<EventStateManager
> parentESM
=
5059 parentPresShell
->GetPresContext()->EventStateManager();
5060 MOZ_LOG(logModule
, LogLevel::Info
,
5061 ("Notifying parent EventStateManager (%p) of \"over\" "
5064 parentESM
->NotifyMouseOver(aMouseEvent
, docContent
);
5068 // Firing the DOM event in the parent document could cause all kinds
5069 // of havoc. Reverify and take care.
5070 if (aContent
== wrapper
->GetOutEventTarget()) {
5074 // Remember the deepest leave event target as the related content for the
5075 // DispatchMouseOrPointerEvent() call below, since NotifyMouseOut() resets it,
5077 nsCOMPtr
<nsIContent
> deepestLeaveEventTarget
=
5078 wrapper
->GetDeepestLeaveEventTarget();
5080 EnterLeaveDispatcher
enterDispatcher(this, aContent
, deepestLeaveEventTarget
,
5082 isPointer
? ePointerEnter
: eMouseEnter
);
5085 SetContentState(aContent
, ElementState::HOVER
);
5088 NotifyMouseOut(aMouseEvent
, aContent
);
5090 wrapper
->WillDispatchOverAndEnterEvent(aContent
);
5093 // XXX If aContent has already been removed from the DOM tree, what should we
5094 // do? At least, dispatching `mouseover` on it is odd.
5095 MOZ_LOG(logModule
, LogLevel::Info
,
5096 ("Dispatching %s event to %s (%p)",
5097 isPointer
? "ePointerOver" : "eMoustOver",
5098 aContent
? ToString(*aContent
).c_str() : "nullptr", aContent
));
5099 wrapper
->mLastOverFrame
= DispatchMouseOrPointerEvent(
5100 aMouseEvent
, isPointer
? ePointerOver
: eMouseOver
, aContent
,
5101 deepestLeaveEventTarget
);
5103 MOZ_LOG(logModule
, LogLevel::Info
,
5104 ("Dispatching %s event to %s (%p) and its ancestors",
5105 isPointer
? "ePointerEnter" : "eMouseEnter",
5106 aContent
? ToString(*aContent
).c_str() : "nullptr", aContent
));
5107 enterDispatcher
.Dispatch();
5109 MOZ_LOG(logModule
, LogLevel::Info
,
5110 ("Dispatched \"over\" and \"enter\" events (the original \"over\" "
5111 "event target was in the document %p, and now in %p)",
5112 aContent
->GetComposedDoc(), mDocument
.get()));
5113 wrapper
->DidDispatchOverAndEnterEvent(
5114 aContent
->GetComposedDoc() == mDocument
? aContent
: nullptr);
5117 // Returns the center point of the window's client area. This is
5118 // in widget coordinates, i.e. relative to the widget's top-left
5119 // corner, not in screen coordinates, the same units that UIEvent::
5120 // refpoint is in. It may not be the exact center of the window if
5121 // the platform requires rounding the coordinate.
5122 static LayoutDeviceIntPoint
GetWindowClientRectCenter(nsIWidget
* aWidget
) {
5123 NS_ENSURE_TRUE(aWidget
, LayoutDeviceIntPoint(0, 0));
5125 LayoutDeviceIntRect rect
= aWidget
->GetClientBounds();
5126 LayoutDeviceIntPoint
point(rect
.x
+ rect
.width
/ 2, rect
.y
+ rect
.height
/ 2);
5127 int32_t round
= aWidget
->RoundsWidgetCoordinatesTo();
5128 point
.x
= point
.x
/ round
* round
;
5129 point
.y
= point
.y
/ round
* round
;
5130 return point
- aWidget
->WidgetToScreenOffset();
5133 void EventStateManager::GeneratePointerEnterExit(EventMessage aMessage
,
5134 WidgetMouseEvent
* aEvent
) {
5135 WidgetPointerEvent
pointerEvent(*aEvent
);
5136 pointerEvent
.mMessage
= aMessage
;
5137 GenerateMouseEnterExit(&pointerEvent
);
5141 void EventStateManager::UpdateLastRefPointOfMouseEvent(
5142 WidgetMouseEvent
* aMouseEvent
) {
5143 if (aMouseEvent
->mMessage
!= eMouseMove
&&
5144 aMouseEvent
->mMessage
!= ePointerMove
) {
5148 // Mouse movement is reported on the MouseEvent.movement{X,Y} fields.
5149 // Movement is calculated in UIEvent::GetMovementPoint() as:
5150 // previous_mousemove_mRefPoint - current_mousemove_mRefPoint.
5151 if (PointerLockManager::IsLocked() && aMouseEvent
->mWidget
) {
5152 // The pointer is locked. If the pointer is not located at the center of
5153 // the window, dispatch a synthetic mousemove to return the pointer there.
5154 // Doing this between "real" pointer moves gives the impression that the
5155 // (locked) pointer can continue moving and won't stop at the screen
5156 // boundary. We cancel the synthetic event so that we don't end up
5157 // dispatching the centering move event to content.
5158 aMouseEvent
->mLastRefPoint
=
5159 GetWindowClientRectCenter(aMouseEvent
->mWidget
);
5161 } else if (sLastRefPoint
== kInvalidRefPoint
) {
5162 // We don't have a valid previous mousemove mRefPoint. This is either
5163 // the first move we've encountered, or the mouse has just re-entered
5164 // the application window. We should report (0,0) movement for this
5165 // case, so make the current and previous mRefPoints the same.
5166 aMouseEvent
->mLastRefPoint
= aMouseEvent
->mRefPoint
;
5168 aMouseEvent
->mLastRefPoint
= sLastRefPoint
;
5173 void EventStateManager::ResetPointerToWindowCenterWhilePointerLocked(
5174 WidgetMouseEvent
* aMouseEvent
) {
5175 MOZ_ASSERT(PointerLockManager::IsLocked());
5176 if ((aMouseEvent
->mMessage
!= eMouseMove
&&
5177 aMouseEvent
->mMessage
!= ePointerMove
) ||
5178 !aMouseEvent
->mWidget
) {
5182 // We generate pointermove from mousemove event, so only synthesize native
5183 // mouse move and update sSynthCenteringPoint by mousemove event.
5184 bool updateSynthCenteringPoint
= aMouseEvent
->mMessage
== eMouseMove
;
5186 // The pointer is locked. If the pointer is not located at the center of
5187 // the window, dispatch a synthetic mousemove to return the pointer there.
5188 // Doing this between "real" pointer moves gives the impression that the
5189 // (locked) pointer can continue moving and won't stop at the screen
5190 // boundary. We cancel the synthetic event so that we don't end up
5191 // dispatching the centering move event to content.
5192 LayoutDeviceIntPoint center
= GetWindowClientRectCenter(aMouseEvent
->mWidget
);
5194 if (aMouseEvent
->mRefPoint
!= center
&& updateSynthCenteringPoint
) {
5195 // Mouse move doesn't finish at the center of the window. Dispatch a
5196 // synthetic native mouse event to move the pointer back to the center
5197 // of the window, to faciliate more movement. But first, record that
5198 // we've dispatched a synthetic mouse movement, so we can cancel it
5199 // in the other branch here.
5200 sSynthCenteringPoint
= center
;
5201 // XXX Once we fix XXX comments in SetPointerLock about this API, we could
5202 // restrict that this API works only in the automation mode or in the
5203 // pointer locked situation.
5204 aMouseEvent
->mWidget
->SynthesizeNativeMouseMove(
5205 center
+ aMouseEvent
->mWidget
->WidgetToScreenOffset(), nullptr);
5206 } else if (aMouseEvent
->mRefPoint
== sSynthCenteringPoint
) {
5207 // This is the "synthetic native" event we dispatched to re-center the
5208 // pointer. Cancel it so we don't expose the centering move to content.
5209 aMouseEvent
->StopPropagation();
5210 // Clear sSynthCenteringPoint so we don't cancel other events
5211 // targeted at the center.
5212 if (updateSynthCenteringPoint
) {
5213 sSynthCenteringPoint
= kInvalidRefPoint
;
5219 void EventStateManager::UpdateLastPointerPosition(
5220 WidgetMouseEvent
* aMouseEvent
) {
5221 if (aMouseEvent
->mMessage
!= eMouseMove
) {
5224 sLastRefPoint
= aMouseEvent
->mRefPoint
;
5227 void EventStateManager::GenerateMouseEnterExit(WidgetMouseEvent
* aMouseEvent
) {
5228 EnsureDocument(mPresContext
);
5229 if (!mDocument
) return;
5231 // Hold onto old target content through the event and reset after.
5232 nsCOMPtr
<nsIContent
> targetBeforeEvent
= mCurrentTargetContent
;
5234 switch (aMouseEvent
->mMessage
) {
5238 case ePointerGotCapture
: {
5239 // Get the target content target (mousemove target == mouseover target)
5240 nsCOMPtr
<nsIContent
> targetElement
= GetEventTargetContent(aMouseEvent
);
5241 if (!targetElement
) {
5242 // We're always over the document root, even if we're only
5243 // over dead space in a page (whose frame is not associated with
5244 // any content) or in print preview dead space
5245 targetElement
= mDocument
->GetRootElement();
5247 if (targetElement
) {
5248 NotifyMouseOver(aMouseEvent
, targetElement
);
5252 // Get the target content target (mousemove target == mouseover target)
5253 nsCOMPtr
<nsIContent
> targetElement
= GetEventTargetContent(aMouseEvent
);
5254 if (!targetElement
) {
5255 // We're always over the document root, even if we're only
5256 // over dead space in a page (whose frame is not associated with
5257 // any content) or in print preview dead space
5258 targetElement
= mDocument
->GetRootElement();
5260 if (targetElement
) {
5261 RefPtr
<OverOutElementsWrapper
> helper
=
5262 GetWrapperByEventID(aMouseEvent
);
5264 helper
->OverrideOverEventTarget(targetElement
);
5266 NotifyMouseOut(aMouseEvent
, nullptr);
5270 case ePointerCancel
:
5271 case eMouseExitFromWidget
: {
5272 // This is actually the window mouse exit or pointer leave event. We're
5273 // not moving into any new element.
5275 RefPtr
<OverOutElementsWrapper
> helper
= GetWrapperByEventID(aMouseEvent
);
5276 if (helper
&& helper
->mLastOverFrame
&&
5277 nsContentUtils::GetTopLevelWidget(aMouseEvent
->mWidget
) !=
5278 nsContentUtils::GetTopLevelWidget(
5279 helper
->mLastOverFrame
->GetNearestWidget())) {
5280 // the Mouse/PointerOut event widget doesn't have same top widget with
5281 // mLastOverFrame, it's a spurious event for mLastOverFrame
5285 // Reset sLastRefPoint, so that we'll know not to report any
5286 // movement the next time we re-enter the window.
5287 sLastRefPoint
= kInvalidRefPoint
;
5289 NotifyMouseOut(aMouseEvent
, nullptr);
5295 // reset mCurretTargetContent to what it was
5296 mCurrentTargetContent
= targetBeforeEvent
;
5299 OverOutElementsWrapper
* EventStateManager::GetWrapperByEventID(
5300 WidgetMouseEvent
* aEvent
) {
5301 WidgetPointerEvent
* pointer
= aEvent
->AsPointerEvent();
5303 MOZ_ASSERT(aEvent
->AsMouseEvent() != nullptr);
5304 if (!mMouseEnterLeaveHelper
) {
5305 mMouseEnterLeaveHelper
= new OverOutElementsWrapper(
5306 OverOutElementsWrapper::BoundaryEventType::Mouse
);
5308 return mMouseEnterLeaveHelper
;
5310 return mPointersEnterLeaveHelper
.GetOrInsertNew(
5311 pointer
->pointerId
, OverOutElementsWrapper::BoundaryEventType::Pointer
);
5315 void EventStateManager::SetPointerLock(nsIWidget
* aWidget
,
5316 nsPresContext
* aPresContext
) {
5317 // Reset mouse wheel transaction
5318 WheelTransaction::EndTransaction();
5320 // Deal with DnD events
5321 nsCOMPtr
<nsIDragService
> dragService
=
5322 do_GetService("@mozilla.org/widget/dragservice;1");
5324 if (PointerLockManager::IsLocked()) {
5325 MOZ_ASSERT(aWidget
, "Locking pointer requires a widget");
5326 MOZ_ASSERT(aPresContext
, "Locking pointer requires a presContext");
5328 // Release all pointer capture when a pointer lock is successfully applied
5330 PointerEventHandler::ReleaseAllPointerCapture();
5332 // Store the last known ref point so we can reposition the pointer after
5334 sPreLockScreenPoint
= LayoutDeviceIntPoint::Round(
5335 sLastScreenPoint
* aPresContext
->CSSToDevPixelScale());
5337 // Fire a synthetic mouse move to ensure event state is updated. We first
5338 // set the mouse to the center of the window, so that the mouse event
5339 // doesn't report any movement.
5340 // XXX Cannot we do synthesize the native mousemove in the parent process
5341 // with calling LockNativePointer below? Then, we could make this API
5342 // work only in the automation mode.
5343 sLastRefPoint
= GetWindowClientRectCenter(aWidget
);
5344 aWidget
->SynthesizeNativeMouseMove(
5345 sLastRefPoint
+ aWidget
->WidgetToScreenOffset(), nullptr);
5349 dragService
->Suppress();
5352 // Activate native pointer lock on platforms where it is required (Wayland)
5353 aWidget
->LockNativePointer();
5356 // Deactivate native pointer lock on platforms where it is required
5357 aWidget
->UnlockNativePointer();
5360 // Reset SynthCenteringPoint to invalid so that next time we start
5361 // locking pointer, it has its initial value.
5362 sSynthCenteringPoint
= kInvalidRefPoint
;
5364 // Unlocking, so return pointer to the original position by firing a
5365 // synthetic mouse event. We first reset sLastRefPoint to its
5366 // pre-pointerlock position, so that the synthetic mouse event reports
5368 sLastRefPoint
= sPreLockScreenPoint
- aWidget
->WidgetToScreenOffset();
5369 // XXX Cannot we do synthesize the native mousemove in the parent process
5370 // with calling `UnlockNativePointer` above? Then, we could make this
5371 // API work only in the automation mode.
5372 aWidget
->SynthesizeNativeMouseMove(sPreLockScreenPoint
, nullptr);
5377 dragService
->Unsuppress();
5382 void EventStateManager::GenerateDragDropEnterExit(nsPresContext
* aPresContext
,
5383 WidgetDragEvent
* aDragEvent
) {
5384 // Hold onto old target content through the event and reset after.
5385 nsCOMPtr
<nsIContent
> targetBeforeEvent
= mCurrentTargetContent
;
5387 switch (aDragEvent
->mMessage
) {
5389 // when dragging from one frame to another, events are fired in the
5390 // order: dragexit, dragenter, dragleave
5391 if (sLastDragOverFrame
!= mCurrentTarget
) {
5392 // We'll need the content, too, to check if it changed separately from
5394 nsCOMPtr
<nsIContent
> lastContent
;
5395 nsCOMPtr
<nsIContent
> targetContent
;
5396 mCurrentTarget
->GetContentForEvent(aDragEvent
,
5397 getter_AddRefs(targetContent
));
5398 if (targetContent
&& targetContent
->IsText()) {
5399 targetContent
= targetContent
->GetFlattenedTreeParent();
5402 if (sLastDragOverFrame
) {
5403 // The frame has changed but the content may not have. Check before
5404 // dispatching to content
5405 sLastDragOverFrame
->GetContentForEvent(aDragEvent
,
5406 getter_AddRefs(lastContent
));
5407 if (lastContent
&& lastContent
->IsText()) {
5408 lastContent
= lastContent
->GetFlattenedTreeParent();
5411 RefPtr
<nsPresContext
> presContext
= sLastDragOverFrame
->PresContext();
5412 FireDragEnterOrExit(presContext
, aDragEvent
, eDragExit
, targetContent
,
5413 lastContent
, sLastDragOverFrame
);
5414 nsIContent
* target
= sLastDragOverFrame
5415 ? sLastDragOverFrame
.GetFrame()->GetContent()
5417 // XXXedgar, look like we need to consider fission OOP iframe, too.
5418 if (IsTopLevelRemoteTarget(target
)) {
5419 // Dragging something and moving from web content to chrome only
5420 // fires dragexit and dragleave to xul:browser. We have to forward
5421 // dragexit to sLastDragOverFrame when its content is a remote
5422 // target. We don't forward dragleave since it's generated from
5424 WidgetDragEvent
remoteEvent(aDragEvent
->IsTrusted(), eDragExit
,
5425 aDragEvent
->mWidget
);
5426 remoteEvent
.AssignDragEventData(*aDragEvent
, true);
5427 remoteEvent
.mFlags
.mIsSynthesizedForTests
=
5428 aDragEvent
->mFlags
.mIsSynthesizedForTests
;
5429 nsEventStatus remoteStatus
= nsEventStatus_eIgnore
;
5430 HandleCrossProcessEvent(&remoteEvent
, &remoteStatus
);
5434 AutoWeakFrame currentTraget
= mCurrentTarget
;
5435 FireDragEnterOrExit(aPresContext
, aDragEvent
, eDragEnter
, lastContent
,
5436 targetContent
, currentTraget
);
5438 if (sLastDragOverFrame
) {
5439 RefPtr
<nsPresContext
> presContext
= sLastDragOverFrame
->PresContext();
5440 FireDragEnterOrExit(presContext
, aDragEvent
, eDragLeave
,
5441 targetContent
, lastContent
, sLastDragOverFrame
);
5444 sLastDragOverFrame
= mCurrentTarget
;
5449 // This is actually the window mouse exit event.
5450 if (sLastDragOverFrame
) {
5451 nsCOMPtr
<nsIContent
> lastContent
;
5452 sLastDragOverFrame
->GetContentForEvent(aDragEvent
,
5453 getter_AddRefs(lastContent
));
5455 RefPtr
<nsPresContext
> lastDragOverFramePresContext
=
5456 sLastDragOverFrame
->PresContext();
5457 FireDragEnterOrExit(lastDragOverFramePresContext
, aDragEvent
, eDragExit
,
5458 nullptr, lastContent
, sLastDragOverFrame
);
5459 FireDragEnterOrExit(lastDragOverFramePresContext
, aDragEvent
,
5460 eDragLeave
, nullptr, lastContent
,
5461 sLastDragOverFrame
);
5463 sLastDragOverFrame
= nullptr;
5471 // reset mCurretTargetContent to what it was
5472 mCurrentTargetContent
= targetBeforeEvent
;
5474 // Now flush all pending notifications, for better responsiveness.
5475 FlushLayout(aPresContext
);
5478 void EventStateManager::FireDragEnterOrExit(nsPresContext
* aPresContext
,
5479 WidgetDragEvent
* aDragEvent
,
5480 EventMessage aMessage
,
5481 nsIContent
* aRelatedTarget
,
5482 nsIContent
* aTargetContent
,
5483 AutoWeakFrame
& aTargetFrame
) {
5484 MOZ_ASSERT(aMessage
== eDragLeave
|| aMessage
== eDragExit
||
5485 aMessage
== eDragEnter
);
5486 nsEventStatus status
= nsEventStatus_eIgnore
;
5487 WidgetDragEvent
event(aDragEvent
->IsTrusted(), aMessage
, aDragEvent
->mWidget
);
5488 event
.AssignDragEventData(*aDragEvent
, false);
5489 event
.mFlags
.mIsSynthesizedForTests
=
5490 aDragEvent
->mFlags
.mIsSynthesizedForTests
;
5491 event
.mRelatedTarget
= aRelatedTarget
;
5492 if (aMessage
== eDragExit
&& !StaticPrefs::dom_event_dragexit_enabled()) {
5493 event
.mFlags
.mOnlyChromeDispatch
= true;
5496 mCurrentTargetContent
= aTargetContent
;
5498 if (aTargetContent
!= aRelatedTarget
) {
5499 // XXX This event should still go somewhere!!
5500 if (aTargetContent
) {
5501 EventDispatcher::Dispatch(aTargetContent
, aPresContext
, &event
, nullptr,
5505 // adjust the drag hover if the dragenter event was cancelled or this is a
5507 if (status
== nsEventStatus_eConsumeNoDefault
|| aMessage
== eDragExit
) {
5508 SetContentState((aMessage
== eDragEnter
) ? aTargetContent
: nullptr,
5509 ElementState::DRAGOVER
);
5512 // collect any changes to moz cursor settings stored in the event's
5514 UpdateDragDataTransfer(&event
);
5517 // Finally dispatch the event to the frame
5519 aTargetFrame
->HandleEvent(aPresContext
, &event
, &status
);
5523 void EventStateManager::UpdateDragDataTransfer(WidgetDragEvent
* dragEvent
) {
5524 NS_ASSERTION(dragEvent
, "drag event is null in UpdateDragDataTransfer!");
5525 if (!dragEvent
->mDataTransfer
) {
5529 nsCOMPtr
<nsIDragSession
> dragSession
= nsContentUtils::GetDragSession();
5532 // the initial dataTransfer is the one from the dragstart event that
5533 // was set on the dragSession when the drag began.
5534 RefPtr
<DataTransfer
> initialDataTransfer
= dragSession
->GetDataTransfer();
5535 if (initialDataTransfer
) {
5536 // retrieve the current moz cursor setting and save it.
5537 nsAutoString mozCursor
;
5538 dragEvent
->mDataTransfer
->GetMozCursor(mozCursor
);
5539 initialDataTransfer
->SetMozCursor(mozCursor
);
5544 nsresult
EventStateManager::SetClickCount(WidgetMouseEvent
* aEvent
,
5545 nsEventStatus
* aStatus
,
5546 nsIContent
* aOverrideClickTarget
) {
5547 nsCOMPtr
<nsIContent
> mouseContent
= aOverrideClickTarget
;
5548 if (!mouseContent
&& mCurrentTarget
) {
5549 mCurrentTarget
->GetContentForEvent(aEvent
, getter_AddRefs(mouseContent
));
5551 if (mouseContent
&& mouseContent
->IsText()) {
5552 nsINode
* parent
= mouseContent
->GetFlattenedTreeParentNode();
5553 if (parent
&& parent
->IsContent()) {
5554 mouseContent
= parent
->AsContent();
5558 LastMouseDownInfo
& mouseDownInfo
= GetLastMouseDownInfo(aEvent
->mButton
);
5559 if (aEvent
->mMessage
== eMouseDown
) {
5560 mouseDownInfo
.mLastMouseDownContent
=
5561 !aEvent
->mClickEventPrevented
? mouseContent
: nullptr;
5563 if (mouseDownInfo
.mLastMouseDownContent
) {
5564 if (HTMLInputElement
* input
= HTMLInputElement::FromNodeOrNull(
5565 mouseDownInfo
.mLastMouseDownContent
)) {
5566 mouseDownInfo
.mLastMouseDownInputControlType
=
5567 Some(input
->ControlType());
5568 } else if (mouseDownInfo
.mLastMouseDownContent
5569 ->IsInNativeAnonymousSubtree()) {
5570 if (HTMLInputElement
* input
= HTMLInputElement::FromNodeOrNull(
5571 mouseDownInfo
.mLastMouseDownContent
5572 ->GetFlattenedTreeParent())) {
5573 mouseDownInfo
.mLastMouseDownInputControlType
=
5574 Some(input
->ControlType());
5579 aEvent
->mClickTarget
=
5580 !aEvent
->mClickEventPrevented
5581 ? GetCommonAncestorForMouseUp(
5582 mouseContent
, mouseDownInfo
.mLastMouseDownContent
,
5583 mouseDownInfo
.mLastMouseDownInputControlType
)
5585 if (aEvent
->mClickTarget
) {
5586 aEvent
->mClickCount
= mouseDownInfo
.mClickCount
;
5587 mouseDownInfo
.mClickCount
= 0;
5589 aEvent
->mClickCount
= 0;
5591 mouseDownInfo
.mLastMouseDownContent
= nullptr;
5592 mouseDownInfo
.mLastMouseDownInputControlType
= Nothing();
5599 bool EventStateManager::EventCausesClickEvents(
5600 const WidgetMouseEvent
& aMouseEvent
) {
5601 if (NS_WARN_IF(aMouseEvent
.mMessage
!= eMouseUp
)) {
5604 // If the mouseup event is synthesized event, we don't need to dispatch
5606 if (!aMouseEvent
.IsReal()) {
5609 // If mouse is still over same element, clickcount will be > 1.
5610 // If it has moved it will be zero, so no click.
5611 if (!aMouseEvent
.mClickCount
|| !aMouseEvent
.mClickTarget
) {
5614 // If click event was explicitly prevented, we shouldn't dispatch it.
5615 if (aMouseEvent
.mClickEventPrevented
) {
5618 // Check that the window isn't disabled before firing a click
5619 // (see bug 366544).
5620 return !(aMouseEvent
.mWidget
&& !aMouseEvent
.mWidget
->IsEnabled());
5623 nsresult
EventStateManager::InitAndDispatchClickEvent(
5624 WidgetMouseEvent
* aMouseUpEvent
, nsEventStatus
* aStatus
,
5625 EventMessage aMessage
, PresShell
* aPresShell
, nsIContent
* aMouseUpContent
,
5626 AutoWeakFrame aCurrentTarget
, bool aNoContentDispatch
,
5627 nsIContent
* aOverrideClickTarget
) {
5628 MOZ_ASSERT(aMouseUpEvent
);
5629 MOZ_ASSERT(EventCausesClickEvents(*aMouseUpEvent
));
5630 MOZ_ASSERT(aMouseUpContent
|| aCurrentTarget
|| aOverrideClickTarget
);
5632 WidgetMouseEvent
event(aMouseUpEvent
->IsTrusted(), aMessage
,
5633 aMouseUpEvent
->mWidget
, WidgetMouseEvent::eReal
);
5635 event
.mRefPoint
= aMouseUpEvent
->mRefPoint
;
5636 event
.mClickCount
= aMouseUpEvent
->mClickCount
;
5637 event
.mModifiers
= aMouseUpEvent
->mModifiers
;
5638 event
.mButtons
= aMouseUpEvent
->mButtons
;
5639 event
.mTimeStamp
= aMouseUpEvent
->mTimeStamp
;
5640 event
.mFlags
.mOnlyChromeDispatch
= aNoContentDispatch
;
5641 event
.mFlags
.mNoContentDispatch
= aNoContentDispatch
;
5642 event
.mButton
= aMouseUpEvent
->mButton
;
5643 event
.pointerId
= aMouseUpEvent
->pointerId
;
5644 event
.mInputSource
= aMouseUpEvent
->mInputSource
;
5645 nsIContent
* target
= aMouseUpContent
;
5646 nsIFrame
* targetFrame
= aCurrentTarget
;
5647 if (aOverrideClickTarget
) {
5648 target
= aOverrideClickTarget
;
5649 targetFrame
= aOverrideClickTarget
->GetPrimaryFrame();
5652 if (!target
->IsInComposedDoc()) {
5656 // Use local event status for each click event dispatching since it'll be
5657 // cleared by EventStateManager::PreHandleEvent(). Therefore, dispatching
5658 // an event means that previous event status will be ignored.
5659 nsEventStatus status
= nsEventStatus_eIgnore
;
5660 nsresult rv
= aPresShell
->HandleEventWithTarget(
5661 &event
, targetFrame
, MOZ_KnownLive(target
), &status
);
5663 // Copy mMultipleActionsPrevented flag from a click event to the mouseup
5664 // event only when it's set to true. It may be set to true if an editor has
5665 // already handled it. This is important to avoid two or more default
5666 // actions handled here.
5667 aMouseUpEvent
->mFlags
.mMultipleActionsPrevented
|=
5668 event
.mFlags
.mMultipleActionsPrevented
;
5669 // If current status is nsEventStatus_eConsumeNoDefault, we don't need to
5671 if (*aStatus
== nsEventStatus_eConsumeNoDefault
) {
5674 // If new status is nsEventStatus_eConsumeNoDefault or
5675 // nsEventStatus_eConsumeDoDefault, use it.
5676 if (status
== nsEventStatus_eConsumeNoDefault
||
5677 status
== nsEventStatus_eConsumeDoDefault
) {
5681 // Otherwise, keep the original status.
5685 nsresult
EventStateManager::PostHandleMouseUp(
5686 WidgetMouseEvent
* aMouseUpEvent
, nsEventStatus
* aStatus
,
5687 nsIContent
* aOverrideClickTarget
) {
5688 MOZ_ASSERT(aMouseUpEvent
);
5689 MOZ_ASSERT(EventCausesClickEvents(*aMouseUpEvent
));
5690 MOZ_ASSERT(aStatus
);
5692 RefPtr
<PresShell
> presShell
= mPresContext
->GetPresShell();
5697 nsCOMPtr
<nsIContent
> clickTarget
=
5698 nsIContent::FromEventTargetOrNull(aMouseUpEvent
->mClickTarget
);
5699 NS_ENSURE_STATE(clickTarget
);
5701 // Fire click events if the event target is still available.
5702 // Note that do not include the eMouseUp event's status since we ignore it
5703 // for compatibility with the other browsers.
5704 nsEventStatus status
= nsEventStatus_eIgnore
;
5705 nsresult rv
= DispatchClickEvents(presShell
, aMouseUpEvent
, &status
,
5706 clickTarget
, aOverrideClickTarget
);
5707 if (NS_WARN_IF(NS_FAILED(rv
))) {
5711 // Do not do anything if preceding click events are consumed.
5712 // Note that Chromium dispatches "paste" event and actually pates clipboard
5713 // text into focused editor even if the preceding click events are consumed.
5714 // However, this is different from our traditional behavior and does not
5715 // conform to DOM events. If we need to keep compatibility with Chromium,
5716 // we should change it later.
5717 if (status
== nsEventStatus_eConsumeNoDefault
) {
5718 *aStatus
= nsEventStatus_eConsumeNoDefault
;
5722 // Handle middle click paste if it's enabled and the mouse button is middle.
5723 if (aMouseUpEvent
->mButton
!= MouseButton::eMiddle
||
5724 !WidgetMouseEvent::IsMiddleClickPasteEnabled()) {
5727 DebugOnly
<nsresult
> rvIgnored
=
5728 HandleMiddleClickPaste(presShell
, aMouseUpEvent
, &status
, nullptr);
5729 NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored
),
5730 "Failed to paste for a middle click");
5732 // If new status is nsEventStatus_eConsumeNoDefault or
5733 // nsEventStatus_eConsumeDoDefault, use it.
5734 if (*aStatus
!= nsEventStatus_eConsumeNoDefault
&&
5735 (status
== nsEventStatus_eConsumeNoDefault
||
5736 status
== nsEventStatus_eConsumeDoDefault
)) {
5740 // Don't return error even if middle mouse paste fails since we haven't
5745 nsresult
EventStateManager::DispatchClickEvents(
5746 PresShell
* aPresShell
, WidgetMouseEvent
* aMouseUpEvent
,
5747 nsEventStatus
* aStatus
, nsIContent
* aClickTarget
,
5748 nsIContent
* aOverrideClickTarget
) {
5749 MOZ_ASSERT(aPresShell
);
5750 MOZ_ASSERT(aMouseUpEvent
);
5751 MOZ_ASSERT(EventCausesClickEvents(*aMouseUpEvent
));
5752 MOZ_ASSERT(aStatus
);
5753 MOZ_ASSERT(aClickTarget
|| aOverrideClickTarget
);
5755 bool notDispatchToContents
=
5756 (aMouseUpEvent
->mButton
== MouseButton::eMiddle
||
5757 aMouseUpEvent
->mButton
== MouseButton::eSecondary
);
5759 bool fireAuxClick
= notDispatchToContents
;
5761 AutoWeakFrame currentTarget
= aClickTarget
->GetPrimaryFrame();
5762 nsresult rv
= InitAndDispatchClickEvent(
5763 aMouseUpEvent
, aStatus
, eMouseClick
, aPresShell
, aClickTarget
,
5764 currentTarget
, notDispatchToContents
, aOverrideClickTarget
);
5765 if (NS_WARN_IF(NS_FAILED(rv
))) {
5769 // Fire auxclick event if necessary.
5770 if (fireAuxClick
&& *aStatus
!= nsEventStatus_eConsumeNoDefault
&&
5771 aClickTarget
&& aClickTarget
->IsInComposedDoc()) {
5772 rv
= InitAndDispatchClickEvent(aMouseUpEvent
, aStatus
, eMouseAuxClick
,
5773 aPresShell
, aClickTarget
, currentTarget
,
5774 false, aOverrideClickTarget
);
5775 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
), "Failed to dispatch eMouseAuxClick");
5778 // Fire double click event if click count is 2.
5779 if (aMouseUpEvent
->mClickCount
== 2 && !fireAuxClick
&& aClickTarget
&&
5780 aClickTarget
->IsInComposedDoc()) {
5781 rv
= InitAndDispatchClickEvent(aMouseUpEvent
, aStatus
, eMouseDoubleClick
,
5782 aPresShell
, aClickTarget
, currentTarget
,
5783 notDispatchToContents
, aOverrideClickTarget
);
5784 if (NS_WARN_IF(NS_FAILED(rv
))) {
5792 nsresult
EventStateManager::HandleMiddleClickPaste(
5793 PresShell
* aPresShell
, WidgetMouseEvent
* aMouseEvent
,
5794 nsEventStatus
* aStatus
, EditorBase
* aEditorBase
) {
5795 MOZ_ASSERT(aPresShell
);
5796 MOZ_ASSERT(aMouseEvent
);
5797 MOZ_ASSERT((aMouseEvent
->mMessage
== eMouseAuxClick
&&
5798 aMouseEvent
->mButton
== MouseButton::eMiddle
) ||
5799 EventCausesClickEvents(*aMouseEvent
));
5800 MOZ_ASSERT(aStatus
);
5801 MOZ_ASSERT(*aStatus
!= nsEventStatus_eConsumeNoDefault
);
5803 // Even if we're called twice or more for a mouse operation, we should
5804 // handle only once. Although mMultipleActionsPrevented may be set to
5805 // true by different event handler in the future, we can use it for now.
5806 if (aMouseEvent
->mFlags
.mMultipleActionsPrevented
) {
5809 aMouseEvent
->mFlags
.mMultipleActionsPrevented
= true;
5811 RefPtr
<Selection
> selection
;
5813 selection
= aEditorBase
->GetSelection();
5814 if (NS_WARN_IF(!selection
)) {
5815 return NS_ERROR_FAILURE
;
5818 Document
* document
= aPresShell
->GetDocument();
5819 if (NS_WARN_IF(!document
)) {
5820 return NS_ERROR_FAILURE
;
5822 selection
= nsCopySupport::GetSelectionForCopy(document
);
5823 if (NS_WARN_IF(!selection
)) {
5824 return NS_ERROR_FAILURE
;
5827 const nsRange
* range
= selection
->GetRangeAt(0);
5829 nsINode
* target
= range
->GetStartContainer();
5830 if (target
&& target
->OwnerDoc()->IsInChromeDocShell()) {
5831 // In Chrome document, limit middle-click pasting to only the editor
5832 // because it looks odd if pasting works in the focused editor when you
5833 // middle-click toolbar or something which are far from the editor.
5834 // However, as DevTools especially Web Console module assumes that paste
5835 // event will be fired when middle-click even on not editor, don't limit
5842 // Don't modify selection here because we've already set caret to the point
5843 // at "mousedown" event.
5845 int32_t clipboardType
= nsIClipboard::kGlobalClipboard
;
5846 nsCOMPtr
<nsIClipboard
> clipboardService
=
5847 do_GetService("@mozilla.org/widget/clipboard;1");
5848 if (clipboardService
&& clipboardService
->IsClipboardTypeSupported(
5849 nsIClipboard::kSelectionClipboard
)) {
5850 clipboardType
= nsIClipboard::kSelectionClipboard
;
5853 // Fire ePaste event by ourselves since we need to dispatch "paste" event
5854 // even if the middle click event was consumed for compatibility with
5856 if (!nsCopySupport::FireClipboardEvent(ePaste
, clipboardType
, aPresShell
,
5858 *aStatus
= nsEventStatus_eConsumeNoDefault
;
5862 // Although we've fired "paste" event, there is no editor to accept the
5863 // clipboard content.
5868 // Check if the editor is still the good target to paste.
5869 if (aEditorBase
->Destroyed() || aEditorBase
->IsReadonly()) {
5870 // XXX Should we consume the event when the editor is readonly and/or
5875 // The selection may have been modified during reflow. Therefore, we
5876 // should adjust event target to pass IsAcceptableInputEvent().
5877 const nsRange
* range
= selection
->GetRangeAt(0);
5881 WidgetMouseEvent
mouseEvent(*aMouseEvent
);
5882 mouseEvent
.mOriginalTarget
= range
->GetStartContainer();
5883 if (NS_WARN_IF(!mouseEvent
.mOriginalTarget
) ||
5884 !aEditorBase
->IsAcceptableInputEvent(&mouseEvent
)) {
5888 // If Control key is pressed, we should paste clipboard content as
5889 // quotation. Otherwise, paste it as is.
5890 if (aMouseEvent
->IsControl()) {
5891 DebugOnly
<nsresult
> rv
= aEditorBase
->PasteAsQuotationAsAction(
5892 clipboardType
, EditorBase::DispatchPasteEvent::No
);
5893 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
), "Failed to paste as quotation");
5895 DebugOnly
<nsresult
> rv
= aEditorBase
->PasteAsAction(
5896 clipboardType
, EditorBase::DispatchPasteEvent::No
);
5897 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
), "Failed to paste");
5899 *aStatus
= nsEventStatus_eConsumeNoDefault
;
5904 void EventStateManager::ConsumeInteractionData(
5905 Record
<nsString
, dom::InteractionData
>& aInteractions
) {
5906 OnTypingInteractionEnded();
5908 aInteractions
.Entries().Clear();
5909 auto newEntry
= aInteractions
.Entries().AppendElement();
5910 newEntry
->mKey
= u
"Typing"_ns
;
5911 newEntry
->mValue
= gTypingInteraction
;
5912 gTypingInteraction
= {};
5915 nsIFrame
* EventStateManager::GetEventTarget() {
5916 PresShell
* presShell
;
5917 if (mCurrentTarget
|| !mPresContext
||
5918 !(presShell
= mPresContext
->GetPresShell())) {
5919 return mCurrentTarget
;
5922 if (mCurrentTargetContent
) {
5923 mCurrentTarget
= mPresContext
->GetPrimaryFrameFor(mCurrentTargetContent
);
5924 if (mCurrentTarget
) {
5925 return mCurrentTarget
;
5929 nsIFrame
* frame
= presShell
->GetCurrentEventFrame();
5930 return (mCurrentTarget
= frame
);
5933 already_AddRefed
<nsIContent
> EventStateManager::GetEventTargetContent(
5934 WidgetEvent
* aEvent
) {
5935 if (aEvent
&& (aEvent
->mMessage
== eFocus
|| aEvent
->mMessage
== eBlur
)) {
5936 nsCOMPtr
<nsIContent
> content
= GetFocusedElement();
5937 return content
.forget();
5940 if (mCurrentTargetContent
) {
5941 nsCOMPtr
<nsIContent
> content
= mCurrentTargetContent
;
5942 return content
.forget();
5945 nsCOMPtr
<nsIContent
> content
;
5946 if (PresShell
* presShell
= mPresContext
->GetPresShell()) {
5947 content
= presShell
->GetEventTargetContent(aEvent
);
5950 // Some events here may set mCurrentTarget but not set the corresponding
5951 // event target in the PresShell.
5952 if (!content
&& mCurrentTarget
) {
5953 mCurrentTarget
->GetContentForEvent(aEvent
, getter_AddRefs(content
));
5956 return content
.forget();
5959 static Element
* GetLabelTarget(nsIContent
* aPossibleLabel
) {
5960 mozilla::dom::HTMLLabelElement
* label
=
5961 mozilla::dom::HTMLLabelElement::FromNode(aPossibleLabel
);
5962 if (!label
) return nullptr;
5964 return label
->GetLabeledElement();
5968 inline void EventStateManager::DoStateChange(Element
* aElement
,
5969 ElementState aState
,
5972 aElement
->AddStates(aState
);
5974 aElement
->RemoveStates(aState
);
5979 inline void EventStateManager::DoStateChange(nsIContent
* aContent
,
5980 ElementState aState
,
5982 if (aContent
->IsElement()) {
5983 DoStateChange(aContent
->AsElement(), aState
, aStateAdded
);
5988 void EventStateManager::UpdateAncestorState(nsIContent
* aStartNode
,
5989 nsIContent
* aStopBefore
,
5990 ElementState aState
,
5992 for (; aStartNode
&& aStartNode
!= aStopBefore
;
5993 aStartNode
= aStartNode
->GetFlattenedTreeParent()) {
5994 // We might be starting with a non-element (e.g. a text node) and
5995 // if someone is doing something weird might be ending with a
5996 // non-element too (e.g. a document fragment)
5997 if (!aStartNode
->IsElement()) {
6000 Element
* element
= aStartNode
->AsElement();
6001 DoStateChange(element
, aState
, aAddState
);
6002 Element
* labelTarget
= GetLabelTarget(element
);
6004 DoStateChange(labelTarget
, aState
, aAddState
);
6009 // We might be in a situation where a node was in hover both
6010 // because it was hovered and because the label for it was
6011 // hovered, and while we stopped hovering the node the label is
6012 // still hovered. Or we might have had two nested labels for the
6013 // same node, and while one is no longer hovered the other still
6014 // is. In that situation, the label that's still hovered will be
6015 // aStopBefore or some ancestor of it, and the call we just made
6016 // to UpdateAncestorState with aAddState = false would have
6017 // removed the hover state from the node. But the node should
6018 // still be in hover state. To handle this situation we need to
6019 // keep walking up the tree and any time we find a label mark its
6020 // corresponding node as still in our state.
6021 for (; aStartNode
; aStartNode
= aStartNode
->GetFlattenedTreeParent()) {
6022 if (!aStartNode
->IsElement()) {
6026 Element
* labelTarget
= GetLabelTarget(aStartNode
->AsElement());
6027 if (labelTarget
&& !labelTarget
->State().HasState(aState
)) {
6028 DoStateChange(labelTarget
, aState
, true);
6035 bool CanContentHaveActiveState(nsIContent
& aContent
) {
6036 // Editable content can never become active since their default actions
6037 // are disabled. Watch out for editable content in native anonymous
6038 // subtrees though, as they belong to text controls.
6039 return !aContent
.IsEditable() || aContent
.IsInNativeAnonymousSubtree();
6042 bool EventStateManager::SetContentState(nsIContent
* aContent
,
6043 ElementState aState
) {
6044 MOZ_ASSERT(ManagesState(aState
), "Unexpected state");
6046 nsCOMPtr
<nsIContent
> notifyContent1
;
6047 nsCOMPtr
<nsIContent
> notifyContent2
;
6048 bool updateAncestors
;
6050 if (aState
== ElementState::HOVER
|| aState
== ElementState::ACTIVE
) {
6051 // Hover and active are hierarchical
6052 updateAncestors
= true;
6054 // check to see that this state is allowed by style. Check dragover too?
6055 // XXX Is this even what we want?
6056 if (mCurrentTarget
&&
6057 mCurrentTarget
->StyleUI()->UserInput() == StyleUserInput::None
) {
6061 if (aState
== ElementState::ACTIVE
) {
6062 if (aContent
&& !CanContentHaveActiveState(*aContent
)) {
6065 if (aContent
!= mActiveContent
) {
6066 notifyContent1
= aContent
;
6067 notifyContent2
= mActiveContent
;
6068 mActiveContent
= aContent
;
6071 NS_ASSERTION(aState
== ElementState::HOVER
, "How did that happen?");
6072 nsIContent
* newHover
;
6074 if (mPresContext
->IsDynamic()) {
6075 newHover
= aContent
;
6077 NS_ASSERTION(!aContent
|| aContent
->GetComposedDoc() ==
6078 mPresContext
->PresShell()->GetDocument(),
6079 "Unexpected document");
6080 nsIFrame
* frame
= aContent
? aContent
->GetPrimaryFrame() : nullptr;
6081 if (frame
&& nsLayoutUtils::IsViewportScrollbarFrame(frame
)) {
6082 // The scrollbars of viewport should not ignore the hover state.
6083 // Because they are *not* the content of the web page.
6084 newHover
= aContent
;
6086 // All contents of the web page should ignore the hover state.
6091 if (newHover
!= mHoverContent
) {
6092 notifyContent1
= newHover
;
6093 notifyContent2
= mHoverContent
;
6094 mHoverContent
= newHover
;
6098 updateAncestors
= false;
6099 if (aState
== ElementState::DRAGOVER
) {
6100 if (aContent
!= sDragOverContent
) {
6101 notifyContent1
= aContent
;
6102 notifyContent2
= sDragOverContent
;
6103 sDragOverContent
= aContent
;
6105 } else if (aState
== ElementState::URLTARGET
) {
6106 if (aContent
!= mURLTargetContent
) {
6107 notifyContent1
= aContent
;
6108 notifyContent2
= mURLTargetContent
;
6109 mURLTargetContent
= aContent
;
6114 // We need to keep track of which of notifyContent1 and notifyContent2 is
6115 // getting the state set and which is getting it unset. If both are
6116 // non-null, then notifyContent1 is having the state set and notifyContent2
6117 // is having it unset. But if one of them is null, we need to keep track of
6118 // the right thing for notifyContent1 explicitly.
6119 bool content1StateSet
= true;
6120 if (!notifyContent1
) {
6121 // This is ok because FindCommonAncestor wouldn't find anything
6122 // anyway if notifyContent1 is null.
6123 notifyContent1
= notifyContent2
;
6124 notifyContent2
= nullptr;
6125 content1StateSet
= false;
6128 if (notifyContent1
&& mPresContext
) {
6129 EnsureDocument(mPresContext
);
6131 nsAutoScriptBlocker scriptBlocker
;
6133 if (updateAncestors
) {
6134 nsCOMPtr
<nsIContent
> commonAncestor
=
6135 FindCommonAncestor(notifyContent1
, notifyContent2
);
6136 if (notifyContent2
) {
6137 // It's very important to first notify the state removal and
6138 // then the state addition, because due to labels it's
6139 // possible that we're removing state from some element but
6140 // then adding it again (say because mHoverContent changed
6141 // from a control to its label).
6142 UpdateAncestorState(notifyContent2
, commonAncestor
, aState
, false);
6144 UpdateAncestorState(notifyContent1
, commonAncestor
, aState
,
6147 if (notifyContent2
) {
6148 DoStateChange(notifyContent2
, aState
, false);
6150 DoStateChange(notifyContent1
, aState
, content1StateSet
);
6158 void EventStateManager::RemoveNodeFromChainIfNeeded(ElementState aState
,
6159 nsIContent
* aContentRemoved
,
6161 MOZ_ASSERT(aState
== ElementState::HOVER
|| aState
== ElementState::ACTIVE
);
6162 if (!aContentRemoved
->IsElement() ||
6163 !aContentRemoved
->AsElement()->State().HasState(aState
)) {
6167 nsCOMPtr
<nsIContent
>& leaf
=
6168 aState
== ElementState::HOVER
? mHoverContent
: mActiveContent
;
6171 // These two NS_ASSERTIONS below can fail for Shadow DOM sometimes, and it's
6172 // not clear how to best handle it, see
6173 // https://github.com/whatwg/html/issues/4795 and bug 1551621.
6175 nsContentUtils::ContentIsFlattenedTreeDescendantOf(leaf
, aContentRemoved
),
6176 "Flat tree and active / hover chain got out of sync");
6178 nsIContent
* newLeaf
= aContentRemoved
->GetFlattenedTreeParent();
6179 MOZ_ASSERT(!newLeaf
|| newLeaf
->IsElement());
6180 NS_ASSERTION(!newLeaf
|| newLeaf
->AsElement()->State().HasState(aState
),
6181 "State got out of sync because of shadow DOM");
6183 SetContentState(newLeaf
, aState
);
6185 // We don't update the removed content's state here, since removing NAC
6186 // happens from layout and we don't really want to notify at that point or
6189 // Also, NAC is not observable and NAC being removed will go away soon.
6192 MOZ_ASSERT(leaf
== newLeaf
|| (aState
== ElementState::ACTIVE
&& !leaf
&&
6193 !CanContentHaveActiveState(*newLeaf
)));
6196 void EventStateManager::NativeAnonymousContentRemoved(nsIContent
* aContent
) {
6197 MOZ_ASSERT(aContent
->IsRootOfNativeAnonymousSubtree());
6198 RemoveNodeFromChainIfNeeded(ElementState::HOVER
, aContent
, false);
6199 RemoveNodeFromChainIfNeeded(ElementState::ACTIVE
, aContent
, false);
6201 nsCOMPtr
<nsIContent
>& lastLeftMouseDownContent
=
6202 mLastLeftMouseDownInfo
.mLastMouseDownContent
;
6203 if (lastLeftMouseDownContent
&&
6204 nsContentUtils::ContentIsFlattenedTreeDescendantOf(
6205 lastLeftMouseDownContent
, aContent
)) {
6206 lastLeftMouseDownContent
= aContent
->GetFlattenedTreeParent();
6209 nsCOMPtr
<nsIContent
>& lastMiddleMouseDownContent
=
6210 mLastMiddleMouseDownInfo
.mLastMouseDownContent
;
6211 if (lastMiddleMouseDownContent
&&
6212 nsContentUtils::ContentIsFlattenedTreeDescendantOf(
6213 lastMiddleMouseDownContent
, aContent
)) {
6214 lastMiddleMouseDownContent
= aContent
->GetFlattenedTreeParent();
6217 nsCOMPtr
<nsIContent
>& lastRightMouseDownContent
=
6218 mLastRightMouseDownInfo
.mLastMouseDownContent
;
6219 if (lastRightMouseDownContent
&&
6220 nsContentUtils::ContentIsFlattenedTreeDescendantOf(
6221 lastRightMouseDownContent
, aContent
)) {
6222 lastRightMouseDownContent
= aContent
->GetFlattenedTreeParent();
6226 void EventStateManager::ContentRemoved(Document
* aDocument
,
6227 nsIContent
* aContent
) {
6229 * Anchor and area elements when focused or hovered might make the UI to show
6230 * the current link. We want to make sure that the UI gets informed when they
6231 * are actually removed from the DOM.
6233 if (aContent
->IsAnyOfHTMLElements(nsGkAtoms::a
, nsGkAtoms::area
) &&
6234 (aContent
->AsElement()->State().HasAtLeastOneOfStates(
6235 ElementState::FOCUS
| ElementState::HOVER
))) {
6236 Element
* element
= aContent
->AsElement();
6237 element
->LeaveLink(element
->GetPresContext(Element::eForComposedDoc
));
6240 if (aContent
->IsElement()) {
6241 if (RefPtr
<nsPresContext
> presContext
= mPresContext
) {
6242 IMEStateManager::OnRemoveContent(*presContext
,
6243 MOZ_KnownLive(*aContent
->AsElement()));
6245 WheelTransaction::OnRemoveElement(aContent
);
6248 // inform the focus manager that the content is being removed. If this
6249 // content is focused, the focus will be removed without firing events.
6250 if (RefPtr
<nsFocusManager
> fm
= nsFocusManager::GetFocusManager()) {
6251 fm
->ContentRemoved(aDocument
, aContent
);
6254 RemoveNodeFromChainIfNeeded(ElementState::HOVER
, aContent
, true);
6255 RemoveNodeFromChainIfNeeded(ElementState::ACTIVE
, aContent
, true);
6257 if (sDragOverContent
&&
6258 sDragOverContent
->OwnerDoc() == aContent
->OwnerDoc() &&
6259 nsContentUtils::ContentIsFlattenedTreeDescendantOf(sDragOverContent
,
6261 sDragOverContent
= nullptr;
6264 PointerEventHandler::ReleaseIfCaptureByDescendant(aContent
);
6266 if (mMouseEnterLeaveHelper
) {
6267 const bool hadMouseOutTarget
=
6268 mMouseEnterLeaveHelper
->GetOutEventTarget() != nullptr;
6269 mMouseEnterLeaveHelper
->ContentRemoved(*aContent
);
6270 // If we lose the mouseout target, we need to dispatch mouseover on an
6271 // ancestor. For ensuring the chance to do it before next user input, we
6272 // need a synthetic mouse move.
6273 if (hadMouseOutTarget
&& !mMouseEnterLeaveHelper
->GetOutEventTarget()) {
6274 if (PresShell
* presShell
=
6275 mPresContext
? mPresContext
->GetPresShell() : nullptr) {
6276 presShell
->SynthesizeMouseMove(false);
6280 for (const auto& entry
: mPointersEnterLeaveHelper
) {
6281 if (entry
.GetData()) {
6282 entry
.GetData()->ContentRemoved(*aContent
);
6287 void EventStateManager::TextControlRootWillBeRemoved(
6288 TextControlElement
& aTextControlElement
) {
6289 if (!mGestureDownInTextControl
|| !mGestureDownFrameOwner
||
6290 !mGestureDownFrameOwner
->IsInNativeAnonymousSubtree()) {
6293 // If we track gesture to start drag in aTextControlElement, we should keep
6294 // tracking it with aTextContrlElement itself for now because this may be
6295 // caused by reframing aTextControlElement which may not be intended by the
6297 if (&aTextControlElement
==
6298 mGestureDownFrameOwner
6299 ->GetClosestNativeAnonymousSubtreeRootParentOrHost()) {
6300 mGestureDownFrameOwner
= &aTextControlElement
;
6304 void EventStateManager::TextControlRootAdded(
6305 Element
& aAnonymousDivElement
, TextControlElement
& aTextControlElement
) {
6306 if (!mGestureDownInTextControl
||
6307 mGestureDownFrameOwner
!= &aTextControlElement
) {
6310 // If we track gesture to start drag in aTextControlElement, but the frame
6311 // owner is the text control element itself, the anonymous nodes in it are
6312 // recreated by a reframe. If so, we should keep tracking it with the
6313 // recreated native anonymous node.
6314 mGestureDownFrameOwner
=
6315 aAnonymousDivElement
.GetFirstChild()
6316 ? aAnonymousDivElement
.GetFirstChild()
6317 : static_cast<nsIContent
*>(&aAnonymousDivElement
);
6320 bool EventStateManager::EventStatusOK(WidgetGUIEvent
* aEvent
) {
6321 return !(aEvent
->mMessage
== eMouseDown
&&
6322 aEvent
->AsMouseEvent()->mButton
== MouseButton::ePrimary
&&
6323 !sNormalLMouseEventInProcess
);
6326 //-------------------------------------------
6327 // Access Key Registration
6328 //-------------------------------------------
6329 void EventStateManager::RegisterAccessKey(Element
* aElement
, uint32_t aKey
) {
6330 if (aElement
&& !mAccessKeys
.Contains(aElement
)) {
6331 mAccessKeys
.AppendObject(aElement
);
6335 void EventStateManager::UnregisterAccessKey(Element
* aElement
, uint32_t aKey
) {
6337 mAccessKeys
.RemoveObject(aElement
);
6341 uint32_t EventStateManager::GetRegisteredAccessKey(Element
* aElement
) {
6342 MOZ_ASSERT(aElement
);
6344 if (!mAccessKeys
.Contains(aElement
)) {
6348 nsAutoString accessKey
;
6349 aElement
->GetAttr(nsGkAtoms::accesskey
, accessKey
);
6350 return accessKey
.First();
6353 void EventStateManager::EnsureDocument(nsPresContext
* aPresContext
) {
6354 if (!mDocument
) mDocument
= aPresContext
->Document();
6357 void EventStateManager::FlushLayout(nsPresContext
* aPresContext
) {
6358 MOZ_ASSERT(aPresContext
, "nullptr ptr");
6359 if (RefPtr
<PresShell
> presShell
= aPresContext
->GetPresShell()) {
6360 presShell
->FlushPendingNotifications(FlushType::InterruptibleLayout
);
6364 Element
* EventStateManager::GetFocusedElement() {
6365 nsFocusManager
* fm
= nsFocusManager::GetFocusManager();
6366 EnsureDocument(mPresContext
);
6367 if (!fm
|| !mDocument
) {
6371 nsCOMPtr
<nsPIDOMWindowOuter
> focusedWindow
;
6372 return nsFocusManager::GetFocusedDescendant(
6373 mDocument
->GetWindow(), nsFocusManager::eOnlyCurrentWindow
,
6374 getter_AddRefs(focusedWindow
));
6377 //-------------------------------------------------------
6378 // Return true if the docshell is visible
6380 bool EventStateManager::IsShellVisible(nsIDocShell
* aShell
) {
6381 NS_ASSERTION(aShell
, "docshell is null");
6383 nsCOMPtr
<nsIBaseWindow
> basewin
= do_QueryInterface(aShell
);
6384 if (!basewin
) return true;
6386 bool isVisible
= true;
6387 basewin
->GetVisibility(&isVisible
);
6389 // We should be doing some additional checks here so that
6390 // we don't tab into hidden tabs of tabbrowser. -bryner
6395 nsresult
EventStateManager::DoContentCommandEvent(
6396 WidgetContentCommandEvent
* aEvent
) {
6397 EnsureDocument(mPresContext
);
6398 NS_ENSURE_TRUE(mDocument
, NS_ERROR_FAILURE
);
6399 nsCOMPtr
<nsPIDOMWindowOuter
> window(mDocument
->GetWindow());
6400 NS_ENSURE_TRUE(window
, NS_ERROR_FAILURE
);
6402 nsCOMPtr
<nsPIWindowRoot
> root
= window
->GetTopWindowRoot();
6403 NS_ENSURE_TRUE(root
, NS_ERROR_FAILURE
);
6405 switch (aEvent
->mMessage
) {
6406 case eContentCommandCut
:
6409 case eContentCommandCopy
:
6412 case eContentCommandPaste
:
6415 case eContentCommandDelete
:
6418 case eContentCommandUndo
:
6421 case eContentCommandRedo
:
6424 case eContentCommandPasteTransferable
:
6425 cmd
= "cmd_pasteTransferable";
6427 case eContentCommandLookUpDictionary
:
6428 cmd
= "cmd_lookUpDictionary";
6431 return NS_ERROR_NOT_IMPLEMENTED
;
6433 // If user tries to do something, user must try to do it in visible window.
6434 // So, let's retrieve controller of visible window.
6435 nsCOMPtr
<nsIController
> controller
;
6437 root
->GetControllerForCommand(cmd
, true, getter_AddRefs(controller
));
6438 NS_ENSURE_SUCCESS(rv
, rv
);
6440 // When GetControllerForCommand succeeded but there is no controller, the
6441 // command isn't supported.
6442 aEvent
->mIsEnabled
= false;
6445 rv
= controller
->IsCommandEnabled(cmd
, &canDoIt
);
6446 NS_ENSURE_SUCCESS(rv
, rv
);
6447 aEvent
->mIsEnabled
= canDoIt
;
6448 if (canDoIt
&& !aEvent
->mOnlyEnabledCheck
) {
6449 switch (aEvent
->mMessage
) {
6450 case eContentCommandPasteTransferable
: {
6451 BrowserParent
* remote
= BrowserParent::GetFocused();
6453 IPCTransferable ipcTransferable
;
6454 nsContentUtils::TransferableToIPCTransferable(
6455 aEvent
->mTransferable
, &ipcTransferable
, false,
6457 remote
->SendPasteTransferable(std::move(ipcTransferable
));
6460 nsCOMPtr
<nsICommandController
> commandController
=
6461 do_QueryInterface(controller
);
6462 NS_ENSURE_STATE(commandController
);
6464 RefPtr
<nsCommandParams
> params
= new nsCommandParams();
6465 rv
= params
->SetISupports("transferable", aEvent
->mTransferable
);
6466 if (NS_WARN_IF(NS_FAILED(rv
))) {
6469 rv
= commandController
->DoCommandWithParams(cmd
, params
);
6474 case eContentCommandLookUpDictionary
: {
6475 nsCOMPtr
<nsICommandController
> commandController
=
6476 do_QueryInterface(controller
);
6477 if (NS_WARN_IF(!commandController
)) {
6478 return NS_ERROR_FAILURE
;
6481 RefPtr
<nsCommandParams
> params
= new nsCommandParams();
6482 rv
= params
->SetInt("x", aEvent
->mRefPoint
.x
);
6483 if (NS_WARN_IF(NS_FAILED(rv
))) {
6487 rv
= params
->SetInt("y", aEvent
->mRefPoint
.y
);
6488 if (NS_WARN_IF(NS_FAILED(rv
))) {
6492 rv
= commandController
->DoCommandWithParams(cmd
, params
);
6497 rv
= controller
->DoCommand(cmd
);
6500 NS_ENSURE_SUCCESS(rv
, rv
);
6503 aEvent
->mSucceeded
= true;
6507 nsresult
EventStateManager::DoContentCommandInsertTextEvent(
6508 WidgetContentCommandEvent
* aEvent
) {
6510 MOZ_ASSERT(aEvent
->mMessage
== eContentCommandInsertText
);
6511 MOZ_DIAGNOSTIC_ASSERT(aEvent
->mString
.isSome());
6512 MOZ_DIAGNOSTIC_ASSERT(!aEvent
->mString
.ref().IsEmpty());
6514 aEvent
->mIsEnabled
= false;
6515 aEvent
->mSucceeded
= false;
6517 NS_ENSURE_TRUE(mPresContext
, NS_ERROR_NOT_AVAILABLE
);
6519 if (XRE_IsParentProcess()) {
6520 // Handle it in focused content process if there is.
6521 if (BrowserParent
* remote
= BrowserParent::GetFocused()) {
6522 remote
->SendInsertText(aEvent
->mString
.ref());
6523 aEvent
->mIsEnabled
= true; // XXX it can be a lie...
6524 aEvent
->mSucceeded
= true;
6529 // If there is no active editor in this process, we should treat the command
6531 RefPtr
<EditorBase
> activeEditor
=
6532 nsContentUtils::GetActiveEditor(mPresContext
);
6533 if (!activeEditor
) {
6534 aEvent
->mSucceeded
= true;
6538 nsresult rv
= activeEditor
->InsertTextAsAction(aEvent
->mString
.ref());
6539 aEvent
->mIsEnabled
= rv
!= NS_SUCCESS_DOM_NO_OPERATION
;
6540 aEvent
->mSucceeded
= NS_SUCCEEDED(rv
);
6544 nsresult
EventStateManager::DoContentCommandScrollEvent(
6545 WidgetContentCommandEvent
* aEvent
) {
6546 NS_ENSURE_TRUE(mPresContext
, NS_ERROR_NOT_AVAILABLE
);
6547 PresShell
* presShell
= mPresContext
->GetPresShell();
6548 NS_ENSURE_TRUE(presShell
, NS_ERROR_NOT_AVAILABLE
);
6549 NS_ENSURE_TRUE(aEvent
->mScroll
.mAmount
!= 0, NS_ERROR_INVALID_ARG
);
6551 ScrollUnit scrollUnit
;
6552 switch (aEvent
->mScroll
.mUnit
) {
6553 case WidgetContentCommandEvent::eCmdScrollUnit_Line
:
6554 scrollUnit
= ScrollUnit::LINES
;
6556 case WidgetContentCommandEvent::eCmdScrollUnit_Page
:
6557 scrollUnit
= ScrollUnit::PAGES
;
6559 case WidgetContentCommandEvent::eCmdScrollUnit_Whole
:
6560 scrollUnit
= ScrollUnit::WHOLE
;
6563 return NS_ERROR_INVALID_ARG
;
6566 aEvent
->mSucceeded
= true;
6568 nsIScrollableFrame
* sf
=
6569 presShell
->GetScrollableFrameToScroll(layers::EitherScrollDirection
);
6570 aEvent
->mIsEnabled
=
6571 sf
? (aEvent
->mScroll
.mIsHorizontal
? WheelHandlingUtils::CanScrollOn(
6572 sf
, aEvent
->mScroll
.mAmount
, 0)
6573 : WheelHandlingUtils::CanScrollOn(
6574 sf
, 0, aEvent
->mScroll
.mAmount
))
6577 if (!aEvent
->mIsEnabled
|| aEvent
->mOnlyEnabledCheck
) {
6581 nsIntPoint
pt(0, 0);
6582 if (aEvent
->mScroll
.mIsHorizontal
) {
6583 pt
.x
= aEvent
->mScroll
.mAmount
;
6585 pt
.y
= aEvent
->mScroll
.mAmount
;
6588 // The caller may want synchronous scrolling.
6589 sf
->ScrollBy(pt
, scrollUnit
, ScrollMode::Instant
);
6593 void EventStateManager::SetActiveManager(EventStateManager
* aNewESM
,
6594 nsIContent
* aContent
) {
6595 if (sActiveESM
&& aNewESM
!= sActiveESM
) {
6596 sActiveESM
->SetContentState(nullptr, ElementState::ACTIVE
);
6598 sActiveESM
= aNewESM
;
6599 if (sActiveESM
&& aContent
) {
6600 sActiveESM
->SetContentState(aContent
, ElementState::ACTIVE
);
6604 void EventStateManager::ClearGlobalActiveContent(EventStateManager
* aClearer
) {
6606 aClearer
->SetContentState(nullptr, ElementState::ACTIVE
);
6607 if (sDragOverContent
) {
6608 aClearer
->SetContentState(nullptr, ElementState::DRAGOVER
);
6611 if (sActiveESM
&& aClearer
!= sActiveESM
) {
6612 sActiveESM
->SetContentState(nullptr, ElementState::ACTIVE
);
6614 sActiveESM
= nullptr;
6617 /******************************************************************/
6618 /* mozilla::EventStateManager::DeltaAccumulator */
6619 /******************************************************************/
6621 void EventStateManager::DeltaAccumulator::InitLineOrPageDelta(
6622 nsIFrame
* aTargetFrame
, EventStateManager
* aESM
, WidgetWheelEvent
* aEvent
) {
6626 // Reset if the previous wheel event is too old.
6627 if (!mLastTime
.IsNull()) {
6628 TimeDuration duration
= TimeStamp::Now() - mLastTime
;
6629 if (duration
.ToMilliseconds() >
6630 StaticPrefs::mousewheel_transaction_timeout()) {
6634 // If we have accumulated delta, we may need to reset it.
6635 if (IsInTransaction()) {
6636 // If wheel event type is changed, reset the values.
6637 if (mHandlingDeltaMode
!= aEvent
->mDeltaMode
||
6638 mIsNoLineOrPageDeltaDevice
!= aEvent
->mIsNoLineOrPageDelta
) {
6641 // If the delta direction is changed, we should reset only the
6642 // accumulated values.
6643 if (mX
&& aEvent
->mDeltaX
&& ((aEvent
->mDeltaX
> 0.0) != (mX
> 0.0))) {
6644 mX
= mPendingScrollAmountX
= 0.0;
6646 if (mY
&& aEvent
->mDeltaY
&& ((aEvent
->mDeltaY
> 0.0) != (mY
> 0.0))) {
6647 mY
= mPendingScrollAmountY
= 0.0;
6652 mHandlingDeltaMode
= aEvent
->mDeltaMode
;
6653 mIsNoLineOrPageDeltaDevice
= aEvent
->mIsNoLineOrPageDelta
;
6656 nsIFrame
* frame
= aESM
->ComputeScrollTarget(aTargetFrame
, aEvent
,
6657 COMPUTE_DEFAULT_ACTION_TARGET
);
6659 frame
? frame
->PresContext() : aTargetFrame
->PresContext();
6660 nsIScrollableFrame
* scrollTarget
= do_QueryFrame(frame
);
6661 aEvent
->mScrollAmount
= aESM
->GetScrollAmount(pc
, aEvent
, scrollTarget
);
6664 // If it's handling neither a device that does not provide line or page deltas
6665 // nor delta values multiplied by prefs, we must not modify lineOrPageDelta
6667 // TODO(emilio): Does this care about overridden scroll speed?
6668 if (!mIsNoLineOrPageDeltaDevice
&&
6669 !EventStateManager::WheelPrefs::GetInstance()
6670 ->NeedToComputeLineOrPageDelta(aEvent
)) {
6671 // Set the delta values to mX and mY. They would be used when above block
6672 // resets mX/mY/mPendingScrollAmountX/mPendingScrollAmountY if the direction
6674 // NOTE: We shouldn't accumulate the delta values, it might could cause
6675 // overflow even though it's not a realistic situation.
6676 if (aEvent
->mDeltaX
) {
6677 mX
= aEvent
->mDeltaX
;
6679 if (aEvent
->mDeltaY
) {
6680 mY
= aEvent
->mDeltaY
;
6682 mLastTime
= TimeStamp::Now();
6686 mX
+= aEvent
->mDeltaX
;
6687 mY
+= aEvent
->mDeltaY
;
6689 if (mHandlingDeltaMode
== WheelEvent_Binding::DOM_DELTA_PIXEL
) {
6690 // Records pixel delta values and init mLineOrPageDeltaX and
6691 // mLineOrPageDeltaY for wheel events which are caused by pixel only
6692 // devices. Ignore mouse wheel transaction for computing this. The
6693 // lineOrPageDelta values will be used by dispatching legacy
6694 // eMouseScrollEventClass (DOMMouseScroll) but not be used for scrolling
6695 // of default action. The transaction should be used only for the default
6697 auto scrollAmountInCSSPixels
=
6698 CSSIntSize::FromAppUnitsRounded(aEvent
->mScrollAmount
);
6700 aEvent
->mLineOrPageDeltaX
= RoundDown(mX
) / scrollAmountInCSSPixels
.width
;
6701 aEvent
->mLineOrPageDeltaY
= RoundDown(mY
) / scrollAmountInCSSPixels
.height
;
6703 mX
-= aEvent
->mLineOrPageDeltaX
* scrollAmountInCSSPixels
.width
;
6704 mY
-= aEvent
->mLineOrPageDeltaY
* scrollAmountInCSSPixels
.height
;
6706 aEvent
->mLineOrPageDeltaX
= RoundDown(mX
);
6707 aEvent
->mLineOrPageDeltaY
= RoundDown(mY
);
6708 mX
-= aEvent
->mLineOrPageDeltaX
;
6709 mY
-= aEvent
->mLineOrPageDeltaY
;
6712 mLastTime
= TimeStamp::Now();
6715 void EventStateManager::DeltaAccumulator::Reset() {
6717 mPendingScrollAmountX
= mPendingScrollAmountY
= 0.0;
6718 mHandlingDeltaMode
= UINT32_MAX
;
6719 mIsNoLineOrPageDeltaDevice
= false;
6723 EventStateManager::DeltaAccumulator::ComputeScrollAmountForDefaultAction(
6724 WidgetWheelEvent
* aEvent
, const nsIntSize
& aScrollAmountInDevPixels
) {
6727 DeltaValues acceleratedDelta
= WheelTransaction::AccelerateWheelDelta(aEvent
);
6729 nsIntPoint
result(0, 0);
6730 if (aEvent
->mDeltaMode
== WheelEvent_Binding::DOM_DELTA_PIXEL
) {
6731 mPendingScrollAmountX
+= acceleratedDelta
.deltaX
;
6732 mPendingScrollAmountY
+= acceleratedDelta
.deltaY
;
6734 mPendingScrollAmountX
+=
6735 aScrollAmountInDevPixels
.width
* acceleratedDelta
.deltaX
;
6736 mPendingScrollAmountY
+=
6737 aScrollAmountInDevPixels
.height
* acceleratedDelta
.deltaY
;
6739 result
.x
= RoundDown(mPendingScrollAmountX
);
6740 result
.y
= RoundDown(mPendingScrollAmountY
);
6741 mPendingScrollAmountX
-= result
.x
;
6742 mPendingScrollAmountY
-= result
.y
;
6747 /******************************************************************/
6748 /* mozilla::EventStateManager::WheelPrefs */
6749 /******************************************************************/
6752 EventStateManager::WheelPrefs
* EventStateManager::WheelPrefs::GetInstance() {
6754 sInstance
= new WheelPrefs();
6760 void EventStateManager::WheelPrefs::Shutdown() {
6762 sInstance
= nullptr;
6766 void EventStateManager::WheelPrefs::OnPrefChanged(const char* aPrefName
,
6768 // forget all prefs, it's not problem for performance.
6770 DeltaAccumulator::GetInstance()->Reset();
6773 EventStateManager::WheelPrefs::WheelPrefs() {
6775 Preferences::RegisterPrefixCallback(OnPrefChanged
, "mousewheel.");
6778 EventStateManager::WheelPrefs::~WheelPrefs() {
6779 Preferences::UnregisterPrefixCallback(OnPrefChanged
, "mousewheel.");
6782 void EventStateManager::WheelPrefs::Reset() { memset(mInit
, 0, sizeof(mInit
)); }
6784 EventStateManager::WheelPrefs::Index
EventStateManager::WheelPrefs::GetIndexFor(
6785 const WidgetWheelEvent
* aEvent
) {
6787 return INDEX_DEFAULT
;
6790 Modifiers modifiers
= (aEvent
->mModifiers
& (MODIFIER_ALT
| MODIFIER_CONTROL
|
6791 MODIFIER_META
| MODIFIER_SHIFT
));
6793 switch (modifiers
) {
6796 case MODIFIER_CONTROL
:
6797 return INDEX_CONTROL
;
6800 case MODIFIER_SHIFT
:
6803 // If two or more modifier keys are pressed, we should use default
6805 return INDEX_DEFAULT
;
6809 void EventStateManager::WheelPrefs::GetBasePrefName(
6810 EventStateManager::WheelPrefs::Index aIndex
, nsACString
& aBasePrefName
) {
6811 aBasePrefName
.AssignLiteral("mousewheel.");
6814 aBasePrefName
.AppendLiteral("with_alt.");
6817 aBasePrefName
.AppendLiteral("with_control.");
6820 aBasePrefName
.AppendLiteral("with_meta.");
6823 aBasePrefName
.AppendLiteral("with_shift.");
6827 aBasePrefName
.AppendLiteral("default.");
6832 void EventStateManager::WheelPrefs::Init(
6833 EventStateManager::WheelPrefs::Index aIndex
) {
6834 if (mInit
[aIndex
]) {
6837 mInit
[aIndex
] = true;
6839 nsAutoCString basePrefName
;
6840 GetBasePrefName(aIndex
, basePrefName
);
6842 nsAutoCString
prefNameX(basePrefName
);
6843 prefNameX
.AppendLiteral("delta_multiplier_x");
6844 mMultiplierX
[aIndex
] =
6845 static_cast<double>(Preferences::GetInt(prefNameX
.get(), 100)) / 100;
6847 nsAutoCString
prefNameY(basePrefName
);
6848 prefNameY
.AppendLiteral("delta_multiplier_y");
6849 mMultiplierY
[aIndex
] =
6850 static_cast<double>(Preferences::GetInt(prefNameY
.get(), 100)) / 100;
6852 nsAutoCString
prefNameZ(basePrefName
);
6853 prefNameZ
.AppendLiteral("delta_multiplier_z");
6854 mMultiplierZ
[aIndex
] =
6855 static_cast<double>(Preferences::GetInt(prefNameZ
.get(), 100)) / 100;
6857 nsAutoCString
prefNameAction(basePrefName
);
6858 prefNameAction
.AppendLiteral("action");
6859 int32_t action
= Preferences::GetInt(prefNameAction
.get(), ACTION_SCROLL
);
6860 if (action
< int32_t(ACTION_NONE
) || action
> int32_t(ACTION_LAST
)) {
6861 NS_WARNING("Unsupported action pref value, replaced with 'Scroll'.");
6862 action
= ACTION_SCROLL
;
6864 mActions
[aIndex
] = static_cast<Action
>(action
);
6866 // Compute action values overridden by .override_x pref.
6867 // At present, override is possible only for the x-direction
6868 // because this pref is introduced mainly for tilt wheels.
6869 // Note that ACTION_HORIZONTALIZED_SCROLL isn't a valid value for this pref
6870 // because it affects only to deltaY.
6871 prefNameAction
.AppendLiteral(".override_x");
6872 int32_t actionOverrideX
= Preferences::GetInt(prefNameAction
.get(), -1);
6873 if (actionOverrideX
< -1 || actionOverrideX
> int32_t(ACTION_LAST
) ||
6874 actionOverrideX
== ACTION_HORIZONTALIZED_SCROLL
) {
6875 NS_WARNING("Unsupported action override pref value, didn't override.");
6876 actionOverrideX
= -1;
6878 mOverriddenActionsX
[aIndex
] = (actionOverrideX
== -1)
6879 ? static_cast<Action
>(action
)
6880 : static_cast<Action
>(actionOverrideX
);
6883 void EventStateManager::WheelPrefs::GetMultiplierForDeltaXAndY(
6884 const WidgetWheelEvent
* aEvent
, Index aIndex
, double* aMultiplierForDeltaX
,
6885 double* aMultiplierForDeltaY
) {
6886 *aMultiplierForDeltaX
= mMultiplierX
[aIndex
];
6887 *aMultiplierForDeltaY
= mMultiplierY
[aIndex
];
6888 // If the event has been horizontalized(I.e. treated as a horizontal wheel
6889 // scroll for a vertical wheel scroll), then we should swap mMultiplierX and
6890 // mMultiplierY. By doing this, multipliers will still apply to the delta
6891 // values they origianlly corresponded to.
6892 if (aEvent
->mDeltaValuesHorizontalizedForDefaultHandler
&&
6893 ComputeActionFor(aEvent
) == ACTION_HORIZONTALIZED_SCROLL
) {
6894 std::swap(*aMultiplierForDeltaX
, *aMultiplierForDeltaY
);
6898 void EventStateManager::WheelPrefs::ApplyUserPrefsToDelta(
6899 WidgetWheelEvent
* aEvent
) {
6900 if (aEvent
->mCustomizedByUserPrefs
) {
6904 Index index
= GetIndexFor(aEvent
);
6907 double multiplierForDeltaX
= 1.0, multiplierForDeltaY
= 1.0;
6908 GetMultiplierForDeltaXAndY(aEvent
, index
, &multiplierForDeltaX
,
6909 &multiplierForDeltaY
);
6910 aEvent
->mDeltaX
*= multiplierForDeltaX
;
6911 aEvent
->mDeltaY
*= multiplierForDeltaY
;
6912 aEvent
->mDeltaZ
*= mMultiplierZ
[index
];
6914 // If the multiplier is 1.0 or -1.0, i.e., it doesn't change the absolute
6915 // value, we should use lineOrPageDelta values which were set by widget.
6916 // Otherwise, we need to compute them from accumulated delta values.
6917 if (!NeedToComputeLineOrPageDelta(aEvent
)) {
6918 aEvent
->mLineOrPageDeltaX
*= static_cast<int32_t>(multiplierForDeltaX
);
6919 aEvent
->mLineOrPageDeltaY
*= static_cast<int32_t>(multiplierForDeltaY
);
6921 aEvent
->mLineOrPageDeltaX
= 0;
6922 aEvent
->mLineOrPageDeltaY
= 0;
6925 aEvent
->mCustomizedByUserPrefs
=
6926 ((mMultiplierX
[index
] != 1.0) || (mMultiplierY
[index
] != 1.0) ||
6927 (mMultiplierZ
[index
] != 1.0));
6930 void EventStateManager::WheelPrefs::CancelApplyingUserPrefsFromOverflowDelta(
6931 WidgetWheelEvent
* aEvent
) {
6932 Index index
= GetIndexFor(aEvent
);
6935 // XXX If the multiplier pref value is negative, the scroll direction was
6936 // changed and caused to scroll different direction. In such case,
6937 // this method reverts the sign of overflowDelta. Does it make widget
6938 // happy? Although, widget can know the pref applied delta values by
6939 // referrencing the deltaX and deltaY of the event.
6941 double multiplierForDeltaX
= 1.0, multiplierForDeltaY
= 1.0;
6942 GetMultiplierForDeltaXAndY(aEvent
, index
, &multiplierForDeltaX
,
6943 &multiplierForDeltaY
);
6944 if (multiplierForDeltaX
) {
6945 aEvent
->mOverflowDeltaX
/= multiplierForDeltaX
;
6947 if (multiplierForDeltaY
) {
6948 aEvent
->mOverflowDeltaY
/= multiplierForDeltaY
;
6952 EventStateManager::WheelPrefs::Action
6953 EventStateManager::WheelPrefs::ComputeActionFor(
6954 const WidgetWheelEvent
* aEvent
) {
6955 Index index
= GetIndexFor(aEvent
);
6958 bool deltaXPreferred
= (Abs(aEvent
->mDeltaX
) > Abs(aEvent
->mDeltaY
) &&
6959 Abs(aEvent
->mDeltaX
) > Abs(aEvent
->mDeltaZ
));
6960 Action
* actions
= deltaXPreferred
? mOverriddenActionsX
: mActions
;
6961 if (actions
[index
] == ACTION_NONE
|| actions
[index
] == ACTION_SCROLL
||
6962 actions
[index
] == ACTION_HORIZONTALIZED_SCROLL
) {
6963 return actions
[index
];
6966 // Momentum events shouldn't run special actions.
6967 if (aEvent
->mIsMomentum
) {
6968 // Use the default action. Note that user might kill the wheel scrolling.
6969 Init(INDEX_DEFAULT
);
6970 if (actions
[INDEX_DEFAULT
] == ACTION_SCROLL
||
6971 actions
[INDEX_DEFAULT
] == ACTION_HORIZONTALIZED_SCROLL
) {
6972 return actions
[INDEX_DEFAULT
];
6977 return actions
[index
];
6980 bool EventStateManager::WheelPrefs::NeedToComputeLineOrPageDelta(
6981 const WidgetWheelEvent
* aEvent
) {
6982 Index index
= GetIndexFor(aEvent
);
6985 return (mMultiplierX
[index
] != 1.0 && mMultiplierX
[index
] != -1.0) ||
6986 (mMultiplierY
[index
] != 1.0 && mMultiplierY
[index
] != -1.0);
6989 void EventStateManager::WheelPrefs::GetUserPrefsForEvent(
6990 const WidgetWheelEvent
* aEvent
, double* aOutMultiplierX
,
6991 double* aOutMultiplierY
) {
6992 Index index
= GetIndexFor(aEvent
);
6995 double multiplierForDeltaX
= 1.0, multiplierForDeltaY
= 1.0;
6996 GetMultiplierForDeltaXAndY(aEvent
, index
, &multiplierForDeltaX
,
6997 &multiplierForDeltaY
);
6998 *aOutMultiplierX
= multiplierForDeltaX
;
6999 *aOutMultiplierY
= multiplierForDeltaY
;
7003 Maybe
<layers::APZWheelAction
> EventStateManager::APZWheelActionFor(
7004 const WidgetWheelEvent
* aEvent
) {
7005 if (aEvent
->mMessage
!= eWheel
) {
7008 WheelPrefs::Action action
=
7009 WheelPrefs::GetInstance()->ComputeActionFor(aEvent
);
7011 case WheelPrefs::ACTION_SCROLL
:
7012 case WheelPrefs::ACTION_HORIZONTALIZED_SCROLL
:
7013 return Some(layers::APZWheelAction::Scroll
);
7014 case WheelPrefs::ACTION_PINCH_ZOOM
:
7015 return Some(layers::APZWheelAction::PinchZoom
);
7022 WheelDeltaAdjustmentStrategy
EventStateManager::GetWheelDeltaAdjustmentStrategy(
7023 const WidgetWheelEvent
& aEvent
) {
7024 if (aEvent
.mMessage
!= eWheel
) {
7025 return WheelDeltaAdjustmentStrategy::eNone
;
7027 switch (WheelPrefs::GetInstance()->ComputeActionFor(&aEvent
)) {
7028 case WheelPrefs::ACTION_SCROLL
:
7029 if (StaticPrefs::mousewheel_autodir_enabled() && 0 == aEvent
.mDeltaZ
) {
7030 if (StaticPrefs::mousewheel_autodir_honourroot()) {
7031 return WheelDeltaAdjustmentStrategy::eAutoDirWithRootHonour
;
7033 return WheelDeltaAdjustmentStrategy::eAutoDir
;
7035 return WheelDeltaAdjustmentStrategy::eNone
;
7036 case WheelPrefs::ACTION_HORIZONTALIZED_SCROLL
:
7037 return WheelDeltaAdjustmentStrategy::eHorizontalize
;
7041 return WheelDeltaAdjustmentStrategy::eNone
;
7044 void EventStateManager::GetUserPrefsForWheelEvent(
7045 const WidgetWheelEvent
* aEvent
, double* aOutMultiplierX
,
7046 double* aOutMultiplierY
) {
7047 WheelPrefs::GetInstance()->GetUserPrefsForEvent(aEvent
, aOutMultiplierX
,
7051 bool EventStateManager::WheelPrefs::IsOverOnePageScrollAllowedX(
7052 const WidgetWheelEvent
* aEvent
) {
7053 Index index
= GetIndexFor(aEvent
);
7055 return Abs(mMultiplierX
[index
]) >=
7056 MIN_MULTIPLIER_VALUE_ALLOWING_OVER_ONE_PAGE_SCROLL
;
7059 bool EventStateManager::WheelPrefs::IsOverOnePageScrollAllowedY(
7060 const WidgetWheelEvent
* aEvent
) {
7061 Index index
= GetIndexFor(aEvent
);
7063 return Abs(mMultiplierY
[index
]) >=
7064 MIN_MULTIPLIER_VALUE_ALLOWING_OVER_ONE_PAGE_SCROLL
;
7067 } // namespace mozilla