Diff
Modified: trunk/LayoutTests/ChangeLog (123623 => 123624)
--- trunk/LayoutTests/ChangeLog 2012-07-25 15:51:26 UTC (rev 123623)
+++ trunk/LayoutTests/ChangeLog 2012-07-25 16:09:53 UTC (rev 123624)
@@ -1,3 +1,18 @@
+2012-07-25 Allan Sandfeld Jensen <[email protected]>
+
+ NodesFromRect and area-based hit-testing can not handle CSS transforms.
+ https://bugs.webkit.org/show_bug.cgi?id=85792
+
+ Reviewed by Eric Seidel.
+
+ Adds nodesFromRect tests that tests the API under the CSS transforms
+ scale and rotate.
+
+ * fast/dom/nodesFromRect/nodesFromRect-rotate-expected.txt: Added.
+ * fast/dom/nodesFromRect/nodesFromRect-rotate.html: Added.
+ * fast/dom/nodesFromRect/nodesFromRect-scale-expected.txt: Added.
+ * fast/dom/nodesFromRect/nodesFromRect-scale.html: Added.
+
2012-07-25 Caio Marcelo de Oliveira Filho <[email protected]>
[Qt] css2.1/t1* tests needs rebaseline after new testfonts
Added: trunk/LayoutTests/fast/dom/nodesFromRect/nodesFromRect-rotate-expected.txt (0 => 123624)
--- trunk/LayoutTests/fast/dom/nodesFromRect/nodesFromRect-rotate-expected.txt (rev 0)
+++ trunk/LayoutTests/fast/dom/nodesFromRect/nodesFromRect-rotate-expected.txt 2012-07-25 16:09:53 UTC (rev 123624)
@@ -0,0 +1,49 @@
+Document::nodesFromRect : CSS rotate transforms - bug 85792
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+Check unrotated area-testing for sanity
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+Check rotated 180deg
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+Check rotated 90deg
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+Check rotated 45deg
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+
Added: trunk/LayoutTests/fast/dom/nodesFromRect/nodesFromRect-rotate.html (0 => 123624)
--- trunk/LayoutTests/fast/dom/nodesFromRect/nodesFromRect-rotate.html (rev 0)
+++ trunk/LayoutTests/fast/dom/nodesFromRect/nodesFromRect-rotate.html 2012-07-25 16:09:53 UTC (rev 123624)
@@ -0,0 +1,112 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Document::nodesFromRect : CSS rotate transforms - bug 85792</title>
+<style type="text/css">
+ #sandbox {
+ position: absolute;
+ left: 0px;
+ top: 0px;
+ width: 700px;
+ height: 700px;
+ }
+ #layer {
+ position: absolute;
+ left: 200px;
+ top: 200px;
+ width: 300px;
+ height: 300px;
+ }
+ .rotate45 { -webkit-transform: rotate(45deg); }
+ .rotate90 { -webkit-transform: rotate(90deg); }
+ .rotate180 { -webkit-transform: rotate(180deg); }
+ #layer > #fleft { float: left; width: 50px; height: 300px; }
+ #layer > #fright { float: right; width: 50px; height: 300px; }
+ #layer > .hbox { height: 100px; margin-right: 50px; margin-left: 50px }
+</style>
+<script src=""
+<script src=""
+</head>
+
+<body>
+ <div id=sandbox>
+ <div id=layer>
+ <div id=fleft></div>
+ <div id=fright></div>
+ <div id=box1 class=hbox></div>
+ <div id=box2 class=hbox></div>
+ <div id=box3 class=hbox></div>
+ </div>
+ </div>
+
+ <script>
+ function runTest()
+ {
+ description(document.title);
+ var e = {};
+
+ // Set up shortcut access to elements
+ ['sandbox', 'layer', 'fleft', 'fright', 'box1', 'box2', 'box3'].forEach(function(a) {
+ e[a] = document.getElementById(a);
+ });
+
+ window.scrollTo(0, 0);
+ debug('Check unrotated area-testing for sanity');
+ check(150, 150, 30, 30, 30, 30, [e.sandbox]);
+ check(220, 220, 10, 10, 10, 10, [e.fleft]);
+ check(470, 220, 10, 10, 10, 10, [e.fright]);
+ check(270, 220, 10, 10, 10, 10, [e.box1]);
+ check(270, 320, 10, 10, 10, 10, [e.box2]);
+ check(240, 290, 30, 30, 30, 30, [e.fleft, e.box2, e.box1, e.layer]);
+ check(180, 220, 10, 30, 10, 0, [e.fleft, e.layer, e.sandbox]);
+ check(270, 280, 0, 10, 30, 10, [e.box2, e.box1, e.layer]);
+ check(180, 220, 10, 0, 10, 30, [e.sandbox]);
+
+
+ debug('Check rotated 180deg');
+ e['layer'].setAttribute('class', 'rotate180');
+ check(150, 150, 30, 30, 30, 30, [e.sandbox]);
+ check(220, 220, 10, 10, 10, 10, [e.fright]);
+ check(470, 220, 10, 10, 10, 10, [e.fleft]);
+ check(270, 220, 10, 10, 10, 10, [e.box3]);
+ check(270, 320, 10, 10, 10, 10, [e.box2]);
+ check(240, 290, 30, 30, 30, 30, [e.fright, e.box3, e.box2, e.layer]);
+ check(180, 220, 10, 30, 10, 0, [e.fright, e.layer, e.sandbox]);
+ check(270, 280, 0, 10, 30, 10, [e.box3, e.box2, e.layer]);
+ check(180, 220, 10, 0, 10, 30, [e.sandbox]);
+
+ debug('Check rotated 90deg');
+ e['layer'].setAttribute('class', 'rotate90');
+ check(150, 150, 30, 30, 30, 30, [e.sandbox]);
+ check(220, 280, 10, 10, 10, 10, [e.box3]);
+ check(470, 280, 10, 10, 10, 10, [e.box1]);
+ check(270, 220, 10, 10, 10, 10, [e.fleft]);
+ check(270, 470, 10, 10, 10, 10, [e.fright]);
+ check(290, 240, 30, 30, 30, 30, [e.fleft, e.box3, e.box2, e.layer]);
+ check(180, 280, 10, 30, 10, 0, [e.box3, e.layer, e.sandbox]);
+ check(330, 180, 0, 10, 30, 10, [e.fleft, e.layer, e.sandbox]);
+ check(280, 260, 40, 10, 10, 10, [e.fleft, e.box3, e.layer]);
+
+ debug('Check rotated 45deg');
+ e['layer'].setAttribute('class', 'rotate45');
+ check(150, 150, 30, 30, 30, 30, [e.sandbox]);
+ check(260, 260, 10, 10, 10, 10, [e.fleft]);
+ check(440, 440, 10, 10, 10, 10, [e.fright]);
+ check(440, 260, 10, 10, 10, 10, [e.box1]);
+ check(260, 440, 10, 10, 10, 10, [e.box3]);
+ check(350, 140, 20, 20, 20, 20, [e.fleft, e.layer, e.sandbox]);
+ check(350, 140, 20, 20, 70, 20, [e.fleft, e.box1, e.layer, e.sandbox]);
+ check(350, 350, 40, 40, 40, 40, [e.box3, e.box2, e.box1, e.layer]);
+ check(260, 260, 1, 27, 1, 27, [e.fleft]);
+
+ }
+
+ window._onload_ = runTest;
+ </script>
+
+ <p id='description'></p>
+ <span id="console"></span>
+ <script src=""
+</body>
+</html>
+
Added: trunk/LayoutTests/fast/dom/nodesFromRect/nodesFromRect-scale-expected.txt (0 => 123624)
--- trunk/LayoutTests/fast/dom/nodesFromRect/nodesFromRect-scale-expected.txt (rev 0)
+++ trunk/LayoutTests/fast/dom/nodesFromRect/nodesFromRect-scale-expected.txt 2012-07-25 16:09:53 UTC (rev 123624)
@@ -0,0 +1,38 @@
+Document::nodesFromRect : CSS scale transforms - bug 85792
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+Check unscaled area-testing for sanity
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+Check scaling 2X
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+Check scaling 0.5X
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+
Added: trunk/LayoutTests/fast/dom/nodesFromRect/nodesFromRect-scale.html (0 => 123624)
--- trunk/LayoutTests/fast/dom/nodesFromRect/nodesFromRect-scale.html (rev 0)
+++ trunk/LayoutTests/fast/dom/nodesFromRect/nodesFromRect-scale.html 2012-07-25 16:09:53 UTC (rev 123624)
@@ -0,0 +1,96 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Document::nodesFromRect : CSS scale transforms - bug 85792</title>
+<style type="text/css">
+ #sandbox {
+ position: absolute;
+ left: 0px;
+ top: 0px;
+ width: 600px;
+ height: 600px;
+ }
+ #layer {
+ position: absolute;
+ left: 200px;
+ top: 200px;
+ width: 200px;
+ height: 200px;
+ }
+ .scaleup { -webkit-transform: scale(2); }
+ .scaledown { -webkit-transform: scale(0.5); }
+ #layer > #fleft { float: left; width: 50px; height: 200px; }
+ #layer > #fright { float: right; width: 50px; height: 200px; }
+ #layer > .hbox { height: 100px; margin-right: 50px; margin-left: 50px }
+</style>
+<script src=""
+<script src=""
+</head>
+
+<body>
+ <div id=sandbox>
+ <div id=layer>
+ <div id=fleft></div>
+ <div id=fright></div>
+ <div id=box1 class=hbox></div>
+ <div id=box2 class=hbox></div>
+ </div>
+ </div>
+
+ <script>
+ function runTest()
+ {
+ description(document.title);
+ var e = {};
+
+ // Set up shortcut access to elements
+ ['sandbox', 'layer', 'fleft', 'fright', 'box1', 'box2', 'box3'].forEach(function(a) {
+ e[a] = document.getElementById(a);
+ });
+
+ window.scrollTo(0, 0);
+ debug('Check unscaled area-testing for sanity');
+ check(150, 150, 30, 30, 30, 30, [e.sandbox]);
+ check(220, 220, 10, 10, 10, 10, [e.fleft]);
+ check(370, 220, 10, 10, 10, 10, [e.fright]);
+ check(270, 220, 10, 10, 10, 10, [e.box1]);
+ check(270, 320, 10, 10, 10, 10, [e.box2]);
+ check(240, 290, 30, 30, 30, 30, [e.fleft, e.box2, e.box1, e.layer]);
+ check(180, 220, 10, 30, 10, 0, [e.fleft, e.layer, e.sandbox]);
+ check(270, 280, 0, 10, 30, 10, [e.box2, e.box1, e.layer]);
+ check(180, 220, 10, 0, 10, 30, [e.sandbox]);
+
+ debug('Check scaling 2X');
+ e['layer'].setAttribute('class', 'scaleup');
+ check(150, 150, 30, 30, 30, 30, [e.fleft]);
+ check(450, 150, 30, 30, 30, 30, [e.fright]);
+ check(350, 220, 10, 10, 10, 10, [e.box1]);
+ check(350, 420, 10, 10, 10, 10, [e.box2]);
+ check(180, 280, 60, 60, 60, 60, [e.fleft, e.box2, e.box1, e.layer]);
+ check(60, 140, 20, 60, 20, 0, [e.fleft, e.layer, e.sandbox]);
+ check(240, 260, 0, 20, 60, 20, [e.box2, e.box1, e.layer]);
+ check(60, 140, 20, 0, 20, 60, [e.sandbox]);
+
+ debug('Check scaling 0.5X');
+ e['layer'].setAttribute('class', 'scaledown');
+ check(225, 225, 15, 15, 15, 15, [e.sandbox]);
+ check(260, 260, 5, 5, 5, 5, [e.fleft]);
+ check(335, 260, 5, 5, 5, 5, [e.fright]);
+ check(285, 260, 5, 5, 5, 5, [e.box1]);
+ check(285, 310, 5, 5, 5, 5, [e.box2]);
+ check(270, 295, 15, 15, 15, 15, [e.fleft, e.box2, e.box1, e.layer]);
+ check(240, 260, 5, 15, 5, 0, [e.fleft, e.layer, e.sandbox]);
+ check(285, 290, 0, 5, 15, 5, [e.box2, e.box1, e.layer]);
+ check(240, 260, 5, 0, 5, 15, [e.sandbox]);
+
+ }
+
+ window._onload_ = runTest;
+ </script>
+
+ <p id='description'></p>
+ <span id="console"></span>
+ <script src=""
+</body>
+</html>
+
Modified: trunk/Source/WebCore/ChangeLog (123623 => 123624)
--- trunk/Source/WebCore/ChangeLog 2012-07-25 15:51:26 UTC (rev 123623)
+++ trunk/Source/WebCore/ChangeLog 2012-07-25 16:09:53 UTC (rev 123624)
@@ -1,3 +1,56 @@
+2012-07-25 Allan Sandfeld Jensen <[email protected]>
+
+ NodesFromRect and area-based hit-testing can not handle CSS transforms.
+ https://bugs.webkit.org/show_bug.cgi?id=85792
+
+ Reviewed by Eric Seidel.
+
+ To support the combination of CSS transforms and rect based hit testing,
+ we need to test against the transformed rect, instead of the original rect.
+
+ This patch makes HitTestPoint store the exact transformed FloatPoint and
+ FloatQuad, and modifies the intersection methods so that they will use a
+ new FloatQuad based intersection when transforms requires it.
+
+ Tests: fast/dom/nodesFromRect/nodesFromRect-rotate.html
+ fast/dom/nodesFromRect/nodesFromRect-scale.html
+
+ * platform/graphics/FloatQuad.cpp:
+ (WebCore::determinant):
+ (WebCore::rightMostCornerToVector):
+ (WebCore::FloatQuad::intersectsRect):
+ (WebCore::FloatQuad::isCounterclockwise):
+ * platform/graphics/FloatQuad.h:
+ (FloatQuad):
+ * rendering/HitTestResult.cpp:
+ (WebCore::HitTestPoint::HitTestPoint):
+ (WebCore::HitTestPoint::operator=):
+ (WebCore::HitTestPoint::move):
+ (WebCore::HitTestPoint::intersectsRect):
+ (WebCore::HitTestPoint::intersects):
+ * rendering/HitTestResult.h:
+ (HitTestPoint):
+ (WebCore::HitTestPoint::isRectilinear):
+ (WebCore::HitTestPoint::transformedPoint):
+ (WebCore::HitTestPoint::transformedRect):
+ * rendering/HitTestingTransformState.cpp:
+ (WebCore::HitTestingTransformState::flattenWithTransform):
+ (WebCore::HitTestingTransformState::mappedArea):
+ (WebCore::HitTestingTransformState::boundsOfMappedArea):
+ * rendering/HitTestingTransformState.h:
+ (WebCore::HitTestingTransformState::create):
+ (WebCore::HitTestingTransformState::HitTestingTransformState):
+ * rendering/RenderFlowThread.cpp:
+ (WebCore::RenderFlowThread::hitTestRegion):
+ * rendering/RenderLayer.cpp:
+ (WebCore::RenderLayer::hitTest):
+ (WebCore::RenderLayer::createLocalTransformState):
+ (WebCore::RenderLayer::hitTestLayer):
+ (WebCore::RenderLayer::hitTestChildLayerColumns):
+ * rendering/RenderLayer.h:
+ * rendering/svg/RenderSVGText.cpp:
+ (WebCore::RenderSVGText::nodeAtFloatPoint):
+
2012-07-25 Kwang Yul Seo <[email protected]>
Add HTMLStackItem.h to project files
Modified: trunk/Source/WebCore/platform/graphics/FloatQuad.cpp (123623 => 123624)
--- trunk/Source/WebCore/platform/graphics/FloatQuad.cpp 2012-07-25 15:51:26 UTC (rev 123623)
+++ trunk/Source/WebCore/platform/graphics/FloatQuad.cpp 2012-07-25 16:09:53 UTC (rev 123624)
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies)
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -51,6 +52,11 @@
return a.width() * b.width() + a.height() * b.height();
}
+inline float determinant(const FloatSize& a, const FloatSize& b)
+{
+ return a.width() * b.height() - a.height() * b.width();
+}
+
inline bool isPointInTriangle(const FloatPoint& p, const FloatPoint& t1, const FloatPoint& t2, const FloatPoint& t3)
{
// Compute vectors
@@ -107,11 +113,74 @@
return containsPoint(other.p1()) && containsPoint(other.p2()) && containsPoint(other.p3()) && containsPoint(other.p4());
}
+static inline FloatPoint rightMostCornerToVector(const FloatRect& rect, const FloatSize& vector)
+{
+ // Return the corner of the rectangle that if it is to the left of the vector
+ // would mean all of the rectangle is to the left of the vector.
+ // The vector here represents the side between two points in a clockwise convex polygon.
+ //
+ // Q XXX
+ // QQQ XXX If the lower left corner of X is left of the vector that goes from the top corner of Q to
+ // QQQ the right corner of Q, then all of X is left of the vector, and intersection impossible.
+ // Q
+ //
+ FloatPoint point;
+ if (vector.width() >= 0)
+ point.setY(rect.maxY());
+ else
+ point.setY(rect.y());
+ if (vector.height() >= 0)
+ point.setX(rect.x());
+ else
+ point.setX(rect.maxX());
+ return point;
+}
+
+bool FloatQuad::intersectsRect(const FloatRect& rect) const
+{
+ // For each side of the quad clockwise we check if the rectangle is to the left of it
+ // since only content on the right can onlap with the quad.
+ // This only works if the quad is convex.
+ FloatSize v1, v2, v3, v4;
+
+ // Ensure we use clockwise vectors.
+ if (!isCounterclockwise()) {
+ v1 = m_p2 - m_p1;
+ v2 = m_p3 - m_p2;
+ v3 = m_p4 - m_p3;
+ v4 = m_p1 - m_p4;
+ } else {
+ v1 = m_p4 - m_p1;
+ v2 = m_p1 - m_p2;
+ v3 = m_p2 - m_p3;
+ v4 = m_p3 - m_p4;
+ }
+
+ FloatPoint p = rightMostCornerToVector(rect, v1);
+ if (determinant(v1, p - m_p1) < 0)
+ return false;
+
+ p = rightMostCornerToVector(rect, v2);
+ if (determinant(v2, p - m_p2) < 0)
+ return false;
+
+ p = rightMostCornerToVector(rect, v3);
+ if (determinant(v3, p - m_p3) < 0)
+ return false;
+
+ p = rightMostCornerToVector(rect, v4);
+ if (determinant(v4, p - m_p4) < 0)
+ return false;
+
+ // If not all of the rectangle is outside one of the quad's four sides, then that means at least
+ // a part of the rectangle is overlapping the quad.
+ return true;
+}
+
bool FloatQuad::isCounterclockwise() const
{
- FloatPoint v1 = FloatPoint(m_p2.x() - m_p1.x(), m_p2.y() - m_p1.y());
- FloatPoint v2 = FloatPoint(m_p3.x() - m_p2.x(), m_p3.y() - m_p2.y());
- return (v1.x() * v2.y() - v1.y() * v2.x()) < 0;
+ // Return if the two first vectors are turning clockwise. If the quad is convex then all following vectors will turn the same way.
+ return determinant(m_p2 - m_p1, m_p3 - m_p2) < 0;
}
} // namespace WebCore
Modified: trunk/Source/WebCore/platform/graphics/FloatQuad.h (123623 => 123624)
--- trunk/Source/WebCore/platform/graphics/FloatQuad.h 2012-07-25 15:51:26 UTC (rev 123623)
+++ trunk/Source/WebCore/platform/graphics/FloatQuad.h 2012-07-25 16:09:53 UTC (rev 123624)
@@ -88,6 +88,10 @@
// from transformed rects.
bool containsQuad(const FloatQuad&) const;
+ // Tests whether any part of the rectangle intersects with this quad.
+ // This only works for convex quads.
+ bool intersectsRect(const FloatRect&) const;
+
// The center of the quad. If the quad is the result of a affine-transformed rectangle this is the same as the original center transformed.
FloatPoint center() const
{
Modified: trunk/Source/WebCore/rendering/HitTestResult.cpp (123623 => 123624)
--- trunk/Source/WebCore/rendering/HitTestResult.cpp 2012-07-25 15:51:26 UTC (rev 123623)
+++ trunk/Source/WebCore/rendering/HitTestResult.cpp 2012-07-25 16:09:53 UTC (rev 123624)
@@ -50,27 +50,57 @@
HitTestPoint::HitTestPoint()
: m_isRectBased(false)
+ , m_isRectilinear(true)
{
}
HitTestPoint::HitTestPoint(const LayoutPoint& point)
: m_point(point)
, m_boundingBox(rectForPoint(point, 0, 0, 0, 0))
+ , m_transformedPoint(point)
+ , m_transformedRect(m_boundingBox)
, m_isRectBased(false)
+ , m_isRectilinear(true)
{
}
+HitTestPoint::HitTestPoint(const FloatPoint& point)
+ : m_point(roundedLayoutPoint(point))
+ , m_boundingBox(rectForPoint(m_point, 0, 0, 0, 0))
+ , m_transformedPoint(point)
+ , m_transformedRect(m_boundingBox)
+ , m_isRectBased(false)
+ , m_isRectilinear(true)
+{
+}
+
+HitTestPoint::HitTestPoint(const FloatPoint& point, const FloatQuad& quad)
+ : m_transformedPoint(point)
+ , m_transformedRect(quad)
+ , m_isRectBased(true)
+{
+ m_point = roundedLayoutPoint(point);
+ m_boundingBox = enclosingIntRect(quad.boundingBox());
+ m_isRectilinear = quad.isRectilinear();
+}
+
HitTestPoint::HitTestPoint(const LayoutPoint& centerPoint, unsigned topPadding, unsigned rightPadding, unsigned bottomPadding, unsigned leftPadding)
: m_point(centerPoint)
, m_boundingBox(rectForPoint(centerPoint, topPadding, rightPadding, bottomPadding, leftPadding))
+ , m_transformedPoint(centerPoint)
, m_isRectBased(topPadding || rightPadding || bottomPadding || leftPadding)
+ , m_isRectilinear(true)
{
+ m_transformedRect = FloatQuad(m_boundingBox);
}
HitTestPoint::HitTestPoint(const HitTestPoint& other)
: m_point(other.m_point)
, m_boundingBox(other.m_boundingBox)
+ , m_transformedPoint(other.m_transformedPoint)
+ , m_transformedRect(other.m_transformedRect)
, m_isRectBased(other.m_isRectBased)
+ , m_isRectilinear(other.m_isRectilinear)
{
}
@@ -82,40 +112,52 @@
{
m_point = other.m_point;
m_boundingBox = other.m_boundingBox;
+ m_transformedPoint = other.m_transformedPoint;
+ m_transformedRect = other.m_transformedRect;
m_isRectBased = other.m_isRectBased;
+ m_isRectilinear = other.m_isRectilinear;
return *this;
}
-void HitTestPoint::setPoint(const LayoutPoint& point)
+void HitTestPoint::move(const LayoutSize& offset)
{
- m_boundingBox.move(roundedIntPoint(point) - roundedIntPoint(m_point));
- m_point = point;
+ m_point.move(offset);
+ m_transformedPoint.move(offset);
+ m_transformedRect.move(offset);
+ m_boundingBox = enclosingIntRect(m_transformedRect.boundingBox());
}
template<typename RectType>
-bool hitTestPointIntersects(const HitTestPoint& hitTestPoint, const RectType& rect)
+bool HitTestPoint::intersectsRect(const RectType& rect) const
{
// FIXME: When the hit test is not rect based we should use rect.contains(m_point).
// That does change some corner case tests though.
- // First check if rect even intersects our bounding rect.
- if (!rect.intersects(hitTestPoint.boundingBox()))
+ // First check if rect even intersects our bounding box.
+ if (!rect.intersects(m_boundingBox))
return false;
- // FIXME: Implement quad based intersection test to handle transformed hit test rectangles.
- return true;
+ // If the transformed rect is rectilinear the bounding box intersection was accurate.
+ if (m_isRectilinear)
+ return true;
+ // If rect fully contains our bounding box, we are also sure of an intersection.
+ if (rect.contains(m_boundingBox))
+ return true;
+
+ // Otherwise we need to do a slower quad based intersection test.
+ return m_transformedRect.intersectsRect(rect);
}
bool HitTestPoint::intersects(const LayoutRect& rect) const
{
- return hitTestPointIntersects(*this, rect);
+ return intersectsRect(rect);
}
bool HitTestPoint::intersects(const FloatRect& rect) const
{
- return hitTestPointIntersects(*this, rect);
+ return intersectsRect(rect);
}
IntRect HitTestPoint::rectForPoint(const LayoutPoint& point, unsigned topPadding, unsigned rightPadding, unsigned bottomPadding, unsigned leftPadding)
Modified: trunk/Source/WebCore/rendering/HitTestResult.h (123623 => 123624)
--- trunk/Source/WebCore/rendering/HitTestResult.h 2012-07-25 15:51:26 UTC (rev 123623)
+++ trunk/Source/WebCore/rendering/HitTestResult.h 2012-07-25 16:09:53 UTC (rev 123624)
@@ -22,6 +22,7 @@
#ifndef HitTestResult_h
#define HitTestResult_h
+#include "FloatQuad.h"
#include "FloatRect.h"
#include "HitTestRequest.h"
#include "LayoutTypes.h"
@@ -51,6 +52,8 @@
HitTestPoint();
HitTestPoint(const LayoutPoint&);
+ HitTestPoint(const FloatPoint&);
+ HitTestPoint(const FloatPoint&, const FloatQuad&);
// Pass non-zero padding values to perform a rect-based hit test.
HitTestPoint(const LayoutPoint& centerPoint, unsigned topPadding, unsigned rightPadding, unsigned bottomPadding, unsigned leftPadding);
HitTestPoint(const HitTestPoint&);
@@ -60,10 +63,11 @@
LayoutPoint point() const { return m_point; }
IntPoint roundedPoint() const { return roundedIntPoint(m_point); }
- void setPoint(const LayoutPoint&);
+ void move(const LayoutSize& offset);
// Rect-based hit test related methods.
bool isRectBasedTest() const { return m_isRectBased; }
+ bool isRectilinear() const { return m_isRectilinear; }
IntRect boundingBox() const { return m_boundingBox; }
static IntRect rectForPoint(const LayoutPoint&, unsigned topPadding, unsigned rightPadding, unsigned bottomPadding, unsigned leftPadding);
@@ -75,11 +79,21 @@
bool intersects(const LayoutRect&) const;
bool intersects(const FloatRect&) const;
+ const FloatPoint& transformedPoint() const { return m_transformedPoint; }
+ const FloatQuad& transformedRect() const { return m_transformedRect; }
+
private:
+ template<typename RectType>
+ bool intersectsRect(const RectType&) const;
+
+ // This is cached forms of the more accurate point and area below.
LayoutPoint m_point;
+ IntRect m_boundingBox;
- IntRect m_boundingBox;
+ FloatPoint m_transformedPoint;
+ FloatQuad m_transformedRect;
bool m_isRectBased;
+ bool m_isRectilinear;
};
class HitTestResult : public HitTestPoint {
Modified: trunk/Source/WebCore/rendering/HitTestingTransformState.cpp (123623 => 123624)
--- trunk/Source/WebCore/rendering/HitTestingTransformState.cpp 2012-07-25 15:51:26 UTC (rev 123623)
+++ trunk/Source/WebCore/rendering/HitTestingTransformState.cpp 2012-07-25 16:09:53 UTC (rev 123624)
@@ -58,6 +58,7 @@
TransformationMatrix inverseTransform = t.inverse();
m_lastPlanarPoint = inverseTransform.projectPoint(m_lastPlanarPoint);
m_lastPlanarQuad = inverseTransform.projectQuad(m_lastPlanarQuad);
+ m_lastPlanarArea = inverseTransform.projectQuad(m_lastPlanarArea);
m_accumulatedTransform.makeIdentity();
m_accumulatingTransform = false;
@@ -73,9 +74,14 @@
return m_accumulatedTransform.inverse().projectQuad(m_lastPlanarQuad);
}
-LayoutRect HitTestingTransformState::boundsOfMappedQuad() const
+FloatQuad HitTestingTransformState::mappedArea() const
{
- return m_accumulatedTransform.inverse().clampedBoundsOfProjectedQuad(m_lastPlanarQuad);
+ return m_accumulatedTransform.inverse().projectQuad(m_lastPlanarArea);
}
+LayoutRect HitTestingTransformState::boundsOfMappedArea() const
+{
+ return m_accumulatedTransform.inverse().clampedBoundsOfProjectedQuad(m_lastPlanarArea);
+}
+
} // namespace WebCore
Modified: trunk/Source/WebCore/rendering/HitTestingTransformState.h (123623 => 123624)
--- trunk/Source/WebCore/rendering/HitTestingTransformState.h 2012-07-25 15:51:26 UTC (rev 123623)
+++ trunk/Source/WebCore/rendering/HitTestingTransformState.h 2012-07-25 16:09:53 UTC (rev 123624)
@@ -42,9 +42,9 @@
// differently than move()) so care has to be taken when this is done.
class HitTestingTransformState : public RefCounted<HitTestingTransformState> {
public:
- static PassRefPtr<HitTestingTransformState> create(const FloatPoint& p, const FloatQuad& quad)
+ static PassRefPtr<HitTestingTransformState> create(const FloatPoint& p, const FloatQuad& quad, const FloatQuad& area)
{
- return adoptRef(new HitTestingTransformState(p, quad));
+ return adoptRef(new HitTestingTransformState(p, quad, area));
}
static PassRefPtr<HitTestingTransformState> create(const HitTestingTransformState& other)
@@ -58,18 +58,21 @@
FloatPoint mappedPoint() const;
FloatQuad mappedQuad() const;
- LayoutRect boundsOfMappedQuad() const;
+ FloatQuad mappedArea() const;
+ LayoutRect boundsOfMappedArea() const;
void flatten();
FloatPoint m_lastPlanarPoint;
FloatQuad m_lastPlanarQuad;
+ FloatQuad m_lastPlanarArea;
TransformationMatrix m_accumulatedTransform;
bool m_accumulatingTransform;
private:
- HitTestingTransformState(const FloatPoint& p, const FloatQuad& quad)
+ HitTestingTransformState(const FloatPoint& p, const FloatQuad& quad, const FloatQuad& area)
: m_lastPlanarPoint(p)
, m_lastPlanarQuad(quad)
+ , m_lastPlanarArea(area)
, m_accumulatingTransform(false)
{
}
@@ -78,6 +81,7 @@
: RefCounted<HitTestingTransformState>()
, m_lastPlanarPoint(other.m_lastPlanarPoint)
, m_lastPlanarQuad(other.m_lastPlanarQuad)
+ , m_lastPlanarArea(other.m_lastPlanarArea)
, m_accumulatedTransform(other.m_accumulatedTransform)
, m_accumulatingTransform(other.m_accumulatingTransform)
{
Modified: trunk/Source/WebCore/rendering/RenderFlowThread.cpp (123623 => 123624)
--- trunk/Source/WebCore/rendering/RenderFlowThread.cpp 2012-07-25 15:51:26 UTC (rev 123623)
+++ trunk/Source/WebCore/rendering/RenderFlowThread.cpp 2012-07-25 16:09:53 UTC (rev 123624)
@@ -315,17 +315,16 @@
} else
renderFlowThreadOffset = accumulatedOffset - regionRect.location();
- LayoutPoint transformedPoint = pointInContainer.point() - renderFlowThreadOffset;
-
// Always ignore clipping, since the RenderFlowThread has nothing to do with the bounds of the FrameView.
HitTestRequest newRequest(request.type() | HitTestRequest::IgnoreClipping);
RenderRegion* oldRegion = result.region();
result.setRegion(region);
- LayoutPoint oldPoint = result.point();
- result.setPoint(transformedPoint);
- bool isPointInsideFlowThread = layer()->hitTest(newRequest, result);
- result.setPoint(oldPoint);
+
+ HitTestPoint newHitTestPoint(pointInContainer);
+ newHitTestPoint.move(-renderFlowThreadOffset);
+
+ bool isPointInsideFlowThread = layer()->hitTest(newRequest, newHitTestPoint, result);
result.setRegion(oldRegion);
// FIXME: Should we set result.m_localPoint back to the RenderRegion's coordinate space or leave it in the RenderFlowThread's coordinate
Modified: trunk/Source/WebCore/rendering/RenderLayer.cpp (123623 => 123624)
--- trunk/Source/WebCore/rendering/RenderLayer.cpp 2012-07-25 15:51:26 UTC (rev 123623)
+++ trunk/Source/WebCore/rendering/RenderLayer.cpp 2012-07-25 16:09:53 UTC (rev 123624)
@@ -3386,13 +3386,18 @@
bool RenderLayer::hitTest(const HitTestRequest& request, HitTestResult& result)
{
+ return hitTest(request, result.hitTestPoint(), result);
+}
+
+bool RenderLayer::hitTest(const HitTestRequest& request, const HitTestPoint& hitTestPoint, HitTestResult& result)
+{
renderer()->document()->updateLayout();
LayoutRect hitTestArea = renderer()->isRenderFlowThread() ? toRenderFlowThread(renderer())->borderBoxRect() : renderer()->view()->documentRect();
if (!request.ignoreClipping())
hitTestArea.intersect(frameVisibleRect(renderer()));
- RenderLayer* insideLayer = hitTestLayer(this, 0, request, result, hitTestArea, result.hitTestPoint(), false);
+ RenderLayer* insideLayer = hitTestLayer(this, 0, request, result, hitTestArea, hitTestPoint, false);
if (!insideLayer) {
// We didn't hit any layer. If we are the root layer and the mouse is -- or just was -- down,
// return ourselves. We do this so mouse events continue getting delivered after a drag has
@@ -3456,7 +3461,7 @@
} else {
// If this is the first time we need to make transform state, then base it off of hitTestPoint,
// which is relative to rootLayer.
- transformState = HitTestingTransformState::create(hitTestPoint.point(), FloatQuad(hitTestRect));
+ transformState = HitTestingTransformState::create(hitTestPoint.transformedPoint(), hitTestPoint.transformedRect(), FloatQuad(hitTestRect));
convertToLayerCoords(rootLayer, offset);
}
@@ -3536,10 +3541,14 @@
//
// We can't just map hitTestPoint and hitTestRect because they may have been flattened (losing z)
// by our container.
- LayoutPoint localPoint = roundedLayoutPoint(newTransformState->mappedPoint());
- LayoutRect localHitTestRect = newTransformState->boundsOfMappedQuad();
- HitTestPoint newHitTestPoint(result.hitTestPoint());
- newHitTestPoint.setPoint(localPoint);
+ FloatPoint localPoint = newTransformState->mappedPoint();
+ FloatQuad localPointQuad = newTransformState->mappedQuad();
+ LayoutRect localHitTestRect = newTransformState->boundsOfMappedArea();
+ HitTestPoint newHitTestPoint;
+ if (hitTestPoint.isRectBasedTest())
+ newHitTestPoint = HitTestPoint(localPoint, localPointQuad);
+ else
+ newHitTestPoint = HitTestPoint(localPoint);
// Now do a hit test with the root layer shifted to be us.
return hitTestLayer(this, containerLayer, request, result, localHitTestRect, newHitTestPoint, true, newTransformState.get(), zOffset);
@@ -3835,10 +3844,14 @@
RenderLayer* nextLayer = columnLayers[columnIndex - 1];
RefPtr<HitTestingTransformState> newTransformState = nextLayer->createLocalTransformState(rootLayer, nextLayer, localClipRect, hitTestPoint, transformState);
newTransformState->translate(offset.width(), offset.height(), HitTestingTransformState::AccumulateTransform);
- LayoutPoint localPoint = roundedLayoutPoint(newTransformState->mappedPoint());
- LayoutRect localHitTestRect = newTransformState->mappedQuad().enclosingBoundingBox();
- HitTestPoint newHitTestPoint(result.hitTestPoint());
- newHitTestPoint.setPoint(localPoint);
+ FloatPoint localPoint = newTransformState->mappedPoint();
+ FloatQuad localPointQuad = newTransformState->mappedQuad();
+ LayoutRect localHitTestRect = newTransformState->mappedArea().enclosingBoundingBox();
+ HitTestPoint newHitTestPoint;
+ if (hitTestPoint.isRectBasedTest())
+ newHitTestPoint = HitTestPoint(localPoint, localPointQuad);
+ else
+ newHitTestPoint = HitTestPoint(localPoint);
newTransformState->flatten();
hitLayer = hitTestChildLayerColumns(childLayer, columnLayers[columnIndex - 1], request, result, localHitTestRect, newHitTestPoint,
Modified: trunk/Source/WebCore/rendering/RenderLayer.h (123623 => 123624)
--- trunk/Source/WebCore/rendering/RenderLayer.h 2012-07-25 15:51:26 UTC (rev 123623)
+++ trunk/Source/WebCore/rendering/RenderLayer.h 2012-07-25 16:09:53 UTC (rev 123624)
@@ -512,6 +512,7 @@
void paint(GraphicsContext*, const LayoutRect& damageRect, PaintBehavior = PaintBehaviorNormal, RenderObject* paintingRoot = 0,
RenderRegion* = 0, PaintLayerFlags = 0);
bool hitTest(const HitTestRequest&, HitTestResult&);
+ bool hitTest(const HitTestRequest&, const HitTestPoint&, HitTestResult&);
void paintOverlayScrollbars(GraphicsContext*, const LayoutRect& damageRect, PaintBehavior, RenderObject* paintingRoot);
// This method figures out our layerBounds in coordinates relative to
Modified: trunk/Source/WebCore/rendering/svg/RenderSVGText.cpp (123623 => 123624)
--- trunk/Source/WebCore/rendering/svg/RenderSVGText.cpp 2012-07-25 15:51:26 UTC (rev 123623)
+++ trunk/Source/WebCore/rendering/svg/RenderSVGText.cpp 2012-07-25 16:09:53 UTC (rev 123624)
@@ -451,7 +451,7 @@
if (!SVGRenderSupport::pointInClippingArea(this, localPoint))
return false;
- HitTestPoint hitTestPoint(flooredIntPoint(localPoint));
+ HitTestPoint hitTestPoint(LayoutPoint(flooredIntPoint(localPoint)));
return RenderBlock::nodeAtPoint(request, result, hitTestPoint, LayoutPoint(), hitTestAction);
}
}