Text decorations do not contribute to visual overflow
authormmaxfield@apple.com <mmaxfield@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 20 May 2014 00:45:52 +0000 (00:45 +0000)
committermmaxfield@apple.com <mmaxfield@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 20 May 2014 00:45:52 +0000 (00:45 +0000)
https://bugs.webkit.org/show_bug.cgi?id=132773

Patch by Myles C. Maxfield <litherum@gmail.com> on 2014-05-19
Reviewed by Antti Koivisto.

Source/WebCore:
This patch creates a function, visualOverflowForDecorations, which computes
how much visual overflow to add around a text box due to text decorations. Most of the time,
text decorations are fully contained within the text box, so the result is usually 0.

This function exists within style/InlineTextBoxStyle.cpp, which is an added file. This is
so that it can be called from setLogicalWidthForTextRun() inside RenderBlockLineLayout.cpp
and from RenderStyle::changeAffectsVisualOverflow(). The former case passes in the full
InlineTextBox and the latter case just passes in a RenderStyle (because the InlineTextBox
is unavailable.)

This patch also modifies RenderTableSection::spannedColumns() to fix an off-by-one error
that was causing table borders to not be drawn when they existed right on the edge of
a repaint rect.

Tests: fast/css3-text/css3-text-decoration/repaint/underline-outside-of-layout-rect.html
Tests: fast/repaint/border-collapse-table-off-by-one-expected.html

* WebCore.vcxproj/WebCore.vcxproj: Adding reference to new InlineTextBoxStyle.cpp file
* WebCore.vcxproj/WebCore.vcxproj.filters: Adding reference to new InlineTextBoxStyle files
* WebCore.xcodeproj/project.pbxproj: Adding reference to new InlineTextBoxStyle files
* rendering/InlineTextBox.cpp:
(WebCore::computeUnderlineOffset): Moved to InlineTextBox.cpp
(WebCore::getWavyStrokeParameters): Moved to InlineTextBox.cpp
(WebCore::InlineTextBox::paintDecoration): Update to use newly refactored functions
* rendering/RenderBlockLineLayout.cpp:
(WebCore::setLogicalWidthForTextRun): Call visualOverflowForDecorations()
* rendering/RenderTableSection.cpp:
* rendering/RenderTableSelection.cpp: Fix off-by-one error when the boundary of a repaint
rect lies exactly on top of a table column position
* rendering/style/RenderStyle.cpp:
(WebCore::RenderStyle::changeAffectsVisualOverflow): Inspects shadows and text decorations
(WebCore::RenderStyle::changeRequiresLayout): Calls changeAffectsVisualOverflow()
(WebCore::RenderStyle::changeRequiresRepaintIfTextOrBorderOrOutline): Moved code from here
to changeAffectsVisualOverflow().
* rendering/style/RenderStyle.h: Function signature
* style/InlineTextBoxStyle.cpp: Added.
(WebCore::computeUnderlineOffset): Moved from InlineTextBox.cpp
(WebCore::getWavyStrokeParameters): Moved from InlineTextBox.cpp
(WebCore::extendIntToFloat): Convenience function for dealing with the fact that
underline bounding boxes use floats and GlyphOverflow uses ints
(WebCore::visualOverflowForDecorations): Given
vertical overflow bounds, possibly extend those to include location of
decorations.
* style/InlineTextBoxStyle.h: Added. Function signatures.
(WebCore::textDecorationStrokeThickness): Refactored from InlineTextBox.cpp
(WebCore::wavyOffsetFromDecoration): Refactored from InlineTextBox.cpp
* platform/graphics/Font.h:
(WebCore::GlyphOverflow::isEmpty): Convenience function
(WebCore::GlyphOverflow::extendTo): Convenience function

LayoutTests:
This first test makes sure that repaint rects are extended to include text decorations that may
lie outside of the text layout rects. It compares text with an underline to text that has
had underline applied to it in a timer.

The second test triggers an off-by-one error in collapsed table borders where a border was not
being drawn if it lay right on top of a repaint rect.

* fast/css3-text/css3-text-decoration/repaint/resources/Litherum.svg: Added. This font has a
descent of 0 (so it will not intersect underlines).
* fast/css3-text/css3-text-decoration/repaint/underline-outside-of-layout-rect-expected.html: Added.
Apply the underline without any timeout.
* fast/css3-text/css3-text-decoration/repaint/underline-outside-of-layout-rect.html: Added.
Apply the underline with a timeout.
* fast/css3-text/css3-text-decoration/repaint/underline-outside-of-layout-rect-removed-expected.html: Added.
Draw text without decorations.
* fast/css3-text/css3-text-decoration/repaint/underline-outside-of-layout-rect-removed.html: Added.
Draw text with decorations which contribute to overflow, then remove those decorations on a timer.
* fast/css3-text/css3-text-decoration/repaint/underline-outside-of-layout-rect-altered-expected.html: Added.
Draw text as if the final state of the decorations had always existed.
* fast/css3-text/css3-text-decoration/repaint/underline-outside-of-layout-rect-altered.html: Added.
Make sure that modifying decorations that contribute to overflow redraw properly.
* fast/repaint/border-collapse-table-off-by-one-expected.html: Added.
* fast/repaint/border-collapse-table-off-by-one.html: Added. Trigger the off-by-one error in
RenderTableSection.cpp

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@169089 268f45cc-cd09-0410-ab3c-d52691b4dbfc

24 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/css3-text/css3-text-decoration/repaint/resources/Litherum.svg [new file with mode: 0644]
LayoutTests/fast/css3-text/css3-text-decoration/repaint/underline-outside-of-layout-rect-altered-expected.html [new file with mode: 0644]
LayoutTests/fast/css3-text/css3-text-decoration/repaint/underline-outside-of-layout-rect-altered.html [new file with mode: 0644]
LayoutTests/fast/css3-text/css3-text-decoration/repaint/underline-outside-of-layout-rect-expected.html [new file with mode: 0644]
LayoutTests/fast/css3-text/css3-text-decoration/repaint/underline-outside-of-layout-rect-removed-expected.html [new file with mode: 0644]
LayoutTests/fast/css3-text/css3-text-decoration/repaint/underline-outside-of-layout-rect-removed.html [new file with mode: 0644]
LayoutTests/fast/css3-text/css3-text-decoration/repaint/underline-outside-of-layout-rect.html [new file with mode: 0644]
LayoutTests/fast/repaint/border-collapse-table-off-by-one-expected.html [new file with mode: 0644]
LayoutTests/fast/repaint/border-collapse-table-off-by-one.html [new file with mode: 0644]
Source/WebCore/CMakeLists.txt
Source/WebCore/ChangeLog
Source/WebCore/WebCore.vcxproj/WebCore.vcxproj
Source/WebCore/WebCore.vcxproj/WebCore.vcxproj.filters
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/platform/graphics/Font.h
Source/WebCore/rendering/InlineTextBox.cpp
Source/WebCore/rendering/RenderBlockLineLayout.cpp
Source/WebCore/rendering/RenderTableSection.cpp
Source/WebCore/rendering/RenderTableSection.h
Source/WebCore/rendering/style/RenderStyle.cpp
Source/WebCore/rendering/style/RenderStyle.h
Source/WebCore/style/InlineTextBoxStyle.cpp [new file with mode: 0644]
Source/WebCore/style/InlineTextBoxStyle.h [new file with mode: 0644]

