Add support for the "last" value of hanging-punctuation
authorhyatt@apple.com <hyatt@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 3 Mar 2016 21:49:32 +0000 (21:49 +0000)
committerhyatt@apple.com <hyatt@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 3 Mar 2016 21:49:32 +0000 (21:49 +0000)
https://bugs.webkit.org/show_bug.cgi?id=154977

Reviewed by Darin Adler and Simon Fraser.

Source/WebCore:

New tests in fast/text.

This patch adds support for the new "last" value and also fixes up both
"first" and "last" to work with leading and trailing whitespace that ends
up getting collapsed away.

* rendering/RenderBlockFlow.cpp:
(WebCore::RenderBlockFlow::computeInlinePreferredLogicalWidths):
* rendering/RenderBlockLineLayout.cpp:
(WebCore::inlineAncestorHasStartBorderPaddingOrMargin):
(WebCore::inlineAncestorHasEndBorderPaddingOrMargin):
(WebCore::isLastInFlowRun):
(WebCore::RenderBlockFlow::computeInlineDirectionPositionsForSegment):
* rendering/RenderText.cpp:
(WebCore::isHangablePunctuationAtLineEnd):
(WebCore::RenderText::hangablePunctuationStartWidth):
(WebCore::RenderText::hangablePunctuationEndWidth):
(WebCore::RenderText::firstCharacterIndexStrippingSpaces):
(WebCore::RenderText::lastCharacterIndexStrippingSpaces):
(WebCore::RenderText::trimmedPrefWidths):
* rendering/RenderText.h:
* rendering/line/BreakingContext.h:
(WebCore::BreakingContext::handleText):

LayoutTests:

* fast/text/hanging-punctuation-first-and-last-together-expected.html: Added.
* fast/text/hanging-punctuation-first-and-last-together.html: Added.
* fast/text/hanging-punctuation-first-ws-expected.html: Added.
* fast/text/hanging-punctuation-first-ws.html: Added.
* fast/text/hanging-punctuation-last-expected.html: Added.
* fast/text/hanging-punctuation-last-rtl-expected.html: Added.
* fast/text/hanging-punctuation-last-rtl.html: Added.
* fast/text/hanging-punctuation-last-ws-expected.html: Added.
* fast/text/hanging-punctuation-last-ws.html: Added.
* fast/text/hanging-punctuation-last.html: Added.

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

17 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/text/hanging-punctuation-first-and-last-together-expected.html [new file with mode: 0644]
LayoutTests/fast/text/hanging-punctuation-first-and-last-together.html [new file with mode: 0644]
LayoutTests/fast/text/hanging-punctuation-first-ws-expected.html [new file with mode: 0644]
LayoutTests/fast/text/hanging-punctuation-first-ws.html [new file with mode: 0644]
LayoutTests/fast/text/hanging-punctuation-last-expected.html [new file with mode: 0644]
LayoutTests/fast/text/hanging-punctuation-last-rtl-expected.html [new file with mode: 0644]
LayoutTests/fast/text/hanging-punctuation-last-rtl.html [new file with mode: 0644]
LayoutTests/fast/text/hanging-punctuation-last-ws-expected.html [new file with mode: 0644]
LayoutTests/fast/text/hanging-punctuation-last-ws.html [new file with mode: 0644]
LayoutTests/fast/text/hanging-punctuation-last.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/rendering/RenderBlockFlow.cpp
Source/WebCore/rendering/RenderBlockLineLayout.cpp
Source/WebCore/rendering/RenderText.cpp
Source/WebCore/rendering/RenderText.h
Source/WebCore/rendering/line/BreakingContext.h

