Bug 837242. Part 3. Keep track of what area is exposed to events so that covered frames don't get events. r=roc

This commit is contained in:
Timothy Nikkel 2013-07-30 11:22:46 -05:00
parent 14c31d8c29
commit c6f1c91b0a
3 changed files with 68 additions and 10 deletions

View File

@ -204,16 +204,62 @@ ComputeDistanceFromRect(const nsPoint& aPoint, const nsRect& aRect)
return float(NS_hypot(dx, dy));
}
static float
ComputeDistanceFromRegion(const nsPoint& aPoint, const nsRegion& aRegion)
{
nsRegionRectIterator iter(aRegion);
const nsRect* r;
float minDist = -1;
while ((r = iter.Next()) != nullptr) {
float dist = ComputeDistanceFromRect(aPoint, *r);
if (dist < minDist || minDist < 0) {
minDist = dist;
}
}
return minDist;
}
// Subtract aRegion from aExposedRegion as long as that doesn't make the
// exposed region get too complex or removes a big chunk of the exposed region.
static void
SubtractFromExposedRegion(nsRegion* aExposedRegion, const nsRegion& aRegion)
{
if (aRegion.IsEmpty())
return;
nsRegion tmp;
tmp.Sub(*aExposedRegion, aRegion);
// Don't let *aExposedRegion get too complex, but don't let it fluff out to
// its bounds either. Do let aExposedRegion get more complex if by doing so
// we reduce its area by at least half.
if (tmp.GetNumRects() <= 15 || tmp.Area() <= aExposedRegion->Area()/2) {
*aExposedRegion = tmp;
}
}
static nsIFrame*
GetClosest(nsIFrame* aRoot, const nsPoint& aPointRelativeToRootFrame,
const EventRadiusPrefs* aPrefs, nsIFrame* aRestrictToDescendants,
nsTArray<nsIFrame*>& aCandidates)
const nsRect& aTargetRect, const EventRadiusPrefs* aPrefs,
nsIFrame* aRestrictToDescendants, nsTArray<nsIFrame*>& aCandidates)
{
nsIFrame* bestTarget = nullptr;
// Lower is better; distance is in appunits
float bestDistance = 1e6f;
nsRegion exposedRegion(aTargetRect);
for (uint32_t i = 0; i < aCandidates.Length(); ++i) {
nsIFrame* f = aCandidates[i];
bool preservesAxisAlignedRectangles = false;
nsRect borderBox = nsLayoutUtils::TransformFrameRectToAncestor(f,
nsRect(nsPoint(0, 0), f->GetSize()), aRoot, &preservesAxisAlignedRectangles);
nsRegion region;
region.And(exposedRegion, borderBox);
if (preservesAxisAlignedRectangles) {
// Subtract from the exposed region if we have a transform that won't make
// the bounds include a bunch of area that we don't actually cover.
SubtractFromExposedRegion(&exposedRegion, region);
}
if (!IsElementClickable(f)) {
continue;
}
@ -226,10 +272,8 @@ GetClosest(nsIFrame* aRoot, const nsPoint& aPointRelativeToRootFrame,
continue;
}
nsRect borderBox = nsLayoutUtils::TransformFrameRectToAncestor(f,
nsRect(nsPoint(0, 0), f->GetSize()), aRoot);
// distance is in appunits
float distance = ComputeDistanceFromRect(aPointRelativeToRootFrame, borderBox);
float distance = ComputeDistanceFromRegion(aPointRelativeToRootFrame, region);
nsIContent* content = f->GetContent();
if (content && content->IsElement() &&
content->AsElement()->State().HasState(nsEventStates(NS_EVENT_STATE_VISITED))) {
@ -283,7 +327,7 @@ FindFrameTargetedByInputEvent(const nsGUIEvent *aEvent,
nsIFrame* restrictToDescendants = target ?
target->PresContext()->PresShell()->GetRootFrame() : aRootFrame;
nsIFrame* closestClickable =
GetClosest(aRootFrame, aPointRelativeToRootFrame, prefs,
GetClosest(aRootFrame, aPointRelativeToRootFrame, targetRect, prefs,
restrictToDescendants, candidates);
return closestClickable ? closestClickable : target;
}

View File

@ -1596,9 +1596,15 @@ TransformGfxRectFromAncestor(nsIFrame *aFrame,
static gfxRect
TransformGfxRectToAncestor(nsIFrame *aFrame,
const gfxRect &aRect,
const nsIFrame *aAncestor)
const nsIFrame *aAncestor,
bool* aPreservesAxisAlignedRectangles = nullptr)
{
gfx3DMatrix ctm = nsLayoutUtils::GetTransformToAncestor(aFrame, aAncestor);
if (aPreservesAxisAlignedRectangles) {
gfxMatrix matrix2d;
*aPreservesAxisAlignedRectangles =
ctm.Is2D(&matrix2d) && matrix2d.PreservesAxisAlignedRectangles();
}
return ctm.TransformBounds(aRect);
}
@ -1666,7 +1672,8 @@ nsLayoutUtils::TransformAncestorRectToFrame(nsIFrame* aFrame,
nsRect
nsLayoutUtils::TransformFrameRectToAncestor(nsIFrame* aFrame,
const nsRect& aRect,
const nsIFrame* aAncestor)
const nsIFrame* aAncestor,
bool* aPreservesAxisAlignedRectangles /* = nullptr */)
{
nsSVGTextFrame2* text = GetContainingSVGTextFrame(aFrame);
@ -1676,12 +1683,16 @@ nsLayoutUtils::TransformFrameRectToAncestor(nsIFrame* aFrame,
if (text) {
result = text->TransformFrameRectFromTextChild(aRect, aFrame);
result = TransformGfxRectToAncestor(text, result, aAncestor);
// TransformFrameRectFromTextChild could involve any kind of transform, we
// could drill down into it to get an answer out of it but we don't yet.
if (aPreservesAxisAlignedRectangles)
*aPreservesAxisAlignedRectangles = false;
} else {
result = gfxRect(NSAppUnitsToFloatPixels(aRect.x, srcAppUnitsPerDevPixel),
NSAppUnitsToFloatPixels(aRect.y, srcAppUnitsPerDevPixel),
NSAppUnitsToFloatPixels(aRect.width, srcAppUnitsPerDevPixel),
NSAppUnitsToFloatPixels(aRect.height, srcAppUnitsPerDevPixel));
result = TransformGfxRectToAncestor(aFrame, result, aAncestor);
result = TransformGfxRectToAncestor(aFrame, result, aAncestor, aPreservesAxisAlignedRectangles);
}
float destAppUnitsPerDevPixel = aAncestor->PresContext()->AppUnitsPerDevPixel();

View File

@ -572,10 +572,13 @@ public:
/**
* Transform aRect relative to aFrame up to the coordinate system of
* aAncestor. Computes the bounding-box of the true quadrilateral.
* Pass non-null aPreservesAxisAlignedRectangles and it will be set to true if
* we only need to use a 2d transform that PreservesAxisAlignedRectangles().
*/
static nsRect TransformFrameRectToAncestor(nsIFrame* aFrame,
const nsRect& aRect,
const nsIFrame* aAncestor);
const nsIFrame* aAncestor,
bool* aPreservesAxisAlignedRectangles = nullptr);
/**