index 9f0a7f7..9644723 100644 (file)
@@ -1,3 +1,35 @@
+2014-05-19  Myles C. Maxfield  <litherum@gmail.com>
+
+        Text decorations do not contribute to visual overflow
+        https://bugs.webkit.org/show_bug.cgi?id=132773
+
+        Reviewed by Antti Koivisto.
+
+        This first test makes sure that repaint rects are extended to include text decorations that may
+        lie outside of the text layout rects. It compares text with an underline to text that has
+        had underline applied to it in a timer.
+
+        The second test triggers an off-by-one error in collapsed table borders where a border was not
+        being drawn if it lay right on top of a repaint rect.
+
+        * fast/css3-text/css3-text-decoration/repaint/resources/Litherum.svg: Added. This font has a
+        descent of 0 (so it will not intersect underlines).
+        * fast/css3-text/css3-text-decoration/repaint/underline-outside-of-layout-rect-expected.html: Added.
+        Apply the underline without any timeout.
+        * fast/css3-text/css3-text-decoration/repaint/underline-outside-of-layout-rect.html: Added.
+        Apply the underline with a timeout.
+        * fast/css3-text/css3-text-decoration/repaint/underline-outside-of-layout-rect-removed-expected.html: Added.
+        Draw text without decorations.
+        * fast/css3-text/css3-text-decoration/repaint/underline-outside-of-layout-rect-removed.html: Added.
+        Draw text with decorations which contribute to overflow, then remove those decorations on a timer.
+        * fast/css3-text/css3-text-decoration/repaint/underline-outside-of-layout-rect-altered-expected.html: Added.
+        Draw text as if the final state of the decorations had always existed.
+        * fast/css3-text/css3-text-decoration/repaint/underline-outside-of-layout-rect-altered.html: Added.
+        Make sure that modifying decorations that contribute to overflow redraw properly.
+        * fast/repaint/border-collapse-table-off-by-one-expected.html: Added.
+        * fast/repaint/border-collapse-table-off-by-one.html: Added. Trigger the off-by-one error in
+        RenderTableSection.cpp
+
 2014-05-19  Chris Fleizach  <cfleizach@apple.com>
 
         AX: VoiceOver sees the WebArea out of order when topContentInset is used
