2008-11-05 Simon Fraser <simon.fraser@apple.com>

        Reviewed by Dave Hyatt

        https://bugs.webkit.org/show_bug.cgi?id=21870

        Implement absoluteToLocal() to convert a point from absolute
        to local coordinates, optionally taking transforms into account.

        Use this to set offsetX/offsetY in mouse events, thus fixing
        offsetX/offsetY in events on elements with transforms.

        Test: fast/events/offsetX-offsetY.html

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@38186 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent e375cf67
2008-11-05 Simon Fraser <simon.fraser@apple.com>
Reviewed by Dave Hyatt
https://bugs.webkit.org/show_bug.cgi?id=21870
Test event.offsetX/offsetY for various types of elements, which
exercises absoluteToLocal() code.
* fast/events/offsetX-offsetY-expected.txt: Added.
* fast/events/offsetX-offsetY.html: Added.
2008-11-05 Gavin Barraclough <barraclough@apple.com>
Reviewed by Maciej Stachowiak.
......
Absolute position
Relative position
Fixed position
First Second
This cell has borderTopExtra
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
In columns
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Div with transform
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
In RTL overflow
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
PASS: event at (112, 62) hit abs-box at offset (32, 37)
PASS: event at (157, 32) hit rel-box at offset (22, 24)
PASS: event at (410, 30) hit fixed-box at offset (10, 10)
PASS: event at (36, 272) hit with-bordertopextra at offset (4, 4)
PASS: event at (563, 410) hit inside-overflow at offset (7, 6)
PASS: event at (112, 369) hit transformed at offset (11, 15)
<html>
<head>
<title>Test offsetX/offsetY on various types of elements</title>
<script type="text/javascript" charset="utf-8">
if (window.layoutTestController) {
layoutTestController.dumpAsText();
layoutTestController.waitUntilDone();
}
function mousemoved(e)
{
var resultBox = document.getElementById('mousepos');
var offsets = 'element id: ' + e.target.id + '<br> clientX: ' + e.clientX + ' clientY: ' + e.clientY + '<br>';
offsets += 'offsetX: ' + e.offsetX + ' offsetY: ' + e.offsetY;
resultBox.innerHTML = offsets;
}
function log(s)
{
var resultsDiv = document.getElementById('results');
resultsDiv.innerHTML += s + '<br>';
}
function test()
{
// Scroll so that frame view offsets are non-zero
window.scrollTo(20, 100);
document.getElementById('overflow').scrollLeft = 80;
document.getElementById('overflow').scrollTop = 60;
// In non-test mode, show the mouse coords for testing
if (!window.layoutTestController)
document.body.addEventListener('mousemove', mousemoved, false);
dispatchEvent(112, 62, 'abs-box', 32, 37);
dispatchEvent(157, 32, 'rel-box', 22, 24);
dispatchEvent(410, 30, 'fixed-box', 10, 10);
dispatchEvent(36, 272, 'with-bordertopextra', 4, 4);
// dispatchEvent(639, 316, 'in-columns', 173, -172); // This is buggy. See https://bugs.webkit.org/show_bug.cgi?id=21993
dispatchEvent(563, 410, 'inside-overflow', 7, 6);
dispatchEvent(112, 369, 'transformed', 11, 15);
if (window.layoutTestController)
layoutTestController.notifyDone();
}
function dispatchEvent(clientX, clientY, expectedElementID, expectedOffsetX, expectedOffsetY)
{
var ev = document.createEvent("MouseEvent");
ev.initMouseEvent("click", true, true, window, 1, 1, 1, clientX, clientY, false, false, false, false, 0, document);
ev.expectedElement = expectedElementID;
ev.expectedOffsetX = expectedOffsetX;
ev.expectedOffsetY = expectedOffsetY;
var target = document.elementFromPoint(ev.pageX, ev.pageY);
target.dispatchEvent(ev);
}
function clicked(event)
{
var element = event.target;
var result;
if (element.id == event.expectedElement &&
event.offsetX == event.expectedOffsetX &&
event.offsetY == event.expectedOffsetY)
result = 'PASS: event at (' + event.clientX + ', ' + event.clientY + ') hit ' + element.id + ' at offset (' + event.offsetX + ', ' + event.offsetY + ')';
else
result = 'FAIL: event at (' + event.clientX + ', ' + event.clientY + ') expected to hit ' + event.expectedElement + ' at (' + event.expectedOffsetX + ', ' + event.expectedOffsetY + ') but hit ' + element.id + ' at (' + event.offsetX + ', ' + event.offsetY + ')';
log(result);
}
window.addEventListener('load', function() {
window.setTimeout(test, 0);
}, false);
</script>
<style type="text/css" media="screen">
body {
height: 2048px;
width: 2048px;
margin: 0;
cursor: crosshair;
}
.box {
height: 200px;
width: 200px;
margin: 20px;
background-color: gray;
}
#abs-box {
position: absolute;
top: 125px;
left: 100px;
height: 100px;
width: 200px;
border: 15px solid gray;
}
#rel-box {
position: relative;
height: 100px;
width: 200px;
left: 40px;
top: -50px;
background-color: #CCC;
}
#fixed-box {
position: fixed;
top: 20px;
left: 400px;
width: 300px;
height: 50px;
background-color: #AAA;
}
#columns {
position: absolute;
-webkit-column-count: 3;
width: 400px;
top: 200px;
left: 460px;
border: 2px solid black;
}
#in-columns {
display: block;
height: 40px;
width: 60%;
margin: 0 auto;
text-align: center;
background-color: gray;
}
#transformed {
height: 120px;
width: 200px;
background-color: #DDD;
-webkit-transform: translate(100px, 50px) rotate(20deg);
}
#overflow {
overflow: scroll;
position: absolute;
width: 300px;
top: 440px;
left: 460px;
height: 200px;
border: 2px solid black;
direction: rtl;
}
#overflow-contents {
height: 500px;
width: 120%;
background-color: #BBB;
}
#inside-overflow {
width: 50%;
background-color: gray;
}
#results {
position: absolute;
left: 30px;
top: 700px;
}
#mousepos {
position: absolute;
left: 30px;
top: 650px;
color: gray;
font-size: smaller;
}
tr {
/* -webkit-transform: rotate(10deg);*/
}
</style>
</head>
<body onclick="clicked(event)">
<div id="filler" style="position: absolute; top: 0; width: 100%; height: 100%"></div>
<!-- Test assumes offsetX/offsetY are relative to border box, but see
http://www.quirksmode.org/dom/w3c_cssom.html#mousepos -->
<div id="abs-box">
Absolute position
<div id="rel-box">
Relative position
</div>
<div id="fixed-box">
Fixed position
</div>
</div>
<table border="2" cellspacing="3" cellpadding="2" style="width: 400px; margin-top: 280px; margin-left: 40px;">
<tr><th>First</th><th>Second</th></tr>
<tr style="vertical-align: center;"><td>This cell has borderTopExtra<div class="box" id="with-bordertopextra" style="position: relative; height: 20px; margin: 4px;"></div></td><td>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
</td></tr>
</table>
<div id="columns">
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. <span id="in-columns">In columns</span> Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
</div>
<div id="transformed">
Div with transform
</div>
<div id="overflow">
<div id="overflow-contents">
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
<div id="inside-overflow">In RTL overflow</div>
<p>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
</div>
</div>
<div id="results"></div>
<div id="mousepos"></div>
</body>
</html>
2008-11-05 Simon Fraser <simon.fraser@apple.com>
Reviewed by Dave Hyatt
https://bugs.webkit.org/show_bug.cgi?id=21870
Implement absoluteToLocal() to convert a point from absolute
to local coordinates, optionally taking transforms into account.
Use this to set offsetX/offsetY in mouse events, thus fixing
offsetX/offsetY in events on elements with transforms.
Test: fast/events/offsetX-offsetY.html
* dom/MouseRelatedEvent.cpp:
(WebCore::MouseRelatedEvent::receivedTarget):
* rendering/RenderBox.cpp:
(WebCore::RenderBox::localToAbsolute):
(WebCore::RenderBox::absoluteToLocal):
(WebCore::RenderBox::offsetFromContainer):
* rendering/RenderBox.h:
* rendering/RenderObject.cpp:
(WebCore::RenderObject::absoluteToLocal):
* rendering/RenderObject.h:
* rendering/RenderTableCell.cpp:
(WebCore::RenderTableCell::localToAbsolute):
(WebCore::RenderTableCell::absoluteToLocal):
* rendering/RenderTableCell.h:
* rendering/RenderView.cpp:
(WebCore::RenderView::absoluteToLocal):
* rendering/RenderView.h:
2008-11-06 Alp Toker <alp@nuanti.com>
Reviewed by Cameron Zwarich.
......@@ -133,11 +133,9 @@ void MouseRelatedEvent::receivedTarget()
// Adjust offsetX/Y to be relative to the target's position.
if (!isSimulated()) {
if (RenderObject* r = targ->renderer()) {
// FIXME: This doesn't work correctly with transforms. We need
// an absoluteToLocal() method.
FloatPoint absPos = r->localToAbsolute();
m_offsetX -= absPos.x();
m_offsetY -= absPos.y();
FloatPoint absPos = r->absoluteToLocal(FloatPoint(m_pageX, m_pageY), false, true);
m_offsetX = absPos.x();
m_offsetY = absPos.y();
}
}
......
......@@ -1014,32 +1014,72 @@ FloatPoint RenderBox::localToAbsolute(FloatPoint localPoint, bool fixed, bool us
localPoint = m_layer->transform()->mapPoint(localPoint);
}
if (isRelPositioned())
localPoint += relativePositionOffset();
if (!isInline() || isReplaced()) {
RenderBlock* cb;
if (o->isBlockFlow() && style()->position() != AbsolutePosition && style()->position() != FixedPosition
&& (cb = static_cast<RenderBlock*>(o))->hasColumns()) {
IntRect rect(m_x, m_y, 1, 1);
cb->adjustRectForColumns(rect);
localPoint.move(rect.x(), rect.y());
} else
localPoint.move(m_x, m_y);
}
localPoint += offsetFromContainer(o);
if (o->hasOverflowClip())
localPoint -= o->layer()->scrolledContentOffset();
return o->localToAbsoluteForContent(localPoint, fixed, useTransforms);
}
return FloatPoint();
}
if (style()->position() == AbsolutePosition)
localPoint += offsetForPositionedInContainer(o);
FloatPoint RenderBox::absoluteToLocal(FloatPoint containerPoint, bool fixed, bool useTransforms) const
{
// We don't expect absoluteToLocal() to be called during layout (yet)
ASSERT(!view() || !view()->layoutState());
if (style()->position() == FixedPosition)
fixed = true;
return o->localToAbsoluteForContent(localPoint, fixed, useTransforms);
if (useTransforms && m_layer && m_layer->transform())
fixed = false;
RenderObject* o = container();
if (o) {
FloatPoint localPoint = o->absoluteToLocal(containerPoint, fixed, useTransforms);
// Take into account space above a vertically aligned table cell
// (see localToAbsoluteForContent())
localPoint.move(0.0f, -static_cast<float>(borderTopExtra()));
localPoint -= offsetFromContainer(o);
if (useTransforms && m_layer && m_layer->transform())
localPoint = m_layer->transform()->inverse().mapPoint(localPoint);
return localPoint;
}
return FloatPoint();
}
IntSize RenderBox::offsetFromContainer(RenderObject* o) const
{
ASSERT(o == container());
IntSize offset;
if (isRelPositioned())
offset += relativePositionOffset();
if (!isInline() || isReplaced()) {
RenderBlock* cb;
if (o->isBlockFlow() && style()->position() != AbsolutePosition && style()->position() != FixedPosition
&& (cb = static_cast<RenderBlock*>(o))->hasColumns()) {
IntRect rect(m_x, m_y, 1, 1);
cb->adjustRectForColumns(rect);
offset.expand(rect.x(), rect.y());
} else
offset.expand(m_x, m_y);
}
if (o->hasOverflowClip())
offset -= o->layer()->scrolledContentOffset();
if (style()->position() == AbsolutePosition)
offset += offsetForPositionedInContainer(o);
return offset;
}
void RenderBox::dirtyLineBoxes(bool fullLayout, bool /*isRootLineBox*/)
{
if (m_inlineBoxWrapper) {
......
......@@ -50,6 +50,7 @@ public:
virtual void setOverrideSize(int);
virtual FloatPoint localToAbsolute(FloatPoint localPoint = FloatPoint(), bool fixed = false, bool useTransforms = false) const;
virtual FloatPoint absoluteToLocal(FloatPoint containerPoint, bool fixed = false, bool useTransforms = false) const;
virtual int xPos() const { return m_x; }
virtual int yPos() const { return m_y; }
......@@ -189,6 +190,8 @@ protected:
virtual bool shouldCalculateSizeAsReplaced() const { return isReplaced() && !isInlineBlockOrInlineTable(); }
IntSize offsetFromContainer(RenderObject*) const;
private:
void paintRootBoxDecorations(PaintInfo&, int tx, int ty);
// Returns true if we did a full repaint
......
......@@ -2385,6 +2385,19 @@ FloatPoint RenderObject::localToAbsolute(FloatPoint localPoint, bool fixed, bool
return FloatPoint();
}
FloatPoint RenderObject::absoluteToLocal(FloatPoint containerPoint, bool fixed, bool useTransforms) const
{
RenderObject* o = parent();
if (o) {
FloatPoint localPoint = o->absoluteToLocal(containerPoint, fixed, useTransforms);
localPoint.move(0.0f, -static_cast<float>(o->borderTopExtra()));
if (o->hasOverflowClip())
localPoint += o->layer()->scrolledContentOffset();
return localPoint;
}
return FloatPoint();
}
IntRect RenderObject::caretRect(InlineBox* inlineBox, int caretOffset, int* extraWidthToEndOfLine)
{
if (extraWidthToEndOfLine)
......
......@@ -583,6 +583,7 @@ public:
// Convert the given local point to absolute coordinates
// FIXME: Temporary. If useTransforms is true, take transforms into account. Eventually localToAbsolute() will always be transform-aware.
virtual FloatPoint localToAbsolute(FloatPoint localPoint = FloatPoint(), bool fixed = false, bool useTransforms = false) const;
virtual FloatPoint absoluteToLocal(FloatPoint, bool fixed = false, bool useTransforms = false) const;
// This function is used to deal with the extra top space that can occur in table cells (called borderTopExtra).
// The children of the cell do not factor this space in, so we have to add it in. Any code that wants to
......
......@@ -198,13 +198,22 @@ void RenderTableCell::computeAbsoluteRepaintRect(IntRect& r, bool fixed)
FloatPoint RenderTableCell::localToAbsolute(FloatPoint localPoint, bool fixed, bool useTransforms) const
{
FloatPoint result = RenderBlock::localToAbsolute(localPoint, fixed, useTransforms);
RenderView* v = view();
if ((!v || !v->layoutState()) && parent()) {
// Rows are in the same coordinate space, so don't add their offset in.
result.move(-parent()->xPos(), -parent()->yPos());
localPoint.move(-parent()->xPos(), -parent()->yPos());
}
return result;
return RenderBlock::localToAbsolute(localPoint, fixed, useTransforms);;
}
FloatPoint RenderTableCell::absoluteToLocal(FloatPoint containerPoint, bool fixed, bool useTransforms) const
{
FloatPoint localPoint = RenderBlock::absoluteToLocal(containerPoint, fixed, useTransforms);
if (parent()) {
// Rows are in the same coordinate space, so add their offset back in.
localPoint.move(parent()->xPos(), parent()->yPos());
}
return localPoint;
}
int RenderTableCell::baselinePosition(bool /*firstLine*/, bool /*isRootLineBox*/) const
......
......@@ -102,6 +102,7 @@ public:
virtual IntRect absoluteClippedOverflowRect();
virtual void computeAbsoluteRepaintRect(IntRect&, bool fixed = false);
virtual FloatPoint localToAbsolute(FloatPoint localPoint = FloatPoint(), bool fixed = false, bool useTransforms = false) const;
virtual FloatPoint absoluteToLocal(FloatPoint containerPoint, bool fixed = false, bool useTransforms = false) const;
virtual int baselinePosition(bool firstLine = false, bool isRootLineBox = false) const;
......
......@@ -139,6 +139,14 @@ FloatPoint RenderView::localToAbsolute(FloatPoint localPoint, bool fixed, bool u
return localPoint;
}
FloatPoint RenderView::absoluteToLocal(FloatPoint containerPoint, bool fixed, bool useTransforms) const
{
if (fixed && m_frameView)
containerPoint -= m_frameView->scrollOffset();
return containerPoint;
}
void RenderView::paint(PaintInfo& paintInfo, int tx, int ty)
{
// If we ever require layout but receive a paint anyway, something has gone horribly wrong.
......
......@@ -45,6 +45,7 @@ public:
virtual void calcHeight();
virtual void calcPrefWidths();
virtual FloatPoint localToAbsolute(FloatPoint localPoint = FloatPoint(), bool fixed = false, bool useTransforms = false) const;
virtual FloatPoint absoluteToLocal(FloatPoint containerPoint, bool fixed = false, bool useTransforms = false) const;
int docHeight() const;
int docWidth() const;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment