Bug 1879449 [wpt PR 44489] - [wptrunner] Add `infrastructure/expected-fail/` test...
[gecko.git] / layout / base / PositionedEventTargeting.cpp
bloba975372dea0812b4a76fe92716764091b474057d
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"
25 #include "nsRegion.h"
26 #include "nsDeviceContext.h"
27 #include "nsIContentInlines.h"
28 #include "nsIFrame.h"
29 #include <algorithm>
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__))
40 namespace mozilla {
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
58 * its descendant.
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 {
90 None,
91 Clickable,
92 Touchable,
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;
102 bool mTouchOnly;
103 bool mReposition;
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;
129 } else {
130 mEnabled = false;
131 mVisitedWeight = 0;
132 mRadiusTopmm = 0;
133 mRadiusRightmm = 0;
134 mRadiusBottommm = 0;
135 mRadiusLeftmm = 0;
136 mTouchOnly = false;
137 mReposition = false;
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);
150 return false;
153 static bool HasTouchListener(nsIContent* aContent) {
154 EventListenerManager* elm = aContent->GetExistingListenerManager();
155 if (!elm) {
156 return false;
159 // FIXME: Should this really use the pref rather than TouchEvent::PrefEnabled
160 // or such?
161 if (!StaticPrefs::dom_w3c_touch_events_enabled()) {
162 return false;
165 return elm->HasNonSystemGroupListenersFor(nsGkAtoms::ontouchstart) ||
166 elm->HasNonSystemGroupListenersFor(nsGkAtoms::ontouchend);
169 static bool HasPointerListener(nsIContent* aContent) {
170 EventListenerManager* elm = aContent->GetExistingListenerManager();
171 if (!elm) {
172 return false;
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) {
187 return true;
190 return false;
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)) {
200 break;
202 if (HasTouchListener(content)) {
203 return content;
206 return nullptr;
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)) {
235 break;
237 if (HasTouchListener(content) || HasMouseListener(content) ||
238 HasPointerListener(content)) {
239 return content;
241 if (content->IsAnyOfHTMLElements(nsGkAtoms::button, nsGkAtoms::input,
242 nsGkAtoms::select, nsGkAtoms::textarea)) {
243 return content;
245 if (content->IsHTMLElement(nsGkAtoms::label)) {
246 if (aLabelTargetId) {
247 content->AsElement()->GetAttr(nsGkAtoms::_for, *aLabelTargetId);
249 return content;
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)) {
258 return content;
261 static Element::AttrValuesArray clickableRoles[] = {
262 nsGkAtoms::button, nsGkAtoms::key, nullptr};
263 if (auto* element = Element::FromNode(*content)) {
264 if (element->IsLink()) {
265 return content;
267 if (element->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::role,
268 clickableRoles, eIgnoreCase) >= 0) {
269 return content;
272 if (content->IsEditable()) {
273 return content;
276 return nullptr;
279 static nscoord AppUnitsFromMM(RelativeTo aFrame, uint32_t aMM) {
280 nsPresContext* pc = aFrame.mFrame->PresContext();
281 float result = float(aMM) * (pc->DeviceContext()->AppUnitsPerPhysicalInch() /
282 MM_PER_INCH_FLOAT);
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,
295 nsRect& aRect) {
296 nsRect bound = nsLayoutUtils::TransformFrameRectToAncestor(
297 aFrame, nsRect(nsPoint(0, 0), aFrame->GetSize()), aRootFrame);
298 nsRect result = bound.Intersect(aRect);
299 return result;
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));
311 r.Inflate(m);
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);
318 return r;
321 static float ComputeDistanceFromRect(const nsPoint& aPoint,
322 const nsRect& aRect) {
323 nscoord dx =
324 std::max(0, std::max(aRect.x - aPoint.x, aPoint.x - aRect.XMost()));
325 nscoord dy =
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");
334 float minDist = -1;
335 for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) {
336 float dist = ComputeDistanceFromRect(aPoint, iter.Get());
337 if (dist < minDist || minDist < 0) {
338 minDist = dist;
341 return minDist;
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()) {
349 return;
352 nsRegion tmp;
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());
382 nsRegion region;
383 region.And(exposedRegion, borderBox);
384 if (region.IsEmpty()) {
385 PET_LOG(" candidate %p had empty hit region\n", f);
386 continue;
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);
399 continue;
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);
407 continue;
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);
413 continue;
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);
422 continue;
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);
428 continue;
431 // distance is in appunits
432 float distance =
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;
443 bestTarget = f;
446 return bestTarget;
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);
457 return candidate;
459 candidate = candidate->GetParent();
461 return aRoot;
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);
474 PET_LOG(
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");
484 return target;
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");
493 return target;
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;
506 }();
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);
512 return 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);
527 if (NS_FAILED(rv)) {
528 return target;
531 nsIContent* clickableAncestor = nullptr;
532 if (target) {
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();
544 nsIFrame* closest =
545 GetClosest(aRootFrame, aPointRelativeToRootFrame, targetRect, prefs,
546 restrictToDescendants, clickableAncestor, candidates);
547 if (closest) {
548 target = closest;
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)) {
558 if (target) {
559 target->DumpFrameTree();
560 } else {
561 aRootFrame.mFrame->DumpFrameTree();
564 #endif
566 if (!target || !prefs.mReposition) {
567 // No repositioning required for this event
568 return target;
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)) {
576 return target;
578 point = target->GetRectRelativeToSelf().ClampPoint(point);
579 if (nsLayoutUtils::TRANSFORM_SUCCEEDED !=
580 nsLayoutUtils::TransformPoint(RelativeTo{target}, aRootFrame, point)) {
581 return target;
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();
586 if (!view) {
587 return target;
589 LayoutDeviceIntPoint widgetPoint = nsLayoutUtils::TranslateViewToWidget(
590 aRootFrame.mFrame->PresContext(), view, point, aRootFrame.mViewportType,
591 aEvent->mWidget);
592 if (widgetPoint.x != NS_UNCONSTRAINEDSIZE) {
593 // If that succeeded, we update the point in the event
594 aEvent->mRefPoint = widgetPoint;
596 return target;
599 uint32_t EventRetargetSuppression::sSuppressionCount = 0;
601 EventRetargetSuppression::EventRetargetSuppression() { sSuppressionCount++; }
603 EventRetargetSuppression::~EventRetargetSuppression() { sSuppressionCount--; }
605 bool EventRetargetSuppression::IsActive() { return sSuppressionCount > 0; }
607 } // namespace mozilla