diff --git a/LayoutTests/fast/css3-text/css3-text-decoration/repaint/resources/Litherum.svg b/LayoutTests/fast/css3-text/css3-text-decoration/repaint/resources/Litherum.svg
new file mode 100644 (file)
index 0000000..c3548e5
--- /dev/null
@@ -0,0 +1,12 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
+<svg xmlns="http://www.w3.org/2000/svg">
+<metadata></metadata>
+<defs>
+<font id="Litherum" horiz-adv-x="1024">
+<font-face units-per-em="14" ascent="14" descent="0"/>
+<glyph unicode="|" horiz-adv-x="14" d="M0 0v14h14v-14z"/>
+<glyph unicode="-" horiz-adv-x="266" d="M0 0v14h266v-14z"/>
+</font>
+</defs>
+</svg>
diff --git a/LayoutTests/fast/css3-text/css3-text-decoration/repaint/underline-outside-of-layout-rect-altered-expected.html b/LayoutTests/fast/css3-text/css3-text-decoration/repaint/underline-outside-of-layout-rect-altered-expected.html
new file mode 100644 (file)
index 0000000..a1e88c1
--- /dev/null
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+@font-face {
+    font-family: 'Litherum';
+    src: url("./resources/Litherum.svg") format(svg)
+}
+div {
+    font-family: 'Litherum';
+    -webkit-text-decoration-skip: none;
+    -webkit-text-decoration-color: green;
+    width: 1px;
+    height: 1px;
+}
+</style>
+</head>
+<body>
+This test makes sure that underlines contribute to visual overflow properly.
+The SVG font has a descent of 0, which means that underlines will
+lie outside of the glyph boundaries. The test makes sure of two things:
+<ol>
+<li>(Left box) That moving a decoration that contributes to overflow from
+the top of a line to the bottom is redrawn as if it was always on the bottom
+side</li>
+<li>(Right box) That moving a decoration that contributes to overflow to a position where
+it does not contribute to overflow anymore redraws as if it had never
+contributed to overflow</li>
+</ol>
+<div style="position: relative; height: 45px;">
+<div id="wavyoverline" style="font-size: 100px; position: absolute; left: 60px; top: 20px; text-decoration: underline; -webkit-text-underline-position: under">|</div>
+<div id="underline" style="font-size: 100px; position: absolute; left: 180px; top: 20px; text-decoration: line-through;">|</div>
+</div>
+</body>
+</html>
diff --git a/LayoutTests/fast/css3-text/css3-text-decoration/repaint/underline-outside-of-layout-rect-altered.html b/LayoutTests/fast/css3-text/css3-text-decoration/repaint/underline-outside-of-layout-rect-altered.html
new file mode 100644 (file)
index 0000000..1bf9973
--- /dev/null
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+@font-face {
+    font-family: 'Litherum';
+    src: url("./resources/Litherum.svg") format(svg)
+}
+div {
+    font-family: 'Litherum';
+    -webkit-text-decoration-skip: none;
+    -webkit-text-decoration-color: green;
+    width: 1px;
+    height: 1px;
+}
+</style>
+</head>
+<body>
+This test makes sure that underlines contribute to visual overflow properly.
+The SVG font has a descent of 0, which means that underlines will
+lie outside of the glyph boundaries. The test makes sure of two things:
+<ol>
+<li>(Left box) That moving a decoration that contributes to overflow from
+the top of a line to the bottom is redrawn as if it was always on the bottom
+side</li>
+<li>(Right box) That moving a decoration that contributes to overflow to a position where
+it does not contribute to overflow anymore redraws as if it had never
+contributed to overflow</li>
+</ol>
+<div style="position: relative; height: 45px;">
+<div id="wavyoverline" style="font-size: 100px; position: absolute; left: 60px; top: 20px; -webkit-text-decoration-style: wavy; text-decoration: overline; -webkit-text-underline-position: under">|</div>
+<div id="underline" style="font-size: 100px; position: absolute; left: 180px; top: 20px; text-decoration: underline; -webkit-text-underline-position: under">|</div>
+</div>
+<script>
+function applyUnderline() {
+    document.getElementById('wavyoverline').style.textDecoration = "underline";
+    document.getElementById('wavyoverline').style.webkitTextDecorationStyle = "solid";
+    document.getElementById('underline').style.textDecoration = "line-through";
+    if (window.testRunner)
+        testRunner.notifyDone();
+}
+
+if (window.testRunner)
+    testRunner.waitUntilDone();
+window.setTimeout(applyUnderline, 17);
+</script>
+</body>
+</html>
diff --git a/LayoutTests/fast/css3-text/css3-text-decoration/repaint/underline-outside-of-layout-rect-expected.html b/LayoutTests/fast/css3-text/css3-text-decoration/repaint/underline-outside-of-layout-rect-expected.html
new file mode 100644 (file)
index 0000000..283e897
--- /dev/null
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+@font-face {
+    font-family: 'Litherum';
+    src: url("./resources/Litherum.svg") format(svg)
+}
+div {
+    font-family: 'Litherum';
+    -webkit-text-decoration-skip: none;
+    -webkit-text-decoration-color: green;
+    width: 1px;
+    height: 1px;
+}
+</style>
+</head>
+<body>
+This test makes sure that underlines contribute to visual overflow.
+The SVG font has a descent of 0, which means that underlines will
+lie outside of the glyph boundaries. The test makes sure that
+drawing underlined text ends up the same as drawing text that has
+had the underline retroactively applied to it with javascript.
+<div style="position: relative; height: 45px;">
+<div id="underline" style="position: absolute; left: 0px; top: 0px; text-decoration: underline;">|</div>
+<div id="underlineunder" style="position: absolute; left: 20px; top: 0px; -webkit-text-underline-position: under; text-decoration: underline">|</div>
+<div id="linethrough" style="position: absolute; left: 40px; top: 0px; text-decoration: line-through;">|</div>
+<div id="overline" style="position: absolute; left: 60px; top: 0px; text-decoration: overline;">|</div>
+
+<div id="wavyunderline" style="position: absolute; left: 0px; top: 20px; -webkit-text-decoration-style: wavy; text-decoration: underline;">|</div>
+<div id="wavyunderlineunder" style="position: absolute; left: 20px; top: 20px; -webkit-text-underline-position: under; -webkit-text-decoration-style: wavy; text-decoration: underline;">|</div>
+<div id="wavylinethrough" style="font-size: 1px; position: absolute; left: 40px; top: 20px; -webkit-text-decoration-style: wavy; text-decoration: line-through;">-</div>
+<div id="wavyoverline" style="position: absolute; left: 60px; top: 20px; -webkit-text-decoration-style: wavy; text-decoration: overline;">|</div>
+</div>
+</body>
+</html>
diff --git a/LayoutTests/fast/css3-text/css3-text-decoration/repaint/underline-outside-of-layout-rect-removed-expected.html b/LayoutTests/fast/css3-text/css3-text-decoration/repaint/underline-outside-of-layout-rect-removed-expected.html
new file mode 100644 (file)
index 0000000..493ade5
--- /dev/null
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+@font-face {
+    font-family: 'Litherum';
+    src: url("./resources/Litherum.svg") format(svg)
+}
+div {
+    font-family: 'Litherum';
+    -webkit-text-decoration-skip: none;
+    -webkit-text-decoration-color: green;
+    width: 1px;
+    height: 1px;
+}
+</style>
+</head>
+<body>
+This test makes sure that underlines contribute to visual overflow properly.
+The SVG font has a descent of 0, which means that underlines will
+lie outside of the glyph boundaries. The test makes sure that
+removing decorations which contribute to visual overflow matches how the
+text would have been drawn as if the decorations had never existed.
+<div style="position: relative; height: 45px;">
+<div id="underline" style="position: absolute; left: 0px; top: 0px;">|</div>
+<div id="underlineunder" style="position: absolute; left: 20px; top: 0px; -webkit-text-underline-position: under;">|</div>
+<div id="linethrough" style="position: absolute; left: 40px; top: 0px;">|</div>
+<div id="overline" style="position: absolute; left: 60px; top: 0px;">|</div>
+
+<div id="wavyunderline" style="position: absolute; left: 0px; top: 20px; -webkit-text-decoration-style: wavy;">|</div>
+<div id="wavyunderlineunder" style="position: absolute; left: 20px; top: 20px; -webkit-text-underline-position: under; -webkit-text-decoration-style: wavy;">|</div>
+<div id="wavylinethrough" style="font-size: 1px; position: absolute; left: 40px; top: 20px; -webkit-text-decoration-style: wavy;">-</div>
+<div id="wavyoverline" style="position: absolute; left: 60px; top: 20px; -webkit-text-decoration-style: wavy;">|</div>
+</div>
+</body>
+</html>
diff --git a/LayoutTests/fast/css3-text/css3-text-decoration/repaint/underline-outside-of-layout-rect-removed.html b/LayoutTests/fast/css3-text/css3-text-decoration/repaint/underline-outside-of-layout-rect-removed.html
new file mode 100644 (file)
index 0000000..c55c841
--- /dev/null
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+@font-face {
+    font-family: 'Litherum';
+    src: url("./resources/Litherum.svg") format(svg)
+}
+div {
+    font-family: 'Litherum';
+    -webkit-text-decoration-skip: none;
+    -webkit-text-decoration-color: green;
+    width: 1px;
+    height: 1px;
+}
+</style>
+</head>
+<body>
+This test makes sure that underlines contribute to visual overflow properly.
+The SVG font has a descent of 0, which means that underlines will
+lie outside of the glyph boundaries. The test makes sure that
+removing decorations which contribute to visual overflow matches how the
+text would have been drawn as if the decorations had never existed.
+<div style="position: relative; height: 45px;">
+<div id="underline" style="position: absolute; left: 0px; top: 0px; text-decoration: underline;">|</div>
+<div id="underlineunder" style="position: absolute; left: 20px; top: 0px; -webkit-text-underline-position: under; text-decoration: underline">|</div>
+<div id="linethrough" style="position: absolute; left: 40px; top: 0px; text-decoration: line-through;">|</div>
+<div id="overline" style="position: absolute; left: 60px; top: 0px; text-decoration: overline;">|</div>
+
+<div id="wavyunderline" style="position: absolute; left: 0px; top: 20px; -webkit-text-decoration-style: wavy; text-decoration: underline;">|</div>
+<div id="wavyunderlineunder" style="position: absolute; left: 20px; top: 20px; -webkit-text-underline-position: under; -webkit-text-decoration-style: wavy; text-decoration: underline;">|</div>
+<div id="wavylinethrough" style="font-size: 1px; position: absolute; left: 40px; top: 20px; -webkit-text-decoration-style: wavy; text-decoration: line-through;">-</div>
+<div id="wavyoverline" style="position: absolute; left: 60px; top: 20px; -webkit-text-decoration-style: wavy; text-decoration: overline;">|</div>
+</div>
+<script>
+function applyUnderline() {
+    document.getElementById('underline').style.textDecoration = "none";
+    document.getElementById('underlineunder').style.textDecoration = "none";
+    document.getElementById('linethrough').style.textDecoration = "none";
+    document.getElementById('overline').style.textDecoration = "none";
+    document.getElementById('wavyunderline').style.textDecoration = "none";
+    document.getElementById('wavyunderlineunder').style.textDecoration = "none";
+    document.getElementById('wavylinethrough').style.textDecoration = "none";
+    document.getElementById('wavyoverline').style.textDecoration = "none";
+    if (window.testRunner)
+        testRunner.notifyDone();
+}
+
+if (window.testRunner)
+    testRunner.waitUntilDone();
+window.setTimeout(applyUnderline, 17);
+</script>
+</body>
+</html>
diff --git a/LayoutTests/fast/css3-text/css3-text-decoration/repaint/underline-outside-of-layout-rect.html b/LayoutTests/fast/css3-text/css3-text-decoration/repaint/underline-outside-of-layout-rect.html
new file mode 100644 (file)
index 0000000..00e0166
--- /dev/null
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+@font-face {
+    font-family: 'Litherum';
+    src: url("./resources/Litherum.svg") format(svg)
+}
+div {
+    font-family: 'Litherum';
+    -webkit-text-decoration-skip: none;
+    -webkit-text-decoration-color: green;
+    width: 1px;
+    height: 1px;
+}
+</style>
+</head>
+<body>
+This test makes sure that underlines contribute to visual overflow.
+The SVG font has a descent of 0, which means that underlines will
+lie outside of the glyph boundaries. The test makes sure that
+drawing underlined text ends up the same as drawing text that has
+had the underline retroactively applied to it with javascript.
+<div style="position: relative; height: 45px;">
+<div id="underline" style="position: absolute; left: 0px; top: 0px;">|</div>
+<div id="underlineunder" style="position: absolute; left: 20px; top: 0px; -webkit-text-underline-position: under;">|</div>
+<div id="linethrough" style="position: absolute; left: 40px; top: 0px;">|</div>
+<div id="overline" style="position: absolute; left: 60px; top: 0px;">|</div>
+
+<div id="wavyunderline" style="position: absolute; left: 0px; top: 20px; -webkit-text-decoration-style: wavy;">|</div>
+<div id="wavyunderlineunder" style="position: absolute; left: 20px; top: 20px; -webkit-text-underline-position: under; -webkit-text-decoration-style: wavy;">|</div>
+<div id="wavylinethrough" style="font-size: 1px; position: absolute; left: 40px; top: 20px; -webkit-text-decoration-style: wavy;">-</div>
+<div id="wavyoverline" style="position: absolute; left: 60px; top: 20px; -webkit-text-decoration-style: wavy;">|</div>
+</div>
+<script>
+function applyUnderline() {
+    document.getElementById('underline').style.textDecoration = "underline";
+    document.getElementById('underlineunder').style.textDecoration = "underline";
+    document.getElementById('linethrough').style.textDecoration = "line-through";
+    document.getElementById('overline').style.textDecoration = "overline";
+    document.getElementById('wavyunderline').style.textDecoration = "underline";
+    document.getElementById('wavyunderlineunder').style.textDecoration = "underline";
+    document.getElementById('wavylinethrough').style.textDecoration = "line-through";
+    document.getElementById('wavyoverline').style.textDecoration = "overline";
+    if (window.testRunner)
+        testRunner.notifyDone();
+}
+
+if (window.testRunner)
+    testRunner.waitUntilDone();
+window.setTimeout(applyUnderline, 17);
+</script>
+</body>
+</html>
diff --git a/LayoutTests/fast/repaint/border-collapse-table-off-by-one-expected.html b/LayoutTests/fast/repaint/border-collapse-table-off-by-one-expected.html
new file mode 100644 (file)
index 0000000..f488681
--- /dev/null
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+table {
+    border-collapse: collapse;
+}
+a:link,
+a:visited,
+a:link:hover,
+a:visited:hover {
+    text-decoration: underline;
+}
+</style>
+</head>
+<body>
+This next test makes sure that grown repaint rects interact nicely with table border-collapse:collapse.
+<table border="1">
+<tbody>
+<tr>
+<td>C</td>
+<td><a id="l" href="http://www.apple.com/">1</a></td>
+</tr>
+</tbody>
+</table>
+</body>
+</html>
diff --git a/LayoutTests/fast/repaint/border-collapse-table-off-by-one.html b/LayoutTests/fast/repaint/border-collapse-table-off-by-one.html
new file mode 100644 (file)
index 0000000..4a1d057
--- /dev/null
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+table {
+    border-collapse: collapse;
+}
+a:link,
+a:visited,
+a:link:hover,
+a:visited:hover {
+    text-decoration: none;
+}
+</style>
+</head>
+<body>
+This next test makes sure that grown repaint rects interact nicely with table border-collapse:collapse.
+<table border="1">
+<tbody>
+<tr>
+<td>C</td>
+<td><a id="l" href="http://www.apple.com/">1</a></td>
+</tr>
+</tbody>
+</table>
+<script>
+function applyUnderline() {
+    document.getElementById('l').style.textDecoration = "underline";
+    if (window.testRunner)
+        testRunner.notifyDone();
+}
+
+if (window.testRunner)
+    testRunner.waitUntilDone();
+window.setTimeout(applyUnderline, 17);
+</script>
+</body>
+</html>
index 1628695..647ce59 100644 (file)
@@ -2477,6 +2477,7 @@ set(WebCore_SOURCES
     storage/StorageThread.cpp
     storage/StorageTracker.cpp
 
+    style/InlineTextBoxStyle.cpp
     style/StyleFontSizeFunctions.cpp
     style/StyleResolveForDocument.cpp
     style/StyleResolveTree.cpp
index edbe6de..29cde5b 100644 (file)
@@ -1,3 +1,60 @@
+2014-05-19  Myles C. Maxfield  <litherum@gmail.com>
+
+        Text decorations do not contribute to visual overflow
+        https://bugs.webkit.org/show_bug.cgi?id=132773
+
+        Reviewed by Antti Koivisto.
+
+        This patch creates a function, visualOverflowForDecorations, which computes
+        how much visual overflow to add around a text box due to text decorations. Most of the time,
+        text decorations are fully contained within the text box, so the result is usually 0.
+
+        This function exists within style/InlineTextBoxStyle.cpp, which is an added file. This is
+        so that it can be called from setLogicalWidthForTextRun() inside RenderBlockLineLayout.cpp
+        and from RenderStyle::changeAffectsVisualOverflow(). The former case passes in the full
+        InlineTextBox and the latter case just passes in a RenderStyle (because the InlineTextBox
+        is unavailable.)
+
+        This patch also modifies RenderTableSection::spannedColumns() to fix an off-by-one error
+        that was causing table borders to not be drawn when they existed right on the edge of
+        a repaint rect.
+
+        Tests: fast/css3-text/css3-text-decoration/repaint/underline-outside-of-layout-rect.html
+        Tests: fast/repaint/border-collapse-table-off-by-one-expected.html
+
+        * WebCore.vcxproj/WebCore.vcxproj: Adding reference to new InlineTextBoxStyle.cpp file
+        * WebCore.vcxproj/WebCore.vcxproj.filters: Adding reference to new InlineTextBoxStyle files
+        * WebCore.xcodeproj/project.pbxproj: Adding reference to new InlineTextBoxStyle files
+        * rendering/InlineTextBox.cpp:
+        (WebCore::computeUnderlineOffset): Moved to InlineTextBox.cpp
+        (WebCore::getWavyStrokeParameters): Moved to InlineTextBox.cpp
+        (WebCore::InlineTextBox::paintDecoration): Update to use newly refactored functions
+        * rendering/RenderBlockLineLayout.cpp:
+        (WebCore::setLogicalWidthForTextRun): Call visualOverflowForDecorations()
+        * rendering/RenderTableSection.cpp:
+        * rendering/RenderTableSelection.cpp: Fix off-by-one error when the boundary of a repaint
+        rect lies exactly on top of a table column position
+        * rendering/style/RenderStyle.cpp:
+        (WebCore::RenderStyle::changeAffectsVisualOverflow): Inspects shadows and text decorations
+        (WebCore::RenderStyle::changeRequiresLayout): Calls changeAffectsVisualOverflow()
+        (WebCore::RenderStyle::changeRequiresRepaintIfTextOrBorderOrOutline): Moved code from here
+        to changeAffectsVisualOverflow().
+        * rendering/style/RenderStyle.h: Function signature
+        * style/InlineTextBoxStyle.cpp: Added.
+        (WebCore::computeUnderlineOffset): Moved from InlineTextBox.cpp
+        (WebCore::getWavyStrokeParameters): Moved from InlineTextBox.cpp
+        (WebCore::extendIntToFloat): Convenience function for dealing with the fact that
+        underline bounding boxes use floats and GlyphOverflow uses ints
+        (WebCore::visualOverflowForDecorations): Given
+        vertical overflow bounds, possibly extend those to include location of
+        decorations.
+        * style/InlineTextBoxStyle.h: Added. Function signatures.
+        (WebCore::textDecorationStrokeThickness): Refactored from InlineTextBox.cpp
+        (WebCore::wavyOffsetFromDecoration): Refactored from InlineTextBox.cpp
+        * platform/graphics/Font.h:
+        (WebCore::GlyphOverflow::isEmpty): Convenience function
+        (WebCore::GlyphOverflow::extendTo): Convenience function
+
 2014-05-19  Alex Christensen  <achristensen@webkit.org>
 
         Unreviewed build fix after r169082
index 27bad29..6fe6622 100644 (file)
     <ClCompile Include="..\storage\StorageSyncManager.cpp" />
     <ClCompile Include="..\storage\StorageThread.cpp" />
     <ClCompile Include="..\storage\StorageTracker.cpp" />
+    <ClCompile Include="..\style\InlineTextBoxStyle.cpp" />
     <ClCompile Include="..\style\StyleFontSizeFunctions.cpp" />
     <ClCompile Include="..\style\StyleResolveForDocument.cpp" />
     <ClCompile Include="..\style\StyleResolveTree.cpp" />
     <ClInclude Include="..\storage\StorageSyncManager.h" />
     <ClInclude Include="..\storage\StorageThread.h" />
     <ClInclude Include="..\storage\StorageTracker.h" />
+    <ClInclude Include="..\style\InlineTextBoxStyle.h" />
     <ClInclude Include="..\style\StyleFontSizeFunctions.h" />
     <ClInclude Include="..\style\StyleResolveForDocument.h" />
     <ClInclude Include="..\style\StyleResolveTree.h" />
index 4aaab8b..b434ee1 100644 (file)
     <ClCompile Include="..\dom\InlineStyleSheetOwner.cpp">
       <Filter>dom</Filter>
     </ClCompile>
+    <ClCompile Include="..\style\InlineTextBoxStyle.cpp">
+      <Filter>css</Filter>
+    </ClCompile>
     <ClCompile Include="..\style\StyleFontSizeFunctions.cpp">
       <Filter>css</Filter>
     </ClCompile>
     <ClInclude Include="..\style\StyleResolveTree.h">
       <Filter>css</Filter>
     </ClInclude>
+    <ClInclude Include="..\style\InlineTextBoxStyle.h">
+      <Filter>css</Filter>
+    </ClInclude>
     <ClInclude Include="..\style\StyleFontSizeFunctions.h">
       <Filter>css</Filter>
     </ClInclude>
index bfe885d..f08f044 100644 (file)
                1AF8E1C3125673E000230FF7 /* ProxyServerCFNet.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1AF8E1C1125673E000230FF7 /* ProxyServerCFNet.cpp */; };
                1AFE11990CBFFCC4003017FA /* JSSQLResultSetRowList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1AFE11970CBFFCC4003017FA /* JSSQLResultSetRowList.cpp */; };
                1AFE119A0CBFFCC4003017FA /* JSSQLResultSetRowList.h in Headers */ = {isa = PBXBuildFile; fileRef = 1AFE11980CBFFCC4003017FA /* JSSQLResultSetRowList.h */; };