index beb6ce1..b25d520 100644 (file)
@@ -1,3 +1,21 @@
+2016-03-03  Dave Hyatt  <hyatt@apple.com>
+
+        Add support for the "last" value of hanging-punctuation
+        https://bugs.webkit.org/show_bug.cgi?id=154977
+
+        Reviewed by Darin Adler and Simon Fraser.
+
+        * fast/text/hanging-punctuation-first-and-last-together-expected.html: Added.
+        * fast/text/hanging-punctuation-first-and-last-together.html: Added.
+        * fast/text/hanging-punctuation-first-ws-expected.html: Added.
+        * fast/text/hanging-punctuation-first-ws.html: Added.
+        * fast/text/hanging-punctuation-last-expected.html: Added.
+        * fast/text/hanging-punctuation-last-rtl-expected.html: Added.
+        * fast/text/hanging-punctuation-last-rtl.html: Added.
+        * fast/text/hanging-punctuation-last-ws-expected.html: Added.
+        * fast/text/hanging-punctuation-last-ws.html: Added.
+        * fast/text/hanging-punctuation-last.html: Added.
+
 2016-03-03  Brady Eidson  <beidson@apple.com>
 
         storage/indexeddb/delete-in-upgradeneeded-close-in-open-success.html flaky on mac-wk2.
