From 764428607951441626191820c6d341bfff5a1a7e Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Tue, 4 Nov 2014 09:52:16 -0500 Subject: [PATCH] Bug 1078029 - Add code to reposition input event coordinates into the target frame. r=roc The PositionedEventTargeting code allows input events to be dispatched to a target not directly under the input event point. However, the coordinates of the input event can then end up outside the bounding rect of the event target. This state is generally unexpected by web content and may cause compatibility issues. Fennec's front-end code used to deal with this by repositioning the input event coordinates to be inside the bounding rect; now that Fennec is using the shared C++ code we need to have that code here. This behaviour is guarded by a pref and disabled by default (but enabled on Fennec). --- layout/base/PositionedEventTargeting.cpp | 39 ++++++++++++++++++++-- layout/base/PositionedEventTargeting.h | 2 +- layout/base/nsLayoutUtils.cpp | 42 ++++++++++++++++-------- layout/base/nsLayoutUtils.h | 12 +++++++ mobile/android/app/mobile.js | 1 + 5 files changed, 80 insertions(+), 16 deletions(-) diff --git a/layout/base/PositionedEventTargeting.cpp b/layout/base/PositionedEventTargeting.cpp index 21521921ca97..19939e541b7f 100644 --- a/layout/base/PositionedEventTargeting.cpp +++ b/layout/base/PositionedEventTargeting.cpp @@ -68,6 +68,7 @@ struct EventRadiusPrefs bool mEnabled; bool mRegistered; bool mTouchOnly; + bool mRepositionEventCoords; }; static EventRadiusPrefs sMouseEventRadiusPrefs; @@ -111,6 +112,9 @@ GetPrefsFor(EventClassID aEventClassID) } else { prefs->mTouchOnly = false; } + + nsPrintfCString repositionPref("ui.%s.radius.reposition", prefBranch); + Preferences::AddBoolVarCache(&prefs->mRepositionEventCoords, repositionPref.get(), false); } return prefs; @@ -360,7 +364,7 @@ GetClosest(nsIFrame* aRoot, const nsPoint& aPointRelativeToRootFrame, } nsIFrame* -FindFrameTargetedByInputEvent(const WidgetGUIEvent* aEvent, +FindFrameTargetedByInputEvent(WidgetGUIEvent* aEvent, nsIFrame* aRootFrame, const nsPoint& aPointRelativeToRootFrame, uint32_t aFlags) @@ -403,7 +407,38 @@ FindFrameTargetedByInputEvent(const WidgetGUIEvent* aEvent, nsIFrame* closestClickable = GetClosest(aRootFrame, aPointRelativeToRootFrame, targetRect, prefs, restrictToDescendants, candidates); - return closestClickable ? closestClickable : target; + if (closestClickable) { + target = closestClickable; + } + + if (!target || !prefs->mRepositionEventCoords) { + // No repositioning required for this event + return target; + } + + // Take the point relative to the root frame, make it relative to the target, + // clamp it to the bounds, and then make it relative to the root frame again. + nsPoint point = aPointRelativeToRootFrame; + if (nsLayoutUtils::TRANSFORM_SUCCEEDED != nsLayoutUtils::TransformPoint(aRootFrame, target, point)) { + return target; + } + point = target->GetRectRelativeToSelf().ClampPoint(point); + if (nsLayoutUtils::TRANSFORM_SUCCEEDED != nsLayoutUtils::TransformPoint(target, aRootFrame, point)) { + return target; + } + // Now we basically undo the operations in GetEventCoordinatesRelativeTo, to + // get back the (now-clamped) coordinates in the event's widget's space. + nsView* view = aRootFrame->GetView(); + if (!view) { + return target; + } + nsIntPoint widgetPoint = nsLayoutUtils::TranslateViewToWidget( + aRootFrame->PresContext(), view, point, aEvent->widget); + if (widgetPoint.x != NS_UNCONSTRAINEDSIZE) { + // If that succeeded, we update the point in the event + aEvent->refPoint = LayoutDeviceIntPoint::FromUntyped(widgetPoint); + } + return target; } } diff --git a/layout/base/PositionedEventTargeting.h b/layout/base/PositionedEventTargeting.h index 9bdebed709f0..d6535fd0b3a0 100644 --- a/layout/base/PositionedEventTargeting.h +++ b/layout/base/PositionedEventTargeting.h @@ -22,7 +22,7 @@ enum { * that are suitable targets, to account for inaccurate pointing devices. */ nsIFrame* -FindFrameTargetedByInputEvent(const WidgetGUIEvent* aEvent, +FindFrameTargetedByInputEvent(WidgetGUIEvent* aEvent, nsIFrame* aRootFrame, const nsPoint& aPointRelativeToRootFrame, uint32_t aFlags = 0); diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index 67d1d4bf05b0..bc44caebe81a 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -2611,6 +2611,18 @@ static nsIntPoint GetWidgetOffset(nsIWidget* aWidget, nsIWidget*& aRootWidget) { return offset; } +static nsIntPoint WidgetToWidgetOffset(nsIWidget* aFrom, nsIWidget* aTo) { + nsIWidget* fromRoot; + nsIntPoint fromOffset = GetWidgetOffset(aFrom, fromRoot); + nsIWidget* toRoot; + nsIntPoint toOffset = GetWidgetOffset(aTo, toRoot); + + if (fromRoot == toRoot) { + return fromOffset - toOffset; + } + return aFrom->WidgetToScreenOffset() - aTo->WidgetToScreenOffset(); +} + nsPoint nsLayoutUtils::TranslateWidgetToView(nsPresContext* aPresContext, nsIWidget* aWidget, nsIntPoint aPt, @@ -2622,24 +2634,28 @@ nsLayoutUtils::TranslateWidgetToView(nsPresContext* aPresContext, return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); } - nsIWidget* fromRoot; - nsIntPoint fromOffset = GetWidgetOffset(aWidget, fromRoot); - nsIWidget* toRoot; - nsIntPoint toOffset = GetWidgetOffset(viewWidget, toRoot); - - nsIntPoint widgetPoint; - if (fromRoot == toRoot) { - widgetPoint = aPt + fromOffset - toOffset; - } else { - nsIntPoint screenPoint = aWidget->WidgetToScreenOffset(); - widgetPoint = aPt + screenPoint - viewWidget->WidgetToScreenOffset(); - } - + nsIntPoint widgetPoint = aPt + WidgetToWidgetOffset(aWidget, viewWidget); nsPoint widgetAppUnits(aPresContext->DevPixelsToAppUnits(widgetPoint.x), aPresContext->DevPixelsToAppUnits(widgetPoint.y)); return widgetAppUnits - viewOffset; } +nsIntPoint +nsLayoutUtils::TranslateViewToWidget(nsPresContext* aPresContext, + nsView* aView, nsPoint aPt, + nsIWidget* aWidget) +{ + nsPoint viewOffset; + nsIWidget* viewWidget = aView->GetNearestWidget(&viewOffset); + if (!viewWidget) { + return nsIntPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); + } + + nsIntPoint relativeToViewWidget(aPresContext->AppUnitsToDevPixels(aPt.x + viewOffset.x), + aPresContext->AppUnitsToDevPixels(aPt.y + viewOffset.y)); + return relativeToViewWidget + WidgetToWidgetOffset(viewWidget, aWidget); +} + // Combine aNewBreakType with aOrigBreakType, but limit the break types // to NS_STYLE_CLEAR_LEFT, RIGHT, BOTH. uint8_t diff --git a/layout/base/nsLayoutUtils.h b/layout/base/nsLayoutUtils.h index a2bf9ad2a127..c78682dd5962 100644 --- a/layout/base/nsLayoutUtils.h +++ b/layout/base/nsLayoutUtils.h @@ -673,6 +673,18 @@ public: nsIWidget* aWidget, nsIntPoint aPt, nsView* aView); + /** + * Translate from view coordinates to the widget's coordinates. + * @param aPresContext the PresContext for the view + * @param aView the view + * @param aPt the point relative to the view + * @param aWidget the widget to which returned coordinates are relative + * @return the point in the view's coordinates + */ + static nsIntPoint TranslateViewToWidget(nsPresContext* aPresContext, + nsView* aView, nsPoint aPt, + nsIWidget* aWidget); + enum FrameForPointFlags { /** * When set, paint suppression is ignored, so we'll return non-root page diff --git a/mobile/android/app/mobile.js b/mobile/android/app/mobile.js index de8ea8e85eae..f03e89d1454d 100644 --- a/mobile/android/app/mobile.js +++ b/mobile/android/app/mobile.js @@ -417,6 +417,7 @@ pref("ui.mouse.radius.topmm", 5); pref("ui.mouse.radius.rightmm", 3); pref("ui.mouse.radius.bottommm", 2); pref("ui.mouse.radius.visitedWeight", 120); +pref("ui.mouse.radius.reposition", true); // The percentage of the screen that needs to be scrolled before margins are exposed. pref("browser.ui.show-margins-threshold", 10);