+               1C010700192594DF008A4201 /* InlineTextBoxStyle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C0106FE192594DF008A4201 /* InlineTextBoxStyle.cpp */; };
+               1C010701192594DF008A4201 /* InlineTextBoxStyle.h in Headers */ = {isa = PBXBuildFile; fileRef = 1C0106FF192594DF008A4201 /* InlineTextBoxStyle.h */; };
                1C11CCB50AA6093700DADB20 /* DOMNotation.h in Copy Generated Headers */ = {isa = PBXBuildFile; fileRef = 85CA96E80A9624E900690CCF /* DOMNotation.h */; };
                1C11CCB60AA6093700DADB20 /* DOMComment.h in Copy Generated Headers */ = {isa = PBXBuildFile; fileRef = 85089CD10A98C42700A275AA /* DOMComment.h */; };
                1C11CCB70AA6093700DADB20 /* DOMNamedNodeMap.h in Copy Generated Headers */ = {isa = PBXBuildFile; fileRef = 8518DD760A9CF31B0091B7A6 /* DOMNamedNodeMap.h */; };
                1AF8E1C1125673E000230FF7 /* ProxyServerCFNet.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ProxyServerCFNet.cpp; sourceTree = "<group>"; };
                1AFE11970CBFFCC4003017FA /* JSSQLResultSetRowList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSSQLResultSetRowList.cpp; sourceTree = "<group>"; };
                1AFE11980CBFFCC4003017FA /* JSSQLResultSetRowList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSSQLResultSetRowList.h; sourceTree = "<group>"; };
+               1C0106FE192594DF008A4201 /* InlineTextBoxStyle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InlineTextBoxStyle.cpp; sourceTree = "<group>"; };
+               1C0106FF192594DF008A4201 /* InlineTextBoxStyle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InlineTextBoxStyle.h; sourceTree = "<group>"; };
                1C18DA56181AF6A500C4EF22 /* TextPainter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TextPainter.cpp; sourceTree = "<group>"; };
                1C18DA57181AF6A500C4EF22 /* TextPainter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TextPainter.h; sourceTree = "<group>"; };
                1C21E57A183ED1FF001C289D /* IOSurfacePool.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = IOSurfacePool.cpp; path = ../cg/IOSurfacePool.cpp; sourceTree = "<group>"; };
                                E4D58EB317B4DBDC00CBDCA8 /* StyleResolveForDocument.h */,
                                E4DEAA1517A93DC3000E0430 /* StyleResolveTree.cpp */,
                                E4DEAA1617A93DC3000E0430 /* StyleResolveTree.h */,
