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).
This commit is contained in:
Kartikaya Gupta 2014-11-04 09:52:16 -05:00
Родитель da1d284214
Коммит 7644286079
5 изменённых файлов: 80 добавлений и 16 удалений

Просмотреть файл

@ -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;
}
}

Просмотреть файл

@ -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);

Просмотреть файл

@ -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

Просмотреть файл

@ -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

Просмотреть файл

@ -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);