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 "PositionedEventTargeting.h"
9 #include "mozilla/EventListenerManager.h"
10 #include "mozilla/MouseEvents.h"
11 #include "mozilla/Preferences.h"
12 #include "mozilla/PresShell.h"
13 #include "mozilla/StaticPrefs_dom.h"
14 #include "mozilla/StaticPrefs_ui.h"
15 #include "mozilla/ToString.h"
16 #include "mozilla/dom/MouseEventBinding.h"
17 #include "nsContainerFrame.h"
18 #include "nsFrameList.h" // for DEBUG_FRAME_DUMP
19 #include "nsHTMLParts.h"
20 #include "nsLayoutUtils.h"
21 #include "nsGkAtoms.h"
22 #include "nsFontMetrics.h"
23 #include "nsPrintfCString.h"
24 #include "mozilla/dom/Element.h"
26 #include "nsDeviceContext.h"
27 #include "nsIContentInlines.h"
31 using namespace mozilla
;
32 using namespace mozilla::dom
;
34 // If debugging this code you may wish to enable this logging, via
35 // the env var MOZ_LOG="event.retarget:4". For extra logging (getting
36 // frame dumps, use MOZ_LOG="event.retarget:5".
37 static mozilla::LazyLogModule
sEvtTgtLog("event.retarget");
38 #define PET_LOG(...) MOZ_LOG(sEvtTgtLog, LogLevel::Debug, (__VA_ARGS__))
43 * The basic goal of FindFrameTargetedByInputEvent() is to find a good
44 * target element that can respond to mouse events. Both mouse events and touch
45 * events are targeted at this element. Note that even for touch events, we
46 * check responsiveness to mouse events. We assume Web authors
47 * designing for touch events will take their own steps to account for
48 * inaccurate touch events.
50 * GetClickableAncestor() encapsulates the heuristic that determines whether an
51 * element is expected to respond to mouse events. An element is deemed
52 * "clickable" if it has registered listeners for "click", "mousedown" or
53 * "mouseup", or is on a whitelist of element tags (<a>, <button>, <input>,
54 * <select>, <textarea>, <label>), or has role="button", or is a link, or
55 * is a suitable XUL element.
56 * Any descendant (in the same document) of a clickable element is also
57 * deemed clickable since events will propagate to the clickable element from
60 * If the element directly under the event position is clickable (or
61 * event radii are disabled), we always use that element. Otherwise we collect
62 * all frames intersecting a rectangle around the event position (taking CSS
63 * transforms into account) and choose the best candidate in GetClosest().
64 * Only GetClickableAncestor() candidates are considered; if none are found,
65 * then we revert to targeting the element under the event position.
66 * We ignore candidates outside the document subtree rooted by the
67 * document of the element directly under the event position. This ensures that
68 * event listeners in ancestor documents don't make it completely impossible
69 * to target a non-clickable element in a child document.
71 * When both a frame and its ancestor are in the candidate list, we ignore
72 * the ancestor. Otherwise a large ancestor element with a mouse event listener
73 * and some descendant elements that need to be individually targetable would
74 * disable intelligent targeting of those descendants within its bounds.
76 * GetClosest() computes the transformed axis-aligned bounds of each
77 * candidate frame, then computes the Manhattan distance from the event point
78 * to the bounds rect (which can be zero). The frame with the
79 * shortest distance is chosen. For visited links we multiply the distance
80 * by a specified constant weight; this can be used to make visited links
81 * more or less likely to be targeted than non-visited links.
84 // Enum that determines which type of elements to count as targets in the
85 // search. Clickable elements are generally ones that respond to click events,
86 // like form inputs and links and things with click event listeners.
87 // Touchable elements are a much narrower set of elements; ones with touchstart
88 // and touchend listeners.
89 enum class SearchType
{
95 struct EventRadiusPrefs
{
96 bool mEnabled
; // other fields are valid iff this field is true
97 uint32_t mVisitedWeight
; // in percent, i.e. default is 100
98 uint32_t mRadiusTopmm
;
99 uint32_t mRadiusRightmm
;
100 uint32_t mRadiusBottommm
;
101 uint32_t mRadiusLeftmm
;
104 SearchType mSearchType
;
106 explicit EventRadiusPrefs(EventClassID aEventClassID
) {
107 if (aEventClassID
== eTouchEventClass
) {
108 mEnabled
= StaticPrefs::ui_touch_radius_enabled();
109 mVisitedWeight
= StaticPrefs::ui_touch_radius_visitedWeight();
110 mRadiusTopmm
= StaticPrefs::ui_touch_radius_topmm();
111 mRadiusRightmm
= StaticPrefs::ui_touch_radius_rightmm();
112 mRadiusBottommm
= StaticPrefs::ui_touch_radius_bottommm();
113 mRadiusLeftmm
= StaticPrefs::ui_touch_radius_leftmm();
114 mTouchOnly
= false; // Always false, unlike mouse events.
115 mReposition
= false; // Always false, unlike mouse events.
116 mSearchType
= SearchType::Touchable
;
118 } else if (aEventClassID
== eMouseEventClass
) {
119 mEnabled
= StaticPrefs::ui_mouse_radius_enabled();
120 mVisitedWeight
= StaticPrefs::ui_mouse_radius_visitedWeight();
121 mRadiusTopmm
= StaticPrefs::ui_mouse_radius_topmm();
122 mRadiusRightmm
= StaticPrefs::ui_mouse_radius_rightmm();
123 mRadiusBottommm
= StaticPrefs::ui_mouse_radius_bottommm();
124 mRadiusLeftmm
= StaticPrefs::ui_mouse_radius_leftmm();
125 mTouchOnly
= StaticPrefs::ui_mouse_radius_inputSource_touchOnly();
126 mReposition
= StaticPrefs::ui_mouse_radius_reposition();
127 mSearchType
= SearchType::Clickable
;
138 mSearchType
= SearchType::None
;
143 static bool HasMouseListener(nsIContent
* aContent
) {
144 if (EventListenerManager
* elm
= aContent
->GetExistingListenerManager()) {
145 return elm
->HasListenersFor(nsGkAtoms::onclick
) ||
146 elm
->HasListenersFor(nsGkAtoms::onmousedown
) ||
147 elm
->HasListenersFor(nsGkAtoms::onmouseup
);
153 static bool HasTouchListener(nsIContent
* aContent
) {
154 EventListenerManager
* elm
= aContent
->GetExistingListenerManager();
159 // FIXME: Should this really use the pref rather than TouchEvent::PrefEnabled
161 if (!StaticPrefs::dom_w3c_touch_events_enabled()) {
165 return elm
->HasNonSystemGroupListenersFor(nsGkAtoms::ontouchstart
) ||
166 elm
->HasNonSystemGroupListenersFor(nsGkAtoms::ontouchend
);
169 static bool HasPointerListener(nsIContent
* aContent
) {
170 EventListenerManager
* elm
= aContent
->GetExistingListenerManager();
175 return elm
->HasListenersFor(nsGkAtoms::onpointerdown
) ||
176 elm
->HasListenersFor(nsGkAtoms::onpointerup
);
179 static bool IsDescendant(nsIFrame
* aFrame
, nsIContent
* aAncestor
,
180 nsAutoString
* aLabelTargetId
) {
181 for (nsIContent
* content
= aFrame
->GetContent(); content
;
182 content
= content
->GetFlattenedTreeParent()) {
183 if (aLabelTargetId
&& content
->IsHTMLElement(nsGkAtoms::label
)) {
184 content
->AsElement()->GetAttr(nsGkAtoms::_for
, *aLabelTargetId
);
186 if (content
== aAncestor
) {
193 static nsIContent
* GetTouchableAncestor(nsIFrame
* aFrame
,
194 nsAtom
* aStopAt
= nullptr) {
195 // Input events propagate up the content tree so we'll follow the content
196 // ancestors to look for elements accepting the touch event.
197 for (nsIContent
* content
= aFrame
->GetContent(); content
;
198 content
= content
->GetFlattenedTreeParent()) {
199 if (aStopAt
&& content
->IsHTMLElement(aStopAt
)) {
202 if (HasTouchListener(content
)) {
209 static nsIContent
* GetClickableAncestor(
210 nsIFrame
* aFrame
, nsAtom
* aStopAt
= nullptr,
211 nsAutoString
* aLabelTargetId
= nullptr) {
212 // If the frame is `cursor:pointer` or inherits `cursor:pointer` from an
213 // ancestor, treat it as clickable. This is a heuristic to deal with pages
214 // where the click event listener is on the <body> or <html> element but it
215 // triggers an action on some specific element. We want the specific element
216 // to be considered clickable, and at least some pages that do this indicate
217 // the clickability by setting `cursor:pointer`, so we use that here.
218 // Note that descendants of `cursor:pointer` elements that override the
219 // inherited `pointer` to `auto` or any other value are NOT treated as
220 // clickable, because it seems like the content author is trying to express
221 // non-clickability on that sub-element.
222 // In the future depending on real-world cases it might make sense to expand
223 // this check to any non-auto cursor. Such a change would also pick up things
224 // like contenteditable or input fields, which can then be removed from the
225 // loop below, and would have better performance.
226 if (aFrame
->StyleUI()->Cursor().keyword
== StyleCursorKind::Pointer
) {
227 return aFrame
->GetContent();
230 // Input events propagate up the content tree so we'll follow the content
231 // ancestors to look for elements accepting the click.
232 for (nsIContent
* content
= aFrame
->GetContent(); content
;
233 content
= content
->GetFlattenedTreeParent()) {
234 if (aStopAt
&& content
->IsHTMLElement(aStopAt
)) {
237 if (HasTouchListener(content
) || HasMouseListener(content
) ||
238 HasPointerListener(content
)) {
241 if (content
->IsAnyOfHTMLElements(nsGkAtoms::button
, nsGkAtoms::input
,
242 nsGkAtoms::select
, nsGkAtoms::textarea
)) {
245 if (content
->IsHTMLElement(nsGkAtoms::label
)) {
246 if (aLabelTargetId
) {
247 content
->AsElement()->GetAttr(nsGkAtoms::_for
, *aLabelTargetId
);
252 // See nsCSSFrameConstructor::FindXULTagData. This code is not
253 // really intended to be used with XUL, though.
254 if (content
->IsAnyOfXULElements(
255 nsGkAtoms::button
, nsGkAtoms::checkbox
, nsGkAtoms::radio
,
256 nsGkAtoms::menu
, nsGkAtoms::menuitem
, nsGkAtoms::menulist
,
257 nsGkAtoms::scrollbarbutton
, nsGkAtoms::resizer
)) {
261 static Element::AttrValuesArray clickableRoles
[] = {
262 nsGkAtoms::button
, nsGkAtoms::key
, nullptr};
263 if (auto* element
= Element::FromNode(*content
)) {
264 if (element
->IsLink()) {
267 if (element
->FindAttrValueIn(kNameSpaceID_None
, nsGkAtoms::role
,
268 clickableRoles
, eIgnoreCase
) >= 0) {
272 if (content
->IsEditable()) {
279 static nscoord
AppUnitsFromMM(RelativeTo aFrame
, uint32_t aMM
) {
280 nsPresContext
* pc
= aFrame
.mFrame
->PresContext();
281 float result
= float(aMM
) * (pc
->DeviceContext()->AppUnitsPerPhysicalInch() /
283 if (aFrame
.mViewportType
== ViewportType::Layout
) {
284 PresShell
* presShell
= pc
->PresShell();
285 result
= result
/ presShell
->GetResolution();
287 return NSToCoordRound(result
);
291 * Clip aRect with the bounds of aFrame in the coordinate system of
292 * aRootFrame. aRootFrame is an ancestor of aFrame.
294 static nsRect
ClipToFrame(RelativeTo aRootFrame
, const nsIFrame
* aFrame
,
296 nsRect bound
= nsLayoutUtils::TransformFrameRectToAncestor(
297 aFrame
, nsRect(nsPoint(0, 0), aFrame
->GetSize()), aRootFrame
);
298 nsRect result
= bound
.Intersect(aRect
);
302 static nsRect
GetTargetRect(RelativeTo aRootFrame
,
303 const nsPoint
& aPointRelativeToRootFrame
,
304 const nsIFrame
* aRestrictToDescendants
,
305 const EventRadiusPrefs
& aPrefs
, uint32_t aFlags
) {
306 nsMargin
m(AppUnitsFromMM(aRootFrame
, aPrefs
.mRadiusTopmm
),
307 AppUnitsFromMM(aRootFrame
, aPrefs
.mRadiusRightmm
),
308 AppUnitsFromMM(aRootFrame
, aPrefs
.mRadiusBottommm
),
309 AppUnitsFromMM(aRootFrame
, aPrefs
.mRadiusLeftmm
));
310 nsRect
r(aPointRelativeToRootFrame
, nsSize(0, 0));
312 if (!(aFlags
& INPUT_IGNORE_ROOT_SCROLL_FRAME
)) {
313 // Don't clip this rect to the root scroll frame if the flag to ignore the
314 // root scroll frame is set. Note that the GetClosest code will still
315 // enforce that the target found is a descendant of aRestrictToDescendants.
316 r
= ClipToFrame(aRootFrame
, aRestrictToDescendants
, r
);
321 static float ComputeDistanceFromRect(const nsPoint
& aPoint
,
322 const nsRect
& aRect
) {
324 std::max(0, std::max(aRect
.x
- aPoint
.x
, aPoint
.x
- aRect
.XMost()));
326 std::max(0, std::max(aRect
.y
- aPoint
.y
, aPoint
.y
- aRect
.YMost()));
327 return float(NS_hypot(dx
, dy
));
330 static float ComputeDistanceFromRegion(const nsPoint
& aPoint
,
331 const nsRegion
& aRegion
) {
332 MOZ_ASSERT(!aRegion
.IsEmpty(),
333 "can't compute distance between point and empty region");
335 for (auto iter
= aRegion
.RectIter(); !iter
.Done(); iter
.Next()) {
336 float dist
= ComputeDistanceFromRect(aPoint
, iter
.Get());
337 if (dist
< minDist
|| minDist
< 0) {
344 // Subtract aRegion from aExposedRegion as long as that doesn't make the
345 // exposed region get too complex or removes a big chunk of the exposed region.
346 static void SubtractFromExposedRegion(nsRegion
* aExposedRegion
,
347 const nsRegion
& aRegion
) {
348 if (aRegion
.IsEmpty()) {
353 tmp
.Sub(*aExposedRegion
, aRegion
);
354 // Don't let *aExposedRegion get too complex, but don't let it fluff out to
355 // its bounds either. Do let aExposedRegion get more complex if by doing so
356 // we reduce its area by at least half.
357 if (tmp
.GetNumRects() <= 15 || tmp
.Area() <= aExposedRegion
->Area() / 2) {
358 *aExposedRegion
= tmp
;
362 static nsIFrame
* GetClosest(RelativeTo aRoot
,
363 const nsPoint
& aPointRelativeToRootFrame
,
364 const nsRect
& aTargetRect
,
365 const EventRadiusPrefs
& aPrefs
,
366 const nsIFrame
* aRestrictToDescendants
,
367 nsIContent
* aClickableAncestor
,
368 nsTArray
<nsIFrame
*>& aCandidates
) {
369 nsIFrame
* bestTarget
= nullptr;
370 // Lower is better; distance is in appunits
371 float bestDistance
= 1e6f
;
372 nsRegion
exposedRegion(aTargetRect
);
373 for (uint32_t i
= 0; i
< aCandidates
.Length(); ++i
) {
374 nsIFrame
* f
= aCandidates
[i
];
376 bool preservesAxisAlignedRectangles
= false;
377 nsRect borderBox
= nsLayoutUtils::TransformFrameRectToAncestor(
378 f
, nsRect(nsPoint(0, 0), f
->GetSize()), aRoot
,
379 &preservesAxisAlignedRectangles
);
380 PET_LOG("Checking candidate %p with border box %s\n", f
,
381 ToString(borderBox
).c_str());
383 region
.And(exposedRegion
, borderBox
);
384 if (region
.IsEmpty()) {
385 PET_LOG(" candidate %p had empty hit region\n", f
);
389 if (preservesAxisAlignedRectangles
) {
390 // Subtract from the exposed region if we have a transform that won't make
391 // the bounds include a bunch of area that we don't actually cover.
392 SubtractFromExposedRegion(&exposedRegion
, region
);
395 nsAutoString labelTargetId
;
396 if (aClickableAncestor
&&
397 !IsDescendant(f
, aClickableAncestor
, &labelTargetId
)) {
398 PET_LOG(" candidate %p is not a descendant of required ancestor\n", f
);
402 if (aPrefs
.mSearchType
== SearchType::Clickable
) {
403 nsIContent
* clickableContent
=
404 GetClickableAncestor(f
, nsGkAtoms::body
, &labelTargetId
);
405 if (!aClickableAncestor
&& !clickableContent
) {
406 PET_LOG(" candidate %p was not clickable\n", f
);
409 } else if (aPrefs
.mSearchType
== SearchType::Touchable
) {
410 nsIContent
* touchableContent
= GetTouchableAncestor(f
, nsGkAtoms::body
);
411 if (!touchableContent
) {
412 PET_LOG(" candidate %p was not touchable\n", f
);
417 // If our current closest frame is a descendant of 'f', skip 'f' (prefer
418 // the nested frame).
419 if (bestTarget
&& nsLayoutUtils::IsProperAncestorFrameCrossDoc(
420 f
, bestTarget
, aRoot
.mFrame
)) {
421 PET_LOG(" candidate %p was ancestor for bestTarget %p\n", f
, bestTarget
);
424 if (!aClickableAncestor
&& !nsLayoutUtils::IsAncestorFrameCrossDoc(
425 aRestrictToDescendants
, f
, aRoot
.mFrame
)) {
426 PET_LOG(" candidate %p was not descendant of restrictroot %p\n", f
,
427 aRestrictToDescendants
);
431 // distance is in appunits
433 ComputeDistanceFromRegion(aPointRelativeToRootFrame
, region
);
434 nsIContent
* content
= f
->GetContent();
435 if (content
&& content
->IsElement() &&
436 content
->AsElement()->State().HasState(
437 ElementState(ElementState::VISITED
))) {
438 distance
*= aPrefs
.mVisitedWeight
/ 100.0f
;
440 if (distance
< bestDistance
) {
441 PET_LOG(" candidate %p is the new best\n", f
);
442 bestDistance
= distance
;
449 // Walk from aTarget up to aRoot, and return the first frame found with an
450 // explicit z-index set on it. If no such frame is found, aRoot is returned.
451 static const nsIFrame
* FindZIndexAncestor(const nsIFrame
* aTarget
,
452 const nsIFrame
* aRoot
) {
453 const nsIFrame
* candidate
= aTarget
;
454 while (candidate
&& candidate
!= aRoot
) {
455 if (candidate
->ZIndex().valueOr(0) > 0) {
456 PET_LOG("Restricting search to z-index root %p\n", candidate
);
459 candidate
= candidate
->GetParent();
464 nsIFrame
* FindFrameTargetedByInputEvent(
465 WidgetGUIEvent
* aEvent
, RelativeTo aRootFrame
,
466 const nsPoint
& aPointRelativeToRootFrame
, uint32_t aFlags
) {
467 using FrameForPointOption
= nsLayoutUtils::FrameForPointOption
;
468 EnumSet
<FrameForPointOption
> options
;
469 if (aFlags
& INPUT_IGNORE_ROOT_SCROLL_FRAME
) {
470 options
+= FrameForPointOption::IgnoreRootScrollFrame
;
472 nsIFrame
* target
= nsLayoutUtils::GetFrameForPoint(
473 aRootFrame
, aPointRelativeToRootFrame
, options
);
475 "Found initial target %p for event class %s message %s point %s "
476 "relative to root frame %s\n",
477 target
, ToChar(aEvent
->mClass
), ToChar(aEvent
->mMessage
),
478 ToString(aPointRelativeToRootFrame
).c_str(),
479 ToString(aRootFrame
).c_str());
481 EventRadiusPrefs
prefs(aEvent
->mClass
);
482 if (!prefs
.mEnabled
|| EventRetargetSuppression::IsActive()) {
483 PET_LOG("Retargeting disabled\n");
487 // Do not modify targeting for actual mouse hardware; only for mouse
488 // events generated by touch-screen hardware.
489 if (aEvent
->mClass
== eMouseEventClass
&& prefs
.mTouchOnly
&&
490 aEvent
->AsMouseEvent()->mInputSource
!=
491 MouseEvent_Binding::MOZ_SOURCE_TOUCH
) {
492 PET_LOG("Mouse input event is not from a touch source\n");
496 // If the exact target is non-null, only consider candidate targets in the
497 // same document as the exact target. Otherwise, if an ancestor document has
498 // a mouse event handler for example, targets that are !GetClickableAncestor
499 // can never be targeted --- something nsSubDocumentFrame in an ancestor
500 // document would be targeted instead.
501 const nsIFrame
* restrictToDescendants
= [&]() -> const nsIFrame
* {
502 if (target
&& target
->PresContext() != aRootFrame
.mFrame
->PresContext()) {
503 return target
->PresShell()->GetRootFrame();
505 return aRootFrame
.mFrame
;
508 // Ignore retarget if target is editable.
509 nsIContent
* targetContent
= target
? target
->GetContent() : nullptr;
510 if (targetContent
&& targetContent
->IsEditable()) {
511 PET_LOG("Target %p is editable\n", target
);
515 // If the target element inside an element with a z-index, restrict the
516 // search to other elements inside that z-index. This is a heuristic
517 // intended to help with a class of scenarios involving web modals or
518 // web popup type things. In particular it helps alleviate bug 1666792.
519 restrictToDescendants
= FindZIndexAncestor(target
, restrictToDescendants
);
521 nsRect targetRect
= GetTargetRect(aRootFrame
, aPointRelativeToRootFrame
,
522 restrictToDescendants
, prefs
, aFlags
);
523 PET_LOG("Expanded point to target rect %s\n", ToString(targetRect
).c_str());
524 AutoTArray
<nsIFrame
*, 8> candidates
;
525 nsresult rv
= nsLayoutUtils::GetFramesForArea(aRootFrame
, targetRect
,
526 candidates
, options
);
531 nsIContent
* clickableAncestor
= nullptr;
533 clickableAncestor
= GetClickableAncestor(target
, nsGkAtoms::body
);
534 if (clickableAncestor
) {
535 PET_LOG("Target %p is clickable\n", target
);
536 // If the target that was directly hit has a clickable ancestor, that
537 // means it too is clickable. And since it is the same as or a
538 // descendant of clickableAncestor, it should become the root for the
539 // GetClosest search.
540 clickableAncestor
= target
->GetContent();
545 GetClosest(aRootFrame
, aPointRelativeToRootFrame
, targetRect
, prefs
,
546 restrictToDescendants
, clickableAncestor
, candidates
);
551 PET_LOG("Final target is %p\n", target
);
553 #ifdef DEBUG_FRAME_DUMP
554 // At verbose logging level, dump the frame tree to help with debugging.
555 // Note that dumping the frame tree at the top of the function may flood
556 // logcat on Android devices and cause the PET_LOGs to get dropped.
557 if (MOZ_LOG_TEST(sEvtTgtLog
, LogLevel::Verbose
)) {
559 target
->DumpFrameTree();
561 aRootFrame
.mFrame
->DumpFrameTree();
566 if (!target
|| !prefs
.mReposition
) {
567 // No repositioning required for this event
571 // Take the point relative to the root frame, make it relative to the target,
572 // clamp it to the bounds, and then make it relative to the root frame again.
573 nsPoint point
= aPointRelativeToRootFrame
;
574 if (nsLayoutUtils::TRANSFORM_SUCCEEDED
!=
575 nsLayoutUtils::TransformPoint(aRootFrame
, RelativeTo
{target
}, point
)) {
578 point
= target
->GetRectRelativeToSelf().ClampPoint(point
);
579 if (nsLayoutUtils::TRANSFORM_SUCCEEDED
!=
580 nsLayoutUtils::TransformPoint(RelativeTo
{target
}, aRootFrame
, point
)) {
583 // Now we basically undo the operations in GetEventCoordinatesRelativeTo, to
584 // get back the (now-clamped) coordinates in the event's widget's space.
585 nsView
* view
= aRootFrame
.mFrame
->GetView();
589 LayoutDeviceIntPoint widgetPoint
= nsLayoutUtils::TranslateViewToWidget(
590 aRootFrame
.mFrame
->PresContext(), view
, point
, aRootFrame
.mViewportType
,
592 if (widgetPoint
.x
!= NS_UNCONSTRAINEDSIZE
) {
593 // If that succeeded, we update the point in the event
594 aEvent
->mRefPoint
= widgetPoint
;
599 uint32_t EventRetargetSuppression::sSuppressionCount
= 0;
601 EventRetargetSuppression::EventRetargetSuppression() { sSuppressionCount
++; }
603 EventRetargetSuppression::~EventRetargetSuppression() { sSuppressionCount
--; }
605 bool EventRetargetSuppression::IsActive() { return sSuppressionCount
> 0; }
607 } // namespace mozilla