+                               1C0106FE192594DF008A4201 /* InlineTextBoxStyle.cpp */,
+                               1C0106FF192594DF008A4201 /* InlineTextBoxStyle.h */,
                        );
                        path = style;
                        sourceTree = "<group>";
                                E1C36D350EB0A094007410BC /* JSWorkerGlobalScopeBase.h in Headers */,
                                E1C362EF0EAF2AA9007410BC /* JSWorkerLocation.h in Headers */,
                                E1271A580EEECDE400F61213 /* JSWorkerNavigator.h in Headers */,
+                               1C010701192594DF008A4201 /* InlineTextBoxStyle.h in Headers */,
                                BC348BD40DB7F804004ABAB9 /* JSXMLHttpRequest.h in Headers */,
                                BC60DA3A0D2A302800B9918F /* JSXMLHttpRequestException.h in Headers */,
                                F916C48E0DB510F80076CD83 /* JSXMLHttpRequestProgressEvent.h in Headers */,
                                B2FA3D380AB75A6F000E5AC4 /* JSSVGAnimatedAngle.cpp in Sources */,
                                B2FA3D3A0AB75A6F000E5AC4 /* JSSVGAnimatedBoolean.cpp in Sources */,
                                B2FA3D3C0AB75A6F000E5AC4 /* JSSVGAnimatedEnumeration.cpp in Sources */,
+                               1C010700192594DF008A4201 /* InlineTextBoxStyle.cpp in Sources */,
                                B2FA3D3E0AB75A6F000E5AC4 /* JSSVGAnimatedInteger.cpp in Sources */,
                                B2FA3D400AB75A6F000E5AC4 /* JSSVGAnimatedLength.cpp in Sources */,
                                B2FA3D420AB75A6F000E5AC4 /* JSSVGAnimatedLengthList.cpp in Sources */,