diff --git a/LayoutTests/fast/text/hanging-punctuation-first-and-last-together-expected.html b/LayoutTests/fast/text/hanging-punctuation-first-and-last-together-expected.html
new file mode 100644 (file)
index 0000000..11b6fc6
--- /dev/null
@@ -0,0 +1,10 @@
+<head>
+    <style>
+        body { font-family: 'Ahem'; color:green }
+        .hang { margin:1em; border:1px solid black }
+        </style>
+</head>
+<body>
+    <div style="float:left" class="hang"><div style="margin: 0 -1em">(Hang test)</div></div>
+    <div style="clear:both">
+        <div class="hang" style="width:10em; text-align:justify;"><span style="margin:0 -1em">(This should hang.<br>(This should also hang.)</span></div>
diff --git a/LayoutTests/fast/text/hanging-punctuation-first-and-last-together.html b/LayoutTests/fast/text/hanging-punctuation-first-and-last-together.html
new file mode 100644 (file)
index 0000000..a996e25
--- /dev/null
@@ -0,0 +1,10 @@
+<head>
+<style>
+    body { font-family: 'Ahem'; color:green }
+    .hang { hanging-punctuation: first last; margin:1em; border:1px solid black }
+</style>
+</head>
+<body>
+<div style="float:left" class="hang">(Hang test)</div>
+<div style="clear:both">
+<div class="hang" style="width:10em; text-align:justify;">(This should hang.<br>(This should also hang.)</div>
diff --git a/LayoutTests/fast/text/hanging-punctuation-first-ws-expected.html b/LayoutTests/fast/text/hanging-punctuation-first-ws-expected.html
new file mode 100644 (file)
index 0000000..7d6ff2d
--- /dev/null
@@ -0,0 +1,14 @@
+<head>
+<style>
+    body { font-family: 'Ahem'; color:green }
+    .hang { text-indent: -1em; margin:1em }
+</style>
+</head>
+<body>
+<div style="float:left" class="hang">(Hang test)</div>
+<div style="clear:both">
+<div class="hang">(This should hang.<br>(This should not.</div>
+<div class="hang" style="text-align:justify; width:300px;">(This should hang and justifybecause and now were fine.</div>
+<div class="hang" style="text-align:justify; width:300px;"><span>(</span>This should hang.</div>
+<div style="margin-left:1em; text-align:justify; width:300px;"><span style="border-left:1em solid blue">(</span>This should not hang.</div>
+<div class="hang" style="text-indent:1em; text-align:justify; width:300px;">(This should hang into the text-indent.</div>
\ No newline at end of file
diff --git a/LayoutTests/fast/text/hanging-punctuation-first-ws.html b/LayoutTests/fast/text/hanging-punctuation-first-ws.html
new file mode 100644 (file)
index 0000000..53e2d0e
--- /dev/null
@@ -0,0 +1,14 @@
+<head>
+<style>
+    body { font-family: 'Ahem'; color:green }
+    .hang { hanging-punctuation: first; margin:1em }
+</style>
+</head>
+<body>
+<div style="float:left" class="hang">    (Hang test)</div>
+<div style="clear:both">
+<div class="hang">    (This should hang.<br>(This should not.</div>
+<div class="hang" style="text-align:justify; width:300px;">   (This should hang and justifybecause and now were fine.</div>
+<div class="hang" style="text-align:justify; width:300px;">   <span>   (</span>This should hang.</div>
+<div class="hang" style="text-align:justify; width:300px;">   <span style="border-left:1em solid blue">  (</span>This should not hang.</div>
+<div class="hang" style="text-indent:2em; text-align:justify; width:300px;">   (This should hang into the text-indent.</div>
\ No newline at end of file
diff --git a/LayoutTests/fast/text/hanging-punctuation-last-expected.html b/LayoutTests/fast/text/hanging-punctuation-last-expected.html
new file mode 100644 (file)
index 0000000..bd9d2ee
--- /dev/null
@@ -0,0 +1,12 @@
+<head>
+    <style>
+    body { font-family: 'Ahem'; color:green }
+    .hang { margin:1em }
+    </style>
+</head>
+<body>
+<div style="float:left;" class="hang">(Hang test)</div>
+<div style="clear:both">
+    <div class="hang" style="width:5em">No.<br>Yes.)</div>
+    <div class="hang" style="text-align:justify; width:5em;">Yes <span>)</span></div>
+    <div class="hang" style="text-align:justify; width:4em;">No. <span style="border-right:1em solid blue">)</span></div>
diff --git a/LayoutTests/fast/text/hanging-punctuation-last-rtl-expected.html b/LayoutTests/fast/text/hanging-punctuation-last-rtl-expected.html
new file mode 100644 (file)
index 0000000..43a4ec0
--- /dev/null
@@ -0,0 +1,12 @@
+<head>
+    <style>
+        body { font-family: 'Ahem'; color:green; direction:rtl }
+    .hang { margin:1em }
+    </style>
+</head>
+<body>
+<div style="float:right;" class="hang">(Hang test)</div>
+<div style="clear:both">
+    <div class="hang" style="width:5em">No.<br>Yes.)</div>
+    <div class="hang" style="text-align:justify; width:5em;">Yes <span>)</span></div>
+    <div class="hang" style="text-align:justify; width:4em;">No. <span style="border-left:1em solid blue">)</span></div>
diff --git a/LayoutTests/fast/text/hanging-punctuation-last-rtl.html b/LayoutTests/fast/text/hanging-punctuation-last-rtl.html
new file mode 100644 (file)
index 0000000..2d238ee
--- /dev/null
@@ -0,0 +1,13 @@
+<head>
+<style>
+    body { font-family: 'Ahem'; color:green; direction:rtl }
+    .hang { hanging-punctuation: last; margin:1em }
+</style>
+</head>
+<body>
+<div style="float:right;" class="hang">(Hang test)</div>
+<div style="clear:both">
+<div class="hang" style="width:4em"
+    >No.<br>Yes.)</div>
+<div class="hang" style="text-align:justify; width:4em;">Yes <span>)</span></div>
+<div class="hang" style="text-align:justify; width:4em;">No. <span style="border-left:1em solid blue">)</span></div>
diff --git a/LayoutTests/fast/text/hanging-punctuation-last-ws-expected.html b/LayoutTests/fast/text/hanging-punctuation-last-ws-expected.html
new file mode 100644 (file)
index 0000000..bd9d2ee
--- /dev/null
@@ -0,0 +1,12 @@
+<head>
+    <style>
+    body { font-family: 'Ahem'; color:green }
+    .hang { margin:1em }
+    </style>
+</head>
+<body>
+<div style="float:left;" class="hang">(Hang test)</div>
+<div style="clear:both">
+    <div class="hang" style="width:5em">No.<br>Yes.)</div>
+    <div class="hang" style="text-align:justify; width:5em;">Yes <span>)</span></div>
+    <div class="hang" style="text-align:justify; width:4em;">No. <span style="border-right:1em solid blue">)</span></div>
diff --git a/LayoutTests/fast/text/hanging-punctuation-last-ws.html b/LayoutTests/fast/text/hanging-punctuation-last-ws.html
new file mode 100644 (file)
index 0000000..fe0c5b2
--- /dev/null
@@ -0,0 +1,12 @@
+<head>
+<style>
+    body { font-family: 'Ahem'; color:green }
+    .hang { hanging-punctuation: last; margin:1em }
+</style>
+</head>
+<body>
+<div style="float:left;" class="hang">(Hang test)  </div>
+<div style="clear:both">
+<div class="hang" style="width:4em">No.<br>Yes.)  </div>
+<div class="hang" style="text-align:justify; width:4em;">Yes <span>) </span></div>
+<div class="hang" style="text-align:justify; width:4em;">No. <span style="border-right:1em solid blue">) </span>  </div>
diff --git a/LayoutTests/fast/text/hanging-punctuation-last.html b/LayoutTests/fast/text/hanging-punctuation-last.html
new file mode 100644 (file)
index 0000000..f41a918
--- /dev/null
@@ -0,0 +1,13 @@
+<head>
+<style>
+    body { font-family: 'Ahem'; color:green }
+    .hang { hanging-punctuation: last; margin:1em }
+</style>
+</head>
+<body>
+<div style="float:left;" class="hang">(Hang test)</div>
+<div style="clear:both">
+<div class="hang" style="width:4em"
+    >No.<br>Yes.)</div>
+<div class="hang" style="text-align:justify; width:4em;">Yes <span>)</span></div>
+<div class="hang" style="text-align:justify; width:4em;">No. <span style="border-right:1em solid blue">)</span></div>
index f6c73be..34617e6 100644 (file)
@@ -1,3 +1,34 @@
+2016-03-03  Dave Hyatt  <hyatt@apple.com>
+
+        Add support for the "last" value of hanging-punctuation
+        https://bugs.webkit.org/show_bug.cgi?id=154977
+
+        Reviewed by Darin Adler and Simon Fraser.
+
+        New tests in fast/text.
+
+        This patch adds support for the new "last" value and also fixes up both
+        "first" and "last" to work with leading and trailing whitespace that ends
+        up getting collapsed away.
+
+        * rendering/RenderBlockFlow.cpp:
+        (WebCore::RenderBlockFlow::computeInlinePreferredLogicalWidths):
+        * rendering/RenderBlockLineLayout.cpp:
+        (WebCore::inlineAncestorHasStartBorderPaddingOrMargin):
+        (WebCore::inlineAncestorHasEndBorderPaddingOrMargin):
+        (WebCore::isLastInFlowRun):
+        (WebCore::RenderBlockFlow::computeInlineDirectionPositionsForSegment):
+        * rendering/RenderText.cpp:
+        (WebCore::isHangablePunctuationAtLineEnd):
+        (WebCore::RenderText::hangablePunctuationStartWidth):
+        (WebCore::RenderText::hangablePunctuationEndWidth):
+        (WebCore::RenderText::firstCharacterIndexStrippingSpaces):
+        (WebCore::RenderText::lastCharacterIndexStrippingSpaces):
+        (WebCore::RenderText::trimmedPrefWidths):
+        * rendering/RenderText.h:
+        * rendering/line/BreakingContext.h:
+        (WebCore::BreakingContext::handleText):
+
 2016-03-03  Andy Estes  <aestes@apple.com>
 
         Adopt CFNetwork storage partitioning SPI
