Added: trunk/LayoutTests/fast/dom/nodesFromRect-table.html (0 => 119967)
--- trunk/LayoutTests/fast/dom/nodesFromRect-table.html (rev 0)
+++ trunk/LayoutTests/fast/dom/nodesFromRect-table.html 2012-06-11 09:38:48 UTC (rev 119967)
@@ -0,0 +1,121 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Document::nodesFromRect : Rect-based hit-testing on tables - bug 86605</title>
+<style type="text/css">
+ #sandbox {
+ position: absolute;
+ left: 0px;
+ top: 0px;
+ width: 600px;
+ height: 800px;
+ }
+ #testtable {
+ margin: 100px;
+ width: 200px;
+ height: 200px;
+ }
+ .rtl {
+ direction: rtl;
+ }
+ .tblr {
+ -webkit-writing-mode: vertical-lr;
+ }
+</style>
+<script src=""
+<script src=""
+</head>
+
+<body>
+ <div id=sandbox>
+ <table id=testtable>
+ <tr id=tr1>
+ <td id=td11>
+ <td id=td12>
+ <td id=td13>
+ <td id=td14>
+ <tr id=tr2>
+ <td id=td21>
+ <td id=td22>
+ <td id=td23>
+ <td id=td24>
+ <tr id=tr3>
+ <td id=td31>
+ <td id=td32>
+ <td id=td33>
+ <td id=td34>
+ <tr id=tr4>
+ <td id=td41>
+ <td id=td42>
+ <td id=td43>
+ <td id=td44>
+ </table>
+ </div>
+
+ <script>
+ function runTest()
+ {
+ description(document.title);
+ var e = {};
+
+ // Set up shortcut access to elements
+ ['sandbox', 'testtable', 'tr1', 'tr2', 'tr3', 'tr4',
+ 'td11', 'td12', 'td13', 'td14',
+ 'td21', 'td22', 'td23', 'td24',
+ 'td31', 'td32', 'td33', 'td34',
+ 'td41', 'td42', 'td43', 'td44'].forEach(function(a) {
+ e[a] = document.getElementById(a);
+ });
+
+ window.scrollTo(0, 0);
+ debug('Check area hits fully inside table cells');
+ check(125, 125, 10, 10, 10, 10, [e.td11]);
+ check(275, 125, 10, 10, 10, 10, [e.td14]);
+ check(175, 175, 10, 10, 10, 10, [e.td22]);
+ check(125, 275, 10, 10, 10, 10, [e.td41]);
+
+ debug('Check area hits across table border');
+ check(90, 125, 10, 20, 10, 20, [e.td11, e.testtable, e.sandbox]);
+ check(275, 90, 20, 10, 20, 10, [e.td14, e.testtable, e.sandbox]);
+ check(310, 310, 20, 20, 20, 20, [e.td44, e.testtable, e.sandbox]);
+
+ debug('Check area hits crossing neighbouring table cells');
+ check(190, 175, 10, 20, 10, 20, [e.td22, e.td23, e.testtable]);
+ check(260, 275, 10, 20, 10, 20, [e.td43, e.td44, e.testtable]);
+ check(175, 190, 20, 10, 20, 10, [e.td22, e.td32, e.testtable]);
+ check(275, 260, 20, 10, 20, 10, [e.td34, e.td44, e.testtable]);
+ check(190, 190, 20, 20, 20, 20, [e.td22, e.td23, e.td32, e.td33, e.testtable]);
+
+ debug('Check area hits crossing entire table cells');
+ check(200, 175, 10, 90, 10, 90, [e.td21, e.td22, e.td23, e.td24, e.testtable]);
+ check(175, 200, 90, 10, 90, 10, [e.td12, e.td22, e.td32, e.td42, e.testtable]);
+ check(150, 150, 60, 60, 60, 60, [e.td11, e.td12, e.td13,
+ e.td21, e.td22, e.td23,
+ e.td31, e.td32, e.td33,
+ e.testtable, e.sandbox]);
+ check(125, 350, 120, 10, 10, 10, [e.td31, e.td41, e.testtable, e.sandbox]);
+ check(350, 150, 10, 10, 10, 120, [e.td13, e.td14, e.td23, e.td24, e.testtable, e.sandbox]);
+
+ debug('Check area hits on right-to-left table');
+ e['testtable'].setAttribute('class', 'rtl');
+ check(275, 125, 10, 10, 10, 10, [e.td11]);
+ check(175, 175, 10, 10, 10, 10, [e.td23]);
+ check(175, 190, 20, 10, 20, 10, [e.td23, e.td33, e.testtable]);
+
+ debug('Check area hits on flipped (tb-lr) table');
+ e['testtable'].setAttribute('class', 'tblr');
+ check(275, 125, 10, 10, 10, 10, [e.td41]);
+ check(125, 275, 10, 10, 10, 10, [e.td14]);
+ check(200, 175, 10, 90, 10, 90, [e.td12, e.td22, e.td32, e.td42, e.testtable]);
+
+ }
+
+ window._onload_ = runTest;
+ </script>
+
+ <p id='description'></p>
+ <span id="console"></span>
+ <script src=""
+</body>
+</html>
+
Modified: trunk/Source/WebCore/rendering/RenderTableSection.cpp (119966 => 119967)
--- trunk/Source/WebCore/rendering/RenderTableSection.cpp 2012-06-11 09:26:31 UTC (rev 119966)
+++ trunk/Source/WebCore/rendering/RenderTableSection.cpp 2012-06-11 09:38:48 UTC (rev 119967)
@@ -1026,6 +1026,23 @@
cell->paint(paintInfo, cellPoint);
}
+LayoutRect RenderTableSection::logicalRectForWritingModeAndDirection(const LayoutRect& rect) const
+{
+ LayoutRect tableAlignedRect(rect);
+
+ flipForWritingMode(tableAlignedRect);
+
+ if (!style()->isHorizontalWritingMode())
+ tableAlignedRect = tableAlignedRect.transposedRect();
+
+ const Vector<int>& columnPos = table()->columnPositions();
+ if (!style()->isLeftToRightDirection())
+ tableAlignedRect.setX(columnPos[columnPos.size() - 1] - tableAlignedRect.maxX());
+
+ return tableAlignedRect;
+}
+
+// FIXME: Use spannedRows internally, see https://bugs.webkit.org/show_bug.cgi?id=88066
CellSpan RenderTableSection::dirtiedRows(const LayoutRect& damageRect) const
{
if (m_forceSlowPaintPathWithOverflowingCell)
@@ -1054,6 +1071,7 @@
}
+// FIXME: Use spannedColumns internally, see https://bugs.webkit.org/show_bug.cgi?id=88066
CellSpan RenderTableSection::dirtiedColumns(const LayoutRect& damageRect) const
{
if (m_forceSlowPaintPathWithOverflowingCell)
@@ -1080,6 +1098,57 @@
return CellSpan(startCol, endCol);
}
+CellSpan RenderTableSection::spannedRows(const LayoutRect& flippedRect) const
+{
+ // Find the first row that starts after rect top.
+ // FIXME: Upper_bound might not be the correct algorithm here since it might skip empty rows, but it is
+ // consistent with behavior in the former point based hit-testing (but inconsistent with spannedColumns).
+ unsigned nextRow = std::upper_bound(m_rowPos.begin(), m_rowPos.end(), flippedRect.y()) - m_rowPos.begin();
+
+ if (nextRow == m_rowPos.size())
+ return CellSpan(m_rowPos.size() - 1, m_rowPos.size() - 1); // After all rows.
+
+ unsigned startRow = nextRow > 0 ? nextRow - 1 : 0;
+
+ // Find the first row that starts after rect bottom.
+ unsigned endRow;
+ if (m_rowPos[nextRow] >= flippedRect.maxY())
+ endRow = nextRow;
+ else {
+ endRow = std::upper_bound(m_rowPos.begin() + nextRow, m_rowPos.end(), flippedRect.maxY()) - m_rowPos.begin();
+ if (endRow == m_rowPos.size())
+ endRow = m_rowPos.size() - 1;
+ }
+
+ return CellSpan(startRow, endRow);
+}
+
+CellSpan RenderTableSection::spannedColumns(const LayoutRect& flippedRect) const
+{
+ const Vector<int>& columnPos = table()->columnPositions();
+
+ // Find the first columnt that starts after rect left.
+ unsigned nextColumn = std::lower_bound(columnPos.begin(), columnPos.end(), flippedRect.x()) - columnPos.begin();
+
+ if (nextColumn == columnPos.size())
+ return CellSpan(columnPos.size() - 1, columnPos.size() - 1); // After all columns.
+
+ unsigned startColumn = nextColumn > 0 ? nextColumn - 1 : 0;
+
+ // Find the first row that starts after rect right.
+ unsigned endColumn;
+ if (columnPos[nextColumn] >= flippedRect.maxX())
+ endColumn = nextColumn;
+ else {
+ endColumn = std::lower_bound(columnPos.begin() + nextColumn, columnPos.end(), flippedRect.maxX()) - columnPos.begin();
+ if (endColumn == columnPos.size())
+ endColumn = columnPos.size() - 1;
+ }
+
+ return CellSpan(startColumn, endColumn);
+}
+
+
void RenderTableSection::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
PaintPhase paintPhase = paintInfo.phase;
@@ -1344,52 +1413,41 @@
return false;
}
- LayoutPoint location = pointInContainer - toLayoutSize(adjustedLocation);
- if (style()->isFlippedBlocksWritingMode()) {
- if (style()->isHorizontalWritingMode())
- location.setY(height() - location.y());
- else
- location.setX(width() - location.x());
- }
-
- LayoutUnit offsetInColumnDirection = style()->isHorizontalWritingMode() ? location.y() : location.x();
-
recalcCellsIfNeeded();
- // Find the first row that starts after offsetInColumnDirection.
- unsigned nextRow = std::upper_bound(m_rowPos.begin(), m_rowPos.end(), offsetInColumnDirection) - m_rowPos.begin();
- if (nextRow == m_rowPos.size())
- return false;
- // Now set hitRow to the index of the hit row, or 0.
- unsigned hitRow = nextRow > 0 ? nextRow - 1 : 0;
+ LayoutRect hitTestRect = result.rectForPoint(pointInContainer);
+ hitTestRect.moveBy(-adjustedLocation);
- Vector<int>& columnPos = table()->columnPositions();
- LayoutUnit offsetInRowDirection = style()->isHorizontalWritingMode() ? location.x() : location.y();
- if (!style()->isLeftToRightDirection())
- offsetInRowDirection = columnPos[columnPos.size() - 1] - offsetInRowDirection;
+ LayoutRect tableAlignedRect = logicalRectForWritingModeAndDirection(hitTestRect);
+ CellSpan rowSpan = spannedRows(tableAlignedRect);
+ CellSpan columnSpan = spannedColumns(tableAlignedRect);
- unsigned nextColumn = std::lower_bound(columnPos.begin(), columnPos.end(), offsetInRowDirection) - columnPos.begin();
- if (nextColumn == columnPos.size())
- return false;
- unsigned hitColumn = nextColumn > 0 ? nextColumn - 1 : 0;
+ // Now iterate over the spanned rows and columns.
+ for (unsigned hitRow = rowSpan.start(); hitRow < rowSpan.end(); ++hitRow) {
+ for (unsigned hitColumn = columnSpan.start(); hitColumn < columnSpan.end(); ++hitColumn) {
+ CellStruct& current = cellAt(hitRow, hitColumn);
- CellStruct& current = cellAt(hitRow, hitColumn);
+ // If the cell is empty, there's nothing to do
+ if (!current.hasCells())
+ continue;
- // If the cell is empty, there's nothing to do
- if (!current.hasCells())
- return false;
-
- for (unsigned i = current.cells.size() ; i; ) {
- --i;
- RenderTableCell* cell = current.cells[i];
- LayoutPoint cellPoint = flipForWritingModeForChild(cell, adjustedLocation);
- if (static_cast<RenderObject*>(cell)->nodeAtPoint(request, result, pointInContainer, cellPoint, action)) {
- updateHitTestResult(result, toLayoutPoint(pointInContainer - cellPoint));
- return true;
+ for (unsigned i = current.cells.size() ; i; ) {
+ --i;
+ RenderTableCell* cell = current.cells[i];
+ LayoutPoint cellPoint = flipForWritingModeForChild(cell, adjustedLocation);
+ if (static_cast<RenderObject*>(cell)->nodeAtPoint(request, result, pointInContainer, cellPoint, action)) {
+ updateHitTestResult(result, toLayoutPoint(pointInContainer - cellPoint));
+ return true;
+ }
+ }
+ if (!result.isRectBasedTest())
+ break;
}
+ if (!result.isRectBasedTest())
+ break;
}
+
return false;
-
}
void RenderTableSection::removeCachedCollapsedBorders(const RenderTableCell* cell)