index 0da5846..fcdd68f 100644 (file)
@@ -69,6 +69,24 @@ struct GlyphOverflow {
     {
     }
 
+    inline bool isEmpty()
+    {
+        return !left && !right && !top && !bottom;
+    }
+
+    inline void extendTo(const GlyphOverflow& other)
+    {
+        left = std::max(left, other.left);
+        right = std::max(right, other.right);
+        top = std::max(top, other.top);
+        bottom = std::max(bottom, other.bottom);
+    }
+
+    bool operator!=(const GlyphOverflow& other)
+    {
+        return left != other.left || right != other.right || top != other.top || bottom != other.bottom;
+    }
+
     int left;
     int right;
     int top;
index 42fc17a..aa44dce 100644 (file)
@@ -35,6 +35,7 @@
 #include "GraphicsContext.h"
 #include "HitTestResult.h"
 #include "ImageBuffer.h"
+#include "InlineTextBoxStyle.h"
 #include "Page.h"
 #include "PaintInfo.h"
 #include "RenderedDocumentMarker.h"
@@ -821,29 +822,6 @@ static StrokeStyle textDecorationStyleToStrokeStyle(TextDecorationStyle decorati
     return strokeStyle;
 }
 
-static int computeUnderlineOffset(const TextUnderlinePosition underlinePosition, const FontMetrics& fontMetrics, const InlineTextBox* inlineTextBox, const int textDecorationThickness)
-{
-    // Compute the gap between the font and the underline. Use at least one
-    // pixel gap, if underline is thick then use a bigger gap.
-    const int gap = std::max<int>(1, ceilf(textDecorationThickness / 2.0));
-
-    // According to the specification TextUnderlinePositionAuto should default to 'alphabetic' for horizontal text
-    // and to 'under Left' for vertical text (e.g. japanese). We support only horizontal text for now.
-    switch (underlinePosition) {
-    case TextUnderlinePositionAlphabetic:
-    case TextUnderlinePositionAuto:
-        return fontMetrics.ascent() + gap; // Position underline near the alphabetic baseline.
-    case TextUnderlinePositionUnder: {
-        // Position underline relative to the under edge of the lowest element's content box.
-        const float offset = inlineTextBox->root().maxLogicalTop() - inlineTextBox->logicalTop();
-        return inlineTextBox->logicalHeight() + gap + std::max<float>(offset, 0);
-    }
-    }
-
-    ASSERT_NOT_REACHED();
-    return fontMetrics.ascent() + gap;
-}
-
 static void adjustStepToDecorationLength(float& step, float& controlPointDistance, float length)
 {
     ASSERT(step > 0);
@@ -862,21 +840,6 @@ static void adjustStepToDecorationLength(float& step, float& controlPointDistanc
     controlPointDistance += adjustment;
 }
 
-static void getWavyStrokeParameters(float strokeThickness, float& controlPointDistance, float& step)
-{
-    // Distance between decoration's axis and Bezier curve's control points.
-    // The height of the curve is based on this distance. Use a minimum of 6 pixels distance since
-    // the actual curve passes approximately at half of that distance, that is 3 pixels.
-    // The minimum height of the curve is also approximately 3 pixels. Increases the curve's height
-    // as strockThickness increases to make the curve looks better.
-    controlPointDistance = 3 * std::max<float>(2, strokeThickness);
-
-    // Increment used to form the diamond shape between start point (p1), control
-    // points and end point (p2) along the axis of the decoration. Makes the
-    // curve wider as strockThickness increases to make the curve looks better.
-    step = 2 * std::max<float>(2, strokeThickness);
-}
-
 /*
  * Draw one cubic Bezier curve and repeat the same pattern long the the decoration's axis.
  * The start point (p1), controlPoint1, controlPoint2 and end point (p2) of the Bezier curve
@@ -1000,8 +963,7 @@ void InlineTextBox::paintDecoration(GraphicsContext& context, const FloatPoint&
     // Use a special function for underlines to get the positioning exactly right.
     bool isPrinting = renderer().document().printing();
 
-    const float textDecorationBaseFontSize = 16;
-    float textDecorationThickness = renderer().style().fontSize() / textDecorationBaseFontSize;
+    float textDecorationThickness = textDecorationStrokeThickness(renderer().style().fontSize());
     context.setStrokeThickness(textDecorationThickness);
 
     bool linesAreOpaque = !isPrinting && (!(decoration & TextDecorationUnderline) || underline.alpha() == 255) && (!(decoration & TextDecorationOverline) || overline.alpha() == 255) && (!(decoration & TextDecorationLineThrough) || linethrough.alpha() == 255);
@@ -1047,13 +1009,13 @@ void InlineTextBox::paintDecoration(GraphicsContext& context, const FloatPoint&
             shadow = shadow->next();
         }
         
-        float wavyOffset = 2.f;
+        float wavyOffset = wavyOffsetFromDecoration();
 
         context.setStrokeStyle(textDecorationStyleToStrokeStyle(decorationStyle));
+        // These decorations should match the visual overflows computed in visualOverflowForDecorations()
         if (decoration & TextDecorationUnderline) {
             context.setStrokeColor(underline, colorSpace);
-            TextUnderlinePosition underlinePosition = lineStyle.textUnderlinePosition();
-            const int underlineOffset = computeUnderlineOffset(underlinePosition, lineStyle.fontMetrics(), this, textDecorationThickness);
+            const int underlineOffset = computeUnderlineOffset(lineStyle.textUnderlinePosition(), lineStyle.fontMetrics(), this, textDecorationThickness);
 
             switch (decorationStyle) {
             case TextDecorationStyleWavy: {
index 708a466..7c4bfa9 100644 (file)
@@ -31,6 +31,7 @@
 #include "InlineElementBox.h"
 #include "InlineIterator.h"
 #include "InlineTextBox.h"
+#include "InlineTextBoxStyle.h"
 #include "LineLayoutState.h"
 #include "Logging.h"
 #include "RenderBlockFlow.h"
@@ -524,7 +525,12 @@ static inline void setLogicalWidthForTextRun(RootInlineBox* lineBox, BidiRun* ru
         copyToVector(fallbackFonts, it->value.first);
         run->box()->parent()->clearDescendantsHaveSameLineHeightAndBaseline();
     }
-    if ((glyphOverflow.top || glyphOverflow.bottom || glyphOverflow.left || glyphOverflow.right)) {
+
+    // Include text decoration visual overflow as part of the glyph overflow.
+    if (renderer->style().textDecorationsInEffect() != TextDecorationNone)
+        glyphOverflow.extendTo(visualOverflowForDecorations(run->box()->lineStyle(), toInlineTextBox(run->box())));
+
+    if (!glyphOverflow.isEmpty()) {
         ASSERT(run->box()->behavesLikeText());
         GlyphOverflowAndFallbackFontsMap::iterator it = textBoxDataMap.add(toInlineTextBox(run->box()), std::make_pair(Vector<const SimpleFontData*>(), GlyphOverflow())).iterator;
         it->value.second = glyphOverflow;
index b5cf014..c9288e9 100644 (file)
@@ -1035,7 +1035,7 @@ CellSpan RenderTableSection::dirtiedRows(const LayoutRect& damageRect) const
     if (m_forceSlowPaintPathWithOverflowingCell) 
         return fullTableRowSpan();
 
-    CellSpan coveredRows = spannedRows(damageRect);
+    CellSpan coveredRows = spannedRows(damageRect, IncludeAllIntersectingCells);
 
     // To repaint the border we might need to repaint first or last row even if they are not spanned themselves.
     if (coveredRows.start() >= m_rowPos.size() - 1 && m_rowPos[m_rowPos.size() - 1] + table()->outerBorderAfter() >= damageRect.y())
@@ -1052,7 +1052,7 @@ CellSpan RenderTableSection::dirtiedColumns(const LayoutRect& damageRect) const
     if (m_forceSlowPaintPathWithOverflowingCell) 
         return fullTableColumnSpan();
 
-    CellSpan coveredColumns = spannedColumns(damageRect);
+    CellSpan coveredColumns = spannedColumns(damageRect, IncludeAllIntersectingCells);
 
     const Vector<int>& columnPos = table()->columnPositions();
     // To repaint the border we might need to repaint first or last column even if they are not spanned themselves.
@@ -1065,10 +1065,12 @@ CellSpan RenderTableSection::dirtiedColumns(const LayoutRect& damageRect) const
     return coveredColumns;
 }
 
-CellSpan RenderTableSection::spannedRows(const LayoutRect& flippedRect) const
+CellSpan RenderTableSection::spannedRows(const LayoutRect& flippedRect, ShouldIncludeAllIntersectingCells shouldIncludeAllIntersectionCells) const
 {
     // Find the first row that starts after rect top.
     unsigned nextRow = std::upper_bound(m_rowPos.begin(), m_rowPos.end(), flippedRect.y()) - m_rowPos.begin();
+    if (shouldIncludeAllIntersectionCells == IncludeAllIntersectingCells && nextRow && m_rowPos[nextRow - 1] == flippedRect.y())
+        --nextRow;
 
     if (nextRow == m_rowPos.size())
         return CellSpan(m_rowPos.size() - 1, m_rowPos.size() - 1); // After all rows.
@@ -1088,7 +1090,7 @@ CellSpan RenderTableSection::spannedRows(const LayoutRect& flippedRect) const
     return CellSpan(startRow, endRow);
 }
 
-CellSpan RenderTableSection::spannedColumns(const LayoutRect& flippedRect) const
+CellSpan RenderTableSection::spannedColumns(const LayoutRect& flippedRect, ShouldIncludeAllIntersectingCells shouldIncludeAllIntersectionCells) const
 {
     const Vector<int>& columnPos = table()->columnPositions();
 
@@ -1098,6 +1100,8 @@ CellSpan RenderTableSection::spannedColumns(const LayoutRect& flippedRect) const
     // upper_bound on the other hand properly returns the cell on the logical bottom/right, which also
     // matches the behavior of other browsers.
     unsigned nextColumn = std::upper_bound(columnPos.begin(), columnPos.end(), flippedRect.x()) - columnPos.begin();
+    if (shouldIncludeAllIntersectionCells == IncludeAllIntersectingCells && nextColumn && columnPos[nextColumn - 1] == flippedRect.x())
+        --nextColumn;
 
     if (nextColumn == columnPos.size())
         return CellSpan(columnPos.size() - 1, columnPos.size() - 1); // After all columns.
@@ -1521,8 +1525,8 @@ bool RenderTableSection::nodeAtPoint(const HitTestRequest& request, HitTestResul
     hitTestRect.moveBy(-adjustedLocation);
 
     LayoutRect tableAlignedRect = logicalRectForWritingModeAndDirection(hitTestRect);
-    CellSpan rowSpan = spannedRows(tableAlignedRect);
-    CellSpan columnSpan = spannedColumns(tableAlignedRect);
+    CellSpan rowSpan = spannedRows(tableAlignedRect, DoNotIncludeAllIntersectingCells);
+    CellSpan columnSpan = spannedColumns(tableAlignedRect, DoNotIncludeAllIntersectingCells);
 
     // Now iterate over the spanned rows and columns.
     for (unsigned hitRow = rowSpan.start(); hitRow < rowSpan.end(); ++hitRow) {
index ed589e7..ee23309 100644 (file)
@@ -254,6 +254,11 @@ protected:
     virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle) override;
 
 private:
+    enum ShouldIncludeAllIntersectingCells {
+        IncludeAllIntersectingCells,
+        DoNotIncludeAllIntersectingCells
+    };
+
     virtual const char* renderName() const override { return (isAnonymous() || isPseudoElement()) ? "RenderTableSection (anonymous)" : "RenderTableSection"; }
 
     virtual bool canHaveChildren() const override { return true; }
@@ -303,8 +308,12 @@ private:
 
     // These two functions take a rectangle as input that has been flipped by logicalRectForWritingModeAndDirection.
     // The returned span of rows or columns is end-exclusive, and empty if start==end.
-    CellSpan spannedRows(const LayoutRect& flippedRect) const;
-    CellSpan spannedColumns(const LayoutRect& flippedRect) const;
+    // The IncludeAllIntersectingCells argument is used to determine which cells to include when
+    // an edge of the flippedRect lies exactly on a cell boundary. Using IncludeAllIntersectingCells
+    // will return both cells, and using DoNotIncludeAllIntersectingCells will return only the cell
+    // that hittesting should return.
+    CellSpan spannedRows(const LayoutRect& flippedRect, ShouldIncludeAllIntersectingCells) const;
+    CellSpan spannedColumns(const LayoutRect& flippedRect, ShouldIncludeAllIntersectingCells) const;
 
     void setLogicalPositionForCell(RenderTableCell*, unsigned effectiveColumn) const;
 
index f4b38dd..172a1ed 100644 (file)
@@ -29,6 +29,7 @@
 #include "FloatRoundedRect.h"
 #include "Font.h"
 #include "FontSelector.h"
+#include "InlineTextBoxStyle.h"
 #include "Pagination.h"
 #include "QuotesData.h"
 #include "RenderObject.h"
@@ -382,6 +383,26 @@ static bool positionChangeIsMovementOnly(const LengthBox& a, const LengthBox& b,
     return true;
 }
 
+inline bool RenderStyle::changeAffectsVisualOverflow(const RenderStyle& other) const
+{
+    if (rareNonInheritedData.get() != other.rareNonInheritedData.get()
+        && !rareNonInheritedData->shadowDataEquivalent(*other.rareNonInheritedData.get()))
+        return true;
+
+    if (inherited_flags._text_decorations != other.inherited_flags._text_decorations
+        || visual->textDecoration != other.visual->textDecoration
+        || rareNonInheritedData->m_textDecorationStyle != other.rareNonInheritedData->m_textDecorationStyle) {
+        // Underlines are always drawn outside of their textbox bounds when text-underline-position: under;
+        // is specified. We can take an early out here.
+        if (textUnderlinePosition() == TextUnderlinePositionUnder
+            || other.textUnderlinePosition() == TextUnderlinePositionUnder)
+            return true;
+        return visualOverflowForDecorations(*this, nullptr) != visualOverflowForDecorations(other, nullptr);
+    }
+
+    return false;
+}
+
 bool RenderStyle::changeRequiresLayout(const RenderStyle* other, unsigned& changedContextSensitiveProperties) const
 {
     if (m_box->width() != other->m_box->width()
@@ -404,6 +425,10 @@ bool RenderStyle::changeRequiresLayout(const RenderStyle* other, unsigned& chang
     if (surround->padding != other->surround->padding)
         return true;
 
+    // FIXME: We should add an optimized form of layout that just recomputes visual overflow.
+    if (changeAffectsVisualOverflow(*other))
+        return true;
+
     if (rareNonInheritedData.get() != other->rareNonInheritedData.get()) {
         if (rareNonInheritedData->m_appearance != other->rareNonInheritedData->m_appearance 
             || rareNonInheritedData->marginBeforeCollapse != other->rareNonInheritedData->marginBeforeCollapse
@@ -434,10 +459,6 @@ bool RenderStyle::changeRequiresLayout(const RenderStyle* other, unsigned& chang
             || rareNonInheritedData->m_justifyContent != other->rareNonInheritedData->m_justifyContent)
             return true;
 
-        // FIXME: We should add an optimized form of layout that just recomputes visual overflow.
-        if (!rareNonInheritedData->shadowDataEquivalent(*other->rareNonInheritedData.get()))
-            return true;
-
         if (!rareNonInheritedData->reflectionDataEquivalent(*other->rareNonInheritedData.get()))
             return true;
 
index 116e6eb..19ba285 100644 (file)
@@ -1936,6 +1936,7 @@ public:
     static ptrdiff_t noninheritedFlagsMemoryOffset() { return OBJECT_OFFSETOF(RenderStyle, noninherited_flags); }
 
 private:
+    bool changeAffectsVisualOverflow(const RenderStyle&) const;
     bool changeRequiresLayout(const RenderStyle*, unsigned& changedContextSensitiveProperties) const;
     bool changeRequiresPositionedLayoutOnly(const RenderStyle*, unsigned& changedContextSensitiveProperties) const;
     bool changeRequiresLayerRepaint(const RenderStyle*, unsigned& changedContextSensitiveProperties) const;
diff --git a/Source/WebCore/style/InlineTextBoxStyle.cpp b/Source/WebCore/style/InlineTextBoxStyle.cpp
new file mode 100644 (file)
index 0000000..0f3acde
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2014 Apple Inc.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#include "config.h"
+#include "InlineTextBoxStyle.h"
+
+#include "Font.h"
+#include "InlineTextBox.h"
+#include "RootInlineBox.h"
+
+namespace WebCore {
+    
+int computeUnderlineOffset(TextUnderlinePosition underlinePosition, const FontMetrics& fontMetrics, InlineTextBox* inlineTextBox, int textDecorationThickness)
+{
+    // This represents the gap between the baseline and the closest edge of the underline.
+    int gap = std::max<int>(1, ceilf(textDecorationThickness / 2.0));
+
+    // According to the specification TextUnderlinePositionAuto should default to 'alphabetic' for horizontal text
+    // and to 'under Left' for vertical text (e.g. japanese). We support only horizontal text for now.
+    switch (underlinePosition) {
+    case TextUnderlinePositionAlphabetic:
+    case TextUnderlinePositionAuto:
+        return fontMetrics.ascent() + gap;
+    case TextUnderlinePositionUnder: {
+        ASSERT(inlineTextBox);
+        // Position underline relative to the bottom edge of the lowest element's content box.
+        float offset = inlineTextBox->root().maxLogicalTop() - inlineTextBox->logicalTop();
+        return inlineTextBox->logicalHeight() + gap + std::max<float>(offset, 0);
+    }
+    }
+
+    ASSERT_NOT_REACHED();
+    return fontMetrics.ascent() + gap;
+}
+    
+void getWavyStrokeParameters(float strokeThickness, float& controlPointDistance, float& step)
+{
+    // Distance between decoration's axis and Bezier curve's control points.
+    // The height of the curve is based on this distance. Use a minimum of 6 pixels distance since
+    // the actual curve passes approximately at half of that distance, that is 3 pixels.
+    // The minimum height of the curve is also approximately 3 pixels. Increases the curve's height
+    // as strokeThickness increases to make the curve look better.
+    controlPointDistance = 3 * std::max<float>(2, strokeThickness);
+
+    // Increment used to form the diamond shape between start point (p1), control
+    // points and end point (p2) along the axis of the decoration. Makes the
+    // curve wider as strokeThickness increases to make the curve look better.
+    step = 2 * std::max<float>(2, strokeThickness);
+}
+
+static inline void extendIntToFloat(int& extendMe, float extendTo)
+{
+    extendMe = std::max(extendMe, static_cast<int>(ceilf(extendTo)));
+}
+
+GlyphOverflow visualOverflowForDecorations(const RenderStyle& lineStyle, InlineTextBox* inlineTextBox)
+{
+    ASSERT(!inlineTextBox || inlineTextBox->lineStyle() == lineStyle);
+    
+    TextDecoration decoration = lineStyle.textDecorationsInEffect();
+    if (decoration == TextDecorationNone)
+        return GlyphOverflow();
+    
+    float strokeThickness = textDecorationStrokeThickness(lineStyle.fontSize());
+    float controlPointDistance;
+    float step;
+    float wavyOffset;
+        
+    TextDecorationStyle decorationStyle = lineStyle.textDecorationStyle();
+    float height = lineStyle.font().fontMetrics().floatHeight();
+    GlyphOverflow overflowResult;
+    
+    if (decorationStyle == TextDecorationStyleWavy) {
+        getWavyStrokeParameters(strokeThickness, controlPointDistance, step);
+        wavyOffset = wavyOffsetFromDecoration();
+        overflowResult.left = strokeThickness;
+        overflowResult.right = strokeThickness;
+    }
+
+    // These metrics must match where underlines get drawn.
+    if (decoration & TextDecorationUnderline) {
+        float underlineOffset = computeUnderlineOffset(lineStyle.textUnderlinePosition(), lineStyle.fontMetrics(), inlineTextBox, strokeThickness);
+        if (decorationStyle == TextDecorationStyleWavy) {
+            extendIntToFloat(overflowResult.bottom, underlineOffset + wavyOffset + controlPointDistance + strokeThickness - height);
+            extendIntToFloat(overflowResult.top, -(underlineOffset + wavyOffset - controlPointDistance - strokeThickness));
+        } else {
+            extendIntToFloat(overflowResult.bottom, underlineOffset + strokeThickness - height);
+            extendIntToFloat(overflowResult.top, -underlineOffset);
+        }
+    }
+    if (decoration & TextDecorationOverline) {
+        if (decorationStyle == TextDecorationStyleWavy) {
+            extendIntToFloat(overflowResult.bottom, -wavyOffset + controlPointDistance + strokeThickness - height);
+            extendIntToFloat(overflowResult.top, wavyOffset + controlPointDistance + strokeThickness);
+        } else {
+            extendIntToFloat(overflowResult.bottom, strokeThickness - height);
+            // top is untouched
+        }
+    }
+    if (decoration & TextDecorationLineThrough) {
+        float baseline = lineStyle.fontMetrics().floatAscent();
+        if (decorationStyle == TextDecorationStyleWavy) {
+            extendIntToFloat(overflowResult.bottom, 2 * baseline / 3 + controlPointDistance + strokeThickness - height);
+            extendIntToFloat(overflowResult.top, -(2 * baseline / 3 - controlPointDistance - strokeThickness));
+        } else {
+            extendIntToFloat(overflowResult.bottom, 2 * baseline / 3 + strokeThickness - height);
+            extendIntToFloat(overflowResult.top, -(2 * baseline / 3));
+        }
+    }
+    return overflowResult;
+}
+    
+}
diff --git a/Source/WebCore/style/InlineTextBoxStyle.h b/Source/WebCore/style/InlineTextBoxStyle.h
new file mode 100644 (file)
index 0000000..9689341
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2014 Apple Inc.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#ifndef InlineTextBoxStyle_h
+#define InlineTextBoxStyle_h
+
+#include "Font.h"
+#include "RenderStyle.h"
+
+namespace WebCore {
+    
+class InlineTextBox;
+
+inline float textDecorationStrokeThickness(float fontSize)
+{
+    const float textDecorationBaseFontSize = 16;
+    return fontSize / textDecorationBaseFontSize;
+}
+
+inline float wavyOffsetFromDecoration()
+{
+    return 2;
+}
+
+GlyphOverflow visualOverflowForDecorations(const RenderStyle& lineStyle, InlineTextBox*);
+void getWavyStrokeParameters(float strokeThickness, float& controlPointDistance, float& step);
+int computeUnderlineOffset(TextUnderlinePosition, const FontMetrics&, InlineTextBox*, int textDecorationThickness);
+    
+}
+
+#endif