index b0b9024..6204ffa 100644 (file)
@@ -4065,6 +4065,9 @@ void RenderBlockFlow::computeInlinePreferredLogicalWidths(LayoutUnit& minLogical
     bool isPrevChildInlineFlow = false;
     bool shouldBreakLineAfterText = false;
     bool canHangPunctuationAtStart = styleToUse.hangingPunctuation() & FirstHangingPunctuation;
+    bool canHangPunctuationAtEnd = styleToUse.hangingPunctuation() & LastHangingPunctuation;
+    RenderText* lastText = nullptr;
+
     bool addedStartPunctuationHang = false;
     
     while (RenderObject* child = childIterator.next()) {
@@ -4111,6 +4114,7 @@ void RenderBlockFlow::computeInlinePreferredLogicalWidths(LayoutUnit& minLogical
             float childMax = 0;
 
             if (!child->isText()) {
+                lastText = nullptr;
                 if (child->isLineBreakOpportunity()) {
                     minLogicalWidth = preferredWidth(minLogicalWidth, inlineMin);
                     inlineMin = 0;
@@ -4217,6 +4221,7 @@ void RenderBlockFlow::computeInlinePreferredLogicalWidths(LayoutUnit& minLogical
                 if (!child->isFloating()) {
                     stripFrontSpaces = false;
                     trailingSpaceChild = nullptr;
+                    lastText = nullptr;
                 }
             } else if (is<RenderText>(*child)) {
                 // Case (3). Text.
@@ -4234,6 +4239,7 @@ void RenderBlockFlow::computeInlinePreferredLogicalWidths(LayoutUnit& minLogical
                 float beginMin, endMin;
                 bool beginWS, endWS;
                 float beginMax, endMax;
+                bool strippingBeginWS = stripFrontSpaces;
                 renderText.trimmedPrefWidths(inlineMax, beginMin, beginWS, endMin, endWS,
                                      hasBreakableChar, hasBreak, beginMax, endMax,
                                      childMin, childMax, stripFrontSpaces);
@@ -4246,6 +4252,8 @@ void RenderBlockFlow::computeInlinePreferredLogicalWidths(LayoutUnit& minLogical
                     }
                     continue;
                 }
+                
+                lastText = &renderText;
 
                 if (stripFrontSpaces)
                     trailingSpaceChild = child;
@@ -4276,7 +4284,8 @@ void RenderBlockFlow::computeInlinePreferredLogicalWidths(LayoutUnit& minLogical
                 
                 // See if we have a hanging punctuation situation at the start.
                 if (canHangPunctuationAtStart && !addedStartPunctuationHang) {
-                    float hangStartWidth = renderText.hangablePunctuationStartWidth();
+                    unsigned startIndex = strippingBeginWS ? renderText.firstCharacterIndexStrippingSpaces() : 0;
+                    float hangStartWidth = renderText.hangablePunctuationStartWidth(startIndex);
                     childMin -= hangStartWidth;
                     beginMin -= hangStartWidth;
                     childMax -= hangStartWidth;
@@ -4349,6 +4358,13 @@ void RenderBlockFlow::computeInlinePreferredLogicalWidths(LayoutUnit& minLogical
 
     if (styleToUse.collapseWhiteSpace())
         stripTrailingSpace(inlineMax, inlineMin, trailingSpaceChild);
+    
+    if (canHangPunctuationAtEnd && lastText && lastText->textLength() > 0) {
+        unsigned endIndex = trailingSpaceChild == lastText ? lastText->lastCharacterIndexStrippingSpaces() : lastText->textLength() - 1;
+        float endHangWidth = lastText->hangablePunctuationEndWidth(endIndex);
+        inlineMin -= endHangWidth;
+        inlineMax -= endHangWidth;
+    }
 
     minLogicalWidth = preferredWidth(minLogicalWidth, inlineMin);
     maxLogicalWidth = preferredWidth(maxLogicalWidth, inlineMax);
index 1f0556c..15a1d5d 100644 (file)
@@ -817,6 +817,17 @@ static bool inlineAncestorHasStartBorderPaddingOrMargin(const RenderBlockFlow& b
     return false;
 }
 
+static bool inlineAncestorHasEndBorderPaddingOrMargin(const RenderBlockFlow& block, const InlineBox& box)
+{
+    bool isLTR = block.style().isLeftToRightDirection();
+    for (auto* currentBox = box.parent(); currentBox; currentBox = currentBox->parent()) {
+        if ((isLTR && currentBox->marginBorderPaddingLogicalRight() > 0)
+            || (!isLTR && currentBox->marginBorderPaddingLogicalLeft() > 0))
+            return true;
+    }
+    return false;
+}
+    
 static bool isLastInFlowRun(BidiRun& runToCheck)
 {
     for (auto* run = runToCheck.next(); run; run = run->next()) {
@@ -833,6 +844,7 @@ BidiRun* RenderBlockFlow::computeInlineDirectionPositionsForSegment(RootInlineBo
 {
     bool needsWordSpacing = false;
     bool canHangPunctuationAtStart = style().hangingPunctuation() & FirstHangingPunctuation;
+    bool canHangPunctuationAtEnd = style().hangingPunctuation() & LastHangingPunctuation;
     bool isLTR = style().isLeftToRightDirection();
     float totalLogicalWidth = lineBox->getFlowSpacingLogicalWidth();
     unsigned expansionOpportunityCount = 0;
@@ -852,13 +864,22 @@ BidiRun* RenderBlockFlow::computeInlineDirectionPositionsForSegment(RootInlineBo
             auto& textBox = downcast<InlineTextBox>(*run->box());
             if (canHangPunctuationAtStart && lineInfo.isFirstLine() && (isLTR || isLastInFlowRun(*run))
                 && !inlineAncestorHasStartBorderPaddingOrMargin(*this, *run->box())) {
-                float hangStartWidth = renderText.hangablePunctuationStartWidth();
+                float hangStartWidth = renderText.hangablePunctuationStartWidth(run->m_start);
                 availableLogicalWidth += hangStartWidth;
                 if (style().isLeftToRightDirection())
                     logicalLeft -= hangStartWidth;
                 canHangPunctuationAtStart = false;
             }
             
+            if (canHangPunctuationAtEnd && lineInfo.isLastLine() && run->m_stop > 0 && (!isLTR || isLastInFlowRun(*run))
+                && !inlineAncestorHasEndBorderPaddingOrMargin(*this, *run->box())) {
+                float hangEndWidth = renderText.hangablePunctuationEndWidth(run->m_stop - 1);
+                availableLogicalWidth += hangEndWidth;
+                if (!style().isLeftToRightDirection())
+                    logicalLeft -= hangEndWidth;
+                canHangPunctuationAtEnd = false;
+            }
+            
             if (textAlign == JUSTIFY && run != trailingSpaceRun) {
                 ExpansionBehavior expansionBehavior = expansionBehaviorForInlineTextBox(*this, textBox, previousRun, run->next(), textAlign, isAfterExpansion);
                 applyExpansionBehavior(textBox, expansionBehavior);
index 9fe8739..17e1e21 100644 (file)
@@ -512,22 +512,75 @@ inline bool isHangablePunctuationAtLineEnd(UChar c)
     return U_GET_GC_MASK(c) & (U_GC_PE_MASK | U_GC_PI_MASK | U_GC_PF_MASK);
 }
 
-float RenderText::hangablePunctuationStartWidth() const
+float RenderText::hangablePunctuationStartWidth(unsigned index) const
 {
     if (!textLength())
         return 0;
     
     ASSERT(m_text);
     StringImpl& text = *m_text.impl();
-    if (!isHangablePunctuationAtLineStart(text[0]))
-        return 0;
     
+    if (!isHangablePunctuationAtLineStart(text[index]))
+        return 0;
+
     const RenderStyle& style = this->style();
     const FontCascade& font = style.fontCascade();
         
     return widthFromCache(font, 0, 1, 0, 0, 0, style);
 }
+
+float RenderText::hangablePunctuationEndWidth(unsigned index) const
+{
+    if (!textLength())
+        return 0;
+    
+    ASSERT(m_text);
+    StringImpl& text = *m_text.impl();
+
+    if (!isHangablePunctuationAtLineEnd(text[index]))
+        return 0;
+    
+    const RenderStyle& style = this->style();
+    const FontCascade& font = style.fontCascade();
+    
+    return widthFromCache(font, 0, 1, 0, 0, 0, style);
+}
+
+unsigned RenderText::firstCharacterIndexStrippingSpaces() const
+{
+    if (!style().collapseWhiteSpace())
+        return 0;
+    
+    ASSERT(m_text);
+    StringImpl& text = *m_text.impl();
+    
+    unsigned i = 0;
+    for ( ; i < textLength(); ++i) {
+        if (text[i] != ' ' && (text[i] != '\n' || style().preserveNewline()) && text[i] != '\t')
+            break;
+    }
+    return i;
+}
+
+unsigned RenderText::lastCharacterIndexStrippingSpaces() const
+{
+    if (!textLength())
+        return 0;
+
+    if (!style().collapseWhiteSpace())
+        return textLength() - 1;
+    
+    ASSERT(m_text);
+    StringImpl& text = *m_text.impl();
     
+    int i = textLength() - 1;
+    for ( ; i  >= 0; --i) {
+        if (text[i] != ' ' && (text[i] != '\n' || style().preserveNewline()) && text[i] != '\t')
+            break;
+    }
+    return i;
+}
+
 void RenderText::trimmedPrefWidths(float leadWidth,
                                    float& beginMinW, bool& beginWS,
                                    float& endMinW, bool& endWS,
index e934366..1b5fb48 100644 (file)
@@ -102,7 +102,10 @@ public:
                            bool& hasBreakableChar, bool& hasBreak,
                            float& beginMaxW, float& endMaxW,
                            float& minW, float& maxW, bool& stripFrontSpaces);
-    float hangablePunctuationStartWidth() const;
+    float hangablePunctuationStartWidth(unsigned index) const;
+    float hangablePunctuationEndWidth(unsigned index) const;
+    unsigned firstCharacterIndexStrippingSpaces() const;
+    unsigned lastCharacterIndexStrippingSpaces() const;
 
     WEBCORE_EXPORT virtual IntRect linesBoundingBox() const;
     LayoutRect linesVisualOverflowBoundingBox() const;
index ed32be1..179e936 100644 (file)
@@ -738,7 +738,8 @@ inline bool BreakingContext::handleText(WordMeasurements& wordMeasurements, bool
     bool isFixedPitch = font.isFixedPitch();
     bool canHyphenate = style.hyphens() == HyphensAuto && WebCore::canHyphenate(style.locale());
     bool canHangPunctuationAtStart = style.hangingPunctuation() & FirstHangingPunctuation;
-    
+    bool canHangPunctuationAtEnd = style.hangingPunctuation() & LastHangingPunctuation;
+    int endPunctuationIndex = canHangPunctuationAtEnd && m_collapseWhiteSpace ? renderText.lastCharacterIndexStrippingSpaces() : renderText.textLength() - 1;
     unsigned lastSpace = m_current.offset();
     float wordSpacing = m_currentStyle->fontCascade().wordSpacing();
     float lastSpaceWordSpacing = 0;
@@ -788,9 +789,16 @@ inline bool BreakingContext::handleText(WordMeasurements& wordMeasurements, bool
         UChar c = m_current.current();
         m_currentCharacterIsSpace = c == ' ' || c == '\t' || (!m_preservesNewline && (c == '\n'));
 
-        if (canHangPunctuationAtStart && !m_current.offset() && m_width.isFirstLine() && !m_width.committedWidth() && !wrapW && !m_current.offset())
-            m_width.addUncommittedWidth(-renderText.hangablePunctuationStartWidth());
-            
+        if (canHangPunctuationAtStart && m_width.isFirstLine() && !m_width.committedWidth() && !wrapW && !inlineLogicalWidth(m_current.renderer(), true, false)) {
+            m_width.addUncommittedWidth(-renderText.hangablePunctuationStartWidth(m_current.offset()));
+            canHangPunctuationAtStart = false;
+        }
+        
+        if (canHangPunctuationAtEnd && !m_nextObject && (int)m_current.offset() == endPunctuationIndex && !inlineLogicalWidth(m_current.renderer(), false, true)) {
+            m_width.addUncommittedWidth(-renderText.hangablePunctuationEndWidth(endPunctuationIndex));
+            canHangPunctuationAtStart = false;
+        }
+
         if (!m_collapseWhiteSpace || !m_currentCharacterIsSpace)
             m_lineInfo.setEmpty(false, &m_block, &m_width);